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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 26 /* All Rights Reserved */ 27 28 /* 29 * logins.c 30 * 31 * This file contains the source for the administrative command 32 * "logins" (available to the administrator) that produces a report 33 * containing login-IDs and other requested information. 34 */ 35 36 #include <sys/types.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <string.h> 41 #include <ctype.h> 42 #include <grp.h> 43 #include <pwd.h> 44 #include <shadow.h> 45 #include <time.h> 46 #include <stdarg.h> 47 #include <fmtmsg.h> 48 #include <locale.h> 49 50 /* 51 * Local constant definitions 52 * TRUE Boolean constant 53 * FALSE Boolean constant 54 * USAGE_MSG Message used to display a usage error 55 * MAXLOGINSIZE Maximum length of a valid login-ID 56 * MAXSYSTEMLOGIN Maximum value of a system user-ID. 57 * OPTSTR Options to this command 58 * ROOT_ID The user-ID of an administrator 59 */ 60 61 #ifndef FALSE 62 #define FALSE 0 63 #endif 64 65 #ifndef TRUE 66 #define TRUE ((int)'t') 67 #endif 68 69 #define USAGE_MSG "usage: logins [-admopstux] [-g groups] [-l logins]" 70 #define MAXLOGINSIZE 14 71 #define MAXSYSTEMLOGIN 99 72 #define OPTSTR "adg:l:mopstux" 73 #define ROOT_ID 0 74 75 /* 76 * The following macros do their function for now but will probably have 77 * to be replaced by functions sometime in the near future. The maximum 78 * system login value may someday be administerable, in which case these 79 * will have to be changed to become functions 80 * 81 * isasystemlogin Returns TRUE if the user-ID in the "struct passwd" 82 * structure referenced by the function's argument is 83 * less than or equal to the maximum value for a system 84 * user-ID, FALSE otherwise. 85 * isauserlogin Returns TRUE if the user-ID in the "struct passwd" 86 * structure referenced by the function's argument is 87 * greater than the maximum value for a system user-ID, 88 * FALSE otherwise. 89 */ 90 91 #define isauserlogin(pw) (pw->pw_uid > MAXSYSTEMLOGIN) 92 #define isasystemlogin(pw) (pw->pw_uid <= MAXSYSTEMLOGIN) 93 94 95 /* 96 * Local datatype definitions 97 * struct reqgrp Describes a group as requested through the 98 * -g option 99 * struct reqlogin Describes a login-ID as requested through 100 * the -l option 101 * struct pwdinfo Describes a password's aging information, 102 * as extracted from /etc/shadow 103 * struct secgrp Describes a login-ID's secondary group 104 */ 105 106 /* Describes a specified group name (from the -g groups option) */ 107 struct reqgrp { 108 char *groupname; /* Requested group name */ 109 struct reqgrp *next; /* Next item in the list */ 110 gid_t groupID; /* Group's ID */ 111 }; 112 113 /* Describes a specified login name (from the -l logins option) */ 114 struct reqlogin { 115 char *loginname; /* Requested login name */ 116 struct reqlogin *next; /* Next item in the list */ 117 int found; /* TRUE if login in /etc/passwd */ 118 }; 119 120 /* 121 * This structure describes a password's information 122 */ 123 124 struct pwdinfo { 125 long datechg; /* Date the password was changed (mmddyy) */ 126 char *passwdstatus; /* Password status */ 127 long mindaystilchg; /* Min days b4 pwd can change again */ 128 long maxdaystilchg; /* Max days b4 pwd can change again */ 129 long warninterval; /* Days before expire to warn user */ 130 long inactive; /* Lapsed days of inactivity before lock */ 131 long expdate; /* Date of expiration (mmddyy) */ 132 }; 133 134 /* This structure describes secondary groups that a user belongs to */ 135 struct secgrp { 136 char *groupname; /* Name of the group */ 137 struct secgrp *next; /* Next item in the list */ 138 gid_t groupID; /* Group-ID */ 139 }; 140 141 142 /* 143 * These functions handle error and warning message writing. 144 * (This deals with UNIX(r) standard message generation, so 145 * the rest of the code doesn't have to.) 146 * 147 * Functions included: 148 * initmsg Initialize the message handling functions. 149 * wrtmsg Write the message using fmtmsg(). 150 * 151 * Static data included: 152 * fcnlbl The label for standard messages 153 * msgbuf A buffer to contain the edited message 154 */ 155 156 static char fcnlbl[MM_MXLABELLN+1]; /* Buffer for message label */ 157 static char msgbuf[MM_MXTXTLN+1]; /* Buffer for message text */ 158 159 160 /* 161 * void initmsg(p) 162 * 163 * This function initializes the message handling functions. 164 * 165 * Arguments: 166 * p A pointer to a character string that is the name of the 167 * function, used to generate the label on messages. If this 168 * string contains a slash ('/'), it only uses the characters 169 * beyond the last slash in the string (this permits argv[0] 170 * to be used). 171 * 172 * Returns: Void 173 */ 174 175 static void 176 initmsg(char *p) 177 { 178 char *q; /* Local multi-use pointer */ 179 180 /* Use only the simple filename if there is a slash in the name */ 181 if (!(q = strrchr(p, '/'))) { 182 q = p; 183 } else { 184 q++; 185 } 186 187 /* Build the label for messages */ 188 (void) snprintf(fcnlbl, MM_MXLABELLN, "UX:%s", q); 189 190 /* Restrict messages to the text-component */ 191 (void) putenv("MSGVERB=text"); 192 } 193 194 195 /* 196 * void wrtmsg(severity, action, tag, text[, txtarg1[, txtarg2[, ...]]]) 197 * 198 * This function writes a message using fmtmsg() 199 * 200 * Arguments: 201 * severity The severity-component of the message 202 * action The action-string used to generate the 203 * action-component of the message 204 * tag Tag-component of the message 205 * text The text-string used to generate the text- 206 * component of the message 207 * txtarg Arguments to be inserted into the "text" 208 * string using vsprintf() 209 * 210 * Returns: Void 211 */ 212 /*PRINTFLIKE4*/ 213 static void 214 wrtmsg(int severity, char *action, char *tag, char *text, ...) 215 { 216 int errorflg; /* TRUE if problem generating message */ 217 va_list argp; /* Pointer into vararg list */ 218 219 220 /* No problems yet */ 221 errorflg = FALSE; 222 223 /* Generate the error message */ 224 va_start(argp, text); 225 if (text != MM_NULLTXT) { 226 errorflg = vsnprintf(msgbuf, 227 MM_MXTXTLN, text, argp) > MM_MXTXTLN; 228 } 229 (void) fmtmsg(MM_PRINT, fcnlbl, severity, 230 (text == MM_NULLTXT) ? MM_NULLTXT : msgbuf, action, tag); 231 va_end(argp); 232 233 /* 234 * If there was a buffer overflow generating the error message, 235 * write a message and quit (things are probably corrupt in the 236 * static data space now 237 */ 238 if (errorflg) { 239 (void) fmtmsg(MM_PRINT, fcnlbl, MM_WARNING, 240 gettext("Internal message buffer overflow"), 241 MM_NULLACT, MM_NULLTAG); 242 exit(100); 243 } 244 } 245 246 /* 247 * These functions control the group membership list, as found in 248 * the /etc/group file. 249 * 250 * Functions included: 251 * addmember Adds a member to the membership list 252 * isamember Looks for a particular login-ID in the 253 * list of members 254 * 255 * Datatype Definitions: 256 * struct grpmember Describes a group member 257 * 258 * Static Data: 259 * membershead Pointer to the head of the list of 260 * group members 261 */ 262 263 struct grpmember { 264 char *membername; 265 struct grpmember *next; 266 }; 267 268 static struct grpmember *membershead; 269 270 /* 271 * void addmember(p) 272 * char *p 273 * 274 * This function adds a member to the group member's list. The 275 * group members list is a list of structures containing a pointer 276 * to the member-name and a pointer to the next item in the 277 * structure. The structure is not ordered in any particular way. 278 * 279 * Arguments: 280 * p Pointer to the member name 281 * 282 * Returns: Void 283 */ 284 285 static void 286 addmember(char *p) 287 { 288 struct grpmember *new; /* Member being added */ 289 290 new = malloc(sizeof (struct grpmember)); 291 new->membername = strdup(p); 292 new->next = membershead; 293 membershead = new; 294 } 295 296 297 /* 298 * init isamember(p) 299 * char *p 300 * 301 * This function examines the list of group-members for the string 302 * referenced by 'p'. If 'p' is a member of the members list, the 303 * function returns TRUE. Otherwise it returns FALSE. 304 * 305 * Arguments: 306 * p Pointer to the name to search for. 307 * 308 * Returns: int 309 * TRUE If 'p' is found in the members list, 310 * FALSE otherwise 311 */ 312 313 static int 314 isamember(char *p) 315 { 316 int found; /* TRUE if login found in list */ 317 struct grpmember *pmem; /* Group member being examined */ 318 319 320 /* Search the membership list for 'p' */ 321 found = FALSE; 322 for (pmem = membershead; !found && pmem; pmem = pmem->next) { 323 if (strcmp(p, pmem->membername) == 0) 324 found = TRUE; 325 } 326 327 return (found); 328 } 329 330 331 /* 332 * These functions handle the display list. The display list contains 333 * all of the information we're to display. The list contains a pointer 334 * to the login-name, a pointer to the free-field (comment), and a 335 * pointer to the next item in the list. The list is ordered alpha- 336 * betically (ascending) on the login-name field. The list initially 337 * contains a dummy field (to make insertion easier) that contains a 338 * login-name of "". 339 * 340 * Functions included: 341 * initdisp Initializes the display list 342 * adddisp Adds information to the display list 343 * isuidindisp Looks to see if a particular user-ID is in the 344 * display list 345 * genreport Generates a report from the items in the display 346 * list 347 * applygroup Add group information to the items in the display 348 * list 349 * applypasswd Add extended password information to the items 350 * in the display list 351 * 352 * Datatypes Defined: 353 * struct display Describes the structure that contains the information 354 * to be displayed. Includes pointers to the login-ID, 355 * free-field (comment), and the next structure in the 356 * list. 357 * 358 * Static Data: 359 * displayhead Pointer to the head of the display list. Initially 360 * references the null-item on the head of the list. 361 */ 362 363 struct display { 364 char *loginID; /* Login name */ 365 char *freefield; /* Free (comment) field */ 366 char *groupname; /* Name of the primary group */ 367 char *iwd; /* Initial working directory */ 368 char *shell; /* Shell after login (may be null) */ 369 struct pwdinfo *passwdinfo; /* Password information structure */ 370 struct secgrp *secgrplist; /* Head of the secondary group list */ 371 uid_t userID; /* User ID */ 372 gid_t groupID; /* Group ID of primary group */ 373 struct display *nextlogin; /* Next login in the list */ 374 struct display *nextuid; /* Next user-ID in the list */ 375 }; 376 377 static struct display *displayhead; 378 379 380 /* 381 * void initdisp() 382 * 383 * Initializes the display list. An empty display list contains 384 * a single element, the dummy element. 385 * 386 * Arguments: None 387 * 388 * Returns: Void 389 */ 390 391 static void 392 initdisp(void) 393 { 394 displayhead = malloc(sizeof (struct display)); 395 displayhead->nextlogin = NULL; 396 displayhead->nextuid = NULL; 397 displayhead->loginID = ""; 398 displayhead->freefield = ""; 399 displayhead->userID = (uid_t)-1; 400 } 401 402 403 /* 404 * void adddisp(pwent) 405 * struct passwd *pwent 406 * 407 * This function adds the appropriate information from the login 408 * description referenced by 'pwent' to the list if information 409 * to be displayed. It only adds the information if the login-ID 410 * (user-name) is unique. It inserts the information in the list 411 * in such a way that the list remains ordered alphabetically 412 * (ascending) according to the login-ID (user-name). 413 * 414 * Arguments: 415 * pwent Structure that contains all of the login information 416 * of the login being added to the list. The only 417 * information that this function uses is the login-ID 418 * (user-name) and the free-field (comment field). 419 * 420 * Returns: Void 421 */ 422 423 static void 424 adddisp(struct passwd *pwent) 425 { 426 struct display *new; /* Item being added to the list */ 427 struct display *prev; /* Previous item in the list */ 428 struct display *current; /* Next item in the list */ 429 int found; /* FLAG, insertion point found */ 430 int compare = 1; /* strcmp() compare value */ 431 432 433 /* Find where this value belongs in the list */ 434 prev = displayhead; 435 found = FALSE; 436 while (!found && (current = prev->nextlogin)) { 437 if ((compare = strcmp(current->loginID, pwent->pw_name)) >= 0) { 438 found = TRUE; 439 } else { 440 prev = current; 441 } 442 443 } 444 /* Insert this value in the list, only if it is unique though */ 445 if (compare != 0) { 446 new = malloc(sizeof (struct display)); 447 new->loginID = strdup(pwent->pw_name); 448 if (pwent->pw_comment && pwent->pw_comment[0] != '\0') { 449 new->freefield = strdup(pwent->pw_comment); 450 } else { 451 new->freefield = strdup(pwent->pw_gecos); 452 } 453 if (!pwent->pw_shell || !(*pwent->pw_shell)) { 454 new->shell = "/sbin/sh"; 455 } else { 456 new->shell = strdup(pwent->pw_shell); 457 } 458 new->iwd = strdup(pwent->pw_dir); 459 new->userID = pwent->pw_uid; 460 new->groupID = pwent->pw_gid; 461 new->secgrplist = NULL; 462 new->passwdinfo = NULL; 463 new->groupname = NULL; 464 465 /* Add new display item to the list ordered by login-ID */ 466 new->nextlogin = current; 467 prev->nextlogin = new; 468 469 /* 470 * Find the appropriate place for the new item in the list 471 * ordered by userID 472 */ 473 prev = displayhead; 474 found = FALSE; 475 while (!found && (current = prev->nextuid)) { 476 if (current->userID > pwent->pw_uid) { 477 found = TRUE; 478 } else { 479 prev = current; 480 } 481 } 482 483 /* Add the item into the list that is ordered by user-ID */ 484 new->nextuid = current; 485 prev->nextuid = new; 486 } 487 } 488 489 490 /* 491 * int isuidindisp(pwent) 492 * struct passwd *pwent 493 * 494 * This function examines the display list to see if the uid in 495 * the (struct passwd) referenced by "pwent" is already in the 496 * display list. It returns TRUE if it is in the list, FALSE 497 * otherwise. 498 * 499 * Since the display list is ordered by user-ID, the search continues 500 * until a match is found or a user-ID is found that is larger than 501 * the one we're searching for. 502 * 503 * Arguments: 504 * pwent Structure that contains the user-ID we're to 505 * look for 506 * 507 * Returns: int 508 * TRUE if the user-ID was found, FALSE otherwise. 509 */ 510 511 static int 512 isuidindisp(struct passwd *pwent) 513 { 514 struct display *dp; 515 516 517 /* 518 * Search the list, beginning at the beginning (where else?) 519 * and stopping when the user-ID is found or one is found that 520 * is greater than the user-ID we're searching for. Recall 521 * that this list is ordered by user-ID 522 */ 523 524 for (dp = displayhead->nextuid; dp && (dp->userID < pwent->pw_uid); 525 dp = dp->nextuid) { 526 continue; 527 } 528 529 /* 530 * If the pointer "dp" points to a structure that has a 531 * matching user-ID, return TRUE. Otherwise FALSE 532 */ 533 return (dp && (dp->userID == pwent->pw_uid)); 534 } 535 536 537 /* 538 * void applygroup(allgroups) 539 * int allgroups 540 * 541 * This function applies group information to the login-IDs in the 542 * display list. It always applies the primary group information. 543 * If "allgroups" is TRUE, it applies secondary information as well. 544 * 545 * Arguments: 546 * allgroups FLAG. TRUE if secondary group info is to be 547 * applied -- FALSE otherwise. 548 * 549 * Returns: void 550 */ 551 552 static void 553 applygroup(int allgroups) 554 { 555 struct display *dp; /* Display list running ptr */ 556 struct group *grent; /* Group info, from getgrent() */ 557 char *p; /* Temp char pointer */ 558 char **pp; /* Temp char * pointer */ 559 int compare; /* Value from strcmp() */ 560 int done; /* TRUE if finished, FALSE otherwise */ 561 struct secgrp *psecgrp; /* Block allocated for this info */ 562 struct secgrp *psrch; /* Secondary group -- for searching */ 563 564 if (!allgroups) { 565 /* short circute getting all the groups */ 566 for (dp = displayhead->nextuid; dp; dp = dp->nextuid) { 567 if ((grent = getgrgid(dp->groupID)) != NULL) { 568 dp->groupname = strdup(grent->gr_name); 569 } 570 } 571 return; 572 } 573 574 /* For each group-ID in the /etc/group file ... */ 575 while (grent = getgrent()) { 576 /* 577 * Set the primary group for the login-IDs in the display 578 * list. For each group-ID we get, leaf through the display 579 * list and set the group-name if the group-IDs match 580 */ 581 582 p = NULL; 583 for (dp = displayhead->nextuid; dp; dp = dp->nextuid) { 584 if ((dp->groupID == grent->gr_gid) && !dp->groupname) { 585 if (!p) { 586 p = strdup(grent->gr_name); 587 } 588 dp->groupname = p; 589 } 590 } 591 592 /* 593 * If we're to be displaying secondary group membership, 594 * leaf through the list of group members. Then, attempt 595 * to find that member in the display list. When found, 596 * attach secondary group info to the user. 597 */ 598 599 for (pp = grent->gr_mem; *pp; pp++) { 600 done = FALSE; 601 for (dp = displayhead->nextlogin; !done && dp; 602 dp = dp->nextlogin) { 603 if (((compare = strcmp(dp->loginID, 604 *pp)) == 0) && 605 !(grent->gr_gid == dp->groupID)) { 606 if (!p) { 607 p = strdup(grent->gr_name); 608 } 609 psecgrp = malloc( 610 sizeof (struct secgrp)); 611 psecgrp->groupID = grent->gr_gid; 612 psecgrp->groupname = p; 613 psecgrp->next = NULL; 614 if (!dp->secgrplist) { 615 dp->secgrplist = psecgrp; 616 } else { 617 for (psrch = dp->secgrplist; 618 psrch->next; 619 psrch = psrch->next) { 620 continue; 621 } 622 psrch->next = psecgrp; 623 } 624 done = TRUE; 625 } else if (compare > 0) { 626 done = TRUE; 627 } 628 } 629 } 630 } 631 632 /* Close the /etc/group file */ 633 endgrent(); 634 } 635 636 637 /* 638 * void applypasswd() 639 * 640 * This function applies extended password information to an item 641 * to be displayed. It allocates space for a structure describing 642 * the password, then fills in that structure from the information 643 * in the /etc/shadow file. 644 * 645 * Arguments: None 646 * 647 * Returns: Void 648 */ 649 650 static void 651 applypasswd(void) 652 { 653 struct pwdinfo *ppasswd; /* Ptr to pwd desc in current element */ 654 struct display *dp; /* Ptr to current element */ 655 struct spwd *psp; /* Pointer to a shadow-file entry */ 656 struct tm *ptm; /* Pointer to a time-of-day structure */ 657 time_t pwchg; /* System-time of last pwd chg */ 658 time_t pwexp; /* System-time of password expiration */ 659 660 661 /* Make sure the shadow file is rewound */ 662 setspent(); 663 664 665 /* 666 * For each item in the display list... 667 */ 668 669 for (dp = displayhead->nextuid; dp; dp = dp->nextuid) { 670 671 /* Allocate structure space for the password description */ 672 ppasswd = malloc(sizeof (struct pwdinfo)); 673 dp->passwdinfo = ppasswd; 674 675 /* 676 * If there's no entry in the /etc/shadow file, assume 677 * that the login is locked 678 */ 679 680 if ((psp = getspnam(dp->loginID)) == NULL) { 681 pwchg = 0L; /* Epoch */ 682 ppasswd->passwdstatus = "LK"; /* LK, Locked */ 683 ppasswd->mindaystilchg = 0L; 684 ppasswd->maxdaystilchg = 0L; 685 ppasswd->warninterval = 0L; 686 ppasswd->inactive = 0L; 687 pwexp = 0L; 688 } else { 689 /* 690 * Otherwise, fill in the password information from the 691 * info in the shadow entry 692 */ 693 if (psp->sp_pwdp == NULL || (*psp->sp_pwdp) == '\0') 694 ppasswd->passwdstatus = "NP"; 695 else if (strncmp(psp->sp_pwdp, LOCKSTRING, 696 sizeof (LOCKSTRING)-1) == 0) 697 ppasswd->passwdstatus = "LK"; 698 else if (strncmp(psp->sp_pwdp, NOLOGINSTRING, 699 sizeof (NOLOGINSTRING)-1) == 0) 700 ppasswd->passwdstatus = "NL"; 701 else if ((strlen(psp->sp_pwdp) == 13 && 702 psp->sp_pwdp[0] != '$') || 703 psp->sp_pwdp[0] == '$') 704 ppasswd->passwdstatus = "PS"; 705 else 706 ppasswd->passwdstatus = "UN"; 707 /* 708 * Set up the last-changed date, 709 * the minimum days between changes, 710 * the maximum life of a password, 711 * the interval before expiration that the user 712 * is warned, 713 * the number of days a login can be inactive before 714 * it expires, and the login expiration date 715 */ 716 717 pwchg = psp->sp_lstchg; 718 ppasswd->mindaystilchg = psp->sp_min; 719 ppasswd->maxdaystilchg = psp->sp_max; 720 ppasswd->warninterval = psp->sp_warn; 721 ppasswd->inactive = psp->sp_inact; 722 pwexp = psp->sp_expire; 723 } 724 725 /* 726 * Convert the date of the last password change from days- 727 * since-epoch to mmddyy (integer) form. Involves the 728 * intermediate step of converting the date from days- 729 * since-epoch to seconds-since-epoch. We'll set this to 730 * somewhere near the middle of the day, since there are not 731 * always 24*60*60 seconds in a year. (Yeech) 732 * 733 * Note: The form mmddyy should probably be subject to 734 * internationalization -- Non-Americans will think that 735 * 070888 is 07 August 88 when the software is trying to say 736 * 08 July 88. Systems Engineers seem to think that this isn't 737 * a problem though... 738 */ 739 740 if (pwchg != -1L) { 741 pwchg = (pwchg * DAY) + (DAY/2); 742 ptm = localtime(&pwchg); 743 ppasswd->datechg = ((long)(ptm->tm_mon+1) * 10000L) + 744 (long)((ptm->tm_mday * 100) + 745 (ptm->tm_year % 100)); 746 } else { 747 ppasswd->datechg = 0L; 748 } 749 750 /* 751 * Convert the passwd expiration date from days-since-epoch 752 * to mmddyy (long integer) form using the same algorithm and 753 * comments as above. 754 */ 755 756 if (pwexp != -1L) { 757 pwexp = (pwexp * DAY) + (DAY/2); 758 ptm = localtime(&pwexp); 759 ppasswd->expdate = ((long)(ptm->tm_mon+1) * 10000L) + 760 (long)((ptm->tm_mday * 100) + 761 (ptm->tm_year % 100)); 762 } else { 763 ppasswd->expdate = 0L; 764 } 765 } 766 767 /* Close the shadow password file */ 768 endspent(); 769 } 770 771 772 /* 773 * int hasnopasswd(pwent) 774 * struct passwd *pwent 775 * 776 * This function examines a login's password-file entry 777 * and, if necessary, its shadow password-file entry and 778 * returns TRUE if that user-ID has no password, meaning 779 * that the user-ID can be used to log into the system 780 * without giving a password. The function returns FALSE 781 * otherwise. 782 * 783 * Arguments: 784 * pwent Login to examine. 785 * 786 * Returns: int 787 * TRUE if the login can be used without a password, FALSE 788 * otherwise. 789 */ 790 791 static int 792 hasnopasswd(struct passwd *pwent) 793 { 794 struct spwd *psp; /* /etc/shadow file struct */ 795 int nopwflag; /* TRUE if login has no passwd */ 796 797 /* 798 * A login has no password if: 799 * 1. There exists an entry for that login in the 800 * shadow password-file (/etc/passwd), and 801 * 2. The encrypted password in the structure describing 802 * that entry is either: NULL or a null string ("") 803 */ 804 805 /* Get the login's entry in the shadow password file */ 806 if (psp = getspnam(pwent->pw_name)) { 807 808 /* Look at the encrypted password in that entry */ 809 if (psp->sp_pwdp == (char *)0 || 810 *psp->sp_pwdp == '\0') { 811 nopwflag = TRUE; 812 } else { 813 nopwflag = FALSE; 814 } 815 } else { 816 nopwflag = FALSE; 817 } 818 819 /* Done ... */ 820 return (nopwflag); 821 } 822 823 824 /* 825 * void writeunformatted(current, xtndflag, expflag) 826 * struct display *current 827 * int xtndflag 828 * int expflag 829 * 830 * This function writes the data in the display structure "current" 831 * to the standard output file. It writes the information in the 832 * form of a colon-list. It writes secondary group information if 833 * that information is in the structure, it writes extended 834 * (initial working directory, shell, and password-aging) info 835 * if the "xtndflag" is TRUE, and it writes password expiration 836 * information if "expflag" is TRUE. 837 * 838 * Arguments: 839 * current Structure containing information to write. 840 * xtndflag TRUE if extended information is to be written, 841 * FALSE otherwise 842 * expflag TRUE if password expiration information is to 843 * be written, FALSE otherwise 844 * 845 * Returns: void 846 */ 847 848 static void 849 writeunformatted(struct display *current, int xtndflag, int expflag) 850 { 851 struct secgrp *psecgrp; /* Secondary group info */ 852 struct pwdinfo *pwdinfo; /* Password aging info */ 853 854 /* Write the general information */ 855 (void) fprintf(stdout, "%s:%u:%s:%u:%s", 856 current->loginID, 857 current->userID, 858 current->groupname == NULL ? "" : current->groupname, 859 current->groupID, 860 current->freefield); 861 862 /* 863 * If the group information is there, write it (it's only 864 * there if it's supposed to be written) 865 */ 866 for (psecgrp = current->secgrplist; psecgrp; psecgrp = psecgrp->next) { 867 (void) fprintf(stdout, ":%s:%u", 868 psecgrp->groupname, psecgrp->groupID); 869 } 870 871 /* If the extended info flag is TRUE, write the extended information */ 872 if (xtndflag) { 873 pwdinfo = current->passwdinfo; 874 (void) fprintf(stdout, ":%s:%s:%s:%6.6ld:%ld:%ld:%ld", 875 current->iwd, current->shell, 876 pwdinfo->passwdstatus, 877 pwdinfo->datechg, 878 pwdinfo->mindaystilchg, pwdinfo->maxdaystilchg, 879 pwdinfo->warninterval); 880 } 881 882 /* If the password expiration information is requested, write it. */ 883 if (expflag) { 884 pwdinfo = current->passwdinfo; 885 (void) fprintf(stdout, ":%ld:%ld", 886 pwdinfo->inactive, pwdinfo->expdate); 887 } 888 889 /* Terminate the information with a new-line */ 890 (void) putc('\n', stdout); 891 } 892 893 894 /* 895 * void writeformatted(current, xtndflag, expflag) 896 * struct display *current 897 * int xtndflag 898 * int expflag 899 * 900 * This function writes the data in the display structure "current" 901 * to the standard output file. It writes the information in an 902 * easily readable format. It writes secondary group information 903 * if that information is in the structure, it writes extended 904 * (initial working directory, shell, and password-aging) info if 905 * "xtndflag" is TRUE, and it write password expiration information 906 * if "expflag" is TRUE. 907 * 908 * Arguments: 909 * current Structure containing info to write. 910 * xtndflag TRUE if extended information to be written, 911 * FALSE otherwise 912 * expflag TRUE if password expiration information to be written, 913 * FALSE otherwise 914 * 915 * Returns: void 916 */ 917 918 static void 919 writeformatted(struct display *current, int xtndflag, int expflag) 920 { 921 struct secgrp *psecgrp; /* Secondary group info */ 922 struct pwdinfo *pwdinfo; /* Password aging info */ 923 924 /* Write general information */ 925 (void) fprintf(stdout, "%-14s %-6u %-14s %-6u %s\n", 926 current->loginID, current->userID, 927 current->groupname == NULL ? "" : current->groupname, 928 current->groupID, current->freefield); 929 930 /* 931 * Write information about secondary groups if the info exists 932 * (it only exists if it is to be written) 933 */ 934 for (psecgrp = current->secgrplist; psecgrp; psecgrp = psecgrp->next) { 935 (void) fprintf(stdout, " %-14s %-6u\n", 936 psecgrp->groupname, psecgrp->groupID); 937 } 938 939 /* 940 * If the extended information flag is TRUE, 941 * write the extended information 942 */ 943 944 if (xtndflag) { 945 pwdinfo = current->passwdinfo; 946 (void) fprintf(stdout, " %s\n", 947 current->iwd); 948 (void) fprintf(stdout, " %s\n", 949 current->shell); 950 (void) fprintf(stdout, " %s " 951 "%6.6ld %ld %ld %ld\n", 952 pwdinfo->passwdstatus, 953 pwdinfo->datechg, pwdinfo->mindaystilchg, 954 pwdinfo->maxdaystilchg, 955 pwdinfo->warninterval); 956 } 957 958 /* 959 * If the password expiration info flag is TRUE, 960 * write that information 961 */ 962 if (expflag) { 963 pwdinfo = current->passwdinfo; 964 (void) fprintf(stdout, " %ld %6.6ld\n", 965 pwdinfo->inactive, pwdinfo->expdate); 966 } 967 } 968 969 970 /* 971 * void genuidreport(pipeflag, xtndflag, expflag) 972 * int pipeflag 973 * int xtndflag 974 * int expflag 975 * 976 * This function generates a report on the standard output 977 * stream (stdout) containing the login-IDs in the list of 978 * logins built by this command. The list is ordered based 979 * on user-ID. If the <pipeflag> variable is not zero, it 980 * will generate a report containing parsable records. 981 * Otherwise, it will generate a columnarized report. If 982 * the <xtndflag> variable is not zero, it will include the 983 * extended set of information (password aging info, home 984 * directory, shell process, etc.). If <expflag> is not 985 * zero, it will display password expiration information. 986 * 987 * Arguments: 988 * pipeflag int 989 * TRUE if a parsable report is needed, 990 * FALSE if a columnar report is needed 991 * xtndflag int 992 * TRUE if extended set of info is to be displayed, 993 * FALSE otherwise 994 * expflag int 995 * TRUE if password expiration information is to be 996 * displayed, FALSE otherwise 997 * 998 * Returns: void 999 */ 1000 1001 static void 1002 genuidreport(int pipeflag, int xtndflag, int expflag) 1003 { 1004 1005 struct display *current; /* Data being displayed */ 1006 1007 1008 /* 1009 * Initialization for loop. 1010 * (NOTE: The first element in the list of logins to display is 1011 * a dummy element.) 1012 */ 1013 current = displayhead; 1014 1015 /* 1016 * Display elements in the list 1017 */ 1018 if (pipeflag) { 1019 for (current = displayhead->nextuid; current; 1020 current = current->nextuid) { 1021 writeunformatted(current, xtndflag, expflag); 1022 } 1023 } else { 1024 for (current = displayhead->nextuid; current; 1025 current = current->nextuid) { 1026 writeformatted(current, xtndflag, expflag); 1027 } 1028 } 1029 } 1030 1031 1032 /* 1033 * void genlogreport(pipeflag, xtndflag, expflag) 1034 * int pipeflag 1035 * int xtndflag 1036 * int expflag 1037 * 1038 * This function generates a report on the standard output 1039 * stream (stdout) containing the login-IDs in the list of 1040 * logins built by this command. The list is ordered based 1041 * on user name. If the <pipeflag> variable is not zero, it 1042 * will generate a report containing parsable records. 1043 * Otherwise, it will generate a columnarized report. If 1044 * the <xtndflag> variable is not zero, it will include the 1045 * extended set of information (password aging info, home 1046 * directory, shell process, etc.). If <expflag> is not 1047 * zero, it will include password expiration information. 1048 * 1049 * Arguments: 1050 * pipeflag int 1051 * TRUE if a parsable report is needed, 1052 * FALSE if a columnar report is needed 1053 * xtndflag int 1054 * TRUE if extended set of info is to be displayed, 1055 * FALSE otherwise 1056 * expflag int 1057 * TRUE if password expiration information is to 1058 * be displayed, FALSE otherwise 1059 * 1060 * Returns: void 1061 */ 1062 1063 static void 1064 genlogreport(int pipeflag, int xtndflag, int expflag) 1065 { 1066 struct display *p; /* Value being displayed */ 1067 1068 1069 /* 1070 * Initialization for loop. 1071 * (NOTE: The first element in the list of logins to display is 1072 * a dummy element.) 1073 */ 1074 p = displayhead; 1075 1076 /* 1077 * Display elements in the list 1078 */ 1079 if (pipeflag) { 1080 for (p = displayhead->nextlogin; p; p = p->nextlogin) { 1081 writeunformatted(p, xtndflag, expflag); 1082 } 1083 } else { 1084 for (p = displayhead->nextlogin; p; p = p->nextlogin) { 1085 writeformatted(p, xtndflag, expflag); 1086 } 1087 } 1088 } 1089 1090 struct localpw { 1091 struct localpw *next; 1092 struct passwd pw; 1093 }; 1094 1095 struct localpw *pwtable = NULL; 1096 1097 /* Local passwd pointer for getpwent() -- -1 means not in use, NULL for EOF */ 1098 struct localpw *pwptr; 1099 1100 int in_localgetpwent = 0; /* Set if in local_getpwent */ 1101 1102 static struct localpw * 1103 fill_localpw(struct localpw *lpw, struct passwd *pw) { 1104 struct localpw *cur; 1105 1106 /* 1107 * Copy the data -- we have to alloc areas for it all 1108 */ 1109 lpw->pw.pw_name = strdup(pw->pw_name); 1110 lpw->pw.pw_passwd = strdup(pw->pw_passwd); 1111 lpw->pw.pw_uid = pw->pw_uid; 1112 lpw->pw.pw_gid = pw->pw_gid; 1113 lpw->pw.pw_age = strdup(pw->pw_age); 1114 lpw->pw.pw_comment = strdup(pw->pw_comment); 1115 lpw->pw.pw_gecos = strdup(pw->pw_gecos); 1116 lpw->pw.pw_dir = strdup(pw->pw_dir); 1117 lpw->pw.pw_shell = strdup(pw->pw_shell); 1118 1119 cur = lpw; 1120 lpw->next = malloc(sizeof (struct localpw)); 1121 return (cur); 1122 } 1123 1124 void 1125 build_localpw(struct reqlogin *req_head) 1126 { 1127 struct localpw *next, *cur; 1128 struct passwd *pw; 1129 struct reqlogin *req_next; 1130 1131 next = malloc(sizeof (struct localpw)); 1132 1133 pwtable = next; 1134 1135 req_next = req_head; 1136 1137 while (req_next != NULL) { 1138 if ((pw = getpwnam(req_next->loginname)) != NULL) { 1139 /* 1140 * Copy the data -- we have to alloc areas for it all 1141 */ 1142 cur = fill_localpw(next, pw); 1143 req_next->found = TRUE; 1144 next = cur->next; 1145 } 1146 1147 req_next = req_next->next; 1148 } 1149 1150 if (req_head == NULL) { 1151 while ((pw = getpwent()) != NULL) { 1152 /* 1153 * Copy the data -- we have to alloc areas for it all 1154 */ 1155 cur = fill_localpw(next, pw); 1156 next = cur->next; 1157 } 1158 } 1159 1160 if (pwtable == next) { 1161 pwtable = NULL; 1162 } else { 1163 free(next); 1164 cur->next = NULL; 1165 } 1166 1167 endpwent(); 1168 } 1169 1170 struct passwd * 1171 local_getpwent(void) 1172 { 1173 if (!in_localgetpwent) { 1174 in_localgetpwent = 1; 1175 pwptr = pwtable; 1176 } else if (pwptr != NULL) { 1177 pwptr = pwptr->next; 1178 } 1179 1180 if (pwptr != NULL) 1181 return (&(pwptr->pw)); 1182 else 1183 return (NULL); 1184 } 1185 1186 void 1187 local_endpwent(void) 1188 { 1189 in_localgetpwent = 0; 1190 } 1191 1192 long 1193 local_pwtell(void) 1194 { 1195 return ((long)pwptr); 1196 } 1197 1198 void 1199 local_pwseek(long ptr) 1200 { 1201 pwptr = (struct localpw *)ptr; 1202 } 1203 1204 /* 1205 * logins [-admopstux] [-l logins] [-g groups] 1206 * 1207 * This command generates a report of logins administered on 1208 * the system. The list will contain logins that meet criteria 1209 * described by the options in the list. If there are no options, 1210 * it will list all logins administered. It is intended to be used 1211 * only by administrators. 1212 * 1213 * Options: 1214 * -a Display password expiration information. 1215 * -d list all logins that share user-IDs with another 1216 * login. 1217 * -g groups specifies the names of the groups to which a login 1218 * must belong before it is included in the generated 1219 * list. "groups" is a comma-list of group names. 1220 * -l logins specifies the logins to display. "logins" is a 1221 * comma-list of login names. 1222 * -m in addition to the usual information, for each 1223 * login displayed, list all groups to which that 1224 * login is member. 1225 * -o generate a report as a colon-list instead of in a 1226 * columnar format 1227 * -p list all logins that have no password. 1228 * -s list all system logins 1229 * -t sort the report lexicographically by login name 1230 * instead of by user-ID 1231 * -u list all user logins 1232 * -x in addition to the usual information, display an 1233 * extended set of information that includes the home 1234 * directory, initial process, and password status and 1235 * aging information 1236 * 1237 * Exit Codes: 1238 * 0 All's well that ends well 1239 * 1 Usage error 1240 */ 1241 1242 int 1243 main(int argc, char *argv[]) 1244 { 1245 struct passwd *plookpwd; /* Ptr to searcher pw (-d) */ 1246 struct reqgrp *reqgrphead; /* Head of the req'd group list */ 1247 struct reqgrp *pgrp; /* Current item in req'd group list */ 1248 struct reqgrp *qgrp; /* Prev item in the req'd group list */ 1249 struct reqlogin *reqloginhead; /* Head of req'd login list */ 1250 struct reqlogin *plogin; /* Current item in req'd login list */ 1251 struct reqlogin *qlogin; /* Prev item in req'd login list */ 1252 struct passwd *pwent; /* /etc/passwd entry */ 1253 struct group *grent; /* /etc/group entry */ 1254 char *token; /* Token extracted by strtok() */ 1255 char **pp; /* Group member */ 1256 char *g_arg; /* -g option's argument */ 1257 char *l_arg; /* -l option's argument */ 1258 long lookpos; /* File pos'n, rec we're looking for */ 1259 int a_seen; /* Is -a requested? */ 1260 int d_seen; /* Is -d requested? */ 1261 int g_seen; /* Is -g requested? */ 1262 int l_seen; /* Is -l requested? */ 1263 int m_seen; /* Is -m requested? */ 1264 int o_seen; /* Is -o requested? */ 1265 int p_seen; /* Is -p requested? */ 1266 int s_seen; /* Is -s requested? */ 1267 int t_seen; /* Is -t requested? */ 1268 int u_seen; /* Is -u requested? */ 1269 int x_seen; /* Is -x requested? */ 1270 int errflg; /* Is there a command-line problem */ 1271 int done; /* Is the process (?) is complete */ 1272 int groupcount; /* Number of groups specified */ 1273 int doall; /* Are all logins to be reported */ 1274 int c; /* Character returned from getopt() */ 1275 1276 (void) setlocale(LC_ALL, ""); 1277 1278 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1279 #define TEXT_DOMAIN "SYS_TEST" 1280 #endif 1281 (void) textdomain(TEXT_DOMAIN); 1282 1283 /* Initializations */ 1284 initmsg(argv[0]); 1285 1286 1287 1288 /* Command-line processing */ 1289 1290 /* Initializations */ 1291 a_seen = FALSE; 1292 d_seen = FALSE; 1293 g_seen = FALSE; 1294 l_seen = FALSE; 1295 m_seen = FALSE; 1296 o_seen = FALSE; 1297 p_seen = FALSE; 1298 s_seen = FALSE; 1299 t_seen = FALSE; 1300 u_seen = FALSE; 1301 x_seen = FALSE; 1302 errflg = FALSE; 1303 opterr = 0; 1304 while (!errflg && ((c = getopt(argc, argv, OPTSTR)) != EOF)) { 1305 1306 /* Case on the option character */ 1307 switch (c) { 1308 1309 /* 1310 * -a option: 1311 * Display password expiration information 1312 */ 1313 1314 case 'a': 1315 if (a_seen) 1316 errflg = TRUE; 1317 else 1318 a_seen = TRUE; 1319 break; 1320 1321 /* 1322 * -d option: 1323 * Display logins which share user-IDs with other logins 1324 */ 1325 1326 case 'd': 1327 if (d_seen) 1328 errflg = TRUE; 1329 else 1330 d_seen = TRUE; 1331 break; 1332 1333 /* 1334 * -g <groups> option: 1335 * Display the specified groups 1336 */ 1337 1338 case 'g': 1339 if (g_seen) { 1340 errflg = TRUE; 1341 } else { 1342 g_seen = TRUE; 1343 g_arg = optarg; 1344 } 1345 break; 1346 1347 /* 1348 * -l <logins> option: 1349 * Display the specified logins 1350 */ 1351 1352 case 'l': 1353 if (l_seen) { 1354 errflg = TRUE; 1355 } else { 1356 l_seen = TRUE; 1357 l_arg = optarg; 1358 } 1359 break; 1360 1361 /* 1362 * -m option: 1363 * Display multiple group information 1364 */ 1365 1366 case 'm': 1367 if (m_seen) 1368 errflg = TRUE; 1369 else 1370 m_seen = TRUE; 1371 break; 1372 1373 /* 1374 * -o option: 1375 * Display information as a colon-list 1376 */ 1377 1378 case 'o': 1379 if (o_seen) 1380 errflg = TRUE; 1381 else 1382 o_seen = TRUE; 1383 break; 1384 1385 /* 1386 * -p option: 1387 * Select logins that have no password 1388 */ 1389 1390 case 'p': 1391 if (p_seen) 1392 errflg = TRUE; 1393 else 1394 p_seen = TRUE; 1395 break; 1396 1397 /* 1398 * -s option: 1399 * Select system logins 1400 */ 1401 1402 case 's': 1403 if (s_seen) 1404 errflg = TRUE; 1405 else 1406 s_seen = TRUE; 1407 break; 1408 1409 /* 1410 * -t option: 1411 * Sort alphabetically by login-ID instead of numerically 1412 * by user-ID 1413 */ 1414 1415 case 't': 1416 if (t_seen) 1417 errflg = TRUE; 1418 else 1419 t_seen = TRUE; 1420 break; 1421 1422 /* 1423 * -u option: 1424 * Select user logins 1425 */ 1426 1427 case 'u': 1428 if (u_seen) 1429 errflg = TRUE; 1430 else 1431 u_seen = TRUE; 1432 break; 1433 1434 /* 1435 * -x option: 1436 * Display extended info (init working dir, shell, pwd info) 1437 */ 1438 1439 case 'x': 1440 if (x_seen) 1441 errflg = TRUE; 1442 else 1443 x_seen = TRUE; 1444 break; 1445 1446 default: /* Oops.... */ 1447 errflg = TRUE; 1448 } 1449 } 1450 1451 /* Write out a usage message if necessary and quit */ 1452 if (errflg || (optind != argc)) { 1453 wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(USAGE_MSG)); 1454 exit(1); 1455 } 1456 1457 /* 1458 * The following section does preparation work, setting up for 1459 * building the list of logins to display 1460 */ 1461 1462 1463 /* 1464 * If -l logins is on the command line, build a list of 1465 * logins we're to generate reports for. 1466 */ 1467 1468 if (l_seen) { 1469 reqloginhead = NULL; 1470 if (token = strtok(l_arg, ",")) { 1471 plogin = malloc(sizeof (struct reqlogin)); 1472 plogin->loginname = token; 1473 plogin->found = FALSE; 1474 plogin->next = NULL; 1475 reqloginhead = plogin; 1476 qlogin = plogin; 1477 while (token = strtok(NULL, ",")) { 1478 plogin = malloc(sizeof (struct reqlogin)); 1479 plogin->loginname = token; 1480 plogin->found = FALSE; 1481 plogin->next = NULL; 1482 qlogin->next = plogin; 1483 qlogin = plogin; 1484 } 1485 } 1486 /* 1487 * Build an in-core structure of just the passwd database 1488 * entries requested. This greatly reduces the time 1489 * to get all entries and filter later. 1490 */ 1491 build_localpw(reqloginhead); 1492 } else { 1493 /* 1494 * Build an in-core structure of all passwd database 1495 * entries. This is important since we have to assume that 1496 * getpwent() is going out to one or more network name 1497 * services that could be changing on the fly. This will 1498 * limit us to one pass through the network data. 1499 */ 1500 build_localpw(NULL); 1501 } 1502 1503 /* 1504 * If the -g groups option was on the command line, build a 1505 * list containing groups we're to list logins for. 1506 */ 1507 1508 if (g_seen) { 1509 groupcount = 0; 1510 reqgrphead = NULL; 1511 if (token = strtok(g_arg, ",")) { 1512 pgrp = malloc(sizeof (struct reqgrp)); 1513 pgrp->groupname = token; 1514 pgrp->next = NULL; 1515 groupcount++; 1516 reqgrphead = pgrp; 1517 qgrp = pgrp; 1518 while (token = strtok(NULL, ",")) { 1519 pgrp = malloc(sizeof (struct reqgrp)); 1520 pgrp->groupname = token; 1521 pgrp->next = NULL; 1522 groupcount++; 1523 qgrp->next = pgrp; 1524 qgrp = pgrp; 1525 } 1526 } 1527 } 1528 1529 1530 /* 1531 * Generate the list of login information to display 1532 */ 1533 1534 /* Initialize the login list */ 1535 membershead = NULL; 1536 1537 1538 /* 1539 * If -g groups was specified, generate a list of members 1540 * of the specified groups 1541 */ 1542 1543 if (g_seen) { 1544 /* For each group mentioned with the -g option ... */ 1545 for (pgrp = reqgrphead; (groupcount > 0) && pgrp; 1546 pgrp = pgrp->next) { 1547 if ((grent = getgrnam(pgrp->groupname)) != NULL) { 1548 /* 1549 * Remembering the group-ID for later 1550 */ 1551 1552 groupcount--; 1553 pgrp->groupID = grent->gr_gid; 1554 for (pp = grent->gr_mem; *pp; pp++) { 1555 addmember(*pp); 1556 } 1557 } else { 1558 wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, 1559 gettext("%s was not found"), 1560 pgrp->groupname); 1561 } 1562 } 1563 } 1564 1565 1566 /* Initialize the list of logins to display */ 1567 initdisp(); 1568 1569 1570 /* 1571 * Add logins that have user-IDs that are used more than once, 1572 * if requested. This command is pretty slow, since the algorithm 1573 * reads from the /etc/passwd file 1+2+3+...+n times where n is the 1574 * number of login-IDs in the /etc/passwd file. (Actually, this 1575 * can be optimized so it's not quite that bad, but the order or 1576 * magnitude stays the same.) 1577 * 1578 * Note: This processing needs to be done before any other options 1579 * are processed -- the algorithm contains an optimization 1580 * that insists on the display list being empty before this 1581 * option is processed. 1582 */ 1583 1584 if (d_seen) { 1585 1586 /* 1587 * The following code is a quick&dirty reimplementation of the 1588 * original algorithm, which opened the password file twice (to 1589 * get two file pointer into the data) and then used fgetpwent() 1590 * in undocumented ways to scan through the file, checking for 1591 * duplicates. This does not work when getpwent() is used to 1592 * go out over the network, since there is not file pointer. 1593 * 1594 * Instead an in-memory list of passwd structures is built, 1595 * and then this list is scanned. The routines 1596 * Local_getpwent(), etc., are designed to mimic the standard 1597 * library routines, so this code does not have to be 1598 * extensively modified. 1599 */ 1600 1601 /* 1602 * For reference, here is the original comment about the next 1603 * section of code. Some of the code has changed, but the 1604 * algorithm is the same: 1605 * 1606 * Open the system password file once. This instance will be 1607 * used to leaf through the file once, reading each entry once, 1608 * and searching the remainder of the file for another login-ID 1609 * that has the same user-ID. Note that there are lots of 1610 * contortions one has to go through when reading two instances 1611 * of the /etc/passwd file. That's why there's some seeking, 1612 * re-reading of the same record, and other junk. Luckily, this 1613 * feature won't be requested very often, and still isn't too 1614 * slow... 1615 */ 1616 1617 /* For each entry in the passwd database ... */ 1618 while (plookpwd = local_getpwent()) { 1619 /* 1620 * Optimization -- If the login's user-ID is already 1621 * in the display list, there's no reason to process 1622 * this entry -- it's already there. 1623 */ 1624 if (!isuidindisp(plookpwd)) { 1625 /* 1626 * Rememeber the current entry's position, 1627 * so when we finish scanning through the 1628 * database looking for duplicates we can 1629 * return to the current place, so that the 1630 * enclosing loop will march in an orderly 1631 * fashion through the passwd database. 1632 */ 1633 done = FALSE; 1634 lookpos = local_pwtell(); 1635 1636 /* 1637 * For each record in the passwd database 1638 * beyond the searching record ... 1639 */ 1640 while (pwent = local_getpwent()) { 1641 1642 /* 1643 * If there's a match between the 1644 * searcher's user-ID and the 1645 * searchee's user-ID ... 1646 */ 1647 if (pwent->pw_uid == plookpwd->pw_uid) { 1648 /* 1649 * If this is the first 1650 * duplicate of this searcher 1651 * that we find, 1652 * add the searcher's 1653 * record to the display list 1654 * (It wants to be on the 1655 * list first to avoid 1656 * ordering "flakeyness") 1657 */ 1658 if (done == FALSE) { 1659 adddisp(plookpwd); 1660 done = TRUE; 1661 } 1662 1663 /* 1664 * Now add the searchee's 1665 * record 1666 */ 1667 adddisp(pwent); 1668 1669 } 1670 } 1671 /* Reposition to searcher record */ 1672 local_pwseek(lookpos); 1673 } 1674 } 1675 1676 local_endpwent(); 1677 } 1678 1679 1680 /* 1681 * Loop through the passwd database squirelling away the 1682 * information we need for the display. 1683 * 1684 * NOTE: Once a login is added to the list, the rest of the 1685 * body of the loop is bypassed (via a continue statement). 1686 */ 1687 1688 doall = !(s_seen || u_seen || p_seen || d_seen || l_seen || g_seen); 1689 1690 if (doall || s_seen || u_seen || p_seen || l_seen || g_seen) { 1691 1692 while (pwent = local_getpwent()) { 1693 done = FALSE; 1694 1695 /* 1696 * If no user-specific options were specified, 1697 * include this login-ID 1698 */ 1699 if (doall) { 1700 adddisp(pwent); 1701 continue; 1702 } 1703 1704 /* 1705 * If the user specified system login-IDs, 1706 * and this is a system ID, include it 1707 */ 1708 if (s_seen) { 1709 if (isasystemlogin(pwent)) { 1710 adddisp(pwent); 1711 continue; 1712 } 1713 } 1714 1715 /* 1716 * If the user specified user login-IDs, 1717 * and this is a user ID, include it 1718 */ 1719 if (u_seen) { 1720 if (isauserlogin(pwent)) { 1721 adddisp(pwent); 1722 continue; 1723 } 1724 } 1725 1726 /* 1727 * If the user is asking for login-IDs that have 1728 * no password, and this one has no password, include it 1729 */ 1730 if (p_seen) { 1731 if (hasnopasswd(pwent)) { 1732 adddisp(pwent); 1733 continue; 1734 } 1735 } 1736 1737 /* 1738 * If specific logins were requested, leaf through 1739 * the list of logins they requested. If this login 1740 * is on the list, include it. 1741 */ 1742 if (l_seen) { 1743 for (plogin = reqloginhead; !done && plogin; 1744 plogin = plogin->next) { 1745 if (strcmp(pwent->pw_name, 1746 plogin->loginname) == 0) { 1747 plogin->found = TRUE; 1748 adddisp(pwent); 1749 done = TRUE; 1750 } 1751 } 1752 if (done) 1753 continue; 1754 } 1755 1756 /* 1757 * If specific groups were requested, leaf through the 1758 * list of login-IDs that belong to those groups. 1759 * If this login-ID is in that list, or its primary 1760 * group is one of those requested, include it. 1761 */ 1762 1763 if (g_seen) { 1764 for (pgrp = reqgrphead; !done && pgrp; 1765 pgrp = pgrp->next) { 1766 if (pwent->pw_gid == pgrp->groupID) { 1767 adddisp(pwent); 1768 done = TRUE; 1769 } 1770 } 1771 if (!done && isamember(pwent->pw_name)) { 1772 adddisp(pwent); 1773 done = TRUE; 1774 } 1775 } 1776 if (done) 1777 continue; 1778 } 1779 1780 local_endpwent(); 1781 } 1782 1783 /* Let the user know about logins they requested that don't exist */ 1784 if (l_seen) { 1785 for (plogin = reqloginhead; plogin; plogin = plogin->next) { 1786 if (!plogin->found) { 1787 wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, 1788 gettext("%s was not found"), 1789 plogin->loginname); 1790 } 1791 } 1792 } 1793 1794 /* Apply group information */ 1795 applygroup(m_seen); 1796 1797 1798 /* 1799 * Apply password information (only needed if the extended 1800 * set of information has been requested) 1801 */ 1802 if (x_seen || a_seen) 1803 applypasswd(); 1804 1805 1806 /* 1807 * Generate a report from this display items we've squirreled away 1808 */ 1809 1810 if (t_seen) 1811 genlogreport(o_seen, x_seen, a_seen); 1812 else 1813 genuidreport(o_seen, x_seen, a_seen); 1814 1815 /* We're through! */ 1816 return (0); 1817 } 1818