1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include "ldap_headers.h"
28
29 /*ARGSUSED*/
30 static void
ldap_cleanup(pam_handle_t * pamh,void * data,int pam_status)31 ldap_cleanup(
32 pam_handle_t *pamh,
33 void *data,
34 int pam_status)
35 {
36 free((ldap_authtok_data *)data);
37 }
38
39 /*
40 * warn_user_passwd_will_expire - warn the user when the password will
41 * expire.
42 */
43
44 static void
warn_user_passwd_will_expire(pam_handle_t * pamh,int sec_until_expired)45 warn_user_passwd_will_expire(
46 pam_handle_t *pamh,
47 int sec_until_expired)
48 {
49 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
50 int days = 0, hours = 0;
51 int seconds_d = 0, seconds_h = 0;
52
53 days = sec_until_expired / 86400;
54 seconds_d = sec_until_expired % 86400;
55 hours = (days * 24) + seconds_d / 3600;
56 seconds_h = seconds_d % 3600;
57
58 if (sec_until_expired <= (86400 * 2)) {
59 if (seconds_d <= 3600 && days == 0)
60 (void) snprintf(messages[0], sizeof (messages[0]),
61 dgettext(TEXT_DOMAIN,
62 "Your password will expire within one hour."));
63 else
64 (void) snprintf(messages[0], sizeof (messages[0]),
65 dgettext(TEXT_DOMAIN,
66 "Your password will expire in %d hours."),
67 (seconds_h == 0) ? hours : hours + 1);
68 } else {
69 (void) snprintf(messages[0], sizeof (messages[0]),
70 dgettext(TEXT_DOMAIN,
71 "Your password will expire in %d days."),
72 (seconds_d == 0) ? days : days + 1);
73 }
74
75 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
76 }
77
78 /*
79 * display_acct_unlock_time - Display the time left for the account to
80 * get auto unlocked after the maximum login failures has reached.
81 */
82 static void
display_acct_unlock_time(pam_handle_t * pamh,int sec_b4_unlock)83 display_acct_unlock_time(pam_handle_t *pamh, int sec_b4_unlock)
84 {
85 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
86 int days = 0, hours = 0;
87 int seconds_d = 0, seconds_h = 0;
88
89 /* Account is locked forever */
90 if (sec_b4_unlock == -1) {
91 (void) snprintf(messages[0], sizeof (messages[0]),
92 dgettext(TEXT_DOMAIN,
93 "Your account is locked, please contact administrator."));
94 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
95 messages, NULL);
96 return;
97 }
98
99 days = sec_b4_unlock / 86400;
100 seconds_d = sec_b4_unlock % 86400;
101 hours = (days * 24) + seconds_d / 3600;
102 seconds_h = seconds_d % 3600;
103
104 if (sec_b4_unlock <= (86400 * 2)) {
105 if (seconds_d <= 3600 && days == 0)
106 (void) snprintf(messages[0], sizeof (messages[0]),
107 dgettext(TEXT_DOMAIN,
108 "Your account is locked and will be unlocked"
109 " within one hour."));
110 else
111 (void) snprintf(messages[0], sizeof (messages[0]),
112 dgettext(TEXT_DOMAIN,
113 "Your account is locked and will be unlocked"
114 " in %d hours."),
115 (seconds_h == 0) ? hours : hours + 1);
116 } else {
117 (void) snprintf(messages[0], sizeof (messages[0]),
118 dgettext(TEXT_DOMAIN,
119 "Your account is locked and will be unlocked"
120 " in %d days."),
121 (seconds_d == 0) ? days : days + 1);
122 }
123
124 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
125 }
126
127 /*
128 * warn_user_passwd_expired - warn the user that the password has expired
129 */
130 static void
warn_user_passwd_expired(pam_handle_t * pamh,int grace)131 warn_user_passwd_expired(pam_handle_t *pamh, int grace)
132 {
133 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
134
135 if (grace)
136 (void) snprintf(messages[0], sizeof (messages[0]),
137 dgettext(TEXT_DOMAIN,
138 "Your password has expired. "
139 "Number of grace logins allowed are %d."),
140 grace);
141 else
142 (void) snprintf(messages[0], sizeof (messages[0]),
143 dgettext(TEXT_DOMAIN,
144 "Your password has expired."));
145
146 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
147 }
148
149 /*
150 * display_passwd_reset_msg - tell user that password has been reset by
151 * administrator
152 */
153 static void
display_passwd_reset_msg(pam_handle_t * pamh)154 display_passwd_reset_msg(pam_handle_t *pamh)
155 {
156 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
157
158 (void) snprintf(messages[0], sizeof (messages[0]),
159 dgettext(TEXT_DOMAIN,
160 "Your password has been reset by administrator."));
161
162 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
163 }
164
165 /*
166 * Retreives account management related attributes for the user using
167 * default binding and does local account checks .
168 *
169 * Return Value: PAM_SUCCESS - If account is valid, seconds param will have
170 * seconds left for password to expire
171 * PAM_ACCT_EXPIRED - If account is inactive
172 * PAM_NEW_AUTHTOK_REQD - Password is reset by admin
173 * PAM_AUTHTOK_EXPIRED - User password has expired, grace
174 * param will have no. of grace logins allowed
175 * PAM_MAXTRIES - If maximum failure of wrong password has reached
176 * seconds param will have no. of seconds for the
177 * account to get unlocked
178 * PAM_AUTH_ERR - Failure return code
179 */
180 static int
get_account_mgmt(char * user,int * seconds,int * grace)181 get_account_mgmt(char *user, int *seconds, int *grace)
182 {
183 int rc = PAM_AUTH_ERR;
184 AcctUsableResponse_t acctResp;
185
186 (void *)memset((void*)&acctResp, 0, sizeof (acctResp));
187 /* get the values for local account checking */
188 if ((rc = __ns_ldap_getAcctMgmt(user, &acctResp))
189 != NS_LDAP_SUCCESS) {
190 syslog(LOG_DEBUG,
191 "__ns_ldap_getAcctMgmt() failed for %s with error %d",
192 user, rc);
193 return (PAM_AUTH_ERR);
194 }
195
196 if (acctResp.choice == 0) {
197 /* should be able to login */
198 *seconds =
199 acctResp.AcctUsableResp.seconds_before_expiry;
200 return (PAM_SUCCESS);
201 } else if (acctResp.choice == 1) {
202 /* cannot login */
203 if (acctResp.AcctUsableResp.more_info.inactive)
204 /* entry inactive */
205 return (PAM_ACCT_EXPIRED);
206 if (acctResp.AcctUsableResp.more_info.reset)
207 /* password reset by administrator */
208 return (PAM_NEW_AUTHTOK_REQD);
209 if (acctResp.AcctUsableResp.more_info.expired) {
210 /*
211 * password expired, check for grace logins.
212 */
213 *grace =
214 acctResp.AcctUsableResp.more_info.rem_grace;
215 return (PAM_AUTHTOK_EXPIRED);
216 }
217 if (acctResp.AcctUsableResp.more_info.sec_b4_unlock) {
218 /* max failures reached, seconds before unlock */
219 *seconds =
220 acctResp.AcctUsableResp.more_info.sec_b4_unlock;
221 return (PAM_MAXTRIES);
222 }
223 }
224 return (PAM_AUTH_ERR);
225 }
226
227 /*
228 * pam_sm_acct_mgmt main account managment routine.
229 * This routine relies on the LDAP
230 * directory server to provide the
231 * password aging and account lockout
232 * information. This is done by first
233 * trying to authenticate the user and
234 * then checking the password status
235 * returned.
236 *
237 * Returns: module error or specific
238 * error on failure.
239 */
240
241 int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)242 pam_sm_acct_mgmt(
243 pam_handle_t *pamh,
244 int flags,
245 int argc,
246 const char **argv)
247 {
248
249 char *user = NULL;
250 int result = PAM_AUTH_ERR;
251 int debug = 0;
252 int i;
253 char *password = NULL;
254 ns_cred_t *credp = NULL;
255 int nowarn = 0;
256 int seconds = 0, grace = 0;
257 ldap_authtok_data *status;
258
259 for (i = 0; i < argc; i++) {
260 if (strcmp(argv[i], "debug") == 0)
261 debug = 1;
262 else if (strcasecmp(argv[i], "nowarn") == 0) {
263 nowarn = 1;
264 flags = flags | PAM_SILENT;
265 }
266 else
267 syslog(LOG_DEBUG,
268 "pam_ldap pam_sm_acct_mgmt: "
269 "illegal option %s",
270 argv[i]);
271 }
272
273 if ((result = pam_get_item(pamh, PAM_USER, (void **)&user))
274 != PAM_SUCCESS)
275 goto out;
276
277 if (debug)
278 syslog(LOG_DEBUG,
279 "ldap pam_sm_acct_mgmt(%s), flags = %x %s",
280 (user)?user:"no-user", flags,
281 (nowarn)? ", nowarn": "");
282
283 if (user == NULL) {
284 result = PAM_USER_UNKNOWN;
285 goto out;
286 }
287
288 /* retrieve the password from the PAM handle */
289 result = pam_get_item(pamh, PAM_AUTHTOK, (void **) &password);
290 if (password == NULL) {
291 if (debug)
292 syslog(LOG_DEBUG, "ldap pam_sm_acct_mgmt: "
293 "no password for user %s", user);
294 /* Do local account checking */
295 result = get_account_mgmt(user, &seconds, &grace);
296 } else {
297 /* Try to authenticate to get password management info */
298 result = authenticate(&credp, user,
299 password, &seconds);
300 }
301
302 /*
303 * process the password management info.
304 * If user needs to change the password immediately,
305 * just return the rc.
306 * Otherwise, reset rc to the appropriate PAM error or
307 * warn the user about password expiration.
308 */
309 if (result == PAM_MAXTRIES) {
310 /* exceed retry limit, denied access to account */
311 if (!(flags & PAM_SILENT))
312 display_acct_unlock_time(pamh, seconds);
313 result = PAM_PERM_DENIED;
314 } else if (result == PAM_ACCT_EXPIRED)
315 /* account is inactivated */
316 result = PAM_ACCT_EXPIRED;
317 else if (result == PAM_AUTHTOK_EXPIRED) {
318 if (!(flags & PAM_SILENT))
319 warn_user_passwd_expired(pamh, grace);
320 /* password expired, check for grace logins */
321 if (grace > 0)
322 result = PAM_SUCCESS;
323 else
324 result = PAM_AUTHTOK_EXPIRED;
325 } else if (result == PAM_NEW_AUTHTOK_REQD) {
326 /* password has been reset by administrator */
327 if (!(flags & PAM_SILENT))
328 display_passwd_reset_msg(pamh);
329 result = PAM_NEW_AUTHTOK_REQD;
330 } else if (result == PAM_SUCCESS) {
331 /*
332 * warn the user if the password
333 * is about to expire.
334 */
335 if (!(flags & PAM_SILENT) &&
336 seconds > 0)
337 warn_user_passwd_will_expire(pamh,
338 seconds);
339
340 }
341
342 out:
343 if (credp != NULL)
344 (void) __ns_ldap_freeCred(&credp);
345
346 /* store the password aging status in the pam handle */
347 if (result != PAM_SUCCESS) {
348 int pam_res;
349 ldap_authtok_data *authtok_data;
350
351 pam_res = pam_get_data(
352 pamh, LDAP_AUTHTOK_DATA, (const void **)&authtok_data);
353
354 if ((status = (ldap_authtok_data *)calloc
355 (1, sizeof (ldap_authtok_data))) == NULL) {
356 return (PAM_BUF_ERR);
357 }
358
359 if (pam_res == PAM_SUCCESS)
360 (void) memcpy(status, authtok_data,
361 sizeof (ldap_authtok_data));
362
363 status->age_status = result;
364 if (pam_set_data(pamh, LDAP_AUTHTOK_DATA, status, ldap_cleanup)
365 != PAM_SUCCESS) {
366 free(status);
367 return (PAM_SERVICE_ERR);
368 }
369 }
370
371 return (result);
372 }
373