2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "ldap_headers.h"
2N/A#include <malloc.h>
2N/A
2N/A/* ******************************************************************** */
2N/A/* */
2N/A/* Utilities Functions */
2N/A/* */
2N/A/* ******************************************************************** */
2N/A
2N/A/*
2N/A * __ldap_to_pamerror():
2N/A * converts Native LDAP errors to an equivalent PAM error
2N/A */
2N/Aint
2N/A__ldap_to_pamerror(int ldaperror)
2N/A{
2N/A switch (ldaperror) {
2N/A case NS_LDAP_SUCCESS:
2N/A return (PAM_SUCCESS);
2N/A
2N/A case NS_LDAP_OP_FAILED:
2N/A return (PAM_PERM_DENIED);
2N/A
2N/A case NS_LDAP_MEMORY:
2N/A return (PAM_BUF_ERR);
2N/A
2N/A case NS_LDAP_CONFIG:
2N/A return (PAM_SERVICE_ERR);
2N/A
2N/A case NS_LDAP_NOTFOUND:
2N/A case NS_LDAP_INTERNAL:
2N/A case NS_LDAP_PARTIAL:
2N/A case NS_LDAP_INVALID_PARAM:
2N/A return (PAM_SYSTEM_ERR);
2N/A
2N/A default:
2N/A return (PAM_SYSTEM_ERR);
2N/A
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * authenticate():
2N/A * Returns
2N/A * PAM_SUCCESS if authenticated successfully
2N/A * PAM_NEW_AUTHTOK_REQD if authenticated but user needs to
2N/A * change password immediately
2N/A * PAM_MAXTRIES if authentication fails due to too
2N/A * many login failures
2N/A * PAM_AUTHTOK_EXPIRED if user password expired
2N/A * PAM_PERM_DENIED if fail to authenticate
2N/A * PAM_AUTH_ERR other errors
2N/A *
2N/A * Also output the second-until-expired data if authenticated
2N/A * but the password is about to expire.
2N/A * Authentication is checked by calling __ns_ldap_auth.
2N/A */
2N/Aint
2N/Aauthenticate(ns_cred_t **credpp, char *usrname, char *pwd,
2N/A int *sec_until_expired)
2N/A{
2N/A int result = PAM_AUTH_ERR;
2N/A int ldaprc;
2N/A int authstried = 0;
2N/A char *binddn = NULL;
2N/A char **certpath = NULL;
2N/A ns_auth_t **app;
2N/A ns_auth_t **authpp = NULL;
2N/A ns_auth_t *authp = NULL;
2N/A ns_cred_t *credp;
2N/A ns_ldap_error_t *errorp = NULL;
2N/A
2N/A if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
2N/A return (PAM_BUF_ERR);
2N/A
2N/A /* Fill in the user name and password */
2N/A if ((usrname == NULL) || (pwd == NULL) || (usrname[0] == '\0') ||
2N/A (pwd[0] == '\0'))
2N/A goto out;
2N/A
2N/A ldaprc = __ns_ldap_uid2dn(usrname, &binddn, NULL, &errorp);
2N/A if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
2N/A goto out;
2N/A
2N/A credp->cred.unix_cred.userID = strdup(binddn);
2N/A credp->cred.unix_cred.passwd = strdup(pwd);
2N/A if ((credp->cred.unix_cred.userID == NULL) ||
2N/A (credp->cred.unix_cred.passwd == NULL)) {
2N/A result = PAM_BUF_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A /* get host certificate path, if one is configured */
2N/A ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
2N/A (void ***)&certpath, &errorp);
2N/A if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
2N/A goto out;
2N/A if (certpath && *certpath) {
2N/A /*
2N/A * __ns_ldap_freeCred frees the hostcertpath member, so we
2N/A * must assign a copy. Otherwise freeParam and freeCred
2N/A * below will double-free the string.
2N/A */
2N/A credp->hostcertpath = strdup(*certpath);
2N/A if (credp->hostcertpath == NULL) {
2N/A result = PAM_BUF_ERR;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* Load the service specific authentication method */
2N/A ldaprc = __ns_ldap_getServiceAuthMethods("pam_ldap", &authpp, &errorp);
2N/A if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
2N/A goto out;
2N/A
2N/A /*
2N/A * if authpp is null, there is no serviceAuthenticationMethod
2N/A * try default authenticationMethod
2N/A */
2N/A if (authpp == NULL) {
2N/A ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
2N/A &errorp);
2N/A if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * if authpp is still null, then can not authenticate, syslog
2N/A * error message and return error
2N/A */
2N/A if (authpp == NULL) {
2N/A syslog(LOG_ERR,
2N/A "pam_ldap: no authentication method configured");
2N/A result = PAM_AUTH_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Walk the array and try all authentication methods in order except
2N/A * for "none".
2N/A */
2N/A for (app = authpp; *app; app++) {
2N/A authp = *app;
2N/A /* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
2N/A if (authp->type == NS_LDAP_AUTH_NONE)
2N/A continue;
2N/A authstried++;
2N/A credp->auth.type = authp->type;
2N/A credp->auth.tlstype = authp->tlstype;
2N/A credp->auth.saslmech = authp->saslmech;
2N/A credp->auth.saslopt = authp->saslopt;
2N/A ldaprc = __ns_ldap_auth(credp, 0, &errorp, NULL, NULL);
2N/A
2N/A /*
2N/A * If rc is NS_LDAP_SUCCESS, done. If not,
2N/A * check rc and error info to see if
2N/A * there's any password management data.
2N/A * If yes, set appropriate PAM result code
2N/A * and exit.
2N/A */
2N/A if (ldaprc == NS_LDAP_SUCCESS) {
2N/A /*
2N/A * authenticated and no
2N/A * password management info, done.
2N/A */
2N/A result = PAM_SUCCESS;
2N/A goto out;
2N/A } else if (ldaprc == NS_LDAP_SUCCESS_WITH_INFO) {
2N/A /*
2N/A * authenticated but need to deal with
2N/A * password management info
2N/A */
2N/A result = PAM_SUCCESS;
2N/A
2N/A /*
2N/A * clear sec_until_expired just in case
2N/A * there's no error info
2N/A */
2N/A if (sec_until_expired)
2N/A *sec_until_expired = 0;
2N/A
2N/A if (errorp) {
2N/A if (errorp->pwd_mgmt.status ==
2N/A NS_PASSWD_ABOUT_TO_EXPIRE) {
2N/A /*
2N/A * password about to expire;
2N/A * retrieve "seconds until expired"
2N/A */
2N/A if (sec_until_expired)
2N/A *sec_until_expired =
2N/A errorp->
2N/A pwd_mgmt.sec_until_expired;
2N/A } else if (errorp->pwd_mgmt.status ==
2N/A NS_PASSWD_CHANGE_NEEDED)
2N/A /*
2N/A * indicate that passwd need to change
2N/A * right away
2N/A */
2N/A result = PAM_NEW_AUTHTOK_REQD;
2N/A
2N/A (void) __ns_ldap_freeError(&errorp);
2N/A }
2N/A goto out;
2N/A } else if (ldaprc == NS_LDAP_INTERNAL) {
2N/A
2N/A if (errorp) {
2N/A /*
2N/A * If error due to password policy, set
2N/A * appropriate PAM result code and exit.
2N/A */
2N/A if (errorp->pwd_mgmt.status ==
2N/A NS_PASSWD_RETRY_EXCEEDED)
2N/A result = PAM_MAXTRIES;
2N/A else if (errorp->pwd_mgmt.status ==
2N/A NS_PASSWD_EXPIRED)
2N/A result = PAM_AUTHTOK_EXPIRED;
2N/A else {
2N/A /*
2N/A * If invalid credential,
2N/A * return PAM_AUTH_ERR.
2N/A */
2N/A if (errorp->status ==
2N/A LDAP_INVALID_CREDENTIALS)
2N/A result = PAM_AUTH_ERR;
2N/A }
2N/A (void) __ns_ldap_freeError(&errorp);
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* done with the error info, clean it up */
2N/A if (errorp)
2N/A (void) __ns_ldap_freeError(&errorp);
2N/A }
2N/A if (authstried == 0) {
2N/A syslog(LOG_ERR,
2N/A "pam_ldap: no legal authentication method configured");
2N/A result = PAM_AUTH_ERR;
2N/A goto out;
2N/A }
2N/A result = PAM_PERM_DENIED;
2N/A
2N/Aout:
2N/A free(binddn);
2N/A
2N/A if (credpp && (result == PAM_SUCCESS ||
2N/A result == PAM_NEW_AUTHTOK_REQD)) {
2N/A *credpp = credp;
2N/A } else {
2N/A (void) __ns_ldap_freeCred(&credp);
2N/A }
2N/A
2N/A if (certpath)
2N/A (void) __ns_ldap_freeParam((void ***)&certpath);
2N/A
2N/A if (authpp)
2N/A (void) __ns_ldap_freeParam((void ***)&authpp);
2N/A
2N/A if (errorp)
2N/A (void) __ns_ldap_freeError(&errorp);
2N/A
2N/A return (result);
2N/A}