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