xref: /illumos-gate/usr/src/lib/libc/port/gen/getut.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 /*
34  * Compatibility routines to read and write alternate
35  * utmp-like files.  These routines are only used in
36  * the case where utmpname() is used to change to a file
37  * other than /var/adm/utmp or /var/adm/wtmp.  In this case,
38  * we assume that someone really wants to read old utmp-format
39  * files.  Otherwise, the getutent, setutent, getutid, setutline,
40  * and pututline functions are actually wrappers around the
41  * equivalent function operating on utmpx-like files.
42  */
43 
44 #include "synonyms.h"
45 #include <stdio.h>
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <utmpx.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #include <strings.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <ctype.h>
57 #include <utime.h>
58 #include <sys/wait.h>
59 
60 #define	IDLEN	4	/* length of id field in utmp */
61 #define	SC_WILDC	0xff	/* wild char for utmp ids */
62 #define	MAXVAL	255	/* max value for an id 'character' */
63 
64 #ifdef ut_time
65 #undef ut_time
66 #endif
67 
68 static void	utmp_frec2api(const struct futmp *, struct utmp *);
69 static void	utmp_api2frec(const struct utmp *, struct futmp *);
70 struct utmp 	*_compat_getutent(void);
71 struct utmp	*_compat_getutid(const struct utmp *);
72 struct utmp	*_compat_getutline(const struct utmp *);
73 struct utmp	*_compat_pututline(const struct utmp *);
74 void		_compat_setutent(void);
75 void		_compat_endutent(void);
76 void		_compat_updwtmp(const char *, struct utmp *);
77 struct utmp	*_compat_makeut(struct utmp *);
78 struct utmp	*_compat_modut(struct utmp *);
79 
80 static void	unlockut(void);
81 static int	idcmp(const char *, const char *);
82 static int	allocid(char *, unsigned char *);
83 static int	lockut(void);
84 
85 
86 static int fd = -1;	/* File descriptor for the utmp file. */
87 /*
88  * name of the current utmp-like file - set by utmpname (getutx.c)
89  * only if running in backward compatibility mode
90  */
91 extern const char _compat_utmpfile[];
92 
93 #ifdef ERRDEBUG
94 static long loc_utmp;	/* Where in "utmp" the current "ubuf" was found. */
95 #endif
96 
97 static struct futmp fubuf;	/* Copy of last entry read in. */
98 static struct utmp ubuf;	/* Last entry returned to client */
99 
100 /*
101  * In the 64-bit world, the utmp data structure grows because of
102  * the ut_time field (a time_t) at the end of it.
103  */
104 static void
105 utmp_frec2api(const struct futmp *src, struct utmp *dst)
106 {
107 	if (src == NULL)
108 		return;
109 
110 	(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
111 	(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
112 	(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
113 	dst->ut_pid = src->ut_pid;
114 	dst->ut_type = src->ut_type;
115 	dst->ut_exit.e_termination = src->ut_exit.e_termination;
116 	dst->ut_exit.e_exit = src->ut_exit.e_exit;
117 	dst->ut_time = (time_t)src->ut_time;
118 }
119 
120 static void
121 utmp_api2frec(const struct utmp *src, struct futmp *dst)
122 {
123 	if (src == NULL)
124 		return;
125 
126 	(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
127 	(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
128 	(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
129 	dst->ut_pid = src->ut_pid;
130 	dst->ut_type = src->ut_type;
131 	dst->ut_exit.e_termination = src->ut_exit.e_termination;
132 	dst->ut_exit.e_exit = src->ut_exit.e_exit;
133 	dst->ut_time = (time32_t)src->ut_time;
134 }
135 
136 /*
137  * "getutent_frec" gets the raw version of the next entry in the utmp file.
138  */
139 static struct futmp *
140 getutent_frec(void)
141 {
142 	/*
143 	 * If the "utmp" file is not open, attempt to open it for
144 	 * reading.  If there is no file, attempt to create one.  If
145 	 * both attempts fail, return NULL.  If the file exists, but
146 	 * isn't readable and writeable, do not attempt to create.
147 	 */
148 	if (fd < 0) {
149 		if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0) {
150 
151 			/*
152 			 * If the open failed for permissions, try opening
153 			 * it only for reading.  All "pututline()" later
154 			 * will fail the writes.
155 			 */
156 			if ((fd = open(_compat_utmpfile, O_RDONLY)) < 0)
157 				return (NULL);
158 		}
159 	}
160 
161 	/* Try to read in the next entry from the utmp file.  */
162 
163 	if (read(fd, &fubuf, sizeof (fubuf)) != sizeof (fubuf)) {
164 		bzero(&fubuf, sizeof (fubuf));
165 		return (NULL);
166 	}
167 
168 	/* Save the location in the file where this entry was found. */
169 
170 	(void) lseek(fd, 0L, 1);
171 	return (&fubuf);
172 }
173 
174 /*
175  * "_compat_getutent" gets the next entry in the utmp file.
176  */
177 struct utmp *
178 _compat_getutent(void)
179 {
180 	struct futmp *futp;
181 
182 	futp = getutent_frec();
183 	utmp_frec2api(&fubuf, &ubuf);
184 	if (futp == NULL)
185 		return (NULL);
186 	return (&ubuf);
187 }
188 
189 /*
190  * "_compat_getutid" finds the specified entry in the utmp file.  If
191  * it can't find it, it returns NULL.
192  */
193 struct utmp *
194 _compat_getutid(const struct utmp *entry)
195 {
196 	short type;
197 
198 	utmp_api2frec(&ubuf, &fubuf);
199 
200 	/*
201 	 * Start looking for entry.  Look in our current buffer before
202 	 * reading in new entries.
203 	 */
204 	do {
205 		/*
206 		 * If there is no entry in "ubuf", skip to the read.
207 		 */
208 		if (fubuf.ut_type != EMPTY) {
209 			switch (entry->ut_type) {
210 
211 			/*
212 			 * Do not look for an entry if the user sent
213 			 * us an EMPTY entry.
214 			 */
215 			case EMPTY:
216 				return (NULL);
217 
218 			/*
219 			 * For RUN_LVL, BOOT_TIME, DOWN_TIME,
220 			 * OLD_TIME, and NEW_TIME entries, only the
221 			 * types have to match.  If they do, return
222 			 * the address of internal buffer.
223 			 */
224 			case RUN_LVL:
225 			case BOOT_TIME:
226 			case DOWN_TIME:
227 			case OLD_TIME:
228 			case NEW_TIME:
229 				if (entry->ut_type == fubuf.ut_type) {
230 					utmp_frec2api(&fubuf, &ubuf);
231 					return (&ubuf);
232 				}
233 				break;
234 
235 			/*
236 			 * For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS,
237 			 * and DEAD_PROCESS the type of the entry in "fubuf",
238 			 * must be one of the above and id's must match.
239 			 */
240 			case INIT_PROCESS:
241 			case LOGIN_PROCESS:
242 			case USER_PROCESS:
243 			case DEAD_PROCESS:
244 				if (((type = fubuf.ut_type) == INIT_PROCESS ||
245 				    type == LOGIN_PROCESS ||
246 				    type == USER_PROCESS ||
247 				    type == DEAD_PROCESS) &&
248 				    fubuf.ut_id[0] == entry->ut_id[0] &&
249 				    fubuf.ut_id[1] == entry->ut_id[1] &&
250 				    fubuf.ut_id[2] == entry->ut_id[2] &&
251 				    fubuf.ut_id[3] == entry->ut_id[3]) {
252 					utmp_frec2api(&fubuf, &ubuf);
253 					return (&ubuf);
254 				}
255 				break;
256 
257 			/* Do not search for illegal types of entry. */
258 			default:
259 				return (NULL);
260 			}
261 		}
262 	} while (getutent_frec() != NULL);
263 
264 	/* the proper entry wasn't found. */
265 
266 	utmp_frec2api(&fubuf, &ubuf);
267 	return (NULL);
268 }
269 
270 /*
271  * "_compat_getutline" searches the "utmp" file for a LOGIN_PROCESS or
272  * USER_PROCESS with the same "line" as the specified "entry".
273  */
274 struct utmp *
275 _compat_getutline(const struct utmp *entry)
276 {
277 	utmp_api2frec(&ubuf, &fubuf);
278 
279 	do {
280 		/*
281 		 * If the current entry is the one we are interested in,
282 		 * return a pointer to it.
283 		 */
284 		if (fubuf.ut_type != EMPTY &&
285 		    (fubuf.ut_type == LOGIN_PROCESS ||
286 		    fubuf.ut_type == USER_PROCESS) &&
287 		    strncmp(&entry->ut_line[0], &fubuf.ut_line[0],
288 		    sizeof (fubuf.ut_line)) == 0) {
289 			utmp_frec2api(&fubuf, &ubuf);
290 			return (&ubuf);
291 		}
292 	} while (getutent_frec() != NULL);
293 
294 	utmp_frec2api(&fubuf, &ubuf);
295 	return (NULL);
296 }
297 
298 /*
299  * "_compat_pututline" writes the structure sent into the utmp file
300  * If there is already an entry with the same id, then it is
301  * overwritten, otherwise a new entry is made at the end of the
302  * utmp file.
303  */
304 struct utmp *
305 _compat_pututline(const struct utmp *entry)
306 {
307 	int fc;
308 	struct utmp *answer;
309 	struct utmp tmpbuf;
310 	struct futmp ftmpbuf;
311 
312 	/*
313 	 * Copy the user supplied entry into our temporary buffer to
314 	 * avoid the possibility that the user is actually passing us
315 	 * the address of "ubuf".
316 	 */
317 	tmpbuf = *entry;
318 	utmp_api2frec(entry, &ftmpbuf);
319 
320 	(void) getutent_frec();
321 	if (fd < 0) {
322 #ifdef	ERRDEBUG
323 		gdebug("pututline: Unable to create utmp file.\n");
324 #endif
325 		return (NULL);
326 	}
327 
328 	/* Make sure file is writable */
329 
330 	if ((fc = fcntl(fd, F_GETFL, NULL)) == -1 || (fc & O_RDWR) != O_RDWR)
331 		return (NULL);
332 
333 	/*
334 	 * Find the proper entry in the utmp file.  Start at the current
335 	 * location.  If it isn't found from here to the end of the
336 	 * file, then reset to the beginning of the file and try again.
337 	 * If it still isn't found, then write a new entry at the end of
338 	 * the file.  (Making sure the location is an integral number of
339 	 * utmp structures into the file incase the file is scribbled.)
340 	 */
341 
342 	if (_compat_getutid(&tmpbuf) == NULL) {
343 #ifdef	ERRDEBUG
344 		gdebug("1st getutid() failed. fd: %d", fd);
345 #endif
346 		_compat_setutent();
347 		if (_compat_getutid(&tmpbuf) == NULL) {
348 #ifdef	ERRDEBUG
349 			loc_utmp = lseek(fd, 0L, 1);
350 			gdebug("2nd getutid() failed. fd: %d loc_utmp: %ld\n",
351 			    fd, loc_utmp);
352 #endif
353 			(void) fcntl(fd, F_SETFL, fc | O_APPEND);
354 		} else
355 			(void) lseek(fd, -(long)sizeof (struct futmp), 1);
356 	} else
357 		(void) lseek(fd, -(long)sizeof (struct futmp), 1);
358 
359 	/*
360 	 * Write out the user supplied structure.  If the write fails,
361 	 * then the user probably doesn't have permission to write the
362 	 * utmp file.
363 	 */
364 	if (write(fd, &ftmpbuf, sizeof (ftmpbuf)) != sizeof (ftmpbuf)) {
365 #ifdef	ERRDEBUG
366 		gdebug("pututline failed: write-%d\n", errno);
367 #endif
368 		answer = NULL;
369 	} else {
370 		/*
371 		 * Copy the new user structure into ubuf so that it will
372 		 * be up to date in the future.
373 		 */
374 		fubuf = ftmpbuf;
375 		utmp_frec2api(&fubuf, &ubuf);
376 		answer = &ubuf;
377 
378 #ifdef	ERRDEBUG
379 		gdebug("id: %c%c loc: %ld\n", fubuf.ut_id[0],
380 		    fubuf.ut_id[1], fubuf.ut_id[2], fubuf.ut_id[3],
381 		    loc_utmp);
382 #endif
383 	}
384 
385 	(void) fcntl(fd, F_SETFL, fc);
386 
387 	return (answer);
388 }
389 
390 /*
391  * "_compat_setutent" just resets the utmp file back to the beginning.
392  */
393 void
394 _compat_setutent(void)
395 {
396 	if (fd != -1)
397 		(void) lseek(fd, 0L, 0);
398 
399 	/*
400 	 * Zero the stored copy of the last entry read, since we are
401 	 * resetting to the beginning of the file.
402 	 */
403 	bzero(&ubuf, sizeof (ubuf));
404 	bzero(&fubuf, sizeof (fubuf));
405 }
406 
407 /*
408  * "_compat_endutent" closes the utmp file.
409  */
410 void
411 _compat_endutent(void)
412 {
413 	if (fd != -1)
414 		(void) close(fd);
415 	fd = -1;
416 	bzero(&ubuf, sizeof (ubuf));
417 	bzero(&fubuf, sizeof (fubuf));
418 }
419 
420 
421 /*
422  * If one of wtmp and wtmpx files exist, create the other, and the record.
423  * If they both exist add the record.
424  */
425 void
426 _compat_updwtmp(const char *file, struct utmp *ut)
427 {
428 	struct futmp fut;
429 	int fd;
430 
431 
432 	fd = open(file, O_WRONLY | O_APPEND);
433 
434 	if (fd < 0) {
435 		if ((fd = open(file, O_WRONLY|O_CREAT, 0644)) < 0)
436 			return;
437 	}
438 
439 	(void) lseek(fd, 0, 2);
440 
441 	utmp_api2frec(ut, &fut);
442 	(void) write(fd, &fut, sizeof (fut));
443 
444 	(void) close(fd);
445 }
446 
447 
448 
449 /*
450  * makeut - create a utmp entry, recycling an id if a wild card is
451  *	specified.
452  *
453  *	args:	utmp - point to utmp structure to be created
454  */
455 struct utmp *
456 _compat_makeut(struct utmp *utmp)
457 {
458 	int i;
459 	struct utmp *utp;	/* "current" utmp entry being examined */
460 	int wild;		/* flag, true iff wild card char seen */
461 
462 	/* the last id we matched that was NOT a dead proc */
463 	unsigned char saveid[IDLEN];
464 
465 	wild = 0;
466 	for (i = 0; i < IDLEN; i++)
467 		if ((unsigned char)utmp->ut_id[i] == SC_WILDC) {
468 			wild = 1;
469 			break;
470 		}
471 
472 	if (wild) {
473 
474 		/*
475 		 * try to lock the utmp file, only needed if we're
476 		 * doing wildcard matching
477 		 */
478 
479 		if (lockut())
480 			return (0);
481 		_compat_setutent();
482 
483 		/* find the first alphanumeric character */
484 		for (i = 0; i < MAXVAL; ++i)
485 			if (isalnum(i))
486 				break;
487 
488 		(void) memset(saveid, i, IDLEN);
489 
490 		while ((utp = _compat_getutent()) != 0) {
491 			if (idcmp(utmp->ut_id, utp->ut_id))
492 				continue;
493 			if (utp->ut_type == DEAD_PROCESS)
494 				break;
495 			(void) memcpy(saveid, utp->ut_id, IDLEN);
496 		}
497 
498 		if (utp) {
499 			/*
500 			 * found an unused entry, reuse it
501 			 */
502 			(void) memcpy(utmp->ut_id, utp->ut_id, IDLEN);
503 			utp = _compat_pututline(utmp);
504 			if (utp)
505 				_compat_updwtmp(WTMP_FILE, utp);
506 			_compat_endutent();
507 			unlockut();
508 			return (utp);
509 
510 		} else {
511 			/*
512 			 * nothing available, try to allocate an id
513 			 */
514 			if (allocid(utmp->ut_id, saveid)) {
515 				_compat_endutent();
516 				unlockut();
517 				return (NULL);
518 			} else {
519 				utp = _compat_pututline(utmp);
520 				if (utp)
521 					_compat_updwtmp(WTMP_FILE, utp);
522 				_compat_endutent();
523 				unlockut();
524 				return (utp);
525 			}
526 		}
527 	} else {
528 		utp = _compat_pututline(utmp);
529 		if (utp)
530 			_compat_updwtmp(WTMP_FILE, utp);
531 		_compat_endutent();
532 		return (utp);
533 	}
534 }
535 
536 
537 /*
538  * _compat_modut - modify a utmp entry.
539  *
540  *	args:	utmp - point to utmp structure to be created
541  */
542 struct utmp *
543 _compat_modut(struct utmp *utp)
544 {
545 	int i;					/* scratch variable */
546 	struct utmp utmp;			/* holding area */
547 	struct utmp *ucp = &utmp;		/* and a pointer to it */
548 	struct utmp *up;	/* "current" utmp entry being examined */
549 	struct futmp *fup;
550 
551 	for (i = 0; i < IDLEN; ++i)
552 		if ((unsigned char)utp->ut_id[i] == SC_WILDC)
553 			return (0);
554 
555 	/* copy the supplied utmp structure someplace safe */
556 	utmp = *utp;
557 	_compat_setutent();
558 	while (fup = getutent_frec()) {
559 		if (idcmp(ucp->ut_id, fup->ut_id))
560 			continue;
561 		break;
562 	}
563 	up = _compat_pututline(ucp);
564 	if (up)
565 		_compat_updwtmp(WTMP_FILE, up);
566 	_compat_endutent();
567 	return (up);
568 }
569 
570 
571 
572 /*
573  * idcmp - compare two id strings, return 0 if same, non-zero if not *
574  *	args:	s1 - first id string
575  *		s2 - second id string
576  */
577 static int
578 idcmp(const char *s1, const char *s2)
579 {
580 	int i;
581 
582 	for (i = 0; i < IDLEN; ++i)
583 		if ((unsigned char)*s1 != SC_WILDC && (*s1++ != *s2++))
584 			return (-1);
585 	return (0);
586 }
587 
588 
589 /*
590  * allocid - allocate an unused id for utmp, either by recycling a
591  *	DEAD_PROCESS entry or creating a new one.  This routine only
592  *	gets called if a wild card character was specified.
593  *
594  *	args:	srcid - pattern for new id
595  *		saveid - last id matching pattern for a non-dead process
596  */
597 static int
598 allocid(char *srcid, unsigned char *saveid)
599 {
600 	int i;		/* scratch variable */
601 	int changed;	/* flag to indicate that a new id has been generated */
602 	char copyid[IDLEN];	/* work area */
603 
604 	(void) memcpy(copyid, srcid, IDLEN);
605 	changed = 0;
606 	for (i = 0; i < IDLEN; ++i) {
607 		/*
608 		 * if this character isn't wild, it'll
609 		 * be part of the generated id
610 		 */
611 		if ((unsigned char) copyid[i] != SC_WILDC)
612 			continue;
613 		/*
614 		 * it's a wild character, retrieve the
615 		 * character from the saved id
616 		 */
617 		copyid[i] = saveid[i];
618 		/*
619 		 * if we haven't changed anything yet,
620 		 * try to find a new char to use
621 		 */
622 		if (!changed && (saveid[i] < MAXVAL)) {
623 
624 /*
625  * Note: this algorithm is taking the "last matched" id and trying to make
626  * a 1 character change to it to create a new one.  Rather than special-case
627  * the first time (when no perturbation is really necessary), just don't
628  * allocate the first valid id.
629  */
630 
631 			while (++saveid[i] < MAXVAL) {
632 				/* make sure new char is alphanumeric */
633 				if (isalnum(saveid[i])) {
634 					copyid[i] = saveid[i];
635 					changed = 1;
636 					break;
637 				}
638 			}
639 
640 			if (!changed) {
641 				/*
642 				 * Then 'reset' the current count at
643 				 * this position to it's lowest valid
644 				 * value, and propagate the carry to
645 				 * the next wild-card slot
646 				 *
647 				 * See 1113208.
648 				 */
649 				saveid[i] = 0;
650 				while (!isalnum(saveid[i]))
651 					saveid[i]++;
652 				copyid[i] = ++saveid[i];
653 			}
654 		}
655 	}
656 	/* changed is true if we were successful in allocating an id */
657 	if (changed) {
658 		(void) memcpy(srcid, copyid, IDLEN);
659 		return (0);
660 	} else
661 		return (-1);
662 }
663 
664 
665 /*
666  * lockut - lock utmp file
667  */
668 static int
669 lockut(void)
670 {
671 	if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0)
672 		return (-1);
673 
674 	if (lockf(fd, F_LOCK, 0) < 0) {
675 		(void) close(fd);
676 		fd = -1;
677 		return (-1);
678 	}
679 	return (0);
680 }
681 
682 
683 /*
684  * unlockut - unlock utmp file
685  */
686 static void
687 unlockut(void)
688 {
689 	(void) lockf(fd, F_ULOCK, 0);
690 	(void) close(fd);
691 	fd = -1;
692 }
693 
694 
695 
696 #ifdef  ERRDEBUG
697 
698 #include <stdarg.h>
699 #include <stdio.h>
700 
701 static void
702 gdebug(const char *fmt, ...)
703 {
704 	FILE *fp;
705 	int errnum;
706 	va_list ap;
707 
708 	if ((fp = fopen("/etc/dbg.getut", "a+")) == NULL)
709 		return;
710 	va_start(ap, fmt);
711 	(void) vfprintf(fp, fmt, ap);
712 	va_end(ap);
713 	(void) fclose(fp);
714 }
715 #endif
716