186 if (seteuid(userid) < 0) { 187 plog(XLOG_WARNING, "could not seteuid to %d: %m", userid); 188 return linkval; 189 } 190 if (hlfsd_stat(linkval, &homestat) < 0) { 191 if (errno == ENOENT) { /* make the spool dir if possible */ 192 /* don't use recursive mkdirs here */ 193 if (mkdir(linkval, PERS_SPOOLMODE) < 0) { 194 seteuid(0); 195 plog(XLOG_WARNING, "can't make directory %s: %m", linkval); 196 return alt_spooldir; 197 } 198 /* fall through to testing the disk space / quota */ 199 } else { /* the home dir itself must not exist then */ 200 seteuid(0); 201 plog(XLOG_WARNING, "bad link to %s: %m", linkval); 202 return alt_spooldir; 203 } 204 } 205 206 /* 207 * If gets here, then either the spool dir in the home dir exists, 208 * or it was just created. In either case, we now need to 209 * test if we can create a small file and write at least one 210 * byte into it. This will test that we have both enough inodes 211 * and disk blocks to spare, or they fall within the user's quotas too. 212 * We are still seteuid to the user at this point. 213 */ 214 if (hlfsd_diskspace(linkval) < 0) { 215 seteuid(0); 216 plog(XLOG_WARNING, "no more space in %s: %m", linkval); 217 return alt_spooldir; 218 } else { 219 seteuid(0); 220 return linkval; 221 } 222} 223 224 225static int 226hlfsd_diskspace(char *path) 227{ 228 char buf[MAXPATHLEN]; 229 int fd, len; 230 231 clock_valid = 0; /* invalidate logging clock */ 232 233 sprintf(buf, "%s/._hlfstmp_%lu", path, (long) getpid()); 234 if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) { 235 plog(XLOG_ERROR, "cannot open %s: %m", buf); 236 return -1; 237 } 238 len = strlen(buf); 239 if (write(fd, buf, len) < len) { 240 plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf); 241 close(fd); 242 unlink(buf); /* cleanup just in case */ 243 return -1; 244 } 245 if (unlink(buf) < 0) { 246 plog(XLOG_ERROR, "cannot unlink %s : %m", buf); 247 } 248 close(fd); 249 return 0; 250} 251 252 253static int 254hlfsd_stat(char *path, struct stat *statp) 255{ 256 if (stat(path, statp) < 0) 257 return -1; 258 else if (!S_ISDIR(statp->st_mode)) { 259 errno = ENOTDIR; 260 return -1; 261 } 262 return 0; 263} 264 265 266static void 267delay(uid2home_t *found, int secs) 268{ 269 struct timeval tv; 270 271#ifdef DEBUG 272 if (found) 273 dlog("delaying on child %d for %d seconds", found->child, secs); 274#endif /* DEBUG */ 275 276 tv.tv_usec = 0; 277 278 do { 279 tv.tv_sec = secs; 280 if (select(0, 0, 0, 0, &tv) == 0) 281 break; 282 } while (--secs && found->child); 283} 284 285 286/* 287 * This function is called when a child has terminated after 288 * servicing an nfs request. We need to check the exit status and 289 * update the last_status field of the requesting user. 290 */ 291RETSIGTYPE 292interlock(int signum) 293{ 294 int child; 295 uid2home_t *lostchild; 296 int status; 297 298#ifdef HAVE_WAITPID 299 while ((child = waitpid((pid_t) -1, &status, WNOHANG)) > 0) { 300#else /* not HAVE_WAITPID */ 301 while ((child = wait3(&status, WNOHANG, (struct rusage *) 0)) > 0) { 302#endif /* not HAVE_WAITPID */ 303 304 /* high chances this was the last child forked */ 305 if (lastchild && lastchild->child == child) { 306 lastchild->child = 0; 307 308 if (WIFEXITED(status)) 309 lastchild->last_status = WEXITSTATUS(status); 310 lastchild = (uid2home_t *) NULL; 311 } else { 312 /* and if not, we have to search for it... */ 313 for (lostchild = pwtab; lostchild < &pwtab[cur_pwtab_num]; lostchild++) { 314 if (lostchild->child == child) { 315 if (WIFEXITED(status)) 316 lostchild->last_status = WEXITSTATUS(status); 317 lostchild->child = 0; 318 break; 319 } 320 } 321 } 322 } 323} 324 325 326/* 327 * PASSWORD AND USERNAME LOOKUP TABLES FUNCTIONS 328 */ 329 330/* 331 * get index of UserName table entry which matches username. 332 * must not return uid_t because we want to return a negative number. 333 */ 334int 335untab_index(char *username) 336{ 337 int max, min, mid, cmp; 338 339 max = cur_pwtab_num - 1; 340 min = 0; 341 342 do { 343 mid = (max + min) / 2; 344 cmp = strcmp(untab[mid].username, username); 345 if (cmp == 0) /* record found! */ 346 return mid; 347 if (cmp > 0) 348 max = mid; 349 else 350 min = mid; 351 } while (max > min + 1); 352 353 if (STREQ(untab[max].username, username)) 354 return max; 355 if (STREQ(untab[min].username, username)) 356 return min; 357 358 /* if gets here then record was not found */ 359 return -1; 360} 361 362 363/* 364 * Don't make this return a uid_t, because we need to return negative 365 * numbers as well (error codes.) 366 */ 367int 368uidof(char *username) 369{ 370 int idx; 371 372 if ((idx = untab_index(username)) < 0) /* not found */ 373 return INVALIDID; /* an invalid user id */ 374 return untab[idx].uid; 375} 376 377 378/* 379 * Don't make this return a uid_t, because we need to return negative 380 * numbers as well (error codes.) 381 */ 382char * 383homeof(char *username) 384{ 385 int idx; 386 387 if ((idx = untab_index(username)) < 0) /* not found */ 388 return (char *) NULL; /* an invalid user id */ 389 return untab[idx].home; 390} 391 392 393char * 394mailbox(int uid, char *username) 395{ 396 char *home; 397 398 if (uid < 0) 399 return (char *) NULL; /* not found */ 400 401 if ((home = homeof(username)) == (char *) NULL) 402 return (char *) NULL; 403 if (STREQ(home, "/")) 404 sprintf(mboxfile, "/%s/%s", home_subdir, username); 405 else 406 sprintf(mboxfile, "%s/%s/%s", home, home_subdir, username); 407 return mboxfile; 408} 409 410 411static int 412plt_compare_fxn(const voidp x, const voidp y) 413 414{ 415 uid2home_t *i = (uid2home_t *) x; 416 uid2home_t *j = (uid2home_t *) y; 417 418 return i->uid - j->uid; 419} 420 421 422static int 423unt_compare_fxn(const voidp x, const voidp y) 424{ 425 username2uid_t *i = (username2uid_t *) x; 426 username2uid_t *j = (username2uid_t *) y; 427 428 return strcmp(i->username, j->username); 429} 430 431 432/* perform initialization of user passwd database */ 433static void 434hlfsd_setpwent(void) 435{ 436 if (!passwdfile) { 437 setpwent(); 438 return; 439 } 440 441 passwd_fp = fopen(passwdfile, "r"); 442 if (!passwd_fp) { 443 plog(XLOG_ERROR, "unable to read passwd file %s: %m", passwdfile); 444 return; 445 } 446 plog(XLOG_INFO, "reading password entries from file %s", passwdfile); 447 448 passwd_line = 0; 449 memset((char *) &passwd_ent, 0, sizeof(struct passwd)); 450 passwd_ent.pw_name = (char *) &pw_name; 451 passwd_ent.pw_dir = (char *) &pw_dir; 452} 453 454 455/* perform de-initialization of user passwd database */ 456static void 457hlfsd_endpwent(void) 458{ 459 if (!passwdfile) { 460 /* 461 * Don't actually run this because we will be making more passwd calls 462 * afterwards. On Solaris 2.5.1, making getpwent() calls after calling 463 * endpwent() results in a memory leak! (and no, even Purify didn't 464 * detect it...) 465 * 466 endpwent(); 467 */ 468 return; 469 } 470 471 if (passwd_fp) { 472 fclose(passwd_fp); 473 } 474} 475 476 477/* perform record reading/parsing of individual passwd database records */ 478static struct passwd * 479hlfsd_getpwent(void) 480{ 481 char buf[256], *cp; 482 483 /* check if to perform standard unix function */ 484 if (!passwdfile) { 485 return getpwent(); 486 } 487 488 clock_valid = 0; /* invalidate logging clock */ 489 490 /* return here to read another entry */ 491readent: 492 493 /* return NULL if reached end of file */ 494 if (feof(passwd_fp)) 495 return NULL; 496 497 pw_name[0] = pw_dir[0] = '\0'; 498 499 /* read records */ 500 buf[0] = '\0'; 501 fgets(buf, 256, passwd_fp); 502 passwd_line++; 503 if (!buf || buf[0] == '\0') 504 goto readent; 505 506 /* read user name */ 507 cp = strtok(buf, ":"); 508 if (!cp || cp[0] == '\0') { 509 plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile); 510 goto readent; 511 } 512 strcpy(pw_name, cp); /* will show up in passwd_ent.pw_name */ 513 514 /* skip passwd */ 515 strtok(NULL, ":"); 516 517 /* read uid */ 518 cp = strtok(NULL, ":"); 519 if (!cp || cp[0] == '\0') { 520 plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile); 521 goto readent; 522 } 523 passwd_ent.pw_uid = atoi(cp); 524 525 /* skip gid and gcos */ 526 strtok(NULL, ":"); 527 strtok(NULL, ":"); 528 529 /* read home dir */ 530 cp = strtok(NULL, ":"); 531 if (!cp || cp[0] == '\0') { 532 plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line, passwdfile); 533 goto readent; 534 } 535 strcpy(pw_dir, cp); /* will show up in passwd_ent.pw_dir */ 536 537 /* the rest of the fields are unimportant and not being considered */ 538 539 plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%d, dir=%s", 540 passwd_ent.pw_name, passwd_ent.pw_uid, passwd_ent.pw_dir); 541 542 return &passwd_ent; 543} 544 545 546/* 547 * read and hash the passwd file or NIS map 548 */ 549void 550plt_init(void) 551{ 552 struct passwd *pent_p; 553 554 if (plt_reset() < 0) /* could not reset table. skip. */ 555 return; 556 557 plog(XLOG_INFO, "reading password map"); 558 559 hlfsd_setpwent(); /* prepare to read passwd entries */ 560 while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) { 561 table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name); 562 } 563 hlfsd_endpwent(); 564 565 qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t), 566 plt_compare_fxn); 567 qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t), 568 unt_compare_fxn); 569 570 plog(XLOG_INFO, "password map read and sorted"); 571} 572 573 574/* 575 * This is essentially so that we don't reset known good lookup tables when a 576 * YP server goes down. 577 */ 578static int 579plt_reset(void) 580{ 581 int i; 582 583 clock_valid = 0; /* invalidate logging clock */ 584 585 hlfsd_setpwent(); 586 if (hlfsd_getpwent() == (struct passwd *) NULL) { 587 hlfsd_endpwent(); 588 return -1; /* did not reset table */ 589 } 590 hlfsd_endpwent(); 591 592 lastchild = (uid2home_t *) NULL; 593 594 if (max_pwtab_num > 0) /* was used already. cleanup old table */ 595 for (i = 0; i < cur_pwtab_num; ++i) { 596 if (pwtab[i].home) { 597 XFREE(pwtab[i].home); 598 pwtab[i].home = (char *) NULL; 599 } 600 pwtab[i].uid = INVALIDID; /* not a valid uid (yet...) */ 601 pwtab[i].child = (pid_t) 0; 602 pwtab[i].uname = (char *) NULL; /* only a ptr to untab[i].username */ 603 if (untab[i].username) { 604 XFREE(untab[i].username); 605 untab[i].username = (char *) NULL; 606 } 607 untab[i].uid = INVALIDID; /* invalid uid */ 608 untab[i].home = (char *) NULL; /* only a ptr to pwtab[i].home */ 609 } 610 cur_pwtab_num = 0; /* zero current size */ 611 612 return 0; /* resetting ok */ 613} 614 615 616/* 617 * u: uid number 618 * h: home directory 619 * n: user ID name 620 */ 621static void 622table_add(int u, const char *h, const char *n) 623{ 624 int i; 625 626 clock_valid = 0; /* invalidate logging clock */ 627 628 if (max_pwtab_num <= 0) { /* was never initialized */ 629 max_pwtab_num = 1; 630 pwtab = (uid2home_t *) xmalloc(max_pwtab_num * 631 sizeof(uid2home_t)); 632 memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t)); 633 untab = (username2uid_t *) xmalloc(max_pwtab_num * 634 sizeof(username2uid_t)); 635 memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t)); 636 } 637 638 /* check if need more space. */ 639 if (cur_pwtab_num + 1 > max_pwtab_num) { 640 /* need more space in table */ 641 max_pwtab_num *= 2; 642 plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num); 643 pwtab = (uid2home_t *) xrealloc(pwtab, 644 sizeof(uid2home_t) * max_pwtab_num); 645 untab = (username2uid_t *) xrealloc(untab, 646 sizeof(username2uid_t) * 647 max_pwtab_num); 648 /* zero out newly added entries */ 649 for (i=cur_pwtab_num; i<max_pwtab_num; ++i) { 650 memset((char *) &pwtab[i], 0, sizeof(uid2home_t)); 651 memset((char *) &untab[i], 0, sizeof(username2uid_t)); 652 } 653 } 654 655 /* do NOT add duplicate entries (this is an O(N^2) algorithm... */ 656 for (i=0; i<cur_pwtab_num; ++i) 657 if (u == pwtab[i].uid && u != 0 ) { 658#ifdef DEBUG 659 dlog("ignoring duplicate home %s for uid %d (already %s)", 660 h, u, pwtab[i].home); 661#endif /* DEBUG */ 662 return; 663 } 664 665 /* add new password entry */ 666 pwtab[cur_pwtab_num].home = strdup(h); 667 pwtab[cur_pwtab_num].child = 0; 668 pwtab[cur_pwtab_num].last_access_time = 0; 669 pwtab[cur_pwtab_num].last_status = 0; /* assume best: used homedir */ 670 pwtab[cur_pwtab_num].uid = u; 671 672 /* add new userhome entry */ 673 untab[cur_pwtab_num].username = strdup(n); 674 675 /* just a second pointer */ 676 pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username; 677 untab[cur_pwtab_num].uid = u; 678 untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home; /* a ptr */ 679 680 /* increment counter */ 681 ++cur_pwtab_num; 682} 683 684 685/* 686 * return entry in lookup table 687 */ 688uid2home_t * 689plt_search(int u) 690{ 691 int max, min, mid; 692 693 /* 694 * empty table should not happen, 695 * but I have a bug with signals to trace... 696 */ 697 if (pwtab == (uid2home_t *) NULL) 698 return (uid2home_t *) NULL; 699 700 max = cur_pwtab_num - 1; 701 min = 0; 702 703 do { 704 mid = (max + min) / 2; 705 if (pwtab[mid].uid == u) /* record found! */ 706 return &pwtab[mid]; 707 if (pwtab[mid].uid > u) 708 max = mid; 709 else 710 min = mid; 711 } while (max > min + 1); 712 713 if (pwtab[max].uid == u) 714 return &pwtab[max]; 715 if (pwtab[min].uid == u) 716 return &pwtab[min]; 717 718 /* if gets here then record was not found */ 719 return (uid2home_t *) NULL; 720} 721 722 723#if defined(DEBUG) || defined(DEBUG_PRINT) 724void 725plt_print(int signum) 726{ 727 FILE *dumpfile; 728 int dumpfd; 729 char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX"; 730 int i; 731 732#ifdef HAVE_MKSTEMP 733 dumpfd = mkstemp(dumptmp); 734#else /* not HAVE_MKSTEMP */ 735 mktemp(dumptmp); 736 if (!dumptmp) { 737 plot(XLOG_ERROR, "cannot create temporary dump file"); 738 return; 739 } 740 dumpfd = open(dumptmp, O_RDONLY); 741#endif /* not HAVE_MKSTEMP */ 742 if (dumpfd < 0) { 743 plog(XLOG_ERROR, "cannot open temporary dump file"); 744 return; 745 } 746 if ((dumpfile = fdopen(dumpfd, "a")) != NULL) { 747 plog(XLOG_INFO, "dumping internal state to file %s", dumptmp); 748 fprintf(dumpfile, "\n\nNew plt_dump():\n"); 749 for (i = 0; i < cur_pwtab_num; ++i) 750 fprintf(dumpfile, 751 "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n", 752 i, 753 (long) pwtab[i].child, 754 pwtab[i].last_access_time, 755 pwtab[i].last_status, 756 (long) pwtab[i].uid, 757 pwtab[i].home, 758 pwtab[i].uname); 759 fprintf(dumpfile, "\nUserName table by plt_print():\n"); 760 for (i = 0; i < cur_pwtab_num; ++i) 761 fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, 762 untab[i].username, (long) untab[i].uid, untab[i].home); 763 close(dumpfd); 764 fclose(dumpfile); 765 } 766} 767 768 769void 770plt_dump(uid2home_t *lastc, pid_t this) 771{ 772 FILE *dumpfile; 773 int i; 774 775 if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) { 776 fprintf(dumpfile, "\n\nNEW PLT_DUMP -- "); 777 fprintf(dumpfile, "lastchild->child=%d ", 778 (int) (lastc ? lastc->child : -999)); 779 fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this); 780 for (i = 0; i < cur_pwtab_num; ++i) 781 fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i, 782 (long) pwtab[i].child, (long) pwtab[i].uid, 783 pwtab[i].home, pwtab[i].uname); 784 fprintf(dumpfile, "\nUserName table by plt_dump():\n"); 785 for (i = 0; i < cur_pwtab_num; ++i) 786 fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, 787 untab[i].username, (long) untab[i].uid, untab[i].home); 788 fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n", 789 untab_index("ezk"), 790 (long) untab[untab_index("ezk")].uid, 791 pwtab[untab[untab_index("ezk")].uid].home); 792 fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n", 793 untab_index("rezk"), 794 (long) untab[untab_index("rezk")].uid, 795 pwtab[untab[untab_index("rezk")].uid].home); 796 fclose(dumpfile); 797 } 798} 799#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */
| 186 if (seteuid(userid) < 0) { 187 plog(XLOG_WARNING, "could not seteuid to %d: %m", userid); 188 return linkval; 189 } 190 if (hlfsd_stat(linkval, &homestat) < 0) { 191 if (errno == ENOENT) { /* make the spool dir if possible */ 192 /* don't use recursive mkdirs here */ 193 if (mkdir(linkval, PERS_SPOOLMODE) < 0) { 194 seteuid(0); 195 plog(XLOG_WARNING, "can't make directory %s: %m", linkval); 196 return alt_spooldir; 197 } 198 /* fall through to testing the disk space / quota */ 199 } else { /* the home dir itself must not exist then */ 200 seteuid(0); 201 plog(XLOG_WARNING, "bad link to %s: %m", linkval); 202 return alt_spooldir; 203 } 204 } 205 206 /* 207 * If gets here, then either the spool dir in the home dir exists, 208 * or it was just created. In either case, we now need to 209 * test if we can create a small file and write at least one 210 * byte into it. This will test that we have both enough inodes 211 * and disk blocks to spare, or they fall within the user's quotas too. 212 * We are still seteuid to the user at this point. 213 */ 214 if (hlfsd_diskspace(linkval) < 0) { 215 seteuid(0); 216 plog(XLOG_WARNING, "no more space in %s: %m", linkval); 217 return alt_spooldir; 218 } else { 219 seteuid(0); 220 return linkval; 221 } 222} 223 224 225static int 226hlfsd_diskspace(char *path) 227{ 228 char buf[MAXPATHLEN]; 229 int fd, len; 230 231 clock_valid = 0; /* invalidate logging clock */ 232 233 sprintf(buf, "%s/._hlfstmp_%lu", path, (long) getpid()); 234 if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) { 235 plog(XLOG_ERROR, "cannot open %s: %m", buf); 236 return -1; 237 } 238 len = strlen(buf); 239 if (write(fd, buf, len) < len) { 240 plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf); 241 close(fd); 242 unlink(buf); /* cleanup just in case */ 243 return -1; 244 } 245 if (unlink(buf) < 0) { 246 plog(XLOG_ERROR, "cannot unlink %s : %m", buf); 247 } 248 close(fd); 249 return 0; 250} 251 252 253static int 254hlfsd_stat(char *path, struct stat *statp) 255{ 256 if (stat(path, statp) < 0) 257 return -1; 258 else if (!S_ISDIR(statp->st_mode)) { 259 errno = ENOTDIR; 260 return -1; 261 } 262 return 0; 263} 264 265 266static void 267delay(uid2home_t *found, int secs) 268{ 269 struct timeval tv; 270 271#ifdef DEBUG 272 if (found) 273 dlog("delaying on child %d for %d seconds", found->child, secs); 274#endif /* DEBUG */ 275 276 tv.tv_usec = 0; 277 278 do { 279 tv.tv_sec = secs; 280 if (select(0, 0, 0, 0, &tv) == 0) 281 break; 282 } while (--secs && found->child); 283} 284 285 286/* 287 * This function is called when a child has terminated after 288 * servicing an nfs request. We need to check the exit status and 289 * update the last_status field of the requesting user. 290 */ 291RETSIGTYPE 292interlock(int signum) 293{ 294 int child; 295 uid2home_t *lostchild; 296 int status; 297 298#ifdef HAVE_WAITPID 299 while ((child = waitpid((pid_t) -1, &status, WNOHANG)) > 0) { 300#else /* not HAVE_WAITPID */ 301 while ((child = wait3(&status, WNOHANG, (struct rusage *) 0)) > 0) { 302#endif /* not HAVE_WAITPID */ 303 304 /* high chances this was the last child forked */ 305 if (lastchild && lastchild->child == child) { 306 lastchild->child = 0; 307 308 if (WIFEXITED(status)) 309 lastchild->last_status = WEXITSTATUS(status); 310 lastchild = (uid2home_t *) NULL; 311 } else { 312 /* and if not, we have to search for it... */ 313 for (lostchild = pwtab; lostchild < &pwtab[cur_pwtab_num]; lostchild++) { 314 if (lostchild->child == child) { 315 if (WIFEXITED(status)) 316 lostchild->last_status = WEXITSTATUS(status); 317 lostchild->child = 0; 318 break; 319 } 320 } 321 } 322 } 323} 324 325 326/* 327 * PASSWORD AND USERNAME LOOKUP TABLES FUNCTIONS 328 */ 329 330/* 331 * get index of UserName table entry which matches username. 332 * must not return uid_t because we want to return a negative number. 333 */ 334int 335untab_index(char *username) 336{ 337 int max, min, mid, cmp; 338 339 max = cur_pwtab_num - 1; 340 min = 0; 341 342 do { 343 mid = (max + min) / 2; 344 cmp = strcmp(untab[mid].username, username); 345 if (cmp == 0) /* record found! */ 346 return mid; 347 if (cmp > 0) 348 max = mid; 349 else 350 min = mid; 351 } while (max > min + 1); 352 353 if (STREQ(untab[max].username, username)) 354 return max; 355 if (STREQ(untab[min].username, username)) 356 return min; 357 358 /* if gets here then record was not found */ 359 return -1; 360} 361 362 363/* 364 * Don't make this return a uid_t, because we need to return negative 365 * numbers as well (error codes.) 366 */ 367int 368uidof(char *username) 369{ 370 int idx; 371 372 if ((idx = untab_index(username)) < 0) /* not found */ 373 return INVALIDID; /* an invalid user id */ 374 return untab[idx].uid; 375} 376 377 378/* 379 * Don't make this return a uid_t, because we need to return negative 380 * numbers as well (error codes.) 381 */ 382char * 383homeof(char *username) 384{ 385 int idx; 386 387 if ((idx = untab_index(username)) < 0) /* not found */ 388 return (char *) NULL; /* an invalid user id */ 389 return untab[idx].home; 390} 391 392 393char * 394mailbox(int uid, char *username) 395{ 396 char *home; 397 398 if (uid < 0) 399 return (char *) NULL; /* not found */ 400 401 if ((home = homeof(username)) == (char *) NULL) 402 return (char *) NULL; 403 if (STREQ(home, "/")) 404 sprintf(mboxfile, "/%s/%s", home_subdir, username); 405 else 406 sprintf(mboxfile, "%s/%s/%s", home, home_subdir, username); 407 return mboxfile; 408} 409 410 411static int 412plt_compare_fxn(const voidp x, const voidp y) 413 414{ 415 uid2home_t *i = (uid2home_t *) x; 416 uid2home_t *j = (uid2home_t *) y; 417 418 return i->uid - j->uid; 419} 420 421 422static int 423unt_compare_fxn(const voidp x, const voidp y) 424{ 425 username2uid_t *i = (username2uid_t *) x; 426 username2uid_t *j = (username2uid_t *) y; 427 428 return strcmp(i->username, j->username); 429} 430 431 432/* perform initialization of user passwd database */ 433static void 434hlfsd_setpwent(void) 435{ 436 if (!passwdfile) { 437 setpwent(); 438 return; 439 } 440 441 passwd_fp = fopen(passwdfile, "r"); 442 if (!passwd_fp) { 443 plog(XLOG_ERROR, "unable to read passwd file %s: %m", passwdfile); 444 return; 445 } 446 plog(XLOG_INFO, "reading password entries from file %s", passwdfile); 447 448 passwd_line = 0; 449 memset((char *) &passwd_ent, 0, sizeof(struct passwd)); 450 passwd_ent.pw_name = (char *) &pw_name; 451 passwd_ent.pw_dir = (char *) &pw_dir; 452} 453 454 455/* perform de-initialization of user passwd database */ 456static void 457hlfsd_endpwent(void) 458{ 459 if (!passwdfile) { 460 /* 461 * Don't actually run this because we will be making more passwd calls 462 * afterwards. On Solaris 2.5.1, making getpwent() calls after calling 463 * endpwent() results in a memory leak! (and no, even Purify didn't 464 * detect it...) 465 * 466 endpwent(); 467 */ 468 return; 469 } 470 471 if (passwd_fp) { 472 fclose(passwd_fp); 473 } 474} 475 476 477/* perform record reading/parsing of individual passwd database records */ 478static struct passwd * 479hlfsd_getpwent(void) 480{ 481 char buf[256], *cp; 482 483 /* check if to perform standard unix function */ 484 if (!passwdfile) { 485 return getpwent(); 486 } 487 488 clock_valid = 0; /* invalidate logging clock */ 489 490 /* return here to read another entry */ 491readent: 492 493 /* return NULL if reached end of file */ 494 if (feof(passwd_fp)) 495 return NULL; 496 497 pw_name[0] = pw_dir[0] = '\0'; 498 499 /* read records */ 500 buf[0] = '\0'; 501 fgets(buf, 256, passwd_fp); 502 passwd_line++; 503 if (!buf || buf[0] == '\0') 504 goto readent; 505 506 /* read user name */ 507 cp = strtok(buf, ":"); 508 if (!cp || cp[0] == '\0') { 509 plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile); 510 goto readent; 511 } 512 strcpy(pw_name, cp); /* will show up in passwd_ent.pw_name */ 513 514 /* skip passwd */ 515 strtok(NULL, ":"); 516 517 /* read uid */ 518 cp = strtok(NULL, ":"); 519 if (!cp || cp[0] == '\0') { 520 plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile); 521 goto readent; 522 } 523 passwd_ent.pw_uid = atoi(cp); 524 525 /* skip gid and gcos */ 526 strtok(NULL, ":"); 527 strtok(NULL, ":"); 528 529 /* read home dir */ 530 cp = strtok(NULL, ":"); 531 if (!cp || cp[0] == '\0') { 532 plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line, passwdfile); 533 goto readent; 534 } 535 strcpy(pw_dir, cp); /* will show up in passwd_ent.pw_dir */ 536 537 /* the rest of the fields are unimportant and not being considered */ 538 539 plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%d, dir=%s", 540 passwd_ent.pw_name, passwd_ent.pw_uid, passwd_ent.pw_dir); 541 542 return &passwd_ent; 543} 544 545 546/* 547 * read and hash the passwd file or NIS map 548 */ 549void 550plt_init(void) 551{ 552 struct passwd *pent_p; 553 554 if (plt_reset() < 0) /* could not reset table. skip. */ 555 return; 556 557 plog(XLOG_INFO, "reading password map"); 558 559 hlfsd_setpwent(); /* prepare to read passwd entries */ 560 while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) { 561 table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name); 562 } 563 hlfsd_endpwent(); 564 565 qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t), 566 plt_compare_fxn); 567 qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t), 568 unt_compare_fxn); 569 570 plog(XLOG_INFO, "password map read and sorted"); 571} 572 573 574/* 575 * This is essentially so that we don't reset known good lookup tables when a 576 * YP server goes down. 577 */ 578static int 579plt_reset(void) 580{ 581 int i; 582 583 clock_valid = 0; /* invalidate logging clock */ 584 585 hlfsd_setpwent(); 586 if (hlfsd_getpwent() == (struct passwd *) NULL) { 587 hlfsd_endpwent(); 588 return -1; /* did not reset table */ 589 } 590 hlfsd_endpwent(); 591 592 lastchild = (uid2home_t *) NULL; 593 594 if (max_pwtab_num > 0) /* was used already. cleanup old table */ 595 for (i = 0; i < cur_pwtab_num; ++i) { 596 if (pwtab[i].home) { 597 XFREE(pwtab[i].home); 598 pwtab[i].home = (char *) NULL; 599 } 600 pwtab[i].uid = INVALIDID; /* not a valid uid (yet...) */ 601 pwtab[i].child = (pid_t) 0; 602 pwtab[i].uname = (char *) NULL; /* only a ptr to untab[i].username */ 603 if (untab[i].username) { 604 XFREE(untab[i].username); 605 untab[i].username = (char *) NULL; 606 } 607 untab[i].uid = INVALIDID; /* invalid uid */ 608 untab[i].home = (char *) NULL; /* only a ptr to pwtab[i].home */ 609 } 610 cur_pwtab_num = 0; /* zero current size */ 611 612 return 0; /* resetting ok */ 613} 614 615 616/* 617 * u: uid number 618 * h: home directory 619 * n: user ID name 620 */ 621static void 622table_add(int u, const char *h, const char *n) 623{ 624 int i; 625 626 clock_valid = 0; /* invalidate logging clock */ 627 628 if (max_pwtab_num <= 0) { /* was never initialized */ 629 max_pwtab_num = 1; 630 pwtab = (uid2home_t *) xmalloc(max_pwtab_num * 631 sizeof(uid2home_t)); 632 memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t)); 633 untab = (username2uid_t *) xmalloc(max_pwtab_num * 634 sizeof(username2uid_t)); 635 memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t)); 636 } 637 638 /* check if need more space. */ 639 if (cur_pwtab_num + 1 > max_pwtab_num) { 640 /* need more space in table */ 641 max_pwtab_num *= 2; 642 plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num); 643 pwtab = (uid2home_t *) xrealloc(pwtab, 644 sizeof(uid2home_t) * max_pwtab_num); 645 untab = (username2uid_t *) xrealloc(untab, 646 sizeof(username2uid_t) * 647 max_pwtab_num); 648 /* zero out newly added entries */ 649 for (i=cur_pwtab_num; i<max_pwtab_num; ++i) { 650 memset((char *) &pwtab[i], 0, sizeof(uid2home_t)); 651 memset((char *) &untab[i], 0, sizeof(username2uid_t)); 652 } 653 } 654 655 /* do NOT add duplicate entries (this is an O(N^2) algorithm... */ 656 for (i=0; i<cur_pwtab_num; ++i) 657 if (u == pwtab[i].uid && u != 0 ) { 658#ifdef DEBUG 659 dlog("ignoring duplicate home %s for uid %d (already %s)", 660 h, u, pwtab[i].home); 661#endif /* DEBUG */ 662 return; 663 } 664 665 /* add new password entry */ 666 pwtab[cur_pwtab_num].home = strdup(h); 667 pwtab[cur_pwtab_num].child = 0; 668 pwtab[cur_pwtab_num].last_access_time = 0; 669 pwtab[cur_pwtab_num].last_status = 0; /* assume best: used homedir */ 670 pwtab[cur_pwtab_num].uid = u; 671 672 /* add new userhome entry */ 673 untab[cur_pwtab_num].username = strdup(n); 674 675 /* just a second pointer */ 676 pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username; 677 untab[cur_pwtab_num].uid = u; 678 untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home; /* a ptr */ 679 680 /* increment counter */ 681 ++cur_pwtab_num; 682} 683 684 685/* 686 * return entry in lookup table 687 */ 688uid2home_t * 689plt_search(int u) 690{ 691 int max, min, mid; 692 693 /* 694 * empty table should not happen, 695 * but I have a bug with signals to trace... 696 */ 697 if (pwtab == (uid2home_t *) NULL) 698 return (uid2home_t *) NULL; 699 700 max = cur_pwtab_num - 1; 701 min = 0; 702 703 do { 704 mid = (max + min) / 2; 705 if (pwtab[mid].uid == u) /* record found! */ 706 return &pwtab[mid]; 707 if (pwtab[mid].uid > u) 708 max = mid; 709 else 710 min = mid; 711 } while (max > min + 1); 712 713 if (pwtab[max].uid == u) 714 return &pwtab[max]; 715 if (pwtab[min].uid == u) 716 return &pwtab[min]; 717 718 /* if gets here then record was not found */ 719 return (uid2home_t *) NULL; 720} 721 722 723#if defined(DEBUG) || defined(DEBUG_PRINT) 724void 725plt_print(int signum) 726{ 727 FILE *dumpfile; 728 int dumpfd; 729 char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX"; 730 int i; 731 732#ifdef HAVE_MKSTEMP 733 dumpfd = mkstemp(dumptmp); 734#else /* not HAVE_MKSTEMP */ 735 mktemp(dumptmp); 736 if (!dumptmp) { 737 plot(XLOG_ERROR, "cannot create temporary dump file"); 738 return; 739 } 740 dumpfd = open(dumptmp, O_RDONLY); 741#endif /* not HAVE_MKSTEMP */ 742 if (dumpfd < 0) { 743 plog(XLOG_ERROR, "cannot open temporary dump file"); 744 return; 745 } 746 if ((dumpfile = fdopen(dumpfd, "a")) != NULL) { 747 plog(XLOG_INFO, "dumping internal state to file %s", dumptmp); 748 fprintf(dumpfile, "\n\nNew plt_dump():\n"); 749 for (i = 0; i < cur_pwtab_num; ++i) 750 fprintf(dumpfile, 751 "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n", 752 i, 753 (long) pwtab[i].child, 754 pwtab[i].last_access_time, 755 pwtab[i].last_status, 756 (long) pwtab[i].uid, 757 pwtab[i].home, 758 pwtab[i].uname); 759 fprintf(dumpfile, "\nUserName table by plt_print():\n"); 760 for (i = 0; i < cur_pwtab_num; ++i) 761 fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, 762 untab[i].username, (long) untab[i].uid, untab[i].home); 763 close(dumpfd); 764 fclose(dumpfile); 765 } 766} 767 768 769void 770plt_dump(uid2home_t *lastc, pid_t this) 771{ 772 FILE *dumpfile; 773 int i; 774 775 if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) { 776 fprintf(dumpfile, "\n\nNEW PLT_DUMP -- "); 777 fprintf(dumpfile, "lastchild->child=%d ", 778 (int) (lastc ? lastc->child : -999)); 779 fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this); 780 for (i = 0; i < cur_pwtab_num; ++i) 781 fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i, 782 (long) pwtab[i].child, (long) pwtab[i].uid, 783 pwtab[i].home, pwtab[i].uname); 784 fprintf(dumpfile, "\nUserName table by plt_dump():\n"); 785 for (i = 0; i < cur_pwtab_num; ++i) 786 fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, 787 untab[i].username, (long) untab[i].uid, untab[i].home); 788 fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n", 789 untab_index("ezk"), 790 (long) untab[untab_index("ezk")].uid, 791 pwtab[untab[untab_index("ezk")].uid].home); 792 fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n", 793 untab_index("rezk"), 794 (long) untab[untab_index("rezk")].uid, 795 pwtab[untab[untab_index("rezk")].uid].home); 796 fclose(dumpfile); 797 } 798} 799#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */
|