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.
35 #include <user_attr.h>
36 #include <auth_attr.h>
40 #include <sys/param.h>
43 #include <security/pam_appl.h>
44 #include <security/pam_modules.h>
45 #include <security/pam_impl.h>
49 #include <passwdutil.h>
51 #define LOGINADMIN "/etc/default/login"
56 error(pam_handle_t
*pamh
, char *fmt
, ...)
59 char messages
[1][PAM_MAX_MSG_SIZE
];
62 (void) vsnprintf(messages
[0], sizeof (messages
[0]), fmt
, ap
);
63 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1, messages
, NULL
);
68 get_max_failed(char *user
)
77 if ((uattr
= getusernam(user
)) != NULL
)
78 val
= kva_match(uattr
->attr
, USERATTR_LOCK_AFTER_RETRIES_KW
);
81 do_lock
= (strcasecmp(val
, "yes") == 0);
82 } else if ((defp
= defopen_r(AUTH_POLICY
)) != NULL
) {
84 flags
= defcntl_r(DC_GETFLAGS
, 0, defp
);
85 TURNOFF(flags
, DC_CASE
);
86 (void) defcntl_r(DC_SETFLAGS
, flags
, defp
);
87 if ((p
= defread_r("LOCK_AFTER_RETRIES=", defp
)) != NULL
)
88 do_lock
= (strcasecmp(p
, "yes") == 0);
97 if ((defp
= defopen_r(LOGINADMIN
)) != NULL
) {
98 if ((p
= defread_r("RETRIES=", defp
)) != NULL
)
108 display_warning(pam_handle_t
*pamh
, int failures
, char *homedir
)
110 char hushpath
[MAXPATHLEN
];
113 (void) snprintf(hushpath
, sizeof (hushpath
), "%s/.hushlogin", homedir
);
114 if (stat(hushpath
, &buf
) == 0)
118 error(pamh
, "Warning: 1 failed login attempt since last "
119 "successful login.");
120 else if (failures
< FAILCOUNT_MASK
)
121 error(pamh
, "Warning: %d failed login attempts since last "
122 "successful login.", failures
);
124 error(pamh
, "Warning: at least %d failed login attempts since "
125 "last successful login.", failures
);
129 * int pam_sm_authenticate(pamh, flags, arc, argv)
131 * This routine verifies that the password as stored in the
132 * PAM_AUTHTOK item is indeed the password that belongs to the user
133 * as stored in PAM_USER.
135 * This routine will not establish Secure RPC Credentials, the pam_dhkeys
136 * module should be stacked before us if Secure RPC Credentials are needed
137 * to obtain passwords.
140 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
144 int nowarn
= (flags
& PAM_SILENT
) != 0;
149 char *repository_name
;
150 struct pam_repository
*auth_rep
;
151 pwu_repository_t
*pwu_rep
;
154 int server_policy
= 0;
155 int old_failed_count
;
156 char *homedir
= NULL
;
159 for (i
= 0; i
< argc
; i
++) {
160 if (strcmp(argv
[i
], "debug") == 0)
162 else if (strcmp(argv
[i
], "nowarn") == 0)
164 else if (strcmp(argv
[i
], "server_policy") == 0)
166 else if (strcmp(argv
[i
], "nolock") == 0)
171 __pam_log(LOG_AUTH
| LOG_DEBUG
,
172 "pam_unix_auth: entering pam_sm_authenticate()");
174 if (pam_get_item(pamh
, PAM_USER
, (void **)&user
) != PAM_SUCCESS
) {
175 __pam_log(LOG_AUTH
| LOG_DEBUG
, "pam_unix_auth: USER not set");
176 return (PAM_SYSTEM_ERR
);
179 if (user
== NULL
|| *user
== '\0') {
180 __pam_log(LOG_AUTH
| LOG_DEBUG
,
181 "pam_unix_auth: USER NULL or empty!\n");
182 return (PAM_USER_UNKNOWN
);
185 if (pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&passwd
) != PAM_SUCCESS
) {
186 __pam_log(LOG_AUTH
| LOG_DEBUG
,
187 "pam_unix_auth: AUTHTOK not set!\n");
188 return (PAM_SYSTEM_ERR
);
191 result
= pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&auth_rep
);
192 if (result
== PAM_SUCCESS
&& auth_rep
!= NULL
) {
193 if ((pwu_rep
= calloc(1, sizeof (*pwu_rep
))) == NULL
)
194 return (PAM_BUF_ERR
);
195 pwu_rep
->type
= auth_rep
->type
;
196 pwu_rep
->scope
= auth_rep
->scope
;
197 pwu_rep
->scope_len
= auth_rep
->scope_len
;
199 pwu_rep
= PWU_DEFAULT_REP
;
203 * Get password and the name of the repository where the
206 attr_pw
[0].type
= ATTR_PASSWD
; attr_pw
[0].next
= &attr_pw
[1];
207 attr_pw
[1].type
= ATTR_REP_NAME
; attr_pw
[1].next
= &attr_pw
[2];
209 * Also get the current number of failed logins; we use
210 * this later to determine whether we need to reset the count
211 * on a succesful authentication. We use the home-directory
212 * to look for .hushlogin in order to optionaly surpress the
213 * "failed attempts" message.
215 attr_pw
[2].type
= ATTR_FAILED_LOGINS
; attr_pw
[2].next
= &attr_pw
[3];
216 attr_pw
[3].type
= ATTR_HOMEDIR
; attr_pw
[3].next
= NULL
;
218 result
= __get_authtoken_attr(user
, pwu_rep
, attr_pw
);
220 if (pwu_rep
!= PWU_DEFAULT_REP
)
223 if (result
== PWU_NOT_FOUND
) {
224 __pam_log(LOG_AUTH
| LOG_DEBUG
,
225 "pam_unix_auth: user %s not found\n", user
);
226 return (PAM_USER_UNKNOWN
);
229 if (result
== PWU_DENIED
) {
230 __pam_log(LOG_AUTH
| LOG_DEBUG
,
231 "pam_unix_auth: failed to obtain attributes");
232 return (PAM_PERM_DENIED
);
235 if (result
!= PWU_SUCCESS
)
236 return (PAM_SYSTEM_ERR
);
238 rep_passwd
= attr_pw
[0].data
.val_s
;
239 repository_name
= attr_pw
[1].data
.val_s
;
240 old_failed_count
= attr_pw
[2].data
.val_i
;
241 homedir
= attr_pw
[3].data
.val_s
;
244 * Chop off old SunOS-style password aging information.
246 * Note: old style password aging is only defined for UNIX-style
247 * crypt strings, hence the comma will always be at position 14.
248 * Note: This code is here because some other vendors might still
249 * support this style of password aging. If we don't remove
250 * the age field, no one will be able to login.
251 * XXX yank this code when we're certain this "compatibility"
252 * isn't needed anymore.
254 if (rep_passwd
!= NULL
&& rep_passwd
[0] != '$' &&
255 strlen(rep_passwd
) > 13 && rep_passwd
[13] == ',')
256 rep_passwd
[13] = '\0';
258 /* Is a password check required? */
259 if (rep_passwd
== NULL
|| *rep_passwd
== '\0') {
260 if (flags
& PAM_DISALLOW_NULL_AUTHTOK
) {
261 result
= PAM_AUTH_ERR
;
262 __pam_log(LOG_AUTH
| LOG_NOTICE
,
263 "pam_unix_auth: empty password for %s not allowed.",
267 result
= PAM_SUCCESS
;
273 * Password check *is* required. Make sure we have a valid
274 * pointer in PAM_AUTHTOK
276 if (passwd
== NULL
) {
277 result
= PAM_AUTH_ERR
;
282 strcmp(repository_name
, "files") != 0 &&
283 strcmp(repository_name
, "nis") != 0) {
288 /* Now check the entered password */
289 if ((crypt_passwd
= crypt(passwd
, rep_passwd
)) == NULL
) {
292 result
= PAM_BUF_ERR
;
295 result
= PAM_OPEN_ERR
;
298 result
= PAM_SYSTEM_ERR
;
303 if (strcmp(crypt_passwd
, rep_passwd
) == 0)
304 result
= PAM_SUCCESS
;
306 result
= PAM_AUTH_ERR
;
308 /* Clear or increment failed failed count */
309 if (dolock
&& (result
== PAM_SUCCESS
&& old_failed_count
> 0)) {
310 old_failed_count
= __rst_failed_count(user
, repository_name
);
311 if (nowarn
== 0 && old_failed_count
> 0)
312 display_warning(pamh
, old_failed_count
, homedir
);
313 } else if (dolock
&& result
== PAM_AUTH_ERR
) {
314 int max_failed
= get_max_failed(user
);
315 if (max_failed
!= 0) {
316 if (__incr_failed_count(user
, repository_name
,
317 max_failed
) == PWU_ACCOUNT_LOCKED
)
318 result
= PAM_MAXTRIES
;
325 free(repository_name
);
333 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)