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