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]
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.
42 #include <sys/types.h>
50 #include <security/pam_appl.h>
51 #include <security/pam_modules.h>
52 #include <security/pam_impl.h>
53 #include <rpcsvc/nis.h>
57 #include <passwdutil.h>
59 #include <nss_dbdefs.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)
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
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) */
167 #define dprintf1 printf
169 #define dprintf1(w, x)
174 static int retval
= SUCCESS
;
175 static int pam_retval
= PAM_SUCCESS
;
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);
199 static void passwd_exit(int retcode
) __NORETURN
;
200 static void rusage(void);
201 static int ckuid(void);
202 static int ckarg(int argc
, char **argv
, attrlist
**attributes
);
204 static int get_namelist(pwu_repository_t
, char ***, int *);
205 static int get_namelist_files(char ***, int *);
206 static int get_namelist_local(char ***, int *);
207 static int get_attr(char *, pwu_repository_t
*, attrlist
**);
208 static void display_attr(char *, attrlist
*);
209 static void free_attr(attrlist
*);
210 static void attrlist_add(attrlist
**, attrtype
, char *);
211 static void attrlist_reorder(attrlist
**);
212 static char *userinput(char *, pwu_repository_t
*, attrtype
);
213 static char *getresponse(char *);
217 * The main routine will call ckarg() to parse the command line
218 * arguments and call the appropriate functions to perform the
219 * tasks specified by the arguments. It allows system
220 * administrator to add, change and display password attributes.
221 * Non privileged user can change password or display
222 * password attributes which corresponds to their login name.
226 main(int argc
, char *argv
[])
233 attrlist
*attributes
= NULL
;
239 if ((prognamep
= strrchr(argv
[0], '/')) != NULL
)
244 auth_rep
.type
= NULL
;
245 auth_rep
.scope
= NULL
;
246 repository
.type
= NULL
;
247 repository
.scope
= NULL
;
248 repository
.scope_len
= 0;
251 /* initialization for variables, set locale and textdomain */
255 uid
= getuid(); /* get the user id */
256 (void) setlocale(LC_ALL
, "");
258 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
259 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
261 (void) textdomain(TEXT_DOMAIN
);
264 * ckarg() parses the arguments. In case of an error,
265 * it sets the retval and returns FAIL (-1).
268 flag
= ckarg(argc
, argv
, &attributes
);
269 dprintf1("flag is %0x\n", flag
);
276 if ((usrname
= getlogin()) == NULL
) {
277 struct passwd
*pass
= getpwuid(uid
);
279 usrname
= pass
->pw_name
;
284 } else if (flag
== 0) {
286 * If flag is zero, change passwd.
287 * Otherwise, it will display or
288 * modify password aging attributes
290 (void) fprintf(stderr
, gettext(MSG_INFO
), prognamep
,
294 usrname
= argv
[optind
];
297 if (pam_start("passwd", usrname
, &pam_conv
, &pamh
) != PAM_SUCCESS
) {
301 auth_rep
.type
= repository
.type
;
302 auth_rep
.scope
= repository
.scope
;
303 auth_rep
.scope_len
= repository
.scope_len
;
305 if (auth_rep
.type
!= NULL
) {
306 if (pam_set_item(pamh
, PAM_REPOSITORY
, (void *)&auth_rep
)
312 if (flag
== SAFLAG
) { /* display password attributes for all users */
313 retval
= get_namelist(repository
, &namelist
, &num_user
);
314 if (retval
!= SUCCESS
)
315 (void) passwd_exit(retval
);
318 (void) fprintf(stderr
, "%s: %s\n", prognamep
,
323 while (namelist
[i
] != NULL
) {
324 (void) get_attr(namelist
[i
], &repository
,
326 (void) display_attr(namelist
[i
], attributes
);
327 (void) free(namelist
[i
]);
328 (void) free_attr(attributes
);
331 (void) free(namelist
);
332 passwd_exit(SUCCESS
);
333 } else if (flag
== SFLAG
) { /* display password attributes by user */
334 if (get_attr(usrname
, &repository
, &attributes
) ==
336 (void) display_attr(usrname
, attributes
);
337 (void) free_attr(attributes
);
339 passwd_exit(SUCCESS
);
344 switch (pam_authenticate(pamh
, 0)) {
347 case PAM_USER_UNKNOWN
:
348 (void) fprintf(stderr
, gettext(MSG_UNKNOWN
), prognamep
,
352 case PAM_PERM_DENIED
:
356 (void) fprintf(stderr
, gettext(MSG_SORRY
), prognamep
);
365 if (flag
== 0) { /* changing user password */
366 int chk_authtok
= 0; /* check password strength */
368 dprintf1("call pam_chauthtok() repository name =%s\n",
371 /* Set up for Audit */
372 if (adt_start_session(&ah
, NULL
, ADT_USE_PROC_DATA
) != 0) {
373 perror("adt_start_session");
376 if ((event
= adt_alloc_event(ah
, ADT_passwd
)) == NULL
) {
377 perror("adt_alloc_event");
381 /* Don't check account expiration when invoked by root */
382 if (ckuid() != SUCCESS
) {
383 pam_retval
= pam_acct_mgmt(pamh
, PAM_SILENT
);
384 switch (pam_retval
) {
385 case PAM_ACCT_EXPIRED
:
386 (void) fprintf(stderr
,
387 gettext(MSG_ACCOUNT_EXP
), usrname
);
388 passwd_exit(EXPIRED
);
390 case PAM_AUTHTOK_EXPIRED
:
391 (void) fprintf(stderr
,
392 gettext(MSG_AUTHTOK_EXP
));
395 case PAM_NEW_AUTHTOK_REQD
:
396 /* valid error when changing passwords */
399 /* Ok to change password */
407 pam_retval
= PAM_AUTHTOK_ERR
;
409 if (ckuid() == SUCCESS
) {
410 /* bypass password strength checks */
411 chk_authtok
= PAM_NO_AUTHTOK_CHECK
;
414 while (pam_retval
== PAM_AUTHTOK_ERR
&& tries
<= DEF_ATTEMPTS
) {
416 (void) printf(gettext(MSG_AGAIN
));
417 pam_retval
= pam_chauthtok(pamh
, chk_authtok
);
418 if (pam_retval
== PAM_TRY_AGAIN
) {
420 pam_retval
= pam_chauthtok(pamh
, chk_authtok
);
425 switch (pam_retval
) {
429 case PAM_AUTHTOK_DISABLE_AGING
:
432 case PAM_AUTHTOK_LOCK_BUSY
:
438 case PAM_AUTHTOK_ERR
:
439 case PAM_AUTHTOK_RECOVERY_ERR
:
445 (void) passwd_exit(retval
);
447 } else { /* changing attributes */
449 case EFLAG
: /* changing user password attributes */
450 input
= userinput(usrname
, &repository
, ATTR_SHELL
);
452 attrlist_add(&attributes
, ATTR_SHELL
, input
);
454 (void) printf(gettext(MSG_SHELL_UNCHANGED
));
457 input
= userinput(usrname
, &repository
, ATTR_GECOS
);
459 attrlist_add(&attributes
, ATTR_GECOS
, input
);
461 (void) printf(gettext(MSG_GECOS_UNCHANGED
));
464 input
= userinput(usrname
, &repository
, ATTR_HOMEDIR
);
466 attrlist_add(&attributes
, ATTR_HOMEDIR
, input
);
468 (void) printf(gettext(MSG_DIR_UNCHANGED
));
472 if (attributes
!= NULL
) {
473 retval
= __set_authtoken_attr(usrname
,
474 pamh
->ps_item
[PAM_AUTHTOK
].pi_addr
,
475 &repository
, attributes
, &updated_reps
);
478 for (i
= 1; i
<= REP_LAST
; i
<<= 1) {
479 if ((updated_reps
& i
) == 0)
481 (void) printf(gettext(MSG_SUCCESS
),
486 case PWU_AGING_DISABLED
:
494 retval
= SUCCESS
; /* nothing to change won't fail */
496 (void) passwd_exit(retval
);
503 * Get a line of input from the user.
505 * If the line is empty, or the input equals 'oldval', NULL is returned.
506 * therwise, a malloced string containing the input (minus the trailing
507 * newline) is returned.
510 getresponse(char *oldval
)
512 char resp
[MAX_INPUT_LEN
];
516 (void) fgets(resp
, sizeof (resp
) - 1, stdin
);
517 resplen
= strlen(resp
) - 1;
518 if (resp
[resplen
] == '\n')
519 resp
[resplen
] = '\0';
520 if (*resp
!= '\0' && strcmp(resp
, oldval
) != 0)
521 retval
= strdup(resp
);
526 * char *userinput(item)
528 * user conversation function. The old value of attribute "item" is
529 * displayed while the user is asked to provide a new value.
531 * returns a malloc()-ed string if the user actualy provided input
532 * or NULL if the user simply hit return or the input equals the old
533 * value (not changed).
536 userinput(char *name
, pwu_repository_t
*rep
, attrtype type
)
539 char *oldval
; /* shorthand for oldattr.data.val_s */
540 char *valid
; /* points to valid shells */
547 if (__get_authtoken_attr(name
, rep
, &oldattr
) != PWU_SUCCESS
)
550 oldval
= oldattr
.data
.val_s
;
552 if (type
== ATTR_SHELL
) {
553 /* No current shell: set DEFSHL as default choice */
554 if (*oldval
== '\0') {
556 oldval
= strdup(DEFSHL
);
559 if (ckuid() != SUCCESS
) {
560 /* User must currently have a valid shell */
562 valid
= getusershell();
563 while (valid
&& strcmp(valid
, oldval
) != 0)
564 valid
= getusershell();
568 (void) fprintf(stderr
, gettext(MSG_RS
), oldval
);
573 (void) printf(gettext(MSG_OLDSHELL
), oldval
);
574 (void) printf(gettext(MSG_NEWSHELL
));
575 (void) fflush(stdout
);
577 response
= getresponse(oldval
);
578 free(oldval
); /* We don't need the old value anymore */
580 if (response
== NULL
|| *response
== '\0')
583 /* Make sure new shell is listed */
585 valid
= getusershell();
589 /* Allow user to give shell without path */
590 if (*response
== '/') {
593 if ((cp
= strrchr(valid
, '/')) == NULL
)
598 if (strcmp(cp
, response
) == 0) {
599 if (*response
!= '/') {
600 /* take shell name including path */
602 response
= strdup(valid
);
606 valid
= getusershell();
610 if (valid
== NULL
) { /* No valid shell matches */
611 (void) fprintf(stderr
, gettext(MSG_UNACCEPT
), response
);
615 if (access(response
, X_OK
) < 0)
616 (void) fprintf(stderr
, gettext(MSG_UNAVAIL
), response
);
621 * if type == SHELL, we have returned by now. Only GECOS and
622 * HOMEDIR get to this point.
624 (void) printf(gettext(MSG_INPUTHDR
));
627 * PRE: oldval points to malloced string with Old Value
628 * INV: oldval remains unchanged
629 * POST:response points to valid string or NULL.
632 if (type
== ATTR_GECOS
)
633 (void) printf(gettext(MSG_NAME
), oldval
);
634 else if (type
== ATTR_HOMEDIR
)
635 (void) printf(gettext(MSG_HOMEDIR
), oldval
);
637 response
= getresponse(oldval
);
639 if (response
&& strcmp(response
, "none") == 0)
642 /* No-change or empty string are OK */
643 if (response
== NULL
|| *response
== '\0')
646 /* Check for illegal characters */
647 if (strchr(response
, ':')) {
648 (void) fprintf(stderr
, "%s", gettext(MSG_COLON
));
650 } else if (strlen(response
) > MAX_INPUT_LEN
- 1) {
651 (void) fprintf(stderr
, gettext(MSG_MAXLEN
),
655 /* don't allow control characters */
656 for (cp
= response
; *cp
>= 040; cp
++)
659 (void) fprintf(stderr
, gettext(MSG_CONTROL
));
662 break; /* response is a valid string */
665 * We only get here if the input was invalid.
666 * In that case, we again ask the user for input.
674 * This function parses and verifies the
675 * arguments. It takes three parameters:
676 * argc => # of arguments
677 * argv => pointer to an argument
678 * attrlist => pointer to list of password attributes
682 ckarg(int argc
, char **argv
, attrlist
**attributes
)
691 while ((opt
= getopt(argc
, argv
, "r:aldefghsux:n:w:N")) != EOF
) {
694 case 'r': /* Repository Specified */
695 /* repository: this option should be specified first */
697 if (repository
.type
!= NULL
) {
698 (void) fprintf(stderr
, gettext(
699 "Repository is already defined or specified.\n"));
704 if (strcmp(optarg
, "nis") == 0) {
705 repository
.type
= optarg
;
706 } else if (strcmp(optarg
, "ldap") == 0) {
707 repository
.type
= optarg
;
708 } else if (strcmp(optarg
, "files") == 0) {
709 repository
.type
= optarg
;
711 (void) fprintf(stderr
,
712 gettext("invalid repository: %s\n"),
720 case 'd': /* Delete Auth Token */
721 /* if no repository the default for -d is files */
722 if (repository
.type
== NULL
)
723 repository
= __REPFILES
;
726 * Delete the password - only privileged processes
727 * can execute this for FILES or LDAP
729 if (IS_FILES(repository
) == FALSE
&&
730 IS_LDAP(repository
) == FALSE
) {
731 (void) fprintf(stderr
, gettext(
732 "-d only applies to files "
733 "or ldap repository\n"));
739 if (ckuid() != SUCCESS
) {
743 if (flag
& (LFLAG
|SAFLAG
|DFLAG
|XFLAG
|UFLAG
)) {
749 attrlist_add(attributes
, ATTR_PASSWD
, NULL
);
752 case 'N': /* set account to be "no login" */
754 /* if no repository the default for -N is files */
755 if (repository
.type
== NULL
)
756 repository
= __REPFILES
;
758 if (IS_FILES(repository
) == FALSE
&&
759 IS_LDAP(repository
) == FALSE
) {
760 (void) fprintf(stderr
, gettext(
761 "-N only applies to files or ldap "
769 * Only privileged processes can execute this
772 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
773 ((retval
= ckuid()) != SUCCESS
))
775 if (flag
& (MUTEXFLAG
|NONAGEFLAG
)) {
781 attrlist_add(attributes
, ATTR_NOLOGIN_ACCOUNT
, NULL
);
784 case 'l': /* lock the password */
786 /* if no repository the default for -l is files */
787 if (repository
.type
== NULL
)
788 repository
= __REPFILES
;
790 if (IS_FILES(repository
) == FALSE
&&
791 IS_LDAP(repository
) == FALSE
) {
792 (void) fprintf(stderr
, gettext(
793 "-l only applies to files or ldap "
801 * Only privileged processes can execute this
804 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
805 ((retval
= ckuid()) != SUCCESS
))
807 if (flag
& (MUTEXFLAG
|NONAGEFLAG
)) {
813 attrlist_add(attributes
, ATTR_LOCK_ACCOUNT
, NULL
);
816 case 'u': /* unlock the password */
818 /* if no repository the default for -u is files */
819 if (repository
.type
== NULL
)
820 repository
= __REPFILES
;
822 if (IS_FILES(repository
) == FALSE
&&
823 IS_LDAP(repository
) == FALSE
) {
824 (void) fprintf(stderr
, gettext(
825 "-u only applies to files or ldap "
833 * Only privileged processes can execute this
836 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
837 ((retval
= ckuid()) != SUCCESS
))
839 if (flag
& (MUTEXFLAG
|NONAGEFLAG
)) {
845 attrlist_add(attributes
, ATTR_UNLOCK_ACCOUNT
, NULL
);
846 attrlist_add(attributes
, ATTR_RST_FAILED_LOGINS
, NULL
);
849 case 'x': /* set the max date */
851 /* if no repository the default for -x is files */
852 if (repository
.type
== NULL
)
853 repository
= __REPFILES
;
855 if (IS_FILES(repository
) == FALSE
&&
856 IS_LDAP(repository
) == FALSE
) {
857 (void) fprintf(stderr
, gettext(
858 "-x only applies to files or ldap "
866 * Only privileged process can execute this
869 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
870 (ckuid() != SUCCESS
)) {
874 if (flag
& (SAFLAG
|MFLAG
|NONAGEFLAG
)) {
879 if ((int)strlen(optarg
) <= 0 ||
880 (maxdate
= strtol(optarg
, &char_p
, 10)) < -1 ||
882 (void) fprintf(stderr
, "%s: %s -x\n",
883 prognamep
, gettext(MSG_NV
));
887 attrlist_add(attributes
, ATTR_MAX
, optarg
);
890 case 'n': /* set the min date */
892 /* if no repository the default for -n is files */
893 if (repository
.type
== NULL
)
894 repository
= __REPFILES
;
896 if (IS_FILES(repository
) == FALSE
&&
897 IS_LDAP(repository
) == FALSE
) {
898 (void) fprintf(stderr
, gettext(
899 "-n only applies to files or ldap "
907 * Only privileged process can execute this
910 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
911 ((retval
= ckuid()) != SUCCESS
))
913 if (flag
& (SAFLAG
|NFLAG
|NONAGEFLAG
)) {
918 if ((int)strlen(optarg
) <= 0 ||
919 (strtol(optarg
, &char_p
, 10)) < 0 ||
921 (void) fprintf(stderr
, "%s: %s -n\n",
922 prognamep
, gettext(MSG_NV
));
926 attrlist_add(attributes
, ATTR_MIN
, optarg
);
929 case 'w': /* set the warning field */
931 /* if no repository the default for -w is files */
932 if (repository
.type
== NULL
)
933 repository
= __REPFILES
;
935 if (IS_FILES(repository
) == FALSE
&&
936 IS_LDAP(repository
) == FALSE
) {
937 (void) fprintf(stderr
, gettext(
938 "-w only applies to files or ldap "
946 * Only privileged process can execute this
949 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
950 (ckuid() != SUCCESS
)) {
954 if (flag
& (SAFLAG
|WFLAG
|NONAGEFLAG
)) {
959 if ((int)strlen(optarg
) <= 0 ||
960 (strtol(optarg
, &char_p
, 10)) < 0 ||
962 (void) fprintf(stderr
, "%s: %s -w\n",
963 prognamep
, gettext(MSG_NV
));
967 attrlist_add(attributes
, ATTR_WARN
, optarg
);
970 case 's': /* display password attributes */
972 /* if no repository the default for -s is files */
973 if (repository
.type
== NULL
)
974 repository
= __REPFILES
;
977 /* display password attributes */
978 if (IS_FILES(repository
) == FALSE
&&
979 IS_LDAP(repository
) == FALSE
) {
980 (void) fprintf(stderr
, gettext(
981 "-s only applies to files or ldap "
989 * Only privileged process can execute this
992 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
993 ((retval
= ckuid()) != SUCCESS
))
995 if (flag
&& (flag
!= AFLAG
)) {
1002 case 'a': /* display password attributes */
1004 /* if no repository the default for -a is files */
1005 if (repository
.type
== NULL
)
1006 repository
= __REPFILES
;
1008 if (IS_FILES(repository
) == FALSE
&&
1009 IS_LDAP(repository
) == FALSE
) {
1010 (void) fprintf(stderr
, gettext(
1011 "-a only applies to files or ldap "
1013 rusage(); /* exit */
1019 * Only privileged process can execute this
1022 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
1023 ((retval
= ckuid()) != SUCCESS
))
1025 if (flag
&& (flag
!= SFLAG
)) {
1032 case 'f': /* expire password attributes */
1034 /* if no repository the default for -f is files */
1035 if (repository
.type
== NULL
)
1036 repository
= __REPFILES
;
1038 if (IS_FILES(repository
) == FALSE
&&
1039 IS_LDAP(repository
) == FALSE
) {
1040 (void) fprintf(stderr
, gettext(
1041 "-f only applies to files or ldap "
1043 rusage(); /* exit */
1049 * Only privileged process can execute this
1052 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
1053 ((retval
= ckuid()) != SUCCESS
))
1055 if (flag
& (SAFLAG
|FFLAG
|NONAGEFLAG
)) {
1060 attrlist_add(attributes
, ATTR_EXPIRE_PASSWORD
, NULL
);
1063 case 'e': /* change login shell */
1065 /* if no repository the default for -e is files */
1066 if (repository
.type
== NULL
)
1067 repository
= __REPFILES
;
1069 if (flag
& (EFLAG
|SAFLAG
|AGEFLAG
)) {
1076 case 'g': /* change gecos information */
1078 /* if no repository the default for -g is files */
1079 if (repository
.type
== NULL
)
1080 repository
= __REPFILES
;
1083 * Only privileged process can execute this
1086 if (IS_FILES(repository
) && (ckuid() != SUCCESS
)) {
1090 if (flag
& (GFLAG
|SAFLAG
|AGEFLAG
)) {
1097 case 'h': /* change home dir */
1099 /* if no repository the default for -h is files */
1100 if (repository
.type
== NULL
)
1101 repository
= __REPFILES
;
1103 * Only privileged process can execute this
1106 if (IS_FILES(repository
) && (ckuid() != SUCCESS
)) {
1110 if (IS_NIS(repository
)) {
1111 (void) fprintf(stderr
, "%s\n",
1112 gettext(MSG_NIS_HOMEDIR
));
1117 if (flag
& (HFLAG
|SAFLAG
|AGEFLAG
)) {
1138 /* Make sure (EXPIRE comes after (MAX comes after MIN)) */
1139 attrlist_reorder(attributes
);
1141 /* If no options are specified or only the show option */
1142 /* is specified, return because no option error checking */
1144 if (!flag
|| (flag
== SFLAG
))
1147 /* AFLAG must be used with SFLAG */
1148 if (flag
== AFLAG
) {
1154 if (flag
!= SAFLAG
&& argc
< 1) {
1156 * user name is not specified (argc<1), it can't be
1157 * aging info update.
1159 if (!(flag
& NONAGEFLAG
)) {
1166 /* user name(s) may not be specified when SAFLAG is used. */
1167 if (flag
== SAFLAG
&& argc
>= 1) {
1174 * If aging is being turned off (maxdate == -1), mindate may not
1177 if ((maxdate
== -1) && (flag
& NFLAG
)) {
1178 (void) fprintf(stderr
, "%s: %s -n\n",
1179 prognamep
, gettext(MSG_NV
));
1190 * This function returns SUCCESS if the caller is root, else
1191 * it returns NOPERM.
1199 return (retval
= NOPERM
);
1209 get_attr(char *username
, pwu_repository_t
*repository
, attrlist
**attributes
)
1213 attrlist_add(attributes
, ATTR_PASSWD
, NULL
);
1214 attrlist_add(attributes
, ATTR_LSTCHG
, "0");
1215 attrlist_add(attributes
, ATTR_MIN
, "0");
1216 attrlist_add(attributes
, ATTR_MAX
, "0");
1217 attrlist_add(attributes
, ATTR_WARN
, "0");
1219 res
= __get_authtoken_attr(username
, repository
, *attributes
);
1221 if (res
== PWU_SUCCESS
) {
1223 return (PWU_SUCCESS
);
1226 if (res
== PWU_NOT_FOUND
)
1227 (void) fprintf(stderr
, gettext(MSG_UNKNOWN
), prognamep
,
1231 passwd_exit(retval
);
1237 * This function prints out the password attributes of a usr
1238 * onto standand output.
1241 display_attr(char *usrname
, attrlist
*attributes
)
1243 char *status
= NULL
;
1246 int min
= 0, max
= 0, warn
= 0;
1248 while (attributes
) {
1249 switch (attributes
->type
) {
1251 passwd
= attributes
->data
.val_s
;
1252 if (passwd
== NULL
|| *passwd
== '\0')
1254 else if (strncmp(passwd
, LOCKSTRING
,
1255 sizeof (LOCKSTRING
)-1) == 0)
1257 else if (strncmp(passwd
, NOLOGINSTRING
,
1258 sizeof (NOLOGINSTRING
)-1) == 0)
1260 else if ((strlen(passwd
) == 13 && passwd
[0] != '$') ||
1267 lstchg
= attributes
->data
.val_i
* DAY
;
1270 min
= attributes
->data
.val_i
;
1273 max
= attributes
->data
.val_i
;
1276 warn
= attributes
->data
.val_i
;
1281 attributes
= attributes
->next
;
1283 (void) fprintf(stdout
, "%-8s ", usrname
);
1286 (void) fprintf(stdout
, "%s ", status
);
1290 (void) fprintf(stdout
, "00/00/00 ");
1293 tmp
= gmtime(&lstchg
);
1294 (void) fprintf(stdout
, "%.2d/%.2d/%.2d ",
1297 tmp
->tm_year
% 100);
1299 (void) fprintf(stdout
, (min
>= 0) ? "%4d " : " ", min
);
1300 (void) fprintf(stdout
, "%4d ", max
);
1301 (void) fprintf(stdout
, (warn
> 0) ? "%4d " : " ", warn
);
1303 (void) fprintf(stdout
, "\n");
1307 free_attr(attrlist
*attributes
)
1309 while (attributes
) {
1310 if (attributes
->type
== ATTR_PASSWD
)
1311 free(attributes
->data
.val_s
);
1312 attributes
= attributes
->next
;
1318 * get_namelist_files():
1319 * This function gets a list of user names on the system from
1320 * the /etc/passwd file.
1324 get_namelist_files(char ***namelist_p
, int *num_user
)
1336 if ((pwfp
= fopen(PASSWD
, "r")) == NULL
)
1340 * find out the actual number of entries in the PASSWD file
1342 max_user
= 1; /* need one slot for terminator NULL */
1343 while ((pwd
= fgetpwent(pwfp
)) != NULL
)
1347 * reset the file stream pointer
1351 nl
= (char **)calloc(max_user
, (sizeof (char *)));
1353 (void) fclose(pwfp
);
1357 while ((pwd
= fgetpwent(pwfp
)) != NULL
) {
1358 if ((nl
[nuser
] = strdup(pwd
->pw_name
)) == NULL
) {
1359 (void) fclose(pwfp
);
1368 (void) fclose(pwfp
);
1373 * get_namelist_local
1378 * Our private version of the switch frontend for getspent. We want
1379 * to search just the ldap sp file, so we want to bypass
1380 * normal nsswitch.conf based processing. This implementation
1381 * compatible with version 2 of the name service switch.
1383 #define NSS_LDAP_ONLY "ldap"
1385 extern int str2spwd(const char *, int, void *, char *, int);
1387 static DEFINE_NSS_DB_ROOT(db_root
);
1388 static DEFINE_NSS_GETENT(context
);
1390 static char *local_config
;
1392 _lc_nss_initf_shadow(nss_db_params_t
*p
)
1394 p
->name
= NSS_DBNAM_SHADOW
;
1395 p
->config_name
= NSS_DBNAM_PASSWD
; /* Use config for "passwd" */
1396 p
->default_config
= local_config
; /* Use ldap only */
1397 p
->flags
= NSS_USE_DEFAULT_CONFIG
;
1403 nss_setent(&db_root
, _lc_nss_initf_shadow
, &context
);
1409 nss_endent(&db_root
, _lc_nss_initf_shadow
, &context
);
1410 nss_delete(&db_root
);
1413 static struct spwd
*
1414 _lc_getspent_r(struct spwd
*result
, char *buffer
, int buflen
)
1416 nss_XbyY_args_t arg
;
1419 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */
1422 NSS_XbyY_INIT(&arg
, result
, buffer
, buflen
, str2spwd
);
1423 /* No key to fill in */
1424 (void) nss_getent(&db_root
, _lc_nss_initf_shadow
, &context
,
1426 } while (arg
.returnval
!= 0 &&
1427 (nam
= ((struct spwd
*)arg
.returnval
)->sp_namp
) != 0 &&
1428 (*nam
== '+' || *nam
== '-'));
1430 return (struct spwd
*)NSS_XbyY_FINI(&arg
);
1433 static nss_XbyY_buf_t
*buffer
;
1435 static struct spwd
*
1440 b
= NSS_XbyY_ALLOC(&buffer
, sizeof (struct spwd
), NSS_BUFLEN_SHADOW
);
1442 return (b
== 0 ? 0 : _lc_getspent_r(b
->result
, b
->buffer
, b
->buflen
));
1446 get_namelist_local(char ***namelist_p
, int *num_user
)
1454 if ((nl
= calloc(alloced
, sizeof (*nl
))) == NULL
)
1457 (void) _lc_setspent();
1458 while ((p
= _lc_getspent()) != NULL
) {
1459 if ((nl
[nuser
] = strdup(p
->sp_namp
)) == NULL
) {
1463 if (++nuser
== alloced
) {
1465 nl
= realloc(nl
, alloced
* (sizeof (*nl
)));
1472 (void) _lc_endspent();
1476 *num_user
= nuser
; /* including NULL */
1482 get_namelist(pwu_repository_t repository
, char ***namelist
, int *num_user
)
1484 if (IS_LDAP(repository
)) {
1485 local_config
= NSS_LDAP_ONLY
;
1486 return (get_namelist_local(namelist
, num_user
));
1487 } else if (IS_FILES(repository
))
1488 return (get_namelist_files(namelist
, num_user
));
1497 * This function will call exit() with appropriate exit code
1498 * according to the input "retcode" value.
1499 * It also calls pam_end() to clean-up buffers before exit.
1504 passwd_exit(int retcode
)
1508 (void) pam_end(pamh
, pam_retval
);
1514 (void) fprintf(stderr
, "%s\n", gettext(MSG_NP
));
1517 (void) fprintf(stderr
, "%s\n", gettext(MSG_BS
));
1520 (void) fprintf(stderr
, "%s\n", gettext(MSG_FE
));
1523 (void) fprintf(stderr
, "%s\n", gettext(MSG_FF
));
1526 (void) fprintf(stderr
, "%s\n", gettext(MSG_FB
));
1529 (void) fprintf(stderr
, "%s\n", gettext(MSG_NV
));
1532 (void) fprintf(stderr
, "%s\n", gettext(MSG_AD
));
1535 (void) fprintf(stderr
, "%s\n", gettext(MSG_NM
));
1538 (void) fprintf(stderr
, "%s\n", gettext(MSG_NP
));
1542 /* write password record */
1543 if (event
!= NULL
) {
1544 struct passwd
*pass
;
1546 if ((pass
= getpwnam(usrname
)) == NULL
) {
1547 /* unlikely to ever get here, but ... */
1548 event
->adt_passwd
.username
= usrname
;
1549 } else if (pass
->pw_uid
!= uid
) {
1550 /* save target user */
1551 event
->adt_passwd
.uid
= pass
->pw_uid
;
1552 event
->adt_passwd
.username
= pass
->pw_name
;
1555 if (adt_put_event(event
,
1556 retcode
== SUCCESS
? ADT_SUCCESS
: ADT_FAILURE
,
1557 retcode
== SUCCESS
? ADT_SUCCESS
: ADT_FAIL_PAM
+
1559 adt_free_event(event
);
1560 (void) adt_end_session(ah
);
1561 perror("adt_put_event");
1564 adt_free_event(event
);
1566 (void) adt_end_session(ah
);
1573 * This is the conv (conversation) function called from
1574 * a PAM authentication module to print error messages
1575 * or garner information from the user.
1581 passwd_conv(int num_msg
, struct pam_message
**msg
,
1582 struct pam_response
**response
, void *appdata_ptr
)
1584 struct pam_message
*m
;
1585 struct pam_response
*r
;
1590 return (PAM_CONV_ERR
);
1592 *response
= (struct pam_response
*)calloc(num_msg
,
1593 sizeof (struct pam_response
));
1594 if (*response
== NULL
)
1595 return (PAM_BUF_ERR
);
1602 switch (m
->msg_style
) {
1604 case PAM_PROMPT_ECHO_OFF
:
1605 temp
= getpassphrase(m
->msg
);
1607 r
->resp
= strdup(temp
);
1608 (void) memset(temp
, 0, strlen(temp
));
1609 if (r
->resp
== NULL
) {
1610 /* free responses */
1612 for (i
= 0; i
< num_msg
; i
++, r
++) {
1618 return (PAM_BUF_ERR
);
1625 case PAM_PROMPT_ECHO_ON
:
1626 if (m
->msg
!= NULL
) {
1627 (void) fputs(m
->msg
, stdout
);
1629 r
->resp
= (char *)calloc(PAM_MAX_RESP_SIZE
,
1631 if (r
->resp
== NULL
) {
1632 /* free responses */
1634 for (i
= 0; i
< num_msg
; i
++, r
++) {
1640 return (PAM_BUF_ERR
);
1642 if (fgets(r
->resp
, PAM_MAX_RESP_SIZE
-1, stdin
)) {
1643 int len
= strlen(r
->resp
);
1644 if (r
->resp
[len
-1] == '\n')
1645 r
->resp
[len
-1] = '\0';
1652 if (m
->msg
!= NULL
) {
1653 (void) fputs(m
->msg
, stderr
);
1654 (void) fputs("\n", stderr
);
1660 if (m
->msg
!= NULL
) {
1661 (void) fputs(m
->msg
, stdout
);
1662 (void) fputs("\n", stdout
);
1672 return (PAM_SUCCESS
);
1676 * Utilities Functions
1680 * int attrlist_add(attrlist **l, attrtype type, char *val)
1681 * add an item, with type "type" and value "val", at the tail of list l.
1682 * This functions exits the application on OutOfMem error.
1685 attrlist_add(attrlist
**l
, attrtype type
, char *val
)
1690 for (w
= l
; *w
!= NULL
; w
= &(*w
)->next
)
1693 if ((*w
= malloc(sizeof (**w
))) == NULL
)
1703 (*w
)->data
.val_i
= atoi(val
);
1706 (*w
)->data
.val_s
= val
;
1712 * attrlist_reorder(attrlist **l)
1714 * - if EXPIRE and MAX or MIN is set, EXPIRE comes after MAX/MIN
1715 * - if both MIN and MAX are set, MAX comes before MIN.
1719 attrlist_reorder(attrlist
**l
)
1722 attrlist
*exp
= NULL
; /* ATTR_EXPIRE_PASSWORD, if found */
1723 attrlist
*max
= NULL
; /* ATTR_MAX, if found */
1725 if (*l
== NULL
|| (*l
)->next
== NULL
)
1726 return; /* order of list with <= one item is ok */
1729 * We simply walk the list, take off the EXPIRE and MAX items if
1730 * they appear, and put them (first MAX, them EXPIRE) at the end
1734 while (*w
!= NULL
) {
1735 if ((*w
)->type
== ATTR_EXPIRE_PASSWORD
) {
1738 } else if ((*w
)->type
== ATTR_MAX
) {
1745 /* 'w' points to the address of the 'next' field of the last element */
1762 #define MSG(a) (void) fprintf(stderr, gettext((a)));
1765 MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n");
1766 MSG("\tpasswd [-r files] [-egh] [name]\n");
1767 MSG("\tpasswd [-r files] -sa\n");
1768 MSG("\tpasswd [-r files] -s [name]\n");
1769 MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
1771 MSG("\tpasswd -r nis [-eg] [name]\n");
1772 MSG("\t\t[-x max] name\n");
1773 MSG("\tpasswd -r ldap [-egh] [name]\n");
1774 MSG("\tpasswd -r ldap -sa\n");
1775 MSG("\tpasswd -r ldap -s [name]\n");
1776 MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "