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