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