Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  dict_ldap.c

  Sprache: C
 

/*++
/* 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

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002