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);
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 *);
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
[])
234 attrlist
*attributes
= NULL
;
240 if ((prognamep
= strrchr(argv
[0], '/')) != NULL
)
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 */
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 */
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
);
277 if ((usrname
= getlogin()) == NULL
) {
278 struct passwd
*pass
= getpwuid(uid
);
280 usrname
= pass
->pw_name
;
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
,
295 usrname
= argv
[optind
];
298 if (pam_start("passwd", usrname
, &pam_conv
, &pamh
) != PAM_SUCCESS
) {
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
)
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
);
319 (void) fprintf(stderr
, "%s: %s\n", prognamep
,
324 while (namelist
[i
] != NULL
) {
325 (void) get_attr(namelist
[i
], &repository
,
327 (void) display_attr(namelist
[i
], attributes
);
328 (void) free(namelist
[i
]);
329 (void) free_attr(attributes
);
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
) ==
337 (void) display_attr(usrname
, attributes
);
338 (void) free_attr(attributes
);
340 passwd_exit(SUCCESS
);
345 switch (pam_authenticate(pamh
, 0)) {
348 case PAM_USER_UNKNOWN
:
349 (void) fprintf(stderr
, gettext(MSG_UNKNOWN
), prognamep
,
353 case PAM_PERM_DENIED
:
357 (void) fprintf(stderr
, gettext(MSG_SORRY
), prognamep
);
366 if (flag
== 0) { /* changing user password */
367 int chk_authtok
= 0; /* check password strength */
369 dprintf1("call pam_chauthtok() repository name =%s\n",
372 /* Set up for Audit */
373 if (adt_start_session(&ah
, NULL
, ADT_USE_PROC_DATA
) != 0) {
374 perror("adt_start_session");
377 if ((event
= adt_alloc_event(ah
, ADT_passwd
)) == NULL
) {
378 perror("adt_alloc_event");
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
);
391 case PAM_AUTHTOK_EXPIRED
:
392 (void) fprintf(stderr
,
393 gettext(MSG_AUTHTOK_EXP
));
396 case PAM_NEW_AUTHTOK_REQD
:
397 /* valid error when changing passwords */
400 /* Ok to change password */
408 pam_retval
= PAM_AUTHTOK_ERR
;
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
) {
417 (void) printf(gettext(MSG_AGAIN
));
418 pam_retval
= pam_chauthtok(pamh
, chk_authtok
);
419 if (pam_retval
== PAM_TRY_AGAIN
) {
421 pam_retval
= pam_chauthtok(pamh
, chk_authtok
);
426 switch (pam_retval
) {
430 case PAM_AUTHTOK_DISABLE_AGING
:
433 case PAM_AUTHTOK_LOCK_BUSY
:
439 case PAM_AUTHTOK_ERR
:
440 case PAM_AUTHTOK_RECOVERY_ERR
:
446 (void) passwd_exit(retval
);
448 } else { /* changing attributes */
450 case EFLAG
: /* changing user password attributes */
451 input
= userinput(usrname
, &repository
, ATTR_SHELL
);
453 attrlist_add(&attributes
, ATTR_SHELL
, input
);
455 (void) printf(gettext(MSG_SHELL_UNCHANGED
));
458 input
= userinput(usrname
, &repository
, ATTR_GECOS
);
460 attrlist_add(&attributes
, ATTR_GECOS
, input
);
462 (void) printf(gettext(MSG_GECOS_UNCHANGED
));
465 input
= userinput(usrname
, &repository
, ATTR_HOMEDIR
);
467 attrlist_add(&attributes
, ATTR_HOMEDIR
, input
);
469 (void) printf(gettext(MSG_DIR_UNCHANGED
));
473 if (attributes
!= NULL
) {
474 retval
= __set_authtoken_attr(usrname
,
475 pamh
->ps_item
[PAM_AUTHTOK
].pi_addr
,
476 &repository
, attributes
, &updated_reps
);
479 for (i
= 1; i
<= REP_LAST
; i
<<= 1) {
480 if ((updated_reps
& i
) == 0)
482 (void) printf(gettext(MSG_SUCCESS
),
487 case PWU_AGING_DISABLED
:
495 retval
= SUCCESS
; /* nothing to change won't fail */
497 (void) passwd_exit(retval
);
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.
511 getresponse(char *oldval
)
513 char resp
[MAX_INPUT_LEN
];
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
);
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).
537 userinput(char *name
, pwu_repository_t
*rep
, attrtype type
)
540 char *oldval
; /* shorthand for oldattr.data.val_s */
541 char *valid
; /* points to valid shells */
548 if (__get_authtoken_attr(name
, rep
, &oldattr
) != PWU_SUCCESS
)
551 oldval
= oldattr
.data
.val_s
;
553 if (type
== ATTR_SHELL
) {
554 /* No current shell: set DEFSHL as default choice */
555 if (*oldval
== '\0') {
557 oldval
= strdup(DEFSHL
);
560 if (ckuid() != SUCCESS
) {
561 /* User must currently have a valid shell */
563 valid
= getusershell();
564 while (valid
&& strcmp(valid
, oldval
) != 0)
565 valid
= getusershell();
569 (void) fprintf(stderr
, gettext(MSG_RS
), oldval
);
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')
584 /* Make sure new shell is listed */
586 valid
= getusershell();
590 /* Allow user to give shell without path */
591 if (*response
== '/') {
594 if ((cp
= strrchr(valid
, '/')) == NULL
)
599 if (strcmp(cp
, response
) == 0) {
600 if (*response
!= '/') {
601 /* take shell name including path */
603 response
= strdup(valid
);
607 valid
= getusershell();
611 if (valid
== NULL
) { /* No valid shell matches */
612 (void) fprintf(stderr
, gettext(MSG_UNACCEPT
), response
);
616 if (access(response
, X_OK
) < 0)
617 (void) fprintf(stderr
, gettext(MSG_UNAVAIL
), response
);
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.
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)
643 /* No-change or empty string are OK */
644 if (response
== NULL
|| *response
== '\0')
647 /* Check for illegal characters */
648 if (strchr(response
, ':')) {
649 (void) fprintf(stderr
, "%s", gettext(MSG_COLON
));
651 } else if (strlen(response
) > MAX_INPUT_LEN
- 1) {
652 (void) fprintf(stderr
, gettext(MSG_MAXLEN
),
656 /* don't allow control characters */
657 for (cp
= response
; *cp
>= 040; cp
++)
660 (void) fprintf(stderr
, gettext(MSG_CONTROL
));
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.
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
683 ckarg(int argc
, char **argv
, attrlist
**attributes
)
692 while ((opt
= getopt(argc
, argv
, "r:aldefghsux:n:w:N")) != EOF
) {
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"));
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
;
712 (void) fprintf(stderr
,
713 gettext("invalid repository: %s\n"),
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"));
740 if (ckuid() != SUCCESS
) {
744 if (flag
& (LFLAG
|SAFLAG
|DFLAG
|XFLAG
|UFLAG
)) {
750 attrlist_add(attributes
, ATTR_PASSWD
, NULL
);
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 "
770 * Only privileged processes can execute this
773 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
774 ((retval
= ckuid()) != SUCCESS
))
776 if (flag
& (MUTEXFLAG
|NONAGEFLAG
)) {
782 attrlist_add(attributes
, ATTR_NOLOGIN_ACCOUNT
, NULL
);
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 "
802 * Only privileged processes can execute this
805 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
806 ((retval
= ckuid()) != SUCCESS
))
808 if (flag
& (MUTEXFLAG
|NONAGEFLAG
)) {
814 attrlist_add(attributes
, ATTR_LOCK_ACCOUNT
, NULL
);
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 "
834 * Only privileged processes can execute this
837 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
838 ((retval
= ckuid()) != SUCCESS
))
840 if (flag
& (MUTEXFLAG
|NONAGEFLAG
)) {
846 attrlist_add(attributes
, ATTR_UNLOCK_ACCOUNT
, NULL
);
847 attrlist_add(attributes
, ATTR_RST_FAILED_LOGINS
, NULL
);
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 "
867 * Only privileged process can execute this
870 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
871 (ckuid() != SUCCESS
)) {
875 if (flag
& (SAFLAG
|MFLAG
|NONAGEFLAG
)) {
880 if ((int)strlen(optarg
) <= 0 ||
881 (maxdate
= strtol(optarg
, &char_p
, 10)) < -1 ||
883 (void) fprintf(stderr
, "%s: %s -x\n",
884 prognamep
, gettext(MSG_NV
));
888 attrlist_add(attributes
, ATTR_MAX
, optarg
);
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 "
908 * Only privileged process can execute this
911 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
912 ((retval
= ckuid()) != SUCCESS
))
914 if (flag
& (SAFLAG
|NFLAG
|NONAGEFLAG
)) {
919 if ((int)strlen(optarg
) <= 0 ||
920 (strtol(optarg
, &char_p
, 10)) < 0 ||
922 (void) fprintf(stderr
, "%s: %s -n\n",
923 prognamep
, gettext(MSG_NV
));
927 attrlist_add(attributes
, ATTR_MIN
, optarg
);
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 "
947 * Only privileged process can execute this
950 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
951 (ckuid() != SUCCESS
)) {
955 if (flag
& (SAFLAG
|WFLAG
|NONAGEFLAG
)) {
960 if ((int)strlen(optarg
) <= 0 ||
961 (strtol(optarg
, &char_p
, 10)) < 0 ||
963 (void) fprintf(stderr
, "%s: %s -w\n",
964 prognamep
, gettext(MSG_NV
));
968 attrlist_add(attributes
, ATTR_WARN
, optarg
);
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 "
990 * Only privileged process can execute this
993 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
994 ((retval
= ckuid()) != SUCCESS
))
996 if (flag
&& (flag
!= AFLAG
)) {
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 "
1014 rusage(); /* exit */
1020 * Only privileged process can execute this
1023 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
1024 ((retval
= ckuid()) != SUCCESS
))
1026 if (flag
&& (flag
!= SFLAG
)) {
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 "
1044 rusage(); /* exit */
1050 * Only privileged process can execute this
1053 if ((IS_FILES(repository
) || IS_LDAP(repository
)) &&
1054 ((retval
= ckuid()) != SUCCESS
))
1056 if (flag
& (SAFLAG
|FFLAG
|NONAGEFLAG
)) {
1061 attrlist_add(attributes
, ATTR_EXPIRE_PASSWORD
, NULL
);
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
)) {
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
1087 if (IS_FILES(repository
) && (ckuid() != SUCCESS
)) {
1091 if (flag
& (GFLAG
|SAFLAG
|AGEFLAG
)) {
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
1107 if (IS_FILES(repository
) && (ckuid() != SUCCESS
)) {
1111 if (IS_NIS(repository
)) {
1112 (void) fprintf(stderr
, "%s\n",
1113 gettext(MSG_NIS_HOMEDIR
));
1118 if (flag
& (HFLAG
|SAFLAG
|AGEFLAG
)) {
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 */
1145 if (!flag
|| (flag
== SFLAG
))
1148 /* AFLAG must be used with SFLAG */
1149 if (flag
== AFLAG
) {
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
)) {
1167 /* user name(s) may not be specified when SAFLAG is used. */
1168 if (flag
== SAFLAG
&& argc
>= 1) {
1175 * If aging is being turned off (maxdate == -1), mindate may not
1178 if ((maxdate
== -1) && (flag
& NFLAG
)) {
1179 (void) fprintf(stderr
, "%s: %s -n\n",
1180 prognamep
, gettext(MSG_NV
));
1191 * This function returns SUCCESS if the caller is root, else
1192 * it returns NOPERM.
1200 return (retval
= NOPERM
);
1210 get_attr(char *username
, pwu_repository_t
*repository
, attrlist
**attributes
)
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
) {
1224 return (PWU_SUCCESS
);
1227 if (res
== PWU_NOT_FOUND
)
1228 (void) fprintf(stderr
, gettext(MSG_UNKNOWN
), prognamep
,
1232 passwd_exit(retval
);
1238 * This function prints out the password attributes of a usr
1239 * onto standand output.
1242 display_attr(char *usrname
, attrlist
*attributes
)
1244 char *status
= NULL
;
1247 int min
= 0, max
= 0, warn
= 0;
1249 while (attributes
) {
1250 switch (attributes
->type
) {
1252 passwd
= attributes
->data
.val_s
;
1253 if (passwd
== NULL
|| *passwd
== '\0')
1255 else if (strncmp(passwd
, LOCKSTRING
,
1256 sizeof (LOCKSTRING
)-1) == 0)
1258 else if (strncmp(passwd
, NOLOGINSTRING
,
1259 sizeof (NOLOGINSTRING
)-1) == 0)
1261 else if ((strlen(passwd
) == 13 && passwd
[0] != '$') ||
1268 lstchg
= attributes
->data
.val_i
* DAY
;
1271 min
= attributes
->data
.val_i
;
1274 max
= attributes
->data
.val_i
;
1277 warn
= attributes
->data
.val_i
;
1282 attributes
= attributes
->next
;
1284 (void) fprintf(stdout
, "%-8s ", usrname
);
1287 (void) fprintf(stdout
, "%s ", status
);
1291 (void) fprintf(stdout
, "00/00/00 ");
1294 tmp
= gmtime(&lstchg
);
1295 (void) fprintf(stdout
, "%.2d/%.2d/%.2d ",
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");
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
)
1337 if ((pwfp
= fopen(PASSWD
, "r")) == NULL
)
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
)
1348 * reset the file stream pointer
1352 nl
= (char **)calloc(max_user
, (sizeof (char *)));
1354 (void) fclose(pwfp
);
1358 while ((pwd
= fgetpwent(pwfp
)) != NULL
) {
1359 if ((nl
[nuser
] = strdup(pwd
->pw_name
)) == NULL
) {
1360 (void) fclose(pwfp
);
1369 (void) fclose(pwfp
);
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
;
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
;
1404 nss_setent(&db_root
, _lc_nss_initf_shadow
, &context
);
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
*
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
)
1446 if ((nl
= calloc(alloced
, sizeof (*nl
))) == NULL
)
1449 (void) _lc_setspent();
1450 while ((p
= _lc_getspent()) != NULL
) {
1451 if ((nl
[nuser
] = strdup(p
->sp_namp
)) == NULL
) {
1455 if (++nuser
== alloced
) {
1457 nl
= reallocarray(nl
, alloced
, sizeof (*nl
));
1464 (void) _lc_endspent();
1468 *num_user
= nuser
; /* including NULL */
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
));
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.
1496 passwd_exit(int retcode
)
1500 (void) pam_end(pamh
, pam_retval
);
1506 (void) fprintf(stderr
, "%s\n", gettext(MSG_NP
));
1509 (void) fprintf(stderr
, "%s\n", gettext(MSG_BS
));
1512 (void) fprintf(stderr
, "%s\n", gettext(MSG_FE
));
1515 (void) fprintf(stderr
, "%s\n", gettext(MSG_FF
));
1518 (void) fprintf(stderr
, "%s\n", gettext(MSG_FB
));
1521 (void) fprintf(stderr
, "%s\n", gettext(MSG_NV
));
1524 (void) fprintf(stderr
, "%s\n", gettext(MSG_AD
));
1527 (void) fprintf(stderr
, "%s\n", gettext(MSG_NM
));
1530 (void) fprintf(stderr
, "%s\n", gettext(MSG_NP
));
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
+
1551 adt_free_event(event
);
1552 (void) adt_end_session(ah
);
1553 perror("adt_put_event");
1556 adt_free_event(event
);
1558 (void) adt_end_session(ah
);
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.
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
;
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
);
1594 switch (m
->msg_style
) {
1596 case PAM_PROMPT_ECHO_OFF
:
1597 temp
= getpassphrase(m
->msg
);
1599 r
->resp
= strdup(temp
);
1600 (void) memset(temp
, 0, strlen(temp
));
1601 if (r
->resp
== NULL
) {
1602 /* free responses */
1604 for (i
= 0; i
< num_msg
; i
++, r
++) {
1609 return (PAM_BUF_ERR
);
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
,
1622 if (r
->resp
== NULL
) {
1623 /* free responses */
1625 for (i
= 0; i
< num_msg
; i
++, r
++) {
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';
1642 if (m
->msg
!= NULL
) {
1643 (void) fputs(m
->msg
, stderr
);
1644 (void) fputs("\n", stderr
);
1650 if (m
->msg
!= NULL
) {
1651 (void) fputs(m
->msg
, stdout
);
1652 (void) fputs("\n", stdout
);
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.
1675 attrlist_add(attrlist
**l
, attrtype type
, char *val
)
1680 for (w
= l
; *w
!= NULL
; w
= &(*w
)->next
)
1683 if ((*w
= malloc(sizeof (**w
))) == NULL
)
1693 (*w
)->data
.val_i
= atoi(val
);
1696 (*w
)->data
.val_s
= val
;
1702 * attrlist_reorder(attrlist **l)
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.
1709 attrlist_reorder(attrlist
**l
)
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
1724 while (*w
!= NULL
) {
1725 if ((*w
)->type
== ATTR_EXPIRE_PASSWORD
) {
1728 } else if ((*w
)->type
== ATTR_MAX
) {
1735 /* 'w' points to the address of the 'next' field of the last element */
1752 #define MSG(a) (void) fprintf(stderr, gettext((a)));
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] "
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] "