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