xref: /illumos-gate/usr/src/lib/pam_modules/ldap/ldap_acct_mgmt.c (revision 86d949f9497332fe19be6b5d711d265eb957439f)
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
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
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
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
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
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
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
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