dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / passwd / passwd.c
bloba99f0f6928be2f93440a85b088a1b6e3202b2381
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
29 /* Copyright (c) 1987, 1988 Microsoft Corporation */
30 /* All Rights Reserved */
33 * passwd is a program whose sole purpose is to manage
34 * the password file, map, or table. It allows system administrator
35 * to add, change and display password attributes.
36 * Non privileged user can change password or display
37 * password attributes which corresponds to their login name.
40 #include <stdio.h>
41 #include <pwd.h>
42 #include <sys/types.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <stdlib.h>
46 #include <locale.h>
47 #include <stdarg.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <security/pam_appl.h>
51 #include <security/pam_modules.h>
52 #include <security/pam_impl.h>
53 #include <rpcsvc/nis.h>
54 #undef GROUP
55 #include <syslog.h>
56 #include <userdefs.h>
57 #include <passwdutil.h>
59 #include <nss_dbdefs.h>
61 #include <deflt.h>
63 #undef GROUP
64 #include <bsm/adt.h>
65 #include <bsm/adt_event.h>
68 * flags indicate password attributes to be modified
71 #define LFLAG 0x001 /* lock user's password */
72 #define DFLAG 0x002 /* delete user's password */
73 #define MFLAG 0x004 /* set max field -- # of days passwd is valid */
74 #define NFLAG 0x008 /* set min field -- # of days between */
75 /* password changes */
76 #define SFLAG 0x010 /* display password attributes */
77 #define FFLAG 0x020 /* expire user's password */
78 #define AFLAG 0x040 /* display password attributes for all users */
79 #define SAFLAG (SFLAG|AFLAG) /* display password attributes for all users */
80 #define WFLAG 0x100 /* warn user to change passwd */
81 #define OFLAG 0x200 /* domain name */
82 #define EFLAG 0x400 /* change shell */
83 #define GFLAG 0x800 /* change gecos information */
84 #define HFLAG 0x1000 /* change home directory */
85 #define XFLAG 0x2000 /* no login */
86 #define UFLAG 0x4000 /* unlock user's password */
88 #define NONAGEFLAG (EFLAG | GFLAG | HFLAG)
89 #define AGEFLAG (LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG)
90 #define MUTEXFLAG (DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG)
94 * exit code
97 #define SUCCESS 0 /* succeeded */
98 #define NOPERM 1 /* No permission */
99 #define BADOPT 2 /* Invalid combination of option */
100 #define FMERR 3 /* File/table manipulation error */
101 #define FATAL 4 /* Old file/table can not be recovered */
102 #define FBUSY 5 /* Lock file/table busy */
103 #define BADSYN 6 /* Incorrect syntax */
104 #define BADAGE 7 /* Aging is disabled */
105 #define NOMEM 8 /* No memory */
106 #define SYSERR 9 /* System error */
107 #define EXPIRED 10 /* Account expired */
110 * define error messages
112 #define MSG_NP "Permission denied"
113 #define MSG_BS "Invalid combination of options"
114 #define MSG_FE "Unexpected failure. Password file/table unchanged."
115 #define MSG_FF "Unexpected failure. Password file/table missing."
116 #define MSG_FB "Password file/table busy. Try again later."
117 #define MSG_NV "Invalid argument to option"
118 #define MSG_AD "Password aging is disabled"
119 #define MSG_RS "Cannot change from restricted shell %s\n"
120 #define MSG_NM "Out of memory."
121 #define MSG_UNACCEPT "%s is unacceptable as a new shell\n"
122 #define MSG_UNAVAIL "warning: %s is unavailable on this machine\n"
123 #define MSG_COLON "':' is not allowed.\n"
124 #define MSG_MAXLEN "Maximum number of characters allowed is %d."
125 #define MSG_CONTROL "Control characters are not allowed.\n"
126 #define MSG_SHELL_UNCHANGED "Login shell unchanged.\n"
127 #define MSG_GECOS_UNCHANGED "Finger information unchanged.\n"
128 #define MSG_DIR_UNCHANGED "Homedir information unchanged.\n"
129 #define MSG_NAME "\nName [%s]: "
130 #define MSG_HOMEDIR "\nHome Directory [%s]: "
131 #define MSG_OLDSHELL "Old shell: %s\n"
132 #define MSG_NEWSHELL "New shell: "
133 #define MSG_AGAIN "\nPlease try again\n"
134 #define MSG_INPUTHDR "Default values are printed inside of '[]'.\n" \
135 "To accept the default, type <return>.\n" \
136 "To have a blank entry, type the word 'none'.\n"
137 #define MSG_UNKNOWN "%s: User unknown: %s\n"
138 #define MSG_ACCOUNT_EXP "User account has expired: %s\n"
139 #define MSG_AUTHTOK_EXP "Your password has been expired for too long.\n" \
140 "Please contact the system administrator.\n"
141 #define MSG_NIS_HOMEDIR "-h does not apply to NIS"
142 #define MSG_CUR_PASS "Enter existing login password: "
143 #define MSG_CUR_PASS_UNAME "Enter %s's existing login password: "
144 #define MSG_SUCCESS "%s: password information changed for %s\n"
145 #define MSG_SORRY "%s: Sorry, wrong passwd\n"
146 #define MSG_INFO "%s: Changing password for %s\n"
150 * return code from ckarg() routine
152 #define FAIL -1
155 * defind password file name
157 #define PASSWD "/etc/passwd"
159 #define MAX_INPUT_LEN 512
161 #define DEF_ATTEMPTS 3
163 /* Number of characters in that make up an encrypted password (for now) */
164 #define NUMCP 13
166 #ifdef DEBUG
167 #define dprintf1 printf
168 #else
169 #define dprintf1(w, x)
170 #endif
172 extern int optind;
174 static int retval = SUCCESS;
175 static int pam_retval = PAM_SUCCESS;
176 static uid_t uid;
177 static char *prognamep;
178 static long maxdate; /* password aging information */
179 static int passwd_conv(int, struct pam_message **,
180 struct pam_response **, void *);
181 static struct pam_conv pam_conv = {passwd_conv, NULL};
182 static pam_handle_t *pamh; /* Authentication handle */
183 static char *usrname; /* user whose attribute we update */
184 static adt_session_data_t *ah; /* audit session handle */
185 static adt_event_data_t *event = NULL; /* event to be generated */
187 static pam_repository_t auth_rep;
188 static pwu_repository_t repository;
189 static pwu_repository_t __REPFILES = { "files", NULL, 0 };
192 * Function Declarations
195 extern void setusershell(void);
196 extern char *getusershell(void);
197 extern void endusershell(void);
198 extern struct passwd *fgetpwent(FILE *);
200 static void passwd_exit(int retcode) __NORETURN;
201 static void rusage(void);
202 static int ckuid(void);
203 static int ckarg(int argc, char **argv, attrlist **attributes);
205 static int get_namelist(pwu_repository_t, char ***, int *);
206 static int get_namelist_files(char ***, int *);
207 static int get_namelist_local(char ***, int *);
208 static int get_attr(char *, pwu_repository_t *, attrlist **);
209 static void display_attr(char *, attrlist *);
210 static void free_attr(attrlist *);
211 static void attrlist_add(attrlist **, attrtype, char *);
212 static void attrlist_reorder(attrlist **);
213 static char *userinput(char *, pwu_repository_t *, attrtype);
214 static char *getresponse(char *);
217 * main():
218 * The main routine will call ckarg() to parse the command line
219 * arguments and call the appropriate functions to perform the
220 * tasks specified by the arguments. It allows system
221 * administrator to add, change and display password attributes.
222 * Non privileged user can change password or display
223 * password attributes which corresponds to their login name.
227 main(int argc, char *argv[])
230 int flag;
231 char **namelist;
232 int num_user;
233 int i;
234 attrlist *attributes = NULL;
235 char *input;
236 int tries = 1;
237 int updated_reps;
240 if ((prognamep = strrchr(argv[0], '/')) != NULL)
241 ++prognamep;
242 else
243 prognamep = argv[0];
245 auth_rep.type = NULL;
246 auth_rep.scope = NULL;
247 repository.type = NULL;
248 repository.scope = NULL;
249 repository.scope_len = 0;
252 /* initialization for variables, set locale and textdomain */
253 i = 0;
254 flag = 0;
256 uid = getuid(); /* get the user id */
257 (void) setlocale(LC_ALL, "");
259 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
260 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
261 #endif
262 (void) textdomain(TEXT_DOMAIN);
265 * ckarg() parses the arguments. In case of an error,
266 * it sets the retval and returns FAIL (-1).
269 flag = ckarg(argc, argv, &attributes);
270 dprintf1("flag is %0x\n", flag);
271 if (flag == FAIL)
272 passwd_exit(retval);
274 argc -= optind;
276 if (argc < 1) {
277 if ((usrname = getlogin()) == NULL) {
278 struct passwd *pass = getpwuid(uid);
279 if (pass != NULL)
280 usrname = pass->pw_name;
281 else {
282 rusage();
283 exit(NOPERM);
285 } else if (flag == 0) {
287 * If flag is zero, change passwd.
288 * Otherwise, it will display or
289 * modify password aging attributes
291 (void) fprintf(stderr, gettext(MSG_INFO), prognamep,
292 usrname);
294 } else {
295 usrname = argv[optind];
298 if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS) {
299 passwd_exit(NOPERM);
302 auth_rep.type = repository.type;
303 auth_rep.scope = repository.scope;
304 auth_rep.scope_len = repository.scope_len;
306 if (auth_rep.type != NULL) {
307 if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep)
308 != PAM_SUCCESS) {
309 passwd_exit(NOPERM);
313 if (flag == SAFLAG) { /* display password attributes for all users */
314 retval = get_namelist(repository, &namelist, &num_user);
315 if (retval != SUCCESS)
316 (void) passwd_exit(retval);
318 if (num_user == 0) {
319 (void) fprintf(stderr, "%s: %s\n", prognamep,
320 gettext(MSG_FF));
321 passwd_exit(FATAL);
323 i = 0;
324 while (namelist[i] != NULL) {
325 (void) get_attr(namelist[i], &repository,
326 &attributes);
327 (void) display_attr(namelist[i], attributes);
328 (void) free(namelist[i]);
329 (void) free_attr(attributes);
330 i++;
332 (void) free(namelist);
333 passwd_exit(SUCCESS);
334 } else if (flag == SFLAG) { /* display password attributes by user */
335 if (get_attr(usrname, &repository, &attributes) ==
336 PWU_SUCCESS) {
337 (void) display_attr(usrname, attributes);
338 (void) free_attr(attributes);
340 passwd_exit(SUCCESS);
341 /* NOT REACHED */
345 switch (pam_authenticate(pamh, 0)) {
346 case PAM_SUCCESS:
347 break;
348 case PAM_USER_UNKNOWN:
349 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
350 usrname);
351 passwd_exit(NOPERM);
352 break;
353 case PAM_PERM_DENIED:
354 passwd_exit(NOPERM);
355 break;
356 case PAM_AUTH_ERR:
357 (void) fprintf(stderr, gettext(MSG_SORRY), prognamep);
358 passwd_exit(NOPERM);
359 break;
360 default:
361 /* system error */
362 passwd_exit(FMERR);
363 break;
366 if (flag == 0) { /* changing user password */
367 int chk_authtok = 0; /* check password strength */
369 dprintf1("call pam_chauthtok() repository name =%s\n",
370 repository.type);
372 /* Set up for Audit */
373 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
374 perror("adt_start_session");
375 passwd_exit(SYSERR);
377 if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
378 perror("adt_alloc_event");
379 passwd_exit(NOMEM);
382 /* Don't check account expiration when invoked by root */
383 if (ckuid() != SUCCESS) {
384 pam_retval = pam_acct_mgmt(pamh, PAM_SILENT);
385 switch (pam_retval) {
386 case PAM_ACCT_EXPIRED:
387 (void) fprintf(stderr,
388 gettext(MSG_ACCOUNT_EXP), usrname);
389 passwd_exit(EXPIRED);
390 break;
391 case PAM_AUTHTOK_EXPIRED:
392 (void) fprintf(stderr,
393 gettext(MSG_AUTHTOK_EXP));
394 passwd_exit(NOPERM);
395 break;
396 case PAM_NEW_AUTHTOK_REQD:
397 /* valid error when changing passwords */
398 break;
399 case PAM_SUCCESS:
400 /* Ok to change password */
401 break;
402 default:
403 passwd_exit(NOPERM);
408 pam_retval = PAM_AUTHTOK_ERR;
409 tries = 1;
410 if (ckuid() == SUCCESS) {
411 /* bypass password strength checks */
412 chk_authtok = PAM_NO_AUTHTOK_CHECK;
415 while (pam_retval == PAM_AUTHTOK_ERR && tries <= DEF_ATTEMPTS) {
416 if (tries > 1)
417 (void) printf(gettext(MSG_AGAIN));
418 pam_retval = pam_chauthtok(pamh, chk_authtok);
419 if (pam_retval == PAM_TRY_AGAIN) {
420 (void) sleep(1);
421 pam_retval = pam_chauthtok(pamh, chk_authtok);
423 tries++;
426 switch (pam_retval) {
427 case PAM_SUCCESS:
428 retval = SUCCESS;
429 break;
430 case PAM_AUTHTOK_DISABLE_AGING:
431 retval = BADAGE;
432 break;
433 case PAM_AUTHTOK_LOCK_BUSY:
434 retval = FBUSY;
435 break;
436 case PAM_TRY_AGAIN:
437 retval = FBUSY;
438 break;
439 case PAM_AUTHTOK_ERR:
440 case PAM_AUTHTOK_RECOVERY_ERR:
441 default:
442 retval = NOPERM;
443 break;
446 (void) passwd_exit(retval);
447 /* NOT REACHED */
448 } else { /* changing attributes */
449 switch (flag) {
450 case EFLAG: /* changing user password attributes */
451 input = userinput(usrname, &repository, ATTR_SHELL);
452 if (input)
453 attrlist_add(&attributes, ATTR_SHELL, input);
454 else
455 (void) printf(gettext(MSG_SHELL_UNCHANGED));
456 break;
457 case GFLAG:
458 input = userinput(usrname, &repository, ATTR_GECOS);
459 if (input)
460 attrlist_add(&attributes, ATTR_GECOS, input);
461 else
462 (void) printf(gettext(MSG_GECOS_UNCHANGED));
463 break;
464 case HFLAG:
465 input = userinput(usrname, &repository, ATTR_HOMEDIR);
466 if (input)
467 attrlist_add(&attributes, ATTR_HOMEDIR, input);
468 else
469 (void) printf(gettext(MSG_DIR_UNCHANGED));
470 break;
473 if (attributes != NULL) {
474 retval = __set_authtoken_attr(usrname,
475 pamh->ps_item[PAM_AUTHTOK].pi_addr,
476 &repository, attributes, &updated_reps);
477 switch (retval) {
478 case PWU_SUCCESS:
479 for (i = 1; i <= REP_LAST; i <<= 1) {
480 if ((updated_reps & i) == 0)
481 continue;
482 (void) printf(gettext(MSG_SUCCESS),
483 prognamep, usrname);
485 retval = SUCCESS;
486 break;
487 case PWU_AGING_DISABLED:
488 retval = BADAGE;
489 break;
490 default:
491 retval = NOPERM;
492 break;
494 } else {
495 retval = SUCCESS; /* nothing to change won't fail */
497 (void) passwd_exit(retval);
499 /* NOTREACHED */
500 return (0);
504 * Get a line of input from the user.
506 * If the line is empty, or the input equals 'oldval', NULL is returned.
507 * therwise, a malloced string containing the input (minus the trailing
508 * newline) is returned.
510 char *
511 getresponse(char *oldval)
513 char resp[MAX_INPUT_LEN];
514 char *retval = NULL;
515 int resplen;
517 (void) fgets(resp, sizeof (resp) - 1, stdin);
518 resplen = strlen(resp) - 1;
519 if (resp[resplen] == '\n')
520 resp[resplen] = '\0';
521 if (*resp != '\0' && strcmp(resp, oldval) != 0)
522 retval = strdup(resp);
523 return (retval);
527 * char *userinput(item)
529 * user conversation function. The old value of attribute "item" is
530 * displayed while the user is asked to provide a new value.
532 * returns a malloc()-ed string if the user actualy provided input
533 * or NULL if the user simply hit return or the input equals the old
534 * value (not changed).
536 char *
537 userinput(char *name, pwu_repository_t *rep, attrtype type)
539 attrlist oldattr;
540 char *oldval; /* shorthand for oldattr.data.val_s */
541 char *valid; /* points to valid shells */
542 char *response;
543 char *cp;
545 oldattr.type = type;
546 oldattr.next = NULL;
548 if (__get_authtoken_attr(name, rep, &oldattr) != PWU_SUCCESS)
549 passwd_exit(FMERR);
551 oldval = oldattr.data.val_s;
553 if (type == ATTR_SHELL) {
554 /* No current shell: set DEFSHL as default choice */
555 if (*oldval == '\0') {
556 free(oldval);
557 oldval = strdup(DEFSHL);
560 if (ckuid() != SUCCESS) {
561 /* User must currently have a valid shell */
562 setusershell();
563 valid = getusershell();
564 while (valid && strcmp(valid, oldval) != 0)
565 valid = getusershell();
566 endusershell();
568 if (valid == NULL) {
569 (void) fprintf(stderr, gettext(MSG_RS), oldval);
570 free(oldval);
571 return (NULL);
574 (void) printf(gettext(MSG_OLDSHELL), oldval);
575 (void) printf(gettext(MSG_NEWSHELL));
576 (void) fflush(stdout);
578 response = getresponse(oldval);
579 free(oldval); /* We don't need the old value anymore */
581 if (response == NULL || *response == '\0')
582 return (NULL);
584 /* Make sure new shell is listed */
585 setusershell();
586 valid = getusershell();
587 while (valid) {
588 char *cp;
590 /* Allow user to give shell without path */
591 if (*response == '/') {
592 cp = valid;
593 } else {
594 if ((cp = strrchr(valid, '/')) == NULL)
595 cp = valid;
596 else
597 cp++;
599 if (strcmp(cp, response) == 0) {
600 if (*response != '/') {
601 /* take shell name including path */
602 free(response);
603 response = strdup(valid);
605 break;
607 valid = getusershell();
609 endusershell();
611 if (valid == NULL) { /* No valid shell matches */
612 (void) fprintf(stderr, gettext(MSG_UNACCEPT), response);
613 return (NULL);
616 if (access(response, X_OK) < 0)
617 (void) fprintf(stderr, gettext(MSG_UNAVAIL), response);
618 return (response);
619 /* NOT REACHED */
622 * if type == SHELL, we have returned by now. Only GECOS and
623 * HOMEDIR get to this point.
625 (void) printf(gettext(MSG_INPUTHDR));
628 * PRE: oldval points to malloced string with Old Value
629 * INV: oldval remains unchanged
630 * POST:response points to valid string or NULL.
632 for (;;) {
633 if (type == ATTR_GECOS)
634 (void) printf(gettext(MSG_NAME), oldval);
635 else if (type == ATTR_HOMEDIR)
636 (void) printf(gettext(MSG_HOMEDIR), oldval);
638 response = getresponse(oldval);
640 if (response && strcmp(response, "none") == 0)
641 *response = '\0';
643 /* No-change or empty string are OK */
644 if (response == NULL || *response == '\0')
645 break;
647 /* Check for illegal characters */
648 if (strchr(response, ':')) {
649 (void) fprintf(stderr, "%s", gettext(MSG_COLON));
650 free(response);
651 } else if (strlen(response) > MAX_INPUT_LEN - 1) {
652 (void) fprintf(stderr, gettext(MSG_MAXLEN),
653 MAX_INPUT_LEN);
654 free(response);
655 } else {
656 /* don't allow control characters */
657 for (cp = response; *cp >= 040; cp++)
659 if (*cp != '\0') {
660 (void) fprintf(stderr, gettext(MSG_CONTROL));
661 free(response);
662 } else
663 break; /* response is a valid string */
666 * We only get here if the input was invalid.
667 * In that case, we again ask the user for input.
670 free(oldval);
671 return (response);
674 * ckarg():
675 * This function parses and verifies the
676 * arguments. It takes three parameters:
677 * argc => # of arguments
678 * argv => pointer to an argument
679 * attrlist => pointer to list of password attributes
682 static int
683 ckarg(int argc, char **argv, attrlist **attributes)
685 extern char *optarg;
686 char *char_p;
687 int opt;
688 int flag;
690 flag = 0;
692 while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:N")) != EOF) {
693 switch (opt) {
695 case 'r': /* Repository Specified */
696 /* repository: this option should be specified first */
698 if (repository.type != NULL) {
699 (void) fprintf(stderr, gettext(
700 "Repository is already defined or specified.\n"));
701 rusage();
702 retval = BADSYN;
703 return (FAIL);
705 if (strcmp(optarg, "nis") == 0) {
706 repository.type = optarg;
707 } else if (strcmp(optarg, "ldap") == 0) {
708 repository.type = optarg;
709 } else if (strcmp(optarg, "files") == 0) {
710 repository.type = optarg;
711 } else {
712 (void) fprintf(stderr,
713 gettext("invalid repository: %s\n"),
714 optarg);
715 rusage();
716 retval = BADSYN;
717 return (FAIL);
719 break;
721 case 'd': /* Delete Auth Token */
722 /* if no repository the default for -d is files */
723 if (repository.type == NULL)
724 repository = __REPFILES;
727 * Delete the password - only privileged processes
728 * can execute this for FILES or LDAP
730 if (IS_FILES(repository) == FALSE &&
731 IS_LDAP(repository) == FALSE) {
732 (void) fprintf(stderr, gettext(
733 "-d only applies to files "
734 "or ldap repository\n"));
735 rusage(); /* exit */
736 retval = BADSYN;
737 return (FAIL);
740 if (ckuid() != SUCCESS) {
741 retval = NOPERM;
742 return (FAIL);
744 if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG)) {
745 rusage();
746 retval = BADOPT;
747 return (FAIL);
749 flag |= DFLAG;
750 attrlist_add(attributes, ATTR_PASSWD, NULL);
751 break;
753 case 'N': /* set account to be "no login" */
755 /* if no repository the default for -N is files */
756 if (repository.type == NULL)
757 repository = __REPFILES;
759 if (IS_FILES(repository) == FALSE &&
760 IS_LDAP(repository) == FALSE) {
761 (void) fprintf(stderr, gettext(
762 "-N only applies to files or ldap "
763 "repository\n"));
764 rusage(); /* exit */
765 retval = BADOPT;
766 return (FAIL);
770 * Only privileged processes can execute this
771 * for FILES or LDAP
773 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
774 ((retval = ckuid()) != SUCCESS))
775 return (FAIL);
776 if (flag & (MUTEXFLAG|NONAGEFLAG)) {
777 rusage(); /* exit */
778 retval = BADOPT;
779 return (FAIL);
781 flag |= XFLAG;
782 attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL);
783 break;
785 case 'l': /* lock the password */
787 /* if no repository the default for -l is files */
788 if (repository.type == NULL)
789 repository = __REPFILES;
791 if (IS_FILES(repository) == FALSE &&
792 IS_LDAP(repository) == FALSE) {
793 (void) fprintf(stderr, gettext(
794 "-l only applies to files or ldap "
795 "repository\n"));
796 rusage(); /* exit */
797 retval = BADOPT;
798 return (FAIL);
802 * Only privileged processes can execute this
803 * for FILES or LDAP
805 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
806 ((retval = ckuid()) != SUCCESS))
807 return (FAIL);
808 if (flag & (MUTEXFLAG|NONAGEFLAG)) {
809 rusage(); /* exit */
810 retval = BADOPT;
811 return (FAIL);
813 flag |= LFLAG;
814 attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL);
815 break;
817 case 'u': /* unlock the password */
819 /* if no repository the default for -u is files */
820 if (repository.type == NULL)
821 repository = __REPFILES;
823 if (IS_FILES(repository) == FALSE &&
824 IS_LDAP(repository) == FALSE) {
825 (void) fprintf(stderr, gettext(
826 "-u only applies to files or ldap "
827 "repository\n"));
828 rusage(); /* exit */
829 retval = BADOPT;
830 return (FAIL);
834 * Only privileged processes can execute this
835 * for FILES or LDAP
837 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
838 ((retval = ckuid()) != SUCCESS))
839 return (FAIL);
840 if (flag & (MUTEXFLAG|NONAGEFLAG)) {
841 rusage(); /* exit */
842 retval = BADOPT;
843 return (FAIL);
845 flag |= UFLAG;
846 attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL);
847 attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL);
848 break;
850 case 'x': /* set the max date */
852 /* if no repository the default for -x is files */
853 if (repository.type == NULL)
854 repository = __REPFILES;
856 if (IS_FILES(repository) == FALSE &&
857 IS_LDAP(repository) == FALSE) {
858 (void) fprintf(stderr, gettext(
859 "-x only applies to files or ldap "
860 "repository\n"));
861 rusage(); /* exit */
862 retval = BADSYN;
863 return (FAIL);
867 * Only privileged process can execute this
868 * for FILES or LDAP
870 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
871 (ckuid() != SUCCESS)) {
872 retval = NOPERM;
873 return (FAIL);
875 if (flag & (SAFLAG|MFLAG|NONAGEFLAG)) {
876 retval = BADOPT;
877 return (FAIL);
879 flag |= MFLAG;
880 if ((int)strlen(optarg) <= 0 ||
881 (maxdate = strtol(optarg, &char_p, 10)) < -1 ||
882 *char_p != '\0') {
883 (void) fprintf(stderr, "%s: %s -x\n",
884 prognamep, gettext(MSG_NV));
885 retval = BADSYN;
886 return (FAIL);
888 attrlist_add(attributes, ATTR_MAX, optarg);
889 break;
891 case 'n': /* set the min date */
893 /* if no repository the default for -n is files */
894 if (repository.type == NULL)
895 repository = __REPFILES;
897 if (IS_FILES(repository) == FALSE &&
898 IS_LDAP(repository) == FALSE) {
899 (void) fprintf(stderr, gettext(
900 "-n only applies to files or ldap "
901 "repository\n"));
902 rusage(); /* exit */
903 retval = BADSYN;
904 return (FAIL);
908 * Only privileged process can execute this
909 * for FILES or LDAP
911 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
912 ((retval = ckuid()) != SUCCESS))
913 return (FAIL);
914 if (flag & (SAFLAG|NFLAG|NONAGEFLAG)) {
915 retval = BADOPT;
916 return (FAIL);
918 flag |= NFLAG;
919 if ((int)strlen(optarg) <= 0 ||
920 (strtol(optarg, &char_p, 10)) < 0 ||
921 *char_p != '\0') {
922 (void) fprintf(stderr, "%s: %s -n\n",
923 prognamep, gettext(MSG_NV));
924 retval = BADSYN;
925 return (FAIL);
927 attrlist_add(attributes, ATTR_MIN, optarg);
928 break;
930 case 'w': /* set the warning field */
932 /* if no repository the default for -w is files */
933 if (repository.type == NULL)
934 repository = __REPFILES;
936 if (IS_FILES(repository) == FALSE &&
937 IS_LDAP(repository) == FALSE) {
938 (void) fprintf(stderr, gettext(
939 "-w only applies to files or ldap "
940 "repository\n"));
941 rusage(); /* exit */
942 retval = BADSYN;
943 return (FAIL);
947 * Only privileged process can execute this
948 * for FILES or LDAP
950 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
951 (ckuid() != SUCCESS)) {
952 retval = NOPERM;
953 return (FAIL);
955 if (flag & (SAFLAG|WFLAG|NONAGEFLAG)) {
956 retval = BADOPT;
957 return (FAIL);
959 flag |= WFLAG;
960 if ((int)strlen(optarg) <= 0 ||
961 (strtol(optarg, &char_p, 10)) < 0 ||
962 *char_p != '\0') {
963 (void) fprintf(stderr, "%s: %s -w\n",
964 prognamep, gettext(MSG_NV));
965 retval = BADSYN;
966 return (FAIL);
968 attrlist_add(attributes, ATTR_WARN, optarg);
969 break;
971 case 's': /* display password attributes */
973 /* if no repository the default for -s is files */
974 if (repository.type == NULL)
975 repository = __REPFILES;
978 /* display password attributes */
979 if (IS_FILES(repository) == FALSE &&
980 IS_LDAP(repository) == FALSE) {
981 (void) fprintf(stderr, gettext(
982 "-s only applies to files or ldap "
983 "repository\n"));
984 rusage(); /* exit */
985 retval = BADSYN;
986 return (FAIL);
990 * Only privileged process can execute this
991 * for FILES or LDAP
993 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
994 ((retval = ckuid()) != SUCCESS))
995 return (FAIL);
996 if (flag && (flag != AFLAG)) {
997 retval = BADOPT;
998 return (FAIL);
1000 flag |= SFLAG;
1001 break;
1003 case 'a': /* display password attributes */
1005 /* if no repository the default for -a is files */
1006 if (repository.type == NULL)
1007 repository = __REPFILES;
1009 if (IS_FILES(repository) == FALSE &&
1010 IS_LDAP(repository) == FALSE) {
1011 (void) fprintf(stderr, gettext(
1012 "-a only applies to files or ldap "
1013 "repository\n"));
1014 rusage(); /* exit */
1015 retval = BADSYN;
1016 return (FAIL);
1020 * Only privileged process can execute this
1021 * for FILES or LDAP
1023 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1024 ((retval = ckuid()) != SUCCESS))
1025 return (FAIL);
1026 if (flag && (flag != SFLAG)) {
1027 retval = BADOPT;
1028 return (FAIL);
1030 flag |= AFLAG;
1031 break;
1033 case 'f': /* expire password attributes */
1035 /* if no repository the default for -f is files */
1036 if (repository.type == NULL)
1037 repository = __REPFILES;
1039 if (IS_FILES(repository) == FALSE &&
1040 IS_LDAP(repository) == FALSE) {
1041 (void) fprintf(stderr, gettext(
1042 "-f only applies to files or ldap "
1043 "repository\n"));
1044 rusage(); /* exit */
1045 retval = BADSYN;
1046 return (FAIL);
1050 * Only privileged process can execute this
1051 * for FILES or LDAP
1053 if ((IS_FILES(repository) || IS_LDAP(repository)) &&
1054 ((retval = ckuid()) != SUCCESS))
1055 return (FAIL);
1056 if (flag & (SAFLAG|FFLAG|NONAGEFLAG)) {
1057 retval = BADOPT;
1058 return (FAIL);
1060 flag |= FFLAG;
1061 attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL);
1062 break;
1064 case 'e': /* change login shell */
1066 /* if no repository the default for -e is files */
1067 if (repository.type == NULL)
1068 repository = __REPFILES;
1070 if (flag & (EFLAG|SAFLAG|AGEFLAG)) {
1071 retval = BADOPT;
1072 return (FAIL);
1074 flag |= EFLAG;
1075 break;
1077 case 'g': /* change gecos information */
1079 /* if no repository the default for -g is files */
1080 if (repository.type == NULL)
1081 repository = __REPFILES;
1084 * Only privileged process can execute this
1085 * for FILES
1087 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1088 retval = NOPERM;
1089 return (FAIL);
1091 if (flag & (GFLAG|SAFLAG|AGEFLAG)) {
1092 retval = BADOPT;
1093 return (FAIL);
1095 flag |= GFLAG;
1096 break;
1098 case 'h': /* change home dir */
1100 /* if no repository the default for -h is files */
1101 if (repository.type == NULL)
1102 repository = __REPFILES;
1104 * Only privileged process can execute this
1105 * for FILES
1107 if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1108 retval = NOPERM;
1109 return (FAIL);
1111 if (IS_NIS(repository)) {
1112 (void) fprintf(stderr, "%s\n",
1113 gettext(MSG_NIS_HOMEDIR));
1114 retval = BADSYN;
1115 return (FAIL);
1118 if (flag & (HFLAG|SAFLAG|AGEFLAG)) {
1119 retval = BADOPT;
1120 return (FAIL);
1122 flag |= HFLAG;
1123 break;
1125 case '?':
1126 rusage();
1127 retval = BADOPT;
1128 return (FAIL);
1132 argc -= optind;
1133 if (argc > 1) {
1134 rusage();
1135 retval = BADSYN;
1136 return (FAIL);
1139 /* Make sure (EXPIRE comes after (MAX comes after MIN)) */
1140 attrlist_reorder(attributes);
1142 /* If no options are specified or only the show option */
1143 /* is specified, return because no option error checking */
1144 /* is needed */
1145 if (!flag || (flag == SFLAG))
1146 return (flag);
1148 /* AFLAG must be used with SFLAG */
1149 if (flag == AFLAG) {
1150 rusage();
1151 retval = BADSYN;
1152 return (FAIL);
1155 if (flag != SAFLAG && argc < 1) {
1157 * user name is not specified (argc<1), it can't be
1158 * aging info update.
1160 if (!(flag & NONAGEFLAG)) {
1161 rusage();
1162 retval = BADSYN;
1163 return (FAIL);
1167 /* user name(s) may not be specified when SAFLAG is used. */
1168 if (flag == SAFLAG && argc >= 1) {
1169 rusage();
1170 retval = BADSYN;
1171 return (FAIL);
1175 * If aging is being turned off (maxdate == -1), mindate may not
1176 * be specified.
1178 if ((maxdate == -1) && (flag & NFLAG)) {
1179 (void) fprintf(stderr, "%s: %s -n\n",
1180 prognamep, gettext(MSG_NV));
1181 retval = BADOPT;
1182 return (FAIL);
1185 return (flag);
1190 * ckuid():
1191 * This function returns SUCCESS if the caller is root, else
1192 * it returns NOPERM.
1196 static int
1197 ckuid(void)
1199 if (uid != 0) {
1200 return (retval = NOPERM);
1202 return (SUCCESS);
1207 * get_attr()
1210 get_attr(char *username, pwu_repository_t *repository, attrlist **attributes)
1212 int res;
1214 attrlist_add(attributes, ATTR_PASSWD, NULL);
1215 attrlist_add(attributes, ATTR_LSTCHG, "0");
1216 attrlist_add(attributes, ATTR_MIN, "0");
1217 attrlist_add(attributes, ATTR_MAX, "0");
1218 attrlist_add(attributes, ATTR_WARN, "0");
1220 res = __get_authtoken_attr(username, repository, *attributes);
1222 if (res == PWU_SUCCESS) {
1223 retval = SUCCESS;
1224 return (PWU_SUCCESS);
1227 if (res == PWU_NOT_FOUND)
1228 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
1229 username);
1231 retval = NOPERM;
1232 passwd_exit(retval);
1233 /*NOTREACHED*/
1237 * display_attr():
1238 * This function prints out the password attributes of a usr
1239 * onto standand output.
1241 void
1242 display_attr(char *usrname, attrlist *attributes)
1244 char *status = NULL;
1245 char *passwd;
1246 long lstchg;
1247 int min = 0, max = 0, warn = 0;
1249 while (attributes) {
1250 switch (attributes->type) {
1251 case ATTR_PASSWD:
1252 passwd = attributes->data.val_s;
1253 if (passwd == NULL || *passwd == '\0')
1254 status = "NP ";
1255 else if (strncmp(passwd, LOCKSTRING,
1256 sizeof (LOCKSTRING)-1) == 0)
1257 status = "LK ";
1258 else if (strncmp(passwd, NOLOGINSTRING,
1259 sizeof (NOLOGINSTRING)-1) == 0)
1260 status = "NL ";
1261 else if ((strlen(passwd) == 13 && passwd[0] != '$') ||
1262 passwd[0] == '$')
1263 status = "PS ";
1264 else
1265 status = "UN ";
1266 break;
1267 case ATTR_LSTCHG:
1268 lstchg = attributes->data.val_i * DAY;
1269 break;
1270 case ATTR_MIN:
1271 min = attributes->data.val_i;
1272 break;
1273 case ATTR_MAX:
1274 max = attributes->data.val_i;
1275 break;
1276 case ATTR_WARN:
1277 warn = attributes->data.val_i;
1278 break;
1279 default:
1280 break;
1282 attributes = attributes->next;
1284 (void) fprintf(stdout, "%-8s ", usrname);
1286 if (status)
1287 (void) fprintf(stdout, "%s ", status);
1289 if (max != -1) {
1290 if (lstchg == 0) {
1291 (void) fprintf(stdout, "00/00/00 ");
1292 } else {
1293 struct tm *tmp;
1294 tmp = gmtime(&lstchg);
1295 (void) fprintf(stdout, "%.2d/%.2d/%.2d ",
1296 tmp->tm_mon + 1,
1297 tmp->tm_mday,
1298 tmp->tm_year % 100);
1300 (void) fprintf(stdout, (min >= 0) ? "%4d " : " ", min);
1301 (void) fprintf(stdout, "%4d ", max);
1302 (void) fprintf(stdout, (warn > 0) ? "%4d " : " ", warn);
1304 (void) fprintf(stdout, "\n");
1307 void
1308 free_attr(attrlist *attributes)
1310 while (attributes) {
1311 if (attributes->type == ATTR_PASSWD)
1312 free(attributes->data.val_s);
1313 attributes = attributes->next;
1319 * get_namelist_files():
1320 * This function gets a list of user names on the system from
1321 * the /etc/passwd file.
1325 get_namelist_files(char ***namelist_p, int *num_user)
1327 FILE *pwfp;
1328 struct passwd *pwd;
1329 int max_user;
1330 int nuser;
1331 char **nl;
1333 nuser = 0;
1334 errno = 0;
1335 pwd = NULL;
1337 if ((pwfp = fopen(PASSWD, "r")) == NULL)
1338 return (NOPERM);
1341 * find out the actual number of entries in the PASSWD file
1343 max_user = 1; /* need one slot for terminator NULL */
1344 while ((pwd = fgetpwent(pwfp)) != NULL)
1345 max_user++;
1348 * reset the file stream pointer
1350 rewind(pwfp);
1352 nl = (char **)calloc(max_user, (sizeof (char *)));
1353 if (nl == NULL) {
1354 (void) fclose(pwfp);
1355 return (FMERR);
1358 while ((pwd = fgetpwent(pwfp)) != NULL) {
1359 if ((nl[nuser] = strdup(pwd->pw_name)) == NULL) {
1360 (void) fclose(pwfp);
1361 return (FMERR);
1363 nuser++;
1366 nl[nuser] = NULL;
1367 *num_user = nuser;
1368 *namelist_p = nl;
1369 (void) fclose(pwfp);
1370 return (SUCCESS);
1374 * get_namelist_local
1379 * Our private version of the switch frontend for getspent. We want
1380 * to search just the ldap sp file, so we want to bypass
1381 * normal nsswitch.conf based processing. This implementation
1382 * compatible with version 2 of the name service switch.
1384 #define NSS_LDAP_ONLY "ldap"
1386 extern int str2spwd(const char *, int, void *, char *, int);
1388 static DEFINE_NSS_DB_ROOT(db_root);
1389 static DEFINE_NSS_GETENT(context);
1391 static char *local_config;
1392 static void
1393 _lc_nss_initf_shadow(nss_db_params_t *p)
1395 p->name = NSS_DBNAM_SHADOW;
1396 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
1397 p->default_config = local_config; /* Use ldap only */
1398 p->flags = NSS_USE_DEFAULT_CONFIG;
1401 static void
1402 _lc_setspent(void)
1404 nss_setent(&db_root, _lc_nss_initf_shadow, &context);
1407 static void
1408 _lc_endspent(void)
1410 nss_endent(&db_root, _lc_nss_initf_shadow, &context);
1411 nss_delete(&db_root);
1414 static struct spwd *
1415 _lc_getspent_r(struct spwd *result, char *buffer, int buflen)
1417 nss_XbyY_args_t arg;
1419 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd);
1420 /* No key to fill in */
1421 (void) nss_getent(&db_root, _lc_nss_initf_shadow, &context, &arg);
1422 return (struct spwd *)NSS_XbyY_FINI(&arg);
1425 static nss_XbyY_buf_t *buffer;
1427 static struct spwd *
1428 _lc_getspent(void)
1430 nss_XbyY_buf_t *b;
1432 b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW);
1434 return (b == 0 ? 0 : _lc_getspent_r(b->result, b->buffer, b->buflen));
1438 get_namelist_local(char ***namelist_p, int *num_user)
1440 int nuser = 0;
1441 int alloced = 100;
1442 char **nl;
1443 struct spwd *p;
1446 if ((nl = calloc(alloced, sizeof (*nl))) == NULL)
1447 return (FMERR);
1449 (void) _lc_setspent();
1450 while ((p = _lc_getspent()) != NULL) {
1451 if ((nl[nuser] = strdup(p->sp_namp)) == NULL) {
1452 _lc_endspent();
1453 return (FMERR);
1455 if (++nuser == alloced) {
1456 alloced += 100;
1457 nl = reallocarray(nl, alloced, sizeof (*nl));
1458 if (nl == NULL) {
1459 _lc_endspent();
1460 return (FMERR);
1464 (void) _lc_endspent();
1465 nl[nuser] = NULL;
1467 *namelist_p = nl;
1468 *num_user = nuser; /* including NULL */
1470 return (SUCCESS);
1474 get_namelist(pwu_repository_t repository, char ***namelist, int *num_user)
1476 if (IS_LDAP(repository)) {
1477 local_config = NSS_LDAP_ONLY;
1478 return (get_namelist_local(namelist, num_user));
1479 } else if (IS_FILES(repository))
1480 return (get_namelist_files(namelist, num_user));
1482 rusage();
1483 return (BADSYN);
1488 * passwd_exit():
1489 * This function will call exit() with appropriate exit code
1490 * according to the input "retcode" value.
1491 * It also calls pam_end() to clean-up buffers before exit.
1495 void
1496 passwd_exit(int retcode)
1499 if (pamh)
1500 (void) pam_end(pamh, pam_retval);
1502 switch (retcode) {
1503 case SUCCESS:
1504 break;
1505 case NOPERM:
1506 (void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1507 break;
1508 case BADOPT:
1509 (void) fprintf(stderr, "%s\n", gettext(MSG_BS));
1510 break;
1511 case FMERR:
1512 (void) fprintf(stderr, "%s\n", gettext(MSG_FE));
1513 break;
1514 case FATAL:
1515 (void) fprintf(stderr, "%s\n", gettext(MSG_FF));
1516 break;
1517 case FBUSY:
1518 (void) fprintf(stderr, "%s\n", gettext(MSG_FB));
1519 break;
1520 case BADSYN:
1521 (void) fprintf(stderr, "%s\n", gettext(MSG_NV));
1522 break;
1523 case BADAGE:
1524 (void) fprintf(stderr, "%s\n", gettext(MSG_AD));
1525 break;
1526 case NOMEM:
1527 (void) fprintf(stderr, "%s\n", gettext(MSG_NM));
1528 break;
1529 default:
1530 (void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1531 retcode = NOPERM;
1532 break;
1534 /* write password record */
1535 if (event != NULL) {
1536 struct passwd *pass;
1538 if ((pass = getpwnam(usrname)) == NULL) {
1539 /* unlikely to ever get here, but ... */
1540 event->adt_passwd.username = usrname;
1541 } else if (pass->pw_uid != uid) {
1542 /* save target user */
1543 event->adt_passwd.uid = pass->pw_uid;
1544 event->adt_passwd.username = pass->pw_name;
1547 if (adt_put_event(event,
1548 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
1549 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
1550 pam_retval) != 0) {
1551 adt_free_event(event);
1552 (void) adt_end_session(ah);
1553 perror("adt_put_event");
1554 exit(retcode);
1556 adt_free_event(event);
1558 (void) adt_end_session(ah);
1559 exit(retcode);
1564 * passwd_conv():
1565 * This is the conv (conversation) function called from
1566 * a PAM authentication module to print error messages
1567 * or garner information from the user.
1571 /*ARGSUSED*/
1572 static int
1573 passwd_conv(int num_msg, struct pam_message **msg,
1574 struct pam_response **response, void *appdata_ptr)
1576 struct pam_message *m;
1577 struct pam_response *r;
1578 char *temp;
1579 int k, i;
1581 if (num_msg <= 0)
1582 return (PAM_CONV_ERR);
1584 *response = (struct pam_response *)calloc(num_msg,
1585 sizeof (struct pam_response));
1586 if (*response == NULL)
1587 return (PAM_BUF_ERR);
1589 k = num_msg;
1590 m = *msg;
1591 r = *response;
1592 while (k--) {
1594 switch (m->msg_style) {
1596 case PAM_PROMPT_ECHO_OFF:
1597 temp = getpassphrase(m->msg);
1598 if (temp != NULL) {
1599 r->resp = strdup(temp);
1600 (void) memset(temp, 0, strlen(temp));
1601 if (r->resp == NULL) {
1602 /* free responses */
1603 r = *response;
1604 for (i = 0; i < num_msg; i++, r++) {
1605 free(r->resp);
1607 free(*response);
1608 *response = NULL;
1609 return (PAM_BUF_ERR);
1612 m++;
1613 r++;
1614 break;
1616 case PAM_PROMPT_ECHO_ON:
1617 if (m->msg != NULL) {
1618 (void) fputs(m->msg, stdout);
1620 r->resp = (char *)calloc(PAM_MAX_RESP_SIZE,
1621 sizeof (char));
1622 if (r->resp == NULL) {
1623 /* free responses */
1624 r = *response;
1625 for (i = 0; i < num_msg; i++, r++) {
1626 free(r->resp);
1628 free(*response);
1629 *response = NULL;
1630 return (PAM_BUF_ERR);
1632 if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
1633 int len = strlen(r->resp);
1634 if (r->resp[len-1] == '\n')
1635 r->resp[len-1] = '\0';
1637 m++;
1638 r++;
1639 break;
1641 case PAM_ERROR_MSG:
1642 if (m->msg != NULL) {
1643 (void) fputs(m->msg, stderr);
1644 (void) fputs("\n", stderr);
1646 m++;
1647 r++;
1648 break;
1649 case PAM_TEXT_INFO:
1650 if (m->msg != NULL) {
1651 (void) fputs(m->msg, stdout);
1652 (void) fputs("\n", stdout);
1654 m++;
1655 r++;
1656 break;
1658 default:
1659 break;
1662 return (PAM_SUCCESS);
1666 * Utilities Functions
1670 * int attrlist_add(attrlist **l, attrtype type, char *val)
1671 * add an item, with type "type" and value "val", at the tail of list l.
1672 * This functions exits the application on OutOfMem error.
1674 void
1675 attrlist_add(attrlist **l, attrtype type, char *val)
1677 attrlist **w;
1679 /* tail insert */
1680 for (w = l; *w != NULL; w = &(*w)->next)
1683 if ((*w = malloc(sizeof (**w))) == NULL)
1684 passwd_exit(NOMEM);
1686 (*w)->type = type;
1687 (*w)->next = NULL;
1689 switch (type) {
1690 case ATTR_MIN:
1691 case ATTR_WARN:
1692 case ATTR_MAX:
1693 (*w)->data.val_i = atoi(val);
1694 break;
1695 default:
1696 (*w)->data.val_s = val;
1697 break;
1702 * attrlist_reorder(attrlist **l)
1703 * Make sure that
1704 * - if EXPIRE and MAX or MIN is set, EXPIRE comes after MAX/MIN
1705 * - if both MIN and MAX are set, MAX comes before MIN.
1708 static void
1709 attrlist_reorder(attrlist **l)
1711 attrlist **w;
1712 attrlist *exp = NULL; /* ATTR_EXPIRE_PASSWORD, if found */
1713 attrlist *max = NULL; /* ATTR_MAX, if found */
1715 if (*l == NULL || (*l)->next == NULL)
1716 return; /* order of list with <= one item is ok */
1719 * We simply walk the list, take off the EXPIRE and MAX items if
1720 * they appear, and put them (first MAX, them EXPIRE) at the end
1721 * of the list.
1723 w = l;
1724 while (*w != NULL) {
1725 if ((*w)->type == ATTR_EXPIRE_PASSWORD) {
1726 exp = *w;
1727 *w = (*w)->next;
1728 } else if ((*w)->type == ATTR_MAX) {
1729 max = *w;
1730 *w = (*w)->next;
1731 } else
1732 w = &(*w)->next;
1735 /* 'w' points to the address of the 'next' field of the last element */
1737 if (max) {
1738 *w = max;
1739 w = &max->next;
1741 if (exp) {
1742 *w = exp;
1743 w = &exp->next;
1745 *w = NULL;
1748 void
1749 rusage(void)
1752 #define MSG(a) (void) fprintf(stderr, gettext((a)));
1754 MSG("usage:\n");
1755 MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n");
1756 MSG("\tpasswd [-r files] [-egh] [name]\n");
1757 MSG("\tpasswd [-r files] -sa\n");
1758 MSG("\tpasswd [-r files] -s [name]\n");
1759 MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
1760 "[-x max] name\n");
1761 MSG("\tpasswd -r nis [-eg] [name]\n");
1762 MSG("\t\t[-x max] name\n");
1763 MSG("\tpasswd -r ldap [-egh] [name]\n");
1764 MSG("\tpasswd -r ldap -sa\n");
1765 MSG("\tpasswd -r ldap -s [name]\n");
1766 MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "
1767 "[-x max] name\n");
1768 #undef MSG