/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#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) {
/*
* __ns_ldap_freeCred frees the hostcertpath member, so we
* must assign a copy. Otherwise freeParam and freeCred
* below will double-free the string.
*/
credp->hostcertpath = strdup(*certpath);
if (credp->hostcertpath == NULL) {
result = PAM_BUF_ERR;
goto out;
}
}
/* 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:
free(binddn);
if (credpp && (result == PAM_SUCCESS ||
result == PAM_NEW_AUTHTOK_REQD)) {
*credpp = credp;
} else {
(void) __ns_ldap_freeCred(&credp);
}
if (certpath)
(void) __ns_ldap_freeParam((void ***)&certpath);
if (authpp)
(void) __ns_ldap_freeParam((void ***)&authpp);
if (errorp)
(void) __ns_ldap_freeError(&errorp);
return (result);
}