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 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 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 * 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 * 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 * 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 * 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 * 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 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 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 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 * 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 * 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 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 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 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 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 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