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 2004 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 #include <malloc.h>
     31 
     32 /* ******************************************************************** */
     33 /*									*/
     34 /* 		Utilities Functions					*/
     35 /*									*/
     36 /* ******************************************************************** */
     37 
     38 /*
     39  * __ldap_to_pamerror():
     40  *	converts Native LDAP errors to an equivalent PAM error
     41  */
     42 int
     43 __ldap_to_pamerror(int ldaperror)
     44 {
     45 	switch (ldaperror) {
     46 		case NS_LDAP_SUCCESS:
     47 			return (PAM_SUCCESS);
     48 
     49 		case NS_LDAP_OP_FAILED:
     50 			return (PAM_PERM_DENIED);
     51 
     52 		case NS_LDAP_MEMORY:
     53 			return (PAM_BUF_ERR);
     54 
     55 		case NS_LDAP_CONFIG:
     56 			return (PAM_SERVICE_ERR);
     57 
     58 		case NS_LDAP_NOTFOUND:
     59 		case NS_LDAP_INTERNAL:
     60 		case NS_LDAP_PARTIAL:
     61 		case NS_LDAP_INVALID_PARAM:
     62 			return (PAM_SYSTEM_ERR);
     63 
     64 		default:
     65 			return (PAM_SYSTEM_ERR);
     66 
     67 	}
     68 }
     69 
     70 /*
     71  * authenticate():
     72  *	Returns
     73  *	  PAM_SUCCESS            if authenticated successfully
     74  *	  PAM_NEW_AUTHTOK_REQD   if authenticated but user needs to
     75  *                               change password immediately
     76  *        PAM_MAXTRIES           if authentication fails due to too
     77  *                               many login failures
     78  *        PAM_AUTHTOK_EXPIRED    if user password expired
     79  *        PAM_PERM_DENIED        if fail to authenticate
     80  *        PAM_AUTH_ERR           other errors
     81  *
     82  *      Also output the second-until-expired data if authenticated
     83  *      but the password is about to expire.
     84  *	Authentication is checked by calling __ns_ldap_auth.
     85  */
     86 int
     87 authenticate(ns_cred_t **credpp, char *usrname, char *pwd,
     88 		int *sec_until_expired)
     89 {
     90 	int		result = PAM_AUTH_ERR;
     91 	int		ldaprc;
     92 	int		authstried = 0;
     93 	char		*binddn = NULL;
     94 	char		**certpath = NULL;
     95 	ns_auth_t	**app;
     96 	ns_auth_t	**authpp = NULL;
     97 	ns_auth_t	*authp = NULL;
     98 	ns_cred_t	*credp;
     99 	ns_ldap_error_t	*errorp = NULL;
    100 
    101 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
    102 		return (PAM_BUF_ERR);
    103 
    104 	/* Fill in the user name and password */
    105 	if ((usrname == NULL) || (pwd == NULL) || (usrname[0] == '\0') ||
    106 		(pwd[0] == '\0'))
    107 		goto out;
    108 
    109 	ldaprc = __ns_ldap_uid2dn(usrname, &binddn, NULL, &errorp);
    110 	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
    111 		goto out;
    112 
    113 	credp->cred.unix_cred.userID = strdup(binddn);
    114 	credp->cred.unix_cred.passwd = strdup(pwd);
    115 	if ((credp->cred.unix_cred.userID == NULL) ||
    116 		(credp->cred.unix_cred.passwd == NULL)) {
    117 		result = PAM_BUF_ERR;
    118 		goto out;
    119 	}
    120 
    121 	/* get host certificate path, if one is configured */
    122 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
    123 		(void ***)&certpath, &errorp);
    124 	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
    125 		goto out;
    126 	if (certpath && *certpath)
    127 		credp->hostcertpath = *certpath;
    128 
    129 	/* Load the service specific authentication method */
    130 	ldaprc = __ns_ldap_getServiceAuthMethods("pam_ldap", &authpp, &errorp);
    131 	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
    132 		goto out;
    133 
    134 	/*
    135 	 * if authpp is null, there is no serviceAuthenticationMethod
    136 	 * try default authenticationMethod
    137 	 */
    138 	if (authpp == NULL) {
    139 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
    140 			&errorp);
    141 		if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
    142 			goto out;
    143 	}
    144 
    145 	/*
    146 	 * if authpp is still null, then can not authenticate, syslog
    147 	 * error message and return error
    148 	 */
    149 	if (authpp == NULL) {
    150 		syslog(LOG_ERR,
    151 			"pam_ldap: no authentication method configured");
    152 		result = PAM_AUTH_ERR;
    153 		goto out;
    154 	}
    155 
    156 	/*
    157 	 * Walk the array and try all authentication methods in order except
    158 	 * for "none".
    159 	 */
    160 	for (app = authpp; *app; app++) {
    161 		authp = *app;
    162 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
    163 		if (authp->type == NS_LDAP_AUTH_NONE)
    164 			continue;
    165 		authstried++;
    166 		credp->auth.type = authp->type;
    167 		credp->auth.tlstype = authp->tlstype;
    168 		credp->auth.saslmech = authp->saslmech;
    169 		credp->auth.saslopt = authp->saslopt;
    170 		ldaprc = __ns_ldap_auth(credp, 0, &errorp, NULL, NULL);
    171 
    172 		/*
    173 		 * If rc is NS_LDAP_SUCCESS, done. If not,
    174 		 * check rc and error info to see if
    175 		 * there's any password management data.
    176 		 * If yes, set appropriate PAM result code
    177 		 * and exit.
    178 		 */
    179 		if (ldaprc == NS_LDAP_SUCCESS) {
    180 			/*
    181 			 * authenticated and no
    182 			 * password management info, done.
    183 			 */
    184 			result = PAM_SUCCESS;
    185 			goto out;
    186 		} else if (ldaprc == NS_LDAP_SUCCESS_WITH_INFO) {
    187 			/*
    188 			 * authenticated but need to deal with
    189 			 * password management info
    190 			 */
    191 			result = PAM_SUCCESS;
    192 
    193 			/*
    194 			 * clear sec_until_expired just in case
    195 			 * there's no error info
    196 			 */
    197 			if (sec_until_expired)
    198 				*sec_until_expired = 0;
    199 
    200 			if (errorp) {
    201 				if (errorp->pwd_mgmt.status ==
    202 					NS_PASSWD_ABOUT_TO_EXPIRE) {
    203 					/*
    204 					 * password about to expire;
    205 					 * retrieve "seconds until expired"
    206 					 */
    207 					if (sec_until_expired)
    208 						*sec_until_expired =
    209 						errorp->
    210 						pwd_mgmt.sec_until_expired;
    211 				} else if (errorp->pwd_mgmt.status ==
    212 					NS_PASSWD_CHANGE_NEEDED)
    213 					/*
    214 					 * indicate that passwd need to change
    215 					 * right away
    216 					 */
    217 					result = PAM_NEW_AUTHTOK_REQD;
    218 
    219 				(void) __ns_ldap_freeError(&errorp);
    220 			}
    221 			goto out;
    222 		} else if (ldaprc == NS_LDAP_INTERNAL) {
    223 
    224 			if (errorp) {
    225 				/*
    226 				 * If error due to password policy, set
    227 				 * appropriate PAM result code and exit.
    228 				 */
    229 				if (errorp->pwd_mgmt.status ==
    230 					NS_PASSWD_RETRY_EXCEEDED)
    231 					result = PAM_MAXTRIES;
    232 				else if (errorp->pwd_mgmt.status ==
    233 					NS_PASSWD_EXPIRED)
    234 					result = PAM_AUTHTOK_EXPIRED;
    235 				else {
    236 					/*
    237 					 * If invalid credential,
    238 					 * return PAM_AUTH_ERR.
    239 					 */
    240 					if (errorp->status ==
    241 						LDAP_INVALID_CREDENTIALS)
    242 						result = PAM_AUTH_ERR;
    243 				}
    244 				(void) __ns_ldap_freeError(&errorp);
    245 				goto out;
    246 			}
    247 		}
    248 
    249 		/* done with the error info, clean it up */
    250 		if (errorp)
    251 			(void) __ns_ldap_freeError(&errorp);
    252 	}
    253 	if (authstried == 0) {
    254 		syslog(LOG_ERR,
    255 			"pam_ldap: no legal authentication method configured");
    256 		result = PAM_AUTH_ERR;
    257 		goto out;
    258 	}
    259 	result = PAM_PERM_DENIED;
    260 
    261 out:
    262 	if (binddn)
    263 		free(binddn);
    264 
    265 	if (credp && (result == PAM_SUCCESS ||
    266 		result == PAM_NEW_AUTHTOK_REQD))
    267 		if (credpp)
    268 			*credpp = credp;
    269 	else
    270 		(void) __ns_ldap_freeCred(&credp);
    271 
    272 	if (authpp)
    273 		(void) __ns_ldap_freeParam((void ***)&authpp);
    274 
    275 	if (errorp)
    276 		(void) __ns_ldap_freeError(&errorp);
    277 
    278 	return (result);
    279 }
    280