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