Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

einige Melodien

SSL dict_ldap.c   Sprache: unbekannt

 
/*++
/* NAME
/* dict_ldap 3
/* SUMMARY
/* dictionary manager interface to LDAP maps
/* SYNOPSIS
/* #include <dict_ldap.h>
/*
/* DICT    *dict_ldap_open(attribute, dummy, dict_flags)
/* const char *ldapsource;
/* int dummy;
/* int dict_flags;
/* DESCRIPTION
/* dict_ldap_open() makes LDAP user information accessible via
/* the generic dictionary operations described in dict_open(3).
/*
/* Arguments:
/* .IP ldapsource
/* Either the path to the LDAP configuration file (if it starts
/* with '/' or '.'), or the prefix which will be used to obtain
/* configuration parameters for this search.
/*
/* In the first case, the configuration variables below are
/* specified in the file as \fBname\fR=\fBvalue\fR pairs.
/*
/* In the second case, the configuration variables are prefixed
/* with the value of \fIldapsource\fR and an underscore,
/* and they are specified in main.cf.  For example, if this
/* value is \fBldapone\fR, the variables would look like
/* \fBldapone_server_host\fR, \fBldapone_search_base\fR, and so on.
/* .IP dummy
/* Not used; this argument exists only for compatibility with
/* the dict_open(3) interface.
/* SEE ALSO
/* dict(3) generic dictionary manager
/* ldap_table(5) LDAP client configuration
/* AUTHOR(S)
/* Prabhat K Singh
/* VSNL, Bombay, India.
/* prabhat@giasbm01.vsnl.net.in
/*
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
/* John Hensley
/* john@sunislelodge.com
/*
/* LaMont Jones
/* lamont@debian.org
/*
/* Victor Duchovni
/* Morgan Stanley
/* New York, USA
/*
/* Liviu Daia
/* Institute of Mathematics of the Romanian Academy
/* P.O. BOX 1-764
/* RO-014700 Bucharest, ROMANIA
/*--*/


/* System library. */

#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>

#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif

 /*
  * Older APIs have weird memory freeing behavior.
  */

#if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000)
#error "Your LDAP version is too old"
#endif

/* Handle differences between LDAP SDK's constant definitions */
#ifndef LDAP_CONST
#define LDAP_CONST const
#endif
#ifndef LDAP_OPT_SUCCESS
#define LDAP_OPT_SUCCESS 0
#endif

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <dict.h>
#include <stringops.h>
#include <binhash.h>
#include <name_code.h>

/* Global library. */

#include "cfg_parser.h"
#include "db_common.h"
#include "mail_conf.h"

#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)

 /*
  * SASL headers, for sasl_interact_t. Either SASL v1 or v2 should be fine.
  */

#include <sasl.h>
#endif

/* Application-specific. */

#include "dict_ldap.h"

#define DICT_LDAP_BIND_NONE 0
#define DICT_LDAP_BIND_SIMPLE 1
#define DICT_LDAP_BIND_SASL 2
#define DICT_LDAP_DO_BIND(d) ((d)->bind != DICT_LDAP_BIND_NONE)
#define DICT_LDAP_DO_SASL(d) ((d)->bind == DICT_LDAP_BIND_SASL)

static const NAME_CODE bindopt_table[] = {
    CONFIG_BOOL_NO, DICT_LDAP_BIND_NONE,
    "none", DICT_LDAP_BIND_NONE,
    CONFIG_BOOL_YES, DICT_LDAP_BIND_SIMPLE,
    "simple", DICT_LDAP_BIND_SIMPLE,
#ifdef LDAP_API_FEATURE_X_OPENLDAP
#if defined(USE_LDAP_SASL)
    "sasl", DICT_LDAP_BIND_SASL,
#endif
#endif
    0, -1,
};

typedef struct {
    LDAP   *conn_ld;
    int     conn_refcount;
} LDAP_CONN;

/*
 * Structure containing all the configuration parameters for a given
 * LDAP source, plus its connection handle.
 */

typedef struct {
    DICT    dict;   /* generic member */
    CFG_PARSER *parser;   /* common parameter parser */
    char   *query;   /* db_common_expand() query */
    char   *result_format;  /* db_common_expand() result_format */
    void   *ctx;   /* db_common_parse() context */
    int     dynamic_base;  /* Search base has substitutions? */
    int     expansion_limit;
    char   *server_host;
    int     server_port;
    int     scope;
    char   *search_base;
    ARGV   *result_attributes;
    int     num_terminal;  /* Number of terminal attributes. */
    int     num_leaf;   /* Number of leaf attributes */
    int     num_attributes;  /* Combined # of non-special attrs */
    int     bind;
    char   *bind_dn;
    char   *bind_pw;
    int     timeout;
    int     dereference;
    long    recursion_limit;
    long    size_limit;
    int     chase_referrals;
    int     debuglevel;
    int     version;
#ifdef LDAP_API_FEATURE_X_OPENLDAP
#if defined(USE_LDAP_SASL)
    int     sasl;
    char   *sasl_mechs;
    char   *sasl_realm;
    char   *sasl_authz;
    int     sasl_minssf;
#endif
    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;   /* hash entry for LDAP connection */
    LDAP   *ld;    /* duplicated from conn->conn_ld */
} DICT_LDAP;

#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))

#define DICT_LDAP_UNBIND_RETURN(__ld, __err, __ret) do { \
 dict_ldap_unbind(__ld); \
 (__ld) = 0; \
 dict_ldap->dict.error = (__err); \
 return ((__ret)); \
    } while (0)

 /*
  * Bitrot: LDAP_API 3000 and up (OpenLDAP 2.2.x) deprecated ldap_unbind()
  */

#if LDAP_API_VERSION >= 3000
#define dict_ldap_unbind(ld)  ldap_unbind_ext((ld), 00)
#define dict_ldap_abandon(ld, msg) ldap_abandon_ext((ld), (msg), 00)
#else
#define dict_ldap_unbind(ld)  ldap_unbind(ld)
#define dict_ldap_abandon(ld, msg) ldap_abandon((ld), (msg))
#endif

static int dict_ldap_vendor_version(void)
{
    const char *myname = "dict_ldap_api_info";
    LDAPAPIInfo api;

    /*
     * We tell the library our version, and it tells us its version and/or
     * may return an error code if the versions are not the same.
     */

    api.ldapai_info_version = LDAP_API_INFO_VERSION;
    if (ldap_get_option(0, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS
 || api.ldapai_info_version != LDAP_API_INFO_VERSION) {
 if (api.ldapai_info_version != LDAP_API_INFO_VERSION)
     msg_fatal("%s: run-time API_INFO version: %d, compiled with: %d",
      myname, api.ldapai_info_version, LDAP_API_INFO_VERSION);
 else
     msg_fatal("%s: ldap_get_option(API_INFO) failed", myname);
    }
    if (strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME) != 0)
 msg_fatal("%s: run-time API vendor: %s, compiled with: %s",
    myname, api.ldapai_vendor_name, LDAP_VENDOR_NAME);

    return (api.ldapai_vendor_version);
}

/*
 * Quoting rules.
 */


/* rfc2253_quote - Quote input key for safe inclusion in the search base */

static void rfc2253_quote(DICT *unused, const char *name, VSTRING *result)
{
    const char *sub = name;
    size_t  len;

    /*
     * The RFC only requires quoting of a leading or trailing space, but it
     * is harmless to quote whitespace everywhere. Similarly, we quote all
     * '#' characters, even though only the leading '#' character requires
     * quoting per the RFC.
     */

    while (*sub)
 if ((len = strcspn(sub, " \t\"#+,;<>\\")) > 0) {
     vstring_strncat(result, sub, len);
     sub += len;
 } else
     vstring_sprintf_append(result, "\\%02X",
       *((const unsigned char *) sub++));
}

/* rfc2254_quote - Quote input key for safe inclusion in the query filter */

static void rfc2254_quote(DICT *unused, const char *name, VSTRING *result)
{
    const char *sub = name;
    size_t  len;

    /*
     * If any characters in the supplied address should be escaped per RFC
     * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
     * Samuel Tardieu for spotting that wildcard searches were being done in
     * the first place, which prompted the ill-conceived lookup_wildcards
     * parameter and then this more comprehensive mechanism.
     */

    while (*sub)
 if ((len = strcspn(sub, "*()\\")) > 0) {
     vstring_strncat(result, sub, len);
     sub += len;
 } else
     vstring_sprintf_append(result, "\\%02X",
       *((const unsigned char *) sub++));
}

static BINHASH *conn_hash = 0;

#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
/*
 * LDAP connection timeout support.
 */

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)
{
    const 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;
}

#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)

 /*
  * Context structure for SASL property callback.
  */

typedef struct bind_props {
    char   *authcid;
    char   *passwd;
    char   *realm;
    char   *authzid;
} bind_props;

static int ldap_b2_interact(LDAP *ld, unsigned flags, void *props, void *inter)
{

    sasl_interact_t *in;
    bind_props *ctx = (bind_props *) props;

    for (in = inter; in->id != SASL_CB_LIST_END; in++) {
 in->result = NULL;
 switch (in->id) {
 case SASL_CB_GETREALM:
     in->result = ctx->realm;
     break;
 case SASL_CB_AUTHNAME:
     in->result = ctx->authcid;
     break;
 case SASL_CB_USER:
     in->result = ctx->authzid;
     break;
 case SASL_CB_PASS:
     in->result = ctx->passwd;
     break;
 }
 if (in->result)
     in->len = strlen(in->result);
    }
    return LDAP_SUCCESS;
}

#endif

/* dict_ldap_result - Read and parse LDAP result */

static int dict_ldap_result(LDAP *ld, int msgid, int timeout, LDAPMessage **res)
{
    struct timeval mytimeval;
    int     err;

    mytimeval.tv_sec = timeout;
    mytimeval.tv_usec = 0;

#define GET_ALL 1
    if (ldap_result(ld, msgid, GET_ALL, &mytimeval, res) == -1)
 return (dict_ldap_get_errno(ld));

    if ((err = dict_ldap_get_errno(ld)) != LDAP_SUCCESS) {
 if (err == LDAP_TIMEOUT) {
     (void) dict_ldap_abandon(ld, msgid);
     return (dict_ldap_set_errno(ld, LDAP_TIMEOUT));
 }
 return err;
    }
    return LDAP_SUCCESS;
}

#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)

/* Asynchronous SASL auth if SASL is enabled */

static int dict_ldap_bind_sasl(DICT_LDAP *dict_ldap)
{
    int     rc;
    bind_props props;
    static VSTRING *minssf = 0;

    if (minssf == 0)
 minssf = vstring_alloc(12);

    vstring_sprintf(minssf, "minssf=%d", dict_ldap->sasl_minssf);

    if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_X_SASL_SECPROPS,
         (char *) minssf)) != LDAP_OPT_SUCCESS)
 return (rc);

    props.authcid = dict_ldap->bind_dn;
    props.passwd = dict_ldap->bind_pw;
    props.realm = dict_ldap->sasl_realm;
    props.authzid = dict_ldap->sasl_authz;

    if ((rc = ldap_sasl_interactive_bind_s(dict_ldap->ld, NULL,
        dict_ldap->sasl_mechs, NULL, NULL,
        LDAP_SASL_QUIET, ldap_b2_interact,
        &props)) != LDAP_SUCCESS)
 return (rc);

    return (LDAP_SUCCESS);
}

#endif

/* dict_ldap_bind_st - Synchronous simple auth with timeout */

static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
{
    int     rc;
    int     err = LDAP_SUCCESS;
    int     msgid;
    LDAPMessage *res;
    struct berval cred;

    cred.bv_val = dict_ldap->bind_pw;
    cred.bv_len = strlen(cred.bv_val);
    if ((rc = ldap_sasl_bind(dict_ldap->ld, dict_ldap->bind_dn,
        LDAP_SASL_SIMPLE, &cred,
        00, &msgid)) != LDAP_SUCCESS)
 return (rc);
    if ((rc = dict_ldap_result(dict_ldap->ld, msgid, dict_ldap->timeout,
          &res)) != LDAP_SUCCESS)
 return (rc);

#define FREE_RESULT 1
    rc = ldap_parse_result(dict_ldap->ld, res, &err, 0000, FREE_RESULT);
    return (rc == LDAP_SUCCESS ? err : rc);
}

/* search_st - Synchronous search with timeout */

static int search_st(LDAP *ld, char *base, int scope, char *query,
               char **attrs, int timeout, LDAPMessage **res)
{
    struct timeval mytimeval;
    int     msgid;
    int     rc;
    int     err;

    mytimeval.tv_sec = timeout;
    mytimeval.tv_usec = 0;

#define WANTVALS 0
#define USE_SIZE_LIM_OPT -1   /* Any negative value will do */

    if ((rc = ldap_search_ext(ld, base, scope, query, attrs, WANTVALS, 00,
         &mytimeval, USE_SIZE_LIM_OPT,
         &msgid)) != LDAP_SUCCESS)
 return rc;

    if ((rc = dict_ldap_result(ld, msgid, timeout, res)) != LDAP_SUCCESS)
 return (rc);

#define DONT_FREE_RESULT 0
    rc = ldap_parse_result(ld, *res, &err, 0000, DONT_FREE_RESULT);
    return (err != LDAP_SUCCESS ? err : rc);
}

#ifdef LDAP_API_FEATURE_X_OPENLDAP
static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
{
    const char *myname = "dict_ldap_set_tls_options";
    int     rc;

#ifdef LDAP_OPT_X_TLS_NEWCTX
    int     am_server = 0;
    LDAP   *ld = dict_ldap->ld;

#else
    LDAP   *ld = 0;

#endif

    if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
 if (*dict_ldap->tls_random_file) {
     if ((rc = ldap_set_option(ld, 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));
  return (-1);
     }
 }
 if (*dict_ldap->tls_ca_cert_file) {
     if ((rc = ldap_set_option(ld, 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));
  return (-1);
     }
 }
 if (*dict_ldap->tls_ca_cert_dir) {
     if ((rc = ldap_set_option(ld, 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));
  return (-1);
     }
 }
 if (*dict_ldap->tls_cert) {
     if ((rc = ldap_set_option(ld, 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));
  return (-1);
     }
 }
 if (*dict_ldap->tls_key) {
     if ((rc = ldap_set_option(ld, 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));
  return (-1);
     }
 }
 if (*dict_ldap->tls_cipher_suite) {
     if ((rc = ldap_set_option(ld, 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));
  return (-1);
     }
 }
 if ((rc = ldap_set_option(ld, 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));
     return (-1);
 }
#ifdef LDAP_OPT_X_TLS_NEWCTX
 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &am_server))
     != LDAP_SUCCESS) {
     msg_warn("%s: Unable to allocate new TLS context %d: %s",
       myname, rc, ldap_err2string(rc));
     return (-1);
 }
#endif
    }
    return (0);
}

#endif

/* Establish a connection to the LDAP server. */
static int dict_ldap_connect(DICT_LDAP *dict_ldap)
{
    const 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 void *) 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_ldap->dict.error = 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
    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_ldap->dict.error = 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);
 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
    }
#else
    if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
 msg_warn("%s: Error setting signal handler for open timeout: %m",
   myname);
 dict_ldap->dict.error = 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_ldap->dict.error = 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_ldap->dict.error = DICT_ERR_RETRY;
 return (-1);
    }
#endif

    /*
     * v3 support is needed for referral chasing.  Thanks to Sami Haahtinen
     * for the patch.
     */

#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);
 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
    }
    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

    /*
     * Limit the number of entries returned by each query.
     */

    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->parser->name, dict_ldap->size_limit);
     DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
 }
    }

    /*
     * Configure alias dereferencing for this connection. Thanks to Mike
     * Mattice for this, and to Hery Rakotoarisoa for the v3 update.
     */

    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);

    /* Chase referrals. */

#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);
 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
    }
#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_set_tls_options(dict_ldap) != 0)
 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
    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_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
 }
 alarm(dict_ldap->timeout);
 if (setjmp(env) == 0)
     rc = ldap_start_tls_s(dict_ldap->ld, NULL, NULL);
 else {
     rc = LDAP_TIMEOUT;
     dict_ldap->ld = 0;   /* Unknown state after
 * longjmp() */

 }
 alarm(0);

 if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
     msg_warn("%s: Error resetting signal handler after STARTTLS: %m",
       myname);
     dict_ldap->dict.error = DICT_ERR_RETRY;
     return (-1);
 }
 if (rc != LDAP_SUCCESS) {
     msg_error("%s: Unable to set STARTTLS: %d: %s", myname,
        rc, ldap_err2string(rc));
     dict_ldap->dict.error = DICT_ERR_RETRY;
     return (-1);
 }
    }
#endif

#define DN_LOG_VAL(dict_ldap) \
 ((dict_ldap)->bind_dn[0] ? (dict_ldap)->bind_dn : "empty or implicit")

    /*
     * If this server requires a bind, do so. Thanks to Sam Tardieu for
     * noticing that the original bind call was broken.
     */

    if (DICT_LDAP_DO_BIND(dict_ldap)) {
 if (msg_verbose)
     msg_info("%s: Binding to server %s with dn %s",
       myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap));

#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
 if (DICT_LDAP_DO_SASL(dict_ldap)) {
     rc = dict_ldap_bind_sasl(dict_ldap);
 } else {
     rc = dict_ldap_bind_st(dict_ldap);
 }
#else
 rc = dict_ldap_bind_st(dict_ldap);
#endif

 if (rc != LDAP_SUCCESS) {
     msg_warn("%s: Unable to bind to server %s with dn %s: %d (%s)",
       myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap),
       rc, ldap_err2string(rc));
     DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
 }
 if (msg_verbose)
     msg_info("%s: Successful bind to server %s with dn %s",
       myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap));
    }
    /* Save connection handle in shared container */
    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->parser->name);

    return (0);
}

/*
 * Locate or allocate connection cache entry.
 */

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;

    /*
     * Join key fields with null characters.
     */

#define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1)
#define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu%c", (unsigned long)(i), 0)

    ADDSTR(keybuf, dict_ldap->server_host);
    ADDINT(keybuf, dict_ldap->server_port);
    ADDINT(keybuf, dict_ldap->bind);
    ADDSTR(keybuf, DICT_LDAP_DO_BIND(dict_ldap) ? dict_ldap->bind_dn : "");
    ADDSTR(keybuf, DICT_LDAP_DO_BIND(dict_ldap) ? 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
#if defined(USE_LDAP_SASL)
    ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_mechs : "");
    ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_realm : "");
    ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_authz : "");
    ADDINT(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_minssf : 0);
#endif
    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, (void *) conn);
    }
    ++DICT_LDAP_CONN(dict_ldap)->conn_refcount;

    vstring_free(keybuf);
}

/* attr_sub_type - Is one of two attributes a sub-type of another */

static int attrdesc_subtype(const char *a1, const char *a2)
{

    /*
     * RFC 2251 section 4.1.4: LDAP attribute names are case insensitive
     */

    while (*a1 && TOLOWER(*a1) == TOLOWER(*a2))
 ++a1, ++a2;

    /*
     * Names equal to end of a1, is a2 equal or a subtype?
     */

    if (*a1 == 0 && (*a2 == 0 || *a2 == ';'))
 return (1);

    /*
     * Names equal to end of a2, is a1 a subtype?
     */

    if (*a2 == 0 && *a1 == ';')
 return (-1);

    /*
     * Distinct attributes
     */

    return (0);
}

/* url_attrs - attributes we want from LDAP URL */

static char **url_attrs(DICT_LDAP *dict_ldap, LDAPURLDesc *url)
{
    static ARGV *attrs;
    char  **a1;
    char  **a2;
    int     arel;

    /*
     * If the LDAP URI specified no attributes, all entry attributes are
     * returned, leading to unnecessarily large LDAP results, particularly
     * since dynamic groups are most useful for large groups.
     * 
     * Since we only make use of the various mumble_results attributes, we ask
     * only for these, thus making large queries much faster.
     * 
     * In one test case, a query returning 75K users took 16 minutes when all
     * attributes are returned, and just under 3 minutes with only the
     * desired result attribute.
     */

    if (url->lud_attrs == 0 || *url->lud_attrs == 0)
 return (dict_ldap->result_attributes->argv);

    /*
     * When the LDAP URI explicitly specifies a set of attributes, we use the
     * interaction of the URI attributes and our result attributes. This way
     * LDAP URIs can hide certain attributes that should not be part of the
     * query. There is no point in retrieving attributes not listed in our
     * result set, we won't make any use of those.
     */

    if (attrs)
 argv_truncate(attrs, 0);
    else
 attrs = argv_alloc(2);

    /*
     * Retrieve only those attributes that are of interest to us.
     * 
     * If the URL attribute and the attribute we want differ only in the
     * "options" part of the attribute descriptor, select the more specific
     * attribute descriptor.
     */

    for (a1 = url->lud_attrs; *a1; ++a1) {
 for (a2 = dict_ldap->result_attributes->argv; *a2; ++a2) {
     arel = attrdesc_subtype(*a1, *a2);
     if (arel > 0)
  argv_add(attrs, *a2, ARGV_END);
     else if (arel < 0)
  argv_add(attrs, *a1, ARGV_END);
 }
    }

    return ((attrs->argc > 0) ? attrs->argv : 0);
}

/*
 * dict_ldap_get_values: for each entry returned by a search, get the values
 * of all its attributes. Recurses to resolve any DN or URL values found.
 *
 * This and the rest of the handling of multiple attributes, DNs and URLs
 * are thanks to LaMont Jones.
 */

static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage *res,
             VSTRING *result, const char *name)
{
    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   *attr;
    char  **attrs;
    struct berval **vals;
    int     valcount;
    LDAPURLDesc *url;
    const char *myname = "dict_ldap_get_values";
    int     is_leaf = 1;  /* No recursion via this entry */
    int     is_terminal = 0;  /* No expansion via this entry */

    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;

 /*
  * LDAP should not, but may produce more than the requested maximum
  * number of entries.
 */

 if (dict_ldap->dict.error == 0
     && dict_ldap->size_limit
     && ++entries > dict_ldap->size_limit) {
     msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded",
       myname, recursion, dict_ldap->parser->name,
       dict_ldap->size_limit);
     dict_ldap->dict.error = DICT_ERR_RETRY;
 }

 /*
  * Check for terminal attributes, these preclude expansion of all
  * other attributes, and DN/URI recursion. Any terminal attributes
  * are listed first in the attribute array.
 */

 if (dict_ldap->num_terminal > 0) {
     for (i = 0; i < dict_ldap->num_terminal; ++i) {
  attr = dict_ldap->result_attributes->argv[i];
  if (!(vals = ldap_get_values_len(dict_ldap->ld, entry, attr)))
      continue;
  is_terminal = (ldap_count_values_len(vals) > 0);
  ldap_value_free_len(vals);
  if (is_terminal)
      break;
     }
 }

 /*
  * Check for special attributes, these preclude expansion of
  * "leaf-only" attributes, and are at the end of the attribute array
  * after the terminal, leaf and regular attributes.
 */

 if (is_terminal == 0 && dict_ldap->num_leaf > 0) {
     for (i = dict_ldap->num_attributes;
   dict_ldap->result_attributes->argv[i]; ++i) {
  attr = dict_ldap->result_attributes->argv[i];
  if (!(vals = ldap_get_values_len(dict_ldap->ld, entry, attr)))
      continue;
  is_leaf = (ldap_count_values_len(vals) == 0);
  ldap_value_free_len(vals);
  if (!is_leaf)
      break;
     }
 }
 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_len(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;
     }
     valcount = ldap_count_values_len(vals);

     /*
      * If we previously encountered an error, we still continue
      * through the loop, to avoid memory leaks, but we don't waste
      * time accumulating any further results.
      * 
      * XXX: There may be a more efficient way to exit the loop with no
      * leaks, but it will likely be more fragile and not worth the
      * extra code.
     */

     if (dict_ldap->dict.error != 0 || valcount == 0) {
  ldap_value_free_len(vals);
  continue;
     }

     /*
      * The "result_attributes" list enumerates all the requested
      * attributes, first the ordinary result attributes and then the
      * special result attributes that hold DN or LDAP URL values.
      * 
      * The number of ordinary attributes is "num_attributes".
      * 
      * We compute the attribute type (ordinary or special) from its
      * index on the "result_attributes" list.
     */

     for (i = 0; dict_ldap->result_attributes->argv[i]; i++)
  if (attrdesc_subtype(dict_ldap->result_attributes->argv[i],
         attr) > 0)
      break;

     /*
      * Append each returned address to the result list, possibly
      * recursing (for dn or url attributes of non-terminal entries)
     */

     if (i < dict_ldap->num_attributes || is_terminal) {
  if ((is_terminal && i >= dict_ldap->num_terminal)
      || (!is_leaf &&
   i < dict_ldap->num_terminal + dict_ldap->num_leaf)) {
      if (msg_verbose)
   msg_info("%s[%d]: skipping %d value(s) of %s "
     "attribute %s", myname, recursion, valcount,
     is_terminal ? "non-terminal" : "leaf-only",
     attr);
  } else {
      /* Ordinary result attribute */
      for (i = 0; i < valcount; i++) {
   if (db_common_expand(dict_ldap->ctx,
          dict_ldap->result_format,
          vals[i]->bv_val,
          name, result, 0)
       && dict_ldap->expansion_limit > 0
       && ++expansion > dict_ldap->expansion_limit) {
       msg_warn("%s[%d]: %s: Expansion limit exceeded "
         "for key: '%s'", myname, recursion,
         dict_ldap->parser->name, name);
       dict_ldap->dict.error = DICT_ERR_RETRY;
       break;
   }
      }
      if (dict_ldap->dict.error != 0)
   continue;
      if (msg_verbose)
   msg_info("%s[%d]: search returned %d value(s) for"
     " requested result attribute %s",
     myname, recursion, valcount, attr);
  }
     } else if (recursion < dict_ldap->recursion_limit
         && dict_ldap->result_attributes->argv[i]) {
  /* Special result attribute */
  for (i = 0; i < valcount; i++) {
      if (ldap_is_ldap_url(vals[i]->bv_val)) {
   rc = ldap_url_parse(vals[i]->bv_val, &url);
   if (rc == 0) {
       if ((attrs = url_attrs(dict_ldap, url)) != 0) {
    if (msg_verbose)
        msg_info("%s[%d]: looking up URL %s",
          myname, recursion,
          vals[i]->bv_val);
    rc = search_st(dict_ldap->ld, url->lud_dn,
            url->lud_scope,
            url->lud_filter,
            attrs, dict_ldap->timeout,
            &resloop);
       }
       ldap_free_urldesc(url);
       if (attrs == 0) {
    if (msg_verbose)
        msg_info("%s[%d]: skipping URL %s: no "
          "pertinent attributes", myname,
          recursion, vals[i]->bv_val);
    continue;
       }
   } else {
       msg_warn("%s[%d]: malformed URL %s: %s(%d)",
         myname, recursion, vals[i]->bv_val,
         ldap_err2string(rc), rc);
       dict_ldap->dict.error = DICT_ERR_RETRY;
       break;
   }
      } else {
   if (msg_verbose)
       msg_info("%s[%d]: looking up DN %s",
         myname, recursion, vals[i]->bv_val);
   rc = search_st(dict_ldap->ld, vals[i]->bv_val,
           LDAP_SCOPE_BASE, "objectclass=*",
           dict_ldap->result_attributes->argv,
           dict_ldap->timeout, &resloop);
      }
      switch (rc) {
      case LDAP_SUCCESS:
   dict_ldap_get_values(dict_ldap, resloop, result, name);
   break;
      case LDAP_NO_SUCH_OBJECT:

   /*
    * Go ahead and treat this as though the DN existed
    * and just didn't have any result attributes.
 */

   msg_warn("%s[%d]: DN %s not found, skipping ", myname,
     recursion, vals[i]->bv_val);
   break;
      default:
   msg_warn("%s[%d]: search error %d: %s ", myname,
     recursion, rc, ldap_err2string(rc));
   dict_ldap->dict.error = DICT_ERR_RETRY;
   break;
      }

      if (resloop != 0)
   ldap_msgfree(resloop);

      if (dict_ldap->dict.error != 0)
   break;
  }
  if (msg_verbose && dict_ldap->dict.error == 0)
      msg_info("%s[%d]: search returned %d value(s) for"
        " special result attribute %s",
        myname, recursion, valcount, 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->parser->name, attr, vals[0]->bv_val);
  dict_ldap->dict.error = DICT_ERR_RETRY;
     }
     ldap_value_free_len(vals);
 }
 if (ber)
     ber_free(ber, 0);
    }

    if (msg_verbose)
 msg_info("%s[%d]: Leaving %s", myname, recursion, myname);
    --recursion;
}

/* dict_ldap_lookup - find database entry */

static const char *dict_ldap_lookup(DICT *dict, const char *name)
{
    const char *myname = "dict_ldap_lookup";
    DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
    LDAPMessage *res = 0;
    static VSTRING *base;
    static VSTRING *query;
    static VSTRING *result;
    int     rc = 0;
    int     sizelimit;
    int     domain_rc;

    dict_ldap->dict.error = 0;

    if (msg_verbose)
 msg_info("%s: In dict_ldap_lookup", myname);

    /*
     * Don't frustrate future attempts to make Postfix UTF-8 transparent.
     */

    if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
 && !valid_utf8_stringz(name)) {
 if (msg_verbose)
     msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
       myname, dict_ldap->parser->name, name);
 return (0);
    }

    /*
     * Optionally fold the key.
     */

    if (dict->flags & DICT_FLAG_FOLD_FIX) {
 if (dict->fold_buf == 0)
     dict->fold_buf = vstring_alloc(10);
 vstring_strcpy(dict->fold_buf, name);
 name = lowercase(vstring_str(dict->fold_buf));
    }

    /*
     * If they specified a domain list for this map, then only search for
     * addresses in domains on the list. This can significantly reduce the
     * load on the LDAP server.
     */

    if ((domain_rc = db_common_check_domain(dict_ldap->ctx, name)) == 0) {
 if (msg_verbose)
     msg_info("%s: %s: Skipping lookup of key '%s': domain mismatch",
       myname, dict_ldap->parser->name, name);
 return (0);
    }
    if (domain_rc < 0)
 DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);

#define INIT_VSTR(buf, len) do { \
 if (buf == 0) \
     buf = vstring_alloc(len); \
 VSTRING_RESET(buf); \
 VSTRING_TERMINATE(buf); \
    } while (0)

    INIT_VSTR(base, 10);
    INIT_VSTR(query, 10);
    INIT_VSTR(result, 10);

    /*
     * Because the connection may be shared and invalidated via queries for
     * another map, update private copy of "ld" from shared connection
     * container.
     */

    dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;

    /*
     * Connect to the LDAP server, if necessary.
     */

    if (dict_ldap->ld == NULL) {
 if (msg_verbose)
     msg_info
  ("%s: No existing connection for LDAP source %s, reopening",
   myname, dict_ldap->parser->name);

 dict_ldap_connect(dict_ldap);

 /*
  * if dict_ldap_connect() set dict_ldap->dict.error, abort.
 */

 if (dict_ldap->dict.error)
     return (0);
    } else if (msg_verbose)
 msg_info("%s: Using existing connection for LDAP source %s",
   myname, dict_ldap->parser->name);

    /*
     * Connection caching, means that the connection handle may have the
     * wrong size limit. Re-adjust before each query. This is cheap, just
     * sets a field in the ldap connection handle. We also do this in the
     * connect code, because we sometimes reconnect (below) in the middle of
     * a query.
     */

    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->parser->name, dict_ldap->size_limit);
 dict_ldap->dict.error = DICT_ERR_RETRY;
 return (0);
    }

    /*
     * Expand the search base and query. Skip lookup when the input key lacks
     * sufficient domain components to satisfy all the requested
     * %-substitutions.
     * 
     * When the search base is not static, LDAP_NO_SUCH_OBJECT is expected and
     * is therefore treated as a non-error: the lookup returns no results
     * rather than a soft error.
     */

    if (!db_common_expand(dict_ldap->ctx, dict_ldap->search_base,
     name, 0, base, rfc2253_quote)) {
 if (msg_verbose > 1)
     msg_info("%s: %s: Empty expansion for %s", myname,
       dict_ldap->parser->name, dict_ldap->search_base);
 return (0);
    }
    if (!db_common_expand(dict_ldap->ctx, dict_ldap->query,
     name, 0, query, rfc2254_quote)) {
 if (msg_verbose > 1)
     msg_info("%s: %s: Empty expansion for %s", myname,
       dict_ldap->parser->name, dict_ldap->query);
 return (0);
    }

    /*
     * On to the search.
     */

    if (msg_verbose)
 msg_info("%s: %s: Searching with filter %s", myname,
   dict_ldap->parser->name, vstring_str(query));

    rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope,
     vstring_str(query), dict_ldap->result_attributes->argv,
     dict_ldap->timeout, &res);

    if (rc == LDAP_SERVER_DOWN) {
 if (msg_verbose)
     msg_info("%s: Lost connection for LDAP source %s, reopening",
       myname, dict_ldap->parser->name);

 dict_ldap_unbind(dict_ldap->ld);
 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
 dict_ldap_connect(dict_ldap);

 /*
  * if dict_ldap_connect() set dict_ldap->dict.error, abort.
 */

 if (dict_ldap->dict.error)
     return (0);

 rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope,
       vstring_str(query), dict_ldap->result_attributes->argv,
         dict_ldap->timeout, &res);

    }
    switch (rc) {

    case LDAP_SUCCESS:

 /*
  * Search worked; extract the requested result_attribute.
 */


 dict_ldap_get_values(dict_ldap, res, result, name);

 /*
  * OpenLDAP's ldap_next_attribute returns a bogus
  * LDAP_DECODING_ERROR; I'm ignoring that for now.
 */


 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");
 break;

    case LDAP_NO_SUCH_OBJECT:

 /*
  * If the search base is input key dependent, then not finding it, is
  * equivalent to not finding the input key. Sadly, we cannot detect
  * misconfiguration in this case.
 */

 if (dict_ldap->dynamic_base)
     break;

 msg_warn("%s: %s: Search base '%s' not found: %d: %s",
   myname, dict_ldap->parser->name,
   vstring_str(base), rc, ldap_err2string(rc));
 dict_ldap->dict.error = DICT_ERR_RETRY;
 break;

    default:

 /*
  * Rats. The search didn't work.
 */

 msg_warn("%s: Search error %d: %s ", myname, rc,
   ldap_err2string(rc));

 /*
  * Tear down the connection so it gets set up from scratch on the
  * next lookup.
 */

 dict_ldap_unbind(dict_ldap->ld);
 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;

 /*
  * And tell the caller to try again later.
 */

 dict_ldap->dict.error = DICT_ERR_RETRY;
 break;
    }

    /*
     * Cleanup.
     */

    if (res != 0)
 ldap_msgfree(res);

    /*
     * If we had an error, return nothing, Otherwise, return the result, if
     * any.
     */

    return (VSTRING_LEN(result) > 0 && !dict_ldap->dict.error ? vstring_str(result) : 0);
}

/* dict_ldap_close - disassociate from data base */

static void dict_ldap_close(DICT *dict)
{
    const 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->parser->name);
     dict_ldap_unbind(conn->conn_ld);
 }
 binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
    }
    cfg_parser_free(dict_ldap->parser);
    myfree(dict_ldap->server_host);
    myfree(dict_ldap->search_base);
    myfree(dict_ldap->query);
    if (dict_ldap->result_format)
 myfree(dict_ldap->result_format);
    argv_free(dict_ldap->result_attributes);
    myfree(dict_ldap->bind_dn);
    myfree(dict_ldap->bind_pw);
    if (dict_ldap->ctx)
 db_common_free_ctx(dict_ldap->ctx);
#ifdef LDAP_API_FEATURE_X_OPENLDAP
#if defined(USE_LDAP_SASL)
    if (DICT_LDAP_DO_SASL(dict_ldap)) {
 myfree(dict_ldap->sasl_mechs);
 myfree(dict_ldap->sasl_realm);
 myfree(dict_ldap->sasl_authz);
    }
#endif
    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
    if (dict->fold_buf)
 vstring_free(dict->fold_buf);
    dict_free(dict);
}

/* dict_ldap_open - create association with data base */

DICT   *dict_ldap_open(const char *ldapsource, int open_flags, int dict_flags)
{
    const char *myname = "dict_ldap_open";
    DICT_LDAP *dict_ldap;
    VSTRING *url_list;
    char   *s;
    char   *h;
    char   *server_host;
    char   *scope;
    char   *attr;
    char   *bindopt;
    int     tmp;
    int     vendor_version = dict_ldap_vendor_version();
    CFG_PARSER *parser;

    if (msg_verbose)
 msg_info("%s: Using LDAP source %s", myname, ldapsource);

    /*
     * Sanity check.
     */

    if (open_flags != O_RDONLY)
 return (dict_surrogate(DICT_TYPE_LDAP, ldapsource, open_flags, dict_flags,
          "%s:%s map requires O_RDONLY access mode",
          DICT_TYPE_LDAP, ldapsource));

    /*
     * Open the configuration file.
     */

    if ((parser = cfg_parser_alloc(ldapsource)) == 0)
 return (dict_surrogate(DICT_TYPE_LDAP, ldapsource, open_flags, dict_flags,
          "open %s: %m", 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_ldap->ld = NULL;
    dict_ldap->parser = parser;

    server_host = cfg_get_str(dict_ldap->parser, "server_host",
         "localhost"10);

    /*
     * get configured value of "server_port"; default to LDAP_PORT (389)
     */

    dict_ldap->server_port =
 cfg_get_int(dict_ldap->parser, "server_port", LDAP_PORT, 00);

    /*
     * Define LDAP Protocol Version.
     */

    dict_ldap->version = cfg_get_int(dict_ldap->parser, "version"220);
    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, using 2.", 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, CHARS_COMMA_SP)) != NULL) {
#if defined(LDAP_API_FEATURE_X_OPENLDAP)

 /*
  * Convert (host, port) pairs to LDAP URLs
 */

 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);
     if (VSTRING_LEN(url_list) > 0)
  VSTRING_ADDCH(url_list, ' ');
     vstring_strcat(url_list, h);
 } else {
     if (VSTRING_LEN(url_list) > 0)
  VSTRING_ADDCH(url_list, ' ');
     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
 if (VSTRING_LEN(url_list) > 0)
     VSTRING_ADDCH(url_list, ' ');
 vstring_strcat(url_list, h);
#endif
    }
    VSTRING_TERMINATE(url_list);
    dict_ldap->server_host = vstring_export(url_list);

#if defined(LDAP_API_FEATURE_X_OPENLDAP)

    /*
     * With URL scheme, clear port to normalize connection cache key
     */

    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);

    /*
     * Scope handling thanks to Carsten Hoeger of SuSE.
     */

    scope = cfg_get_str(dict_ldap->parser, "scope""sub"10);

    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",
      ""00);

    /*
     * get configured value of "timeout"; default to 10 seconds
     * 
     * Thanks to Manuel Guesdon for spotting that this wasn't really getting
     * set.
     */

    dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout"1000);
    dict_ldap->query =
 cfg_get_str(dict_ldap->parser, "query_filter",
      "(mailacceptinggeneralid=%s)"00);
    if ((dict_ldap->result_format =
  cfg_get_str(dict_ldap->parser, "result_format"000)) == 0)
 dict_ldap->result_format =
     cfg_get_str(dict_ldap->parser, "result_filter""%s"10);

    /*
     * Must parse all templates before we can use db_common_expand() If data
     * dependent substitutions are found in the search base, treat
     * NO_SUCH_OBJECT search errors as a non-matching key, rather than a
     * fatal run-time error.
     */

    dict_ldap->ctx = 0;
    dict_ldap->dynamic_base =
 db_common_parse(&dict_ldap->dict, &dict_ldap->ctx,
   dict_ldap->search_base, 1);
    if (!db_common_parse(0, &dict_ldap->ctx, dict_ldap->query, 1)) {
 msg_warn("%s: %s: Fixed query_filter %s is probably useless",
   myname, ldapsource, dict_ldap->query);
    }
    (void) db_common_parse(0, &dict_ldap->ctx, dict_ldap->result_format, 0);
    db_common_parse_domain(dict_ldap->parser, dict_ldap->ctx);

    /*
     * Maps that use substring keys should only be used with the full input
     * key.
     */

    if (db_common_dict_partial(dict_ldap->ctx))
 dict_ldap->dict.flags |= DICT_FLAG_PATTERN;
    else
 dict_ldap->dict.flags |= DICT_FLAG_FIXED;
    if (dict_flags & DICT_FLAG_FOLD_FIX)
 dict_ldap->dict.fold_buf = vstring_alloc(10);

    /* Order matters, first the terminal attributes: */
    attr = cfg_get_str(dict_ldap->parser, "terminal_result_attribute"""00);
    dict_ldap->result_attributes = argv_split(attr, CHARS_COMMA_SP);
    dict_ldap->num_terminal = dict_ldap->result_attributes->argc;
    myfree(attr);

    /* Order matters, next the leaf-only attributes: */
    attr = cfg_get_str(dict_ldap->parser, "leaf_result_attribute"""00);
    if (*attr)
 argv_split_append(dict_ldap->result_attributes, attr, CHARS_COMMA_SP);
    dict_ldap->num_leaf =
 dict_ldap->result_attributes->argc - dict_ldap->num_terminal;
    myfree(attr);

    /* Order matters, next the regular attributes: */
    attr = cfg_get_str(dict_ldap->parser, "result_attribute""maildrop"00);
    if (*attr)
 argv_split_append(dict_ldap->result_attributes, attr, CHARS_COMMA_SP);
    dict_ldap->num_attributes = dict_ldap->result_attributes->argc;
    myfree(attr);

    /* Order matters, finally the special attributes: */
    attr = cfg_get_str(dict_ldap->parser, "special_result_attribute"""00);
    if (*attr)
 argv_split_append(dict_ldap->result_attributes, attr, CHARS_COMMA_SP);
    myfree(attr);

    /*
     * get configured value of "bind"; default to simple bind
     */

    bindopt = cfg_get_str(dict_ldap->parser, "bind", CONFIG_BOOL_YES, 10);
    dict_ldap->bind = name_code(bindopt_table, NAME_CODE_FLAG_NONE, bindopt);
    if (dict_ldap->bind < 0)
 msg_fatal("%s: unsupported parameter value: %s = %s",
    dict_ldap->parser->name, "bind", bindopt);
    myfree(bindopt);

    /*
     * get configured value of "bind_dn"; default to ""
     */

    dict_ldap->bind_dn = cfg_get_str(dict_ldap->parser, "bind_dn"""00);

    /*
     * get configured value of "bind_pw"; default to ""
     */

    dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw"""00);

    /*
     * LDAP message caching never worked and is no longer supported.
     */

    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", -100);
    if (tmp >= 0)
 msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource);

    tmp = cfg_get_int(dict_ldap->parser, "cache_size", -100);
    if (tmp >= 0)
 msg_warn("%s: %s ignoring cache_size", myname, ldapsource);

    dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser,
          "recursion_limit"100010);

    /*
     * XXX: The default should be non-zero for safety, but that is not
     * backwards compatible.
     */

    dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser,
          "expansion_limit"000);

    dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit",
     dict_ldap->expansion_limit, 00);

    /*
     * Alias dereferencing suggested by Mike Mattice.
     */

    dict_ldap->dereference = cfg_get_int(dict_ldap->parser, "dereference",
      000);
    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;
    }
    /* Referral chasing */
    dict_ldap->chase_referrals = cfg_get_bool(dict_ldap->parser,
           "chase_referrals"0);

#ifdef LDAP_API_FEATURE_X_OPENLDAP
#if defined(USE_LDAP_SASL)

    /*
     * SASL options
     */

    if (DICT_LDAP_DO_SASL(dict_ldap)) {
 dict_ldap->sasl_mechs =
     cfg_get_str(dict_ldap->parser, "sasl_mechs"""00);
 dict_ldap->sasl_realm =
     cfg_get_str(dict_ldap->parser, "sasl_realm"""00);
 dict_ldap->sasl_authz =
     cfg_get_str(dict_ldap->parser, "sasl_authz_id"""00);
 dict_ldap->sasl_minssf =
     cfg_get_int(dict_ldap->parser, "sasl_minssf"004096);
    } else {
 dict_ldap->sasl_mechs = 0;
 dict_ldap->sasl_realm = 0;
 dict_ldap->sasl_authz = 0;
    }
#endif

    /*
     * TLS options
     */

    /* get configured value of "start_tls"; default to no */
    dict_ldap->start_tls = cfg_get_bool(dict_ldap->parser, "start_tls"0);
    if (dict_ldap->start_tls) {
 if (dict_ldap->version < LDAP_VERSION3) {
     msg_warn("%s: %s start_tls requires protocol version 3",
       myname, ldapsource);
     dict_ldap->version = LDAP_VERSION3;
 }
 /* Binary incompatibility in the OpenLDAP API from 2.0.11 to 2.0.12 */
 if (((LDAP_VENDOR_VERSION <= 20011) && !(vendor_version <= 20011))
   || (!(LDAP_VENDOR_VERSION <= 20011) && (vendor_version <= 20011)))
     msg_fatal("%s: incompatible TLS support: "
        "compile-time OpenLDAP version %d, "
        "run-time OpenLDAP version %d",
        myname, LDAP_VENDOR_VERSION, vendor_version);
    }
    /* get configured value of "tls_require_cert"; default to no */
    dict_ldap->tls_require_cert =
 cfg_get_bool(dict_ldap->parser, "tls_require_cert"0) ?
 LDAP_OPT_X_TLS_DEMAND : LDAP_OPT_X_TLS_NEVER;

    /* get configured value of "tls_ca_cert_file"; default "" */
    dict_ldap->tls_ca_cert_file = cfg_get_str(dict_ldap->parser,
           "tls_ca_cert_file"""00);

    /* get configured value of "tls_ca_cert_dir"; default "" */
    dict_ldap->tls_ca_cert_dir = cfg_get_str(dict_ldap->parser,
          "tls_ca_cert_dir"""00);

    /* get configured value of "tls_cert"; default "" */
    dict_ldap->tls_cert = cfg_get_str(dict_ldap->parser, "tls_cert",
          ""00);

    /* get configured value of "tls_key"; default "" */
    dict_ldap->tls_key = cfg_get_str(dict_ldap->parser, "tls_key",
         ""00);

    /* get configured value of "tls_random_file"; default "" */
    dict_ldap->tls_random_file = cfg_get_str(dict_ldap->parser,
          "tls_random_file"""00);

    /* get configured value of "tls_cipher_suite"; default "" */
    dict_ldap->tls_cipher_suite = cfg_get_str(dict_ldap->parser,
           "tls_cipher_suite"""00);
#endif

    /*
     * Debug level.
     */

#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
    dict_ldap->debuglevel = cfg_get_int(dict_ldap->parser, "debuglevel",
     000);
#endif

    /*
     * Find or allocate shared LDAP connection container.
     */

    dict_ldap_conn_find(dict_ldap);

    /*
     * Return the new dict_ldap structure.
     */

    dict_ldap->dict.owner = cfg_get_owner(dict_ldap->parser);
    return (&dict_ldap->dict);
}

#endif

Messung V0.5 in Prozent
C=81 H=96 G=88

[Verzeichnis aufwärts0.52unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-08-08]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002