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