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