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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/varargs.h>
36 #include <security/pam_appl.h>
37 #include <security/pam_modules.h>
38 #include <security/pam_impl.h>
40 #include <passwdutil.h>
46 error(int nowarn
, pam_handle_t
*pamh
, char *fmt
, ...)
49 char messages
[PAM_MAX_NUM_MSG
][PAM_MAX_MSG_SIZE
];
52 (void) vsnprintf(messages
[0], sizeof (messages
[0]), fmt
, ap
);
54 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1, messages
,
60 * int pam_sm_authenticate(pamh, flags, argc, argv)
62 * Read authentication token from user.
65 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
75 char prompt
[PAM_MAX_MSG_SIZE
];
76 char *auth_user
= NULL
;
79 char *rep_passwd
= NULL
;
80 char *repository_name
= NULL
;
85 int server_policy
= 0;
87 pam_repository_t
*auth_rep
= NULL
;
88 pwu_repository_t
*pwu_rep
= NULL
;
90 for (i
= 0; i
< argc
; i
++) {
91 if (strcmp(argv
[i
], "debug") == 0)
93 if (strcmp(argv
[i
], "nowarn") == 0)
95 if (strcmp(argv
[i
], "server_policy") == 0)
99 if (flags
& PAM_SILENT
)
102 if ((res
= pam_get_user(pamh
, &user
, NULL
)) != PAM_SUCCESS
) {
104 syslog(LOG_DEBUG
, "pam_passwd_auth: "
105 "get user failed: %s", pam_strerror(pamh
, res
));
109 if (user
== NULL
|| *user
== '\0') {
110 syslog(LOG_ERR
, "pam_passwd_auth: pam_sm_authenticate: "
111 "PAM_USER NULL or empty");
112 return (PAM_SYSTEM_ERR
);
115 res
= pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&password
);
116 if (res
!= PAM_SUCCESS
)
119 if (password
!= NULL
)
122 res
= pam_get_item(pamh
, PAM_SERVICE
, (void **)&service
);
123 if (res
!= PAM_SUCCESS
)
126 res
= pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&auth_rep
);
127 if (res
!= PAM_SUCCESS
) {
128 syslog(LOG_ERR
, "pam_passwd_auth: pam_sm_authenticate: "
129 "error getting repository");
130 return (PAM_SYSTEM_ERR
);
133 if (auth_rep
== NULL
) {
134 pwu_rep
= PWU_DEFAULT_REP
;
136 if ((pwu_rep
= calloc(1, sizeof (*pwu_rep
))) == NULL
)
137 return (PAM_BUF_ERR
);
138 pwu_rep
->type
= auth_rep
->type
;
139 pwu_rep
->scope
= auth_rep
->scope
;
140 pwu_rep
->scope_len
= auth_rep
->scope_len
;
143 res
= __user_to_authenticate(user
, pwu_rep
, &auth_user
, &privileged
);
144 if (res
!= PWU_SUCCESS
) {
145 if (res
== PWU_NOT_FOUND
)
146 retval
= PAM_USER_UNKNOWN
;
147 else if (res
== PWU_DENIED
)
148 retval
= PAM_PERM_DENIED
;
149 else if (res
== PWU_REPOSITORY_ERROR
) {
151 "pam_passwd_auth: detected unsupported "
152 "configuration in /etc/nsswitch.conf.");
153 error(nowarn
, pamh
, dgettext(TEXT_DOMAIN
, "%s: "
154 "Unsupported nsswitch entry for \"passwd:\"."
155 " Use \"-r repository \"."), service
);
156 retval
= PAM_SYSTEM_ERR
;
158 retval
= PAM_SYSTEM_ERR
;
160 syslog(LOG_DEBUG
, "passwd_auth: __user_to_authenticate "
161 "returned %d", retval
);
165 if (auth_user
== NULL
) { /* No authentication needed */
168 "passwd_auth: no authentication needed.");
169 retval
= PAM_SUCCESS
;
174 * The password prompt differs between users updating their
175 * own password, and users updating other an user's password
180 * The following string has a single space at the end
182 (void) snprintf(prompt
, sizeof (prompt
),
183 dgettext(TEXT_DOMAIN
, "Enter %s's password: "),
188 * The following string has a single space at the end
190 (void) snprintf(prompt
, sizeof (prompt
),
191 dgettext(TEXT_DOMAIN
, "Enter existing login password: "));
194 retval
= __pam_get_authtok(pamh
, PAM_PROMPT
, PAM_AUTHTOK
, prompt
,
196 if (retval
!= PAM_SUCCESS
)
199 if (password
== NULL
) {
200 syslog(LOG_ERR
, "pam_passwd_auth: pam_sm_authenticate: "
201 "got NULL password from get_authtok()");
202 retval
= PAM_AUTH_ERR
;
206 /* Privileged users can skip the tests that follow */
211 * Non privileged user: so we need to check the old password
212 * and possible restrictions on password changes.
215 /* Get password and it's age from the repository specified */
216 al
[0].type
= ATTR_PASSWD
; al
[0].next
= &al
[1];
217 al
[1].type
= ATTR_MIN
; al
[1].next
= &al
[2];
218 al
[2].type
= ATTR_MAX
; al
[2].next
= &al
[3];
219 al
[3].type
= ATTR_LSTCHG
; al
[3].next
= &al
[4];
220 al
[4].type
= ATTR_WARN
; al
[4].next
= &al
[5];
221 al
[5].type
= ATTR_INACT
; al
[5].next
= &al
[6];
222 al
[6].type
= ATTR_EXPIRE
; al
[6].next
= &al
[7];
223 al
[7].type
= ATTR_REP_NAME
; al
[7].next
= NULL
;
225 res
= __get_authtoken_attr(auth_user
, pwu_rep
, al
);
227 if (res
!= PWU_SUCCESS
) {
228 retval
= PAM_SYSTEM_ERR
;
232 repository_name
= al
[7].data
.val_s
;
235 * if repository isn't files|nis, and user wants to follow server
236 * policy, return PAM_IGNORE
239 strcmp(repository_name
, "files") != 0 &&
240 strcmp(repository_name
, "nis") != 0) {
245 rep_passwd
= al
[0].data
.val_s
;
248 * Chop off old SunOS-style password aging information.
250 * Note: old style password aging is only defined for UNIX-style
251 * crypt strings, hence the comma will always be at position 14.
252 * Note: This code is here because some other vendors might still
253 * support this style of password aging. If we don't remove
254 * the age field, users won't be able to change their password.
255 * XXX yank this code when we're certain this "compatibility"
256 * isn't needed anymore.
258 if (rep_passwd
!= NULL
&& rep_passwd
[0] != '$' &&
259 strlen(rep_passwd
) > 13 && rep_passwd
[13] == ',')
260 rep_passwd
[13] = '\0';
262 if (strcmp(crypt(password
, rep_passwd
), rep_passwd
) != 0) {
263 retval
= PAM_AUTH_ERR
;
268 * Now check to see if the user is allowed to change
271 min
= al
[1].data
.val_i
;
272 max
= al
[2].data
.val_i
;
273 lstchg
= al
[3].data
.val_i
;
275 if (max
!= -1 && lstchg
!= 0) {
276 /* aging is turned on, and a change is not forced */
277 time_t daynow
= DAY_NOW_32
;
278 if ((time_t)lstchg
<= daynow
) {
280 if (daynow
< (time_t)(lstchg
+ min
)) {
281 error(nowarn
, pamh
, dgettext(TEXT_DOMAIN
,
282 "%s: Sorry: less than %d days "
283 "since the last change."),
285 retval
= PAM_PERM_DENIED
;
290 * users with min>max are not allowed to
291 * change their password.
294 error(nowarn
, pamh
, dgettext(TEXT_DOMAIN
,
295 "%s: You may not change "
296 "this password."), service
);
297 retval
= PAM_PERM_DENIED
;
305 retval
= pam_set_item(pamh
, PAM_AUTHTOK
, (void *)password
);
309 (void) memset(password
, 0, strlen(password
));
313 (void) memset(rep_passwd
, 0, strlen(rep_passwd
));
318 free(repository_name
);
325 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)