OpenSolaris_b135/lib/pam_modules/ldap/ldap_utils.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "ldap_headers.h"
#include <malloc.h>

/* ******************************************************************** */
/*									*/
/* 		Utilities Functions					*/
/*									*/
/* ******************************************************************** */

/*
 * __ldap_to_pamerror():
 *	converts Native LDAP errors to an equivalent PAM error
 */
int
__ldap_to_pamerror(int ldaperror)
{
	switch (ldaperror) {
		case NS_LDAP_SUCCESS:
			return (PAM_SUCCESS);

		case NS_LDAP_OP_FAILED:
			return (PAM_PERM_DENIED);

		case NS_LDAP_MEMORY:
			return (PAM_BUF_ERR);

		case NS_LDAP_CONFIG:
			return (PAM_SERVICE_ERR);

		case NS_LDAP_NOTFOUND:
		case NS_LDAP_INTERNAL:
		case NS_LDAP_PARTIAL:
		case NS_LDAP_INVALID_PARAM:
			return (PAM_SYSTEM_ERR);

		default:
			return (PAM_SYSTEM_ERR);

	}
}

/*
 * authenticate():
 *	Returns
 *	  PAM_SUCCESS            if authenticated successfully
 *	  PAM_NEW_AUTHTOK_REQD   if authenticated but user needs to
 *                               change password immediately
 *        PAM_MAXTRIES           if authentication fails due to too
 *                               many login failures
 *        PAM_AUTHTOK_EXPIRED    if user password expired
 *        PAM_PERM_DENIED        if fail to authenticate
 *        PAM_AUTH_ERR           other errors
 *
 *      Also output the second-until-expired data if authenticated
 *      but the password is about to expire.
 *	Authentication is checked by calling __ns_ldap_auth.
 */
int
authenticate(ns_cred_t **credpp, char *usrname, char *pwd,
		int *sec_until_expired)
{
	int		result = PAM_AUTH_ERR;
	int		ldaprc;
	int		authstried = 0;
	char		*binddn = NULL;
	char		**certpath = NULL;
	ns_auth_t	**app;
	ns_auth_t	**authpp = NULL;
	ns_auth_t	*authp = NULL;
	ns_cred_t	*credp;
	ns_ldap_error_t	*errorp = NULL;

	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
		return (PAM_BUF_ERR);

	/* Fill in the user name and password */
	if ((usrname == NULL) || (pwd == NULL) || (usrname[0] == '\0') ||
		(pwd[0] == '\0'))
		goto out;

	ldaprc = __ns_ldap_uid2dn(usrname, &binddn, NULL, &errorp);
	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
		goto out;

	credp->cred.unix_cred.userID = strdup(binddn);
	credp->cred.unix_cred.passwd = strdup(pwd);
	if ((credp->cred.unix_cred.userID == NULL) ||
		(credp->cred.unix_cred.passwd == NULL)) {
		result = PAM_BUF_ERR;
		goto out;
	}

	/* get host certificate path, if one is configured */
	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
		(void ***)&certpath, &errorp);
	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
		goto out;
	if (certpath && *certpath)
		credp->hostcertpath = *certpath;

	/* Load the service specific authentication method */
	ldaprc = __ns_ldap_getServiceAuthMethods("pam_ldap", &authpp, &errorp);
	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
		goto out;

	/*
	 * if authpp is null, there is no serviceAuthenticationMethod
	 * try default authenticationMethod
	 */
	if (authpp == NULL) {
		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
			&errorp);
		if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
			goto out;
	}

	/*
	 * if authpp is still null, then can not authenticate, syslog
	 * error message and return error
	 */
	if (authpp == NULL) {
		syslog(LOG_ERR,
			"pam_ldap: no authentication method configured");
		result = PAM_AUTH_ERR;
		goto out;
	}

	/*
	 * Walk the array and try all authentication methods in order except
	 * for "none".
	 */
	for (app = authpp; *app; app++) {
		authp = *app;
		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
		if (authp->type == NS_LDAP_AUTH_NONE)
			continue;
		authstried++;
		credp->auth.type = authp->type;
		credp->auth.tlstype = authp->tlstype;
		credp->auth.saslmech = authp->saslmech;
		credp->auth.saslopt = authp->saslopt;
		ldaprc = __ns_ldap_auth(credp, 0, &errorp, NULL, NULL);

		/*
		 * If rc is NS_LDAP_SUCCESS, done. If not,
		 * check rc and error info to see if
		 * there's any password management data.
		 * If yes, set appropriate PAM result code
		 * and exit.
		 */
		if (ldaprc == NS_LDAP_SUCCESS) {
			/*
			 * authenticated and no
			 * password management info, done.
			 */
			result = PAM_SUCCESS;
			goto out;
		} else if (ldaprc == NS_LDAP_SUCCESS_WITH_INFO) {
			/*
			 * authenticated but need to deal with
			 * password management info
			 */
			result = PAM_SUCCESS;

			/*
			 * clear sec_until_expired just in case
			 * there's no error info
			 */
			if (sec_until_expired)
				*sec_until_expired = 0;

			if (errorp) {
				if (errorp->pwd_mgmt.status ==
					NS_PASSWD_ABOUT_TO_EXPIRE) {
					/*
					 * password about to expire;
					 * retrieve "seconds until expired"
					 */
					if (sec_until_expired)
						*sec_until_expired =
						errorp->
						pwd_mgmt.sec_until_expired;
				} else if (errorp->pwd_mgmt.status ==
					NS_PASSWD_CHANGE_NEEDED)
					/*
					 * indicate that passwd need to change
					 * right away
					 */
					result = PAM_NEW_AUTHTOK_REQD;

				(void) __ns_ldap_freeError(&errorp);
			}
			goto out;
		} else if (ldaprc == NS_LDAP_INTERNAL) {

			if (errorp) {
				/*
				 * If error due to password policy, set
				 * appropriate PAM result code and exit.
				 */
				if (errorp->pwd_mgmt.status ==
					NS_PASSWD_RETRY_EXCEEDED)
					result = PAM_MAXTRIES;
				else if (errorp->pwd_mgmt.status ==
					NS_PASSWD_EXPIRED)
					result = PAM_AUTHTOK_EXPIRED;
				else {
					/*
					 * If invalid credential,
					 * return PAM_AUTH_ERR.
					 */
					if (errorp->status ==
						LDAP_INVALID_CREDENTIALS)
						result = PAM_AUTH_ERR;
				}
				(void) __ns_ldap_freeError(&errorp);
				goto out;
			}
		}

		/* done with the error info, clean it up */
		if (errorp)
			(void) __ns_ldap_freeError(&errorp);
	}
	if (authstried == 0) {
		syslog(LOG_ERR,
			"pam_ldap: no legal authentication method configured");
		result = PAM_AUTH_ERR;
		goto out;
	}
	result = PAM_PERM_DENIED;

out:
	if (binddn)
		free(binddn);

	if (credp && (result == PAM_SUCCESS ||
		result == PAM_NEW_AUTHTOK_REQD))
		if (credpp)
			*credpp = credp;
	else
		(void) __ns_ldap_freeCred(&credp);

	if (authpp)
		(void) __ns_ldap_freeParam((void ***)&authpp);

	if (errorp)
		(void) __ns_ldap_freeError(&errorp);

	return (result);
}