xref: /onnv-gate/usr/src/lib/pam_modules/ldap/ldap_acct_mgmt.c (revision 1179:6b4415013361)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*1179Svv149972  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include "ldap_headers.h"
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /*ARGSUSED*/
320Sstevel@tonic-gate static void
ldap_cleanup(pam_handle_t * pamh,void * data,int pam_status)330Sstevel@tonic-gate ldap_cleanup(
340Sstevel@tonic-gate 	pam_handle_t *pamh,
350Sstevel@tonic-gate 	void *data,
360Sstevel@tonic-gate 	int pam_status)
370Sstevel@tonic-gate {
380Sstevel@tonic-gate 	free((ldap_authtok_data *)data);
390Sstevel@tonic-gate }
400Sstevel@tonic-gate 
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate  * warn_user_passwd_will_expire	- warn the user when the password will
430Sstevel@tonic-gate  *					  expire.
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate 
460Sstevel@tonic-gate static void
warn_user_passwd_will_expire(pam_handle_t * pamh,int sec_until_expired)470Sstevel@tonic-gate warn_user_passwd_will_expire(
480Sstevel@tonic-gate 	pam_handle_t *pamh,
490Sstevel@tonic-gate 	int sec_until_expired)
500Sstevel@tonic-gate {
510Sstevel@tonic-gate 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
520Sstevel@tonic-gate 	int	days = 0, hours = 0;
530Sstevel@tonic-gate 	int	seconds_d = 0, seconds_h = 0;
540Sstevel@tonic-gate 
550Sstevel@tonic-gate 	days = sec_until_expired / 86400;
560Sstevel@tonic-gate 	seconds_d = sec_until_expired % 86400;
570Sstevel@tonic-gate 	hours = (days * 24) + seconds_d / 3600;
580Sstevel@tonic-gate 	seconds_h = seconds_d % 3600;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate 	if (sec_until_expired <= (86400 * 2)) {
610Sstevel@tonic-gate 		if (seconds_d <= 3600 && days == 0)
620Sstevel@tonic-gate 			(void) snprintf(messages[0], sizeof (messages[0]),
630Sstevel@tonic-gate 			    dgettext(TEXT_DOMAIN,
640Sstevel@tonic-gate 			    "Your password will expire within one hour."));
650Sstevel@tonic-gate 		else
660Sstevel@tonic-gate 			(void) snprintf(messages[0], sizeof (messages[0]),
670Sstevel@tonic-gate 			    dgettext(TEXT_DOMAIN,
680Sstevel@tonic-gate 				"Your password will expire in %d hours."),
690Sstevel@tonic-gate 				(seconds_h == 0) ? hours : hours + 1);
700Sstevel@tonic-gate 	} else {
710Sstevel@tonic-gate 			(void) snprintf(messages[0], sizeof (messages[0]),
720Sstevel@tonic-gate 			    dgettext(TEXT_DOMAIN,
730Sstevel@tonic-gate 				"Your password will expire in %d days."),
740Sstevel@tonic-gate 				(seconds_d == 0) ? days : days + 1);
750Sstevel@tonic-gate 	}
760Sstevel@tonic-gate 
770Sstevel@tonic-gate 	(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
780Sstevel@tonic-gate }
790Sstevel@tonic-gate 
80*1179Svv149972 /*
81*1179Svv149972  * display_acct_unlock_time - Display the time left for the account to
82*1179Svv149972  * get auto unlocked after the maximum login failures has reached.
83*1179Svv149972  */
84*1179Svv149972 static void
display_acct_unlock_time(pam_handle_t * pamh,int sec_b4_unlock)85*1179Svv149972 display_acct_unlock_time(pam_handle_t *pamh, int sec_b4_unlock)
86*1179Svv149972 {
87*1179Svv149972 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
88*1179Svv149972 	int	days = 0, hours = 0;
89*1179Svv149972 	int	seconds_d = 0, seconds_h = 0;
90*1179Svv149972 
91*1179Svv149972 	/* Account is locked forever */
92*1179Svv149972 	if (sec_b4_unlock == -1) {
93*1179Svv149972 		(void) snprintf(messages[0], sizeof (messages[0]),
94*1179Svv149972 		dgettext(TEXT_DOMAIN,
95*1179Svv149972 		"Your account is locked, please contact administrator."));
96*1179Svv149972 		(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
97*1179Svv149972 			messages, NULL);
98*1179Svv149972 		return;
99*1179Svv149972 	}
100*1179Svv149972 
101*1179Svv149972 	days = sec_b4_unlock / 86400;
102*1179Svv149972 	seconds_d = sec_b4_unlock % 86400;
103*1179Svv149972 	hours = (days * 24) + seconds_d / 3600;
104*1179Svv149972 	seconds_h = seconds_d % 3600;
105*1179Svv149972 
106*1179Svv149972 	if (sec_b4_unlock <= (86400 * 2)) {
107*1179Svv149972 		if (seconds_d <= 3600 && days == 0)
108*1179Svv149972 			(void) snprintf(messages[0], sizeof (messages[0]),
109*1179Svv149972 				dgettext(TEXT_DOMAIN,
110*1179Svv149972 				"Your account is locked and will be unlocked"
111*1179Svv149972 				" within one hour."));
112*1179Svv149972 		else
113*1179Svv149972 			(void) snprintf(messages[0], sizeof (messages[0]),
114*1179Svv149972 				dgettext(TEXT_DOMAIN,
115*1179Svv149972 				"Your account is locked and will be unlocked"
116*1179Svv149972 				" in %d hours."),
117*1179Svv149972 				(seconds_h == 0) ? hours : hours + 1);
118*1179Svv149972 	} else {
119*1179Svv149972 		(void) snprintf(messages[0], sizeof (messages[0]),
120*1179Svv149972 			dgettext(TEXT_DOMAIN,
121*1179Svv149972 			"Your account is locked and will be unlocked"
122*1179Svv149972 			" in %d days."),
123*1179Svv149972 			(seconds_d == 0) ? days : days + 1);
124*1179Svv149972 	}
125*1179Svv149972 
126*1179Svv149972 	(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
127*1179Svv149972 }
128*1179Svv149972 
129*1179Svv149972 /*
130*1179Svv149972  * warn_user_passwd_expired - warn the user that the password has expired
131*1179Svv149972  */
132*1179Svv149972 static void
warn_user_passwd_expired(pam_handle_t * pamh,int grace)133*1179Svv149972 warn_user_passwd_expired(pam_handle_t *pamh, int grace)
134*1179Svv149972 {
135*1179Svv149972 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
136*1179Svv149972 
137*1179Svv149972 	if (grace)
138*1179Svv149972 		(void) snprintf(messages[0], sizeof (messages[0]),
139*1179Svv149972 			dgettext(TEXT_DOMAIN,
140*1179Svv149972 			"Your password has expired. "
141*1179Svv149972 			"Number of grace logins allowed are %d."),
142*1179Svv149972 			grace);
143*1179Svv149972 	else
144*1179Svv149972 		(void) snprintf(messages[0], sizeof (messages[0]),
145*1179Svv149972 			dgettext(TEXT_DOMAIN,
146*1179Svv149972 			"Your password has expired."));
147*1179Svv149972 
148*1179Svv149972 	(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
149*1179Svv149972 }
150*1179Svv149972 
151*1179Svv149972 /*
152*1179Svv149972  * display_passwd_reset_msg - tell user that password has been reset by
153*1179Svv149972  * administrator
154*1179Svv149972  */
155*1179Svv149972 static void
display_passwd_reset_msg(pam_handle_t * pamh)156*1179Svv149972 display_passwd_reset_msg(pam_handle_t *pamh)
157*1179Svv149972 {
158*1179Svv149972 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
159*1179Svv149972 
160*1179Svv149972 	(void) snprintf(messages[0], sizeof (messages[0]),
161*1179Svv149972 			dgettext(TEXT_DOMAIN,
162*1179Svv149972 			"Your password has been reset by administrator."));
163*1179Svv149972 
164*1179Svv149972 	(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
165*1179Svv149972 }
166*1179Svv149972 
167*1179Svv149972 /*
168*1179Svv149972  * Retreives account management related attributes for the user using
169*1179Svv149972  * default binding and does local account checks .
170*1179Svv149972  *
171*1179Svv149972  * Return Value: PAM_SUCCESS - If account is valid, seconds param will have
172*1179Svv149972  *				seconds left for password to expire
173*1179Svv149972  *		 PAM_ACCT_EXPIRED - If account is inactive
174*1179Svv149972  *		 PAM_NEW_AUTHTOK_REQD - Password is reset by admin
175*1179Svv149972  *		 PAM_AUTHTOK_EXPIRED - User password has expired, grace
176*1179Svv149972  *				param will have no. of grace logins allowed
177*1179Svv149972  *		 PAM_MAXTRIES - If maximum failure of wrong password has reached
178*1179Svv149972  *				seconds param will have no. of seconds for the
179*1179Svv149972  *				account to get unlocked
180*1179Svv149972  *		 PAM_AUTH_ERR - Failure return code
181*1179Svv149972  */
182*1179Svv149972 static int
get_account_mgmt(char * user,int * seconds,int * grace)183*1179Svv149972 get_account_mgmt(char *user, int *seconds, int *grace)
184*1179Svv149972 {
185*1179Svv149972 	int rc	= PAM_AUTH_ERR;
186*1179Svv149972 	AcctUsableResponse_t	acctResp;
187*1179Svv149972 
188*1179Svv149972 	(void *)memset((void*)&acctResp, 0, sizeof (acctResp));
189*1179Svv149972 	/* get the values for local account checking */
190*1179Svv149972 	if ((rc = __ns_ldap_getAcctMgmt(user, &acctResp))
191*1179Svv149972 		!= NS_LDAP_SUCCESS) {
192*1179Svv149972 		syslog(LOG_DEBUG,
193*1179Svv149972 			"__ns_ldap_getAcctMgmt() failed for %s with error %d",
194*1179Svv149972 				user, rc);
195*1179Svv149972 		return (PAM_AUTH_ERR);
196*1179Svv149972 	}
197*1179Svv149972 
198*1179Svv149972 	if (acctResp.choice == 0) {
199*1179Svv149972 		/* should be able to login */
200*1179Svv149972 		*seconds =
201*1179Svv149972 			acctResp.AcctUsableResp.seconds_before_expiry;
202*1179Svv149972 		return (PAM_SUCCESS);
203*1179Svv149972 	} else if (acctResp.choice == 1) {
204*1179Svv149972 		/* cannot login */
205*1179Svv149972 		if (acctResp.AcctUsableResp.more_info.inactive)
206*1179Svv149972 			/* entry inactive */
207*1179Svv149972 			return (PAM_ACCT_EXPIRED);
208*1179Svv149972 		if (acctResp.AcctUsableResp.more_info.reset)
209*1179Svv149972 			/* password reset by administrator */
210*1179Svv149972 			return (PAM_NEW_AUTHTOK_REQD);
211*1179Svv149972 		if (acctResp.AcctUsableResp.more_info.expired) {
212*1179Svv149972 			/*
213*1179Svv149972 			 * password expired, check for grace logins.
214*1179Svv149972 			 */
215*1179Svv149972 			*grace =
216*1179Svv149972 				acctResp.AcctUsableResp.more_info.rem_grace;
217*1179Svv149972 			return (PAM_AUTHTOK_EXPIRED);
218*1179Svv149972 		}
219*1179Svv149972 		if (acctResp.AcctUsableResp.more_info.sec_b4_unlock) {
220*1179Svv149972 			/* max failures reached, seconds before unlock */
221*1179Svv149972 			*seconds =
222*1179Svv149972 				acctResp.AcctUsableResp.more_info.sec_b4_unlock;
223*1179Svv149972 			return (PAM_MAXTRIES);
224*1179Svv149972 		}
225*1179Svv149972 	}
226*1179Svv149972 	return (PAM_AUTH_ERR);
227*1179Svv149972 }
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate /*
2300Sstevel@tonic-gate  * pam_sm_acct_mgmt	main account managment routine.
2310Sstevel@tonic-gate  *			This routine relies on the LDAP
2320Sstevel@tonic-gate  *			directory server to provide the
2330Sstevel@tonic-gate  * 			password aging and account lockout
2340Sstevel@tonic-gate  * 			information. This is done by first
2350Sstevel@tonic-gate  *			trying to authenticate the user and
2360Sstevel@tonic-gate  *			then checking the password status
2370Sstevel@tonic-gate  *			returned.
2380Sstevel@tonic-gate  *
2390Sstevel@tonic-gate  *			Returns: module error or specific
2400Sstevel@tonic-gate  *			error on failure.
2410Sstevel@tonic-gate  */
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)2440Sstevel@tonic-gate pam_sm_acct_mgmt(
2450Sstevel@tonic-gate 	pam_handle_t *pamh,
2460Sstevel@tonic-gate 	int	flags,
2470Sstevel@tonic-gate 	int	argc,
2480Sstevel@tonic-gate 	const char **argv)
2490Sstevel@tonic-gate {
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	char			*user = NULL;
2520Sstevel@tonic-gate 	int			result = PAM_AUTH_ERR;
2530Sstevel@tonic-gate 	int			debug = 0;
2540Sstevel@tonic-gate 	int			i;
2550Sstevel@tonic-gate 	char			*password = NULL;
2560Sstevel@tonic-gate 	ns_cred_t		*credp = NULL;
2570Sstevel@tonic-gate 	int			nowarn = 0;
258*1179Svv149972 	int			seconds = 0, grace = 0;
2590Sstevel@tonic-gate 	ldap_authtok_data	*status;
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	for (i = 0; i < argc; i++) {
2620Sstevel@tonic-gate 		if (strcmp(argv[i], "debug") == 0)
2630Sstevel@tonic-gate 			debug = 1;
2640Sstevel@tonic-gate 		else if (strcasecmp(argv[i], "nowarn") == 0) {
2650Sstevel@tonic-gate 			nowarn = 1;
2660Sstevel@tonic-gate 			flags = flags | PAM_SILENT;
2670Sstevel@tonic-gate 		}
2680Sstevel@tonic-gate 		else
2690Sstevel@tonic-gate 			syslog(LOG_DEBUG,
2700Sstevel@tonic-gate 				"pam_ldap pam_sm_acct_mgmt: "
2710Sstevel@tonic-gate 				"illegal option %s",
2720Sstevel@tonic-gate 				argv[i]);
2730Sstevel@tonic-gate 	}
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	if ((result = pam_get_item(pamh, PAM_USER, (void **)&user))
2760Sstevel@tonic-gate 							!= PAM_SUCCESS)
2770Sstevel@tonic-gate 		goto out;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	if (debug)
2800Sstevel@tonic-gate 		syslog(LOG_DEBUG,
2810Sstevel@tonic-gate 			"ldap pam_sm_acct_mgmt(%s), flags = %x %s",
2820Sstevel@tonic-gate 			(user)?user:"no-user", flags,
2830Sstevel@tonic-gate 			(nowarn)? ", nowarn": "");
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	if (user == NULL) {
2860Sstevel@tonic-gate 		result = PAM_USER_UNKNOWN;
2870Sstevel@tonic-gate 		goto out;
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	/* retrieve the password from the PAM handle */
2910Sstevel@tonic-gate 	result = pam_get_item(pamh, PAM_AUTHTOK, (void **) &password);
2920Sstevel@tonic-gate 	if (password == NULL) {
293*1179Svv149972 		if (debug)
294*1179Svv149972 			syslog(LOG_DEBUG, "ldap pam_sm_acct_mgmt: "
295*1179Svv149972 			    "no password for user %s", user);
296*1179Svv149972 		/* Do local account checking */
297*1179Svv149972 		result = get_account_mgmt(user, &seconds, &grace);
298*1179Svv149972 	} else {
299*1179Svv149972 		/* Try to authenticate to get password management info */
300*1179Svv149972 		result = authenticate(&credp, user,
301*1179Svv149972 				password, &seconds);
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	/*
3050Sstevel@tonic-gate 	 * process the password management info.
3060Sstevel@tonic-gate 	 * If user needs to change the password immediately,
3070Sstevel@tonic-gate 	 * just return the rc.
3080Sstevel@tonic-gate 	 * Otherwise, reset rc to the appropriate PAM error or
3090Sstevel@tonic-gate 	 * warn the user about password expiration.
3100Sstevel@tonic-gate 	 */
311*1179Svv149972 	if (result == PAM_MAXTRIES) {
312*1179Svv149972 		/* exceed retry limit, denied access to account */
313*1179Svv149972 		if (!(flags & PAM_SILENT))
314*1179Svv149972 			display_acct_unlock_time(pamh, seconds);
3150Sstevel@tonic-gate 		result = PAM_PERM_DENIED;
316*1179Svv149972 	} else if (result == PAM_ACCT_EXPIRED)
317*1179Svv149972 		/* account is inactivated */
3180Sstevel@tonic-gate 		result = PAM_ACCT_EXPIRED;
319*1179Svv149972 	else if (result == PAM_AUTHTOK_EXPIRED) {
320*1179Svv149972 		if (!(flags & PAM_SILENT))
321*1179Svv149972 			warn_user_passwd_expired(pamh, grace);
322*1179Svv149972 		/* password expired, check for grace logins */
323*1179Svv149972 		if (grace > 0)
324*1179Svv149972 			result = PAM_SUCCESS;
325*1179Svv149972 		else
326*1179Svv149972 			result = PAM_AUTHTOK_EXPIRED;
327*1179Svv149972 	} else if (result == PAM_NEW_AUTHTOK_REQD) {
328*1179Svv149972 		/* password has been reset by administrator */
329*1179Svv149972 		if (!(flags & PAM_SILENT))
330*1179Svv149972 			display_passwd_reset_msg(pamh);
331*1179Svv149972 		result = PAM_NEW_AUTHTOK_REQD;
332*1179Svv149972 	} else if (result == PAM_SUCCESS) {
3330Sstevel@tonic-gate 		/*
3340Sstevel@tonic-gate 		 * warn the user if the password
3350Sstevel@tonic-gate 		 * is about to expire.
3360Sstevel@tonic-gate 		 */
3370Sstevel@tonic-gate 		if (!(flags & PAM_SILENT) &&
338*1179Svv149972 			seconds > 0)
3390Sstevel@tonic-gate 			warn_user_passwd_will_expire(pamh,
340*1179Svv149972 				seconds);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate out:
3450Sstevel@tonic-gate 	if (credp != NULL)
3460Sstevel@tonic-gate 		(void) __ns_ldap_freeCred(&credp);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	/* store the password aging status in the pam handle */
3490Sstevel@tonic-gate 	if (result != PAM_SUCCESS) {
3500Sstevel@tonic-gate 		int pam_res;
3510Sstevel@tonic-gate 		ldap_authtok_data *authtok_data;
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		pam_res = pam_get_data(
3540Sstevel@tonic-gate 			pamh, LDAP_AUTHTOK_DATA, (const void **)&authtok_data);
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 		if ((status = (ldap_authtok_data *)calloc
3570Sstevel@tonic-gate 			(1, sizeof (ldap_authtok_data))) == NULL) {
3580Sstevel@tonic-gate 			return (PAM_BUF_ERR);
3590Sstevel@tonic-gate 		}
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 		if (pam_res == PAM_SUCCESS)
3620Sstevel@tonic-gate 			(void) memcpy(status, authtok_data,
3630Sstevel@tonic-gate 				sizeof (ldap_authtok_data));
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 		status->age_status = result;
3660Sstevel@tonic-gate 		if (pam_set_data(pamh, LDAP_AUTHTOK_DATA, status, ldap_cleanup)
3670Sstevel@tonic-gate 							!= PAM_SUCCESS) {
3680Sstevel@tonic-gate 			free(status);
3690Sstevel@tonic-gate 			return (PAM_SERVICE_ERR);
3700Sstevel@tonic-gate 		}
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	return (result);
3740Sstevel@tonic-gate }
375