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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /* Copyright (c) 1987, 1988 Microsoft Corporation */
30 /* All Rights Reserved */
31
32 /*
33 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
34 */
35 /*
36 * passwd is a program whose sole purpose is to manage
37 * the password file, map, or table. It allows system administrator
38 * to add, change and display password attributes.
39 * Non privileged user can change password or display
40 * password attributes which corresponds to their login name.
41 */
42
43 #include <stdio.h>
44 #include <pwd.h>
45 #include <sys/types.h>
46 #include <errno.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <locale.h>
50 #include <stdarg.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <security/pam_appl.h>
54 #include <security/pam_modules.h>
55 #include <security/pam_impl.h>
56 #include <rpcsvc/nis.h>
57 #undef GROUP
58 #include <syslog.h>
59 #include <userdefs.h>
60 #include <passwdutil.h>
61
62 #include <nss_dbdefs.h>
63
64 #include <deflt.h>
65
66 #undef GROUP
67 #include <bsm/adt.h>
68 #include <bsm/adt_event.h>
69
70 /*
71 * flags indicate password attributes to be modified
72 */
73
74 #define LFLAG 0x001 /* lock user's password */
75 #define DFLAG 0x002 /* delete user's password */
76 #define MFLAG 0x004 /* set max field -- # of days passwd is valid */
77 #define NFLAG 0x008 /* set min field -- # of days between */
78 /* password changes */
79 #define SFLAG 0x010 /* display password attributes */
80 #define FFLAG 0x020 /* expire user's password */
81 #define AFLAG 0x040 /* display password attributes for all users */
82 #define SAFLAG (SFLAG|AFLAG) /* display password attributes for all users */
83 #define WFLAG 0x100 /* warn user to change passwd */
84 #define OFLAG 0x200 /* domain name */
85 #define EFLAG 0x400 /* change shell */
86 #define GFLAG 0x800 /* change gecos information */
87 #define HFLAG 0x1000 /* change home directory */
88 #define XFLAG 0x2000 /* no login */
89 #define UFLAG 0x4000 /* unlock user's password */
90 #define STFLAG 0x6000 /* read the password from stdin */
91
92 #define NONAGEFLAG (EFLAG | GFLAG | HFLAG)
93 #define AGEFLAG (LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG)
94 #define MUTEXFLAG (DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG)
95
96
97 /*
98 * exit code
99 */
100
101 #define SUCCESS 0 /* succeeded */
102 #define NOPERM 1 /* No permission */
103 #define BADOPT 2 /* Invalid combination of option */
104 #define FMERR 3 /* File/table manipulation error */
105 #define FATAL 4 /* Old file/table can not be recovered */
106 #define FBUSY 5 /* Lock file/table busy */
107 #define BADSYN 6 /* Incorrect syntax */
108 #define BADAGE 7 /* Aging is disabled */
109 #define NOMEM 8 /* No memory */
110 #define SYSERR 9 /* System error */
111 #define EXPIRED 10 /* Account expired */
112
113 /*
114 * define error messages
115 */
116 #define MSG_NP "Permission denied"
117 #define MSG_BS "Invalid combination of options"
118 #define MSG_FE "Unexpected failure. Password file/table unchanged."
119 #define MSG_FF "Unexpected failure. Password file/table missing."
120 #define MSG_FB "Password file/table busy. Try again later."
121 #define MSG_NV "Invalid argument to option"
122 #define MSG_AD "Password aging is disabled"
123 #define MSG_RS "Cannot change from restricted shell %s\n"
124 #define MSG_NM "Out of memory."
125 #define MSG_UNACCEPT "%s is unacceptable as a new shell\n"
126 #define MSG_UNAVAIL "warning: %s is unavailable on this machine\n"
127 #define MSG_COLON "':' is not allowed.\n"
128 #define MSG_MAXLEN "Maximum number of characters allowed is %d."
129 #define MSG_CONTROL "Control characters are not allowed.\n"
130 #define MSG_SHELL_UNCHANGED "Login shell unchanged.\n"
131 #define MSG_GECOS_UNCHANGED "Finger information unchanged.\n"
132 #define MSG_DIR_UNCHANGED "Homedir information unchanged.\n"
133 #define MSG_NAME "\nName [%s]: "
134 #define MSG_HOMEDIR "\nHome Directory [%s]: "
135 #define MSG_OLDSHELL "Old shell: %s\n"
136 #define MSG_NEWSHELL "New shell: "
137 #define MSG_AGAIN "\nPlease try again\n"
138 #define MSG_INPUTHDR "Default values are printed inside of '[]'.\n" \
139 "To accept the default, type <return>.\n" \
140 "To have a blank entry, type the word 'none'.\n"
141 #define MSG_UNKNOWN "%s: User unknown: %s\n"
142 #define MSG_ACCOUNT_EXP "User account has expired: %s\n"
143 #define MSG_AUTHTOK_EXP "Your password has been expired for too long.\n" \
144 "Please contact the system administrator.\n"
145 #define MSG_NIS_HOMEDIR "-h does not apply to NIS"
146 #define MSG_CUR_PASS "Enter existing login password: "
147 #define MSG_CUR_PASS_UNAME "Enter %s's existing login password: "
148 #define MSG_SUCCESS "%s: password information changed for %s\n"
149 #define MSG_SORRY "%s: Sorry, wrong passwd\n"
150 #define MSG_INFO "%s: Changing password for %s\n"
151
152
153 /*
154 * return code from ckarg() routine
155 */
156 #define FAIL -1
157
158 /*
159 * defind password file name
160 */
161 #define PASSWD "/etc/passwd"
162
163 #define MAX_INPUT_LEN 512
164
165 #define DEF_ATTEMPTS 3
166
167 /* Number of characters in that make up an encrypted password (for now) */
168 #define NUMCP 13
169
170 #ifdef DEBUG
171 #define dprintf1 printf
172 #else
173 #define dprintf1(w, x)
174 #endif
175
176 extern int optind;
177
178 static int retval = SUCCESS;
179 static int pam_retval = PAM_SUCCESS;
180 static uid_t uid;
181 static char *prognamep;
182 static long maxdate; /* password aging information */
183 static int passwd_conv(int, struct pam_message **,
184 struct pam_response **, void *);
185 static int stdin_conv(int, struct pam_message **,
186 struct pam_response **, void *);
187 static struct pam_conv pam_conv = {passwd_conv, NULL};
188 static pam_handle_t *pamh; /* Authentication handle */
189 static char *usrname; /* user whose attribute we update */
190 static adt_session_data_t *ah; /* audit session handle */
191 static adt_event_data_t *event = NULL; /* event to be generated */
192
193 static pam_repository_t auth_rep;
194 static pwu_repository_t repository;
195 static pwu_repository_t __REPFILES = { "files", NULL, 0 };
196
197 /*
198 * Function Declarations
199 */
200
201 extern void setusershell(void);
202 extern char *getusershell(void);
203 extern void endusershell(void);
204
205 static void passwd_exit(int retcode) __NORETURN;
206 static void rusage(void);
207 static int ckuid(void);
208 static int ckarg(int argc, char **argv, attrlist **attributes);
209
210 static int get_namelist(pwu_repository_t, char ***, int *);
211 static int get_namelist_files(char ***, int *);
212 static int get_namelist_local(char ***, int *);
213 static int get_attr(char *, pwu_repository_t *, attrlist **);
214 static void display_attr(char *, attrlist *);
215 static void free_attr(attrlist *);
216 static void attrlist_add(attrlist **, attrtype, char *);
217 static void attrlist_reorder(attrlist **);
218 static char *userinput(char *, pwu_repository_t *, attrtype);
219 static char *getresponse(char *);
220
221 /*
222 * main():
223 * The main routine will call ckarg() to parse the command line
224 * arguments and call the appropriate functions to perform the
225 * tasks specified by the arguments. It allows system
226 * administrator to add, change and display password attributes.
227 * Non privileged user can change password or display
228 * password attributes which corresponds to their login name.
229 */
230
231 int
232 main(int argc, char *argv[])
233 {
234
235 int flag;
236 char **namelist;
237 int num_user;
238 int i;
239 attrlist *attributes = NULL;
240 char *input;
241 int tries = 1;
242 int updated_reps;
243 ssize_t s;
244 char st_pass[PASS_MAX];
245
246 if ((prognamep = strrchr(argv[0], '/')) != NULL)
247 ++prognamep;
248 else
249 prognamep = argv[0];
250
251 auth_rep.type = NULL;
252 auth_rep.scope = NULL;
253 repository.type = NULL;
254 repository.scope = NULL;
255 repository.scope_len = 0;
256
257
258 /* initialization for variables, set locale and textdomain */
259 i = 0;
260 flag = 0;
261
262 uid = getuid(); /* get the user id */
263 (void) setlocale(LC_ALL, "");
264
265 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
266 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
267 #endif
268 (void) textdomain(TEXT_DOMAIN);
269
270 /*
271 * ckarg() parses the arguments. In case of an error,
272 * it sets the retval and returns FAIL (-1).
273 */
274
275 flag = ckarg(argc, argv, &attributes);
276 dprintf1("flag is %0x\n", flag);
277 if (flag == FAIL)
278 passwd_exit(retval);
279
280 argc -= optind;
281
282 if (argc < 1) {
283 if ((usrname = getlogin()) == NULL) {
284 struct passwd *pass = getpwuid(uid);
285 if (pass != NULL)
286 usrname = pass->pw_name;
287 else {
288 rusage();
289 exit(NOPERM);
290 }
291 } else if (flag == 0) {
292 /*
293 * If flag is zero, change passwd.
294 * Otherwise, it will display or
295 * modify password aging attributes
296 */
297 (void) fprintf(stderr, gettext(MSG_INFO), prognamep,
298 usrname);
299 }
300 } else {
301 usrname = argv[optind];
302 }
303
304 if (flag == STFLAG) {
305 if ((s = read(STDIN_FILENO, st_pass, sizeof (st_pass))) <= 0)
306 passwd_exit(FMERR);
307
308 st_pass[s - 1] = '\0';
309 if ((pam_conv.appdata_ptr = strdup(st_pass)) == NULL)
310 passwd_exit(FMERR);
311 pam_conv.conv = stdin_conv;
312 }
313
314 if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS) {
315 passwd_exit(NOPERM);
316 }
317
318 auth_rep.type = repository.type;
319 auth_rep.scope = repository.scope;
320 auth_rep.scope_len = repository.scope_len;
321
322 if (auth_rep.type != NULL) {
323 if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep)
324 != PAM_SUCCESS) {
325 passwd_exit(NOPERM);
326 }
327 }
328
329 if (flag == SAFLAG) { /* display password attributes for all users */
330 retval = get_namelist(repository, &namelist, &num_user);
331 if (retval != SUCCESS)
332 (void) passwd_exit(retval);
333
334 if (num_user == 0) {
335 (void) fprintf(stderr, "%s: %s\n", prognamep,
336 gettext(MSG_FF));
337 passwd_exit(FATAL);
338 }
339 i = 0;
340 while (namelist[i] != NULL) {
341 (void) get_attr(namelist[i], &repository,
342 &attributes);
343 (void) display_attr(namelist[i], attributes);
344 (void) free(namelist[i]);
345 (void) free_attr(attributes);
346 i++;
347 }
348 (void) free(namelist);
349 passwd_exit(SUCCESS);
350 } else if (flag == SFLAG) { /* display password attributes by user */
351 if (get_attr(usrname, &repository, &attributes) ==
352 PWU_SUCCESS) {
353 (void) display_attr(usrname, attributes);
354 (void) free_attr(attributes);
355 }
356 passwd_exit(SUCCESS);
357 /* NOT REACHED */
358 }
359
360
361 switch (pam_authenticate(pamh, 0)) {
362 case PAM_SUCCESS:
363 break;
364 case PAM_USER_UNKNOWN:
365 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
366 usrname);
367 passwd_exit(NOPERM);
368 break;
369 case PAM_PERM_DENIED:
370 passwd_exit(NOPERM);
371 break;
372 case PAM_AUTH_ERR:
373 (void) fprintf(stderr, gettext(MSG_SORRY), prognamep);
374 passwd_exit(NOPERM);
375 break;
376 default:
377 /* system error */
378 passwd_exit(FMERR);
379 break;
380 }
381
382 if (flag == STFLAG || flag == 0) { /* changing user password */
383 int chk_authtok = 0; /* check password strength */
384
385 dprintf1("call pam_chauthtok() repository name =%s\n",
386 repository.type);
387
388 /* Set up for Audit */
389 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
390 perror("adt_start_session");
391 passwd_exit(SYSERR);
392 }
393 if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
394 perror("adt_alloc_event");
395 passwd_exit(NOMEM);
396 }
397
398 /* Don't check account expiration when invoked by root */
399 if (ckuid() != SUCCESS) {
400 pam_retval = pam_acct_mgmt(pamh, PAM_SILENT);
401 switch (pam_retval) {
402 case PAM_ACCT_EXPIRED:
403 (void) fprintf(stderr,
404 gettext(MSG_ACCOUNT_EXP), usrname);
405 passwd_exit(EXPIRED);
406 break;
407 case PAM_AUTHTOK_EXPIRED:
408 (void) fprintf(stderr,
409 gettext(MSG_AUTHTOK_EXP));
410 passwd_exit(NOPERM);
411 break;
412 case PAM_NEW_AUTHTOK_REQD:
413 /* valid error when changing passwords */
414 break;
415 case PAM_SUCCESS:
416 /* Ok to change password */
417 break;
418 default:
419 passwd_exit(NOPERM);
420 }
421 }
422
423
424 pam_retval = PAM_AUTHTOK_ERR;
425 tries = 1;
426 if (ckuid() == SUCCESS) {
427 /* bypass password strength checks */
428 chk_authtok = PAM_NO_AUTHTOK_CHECK;
429 }
430
431 while (pam_retval == PAM_AUTHTOK_ERR && tries <= DEF_ATTEMPTS) {
432 if (tries > 1)
433 (void) printf(gettext(MSG_AGAIN));
434 pam_retval = pam_chauthtok(pamh, chk_authtok);
435 if (pam_retval == PAM_TRY_AGAIN) {
436 (void) sleep(1);
437 pam_retval = pam_chauthtok(pamh, chk_authtok);
438 }
439 tries++;
440 }
441
442 switch (pam_retval) {
443 case PAM_SUCCESS:
444 retval = SUCCESS;
445 break;
446 case PAM_AUTHTOK_DISABLE_AGING:
447 retval = BADAGE;
448 break;
449 case PAM_AUTHTOK_LOCK_BUSY:
450 retval = FBUSY;
451 break;
452 case PAM_TRY_AGAIN:
453 retval = FBUSY;
454 break;
455 case PAM_AUTHTOK_ERR:
456 case PAM_AUTHTOK_RECOVERY_ERR:
457 default:
458 retval = NOPERM;
459 break;
460 }
461
462 (void) passwd_exit(retval);
463 /* NOT REACHED */
464 } else { /* changing attributes */
465 switch (flag) {
466 case EFLAG: /* changing user password attributes */
467 input = userinput(usrname, &repository, ATTR_SHELL);
468 if (input)
469 attrlist_add(&attributes, ATTR_SHELL, input);
470 else
471 (void) printf(gettext(MSG_SHELL_UNCHANGED));
472 break;
473 case GFLAG:
474 input = userinput(usrname, &repository, ATTR_GECOS);
475 if (input)
476 attrlist_add(&attributes, ATTR_GECOS, input);
477 else
478 (void) printf(gettext(MSG_GECOS_UNCHANGED));
479 break;
480 case HFLAG:
481 input = userinput(usrname, &repository, ATTR_HOMEDIR);
482 if (input)
483 attrlist_add(&attributes, ATTR_HOMEDIR, input);
484 else
485 (void) printf(gettext(MSG_DIR_UNCHANGED));
486 break;
487 }
488
489 if (attributes != NULL) {
490 retval = __set_authtoken_attr(usrname,
491 pamh->ps_item[PAM_AUTHTOK].pi_addr,
492 &repository, attributes, &updated_reps);
493 switch (retval) {
494 case PWU_SUCCESS:
495 for (i = 1; i <= REP_LAST; i <<= 1) {
496 if ((updated_reps & i) == 0)
497 continue;
498 (void) printf(gettext(MSG_SUCCESS),
499 prognamep, usrname);
500 }
501 retval = SUCCESS;
502 break;
503 case PWU_AGING_DISABLED:
504 retval = BADAGE;
505 break;
506 default:
507 retval = NOPERM;
508 break;
509 }
510 } else {
511 retval = SUCCESS; /* nothing to change won't fail */
512 }
513 (void) passwd_exit(retval);
514 }
515 /* NOTREACHED */
516 return (0);
517 }
518
519 /*
520 * Get a line of input from the user.
521 *
522 * If the line is empty, or the input equals 'oldval', NULL is returned.
523 * therwise, a malloced string containing the input (minus the trailing
524 * newline) is returned.
525 */
526 char *
527 getresponse(char *oldval)
528 {
529 char resp[MAX_INPUT_LEN];
530 char *retval = NULL;
531 int resplen;
532
533 (void) fgets(resp, sizeof (resp) - 1, stdin);
534 resplen = strlen(resp) - 1;
535 if (resp[resplen] == '\n')
536 resp[resplen] = '\0';
537 if (*resp != '\0' && strcmp(resp, oldval) != 0)
538 retval = strdup(resp);
539 return (retval);
540 }
541
542 /*
543 * char *userinput(item)
544 *
545 * user conversation function. The old value of attribute "item" is
546 * displayed while the user is asked to provide a new value.
547 *
548 * returns a malloc()-ed string if the user actualy provided input
549 * or NULL if the user simply hit return or the input equals the old
550 * value (not changed).
551 */
552 char *
553 userinput(char *name, pwu_repository_t *rep, attrtype type)
554 {
555 attrlist oldattr;
556 char *oldval; /* shorthand for oldattr.data.val_s */
557 char *valid; /* points to valid shells */
558 char *response;
559 char *cp;
560
561 oldattr.type = type;
562 oldattr.next = NULL;
563
564 if (__get_authtoken_attr(name, rep, &oldattr) != PWU_SUCCESS)
565 passwd_exit(FMERR);
566
567 oldval = oldattr.data.val_s;
568
569 if (type == ATTR_SHELL) {
570 /* No current shell: set DEFSHL as default choice */
571 if (*oldval == '\0') {
572 free(oldval);
573 oldval = strdup(DEFSHL);
574 }
575
576 if (ckuid() != SUCCESS) {
577 /* User must currently have a valid shell */
578 setusershell();
579 valid = getusershell();
580 while (valid && strcmp(valid, oldval) != 0)
581 valid = getusershell();
582 endusershell();
583
584 if (valid == NULL) {
585 (void) fprintf(stderr, gettext(MSG_RS), oldval);
586 free(oldval);
587 return (NULL);
588 }
589 }
590 (void) printf(gettext(MSG_OLDSHELL), oldval);
591 (void) printf(gettext(MSG_NEWSHELL));
592 (void) fflush(stdout);
593
594 response = getresponse(oldval);
595 free(oldval); /* We don't need the old value anymore */
596
597 if (response == NULL || *response == '\0')
598 return (NULL);
599
600 /* Make sure new shell is listed */
601 setusershell();
602 valid = getusershell();
603 while (valid) {
604 char *cp;
605
606 /* Allow user to give shell without path */
607 if (*response == '/') {
608 cp = valid;
609 } else {
610 if ((cp = strrchr(valid, '/')) == NULL)
611 cp = valid;
612 else
613 cp++;
614 }
615 if (strcmp(cp, response) == 0) {
616 if (*response != '/') {
617 /* take shell name including path */
618 free(response);
619 response = strdup(valid);
620 }
621 break;
622 }
623 valid = getusershell();
624 }
625 endusershell();
626
627 if (valid == NULL) { /* No valid shell matches */
628 (void) fprintf(stderr, gettext(MSG_UNACCEPT), response);
629 return (NULL);
630 }
631
632 if (access(response, X_OK) < 0)
633 (void) fprintf(stderr, gettext(MSG_UNAVAIL), response);
634 return (response);
635 /* NOT REACHED */
636 }
637 /*
638 * if type == SHELL, we have returned by now. Only GECOS and
639 * HOMEDIR get to this point.
640 */
641 (void) printf(gettext(MSG_INPUTHDR));
642
643 /*
644 * PRE: oldval points to malloced string with Old Value
645 * INV: oldval remains unchanged
646 * POST:response points to valid string or NULL.
647 */
648 for (;;) {
649 if (type == ATTR_GECOS)
650 (void) printf(gettext(MSG_NAME), oldval);
651 else if (type == ATTR_HOMEDIR)
652 (void) printf(gettext(MSG_HOMEDIR), oldval);
653
654 response = getresponse(oldval);
655
656 if (response && strcmp(response, "none") == 0)
657 *response = '\0';
658
659 /* No-change or empty string are OK */
660 if (response == NULL || *response == '\0')
661 break;
662
663 /* Check for illegal characters */
664 if (strchr(response, ':')) {
665 (void) fprintf(stderr, "%s", gettext(MSG_COLON));
666 free(response);
667 } else if (strlen(response) > MAX_INPUT_LEN - 1) {
668 (void) fprintf(stderr, gettext(MSG_MAXLEN),
669 MAX_INPUT_LEN);
670 free(response);
671 } else {
672 /* don't allow control characters */
673 for (cp = response; *cp >= 040; cp++)
674 ;
675 if (*cp != '\0') {
676 (void) fprintf(stderr, gettext(MSG_CONTROL));
677 free(response);
678 } else
679 break; /* response is a valid string */
680 }
681 /*
682 * We only get here if the input was invalid.
683 * In that case, we again ask the user for input.
684 */
685 }
686 free(oldval);
687 return (response);
688 }
689 /*
690 * ckarg():
691 * This function parses and verifies the
692 * arguments. It takes three parameters:
693 * argc => # of arguments
694 * argv => pointer to an argument
695 * attrlist => pointer to list of password attributes
696 */
697
698 static int
699 ckarg(int argc, char **argv, attrlist **attributes)
700 {
701 extern char *optarg;
702 char *char_p;
703 int opt;
704 int flag;
705
706 flag = 0;
707
708 while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:N:S")) != EOF) {
709 switch (opt) {
710
711 case 'r': /* Repository Specified */
712 /* repository: this option should be specified first */
713
714 if (repository.type != NULL) {
715 (void) fprintf(stderr, gettext(
716 "Repository is already defined or specified.\n"));
717 rusage();
718 retval = BADSYN;
719 return (FAIL);
720 }
721 if (strcmp(optarg, "nis") == 0) {
722 repository.type = optarg;
723 } else if (strcmp(optarg, "ldap") == 0) {
724 repository.type = optarg;
725 } else if (strcmp(optarg, "files") == 0) {
726 repository.type = optarg;
727 } else {
728 (void) fprintf(stderr,
729 gettext("invalid repository: %s\n"),
730 optarg);
731 rusage();
732 retval = BADSYN;
733 return (FAIL);
734 }
735 break;
736
737 case 'd': /* Delete Auth Token */
738 /* if no repository the default for -d is files */
739 if (repository.type == NULL)
740 repository = __REPFILES;
741
742 /*
743 * Delete the password - only privileged processes
744 * can execute this for FILES or LDAP
745 */
746 if (IS_FILES(repository) == FALSE &&
747 IS_LDAP(repository) == FALSE) {
748 (void) fprintf(stderr, gettext(
749 "-d only applies to files "
750 "or ldap repository\n"));
751 rusage(); /* exit */
752 retval = BADSYN;
753 return (FAIL);
754 }
755
756 if (ckuid() != SUCCESS) {
757 retval = NOPERM;
758 return (FAIL);
759 }
760 if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG|STFLAG)) {
761 rusage();
762 retval = BADOPT;
763 return (FAIL);
764 }
765 flag |= DFLAG;
766 attrlist_add(attributes, ATTR_PASSWD, NULL);
767 break;
768
769 case 'N': /* set account to be "no login" */
770
771 /* if no repository the default for -N is files */
772 if (repository.type == NULL)
773 repository = __REPFILES;
774
775 if (IS_FILES(repository) == FALSE &&
776 IS_LDAP(repository) == FALSE) {
777 (void) fprintf(stderr, gettext(
778 "-N only applies to files or ldap "
779 "repository\n"));
780 rusage(); /* exit */
781 retval = BADOPT;
782 return (FAIL);
783 }
784
785 /*
786 * Only privileged processes can execute this
787 * for FILES or LDAP
788 */
789 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
790 ((retval = ckuid()) != SUCCESS))
791 return (FAIL);
792 if (flag & (MUTEXFLAG|NONAGEFLAG|STFLAG)) {
793 rusage(); /* exit */
794 retval = BADOPT;
795 return (FAIL);
796 }
797 flag |= XFLAG;
798 attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL);
799 break;
800
801 case 'l': /* lock the password */
802
803 /* if no repository the default for -l is files */
804 if (repository.type == NULL)
805 repository = __REPFILES;
806
807 if (IS_FILES(repository) == FALSE &&
808 IS_LDAP(repository) == FALSE) {
809 (void) fprintf(stderr, gettext(
810 "-l only applies to files or ldap "
811 "repository\n"));
812 rusage(); /* exit */
813 retval = BADOPT;
814 return (FAIL);
815 }
816
817 /*
818 * Only privileged processes can execute this
819 * for FILES or LDAP
820 */
821 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
822 ((retval = ckuid()) != SUCCESS))
823 return (FAIL);
824 if (flag & (MUTEXFLAG|NONAGEFLAG|STFLAG)) {
825 rusage(); /* exit */
826 retval = BADOPT;
827 return (FAIL);
828 }
829 flag |= LFLAG;
830 attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL);
831 break;
832
833 case 'u': /* unlock the password */
834
835 /* if no repository the default for -u is files */
836 if (repository.type == NULL)
837 repository = __REPFILES;
838
839 if (IS_FILES(repository) == FALSE &&
840 IS_LDAP(repository) == FALSE) {
841 (void) fprintf(stderr, gettext(
842 "-u only applies to files or ldap "
843 "repository\n"));
844 rusage(); /* exit */
845 retval = BADOPT;
846 return (FAIL);
847 }
848
849 /*
850 * Only privileged processes can execute this
851 * for FILES or LDAP
852 */
853 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
854 ((retval = ckuid()) != SUCCESS))
855 return (FAIL);
856 if (flag & (MUTEXFLAG|NONAGEFLAG|STFLAG)) {
857 rusage(); /* exit */
858 retval = BADOPT;
859 return (FAIL);
860 }
861 flag |= UFLAG;
862 attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL);
863 attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL);
864 break;
865
866 case 'x': /* set the max date */
867
868 /* if no repository the default for -x is files */
869 if (repository.type == NULL)
870 repository = __REPFILES;
871
872 if (IS_FILES(repository) == FALSE &&
873 IS_LDAP(repository) == FALSE) {
874 (void) fprintf(stderr, gettext(
875 "-x only applies to files or ldap "
876 "repository\n"));
877 rusage(); /* exit */
878 retval = BADSYN;
879 return (FAIL);
880 }
881
882 /*
883 * Only privileged process can execute this
884 * for FILES or LDAP
885 */
886 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
887 (ckuid() != SUCCESS)) {
888 retval = NOPERM;
889 return (FAIL);
890 }
891 if (flag & (SAFLAG|MFLAG|NONAGEFLAG|STFLAG)) {
892 retval = BADOPT;
893 return (FAIL);
894 }
895 flag |= MFLAG;
896 if ((int)strlen(optarg) <= 0 ||
897 (maxdate = strtol(optarg, &char_p, 10)) < -1 ||
898 *char_p != '\0') {
899 (void) fprintf(stderr, "%s: %s -x\n",
900 prognamep, gettext(MSG_NV));
901 retval = BADSYN;
902 return (FAIL);
903 }
904 attrlist_add(attributes, ATTR_MAX, optarg);
905 break;
906
907 case 'n': /* set the min date */
908
909 /* if no repository the default for -n is files */
910 if (repository.type == NULL)
911 repository = __REPFILES;
912
913 if (IS_FILES(repository) == FALSE &&
914 IS_LDAP(repository) == FALSE) {
915 (void) fprintf(stderr, gettext(
916 "-n only applies to files or ldap "
917 "repository\n"));
918 rusage(); /* exit */
919 retval = BADSYN;
920 return (FAIL);
921 }
922
923 /*
924 * Only privileged process can execute this
925 * for FILES or LDAP
926 */
927 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
928 ((retval = ckuid()) != SUCCESS))
929 return (FAIL);
930 if (flag & (SAFLAG|NFLAG|NONAGEFLAG|STFLAG)) {
931 retval = BADOPT;
932 return (FAIL);
933 }
934 flag |= NFLAG;
935 if ((int)strlen(optarg) <= 0 ||
936 (strtol(optarg, &char_p, 10)) < 0 ||
937 *char_p != '\0') {
938 (void) fprintf(stderr, "%s: %s -n\n",
939 prognamep, gettext(MSG_NV));
940 retval = BADSYN;
941 return (FAIL);
942 }
943 attrlist_add(attributes, ATTR_MIN, optarg);
944 break;
945
946 case 'w': /* set the warning field */
947
948 /* if no repository the default for -w is files */
949 if (repository.type == NULL)
950 repository = __REPFILES;
951
952 if (IS_FILES(repository) == FALSE &&
953 IS_LDAP(repository) == FALSE) {
954 (void) fprintf(stderr, gettext(
955 "-w only applies to files or ldap "
956 "repository\n"));
957 rusage(); /* exit */
958 retval = BADSYN;
959 return (FAIL);
960 }
961
962 /*
963 * Only privileged process can execute this
964 * for FILES or LDAP
965 */
966 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
967 (ckuid() != SUCCESS)) {
968 retval = NOPERM;
969 return (FAIL);
970 }
971 if (flag & (SAFLAG|WFLAG|NONAGEFLAG|STFLAG)) {
972 retval = BADOPT;
973 return (FAIL);
974 }
975 flag |= WFLAG;
976 if ((int)strlen(optarg) <= 0 ||
977 (strtol(optarg, &char_p, 10)) < 0 ||
978 *char_p != '\0') {
979 (void) fprintf(stderr, "%s: %s -w\n",
980 prognamep, gettext(MSG_NV));
981 retval = BADSYN;
982 return (FAIL);
983 }
984 attrlist_add(attributes, ATTR_WARN, optarg);
985 break;
986
987 case 's': /* display password attributes */
988
989 /* if no repository the default for -s is files */
990 if (repository.type == NULL)
991 repository = __REPFILES;
992
993
994 /* display password attributes */
995 if (IS_FILES(repository) == FALSE &&
996 IS_LDAP(repository) == FALSE) {
997 (void) fprintf(stderr, gettext(
998 "-s only applies to files or ldap "
999 "repository\n"));
1000 rusage(); /* exit */
1001 retval = BADSYN;
1002 return (FAIL);
1003 }
1004
1005 /*
1006 * Only privileged process can execute this
1007 * for FILES or LDAP
1008 */
1009 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1010 ((retval = ckuid()) != SUCCESS))
1011 return (FAIL);
1012 if (flag && (flag != AFLAG)) {
1013 retval = BADOPT;
1014 return (FAIL);
1015 }
1016 flag |= SFLAG;
1017 break;
1018
1019 case 'a': /* display password attributes */
1020
1021 /* if no repository the default for -a is files */
1022 if (repository.type == NULL)
1023 repository = __REPFILES;
1024
1025 if (IS_FILES(repository) == FALSE &&
1026 IS_LDAP(repository) == FALSE) {
1027 (void) fprintf(stderr, gettext(
1028 "-a only applies to files or ldap "
1029 "repository\n"));
1030 rusage(); /* exit */
1031 retval = BADSYN;
1032 return (FAIL);
1033 }
1034
1035 /*
1036 * Only privileged process can execute this
1037 * for FILES or LDAP
1038 */
1039 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1040 ((retval = ckuid()) != SUCCESS))
1041 return (FAIL);
1042 if (flag && (flag != SFLAG)) {
1043 retval = BADOPT;
1044 return (FAIL);
1045 }
1046 flag |= AFLAG;
1047 break;
1048
1049 case 'f': /* expire password attributes */
1050
1051 /* if no repository the default for -f is files */
1052 if (repository.type == NULL)
1053 repository = __REPFILES;
1054
1055 if (IS_FILES(repository) == FALSE &&
1056 IS_LDAP(repository) == FALSE) {
1057 (void) fprintf(stderr, gettext(
1058 "-f only applies to files or ldap "
1059 "repository\n"));
1060 rusage(); /* exit */
1061 retval = BADSYN;
1062 return (FAIL);
1063 }
1064
1065 /*
1066 * Only privileged process can execute this
1067 * for FILES or LDAP
1068 */
1069 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1070 ((retval = ckuid()) != SUCCESS))
1071 return (FAIL);
1072 if (flag & (SAFLAG|FFLAG|NONAGEFLAG|STFLAG)) {
1073 retval = BADOPT;
1074 return (FAIL);
1075 }
1076 flag |= FFLAG;
1077 attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL);
1078 break;
1079
1080 case 'e': /* change login shell */
1081
1082 /* if no repository the default for -e is files */
1083 if (repository.type == NULL)
1084 repository = __REPFILES;
1085
1086 if (flag & (EFLAG|SAFLAG|AGEFLAG|STFLAG)) {
1087 retval = BADOPT;
1088 return (FAIL);
1089 }
1090 flag |= EFLAG;
1091 break;
1092
1093 case 'g': /* change gecos information */
1094
1095 /* if no repository the default for -g is files */
1096 if (repository.type == NULL)
1097 repository = __REPFILES;
1098
1099 /*
1100 * Only privileged process can execute this
1101 * for FILES
1102 */
1103 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1104 retval = NOPERM;
1105 return (FAIL);
1106 }
1107 if (flag & (GFLAG|SAFLAG|AGEFLAG|STFLAG)) {
1108 retval = BADOPT;
1109 return (FAIL);
1110 }
1111 flag |= GFLAG;
1112 break;
1113
1114 case 'h': /* change home dir */
1115
1116 /* if no repository the default for -h is files */
1117 if (repository.type == NULL)
1118 repository = __REPFILES;
1119 /*
1120 * Only privileged process can execute this
1121 * for FILES
1122 */
1123 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1124 retval = NOPERM;
1125 return (FAIL);
1126 }
1127 if (IS_NIS(repository)) {
1128 (void) fprintf(stderr, "%s\n",
1129 gettext(MSG_NIS_HOMEDIR));
1130 retval = BADSYN;
1131 return (FAIL);
1132 }
1133
1134 if (flag & (HFLAG|SAFLAG|AGEFLAG|STFLAG)) {
1135 retval = BADOPT;
1136 return (FAIL);
1137 }
1138 flag |= HFLAG;
1139 break;
1140 case 'S':
1141 if (ckuid() != SUCCESS) {
1142 retval = NOPERM;
1143 return (FAIL);
1144 }
1145 if (flag & (MUTEXFLAG|NONAGEFLAG|AGEFLAG)) {
1146 rusage(); /* exit */
1147 retval = BADOPT;
1148 return (FAIL);
1149 }
1150 flag |= STFLAG;
1151 break;
1152 case '?':
1153 rusage();
1154 retval = BADOPT;
1155 return (FAIL);
1156 }
1157 }
1158
1159 argc -= optind;
1160 if (argc > 1) {
1161 rusage();
1162 retval = BADSYN;
1163 return (FAIL);
1164 }
1165
1166 /* Make sure (EXPIRE comes after (MAX comes after MIN)) */
1167 attrlist_reorder(attributes);
1168
1169 /* If no options are specified or only the show option */
1170 /* is specified, return because no option error checking */
1171 /* is needed */
1172 if (!flag || (flag == SFLAG))
1173 return (flag);
1174
1175 /* AFLAG must be used with SFLAG */
1176 if (flag == AFLAG) {
1177 rusage();
1178 retval = BADSYN;
1179 return (FAIL);
1180 }
1181
1182 if (flag != SAFLAG && argc < 1) {
1183 /*
1184 * user name is not specified (argc<1), it can't be
1185 * aging info update.
1186 */
1187 if (!(flag & NONAGEFLAG)) {
1188 rusage();
1189 retval = BADSYN;
1190 return (FAIL);
1191 }
1192 }
1193
1194 /* user name(s) may not be specified when SAFLAG is used. */
1195 if (flag == SAFLAG && argc >= 1) {
1196 rusage();
1197 retval = BADSYN;
1198 return (FAIL);
1199 }
1200
1201 /*
1202 * If aging is being turned off (maxdate == -1), mindate may not
1203 * be specified.
1204 */
1205 if ((maxdate == -1) && (flag & NFLAG)) {
1206 (void) fprintf(stderr, "%s: %s -n\n",
1207 prognamep, gettext(MSG_NV));
1208 retval = BADOPT;
1209 return (FAIL);
1210 }
1211
1212 return (flag);
1213 }
1214
1215 /*
1216 *
1217 * ckuid():
1218 * This function returns SUCCESS if the caller is root, else
1219 * it returns NOPERM.
1220 *
1221 */
1222
1223 static int
1224 ckuid(void)
1225 {
1226 if (uid != 0) {
1227 return (retval = NOPERM);
1228 }
1229 return (SUCCESS);
1230 }
1231
1232
1233 /*
1234 * get_attr()
1235 */
1236 int
1237 get_attr(char *username, pwu_repository_t *repository, attrlist **attributes)
1238 {
1239 int res;
1240
1241 attrlist_add(attributes, ATTR_PASSWD, NULL);
1242 attrlist_add(attributes, ATTR_LSTCHG, "0");
1243 attrlist_add(attributes, ATTR_MIN, "0");
1244 attrlist_add(attributes, ATTR_MAX, "0");
1245 attrlist_add(attributes, ATTR_WARN, "0");
1246
1247 res = __get_authtoken_attr(username, repository, *attributes);
1248
1249 if (res == PWU_SUCCESS) {
1250 retval = SUCCESS;
1251 return (PWU_SUCCESS);
1252 }
1253
1254 if (res == PWU_NOT_FOUND)
1255 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
1256 username);
1257
1258 retval = NOPERM;
1259 passwd_exit(retval);
1260 /*NOTREACHED*/
1261 }
1262
1263 /*
1264 * display_attr():
1265 * This function prints out the password attributes of a usr
1266 * onto standand output.
1267 */
1268 void
1269 display_attr(char *usrname, attrlist *attributes)
1270 {
1271 char *status = NULL;
1272 char *passwd;
1273 long lstchg;
1274 int min = 0, max = 0, warn = 0;
1275
1276 while (attributes) {
1277 switch (attributes->type) {
1278 case ATTR_PASSWD:
1279 passwd = attributes->data.val_s;
1280 if (passwd == NULL || *passwd == '\0')
1281 status = "NP ";
1282 else if (strncmp(passwd, LOCKSTRING,
1283 sizeof (LOCKSTRING)-1) == 0)
1284 status = "LK ";
1285 else if (strncmp(passwd, NOLOGINSTRING,
1286 sizeof (NOLOGINSTRING)-1) == 0)
1287 status = "NL ";
1288 else if ((strlen(passwd) == 13 && passwd[0] != '$') ||
1289 passwd[0] == '$')
1290 status = "PS ";
1291 else
1292 status = "UN ";
1293 break;
1294 case ATTR_LSTCHG:
1295 lstchg = attributes->data.val_i * DAY;
1296 break;
1297 case ATTR_MIN:
1298 min = attributes->data.val_i;
1299 break;
1300 case ATTR_MAX:
1301 max = attributes->data.val_i;
1302 break;
1303 case ATTR_WARN:
1304 warn = attributes->data.val_i;
1305 break;
1306 default:
1307 break;
1308 }
1309 attributes = attributes->next;
1310 }
1311 (void) fprintf(stdout, "%-8s ", usrname);
1312
1313 if (status)
1314 (void) fprintf(stdout, "%s ", status);
1315
1316 if (max != -1) {
1317 if (lstchg == 0) {
1318 (void) fprintf(stdout, "00/00/00 ");
1319 } else {
1320 struct tm *tmp;
1321 tmp = gmtime(&lstchg);
1322 (void) fprintf(stdout, "%.2d/%.2d/%.2d ",
1323 tmp->tm_mon + 1,
1324 tmp->tm_mday,
1325 tmp->tm_year % 100);
1326 }
1327 (void) fprintf(stdout, (min >= 0) ? "%4d " : " ", min);
1328 (void) fprintf(stdout, "%4d ", max);
1329 (void) fprintf(stdout, (warn > 0) ? "%4d " : " ", warn);
1330 }
1331 (void) fprintf(stdout, "\n");
1332 }
1333
1334 void
1335 free_attr(attrlist *attributes)
1336 {
1337 while (attributes) {
1338 if (attributes->type == ATTR_PASSWD)
1339 free(attributes->data.val_s);
1340 attributes = attributes->next;
1341 }
1342 }
1343
1344 /*
1345 *
1346 * get_namelist_files():
1347 * This function gets a list of user names on the system from
1348 * the /etc/passwd file.
1349 *
1350 */
1351 int
1352 get_namelist_files(char ***namelist_p, int *num_user)
1353 {
1354 FILE *pwfp;
1355 struct passwd *pwd;
1356 int max_user;
1357 int nuser;
1358 char **nl;
1359
1360 nuser = 0;
1361 errno = 0;
1362 pwd = NULL;
1363
1364 if ((pwfp = fopen(PASSWD, "r")) == NULL)
1365 return (NOPERM);
1366
1367 /*
1368 * find out the actual number of entries in the PASSWD file
1369 */
1370 max_user = 1; /* need one slot for terminator NULL */
1371 while ((pwd = fgetpwent(pwfp)) != NULL)
1372 max_user++;
1373
1374 /*
1375 * reset the file stream pointer
1376 */
1377 rewind(pwfp);
1378
1379 nl = (char **)calloc(max_user, (sizeof (char *)));
1380 if (nl == NULL) {
1381 (void) fclose(pwfp);
1382 return (FMERR);
1383 }
1384
1385 while ((pwd = fgetpwent(pwfp)) != NULL) {
1386 if ((nl[nuser] = strdup(pwd->pw_name)) == NULL) {
1387 (void) fclose(pwfp);
1388 return (FMERR);
1389 }
1390 nuser++;
1391 }
1392
1393 nl[nuser] = NULL;
1394 *num_user = nuser;
1395 *namelist_p = nl;
1396 (void) fclose(pwfp);
1397 return (SUCCESS);
1398 }
1399
1400 /*
1401 * get_namelist_local
1402 *
1403 */
1404
1405 /*
1406 * Our private version of the switch frontend for getspent. We want
1407 * to search just the ldap sp file, so we want to bypass
1408 * normal nsswitch.conf based processing. This implementation
1409 * compatible with version 2 of the name service switch.
1410 */
1411 #define NSS_LDAP_ONLY "ldap"
1412
1413 extern int str2spwd(const char *, int, void *, char *, int);
1414
1415 static DEFINE_NSS_DB_ROOT(db_root);
1416 static DEFINE_NSS_GETENT(context);
1417
1418 static char *local_config;
1419 static void
1420 _lc_nss_initf_shadow(nss_db_params_t *p)
1421 {
1422 p->name = NSS_DBNAM_SHADOW;
1423 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
1424 p->default_config = local_config; /* Use ldap only */
1425 p->flags = NSS_USE_DEFAULT_CONFIG;
1426 }
1427
1428 static void
1429 _lc_setspent(void)
1430 {
1431 nss_setent(&db_root, _lc_nss_initf_shadow, &context);
1432 }
1433
1434 static void
1435 _lc_endspent(void)
1436 {
1437 nss_endent(&db_root, _lc_nss_initf_shadow, &context);
1438 nss_delete(&db_root);
1439 }
1440
1441 static struct spwd *
1442 _lc_getspent_r(struct spwd *result, char *buffer, int buflen)
1443 {
1444 nss_XbyY_args_t arg;
1445 char *nam;
1446
1447 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */
1448
1449 do {
1450 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd);
1451 /* No key to fill in */
1452 (void) nss_getent(&db_root, _lc_nss_initf_shadow, &context,
1453 &arg);
1454 } while (arg.returnval != 0 &&
1455 (nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 &&
1456 (*nam == '+' || *nam == '-'));
1457
1458 return (struct spwd *)NSS_XbyY_FINI(&arg);
1459 }
1460
1461 static nss_XbyY_buf_t *buffer;
1462
1463 static struct spwd *
1464 _lc_getspent(void)
1465 {
1466 nss_XbyY_buf_t *b;
1467
1468 b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW);
1469
1470 return (b == 0 ? 0 : _lc_getspent_r(b->result, b->buffer, b->buflen));
1471 }
1472
1473 int
1474 get_namelist_local(char ***namelist_p, int *num_user)
1475 {
1476 int nuser = 0;
1477 int alloced = 100;
1478 char **nl;
1479 struct spwd *p;
1480
1481
1482 if ((nl = calloc(alloced, sizeof (*nl))) == NULL)
1483 return (FMERR);
1484
1485 (void) _lc_setspent();
1486 while ((p = _lc_getspent()) != NULL) {
1487 if ((nl[nuser] = strdup(p->sp_namp)) == NULL) {
1488 _lc_endspent();
1489 return (FMERR);
1490 }
1491 if (++nuser == alloced) {
1492 alloced += 100;
1493 nl = realloc(nl, alloced * (sizeof (*nl)));
1494 if (nl == NULL) {
1495 _lc_endspent();
1496 return (FMERR);
1497 }
1498 }
1499 }
1500 (void) _lc_endspent();
1501 nl[nuser] = NULL;
1502
1503 *namelist_p = nl;
1504 *num_user = nuser; /* including NULL */
1505
1506 return (SUCCESS);
1507 }
1508
1509 int
1510 get_namelist(pwu_repository_t repository, char ***namelist, int *num_user)
1511 {
1512 if (IS_LDAP(repository)) {
1513 local_config = NSS_LDAP_ONLY;
1514 return (get_namelist_local(namelist, num_user));
1515 } else if (IS_FILES(repository))
1516 return (get_namelist_files(namelist, num_user));
1517
1518 rusage();
1519 return (BADSYN);
1520 }
1521
1522 /*
1523 *
1524 * passwd_exit():
1525 * This function will call exit() with appropriate exit code
1526 * according to the input "retcode" value.
1527 * It also calls pam_end() to clean-up buffers before exit.
1528 *
1529 */
1530
1531 void
1532 passwd_exit(int retcode)
1533 {
1534
1535 if (pamh)
1536 (void) pam_end(pamh, pam_retval);
1537
1538 switch (retcode) {
1539 case SUCCESS:
1540 break;
1541 case NOPERM:
1542 (void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1543 break;
1544 case BADOPT:
1545 (void) fprintf(stderr, "%s\n", gettext(MSG_BS));
1546 break;
1547 case FMERR:
1548 (void) fprintf(stderr, "%s\n", gettext(MSG_FE));
1549 break;
1550 case FATAL:
1551 (void) fprintf(stderr, "%s\n", gettext(MSG_FF));
1552 break;
1553 case FBUSY:
1554 (void) fprintf(stderr, "%s\n", gettext(MSG_FB));
1555 break;
1556 case BADSYN:
1557 (void) fprintf(stderr, "%s\n", gettext(MSG_NV));
1558 break;
1559 case BADAGE:
1560 (void) fprintf(stderr, "%s\n", gettext(MSG_AD));
1561 break;
1562 case NOMEM:
1563 (void) fprintf(stderr, "%s\n", gettext(MSG_NM));
1564 break;
1565 default:
1566 (void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1567 retcode = NOPERM;
1568 break;
1569 }
1570 /* write password record */
1571 if (event != NULL) {
1572 struct passwd *pass;
1573
1574 if ((pass = getpwnam(usrname)) == NULL) {
1575 /* unlikely to ever get here, but ... */
1576 event->adt_passwd.username = usrname;
1577 } else if (pass->pw_uid != uid) {
1578 /* save target user */
1579 event->adt_passwd.uid = pass->pw_uid;
1580 event->adt_passwd.username = pass->pw_name;
1581 }
1582
1583 if (adt_put_event(event,
1584 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
1585 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
1586 pam_retval) != 0) {
1587 adt_free_event(event);
1588 (void) adt_end_session(ah);
1589 perror("adt_put_event");
1590 exit(retcode);
1591 }
1592 adt_free_event(event);
1593 }
1594 (void) adt_end_session(ah);
1595 exit(retcode);
1596 }
1597
1598 /*
1599 *
1600 * passwd_conv():
1601 * This is the conv (conversation) function called from
1602 * a PAM authentication module to print error messages
1603 * or garner information from the user.
1604 *
1605 */
1606
1607 /*ARGSUSED*/
1608 static int
1609 passwd_conv(int num_msg, struct pam_message **msg,
1610 struct pam_response **response, void *appdata_ptr)
1611 {
1612 struct pam_message *m;
1613 struct pam_response *r;
1614 char *temp;
1615 int k, i;
1616
1617 if (num_msg <= 0)
1618 return (PAM_CONV_ERR);
1619
1620 *response = (struct pam_response *)calloc(num_msg,
1621 sizeof (struct pam_response));
1622 if (*response == NULL)
1623 return (PAM_BUF_ERR);
1624
1625 k = num_msg;
1626 m = *msg;
1627 r = *response;
1628 while (k--) {
1629
1630 switch (m->msg_style) {
1631
1632 case PAM_PROMPT_ECHO_OFF:
1633 temp = getpassphrase(m->msg);
1634 if (temp != NULL) {
1635 r->resp = strdup(temp);
1636 (void) memset(temp, 0, strlen(temp));
1637 if (r->resp == NULL) {
1638 /* free responses */
1639 r = *response;
1640 for (i = 0; i < num_msg; i++, r++) {
1641 if (r->resp)
1642 free(r->resp);
1643 }
1644 free(*response);
1645 *response = NULL;
1646 return (PAM_BUF_ERR);
1647 }
1648 }
1649 m++;
1650 r++;
1651 break;
1652
1653 case PAM_PROMPT_ECHO_ON:
1654 if (m->msg != NULL) {
1655 (void) fputs(m->msg, stdout);
1656 }
1657 r->resp = (char *)calloc(PAM_MAX_RESP_SIZE,
1658 sizeof (char));
1659 if (r->resp == NULL) {
1660 /* free responses */
1661 r = *response;
1662 for (i = 0; i < num_msg; i++, r++) {
1663 if (r->resp)
1664 free(r->resp);
1665 }
1666 free(*response);
1667 *response = NULL;
1668 return (PAM_BUF_ERR);
1669 }
1670 if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
1671 int len = strlen(r->resp);
1672 if (r->resp[len-1] == '\n')
1673 r->resp[len-1] = '\0';
1674 }
1675 m++;
1676 r++;
1677 break;
1678
1679 case PAM_ERROR_MSG:
1680 if (m->msg != NULL) {
1681 (void) fputs(m->msg, stderr);
1682 (void) fputs("\n", stderr);
1683 }
1684 m++;
1685 r++;
1686 break;
1687 case PAM_TEXT_INFO:
1688 if (m->msg != NULL) {
1689 (void) fputs(m->msg, stdout);
1690 (void) fputs("\n", stdout);
1691 }
1692 m++;
1693 r++;
1694 break;
1695
1696 default:
1697 break;
1698 }
1699 }
1700 return (PAM_SUCCESS);
1701 }
1702
1703 /*
1704 *
1705 * stdin_conv():
1706 * This is the conv function called for reading
1707 * the password from standard input.
1708 *
1709 */
1710 /*ARGSUSED*/
1711 static int
1712 stdin_conv(int num_msg, struct pam_message **msg,
1713 struct pam_response **response, void *appdata_ptr)
1714 {
1715 struct pam_response *reply = NULL;
1716 int replies = 0;
1717
1718 if (num_msg <= 0)
1719 return (PAM_CONV_ERR);
1720
1721 reply = calloc(num_msg, sizeof (struct pam_response));
1722 if (reply == NULL)
1723 return (PAM_BUF_ERR);
1724
1725 for (replies = 0; replies < num_msg; replies ++) {
1726 reply[replies].resp = NULL;
1727 reply[replies].resp_retcode = PAM_SUCCESS;
1728 switch (msg[replies]->msg_style) {
1729 case PAM_PROMPT_ECHO_OFF:
1730 reply[replies].resp = strdup(appdata_ptr);
1731 if (reply[replies].resp == NULL)
1732 goto err;
1733 break;
1734 case PAM_PROMPT_ECHO_ON:
1735 case PAM_ERROR_MSG:
1736 case PAM_TEXT_INFO:
1737 reply[replies].resp = strdup("");
1738 if (reply[replies].resp == NULL)
1739 goto err;
1740 break;
1741 default:
1742 free(reply);
1743 return (PAM_CONV_ERR);
1744 }
1745 }
1746 *response = reply;
1747 return (PAM_SUCCESS);
1748
1749 err:
1750 free(reply);
1751 return (PAM_BUF_ERR);
1752 }
1753
1754 /*
1755 * Utilities Functions
1756 */
1757
1758 /*
1759 * int attrlist_add(attrlist **l, attrtype type, char *val)
1760 * add an item, with type "type" and value "val", at the tail of list l.
1761 * This functions exits the application on OutOfMem error.
1762 */
1763 void
1764 attrlist_add(attrlist **l, attrtype type, char *val)
1765 {
1766 attrlist **w;
1767
1768 /* tail insert */
1769 for (w = l; *w != NULL; w = &(*w)->next)
1770 ;
1771
1772 if ((*w = malloc(sizeof (**w))) == NULL)
1773 passwd_exit(NOMEM);
1774
1775 (*w)->type = type;
1776 (*w)->next = NULL;
1777
1778 switch (type) {
1779 case ATTR_MIN:
1780 case ATTR_WARN:
1781 case ATTR_MAX:
1782 (*w)->data.val_i = atoi(val);
1783 break;
1784 default:
1785 (*w)->data.val_s = val;
1786 break;
1787 }
1788 }
1789
1790 /*
1791 * attrlist_reorder(attrlist **l)
1792 * Make sure that
1793 * - if EXPIRE and MAX or MIN is set, EXPIRE comes after MAX/MIN
1794 * - if both MIN and MAX are set, MAX comes before MIN.
1795 */
1796
1797 static void
1798 attrlist_reorder(attrlist **l)
1799 {
1800 attrlist **w;
1801 attrlist *exp = NULL; /* ATTR_EXPIRE_PASSWORD, if found */
1802 attrlist *max = NULL; /* ATTR_MAX, if found */
1803
1804 if (*l == NULL || (*l)->next == NULL)
1805 return; /* order of list with <= one item is ok */
1806
1807 /*
1808 * We simply walk the list, take off the EXPIRE and MAX items if
1809 * they appear, and put them (first MAX, them EXPIRE) at the end
1810 * of the list.
1811 */
1812 w = l;
1813 while (*w != NULL) {
1814 if ((*w)->type == ATTR_EXPIRE_PASSWORD) {
1815 exp = *w;
1816 *w = (*w)->next;
1817 } else if ((*w)->type == ATTR_MAX) {
1818 max = *w;
1819 *w = (*w)->next;
1820 } else
1821 w = &(*w)->next;
1822 }
1823
1824 /* 'w' points to the address of the 'next' field of the last element */
1825
1826 if (max) {
1827 *w = max;
1828 w = &max->next;
1829 }
1830 if (exp) {
1831 *w = exp;
1832 w = &exp->next;
1833 }
1834 *w = NULL;
1835 }
1836
1837 void
1838 rusage(void)
1839 {
1840
1841 #define MSG(a) (void) fprintf(stderr, gettext((a)));
1842
1843 MSG("usage:\n");
1844 MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n");
1845 MSG("\tpasswd [-r files] [-egh] [name]\n");
1846 MSG("\tpasswd [-r files] -sa\n");
1847 MSG("\tpasswd [-r files] -s [name]\n");
1848 MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
1849 "[-x max] name\n");
1850 MSG("\tpasswd -r nis [-eg] [name]\n");
1851 MSG("\t\t[-x max] name\n");
1852 MSG("\tpasswd -r ldap [-egh] [name]\n");
1853 MSG("\tpasswd -r ldap -sa\n");
1854 MSG("\tpasswd -r ldap -s [name]\n");
1855 MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "
1856 "[-x max] name\n");
1857 MSG("\tpasswd -S [name]\n");
1858 #undef MSG
1859 }