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