Skip to content
Permalink
cyrus-sasl-2.1…
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
1841 lines (1522 sloc) 43.3 KB
/* COPYRIGHT
* Copyright (c) 2002-2003 Igor Brezac
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY IGOR BREZAC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IGOR BREZAC OR
* ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
* END COPYRIGHT */
#ifndef AUTH_LDAP
#include "mechanisms.h"
#include "utils.h"
#endif
#ifdef AUTH_LDAP
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#ifdef HAVE_OPENSSL
#ifndef OPENSSL_DISABLE_OLD_DES_SUPPORT
#define OPENSSL_DISABLE_OLD_DES_SUPPORT
#endif
#include <openssl/evp.h>
#include <openssl/des.h>
/* for legacy libcrypto support */
#include "crypto-compat.h"
#endif
#define LDAP_DEPRECATED 1
#include <ldap.h>
#include <lber.h>
#include <sasl.h>
#include "lak.h"
typedef struct lak_auth_method {
int method;
int (*check) (LAK *lak, const char *user, const char *service, const char *realm, const char *password) ;
} LAK_AUTH_METHOD;
typedef struct lak_hash_rock {
const char *mda;
int salted;
} LAK_HASH_ROCK;
typedef struct lak_password_scheme {
char *hash;
int (*check) (const char *cred, const char *passwd, void *rock);
void *rock;
} LAK_PASSWORD_SCHEME;
static int lak_config_read(LAK_CONF *, const char *);
static int lak_config_int(const char *);
static int lak_config_switch(const char *);
static void lak_config_free(LAK_CONF *);
static int lak_config(const char *, LAK_CONF **);
static int lak_escape(const char *, const unsigned int, char **);
static int lak_tokenize_domains(const char *, int, char **);
static int lak_expand_tokens(const char *, const char *, const char *, const char *, const char *, char **);
static int lak_connect(LAK *);
static int lak_bind(LAK *, LAK_USER *);
static void lak_unbind(LAK *);
static int lak_auth_custom(LAK *, const char *, const char *, const char *, const char *);
static int lak_auth_bind(LAK *, const char *, const char *, const char *, const char *);
static int lak_auth_fastbind(LAK *, const char *, const char *, const char *, const char *);
static int lak_group_member(LAK *, const char *, const char *, const char *, const char *);
#if 0 /* unused */
static char *lak_result_get(const LAK_RESULT *, const char *);
#endif
static int lak_result_add(const char *, const char *, LAK_RESULT **);
static int lak_check_password(const char *, const char *, void *);
static int lak_check_crypt(const char *, const char *, void *);
#ifdef HAVE_OPENSSL
static int lak_base64_decode(const char *, char **, int *);
static int lak_check_hashed(const char *, const char *, void *);
#endif
static int lak_sasl_interact(LDAP *, unsigned, void *, void *);
static int lak_user(const char *, const char *, const char *, const char *, const char *, const char *, LAK_USER **);
static int lak_user_copy(LAK_USER **, const LAK_USER *);
static int lak_user_cmp(const LAK_USER *, const LAK_USER *);
static void lak_user_free(LAK_USER *);
static LAK_AUTH_METHOD authenticator[] = {
{ LAK_AUTH_METHOD_BIND, lak_auth_bind },
{ LAK_AUTH_METHOD_CUSTOM, lak_auth_custom },
{ LAK_AUTH_METHOD_FASTBIND, lak_auth_fastbind },
{ -1, NULL }
};
static LAK_HASH_ROCK hash_rock[] = {
{ "md5", 0 },
{ "md5", 1 },
{ "sha1", 0 },
{ "sha1", 1 }
};
static LAK_PASSWORD_SCHEME password_scheme[] = {
{ "{CRYPT}", lak_check_crypt, NULL },
{ "{UNIX}", lak_check_crypt, NULL },
#ifdef HAVE_OPENSSL
{ "{MD5}", lak_check_hashed, &hash_rock[0] },
{ "{SMD5}", lak_check_hashed, &hash_rock[1] },
{ "{SHA}", lak_check_hashed, &hash_rock[2] },
{ "{SSHA}", lak_check_hashed, &hash_rock[3] },
#endif
{ NULL, NULL, NULL }
};
static const char *dn_attr = "dn";
#define ISSET(x) ((x != NULL) && (*(x) != '\0'))
#define EMPTY(x) ((x == NULL) || (*(x) == '\0'))
static int lak_config_read(
LAK_CONF *conf,
const char *configfile)
{
FILE *infile;
int lineno = 0;
char buf[4096];
char *p, *key;
infile = fopen(configfile, "r");
if (!infile) {
syslog(LOG_ERR|LOG_AUTH,
"Could not open saslauthd config file: %s (%m)",
configfile);
return LAK_FAIL;
}
while (fgets(buf, sizeof(buf), infile)) {
lineno++;
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
for (p = buf; *p && isspace((int) *p); p++);
if (!*p || *p == '#')
continue;
key = p;
while (*p && (isalnum((int) *p) || *p == '-' || *p == '_')) {
if (isupper((int) *p))
*p = tolower(*p);
p++;
}
if (*p != ':') {
fclose(infile);
return LAK_FAIL;
}
*p++ = '\0';
while (*p && isspace((int) *p))
p++;
if (!*p) {
fclose(infile);
return LAK_FAIL;
}
if (!strcasecmp(key, "ldap_servers"))
strlcpy(conf->servers, p, LAK_URL_LEN);
else if (!strcasecmp(key, "ldap_bind_dn"))
strlcpy(conf->bind_dn, p, LAK_DN_LEN);
else if (!strcasecmp(key, "ldap_bind_pw") ||
!strcasecmp(key, "ldap_password"))
strlcpy(conf->password, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_version"))
conf->version = lak_config_int(p);
else if (!strcasecmp(key, "ldap_search_base"))
strlcpy(conf->search_base, p, LAK_DN_LEN);
else if (!strcasecmp(key, "ldap_filter"))
strlcpy(conf->filter, p, LAK_DN_LEN);
else if (!strcasecmp(key, "ldap_password_attr"))
strlcpy(conf->password_attr, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_group_dn"))
strlcpy(conf->group_dn, p, LAK_DN_LEN);
else if (!strcasecmp(key, "ldap_group_attr"))
strlcpy(conf->group_attr, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_group_filter"))
strlcpy(conf->group_filter, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_group_search_base"))
strlcpy(conf->group_search_base, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_group_scope")) {
if (!strcasecmp(p, "one")) {
conf->group_scope = LDAP_SCOPE_ONELEVEL;
} else if (!strcasecmp(p, "base")) {
conf->group_scope = LDAP_SCOPE_BASE;
}
} else if (!strcasecmp(key, "ldap_group_match_method")) {
if (!strcasecmp(p, "filter")) {
conf->group_match_method = LAK_GROUP_MATCH_METHOD_FILTER;
} else if (!strcasecmp(p, "attr")) {
conf->group_match_method = LAK_GROUP_MATCH_METHOD_ATTR;
}
} else if (!strcasecmp(key, "ldap_default_realm") ||
!strcasecmp(key, "ldap_default_domain"))
strlcpy(conf->default_realm, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_auth_method")) {
if (!strcasecmp(p, "custom")) {
conf->auth_method = LAK_AUTH_METHOD_CUSTOM;
} else if (!strcasecmp(p, "fastbind")) {
conf->auth_method = LAK_AUTH_METHOD_FASTBIND;
}
} else if (!strcasecmp(key, "ldap_timeout")) {
conf->timeout.tv_sec = lak_config_int(p);
conf->timeout.tv_usec = 0;
} else if (!strcasecmp(key, "ldap_size_limit"))
conf->size_limit = lak_config_int(p);
else if (!strcasecmp(key, "ldap_time_limit"))
conf->time_limit = lak_config_int(p);
else if (!strcasecmp(key, "ldap_deref")) {
if (!strcasecmp(p, "search")) {
conf->deref = LDAP_DEREF_SEARCHING;
} else if (!strcasecmp(p, "find")) {
conf->deref = LDAP_DEREF_FINDING;
} else if (!strcasecmp(p, "always")) {
conf->deref = LDAP_DEREF_ALWAYS;
} else if (!strcasecmp(p, "never")) {
conf->deref = LDAP_DEREF_NEVER;
}
} else if (!strcasecmp(key, "ldap_referrals")) {
conf->referrals = lak_config_switch(p);
} else if (!strcasecmp(key, "ldap_restart")) {
conf->restart = lak_config_switch(p);
} else if (!strcasecmp(key, "ldap_scope")) {
if (!strcasecmp(p, "one")) {
conf->scope = LDAP_SCOPE_ONELEVEL;
} else if (!strcasecmp(p, "base")) {
conf->scope = LDAP_SCOPE_BASE;
}
} else if (!strcasecmp(key, "ldap_use_sasl")) {
conf->use_sasl = lak_config_switch(p);
} else if (!strcasecmp(key, "ldap_id") ||
!strcasecmp(key, "ldap_sasl_authc_id"))
strlcpy(conf->id, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_authz_id") ||
!strcasecmp(key, "ldap_sasl_authz_id"))
strlcpy(conf->authz_id, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_realm") ||
!strcasecmp(key, "ldap_sasl_realm"))
strlcpy(conf->realm, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_mech") ||
!strcasecmp(key, "ldap_sasl_mech"))
strlcpy(conf->mech, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_sasl_secprops"))
strlcpy(conf->sasl_secprops, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_start_tls"))
conf->start_tls = lak_config_switch(p);
else if (!strcasecmp(key, "ldap_tls_check_peer"))
conf->tls_check_peer = lak_config_switch(p);
else if (!strcasecmp(key, "ldap_tls_cacert_file"))
strlcpy(conf->tls_cacert_file, p, LAK_PATH_LEN);
else if (!strcasecmp(key, "ldap_tls_cacert_dir"))
strlcpy(conf->tls_cacert_dir, p, LAK_PATH_LEN);
else if (!strcasecmp(key, "ldap_tls_ciphers"))
strlcpy(conf->tls_ciphers, p, LAK_BUF_LEN);
else if (!strcasecmp(key, "ldap_tls_cert"))
strlcpy(conf->tls_cert, p, LAK_PATH_LEN);
else if (!strcasecmp(key, "ldap_tls_key"))
strlcpy(conf->tls_key, p, LAK_PATH_LEN);
else if (!strcasecmp(key, "ldap_debug"))
conf->debug = lak_config_int(p);
}
if (conf->version != LDAP_VERSION3 &&
(conf->use_sasl ||
conf->start_tls))
conf->version = LDAP_VERSION3;
if (conf->use_sasl &&
conf->auth_method == LAK_AUTH_METHOD_BIND)
conf->auth_method = LAK_AUTH_METHOD_FASTBIND;
if ( ISSET(conf->group_filter) &&
ISSET(conf->search_base) &&
EMPTY(conf->group_search_base) )
strlcpy(conf->group_search_base, conf->search_base, LAK_DN_LEN);
fclose(infile);
return LAK_OK;
}
static int lak_config_int(
const char *val)
{
if (!val) return 0;
if (!isdigit((int) *val) && (*val != '-' || !isdigit((int) val[1]))) return 0;
return atoi(val);
}
static int lak_config_switch(
const char *val)
{
if (!val) return 0;
if (*val == '0' || *val == 'n' ||
(*val == 'o' && val[1] == 'f') || *val == 'f') {
return 0;
} else if (*val == '1' || *val == 'y' ||
(*val == 'o' && val[1] == 'n') || *val == 't') {
return 1;
}
return 0;
}
static int lak_config(
const char *configfile,
LAK_CONF **ret)
{
LAK_CONF *conf;
int rc = 0;
conf = malloc( sizeof(LAK_CONF) );
if (conf == NULL) {
return LAK_NOMEM;
}
memset(conf, 0, sizeof(LAK_CONF));
strlcpy(conf->servers, "ldap://localhost/", LAK_BUF_LEN);
conf->version = LDAP_VERSION3;
strlcpy(conf->filter, "(uid=%u)", LAK_DN_LEN);
strlcpy(conf->password_attr, "userPassword", LAK_BUF_LEN);
conf->scope = LDAP_SCOPE_SUBTREE;
strlcpy(conf->group_attr, "uniqueMember", LAK_BUF_LEN);
conf->group_scope = LDAP_SCOPE_SUBTREE;
conf->group_match_method = LAK_GROUP_MATCH_METHOD_ATTR;
conf->auth_method = LAK_AUTH_METHOD_BIND;
conf->timeout.tv_sec = 5;
conf->timeout.tv_usec = 0;
conf->size_limit = 1;
conf->time_limit = 5;
conf->deref = LDAP_DEREF_NEVER;
conf->restart = 1;
conf->start_tls = 0;
conf->use_sasl = 0;
strlcpy(conf->path, configfile, LAK_PATH_LEN);
rc = lak_config_read(conf, conf->path);
if (rc != LAK_OK) {
lak_config_free(conf);
return rc;
}
*ret = conf;
return LAK_OK;
}
static void lak_config_free(
LAK_CONF *conf)
{
if (conf == NULL) {
return;
}
memset(conf, 0, sizeof(LAK_CONF));
free (conf);
return;
}
/*
* Note: calling function must free memory.
*/
static int lak_escape(
const char *s,
const unsigned int n,
char **result)
{
char *buf;
char *end, *ptr, *temp;
if (n > strlen(s)) // Sanity check, just in case
return LAK_FAIL;
buf = malloc(n * 5 + 1);
if (buf == NULL) {
return LAK_NOMEM;
}
buf[0] = '\0';
ptr = (char *)s;
end = ptr + n;
while (((temp = strpbrk(ptr, "*()\\\0"))!=NULL) && (temp<end)) {
if (temp>ptr)
strncat(buf, ptr, temp-ptr);
switch (*temp) {
case '*':
strcat(buf, "\\2a");
break;
case '(':
strcat(buf, "\\28");
break;
case ')':
strcat(buf, "\\29");
break;
case '\\':
strcat(buf, "\\5c");
break;
case '\0':
strcat(buf, "\\00");
break;
}
ptr=temp+1;
}
if (ptr<end)
strncat(buf, ptr, end-ptr);
*result = buf;
return LAK_OK;
}
static int lak_tokenize_domains(
const char *d,
int n,
char **result)
{
char *s, *s1;
char *lasts;
int nt, i, rc;
*result = NULL;
if (d == NULL || n < 1 || n > 9)
return LAK_FAIL;
s = strdup(d);
if (s == NULL)
return LAK_NOMEM;
for( nt=0, s1=s; *s1; s1++ )
if( *s1 == '.' ) nt++;
nt++;
if (n > nt) {
free(s);
return LAK_FAIL;
}
i = nt - n;
s1 = (char *)strtok_r(s, ".", &lasts);
while(s1) {
if (i == 0) {
rc = lak_escape(s1, strlen(s1), result);
free(s);
return rc;
}
s1 = (char *)strtok_r(NULL, ".", &lasts);
i--;
}
free(s);
return LAK_FAIL;
}
#define LAK_MAX(a,b) (a>b?a:b)
/*
* lak_expand_tokens
* Parts with the strings provided.
* %% = %
* %u = user
* %U = user part of %u
* %d = domain part of %u if available, othwise same as %r
* %1-9 = domain if not available realm, token
* (%1 = tld, %2 = domain when %r = domain.tld)
* %s = service
* %r = realm
* %R = prepend '@' to realm
* %D = user DN
* Note: calling function must free memory.
*/
static int lak_expand_tokens(
const char *pattern,
const char *username,
const char *service,
const char *realm,
const char *dn,
char **result)
{
char *buf;
char *end, *ptr, *temp;
char *ebuf, *user;
char *domain;
int rc;
/* to permit multiple occurences of username and/or realm in filter */
/* and avoid memory overflow in filter build [eg: (|(uid=%u)(userid=%u)) ] */
int percents, service_len, realm_len, dn_len, user_len, maxparamlength;
if (pattern == NULL) {
syslog(LOG_ERR|LOG_AUTH, "filter pattern not setup");
return LAK_FAIL;
}
/* find the longest param of username and realm,
do not worry about domain because it is always shorter
then username */
user_len=ISSET(username) ? strlen(username) : 0;
service_len=ISSET(service) ? strlen(service) : 0;
realm_len=ISSET(realm) ? strlen(realm) : 0;
dn_len=ISSET(dn) ? strlen(dn) : 0;
maxparamlength = LAK_MAX(user_len, service_len);
maxparamlength = LAK_MAX(maxparamlength, realm_len + 1); /* +1 for %R when '@' is prepended */
maxparamlength = LAK_MAX(maxparamlength, dn_len);
/* find the number of occurences of percent sign in filter */
for( percents=0, buf=(char *)pattern; *buf; buf++ ) {
if( *buf == '%' ) percents++;
}
/* percents * 3 * maxparamlength because we need to account for
* an entirely-escaped worst-case-length parameter */
buf=malloc(strlen(pattern) + (percents * 3 * maxparamlength) + 1);
if(buf == NULL)
return LAK_NOMEM;
buf[0] = '\0';
ptr = (char *)pattern;
end = ptr + strlen(ptr);
while ((temp=strchr(ptr,'%'))!=NULL ) {
if ((temp-ptr) > 0)
strncat(buf, ptr, temp-ptr);
if ((temp+1) >= end) {
syslog(LOG_DEBUG|LOG_AUTH, "Incomplete lookup substitution format");
break;
}
switch (*(temp+1)) {
case '%':
strncat(buf,temp+1,1);
break;
case 'u':
if (ISSET(username)) {
rc=lak_escape(username, strlen(username), &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "Username not available.");
break;
case 'U':
if (ISSET(username)) {
user = strchr(username, '@');
rc=lak_escape(username, (user ? user - username : (unsigned) strlen(username)), &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "Username not available.");
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (ISSET(username) &&
((domain = strchr(username, '@')) && domain[1]!='\0')) {
rc=lak_tokenize_domains(domain+1, (int) *(temp+1)-48, &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else if (ISSET(realm)) {
rc=lak_tokenize_domains(realm, (int) *(temp+1)-48, &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "Domain/Realm not available.");
break;
case 'd':
if (ISSET(username) &&
((domain = strchr(username, '@')) && domain[1]!='\0')) {
rc=lak_escape(domain+1, strlen(domain+1), &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "Domain/Realm not available.");
break;
case 'R':
case 'r':
if (ISSET(realm)) {
rc = lak_escape(realm, strlen(realm), &ebuf);
if (rc == LAK_OK) {
if (*(temp+1) == 'R')
strcat(buf,"@");
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "Domain/Realm not available.");
break;
case 's':
if (ISSET(service)) {
rc = lak_escape(service, strlen(service), &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "Service not available.");
break;
case 'D':
if (ISSET(dn)) {
rc = lak_escape(dn, strlen(dn), &ebuf);
if (rc == LAK_OK) {
strcat(buf,ebuf);
free(ebuf);
}
} else
syslog(LOG_DEBUG|LOG_AUTH, "User DN not available.");
break;
default:
break;
}
ptr=temp+2;
}
if (temp<end)
strcat(buf, ptr);
*result = buf;
return LAK_OK;
}
int lak_init(
const char *configfile,
LAK **ret)
{
LAK *lak;
int rc;
lak = *ret;
if (lak != NULL) {
return LAK_OK;
}
lak = (LAK *)malloc(sizeof(LAK));
if (lak == NULL)
return LAK_NOMEM;
lak->status=LAK_NOT_BOUND;
lak->ld=NULL;
lak->conf=NULL;
lak->user=NULL;
rc = lak_config(configfile, &lak->conf);
if (rc != LAK_OK) {
free(lak);
return rc;
}
#ifdef HAVE_OPENSSL
OpenSSL_add_all_digests();
#endif
*ret=lak;
return LAK_OK;
}
void lak_close(
LAK *lak)
{
if (lak == NULL)
return;
lak_config_free(lak->conf);
lak_unbind(lak);
free(lak);
#ifdef HAVE_OPENSSL
EVP_cleanup();
#endif
return;
}
static int lak_connect(
LAK *lak)
{
int rc = 0;
char *p = NULL;
if (ISSET(lak->conf->tls_cacert_file)) {
rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, lak->conf->tls_cacert_file);
if (rc != LDAP_SUCCESS) {
syslog (LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_X_TLS_CACERTFILE (%s).", ldap_err2string (rc));
}
}
if (ISSET(lak->conf->tls_cacert_dir)) {
rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR, lak->conf->tls_cacert_dir);
if (rc != LDAP_SUCCESS) {
syslog (LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_X_TLS_CACERTDIR (%s).", ldap_err2string (rc));
}
}
if (lak->conf->tls_check_peer != 0) {
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &lak->conf->tls_check_peer);
if (rc != LDAP_SUCCESS) {
syslog (LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_X_TLS_REQUIRE_CERT (%s).", ldap_err2string (rc));
}
}
if (ISSET(lak->conf->tls_ciphers)) {
/* set cipher suite, certificate and private key: */
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, lak->conf->tls_ciphers);
if (rc != LDAP_SUCCESS) {
syslog (LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_X_TLS_CIPHER_SUITE (%s).", ldap_err2string (rc));
}
}
if (ISSET(lak->conf->tls_cert)) {
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, lak->conf->tls_cert);
if (rc != LDAP_SUCCESS) {
syslog (LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_X_TLS_CERTFILE (%s).", ldap_err2string (rc));
}
}
if (ISSET(lak->conf->tls_key)) {
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, lak->conf->tls_key);
if (rc != LDAP_SUCCESS) {
syslog (LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_X_TLS_KEYFILE (%s).", ldap_err2string (rc));
}
}
rc = ldap_initialize(&lak->ld, lak->conf->servers);
if (rc != LDAP_SUCCESS) {
syslog(LOG_ERR|LOG_AUTH, "ldap_initialize failed (%s)", lak->conf->servers);
return LAK_CONNECT_FAIL;
}
if (lak->conf->debug) {
rc = ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &(lak->conf->debug));
if (rc != LDAP_OPT_SUCCESS)
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_DEBUG_LEVEL %x.", lak->conf->debug);
}
rc = ldap_set_option(lak->ld, LDAP_OPT_PROTOCOL_VERSION, &(lak->conf->version));
if (rc != LDAP_OPT_SUCCESS) {
if (lak->conf->use_sasl ||
lak->conf->start_tls) {
syslog(LOG_ERR|LOG_AUTH, "Failed to set LDAP_OPT_PROTOCOL_VERSION %d, required for ldap_start_tls and ldap_use_sasl.", lak->conf->version);
lak_unbind(lak);
return LAK_CONNECT_FAIL;
} else
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_PROTOCOL_VERSION %d.", lak->conf->version);
lak->conf->version = LDAP_VERSION2;
}
rc = ldap_set_option(lak->ld, LDAP_OPT_NETWORK_TIMEOUT, &(lak->conf->timeout));
if (rc != LDAP_OPT_SUCCESS) {
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_NETWORK_TIMEOUT %ld.%ld.", lak->conf->timeout.tv_sec, lak->conf->timeout.tv_usec);
}
rc = ldap_set_option(lak->ld, LDAP_OPT_TIMEOUT, &(lak->conf->timeout));
if (rc != LDAP_OPT_SUCCESS) {
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_TIMEOUT %ld.%ld.", lak->conf->timeout.tv_sec, lak->conf->timeout.tv_usec);
}
rc = ldap_set_option(lak->ld, LDAP_OPT_TIMELIMIT, &(lak->conf->time_limit));
if (rc != LDAP_OPT_SUCCESS) {
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_TIMELIMIT %d.", lak->conf->time_limit);
}
rc = ldap_set_option(lak->ld, LDAP_OPT_DEREF, &(lak->conf->deref));
if (rc != LDAP_OPT_SUCCESS) {
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_DEREF %d.", lak->conf->deref);
}
rc = ldap_set_option(lak->ld, LDAP_OPT_REFERRALS, lak->conf->referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
if (rc != LDAP_OPT_SUCCESS) {
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_REFERRALS.");
}
rc = ldap_set_option(lak->ld, LDAP_OPT_SIZELIMIT, &(lak->conf->size_limit));
if (rc != LDAP_OPT_SUCCESS)
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_SIZELIMIT %d.", lak->conf->size_limit);
rc = ldap_set_option(lak->ld, LDAP_OPT_RESTART, lak->conf->restart ? LDAP_OPT_ON : LDAP_OPT_OFF);
if (rc != LDAP_OPT_SUCCESS) {
syslog(LOG_WARNING|LOG_AUTH, "Unable to set LDAP_OPT_RESTART.");
}
if (lak->conf->start_tls) {
rc = ldap_start_tls_s(lak->ld, NULL, NULL);
if (rc != LDAP_SUCCESS) {
syslog(LOG_ERR|LOG_AUTH, "start tls failed (%s).", ldap_err2string(rc));
lak_unbind(lak);
return LAK_CONNECT_FAIL;
}
}
if (lak->conf->use_sasl) {
if (EMPTY(lak->conf->mech)) {
ldap_get_option(lak->ld, LDAP_OPT_X_SASL_MECH, &p);
if (p)
strlcpy(lak->conf->mech, p, LAK_BUF_LEN);
}
if (EMPTY(lak->conf->realm)) {
ldap_get_option(lak->ld, LDAP_OPT_X_SASL_REALM, &p);
if (p)
strlcpy(lak->conf->realm, p, LAK_BUF_LEN);
}
if (ISSET(lak->conf->sasl_secprops)) {
rc = ldap_set_option(lak->ld, LDAP_OPT_X_SASL_SECPROPS, (void *) lak->conf->sasl_secprops);
if( rc != LDAP_OPT_SUCCESS ) {
syslog(LOG_ERR|LOG_AUTH, "Unable to set LDAP_OPT_X_SASL_SECPROPS.");
lak_unbind(lak);
return LAK_CONNECT_FAIL;
}
}
}
return LAK_OK;
}
static int lak_user(
const char *bind_dn,
const char *id,
const char *authz_id,
const char *mech,
const char *realm,
const char *password,
LAK_USER **ret)
{
LAK_USER *lu = NULL;
*ret = NULL;
lu = (LAK_USER *)malloc(sizeof(LAK_USER));
if (lu == NULL)
return LAK_NOMEM;
memset(lu, 0, sizeof(LAK_USER));
if (ISSET(bind_dn))
strlcpy(lu->bind_dn, bind_dn, LAK_DN_LEN);
if (ISSET(id))
strlcpy(lu->id, id, LAK_BUF_LEN);
if (ISSET(authz_id))
strlcpy(lu->authz_id, authz_id, LAK_BUF_LEN);
if (ISSET(mech))
strlcpy(lu->mech, mech, LAK_BUF_LEN);
if (ISSET(realm))
strlcpy(lu->realm, realm, LAK_BUF_LEN);
if (ISSET(password))
strlcpy(lu->password, password, LAK_BUF_LEN);
*ret = lu;
return LAK_OK;
}
static int lak_user_cmp(
const LAK_USER *lu1,
const LAK_USER *lu2)
{
if (lu1 == NULL ||
lu2 == NULL)
return LAK_FAIL;
if (memcmp(lu1, lu2, sizeof(LAK_USER)) == 0)
return LAK_OK;
return LAK_FAIL;
}
static int lak_user_copy(
LAK_USER **lu1,
const LAK_USER *lu2)
{
LAK_USER *lu;
lu = *lu1;
if (lu2 == NULL)
return LAK_FAIL;
if (lu == NULL) {
lu = (LAK_USER *)malloc(sizeof(LAK_USER));
if (lu == NULL)
return LAK_NOMEM;
*lu1 = lu;
}
memcpy((void *)lu, (void *)lu2, sizeof(LAK_USER));
return LAK_OK;
}
static void lak_user_free(
LAK_USER *user)
{
if (user == NULL) {
return;
}
memset(user, 0, sizeof(LAK_USER));
free(user);
return;
}
static int lak_sasl_interact(
LDAP *ld,
unsigned flags __attribute__((unused)),
void *def,
void *inter)
{
sasl_interact_t *in = inter;
const char *p;
LAK_USER *lu = def;
for (;in->id != SASL_CB_LIST_END;in++) {
p = NULL;
switch(in->id) {
case SASL_CB_AUTHNAME:
if (ISSET(lu->id))
p = lu->id;
if (!p)
ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHCID, &p);
break;
case SASL_CB_USER:
if (ISSET(lu->authz_id))
p = lu->authz_id;
if (!p)
ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHZID, &p);
break;
case SASL_CB_GETREALM:
if (ISSET(lu->realm))
p = lu->realm;
break;
case SASL_CB_PASS:
if (ISSET(lu->password))
p = lu->password;
break;
}
in->result = ISSET(p) ? p : "";
in->len = strlen(in->result);
}
return LDAP_SUCCESS;
}
static int lak_bind(
LAK *lak,
LAK_USER *user)
{
int rc;
if (user == NULL) // Sanity Check
return LAK_FAIL;
if ((lak->status == LAK_BOUND) &&
(lak_user_cmp(lak->user, user) == LAK_OK))
return LAK_OK;
lak_user_free(lak->user);
lak->user = NULL;
if (
#if LDAP_VENDOR_VERSION < 20204
lak->conf->use_sasl ||
#endif
lak->conf->version == LDAP_VERSION2)
lak->status = LAK_NOT_BOUND;
if (lak->status == LAK_NOT_BOUND) {
lak_unbind(lak);
rc = lak_connect(lak);
if (rc != LAK_OK)
return rc;
}
if (lak->conf->use_sasl)
rc = ldap_sasl_interactive_bind_s(
lak->ld,
user->bind_dn,
user->mech,
NULL,
NULL,
LDAP_SASL_QUIET,
lak_sasl_interact,
user);
else
rc = ldap_simple_bind_s(lak->ld, user->bind_dn, user->password);
switch (rc) {
case LDAP_SUCCESS:
break;
case LDAP_INVALID_CREDENTIALS:
case LDAP_INSUFFICIENT_ACCESS:
case LDAP_INVALID_DN_SYNTAX:
case LDAP_OTHER:
lak->status = LAK_NOT_BOUND;
return LAK_BIND_FAIL;
case LDAP_TIMEOUT:
case LDAP_SERVER_DOWN:
default:
syslog(LOG_DEBUG|LOG_AUTH,
(lak->conf->use_sasl ? "ldap_sasl_interactive_bind() failed %d (%s)." : "ldap_simple_bind() failed %d (%s)."), rc, ldap_err2string(rc));
lak->status = LAK_NOT_BOUND;
return LAK_RETRY;
}
rc = lak_user_copy(&(lak->user), user);
if (rc != LAK_OK)
return rc;
lak->status = LAK_BOUND;
return LAK_OK;
}
static void lak_unbind(
LAK *lak)
{
if (!lak)
return;
lak_user_free(lak->user);
if (lak->ld)
ldap_unbind(lak->ld);
lak->ld = NULL;
lak->user = NULL;
lak->status = LAK_NOT_BOUND;
return;
}
/*
* lak_retrieve - retrieve user@realm values specified by 'attrs'
*/
int lak_retrieve(
LAK *lak,
const char *user,
const char *service,
const char *realm,
const char **attrs,
LAK_RESULT **ret)
{
int rc = 0, i;
char *filter = NULL;
char *search_base = NULL;
LDAPMessage *res = NULL;
LDAPMessage *entry = NULL;
BerElement *ber = NULL;
char *attr = NULL, **vals = NULL, *dn = NULL;
LAK_USER *lu = NULL;
*ret = NULL;
if (lak == NULL) {
syslog(LOG_ERR|LOG_AUTH, "lak_init did not run.");
return LAK_FAIL;
}
if (EMPTY(user))
return LAK_FAIL;
if (EMPTY(realm))
realm = lak->conf->default_realm;
rc = lak_user(
lak->conf->bind_dn,
lak->conf->id,
lak->conf->authz_id,
lak->conf->mech,
lak->conf->realm,
lak->conf->password,
&lu);
if (rc != LAK_OK)
return rc;
rc = lak_bind(lak, lu);
if (rc != LAK_OK)
goto done;
rc = lak_expand_tokens(lak->conf->filter, user, service, realm, NULL, &filter);
if (rc != LAK_OK)
goto done;
rc = lak_expand_tokens(lak->conf->search_base, user, service, realm, NULL, &search_base);
if (rc != LAK_OK)
goto done;
rc = ldap_search_st(lak->ld, search_base, lak->conf->scope, filter, (char **) attrs, 0, &(lak->conf->timeout), &res);
switch (rc) {
case LDAP_SUCCESS:
case LDAP_NO_SUCH_OBJECT:
break;
case LDAP_TIMELIMIT_EXCEEDED:
case LDAP_BUSY:
case LDAP_UNAVAILABLE:
case LDAP_INSUFFICIENT_ACCESS:
/* We do not need to re-connect to the LDAP server
under these conditions */
syslog(LOG_ERR|LOG_AUTH, "user ldap_search_st() failed: %s", ldap_err2string(rc));
rc = LAK_USER_NOT_FOUND;
goto done;
case LDAP_TIMEOUT:
case LDAP_SERVER_DOWN:
default:
syslog(LOG_ERR|LOG_AUTH, "user ldap_search_st() failed: %s", ldap_err2string(rc));
rc = LAK_RETRY;
lak->status = LAK_NOT_BOUND;
goto done;
}
i = ldap_count_entries(lak->ld, res);
if (i != 1) {
if (i == 0)
syslog(LOG_DEBUG|LOG_AUTH, "Entry not found (%s).", filter);
else
syslog(LOG_DEBUG|LOG_AUTH, "Duplicate entries found (%s).", filter);
rc = LAK_USER_NOT_FOUND;
goto done;
}
rc = LAK_FAIL;
if ((entry = ldap_first_entry(lak->ld, res)) != NULL) {
for (i=0; attrs[i] != NULL; i++) {
if (!strcmp(attrs[i], dn_attr)) {
dn = ldap_get_dn(lak->ld, entry);
if (dn == NULL)
goto done;
rc = lak_result_add(dn_attr, dn, ret);
if (rc != LAK_OK) {
lak_result_free(*ret);
*ret = NULL;
goto done;
}
}
}
for (attr = ldap_first_attribute(lak->ld, entry, &ber); attr != NULL;
attr = ldap_next_attribute(lak->ld, entry, ber)) {
vals = ldap_get_values(lak->ld, entry, attr);
if (vals == NULL)
continue;
for (i = 0; vals[i] != NULL; i++) {
rc = lak_result_add(attr, vals[i], ret);
if (rc != LAK_OK) {
lak_result_free(*ret);
*ret = NULL;
goto done;
}
}
ldap_value_free(vals);
vals = NULL;
ldap_memfree(attr);
attr = NULL;
}
}
done:;
if (res)
ldap_msgfree(res);
if (dn)
ldap_memfree(dn);
if (vals)
ldap_value_free(vals);
if (attr)
ldap_memfree(attr);
if (ber != NULL)
ber_free(ber, 0);
if (filter)
free(filter);
if (search_base)
free(search_base);
if (lu)
lak_user_free(lu);
return rc;
}
static int lak_group_member(
LAK *lak,
const char *user,
const char *service,
const char *realm,
const char *dn)
{
char *group_dn = NULL, *user_dn = NULL;
char *group_filter = NULL;
char *group_search_base = NULL;
struct berval *dn_bv = NULL;
int rc;
LAK_RESULT *lres = NULL;
const char *attrs[] = { dn_attr, NULL };
const char *group_attrs[] = {"1.1", NULL};
LDAPMessage *res = NULL;
user_dn = (char *)dn;
if (EMPTY(user_dn)) {
if (lak->conf->use_sasl) {
#if LDAP_VENDOR_VERSION >= 20122
if (ldap_whoami_s(lak->ld, &dn_bv, NULL, NULL) != LDAP_SUCCESS || !dn_bv) {
syslog(LOG_ERR|LOG_AUTH, "ldap_whoami_s() failed.");
rc = LAK_NOT_GROUP_MEMBER;
goto done;
}
user_dn = dn_bv->bv_val;
#else
syslog(LOG_ERR|LOG_AUTH, "Your OpenLDAP API does not supported ldap_whoami().");
rc = LAK_NOT_GROUP_MEMBER;
goto done;
#endif
} else {
rc = lak_retrieve(lak, user, service, realm, attrs, &lres);
if (rc != LAK_OK)
goto done;
user_dn = lres->value;
}
}
if (lak->conf->group_match_method == LAK_GROUP_MATCH_METHOD_ATTR) {
rc = lak_expand_tokens(lak->conf->group_dn, user, service, realm, NULL, &group_dn);
if (rc != LAK_OK)
goto done;
rc = ((ldap_compare_s(lak->ld, group_dn, lak->conf->group_attr, user_dn)) == LDAP_COMPARE_TRUE ?
LAK_OK : LAK_NOT_GROUP_MEMBER);
} else if (lak->conf->group_match_method == LAK_GROUP_MATCH_METHOD_FILTER) {
rc = lak_expand_tokens(lak->conf->group_filter, user, service, realm, user_dn, &group_filter);
if (rc != LAK_OK)
goto done;
rc = lak_expand_tokens(lak->conf->group_search_base, user, service, realm, user_dn, &group_search_base);
if (rc != LAK_OK)
goto done;
rc = ldap_search_st(lak->ld, group_search_base, lak->conf->group_scope, group_filter, (char **) group_attrs, 0, &(lak->conf->timeout), &res);
switch (rc) {
case LDAP_SUCCESS:
case LDAP_NO_SUCH_OBJECT:
break;
case LDAP_TIMELIMIT_EXCEEDED:
case LDAP_BUSY:
case LDAP_UNAVAILABLE:
case LDAP_INSUFFICIENT_ACCESS:
syslog(LOG_ERR|LOG_AUTH, "group ldap_search_st() failed: %s", ldap_err2string(rc));
rc = LAK_NOT_GROUP_MEMBER;
goto done;
case LDAP_TIMEOUT:
case LDAP_SERVER_DOWN:
default:
syslog(LOG_ERR|LOG_AUTH, "group ldap_search_st() failed: %s", ldap_err2string(rc));
rc = LAK_RETRY;
lak->status = LAK_NOT_BOUND;
goto done;
}
rc = ( (ldap_count_entries(lak->ld, res) >= 1) ? LAK_OK : LAK_NOT_GROUP_MEMBER );
} else {
syslog(LOG_WARNING|LOG_AUTH, "Unknown ldap_group_match_method value.");
rc = LAK_FAIL;
}
done:;
if (res)
ldap_msgfree(res);
if (group_dn)
free(group_dn);
if (group_filter)
free(group_filter);
if (group_search_base)
free(group_search_base);
if (lres)
lak_result_free(lres);
if (dn_bv)
ber_bvfree(dn_bv);
return rc;
}
static int lak_auth_custom(
LAK *lak,
const char *user,
const char *service,
const char *realm,
const char *password)
{
LAK_RESULT *lres;
int rc;
const char *attrs[] = { lak->conf->password_attr, NULL};
rc = lak_retrieve(lak, user, service, realm, attrs, &lres);
if (rc != LAK_OK)
return rc;
rc = lak_check_password(lres->value, password, NULL);
if ( rc == LAK_OK &&
(ISSET(lak->conf->group_dn) ||
ISSET(lak->conf->group_filter)) )
rc = lak_group_member(lak, user, service, realm, NULL);
lak_result_free(lres);
return(rc);
}
static int lak_auth_bind(
LAK *lak,
const char *user,
const char *service,
const char *realm,
const char *password)
{
LAK_USER *lu = NULL;
LAK_RESULT *dn = NULL;
int rc;
const char *attrs[] = {dn_attr, NULL};
rc = lak_retrieve(lak, user, service, realm, attrs, &dn);
if (rc != LAK_OK)
goto done;
rc = lak_user(
dn->value,
NULL,
NULL,
NULL,
NULL,
password,
&lu);
if (rc != LAK_OK)
goto done;
rc = lak_bind(lak, lu);
if ( rc == LAK_OK &&
(ISSET(lak->conf->group_dn) ||
ISSET(lak->conf->group_filter)) )
{
/* restore config bind */
lak_unbind(lak);
lak_user_free(lu);
rc = lak_user(
lak->conf->bind_dn,
lak->conf->id,
lak->conf->authz_id,
lak->conf->mech,
lak->conf->realm,
lak->conf->password,
&lu);
if (rc != LAK_OK)
goto done;
rc = lak_bind(lak, lu);
if (rc != LAK_OK)
goto done;
rc = lak_group_member(lak, user, service, realm, dn->value);
}
done:
if (lu)
lak_user_free(lu);
if (dn)
lak_result_free(dn);
return rc;
}
static int lak_auth_fastbind(
LAK *lak,
const char *user,
const char *service,
const char *realm,
const char *password)
{
int rc;
LAK_USER *lu = NULL;
char *dn = NULL;
char id[LAK_BUF_LEN];
*id = '\0';
if (lak->conf->use_sasl) {
strlcpy(id, user, LAK_BUF_LEN);
if (!strchr(id, '@') &&
(ISSET(realm))) {
strlcat(id, "@", LAK_BUF_LEN);
strlcat(id, realm, LAK_BUF_LEN);
}
} else {
rc = lak_expand_tokens(lak->conf->filter, user, service, realm, NULL, &dn);
if (rc != LAK_OK ||
EMPTY(dn))
goto done;
}
rc = lak_user(
dn,
id,
NULL,
lak->conf->mech,
lak->conf->realm,
password,
&lu);
if (rc != LAK_OK)
goto done;
rc = lak_bind(lak, lu);
if ( rc == LAK_OK &&
(ISSET(lak->conf->group_dn) ||
ISSET(lak->conf->group_filter)) )
rc = lak_group_member(lak, user, service, realm, dn);
done:;
if (lu)
lak_user_free(lu);
if (dn != NULL)
free(dn);
return rc;
}
int lak_authenticate(
LAK *lak,
const char *user,
const char *service,
const char *realm,
const char *password)
{
int i;
int rc;
int retry = 2;
if (lak == NULL) {
syslog(LOG_ERR|LOG_AUTH, "lak_init did not run.");
return LAK_FAIL;
}
if (EMPTY(user))
return LAK_FAIL;
if (EMPTY(realm))
realm = lak->conf->default_realm;
for (i = 0; authenticator[i].method != -1; i++) {
if (authenticator[i].method == lak->conf->auth_method) {
if (authenticator[i].check) {
for (;retry > 0; retry--) {
rc = (authenticator[i].check)(lak, user, service, realm, password);
switch(rc) {
case LAK_OK:
return LAK_OK;
case LAK_RETRY:
if (retry > 1) {
syslog(LOG_INFO|LOG_AUTH, "Retrying authentication");
break;
}
GCC_FALLTHROUGH
default:
syslog(
LOG_DEBUG|LOG_AUTH,
"Authentication failed for %s%s%s: %s (%d)",
user,
(ISSET(realm) ? "/" : ""),
(ISSET(realm) ? realm : ""),
lak_error(rc),
rc);
return LAK_FAIL;
}
}
}
break;
}
}
/* Should not get here */
syslog(LOG_DEBUG|LOG_AUTH, "Authentication method not setup properly (%d)", lak->conf->auth_method);
return LAK_FAIL;
}
char *lak_error(
const int errno)
{
switch (errno) {
case LAK_OK:
return "Success";
case LAK_FAIL:
return "Generic error";
case LAK_NOMEM:
return "Out of memory";
case LAK_RETRY:
return "Retry condition (ldap server connection reset or broken)";
case LAK_NOT_GROUP_MEMBER:
return "Group member check failed";
case LAK_INVALID_PASSWORD:
return "Invalid password";
case LAK_USER_NOT_FOUND:
return "User not found";
case LAK_BIND_FAIL:
return "Bind to ldap server failed (invalid user/password or insufficient access)";
case LAK_CONNECT_FAIL:
return "Cannot connect to ldap server (configuration error)";
default:
return "Unknow error";
}
}
#if 0 /* unused */
static char *lak_result_get(
const LAK_RESULT *lres,
const char *attr)
{
LAK_RESULT *ptr;
for (ptr = (LAK_RESULT *)lres; ptr != NULL; ptr = ptr->next)
if (!strcasecmp(ptr->attribute, attr))
return ptr->value;
return NULL;
}
#endif
static int lak_result_add(
const char *attr,
const char *val,
LAK_RESULT **ret)
{
LAK_RESULT *lres;
lres = (LAK_RESULT *) malloc(sizeof(LAK_RESULT));
if (lres == NULL) {
return LAK_NOMEM;
}
lres->next = NULL;
lres->attribute = strdup(attr);
if (lres->attribute == NULL) {
lak_result_free(lres);
return LAK_NOMEM;
}
lres->value = strdup(val);
if (lres->value == NULL) {
lak_result_free(lres);
return LAK_NOMEM;
}
lres->len = strlen(lres->value);
lres->next = *ret;
*ret = lres;
return LAK_OK;
}
void lak_result_free(
LAK_RESULT *res)
{
LAK_RESULT *lres, *ptr = res;
if (ptr == NULL)
return;
for (lres = ptr; lres != NULL; lres = ptr) {
ptr = lres->next;
if (lres->attribute != NULL) {
memset(lres->attribute, 0, strlen(lres->attribute));
free(lres->attribute);
}
if (lres->value != NULL) {
memset(lres->value, 0, strlen(lres->value));
free(lres->value);
}
lres->next = NULL;
free(lres);
}
return;
}
static int lak_check_password(
const char *hash,
const char *passwd,
void *rock __attribute__((unused)))
{
int i, hlen;
if (EMPTY(hash))
return LAK_INVALID_PASSWORD;
if (EMPTY(passwd))
return LAK_INVALID_PASSWORD;
for (i = 0; password_scheme[i].hash != NULL; i++) {
hlen = strlen(password_scheme[i].hash);
if (!strncasecmp(password_scheme[i].hash, hash, hlen)) {
if (password_scheme[i].check) {
return (password_scheme[i].check)(hash+hlen, passwd,
password_scheme[i].rock);
}
return LAK_FAIL;
}
}
return strcmp(hash, passwd) ? LAK_INVALID_PASSWORD : LAK_OK;
}
#ifdef HAVE_OPENSSL
static int lak_base64_decode(
const char *src,
char **ret,
int *rlen)
{
int rc, i, tlen = 0;
char *text;
EVP_ENCODE_CTX *enc_ctx = EVP_ENCODE_CTX_new();
if (enc_ctx == NULL)
return LAK_NOMEM;
text = (char *)malloc(((strlen(src)+3)/4 * 3) + 1);
if (text == NULL) {
EVP_ENCODE_CTX_free(enc_ctx);
return LAK_NOMEM;
}
EVP_DecodeInit(enc_ctx);
rc = EVP_DecodeUpdate(enc_ctx, (unsigned char *) text, &i, (const unsigned char *)src, strlen(src));
if (rc < 0) {
EVP_ENCODE_CTX_free(enc_ctx);
free(text);
return LAK_FAIL;
}
tlen += i;
EVP_DecodeFinal(enc_ctx, (unsigned char *) text, &i);
EVP_ENCODE_CTX_free(enc_ctx);
*ret = text;
if (rlen != NULL)
*rlen = tlen;
return LAK_OK;
}
static int lak_check_hashed(
const char *hash,
const char *passwd,
void *rock)
{
int rc, clen;
LAK_HASH_ROCK *hrock = (LAK_HASH_ROCK *) rock;
EVP_MD_CTX *mdctx;
const EVP_MD *md;
unsigned char digest[EVP_MAX_MD_SIZE];
char *cred;
md = EVP_get_digestbyname(hrock->mda);
if (!md)
return LAK_FAIL;
mdctx = EVP_MD_CTX_new();
if (!mdctx)
return LAK_NOMEM;
rc = lak_base64_decode(hash, &cred, &clen);
if (rc != LAK_OK) {
EVP_MD_CTX_free(mdctx);
return rc;
}
EVP_DigestInit(mdctx, md);
EVP_DigestUpdate(mdctx, passwd, strlen(passwd));
if (hrock->salted) {
EVP_DigestUpdate(mdctx, &cred[EVP_MD_size(md)],
clen - EVP_MD_size(md));
}
EVP_DigestFinal(mdctx, digest, NULL);
EVP_MD_CTX_free(mdctx);
rc = memcmp((char *)cred, (char *)digest, EVP_MD_size(md));
free(cred);
return rc ? LAK_INVALID_PASSWORD : LAK_OK;
}
#endif /* HAVE_OPENSSL */
static int lak_check_crypt(
const char *hash,
const char *passwd,
void *rock __attribute__((unused)))
{
char *cred;
if (strlen(hash) < 2 )
return LAK_INVALID_PASSWORD;
cred = crypt(passwd, hash);
if (EMPTY(cred))
return LAK_INVALID_PASSWORD;
return strcmp(hash, cred) ? LAK_INVALID_PASSWORD : LAK_OK;
}
#endif /* AUTH_LDAP */