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*/
32static void
33ldap_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
46static void
47warn_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 */
84static void
85display_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 */
132static void
133warn_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 */
155static void
156display_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 */
182static int
183get_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
243int
244pam_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
344out:
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