Home | History | Annotate | Download | only in ldap
      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