#include "sys_defs.h"
#ifdef HAS_LDAP
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <lber.h>
#include <ldap.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000)
#error "Your LDAP version is too old"
#endif
#ifndef LDAP_CONST
#define LDAP_CONST const
#endif
#ifndef LDAP_OPT_SUCCESS
#define LDAP_OPT_SUCCESS 0
#endif
#include "match_list.h"
#include "match_ops.h"
#include "msg.h"
#include "mymalloc.h"
#include "vstring.h"
#include "dict.h"
#include "stringops.h"
#include "binhash.h"
#include "cfg_parser.h"
#include "dict_ldap.h"
typedef struct {
LDAP *conn_ld;
int conn_refcount;
} LDAP_CONN;
typedef struct {
DICT dict;
CFG_PARSER *parser;
char *ldapsource;
char *server_host;
int server_port;
int scope;
char *search_base;
MATCH_LIST *domain;
char *query_filter;
char *result_filter;
ARGV *result_attributes;
int num_attributes;
int bind;
char *bind_dn;
char *bind_pw;
int timeout;
int dereference;
long recursion_limit;
long expansion_limit;
long size_limit;
int chase_referrals;
int debuglevel;
int version;
#ifdef LDAP_API_FEATURE_X_OPENLDAP
int ldap_ssl;
int start_tls;
int tls_require_cert;
char *tls_ca_cert_file;
char *tls_ca_cert_dir;
char *tls_cert;
char *tls_key;
char *tls_random_file;
char *tls_cipher_suite;
#endif
BINHASH_INFO *ht;
LDAP *ld;
} DICT_LDAP;
#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
static BINHASH *conn_hash = 0;
#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
static jmp_buf env;
static void dict_ldap_timeout(int unused_sig)
{
longjmp(env, 1);
}
#endif
static void dict_ldap_logprint(LDAP_CONST char *data)
{
char *myname = "dict_ldap_debug";
char *buf,
*p;
buf = mystrdup(data);
if (*buf) {
p = buf + strlen(buf) - 1;
while (p - buf >= 0 && ISSPACE(*p))
*p-- = 0;
}
msg_info("%s: %s", myname, buf);
myfree(buf);
}
static int dict_ldap_get_errno(LDAP * ld)
{
int rc;
if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_OPT_SUCCESS)
rc = LDAP_OTHER;
return rc;
}
static int dict_ldap_set_errno(LDAP * ld, int rc)
{
(void) ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &rc);
return rc;
}
static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
{
int msgid;
LDAPMessage *res;
struct timeval mytimeval;
if ((msgid = ldap_bind(dict_ldap->ld, dict_ldap->bind_dn,
dict_ldap->bind_pw, LDAP_AUTH_SIMPLE)) == -1)
return (dict_ldap_get_errno(dict_ldap->ld));
mytimeval.tv_sec = dict_ldap->timeout;
mytimeval.tv_usec = 0;
if (ldap_result(dict_ldap->ld, msgid, 1, &mytimeval, &res) == -1)
return (dict_ldap_get_errno(dict_ldap->ld));
if (dict_ldap_get_errno(dict_ldap->ld) == LDAP_TIMEOUT) {
(void) ldap_abandon(dict_ldap->ld, msgid);
return (dict_ldap_set_errno(dict_ldap->ld, LDAP_TIMEOUT));
}
return (ldap_result2error(dict_ldap->ld, res, 1));
}
#ifdef LDAP_API_FEATURE_X_OPENLDAP
static void dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
{
char *myname = "dict_ldap_set_tls_options";
int rc;
if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
if (*dict_ldap->tls_random_file) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
dict_ldap->tls_random_file)) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_random_file to %s: %d: %s",
myname, dict_ldap->tls_random_file,
rc, ldap_err2string(rc));
}
if (*dict_ldap->tls_ca_cert_file) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s",
myname, dict_ldap->tls_ca_cert_file,
rc, ldap_err2string(rc));
}
if (*dict_ldap->tls_ca_cert_dir) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR,
dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s",
myname, dict_ldap->tls_ca_cert_dir,
rc, ldap_err2string(rc));
}
if (*dict_ldap->tls_cert) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE,
dict_ldap->tls_cert)) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_cert to %s: %d: %s",
myname, dict_ldap->tls_cert,
rc, ldap_err2string(rc));
}
if (*dict_ldap->tls_key) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE,
dict_ldap->tls_key)) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_key to %s: %d: %s",
myname, dict_ldap->tls_key,
rc, ldap_err2string(rc));
}
if (*dict_ldap->tls_cipher_suite) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s",
myname, dict_ldap->tls_cipher_suite,
rc, ldap_err2string(rc));
}
if (dict_ldap->tls_require_cert) {
if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
&(dict_ldap->tls_require_cert))) != LDAP_SUCCESS)
msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s",
myname, dict_ldap->tls_require_cert,
rc, ldap_err2string(rc));
}
}
}
#endif
static int dict_ldap_connect(DICT_LDAP *dict_ldap)
{
char *myname = "dict_ldap_connect";
int rc = 0;
#ifdef LDAP_OPT_NETWORK_TIMEOUT
struct timeval mytimeval;
#endif
#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
void (*saved_alarm) (int);
#endif
#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
if (dict_ldap->debuglevel > 0 &&
ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
(LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
msg_warn("%s: Unable to set ber logprint function.", myname);
#if defined(LBER_OPT_DEBUG_LEVEL)
if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
&(dict_ldap->debuglevel)) != LBER_OPT_SUCCESS)
msg_warn("%s: Unable to set BER debug level.", myname);
#endif
if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
&(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
msg_warn("%s: Unable to set LDAP debug level.", myname);
#endif
dict_errno = 0;
if (msg_verbose)
msg_info("%s: Connecting to server %s", myname,
dict_ldap->server_host);
#ifdef LDAP_OPT_NETWORK_TIMEOUT
#ifdef LDAP_API_FEATURE_X_OPENLDAP
dict_ldap_set_tls_options(dict_ldap);
ldap_initialize(&(dict_ldap->ld), dict_ldap->server_host);
#else
dict_ldap->ld = ldap_init(dict_ldap->server_host,
(int) dict_ldap->server_port);
#endif
if (dict_ldap->ld == NULL) {
msg_warn("%s: Unable to init LDAP server %s",
myname, dict_ldap->server_host);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
mytimeval.tv_sec = dict_ldap->timeout;
mytimeval.tv_usec = 0;
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) !=
LDAP_OPT_SUCCESS)
msg_warn("%s: Unable to set network timeout.", myname);
#else
if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
msg_warn("%s: Error setting signal handler for open timeout: %m",
myname);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
alarm(dict_ldap->timeout);
if (setjmp(env) == 0)
dict_ldap->ld = ldap_open(dict_ldap->server_host,
(int) dict_ldap->server_port);
else
dict_ldap->ld = 0;
alarm(0);
if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
msg_warn("%s: Error resetting signal handler after open: %m",
myname);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
if (dict_ldap->ld == NULL) {
msg_warn("%s: Unable to connect to LDAP server %s",
myname, dict_ldap->server_host);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
#endif
#ifdef LDAP_OPT_PROTOCOL_VERSION
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_PROTOCOL_VERSION,
&dict_ldap->version) != LDAP_OPT_SUCCESS)
msg_warn("%s: Unable to set LDAP protocol version", myname);
if (msg_verbose) {
if (ldap_get_option(dict_ldap->ld,
LDAP_OPT_PROTOCOL_VERSION,
&dict_ldap->version) != LDAP_OPT_SUCCESS)
msg_warn("%s: Unable to get LDAP protocol version", myname);
else
msg_info("%s: Actual Protocol version used is %d.",
myname, dict_ldap->version);
}
#endif
if (dict_ldap->size_limit) {
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT,
&dict_ldap->size_limit) != LDAP_OPT_SUCCESS)
msg_warn("%s: %s: Unable to set query result size limit to %ld.",
myname, dict_ldap->ldapsource, dict_ldap->size_limit);
}
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF,
&(dict_ldap->dereference)) != LDAP_OPT_SUCCESS)
msg_warn("%s: Unable to set dereference option.", myname);
#ifdef LDAP_OPT_REFERRALS
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_REFERRALS,
dict_ldap->chase_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF)
!= LDAP_OPT_SUCCESS)
msg_warn("%s: Unable to set Referral chasing.", myname);
#else
if (dict_ldap->chase_referrals) {
msg_warn("%s: Unable to set Referral chasing.", myname);
}
#endif
#ifdef LDAP_API_FEATURE_X_OPENLDAP
if (dict_ldap->start_tls) {
if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
msg_warn("%s: Error setting signal handler for STARTTLS timeout: %m",
myname);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
alarm(dict_ldap->timeout);
if (setjmp(env) == 0)
rc = ldap_start_tls_s(dict_ldap->ld, NULL, NULL);
else
rc = LDAP_TIMEOUT;
alarm(0);
if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
msg_warn("%s: Error resetting signal handler after STARTTLS: %m",
myname);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
if (rc != LDAP_SUCCESS) {
msg_error("%s: Unable to set STARTTLS: %d: %s", myname,
rc, ldap_err2string(rc));
dict_errno = DICT_ERR_RETRY;
return (-1);
}
}
#endif
if (dict_ldap->bind) {
if (msg_verbose)
msg_info("%s: Binding to server %s as dn %s",
myname, dict_ldap->server_host, dict_ldap->bind_dn);
rc = dict_ldap_bind_st(dict_ldap);
if (rc != LDAP_SUCCESS) {
msg_warn("%s: Unable to bind to server %s as %s: %d (%s)",
myname, dict_ldap->server_host, dict_ldap->bind_dn,
rc, ldap_err2string(rc));
dict_errno = DICT_ERR_RETRY;
return (-1);
}
if (msg_verbose)
msg_info("%s: Successful bind to server %s as %s ",
myname, dict_ldap->server_host, dict_ldap->bind_dn);
}
DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
if (msg_verbose)
msg_info("%s: Cached connection handle for LDAP source %s",
myname, dict_ldap->ldapsource);
return (0);
}
static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
{
VSTRING *keybuf = vstring_alloc(10);
char *key;
int len;
#ifdef LDAP_API_FEATURE_X_OPENLDAP
int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl;
#endif
LDAP_CONN *conn;
#define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1)
#define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu", (unsigned long)(i))
ADDSTR(keybuf, dict_ldap->server_host);
ADDINT(keybuf, dict_ldap->server_port);
ADDINT(keybuf, dict_ldap->bind);
ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : "");
ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : "");
ADDINT(keybuf, dict_ldap->dereference);
ADDINT(keybuf, dict_ldap->chase_referrals);
ADDINT(keybuf, dict_ldap->debuglevel);
ADDINT(keybuf, dict_ldap->version);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
ADDINT(keybuf, dict_ldap->ldap_ssl);
ADDINT(keybuf, dict_ldap->start_tls);
ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : "");
ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : "");
ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : "");
ADDSTR(keybuf, sslon ? dict_ldap->tls_key : "");
ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : "");
ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : "");
#endif
key = vstring_str(keybuf);
len = VSTRING_LEN(keybuf);
if (conn_hash == 0)
conn_hash = binhash_create(0);
if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) {
conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN));
conn->conn_ld = 0;
conn->conn_refcount = 0;
dict_ldap->ht = binhash_enter(conn_hash, key, len, (char *) conn);
}
++DICT_LDAP_CONN(dict_ldap)->conn_refcount;
vstring_free(keybuf);
}
static void dict_ldap_expand_filter(char *ldapsource, char *filter,
char *value, VSTRING *out)
{
char *myname = "dict_ldap_expand_filter";
char *sub,
*end;
sub = filter;
end = sub + strlen(filter);
while (sub < end) {
if (*(sub) == '%') {
char *u = value;
char *p = strrchr(u, '@');
switch (*(sub + 1)) {
case 'd':
if (p)
vstring_strcat(out, p + 1);
break;
case 'u':
if (p)
vstring_strncat(out, u, p - u);
else
vstring_strcat(out, u);
break;
default:
msg_warn("%s: %s: Invalid filter substitution format '%%%c'!",
myname, ldapsource, *(sub + 1));
case 's':
vstring_strcat(out, u);
break;
}
sub++;
} else
vstring_strncat(out, sub, 1);
sub++;
}
}
static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
VSTRING *result)
{
static int recursion = 0;
static int expansion;
long entries = 0;
long i = 0;
int rc = 0;
LDAPMessage *resloop = 0;
LDAPMessage *entry = 0;
BerElement *ber;
char **vals;
char *attr;
char *myname = "dict_ldap_get_values";
struct timeval tv;
LDAPURLDesc *url;
tv.tv_sec = dict_ldap->timeout;
tv.tv_usec = 0;
if (++recursion == 1)
expansion = 0;
if (msg_verbose)
msg_info("%s[%d]: Search found %d match(es)", myname, recursion,
ldap_count_entries(dict_ldap->ld, res));
for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
entry = ldap_next_entry(dict_ldap->ld, entry)) {
ber = NULL;
if (dict_errno == 0 && ++entries > dict_ldap->size_limit
&& dict_ldap->size_limit) {
msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded", myname,
recursion, dict_ldap->ldapsource, dict_ldap->size_limit);
dict_errno = DICT_ERR_RETRY;
}
for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber);
attr != NULL;
ldap_memfree(attr), attr = ldap_next_attribute(dict_ldap->ld,
entry, ber)) {
vals = ldap_get_values(dict_ldap->ld, entry, attr);
if (vals == NULL) {
if (msg_verbose)
msg_info("%s[%d]: Entry doesn't have any values for %s",
myname, recursion, attr);
continue;
}
if (dict_errno != 0 || vals[0] == 0) {
ldap_value_free(vals);
continue;
}
for (i = 0; dict_ldap->result_attributes->argv[i]; i++) {
if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0)
break;
}
if (i < dict_ldap->num_attributes) {
for (i = 0; vals[i] != NULL; i++) {
if (++expansion > dict_ldap->expansion_limit &&
dict_ldap->expansion_limit) {
msg_warn("%s[%d]: %s: Expansion limit exceeded at"
" result attribute %s=%s", myname, recursion,
dict_ldap->ldapsource, attr, vals[i]);
dict_errno = DICT_ERR_RETRY;
break;
}
if (VSTRING_LEN(result) > 0)
vstring_strcat(result, ",");
if (dict_ldap->result_filter == NULL)
vstring_strcat(result, vals[i]);
else
dict_ldap_expand_filter(dict_ldap->ldapsource,
dict_ldap->result_filter,
vals[i], result);
}
if (dict_errno != 0)
continue;
if (msg_verbose)
msg_info("%s[%d]: search returned %ld value(s) for"
" requested result attribute %s",
myname, recursion, i, attr);
} else if (recursion < dict_ldap->recursion_limit
&& dict_ldap->result_attributes->argv[i]) {
for (i = 0; vals[i] != NULL; i++) {
if (ldap_is_ldap_url(vals[i])) {
if (msg_verbose)
msg_info("%s[%d]: looking up URL %s", myname,
recursion, vals[i]);
rc = ldap_url_parse(vals[i], &url);
if (rc == 0) {
rc = ldap_search_st(dict_ldap->ld, url->lud_dn,
url->lud_scope, url->lud_filter,
url->lud_attrs, 0, &tv,
&resloop);
ldap_free_urldesc(url);
}
} else {
if (msg_verbose)
msg_info("%s[%d]: looking up DN %s",
myname, recursion, vals[i]);
rc = ldap_search_st(dict_ldap->ld, vals[i],
LDAP_SCOPE_BASE, "objectclass=*",
dict_ldap->result_attributes->argv,
0, &tv, &resloop);
}
switch (rc) {
case LDAP_SUCCESS:
dict_ldap_get_values(dict_ldap, resloop, result);
break;
case LDAP_NO_SUCH_OBJECT:
msg_warn("%s[%d]: DN %s not found, skipping ", myname,
recursion, vals[i]);
break;
default:
msg_warn("%s[%d]: search error %d: %s ", myname,
recursion, rc, ldap_err2string(rc));
dict_errno = DICT_ERR_RETRY;
break;
}
if (resloop != 0)
ldap_msgfree(resloop);
if (dict_errno != 0)
break;
}
if (dict_errno != 0)
continue;
if (msg_verbose)
msg_info("%s[%d]: search returned %ld value(s) for"
" special result attribute %s",
myname, recursion, i, attr);
} else if (recursion >= dict_ldap->recursion_limit
&& dict_ldap->result_attributes->argv[i]) {
msg_warn("%s[%d]: %s: Recursion limit exceeded"
" for special attribute %s=%s",
myname, recursion, dict_ldap->ldapsource, attr, vals[0]);
dict_errno = DICT_ERR_RETRY;
}
ldap_value_free(vals);
}
if (ber)
ber_free(ber, 0);
}
if (msg_verbose)
msg_info("%s[%d]: Leaving %s", myname, recursion, myname);
--recursion;
}
static const char *dict_ldap_lookup(DICT *dict, const char *name)
{
char *myname = "dict_ldap_lookup";
DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
LDAPMessage *res = 0;
static VSTRING *result;
struct timeval tv;
VSTRING *escaped_name = 0,
*filter_buf = 0;
int rc = 0;
int sizelimit;
char *sub,
*end;
dict_errno = 0;
if (msg_verbose)
msg_info("%s: In dict_ldap_lookup", myname);
if (dict_ldap->domain) {
const char *p = strrchr(name, '@');
if (p == 0 || p == name ||
match_list_match(dict_ldap->domain, ++p) == 0) {
if (msg_verbose)
msg_info("%s: domain of %s not found in domain list", myname,
name);
return (0);
}
}
if (result == 0)
result = vstring_alloc(2);
vstring_strcpy(result, "");
dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;
if (dict_ldap->ld == NULL) {
if (msg_verbose)
msg_info
("%s: No existing connection for LDAP source %s, reopening",
myname, dict_ldap->ldapsource);
dict_ldap_connect(dict_ldap);
if (dict_errno)
return (0);
} else if (msg_verbose)
msg_info("%s: Using existing connection for LDAP source %s",
myname, dict_ldap->ldapsource);
sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT;
if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
!= LDAP_OPT_SUCCESS)
msg_warn("%s: %s: Unable to set query result size limit to %ld.",
myname, dict_ldap->ldapsource, dict_ldap->size_limit);
tv.tv_sec = dict_ldap->timeout;
tv.tv_usec = 0;
escaped_name = vstring_alloc(20);
filter_buf = vstring_alloc(30);
end = (char *) name + strlen((char *) name);
sub = (char *) strpbrk((char *) name, "*()\\\0");
if (sub && sub != end) {
if (msg_verbose)
msg_info("%s: Found character(s) in %s that must be escaped",
myname, name);
for (sub = (char *) name; sub != end; sub++) {
switch (*sub) {
case '*':
vstring_strcat(escaped_name, "\\2a");
break;
case '(':
vstring_strcat(escaped_name, "\\28");
break;
case ')':
vstring_strcat(escaped_name, "\\29");
break;
case '\\':
vstring_strcat(escaped_name, "\\5c");
break;
case '\0':
vstring_strcat(escaped_name, "\\00");
break;
default:
vstring_strncat(escaped_name, sub, 1);
}
}
if (msg_verbose)
msg_info("%s: After escaping, it's %s", myname,
vstring_str(escaped_name));
} else
vstring_strcpy(escaped_name, (char *) name);
if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) {
msg_warn("%s: %s: Fixed query_filter %s is probably useless",
myname, dict_ldap->ldapsource, dict_ldap->query_filter);
vstring_strcpy(filter_buf, dict_ldap->query_filter);
} else {
dict_ldap_expand_filter(dict_ldap->ldapsource, dict_ldap->query_filter,
vstring_str(escaped_name), filter_buf);
}
if (msg_verbose)
msg_info("%s: Searching with filter %s", myname,
vstring_str(filter_buf));
rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
dict_ldap->scope,
vstring_str(filter_buf),
dict_ldap->result_attributes->argv,
0, &tv, &res);
if (rc == LDAP_SERVER_DOWN) {
if (msg_verbose)
msg_info("%s: Lost connection for LDAP source %s, reopening",
myname, dict_ldap->ldapsource);
ldap_unbind(dict_ldap->ld);
dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
dict_ldap_connect(dict_ldap);
if (dict_errno)
return (0);
rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
dict_ldap->scope,
vstring_str(filter_buf),
dict_ldap->result_attributes->argv,
0, &tv, &res);
}
if (rc == LDAP_SUCCESS) {
dict_ldap_get_values(dict_ldap, res, result);
rc = dict_ldap_get_errno(dict_ldap->ld);
if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR)
msg_warn
("%s: Had some trouble with entries returned by search: %s",
myname, ldap_err2string(rc));
if (msg_verbose)
msg_info("%s: Search returned %s", myname,
VSTRING_LEN(result) >
0 ? vstring_str(result) : "nothing");
} else {
msg_warn("%s: Search error %d: %s ", myname, rc,
ldap_err2string(rc));
ldap_unbind(dict_ldap->ld);
dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
dict_errno = DICT_ERR_RETRY;
}
if (res != 0)
ldap_msgfree(res);
if (filter_buf != 0)
vstring_free(filter_buf);
if (escaped_name != 0)
vstring_free(escaped_name);
return (VSTRING_LEN(result) > 0 && !dict_errno ? vstring_str(result) : 0);
}
static void dict_ldap_close(DICT *dict)
{
char *myname = "dict_ldap_close";
DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
BINHASH_INFO *ht = dict_ldap->ht;
if (--conn->conn_refcount == 0) {
if (conn->conn_ld) {
if (msg_verbose)
msg_info("%s: Closed connection handle for LDAP source %s",
myname, dict_ldap->ldapsource);
ldap_unbind(conn->conn_ld);
}
binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
}
cfg_parser_free(dict_ldap->parser);
myfree(dict_ldap->ldapsource);
myfree(dict_ldap->server_host);
myfree(dict_ldap->search_base);
if (dict_ldap->domain)
match_list_free(dict_ldap->domain);
myfree(dict_ldap->query_filter);
if (dict_ldap->result_filter)
myfree(dict_ldap->result_filter);
argv_free(dict_ldap->result_attributes);
myfree(dict_ldap->bind_dn);
myfree(dict_ldap->bind_pw);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
myfree(dict_ldap->tls_ca_cert_file);
myfree(dict_ldap->tls_ca_cert_dir);
myfree(dict_ldap->tls_cert);
myfree(dict_ldap->tls_key);
myfree(dict_ldap->tls_random_file);
myfree(dict_ldap->tls_cipher_suite);
#endif
dict_free(dict);
}
DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
{
char *myname = "dict_ldap_open";
DICT_LDAP *dict_ldap;
VSTRING *url_list;
char *s;
char *h;
char *server_host;
char *domainlist;
char *scope;
char *attr;
int tmp;
if (msg_verbose)
msg_info("%s: Using LDAP source %s", myname, ldapsource);
dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource,
sizeof(*dict_ldap));
dict_ldap->dict.lookup = dict_ldap_lookup;
dict_ldap->dict.close = dict_ldap_close;
dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED;
dict_ldap->ld = NULL;
dict_ldap->parser = cfg_parser_alloc(ldapsource);
dict_ldap->ldapsource = mystrdup(ldapsource);
server_host = cfg_get_str(dict_ldap->parser, "server_host",
"localhost", 1, 0);
dict_ldap->server_port =
cfg_get_int(dict_ldap->parser, "server_port", LDAP_PORT, 0, 0);
dict_ldap->version = cfg_get_int(dict_ldap->parser, "version", 2, 2, 0);
switch (dict_ldap->version) {
case 2:
dict_ldap->version = LDAP_VERSION2;
break;
case 3:
dict_ldap->version = LDAP_VERSION3;
break;
default:
msg_warn("%s: %s Unknown version %d.", myname, ldapsource,
dict_ldap->version);
dict_ldap->version = LDAP_VERSION2;
}
#if defined(LDAP_API_FEATURE_X_OPENLDAP)
dict_ldap->ldap_ssl = 0;
#endif
url_list = vstring_alloc(32);
s = server_host;
while ((h = mystrtok(&s, " \t\n\r,")) != NULL) {
#if defined(LDAP_API_FEATURE_X_OPENLDAP)
if (ldap_is_ldap_url(h)) {
LDAPURLDesc *url_desc;
int rc;
if ((rc = ldap_url_parse(h, &url_desc)) != 0) {
msg_error("%s: error parsing URL %s: %d: %s; skipping", myname,
h, rc, ldap_err2string(rc));
continue;
}
if (strcasecmp(url_desc->lud_scheme, "ldap") != 0 &&
dict_ldap->version != LDAP_VERSION3) {
msg_warn("%s: URL scheme %s requires protocol version 3", myname,
url_desc->lud_scheme);
dict_ldap->version = LDAP_VERSION3;
}
if (strcasecmp(url_desc->lud_scheme, "ldaps") == 0)
dict_ldap->ldap_ssl = 1;
ldap_free_urldesc(url_desc);
vstring_sprintf_append(url_list, " %s", h);
} else {
if (strrchr(h, ':'))
vstring_sprintf_append(url_list, " ldap://%s", h);
else
vstring_sprintf_append(url_list, " ldap://%s:%d", h,
dict_ldap->server_port);
}
#else
vstring_sprintf_append(url_list, " %s", h);
#endif
}
dict_ldap->server_host =
mystrdup(VSTRING_LEN(url_list) > 0 ? vstring_str(url_list) + 1 : "");
#if defined(LDAP_API_FEATURE_X_OPENLDAP)
dict_ldap->server_port = LDAP_PORT;
if (msg_verbose)
msg_info("%s: %s server_host URL is %s", myname, ldapsource,
dict_ldap->server_host);
#endif
myfree(server_host);
vstring_free(url_list);
scope = cfg_get_str(dict_ldap->parser, "scope", "sub", 1, 0);
if (strcasecmp(scope, "one") == 0) {
dict_ldap->scope = LDAP_SCOPE_ONELEVEL;
} else if (strcasecmp(scope, "base") == 0) {
dict_ldap->scope = LDAP_SCOPE_BASE;
} else if (strcasecmp(scope, "sub") == 0) {
dict_ldap->scope = LDAP_SCOPE_SUBTREE;
} else {
msg_warn("%s: %s: Unrecognized value %s specified for scope; using sub",
myname, ldapsource, scope);
dict_ldap->scope = LDAP_SCOPE_SUBTREE;
}
myfree(scope);
dict_ldap->search_base = cfg_get_str(dict_ldap->parser, "search_base",
"", 0, 0);
domainlist = cfg_get_str(dict_ldap->parser, "domain", "", 0, 0);
if (*domainlist) {
#ifdef MATCH_FLAG_NONE
dict_ldap->domain = match_list_init(MATCH_FLAG_NONE,
domainlist, 1, match_string);
#else
dict_ldap->domain = match_list_init(domainlist, 1, match_string);
#endif
if (dict_ldap->domain == NULL)
msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it",
myname, domainlist);
if (msg_verbose)
msg_info("%s: domain list created using \"%s\"", myname,
domainlist);
} else {
dict_ldap->domain = NULL;
}
myfree(domainlist);
dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout",
10, 0, 0);
dict_ldap->query_filter =
cfg_get_str(dict_ldap->parser, "query_filter",
"(mailacceptinggeneralid=%s)", 0, 0);
dict_ldap->result_filter =
cfg_get_str(dict_ldap->parser, "result_filter", "%s", 0, 0);
if (strcmp(dict_ldap->result_filter, "%s") == 0) {
myfree(dict_ldap->result_filter);
dict_ldap->result_filter = NULL;
}
attr = cfg_get_str(dict_ldap->parser, "result_attribute",
"maildrop", 0, 0);
dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n");
dict_ldap->num_attributes = dict_ldap->result_attributes->argc;
myfree(attr);
attr = cfg_get_str(dict_ldap->parser, "special_result_attribute",
"", 0, 0);
if (*attr) {
argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
}
myfree(attr);
dict_ldap->bind = cfg_get_bool(dict_ldap->parser, "bind", 1);
dict_ldap->bind_dn = cfg_get_str(dict_ldap->parser, "bind_dn", "", 0, 0);
dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw", "", 0, 0);
tmp = cfg_get_bool(dict_ldap->parser, "cache", 0);
if (tmp)
msg_warn("%s: %s ignoring cache", myname, ldapsource);
tmp = cfg_get_int(dict_ldap->parser, "cache_expiry", -1, 0, 0);
if (tmp >= 0)
msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource);
tmp = cfg_get_int(dict_ldap->parser, "cache_size", -1, 0, 0);
if (tmp >= 0)
msg_warn("%s: %s ignoring cache_size", myname, ldapsource);
dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser,
"recursion_limit", 1000, 1, 0);
dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser,
"expansion_limit", 0, 0, 0);
dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit",
dict_ldap->expansion_limit,
0, 0);
dict_ldap->dereference = cfg_get_int(dict_ldap->parser, "dereference",
0, 0, 0);
if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) {
msg_warn("%s: %s Unrecognized value %d specified for dereference; using 0",
myname, ldapsource, dict_ldap->dereference);
dict_ldap->dereference = 0;
}
dict_ldap->chase_referrals = cfg_get_bool(dict_ldap->parser,
"chase_referrals", 0);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
dict_ldap->start_tls = cfg_get_bool(dict_ldap->parser, "start_tls", 0);
if (dict_ldap->start_tls && dict_ldap->version < LDAP_VERSION3) {
msg_warn("%s: %s start_tls requires protocol version 3",
myname, ldapsource);
dict_ldap->version = LDAP_VERSION3;
}
dict_ldap->tls_require_cert = cfg_get_bool(dict_ldap->parser,
"tls_require_cert", 0);
dict_ldap->tls_ca_cert_file = cfg_get_str(dict_ldap->parser,
"tls_ca_cert_file", "", 0, 0);
dict_ldap->tls_ca_cert_dir = cfg_get_str(dict_ldap->parser,
"tls_ca_cert_dir", "", 0, 0);
dict_ldap->tls_cert = cfg_get_str(dict_ldap->parser, "tls_cert",
"", 0, 0);
dict_ldap->tls_key = cfg_get_str(dict_ldap->parser, "tls_key",
"", 0, 0);
dict_ldap->tls_random_file = cfg_get_str(dict_ldap->parser,
"tls_random_file", "", 0, 0);
dict_ldap->tls_cipher_suite = cfg_get_str(dict_ldap->parser,
"tls_cipher_suite", "", 0, 0);
#endif
#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
dict_ldap->debuglevel = cfg_get_int(dict_ldap->parser, "debuglevel",
0, 0, 0);
#endif
dict_ldap_conn_find(dict_ldap);
return (DICT_DEBUG (&dict_ldap->dict));
}
#endif