You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Graham Leggett via dev <de...@httpd.apache.org> on 2023/04/18 12:47:29 UTC

POC: updated ldapi:// support for mod_ldap

Hi all,

This is a patch for httpd that adds support for ldapi:// URLs to mod_ldap and friends.

It depends on a patch for apr-util posted to the dev@apr list.

Regards,
Graham
—


Index: include/util_ldap.h
===================================================================
--- include/util_ldap.h	(revision 1909117)
+++ include/util_ldap.h	(working copy)
@@ -45,6 +45,10 @@
 /* this whole thing disappears if LDAP is not enabled */
 #if APR_HAS_LDAP
 
+#ifndef APR_HAS_LDAP_INITIALIZE
+#define APR_HAS_LDAP_INITIALIZE 0
+#endif
+
 #if defined(LDAP_UNAVAILABLE) || APR_HAS_MICROSOFT_LDAPSDK
 #define AP_LDAP_IS_SERVER_DOWN(s)                ((s) == LDAP_SERVER_DOWN \
                 ||(s) == LDAP_UNAVAILABLE)
@@ -126,8 +130,8 @@
     const char *reason;                 /* Reason for an error failure */
 
     struct util_ldap_connection_t *next;
-    struct util_ldap_state_t *st;        /* The LDAP vhost config this connection belongs to */
-    int keep;                            /* Will this connection be kept when it's unlocked */
+    struct util_ldap_state_t *st;       /* The LDAP vhost config this connection belongs to */
+    int keep;                           /* Will this connection be kept when it's unlocked */
 
     int ChaseReferrals;                 /* [on|off] (default = AP_LDAP_CHASEREFERRALS_ON)*/
     int ReferralHopLimit;               /* # of referral hops to follow (default = AP_LDAP_DEFAULT_HOPLIMIT) */
@@ -136,6 +140,12 @@
     int must_rebind;                    /* The connection was last bound with other then binddn/bindpw */
     request_rec *r;                     /* request_rec used to find this util_ldap_connection_t */
     apr_time_t last_backend_conn;       /* the approximate time of the last backend LDAP request */
+
+#if APR_HAS_LDAP_INITIALIZE
+    apr_ldap_err_t result;              /* result of prior operations on this connection */
+    const char *url;                    /* URL of the LDAP server (or space separated list) */
+    apr_ldap_t *ld;
+#endif
 } util_ldap_connection_t;
 
 typedef struct util_ldap_config_t {
@@ -241,6 +251,7 @@
  * @fn util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
  *                                                           const char *binddn, const char *bindpw, deref_options deref,
  *                                                           int netscapessl, int starttls)
+ * @deprecated Replaced by uldap_connection_find_ex()
  */
 APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find,(request_rec *r, const char *host, int port,
                                                   const char *binddn, const char *bindpw, deref_options deref,
@@ -247,6 +258,26 @@
                                                   int secure));
 
 /**
+ * Find a connection in a list of connections
+ * @param r The request record
+ * @param url The URL to connect to (multiple URLs space separated)
+ * @param binddn The DN to bind with
+ * @param bindpw The password to bind with
+ * @param deref The dereferencing behavior
+ * @param secure use SSL on the connection
+ * @tip Once a connection is found and returned, a lock will be acquired to
+ *      lock that particular connection, so that another thread does not try and
+ *      use this connection while it is busy. Once you are finished with a connection,
+ *      apr_ldap_connection_close() must be called to release this connection.
+ * @fn util_ldap_connection_t *util_ldap_connection_find_ex(request_rec *r, const char *url,
+ *                                                           const char *binddn, const char *bindpw, deref_options deref,
+ *                                                           int netscapessl, int starttls)
+ */
+APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find_ex,(request_rec *r, const char *url,
+                                                  const char *binddn, const char *bindpw, deref_options deref,
+                                                  int secure));
+
+/**
  * Compare two DNs for sameness
  * @param r The request record
  * @param ldc The LDAP connection being used.
Index: modules/aaa/mod_authnz_ldap.c
===================================================================
--- modules/aaa/mod_authnz_ldap.c	(revision 1909117)
+++ modules/aaa/mod_authnz_ldap.c	(working copy)
@@ -104,7 +104,7 @@
 module AP_MODULE_DECLARE_DATA authnz_ldap_module;
 
 static APR_OPTIONAL_FN_TYPE(uldap_connection_close) *util_ldap_connection_close;
-static APR_OPTIONAL_FN_TYPE(uldap_connection_find) *util_ldap_connection_find;
+static APR_OPTIONAL_FN_TYPE(uldap_connection_find_ex) *util_ldap_connection_find_ex;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_comparedn) *util_ldap_cache_comparedn;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_compare) *util_ldap_cache_compare;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_check_subgroups) *util_ldap_cache_check_subgroups;
@@ -453,9 +453,7 @@
             bindpw = req->password;
     }
 
-    return util_ldap_connection_find(r, sec->host, sec->port,
-                                     binddn, bindpw,
-                                     sec->deref, sec->secure);
+    return util_ldap_connection_find_ex(r, sec->url, binddn, bindpw, sec->deref, sec->secure);
 }
 /*
  * Authentication Phase
@@ -527,7 +525,7 @@
     }
 
     /* There is a good AuthLDAPURL, right? */
-    if (sec->host) {
+    if (sec->url) {
         const char *binddn = sec->binddn;
         const char *bindpw = sec->bindpw;
         if (sec->initial_bind_as_user) {
@@ -535,13 +533,13 @@
             binddn = ldap_determine_binddn(r, user);
         }
 
-        ldc = util_ldap_connection_find(r, sec->host, sec->port,
-                                       binddn, bindpw,
-                                       sec->deref, sec->secure);
+        ldc = util_ldap_connection_find_ex(r, sec->url,
+                                           binddn, bindpw,
+                                           sec->deref, sec->secure);
     }
     else {
         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01690)
-                      "auth_ldap authenticate: no sec->host - weird...?");
+                      "auth_ldap authenticate: no sec->url - weird...?");
         return AUTH_GENERAL_ERROR;
     }
 
@@ -1436,13 +1434,14 @@
  * host and port.
  */
 static const char *mod_auth_ldap_parse_url(cmd_parms *cmd,
-                                    void *config,
-                                    const char *url,
-                                    const char *mode)
+                                           void *config,
+                                           const char *url,
+                                           const char *mode)
 {
-    int rc;
+    int rc, i;
     apr_ldap_url_desc_t *urld;
     apr_ldap_err_t *result;
+    const char *end = url;
 
     authn_ldap_config_t *sec = config;
 
@@ -1450,8 +1449,18 @@
     if (rc != APR_SUCCESS) {
         return result->reason;
     }
-    sec->url = apr_pstrdup(cmd->pool, url);
 
+    /* isolate the host/port part of the URL */
+    for (i = 0; end && i < 3; i++) {
+        end = strchr((char *)end + 1, '/');
+    }
+    if (end) {
+        sec->url = apr_pstrndup(cmd->pool, url, end - url);
+    }
+    else {
+        sec->url = apr_pstrdup(cmd->pool, url);
+    }
+
     /* Set all the values, or at least some sane defaults */
     if (sec->host) {
         sec->host = apr_pstrcat(cmd->pool, urld->lud_host, " ", sec->host, NULL);
@@ -1528,9 +1537,10 @@
     sec->have_ldap_url = 1;
 
     ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, cmd->server,
-                 "auth_ldap url parse: `%s', Host: %s, Port: %d, DN: %s, "
+                 "auth_ldap url parse: `%s', Url: %s, Host: %s, Port: %d, DN: %s, "
                  "attrib: %s, scope: %s, filter: %s, connection mode: %s",
                  url,
+                 sec->url,
                  urld->lud_host,
                  urld->lud_port,
                  urld->lud_dn,
@@ -1921,13 +1931,13 @@
 
 static void ImportULDAPOptFn(void)
 {
-    util_ldap_connection_close  = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
-    util_ldap_connection_find   = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_find);
-    util_ldap_cache_comparedn   = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_comparedn);
-    util_ldap_cache_compare     = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_compare);
-    util_ldap_cache_checkuserid = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid);
-    util_ldap_cache_getuserdn   = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn);
-    util_ldap_ssl_supported     = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported);
+    util_ldap_connection_close      = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
+    util_ldap_connection_find_ex    = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_find_ex);
+    util_ldap_cache_comparedn       = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_comparedn);
+    util_ldap_cache_compare         = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_compare);
+    util_ldap_cache_checkuserid     = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid);
+    util_ldap_cache_getuserdn       = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn);
+    util_ldap_ssl_supported         = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported);
     util_ldap_cache_check_subgroups = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_check_subgroups);
 }
 
Index: modules/ldap/util_ldap.c
===================================================================
--- modules/ldap/util_ldap.c	(revision 1909117)
+++ modules/ldap/util_ldap.c	(working copy)
@@ -285,6 +285,25 @@
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
     int have_client_certs = !apr_is_empty_array(ldc->client_certs);
+
+#if APR_HAS_LDAP_INITIALIZE
+
+    apr_ldap_initialize(ldc->pool, ldc->url, &(ldc->ld), &(ldc->result));
+
+    if (ldc->ld) {
+        apr_ldap_opt_t opt;
+
+        apr_ldap_get_option_ex(ldc->ld, APR_LDAP_OPT_HANDLE, &opt, &(ldc->result));
+
+        ldc->ldap = opt.handle;
+    }
+
+    result = &ldc->result;
+
+#else
+
+    /* remove after apr-util v1.7 */
+
 #if !APR_HAS_SOLARIS_LDAPSDK
     /*
      * Normally we enable SSL/TLS with apr_ldap_set_option(), except
@@ -322,7 +341,9 @@
         return(APR_EGENERAL);
     }
 
-    if (result->rc) {
+#endif
+
+    if (result && result->rc) {
         ldc->reason = result->reason;
         ldc->bound = 0;
         return result->rc;
@@ -697,19 +718,12 @@
 }
 
 
-/*
- * Find an existing ldap connection struct that matches the
- * provided ldap connection parameters.
- *
- * If not found in the cache, a new ldc structure will be allocated
- * from st->pool and returned to the caller.  If found in the cache,
- * a pointer to the existing ldc structure will be returned.
- */
 static util_ldap_connection_t *
-            uldap_connection_find(request_rec *r,
-                                  const char *host, int port,
-                                  const char *binddn, const char *bindpw,
-                                  deref_options deref, int secure)
+            connection_find(request_rec *r,
+                            const char *url,
+                            const char *host, int port,
+                            const char *binddn, const char *bindpw,
+                            deref_options deref, int secure)
 {
     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
     int secureflag = secure;
@@ -737,12 +751,16 @@
 #if APR_HAS_THREADS
         if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
 #endif
-        if (   (l->port == port) && (strcmp(l->host, host) == 0)
-            && ((!l->binddn && !binddn) || (l->binddn && binddn
-                                             && !strcmp(l->binddn, binddn)))
-            && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
-                                             && !strcmp(l->bindpw, bindpw)))
-            && (l->deref == deref) && (l->secure == secureflag)
+        if (   (l->port == port)
+            && ((!url && !l->url) || (url && l->url
+                                             && !strcmp(url, l->url)))
+            && ((!host && !l->host) || (host && l->host
+                                             && !strcmp(l->host, host)))
+            && ((!binddn && !l->binddn) || (binddn && l->binddn
+                                             && !strcmp(binddn, l->binddn)))
+            && ((!bindpw && !l->bindpw) || (bindpw && l->bindpw
+                                             && !strcmp(bindpw, l->bindpw)))
+            && (deref == l->deref) && (secureflag == l->secure)
             && !compare_client_certs(dc->client_certs, l->client_certs))
         {
             if (st->connection_pool_ttl > 0) {
@@ -779,9 +797,13 @@
             if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
 
 #endif
-            if ((l->port == port) && (strcmp(l->host, host) == 0) &&
-                (l->deref == deref) && (l->secure == secureflag) &&
-                !compare_client_certs(dc->client_certs, l->client_certs))
+            if ((port == l->port)
+                && ((!url && !l->url) || (url && l->url
+                                             && !strcmp(url, l->url)))
+                && ((!host && !l->host) || (host && l->host
+                                             && !strcmp(host, l->host)))
+                && (deref == l->deref) && (secureflag == l->secure)
+                && !compare_client_certs(dc->client_certs, l->client_certs))
             {
                 if (st->connection_pool_ttl > 0) {
                     if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
@@ -849,6 +871,7 @@
         apr_thread_mutex_lock(l->lock);
 #endif
         l->bound = 0;
+        l->url = apr_pstrdup(l->pool, url);
         l->host = apr_pstrdup(l->pool, host);
         l->port = port;
         l->deref = deref;
@@ -897,6 +920,42 @@
     return l;
 }
 
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated
+ * from st->pool and returned to the caller.  If found in the cache,
+ * a pointer to the existing ldc structure will be returned.
+ *
+ * Deprecated: replaced by uldap_connection_find_ex()
+ */
+static util_ldap_connection_t *
+            uldap_connection_find(request_rec *r,
+                                  const char *host, int port,
+                                  const char *binddn, const char *bindpw,
+                                  deref_options deref, int secure)
+{
+    return connection_find(r, NULL, host, port, binddn, bindpw, deref, secure);
+}
+
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated
+ * from st->pool and returned to the caller.  If found in the cache,
+ * a pointer to the existing ldc structure will be returned.
+ */
+static util_ldap_connection_t *
+            uldap_connection_find_ex(request_rec *r,
+                                     const char *url,
+                                     const char *binddn, const char *bindpw,
+                                     deref_options deref, int secure)
+{
+    return connection_find(r, url, NULL, 0, binddn, bindpw, deref, secure);
+}
+
 /* ------------------------------------------------------------------ */
 
 /*
@@ -3216,6 +3275,7 @@
     APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
     APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
     APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
+    APR_REGISTER_OPTIONAL_FN(uldap_connection_find_ex);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);



Re: POC: updated ldapi:// support for mod_ldap

Posted by Graham Leggett via dev <de...@httpd.apache.org>.
On 22 Apr 2023, at 20:17, Graham Leggett via dev <de...@httpd.apache.org> wrote:

> This patch adds limited support for SASL binds. Depends on updated POC patch at the dev@apr list.

Updated patch that handles authname and pass for SASL.

    <If "%{SSL_CLIENT_VERIFY} == 'SUCCESS' || %{SSL_CLIENT_VERIFY} == 'GENEROUS'">

      SSLUserName SSL_CLIENT_CERT_RFC4523_CEA

      LDAPBind mechanism EXTERNAL
      AuthLDAPURL ldapi://%2frun%2fslapd-seawitch.socket/dc=xxx,dc=xx,dc=xx?uid,inetSubscriberAccountId?sub?inetSubscriberAccountId=*
      require ldap-group cn=https://xxx.xxx.xx.xx/xxx,ou=xxx,ou=xxx,dc=xxx,dc=xx,dc=xx

    </If>

Regards,
Graham
—



Index: include/util_ldap.h
===================================================================
--- include/util_ldap.h	(revision 1909117)
+++ include/util_ldap.h	(working copy)
@@ -45,6 +45,10 @@
 /* this whole thing disappears if LDAP is not enabled */
 #if APR_HAS_LDAP
 
+#ifndef APR_HAS_LDAP_INITIALIZE
+#define APR_HAS_LDAP_INITIALIZE 0
+#endif
+
 #if defined(LDAP_UNAVAILABLE) || APR_HAS_MICROSOFT_LDAPSDK
 #define AP_LDAP_IS_SERVER_DOWN(s)                ((s) == LDAP_SERVER_DOWN \
                 ||(s) == LDAP_UNAVAILABLE)
@@ -126,8 +130,8 @@
     const char *reason;                 /* Reason for an error failure */
 
     struct util_ldap_connection_t *next;
-    struct util_ldap_state_t *st;        /* The LDAP vhost config this connection belongs to */
-    int keep;                            /* Will this connection be kept when it's unlocked */
+    struct util_ldap_state_t *st;       /* The LDAP vhost config this connection belongs to */
+    int keep;                           /* Will this connection be kept when it's unlocked */
 
     int ChaseReferrals;                 /* [on|off] (default = AP_LDAP_CHASEREFERRALS_ON)*/
     int ReferralHopLimit;               /* # of referral hops to follow (default = AP_LDAP_DEFAULT_HOPLIMIT) */
@@ -136,12 +140,39 @@
     int must_rebind;                    /* The connection was last bound with other then binddn/bindpw */
     request_rec *r;                     /* request_rec used to find this util_ldap_connection_t */
     apr_time_t last_backend_conn;       /* the approximate time of the last backend LDAP request */
+
+#if APR_HAS_LDAP_INITIALIZE
+    apr_pool_t *init_pool;              /* Pool from which this connection is initialised */
+    apu_err_t result;                   /* result of prior operations on this connection */
+    const char *url;                    /* URL of the LDAP server (or space separated list) */
+    apr_ldap_t *ld;
+    const char *mech;                   /* SASL bind mechanism */
+    const char *realm;                  /* SASL realm for the authentication attempt */
+    const char *user;                   /* SASL username to use for proxy authorization */
+    const char *authname;               /* SASL username to authenticate */
+    const char *pass;                   /* SASL password for the provided username */
+#endif
 } util_ldap_connection_t;
 
 typedef struct util_ldap_config_t {
     int ChaseReferrals;
     int ReferralHopLimit;
-    apr_array_header_t *client_certs;  /* Client certificates */
+    apr_array_header_t *client_certs;   /* Client certificates */
+    const char *mech;                   /* SASL mechanism */
+    const char *realm;                  /* SASL realm for the authentication attempt */
+    const char *user;                   /* SASL username to use for proxy authorization */
+    const char *authname;               /* SASL username to authenticate */
+    const char *pass;                   /* SASL password for the provided username */
+    unsigned int inherit:1;
+    unsigned int inherit_set:1;
+    unsigned int client_certs_set:1;
+    unsigned int ReferralHopLimit_set:1;
+    unsigned int ChaseReferrals_set:1;
+    unsigned int mech_set:1;
+    unsigned int realm_set:1;
+    unsigned int user_set:1;
+    unsigned int authname_set:1;
+    unsigned int pass_set:1;
 } util_ldap_config_t;
 
 /* LDAP cache state information */
@@ -241,6 +272,7 @@
  * @fn util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
  *                                                           const char *binddn, const char *bindpw, deref_options deref,
  *                                                           int netscapessl, int starttls)
+ * @deprecated Replaced by uldap_connection_find_ex()
  */
 APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find,(request_rec *r, const char *host, int port,
                                                   const char *binddn, const char *bindpw, deref_options deref,
@@ -247,6 +279,26 @@
                                                   int secure));
 
 /**
+ * Find a connection in a list of connections
+ * @param r The request record
+ * @param url The URL to connect to (multiple URLs space separated)
+ * @param binddn The DN to bind with
+ * @param bindpw The password to bind with
+ * @param deref The dereferencing behavior
+ * @param secure use SSL on the connection
+ * @tip Once a connection is found and returned, a lock will be acquired to
+ *      lock that particular connection, so that another thread does not try and
+ *      use this connection while it is busy. Once you are finished with a connection,
+ *      apr_ldap_connection_close() must be called to release this connection.
+ * @fn util_ldap_connection_t *util_ldap_connection_find_ex(request_rec *r, const char *url,
+ *                                                           const char *binddn, const char *bindpw, deref_options deref,
+ *                                                           int netscapessl, int starttls)
+ */
+APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find_ex,(request_rec *r, const char *url,
+                                                  const char *binddn, const char *bindpw, deref_options deref,
+                                                  int secure));
+
+/**
  * Compare two DNs for sameness
  * @param r The request record
  * @param ldc The LDAP connection being used.
Index: modules/aaa/mod_authnz_ldap.c
===================================================================
--- modules/aaa/mod_authnz_ldap.c	(revision 1909117)
+++ modules/aaa/mod_authnz_ldap.c	(working copy)
@@ -104,7 +104,7 @@
 module AP_MODULE_DECLARE_DATA authnz_ldap_module;
 
 static APR_OPTIONAL_FN_TYPE(uldap_connection_close) *util_ldap_connection_close;
-static APR_OPTIONAL_FN_TYPE(uldap_connection_find) *util_ldap_connection_find;
+static APR_OPTIONAL_FN_TYPE(uldap_connection_find_ex) *util_ldap_connection_find_ex;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_comparedn) *util_ldap_cache_comparedn;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_compare) *util_ldap_cache_compare;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_check_subgroups) *util_ldap_cache_check_subgroups;
@@ -453,9 +453,7 @@
             bindpw = req->password;
     }
 
-    return util_ldap_connection_find(r, sec->host, sec->port,
-                                     binddn, bindpw,
-                                     sec->deref, sec->secure);
+    return util_ldap_connection_find_ex(r, sec->url, binddn, bindpw, sec->deref, sec->secure);
 }
 /*
  * Authentication Phase
@@ -527,7 +525,7 @@
     }
 
     /* There is a good AuthLDAPURL, right? */
-    if (sec->host) {
+    if (sec->url) {
         const char *binddn = sec->binddn;
         const char *bindpw = sec->bindpw;
         if (sec->initial_bind_as_user) {
@@ -535,13 +533,13 @@
             binddn = ldap_determine_binddn(r, user);
         }
 
-        ldc = util_ldap_connection_find(r, sec->host, sec->port,
-                                       binddn, bindpw,
-                                       sec->deref, sec->secure);
+        ldc = util_ldap_connection_find_ex(r, sec->url,
+                                           binddn, bindpw,
+                                           sec->deref, sec->secure);
     }
     else {
         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01690)
-                      "auth_ldap authenticate: no sec->host - weird...?");
+                      "auth_ldap authenticate: no sec->url - weird...?");
         return AUTH_GENERAL_ERROR;
     }
 
@@ -1436,13 +1434,14 @@
  * host and port.
  */
 static const char *mod_auth_ldap_parse_url(cmd_parms *cmd,
-                                    void *config,
-                                    const char *url,
-                                    const char *mode)
+                                           void *config,
+                                           const char *url,
+                                           const char *mode)
 {
-    int rc;
+    int rc, i;
     apr_ldap_url_desc_t *urld;
     apr_ldap_err_t *result;
+    const char *end = url;
 
     authn_ldap_config_t *sec = config;
 
@@ -1450,8 +1449,18 @@
     if (rc != APR_SUCCESS) {
         return result->reason;
     }
-    sec->url = apr_pstrdup(cmd->pool, url);
 
+    /* isolate the host/port part of the URL */
+    for (i = 0; end && i < 3; i++) {
+        end = strchr((char *)end + 1, '/');
+    }
+    if (end) {
+        sec->url = apr_pstrndup(cmd->pool, url, end - url);
+    }
+    else {
+        sec->url = apr_pstrdup(cmd->pool, url);
+    }
+
     /* Set all the values, or at least some sane defaults */
     if (sec->host) {
         sec->host = apr_pstrcat(cmd->pool, urld->lud_host, " ", sec->host, NULL);
@@ -1528,9 +1537,10 @@
     sec->have_ldap_url = 1;
 
     ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, cmd->server,
-                 "auth_ldap url parse: `%s', Host: %s, Port: %d, DN: %s, "
+                 "auth_ldap url parse: `%s', Url: %s, Host: %s, Port: %d, DN: %s, "
                  "attrib: %s, scope: %s, filter: %s, connection mode: %s",
                  url,
+                 sec->url,
                  urld->lud_host,
                  urld->lud_port,
                  urld->lud_dn,
@@ -1921,13 +1931,13 @@
 
 static void ImportULDAPOptFn(void)
 {
-    util_ldap_connection_close  = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
-    util_ldap_connection_find   = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_find);
-    util_ldap_cache_comparedn   = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_comparedn);
-    util_ldap_cache_compare     = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_compare);
-    util_ldap_cache_checkuserid = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid);
-    util_ldap_cache_getuserdn   = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn);
-    util_ldap_ssl_supported     = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported);
+    util_ldap_connection_close      = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
+    util_ldap_connection_find_ex    = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_find_ex);
+    util_ldap_cache_comparedn       = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_comparedn);
+    util_ldap_cache_compare         = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_compare);
+    util_ldap_cache_checkuserid     = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid);
+    util_ldap_cache_getuserdn       = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn);
+    util_ldap_ssl_supported         = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported);
     util_ldap_cache_check_subgroups = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_check_subgroups);
 }
 
Index: modules/ldap/util_ldap.c
===================================================================
--- modules/ldap/util_ldap.c	(revision 1909117)
+++ modules/ldap/util_ldap.c	(working copy)
@@ -199,9 +199,19 @@
             if (ldc->r) { 
                 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp unbind", ldc); 
             }
+#if !APR_HAS_LDAP_INITIALIZE
             ldap_unbind_s(ldc->ldap);
+#endif
             ldc->ldap = NULL;
         }
+
+#if APR_HAS_LDAP_INITIALIZE
+        if (ldc->ld) {
+            apr_pool_clear(ldc->init_pool);
+            ldc->ld = NULL;
+        }
+#endif
+
         ldc->bound = 0;
 
         /* forget the rebind info for this conn */
@@ -285,6 +295,27 @@
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
     int have_client_certs = !apr_is_empty_array(ldc->client_certs);
+
+#if APR_HAS_LDAP_INITIALIZE
+
+    apr_ldap_initialize(ldc->init_pool, ldc->url, &(ldc->ld), &(ldc->result));
+
+    if (ldc->ld) {
+        apr_ldap_opt_t opt;
+
+        apr_ldap_get_option_ex(ldc->ld, APR_LDAP_OPT_HANDLE, &opt, &(ldc->result));
+
+        ldc->ldap = opt.handle;
+    }
+
+    result = (apr_ldap_err_t *)&ldc->result;
+
+#else
+
+    /* remove after apr-util v1.7 */
+
+    apr_ldap_err_t *result = NULL;
+
 #if !APR_HAS_SOLARIS_LDAPSDK
     /*
      * Normally we enable SSL/TLS with apr_ldap_set_option(), except
@@ -322,7 +353,9 @@
         return(APR_EGENERAL);
     }
 
-    if (result->rc) {
+#endif
+
+    if (result && result->rc) {
         ldc->reason = result->reason;
         ldc->bound = 0;
         return result->rc;
@@ -508,6 +541,7 @@
     return LDAP_OTHER;
 }
 
+
 /*
  * Replacement function for ldap_simple_bind_s() with a timeout.
  * To do this in a portable way, we have to use ldap_simple_bind() and
@@ -546,6 +580,58 @@
     return rc;
 }
 
+
+#if APR_HAS_LDAP_INITIALIZE
+static apr_status_t bind_interact(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{
+    util_ldap_connection_t *ldc = ctx;
+
+    switch (interact->id) {
+    case APR_LDAP_INTERACT_GETREALM:
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ldc->r, APLOGNO()
+                      "LDAP bind realm %s: %s", ldc->realm ? "set to" : "left unset",
+                      ldc->realm ? ldc->realm : "");
+        interact->result = ldc->realm;
+        interact->len = interact->result ? strlen(interact->result) : 0;
+        break;
+    case APR_LDAP_INTERACT_USER:
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ldc->r, APLOGNO()
+                      "LDAP bind user %s: %s", ldc->user ? "set to" : "left unset",
+                      ldc->user ? ldc->user : "");
+        interact->result = ldc->user;
+        interact->len = interact->result ? strlen(interact->result) : 0;
+        break;
+    case APR_LDAP_INTERACT_AUTHNAME:
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ldc->r, APLOGNO()
+                      "LDAP bind authname %s: %s", ldc->authname ? "set to" : "left unset",
+                      ldc->authname ? ldc->authname : "");
+        interact->result = ldc->authname;
+        interact->len = interact->result ? strlen(interact->result) : 0;
+        break;
+    case APR_LDAP_INTERACT_PASS:
+        if (ldc->binddn && ldc->bindpw) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ldc->r, APLOGNO()
+                          "LDAP simple bind pass %s", ldc->bindpw ? "set" : "left unset");
+            interact->result = ldc->bindpw;
+        }
+        else {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ldc->r, APLOGNO()
+                          "LDAP bind pass %s", ldc->pass ? "set" : "left unset");
+            interact->result = ldc->pass;
+        }
+        interact->len = interact->result ? strlen(interact->result) : 0;
+        break;
+    default:
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ldc->r, APLOGNO()
+                      "LDAP bind id %d with prompt '%s' unrecognised", interact->id, interact->prompt);
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+#endif
+
+
 /*
  * Connect to the LDAP server and binds. Does not connect if already
  * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
@@ -559,6 +645,10 @@
     int failures = 0;
     int new_connection = 0;
     util_ldap_state_t *st;
+#if APR_HAS_LDAP_INITIALIZE
+    apr_status_t status = APR_SUCCESS;
+    apu_err_t result = { 0 };
+#endif
 
     /* sanity check for NULL */
     if (!ldc) {
@@ -601,10 +691,130 @@
      * the error condition.
      */
 
+#if APR_HAS_LDAP_INITIALIZE
+
     while (failures <= st->retries) {
         if (failures > 0 && st->retry_delay > 0) {
             apr_sleep(st->retry_delay);
         }
+
+        apr_ldap_message_t *msg = NULL;
+
+        do {
+
+            /* If a distinguished name is supplied, we've been asked for a simple bind.
+             * If the distinguished name is unset, we perform a sasl bind.
+             */
+
+            status = apr_ldap_bind(ldc->ld, ldc->binddn, ldc->mech, bind_interact, ldc,
+                                   apr_time_from_sec(st->opTimeout ?
+                                   st->opTimeout->tv_sec : -1), &msg, &result);
+
+            if (APR_SUCCESS == status) {
+                break;
+            }
+            else if (APR_INCOMPLETE == status) {
+                /* keep going */
+            }
+            else {
+                break;
+            }
+
+            status = apr_ldap_result(msg, apr_time_from_sec(st->opTimeout ?
+                                   st->opTimeout->tv_sec : -1), &result);
+
+            if (APR_SUCCESS == status) {
+                /* no more messages */
+                break;
+            }
+            else if (APR_INCOMPLETE == status) {
+                /* keep going */
+            }
+            else {
+                break;
+            }
+
+        } while (APR_INCOMPLETE == status);
+
+        if (APR_SUCCESS != status) {
+            failures++;
+        }
+
+        if (APR_STATUS_IS_EDOWN(status)) {
+             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+                          "LDAP bind failed with server down "
+                          "(try %d)", failures);
+        }
+        else if (APR_STATUS_IS_ETIMEDOUT(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01284)
+                          "LDAP bind timed out on %s "
+                          "connection, dropped by firewall?",
+                          new_connection ? "new" : "reused");
+        }
+        else if (APR_STATUS_IS_AUTH_UNKNOWN(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, %s not supported for this user (auth unknown)",
+                          new_connection ? "new" : "reused", ldc->mech);
+            break;
+        }
+        else if (APR_STATUS_IS_PROXY_AUTH(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, proxy auth failed for this user",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else if (APR_STATUS_IS_INAPPROPRIATE_AUTH(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, type of authentication not valid for this user (inappropriate auth)",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else if (APR_STATUS_IS_INVALID_CREDENTIALS(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, invalid credentials for this user (wrong password?)",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else if (APR_STATUS_IS_INSUFFICIENT_ACCESS(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()    
+                          "LDAP bind failed on %s "           
+                          "connection, user does not have permission to bind (unsufficient access)",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else {
+            /* Other errors not retryable */
+            break;
+        }
+
+    }
+
+    /* free the handle if there was an error
+    */
+    if (APR_SUCCESS != status)
+    {
+        uldap_connection_unbind(ldc);
+        ldc->reason = "LDAP: bind failed";
+    }
+    else {
+        ldc->bound = 1;
+        ldc->must_rebind = 0;
+        ldc->reason = "LDAP: connection open successful";
+    }
+
+    return(result.rc);
+
+#else
+
+    while (failures <= st->retries) {
+        if (failures > 0 && st->retry_delay > 0) {
+            apr_sleep(st->retry_delay);
+        }
+
         rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
                                st->opTimeout);
 
@@ -637,6 +847,7 @@
                 break;
             }
         }
+
     }
 
     /* free the handle if there was an error
@@ -653,6 +864,7 @@
     }
 
     return(rc);
+#endif
 }
 
 
@@ -697,19 +909,12 @@
 }
 
 
-/*
- * Find an existing ldap connection struct that matches the
- * provided ldap connection parameters.
- *
- * If not found in the cache, a new ldc structure will be allocated
- * from st->pool and returned to the caller.  If found in the cache,
- * a pointer to the existing ldc structure will be returned.
- */
 static util_ldap_connection_t *
-            uldap_connection_find(request_rec *r,
-                                  const char *host, int port,
-                                  const char *binddn, const char *bindpw,
-                                  deref_options deref, int secure)
+            connection_find(request_rec *r,
+                            const char *url,
+                            const char *host, int port,
+                            const char *binddn, const char *bindpw,
+                            deref_options deref, int secure)
 {
     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
     int secureflag = secure;
@@ -737,13 +942,27 @@
 #if APR_HAS_THREADS
         if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
 #endif
-        if (   (l->port == port) && (strcmp(l->host, host) == 0)
-            && ((!l->binddn && !binddn) || (l->binddn && binddn
-                                             && !strcmp(l->binddn, binddn)))
-            && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
-                                             && !strcmp(l->bindpw, bindpw)))
-            && (l->deref == deref) && (l->secure == secureflag)
-            && !compare_client_certs(dc->client_certs, l->client_certs))
+        if (   (l->port == port)
+            && ((!url && !l->url) || (url && l->url
+                                             && !strcmp(url, l->url)))
+            && ((!host && !l->host) || (host && l->host
+                                             && !strcmp(l->host, host)))
+            && ((!binddn && !l->binddn) || (binddn && l->binddn
+                                             && !strcmp(binddn, l->binddn)))
+            && ((!bindpw && !l->bindpw) || (bindpw && l->bindpw
+                                             && !strcmp(bindpw, l->bindpw)))
+            && (deref == l->deref) && (secureflag == l->secure)
+            && !compare_client_certs(dc->client_certs, l->client_certs)
+            && ((!dc->mech && !l->mech) || (dc->mech && l->mech
+                                             && !strcmp(dc->mech, l->mech)))
+            && ((!dc->realm && !l->realm) || (dc->realm && l->realm
+                                             && !strcmp(dc->realm, l->realm)))
+            && ((!dc->user && !l->user) || (dc->user && l->user
+                                             && !strcmp(dc->user, l->user))) 
+            && ((!dc->authname && !l->authname) || (dc->authname && l->authname
+                                             && !strcmp(dc->authname, l->authname)))
+            && ((!dc->pass && !l->pass) || (dc->pass && l->pass
+                                             && !strcmp(dc->pass, l->pass))) )
         {
             if (st->connection_pool_ttl > 0) {
                 if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
@@ -779,9 +998,23 @@
             if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
 
 #endif
-            if ((l->port == port) && (strcmp(l->host, host) == 0) &&
-                (l->deref == deref) && (l->secure == secureflag) &&
-                !compare_client_certs(dc->client_certs, l->client_certs))
+            if ((port == l->port)
+                && ((!url && !l->url) || (url && l->url
+                                             && !strcmp(url, l->url)))
+                && ((!host && !l->host) || (host && l->host
+                                             && !strcmp(host, l->host)))
+                && (deref == l->deref) && (secureflag == l->secure)
+                && !compare_client_certs(dc->client_certs, l->client_certs)
+                && ((!dc->mech && !l->mech) || (dc->mech && l->mech
+                                             && !strcmp(dc->mech, l->mech)))
+                && ((!dc->realm && !l->realm) || (dc->realm && l->realm
+                                             && !strcmp(dc->realm, l->realm)))
+                && ((!dc->user && !l->user) || (dc->user && l->user
+                                             && !strcmp(dc->user, l->user)))
+                && ((!dc->authname && !l->authname) || (dc->authname && l->authname
+                                             && !strcmp(dc->authname, l->authname)))
+                && ((!dc->pass && !l->pass) || (dc->pass && l->pass
+                                             && !strcmp(dc->pass, l->pass))) )
             {
                 if (st->connection_pool_ttl > 0) {
                     if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
@@ -849,6 +1082,7 @@
         apr_thread_mutex_lock(l->lock);
 #endif
         l->bound = 0;
+        l->url = apr_pstrdup(l->pool, url);
         l->host = apr_pstrdup(l->pool, host);
         l->port = port;
         l->deref = deref;
@@ -882,6 +1116,24 @@
             apr_pool_tag(l->rebind_pool, "util_ldap_rebind");
         }
 
+#if APR_HAS_LDAP_INITIALIZE
+        if (apr_pool_create(&(l->init_pool), l->pool) != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01286)
+                          "util_ldap: Failed to create memory pool");
+#if APR_HAS_THREADS
+            apr_thread_mutex_unlock(st->mutex);
+#endif
+            return NULL;
+        }
+        apr_pool_tag(l->init_pool, "util_ldap_init");
+
+        l->mech = dc->mech;
+        l->realm = dc->realm;
+        l->user = dc->user;
+        l->authname = dc->authname;
+        l->pass = dc->pass;
+#endif
+
         if (p) {
             p->next = l;
         }
@@ -897,6 +1149,42 @@
     return l;
 }
 
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated
+ * from st->pool and returned to the caller.  If found in the cache,
+ * a pointer to the existing ldc structure will be returned.
+ *
+ * Deprecated: replaced by uldap_connection_find_ex()
+ */
+static util_ldap_connection_t *
+            uldap_connection_find(request_rec *r,
+                                  const char *host, int port,
+                                  const char *binddn, const char *bindpw,
+                                  deref_options deref, int secure)
+{
+    return connection_find(r, NULL, host, port, binddn, bindpw, deref, secure);
+}
+
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated
+ * from st->pool and returned to the caller.  If found in the cache,
+ * a pointer to the existing ldc structure will be returned.
+ */
+static util_ldap_connection_t *
+            uldap_connection_find_ex(request_rec *r,
+                                     const char *url,
+                                     const char *binddn, const char *bindpw,
+                                     deref_options deref, int secure)
+{
+    return connection_find(r, url, NULL, 0, binddn, bindpw, deref, secure);
+}
+
 /* ------------------------------------------------------------------ */
 
 /*
@@ -2525,6 +2813,8 @@
 
     }
 
+    dc->client_certs_set = 1;
+
     return(NULL);
 }
 
@@ -2643,6 +2933,8 @@
         return "LDAPReferrals must be 'on', 'off', or 'default'";
     }
 
+    dc->ChaseReferrals_set = 1;
+
     return(NULL);
 }
 
@@ -2684,9 +2976,61 @@
                  "LDAP: Limit chased referrals to maximum of %d hops.",
                  dc->ReferralHopLimit);
 
+    dc->ReferralHopLimit_set = 1;
+
     return NULL;
 }
 
+static const char *util_ldap_set_bind(cmd_parms *cmd,
+                                      void *config,
+                                      const char *prompt, const char *value)
+{
+    util_ldap_config_t *dc = config;
+    char c;
+
+    if (!prompt || !value) {
+        return "LDAPBind takes two parameters";
+    }
+
+    c = prompt[0];
+
+    if (c == 'm' && !strcmp(prompt, "mechanism")) {
+        dc->mech = value;
+        dc->mech_set = 1;
+    }
+    else if (c == 'r' && !strcmp(prompt, "realm")) {
+        dc->realm = value;
+        dc->realm_set = 1;
+    }
+    else if (c == 'a' && !strcmp(prompt, "authname")) {
+        dc->authname = value;
+        dc->authname_set = 1;
+    }
+    else if (c == 'u' && !strcmp(prompt, "user")) {
+        dc->user = value;
+        dc->user_set = 1;
+    }
+    else if (c == 'p' && !strcmp(prompt, "pass")) {
+        dc->pass = value;
+        dc->pass_set = 1;
+    }
+    else {
+        return "LDAPBind parameter must be one of 'mechanism', 'realm', 'authname', 'user', 'pass'";
+    }
+
+    return NULL;
+}
+
+static const char *util_ldap_set_inherit(cmd_parms *parms, void *config, int flag)
+{
+    util_ldap_config_t *dc = config;
+
+    dc->inherit = flag;
+    dc->inherit_set = 1;
+
+    return NULL;
+}
+
 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d)
 {
     util_ldap_config_t *dc =
@@ -2697,9 +3041,65 @@
     dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
     dc->ReferralHopLimit = AP_LDAP_HOPLIMIT_UNSET;
 
+    dc->inherit = 0;
+    dc->inherit_set = 0;
+
     return dc;
 }
 
+static void *util_ldap_merge_dir_config(apr_pool_t *p, void *basev,
+                                        void *overridesv)
+{
+    util_ldap_config_t *dc = apr_pcalloc(p, sizeof(util_ldap_config_t));
+    util_ldap_config_t *base = (util_ldap_config_t *) basev;
+    util_ldap_config_t *overrides = (util_ldap_config_t *) overridesv;
+
+    dc->inherit = (overrides->inherit_set == 0) ? base->inherit : overrides->inherit;
+    dc->inherit_set = overrides->inherit_set || base->inherit_set;
+
+    if (dc->inherit) {
+
+        dc->client_certs = (overrides->client_certs_set == 0) ? base->client_certs :
+                            overrides->client_certs;
+        dc->client_certs_set = overrides->client_certs_set || base->client_certs_set;
+
+        dc->ChaseReferrals = (overrides->ChaseReferrals_set == 0) ? base->ChaseReferrals :
+                            overrides->ChaseReferrals;
+        dc->ChaseReferrals_set = overrides->ChaseReferrals_set || base->ChaseReferrals_set;
+
+        dc->ReferralHopLimit = (overrides->ReferralHopLimit_set == 0) ? base->ReferralHopLimit :
+                            overrides->ReferralHopLimit;
+        dc->ReferralHopLimit_set = overrides->ReferralHopLimit_set || base->ReferralHopLimit_set;
+
+        dc->mech = (overrides->mech_set == 0) ? base->mech : overrides->mech;
+        dc->mech_set = overrides->mech_set || base->mech_set;
+
+        dc->realm = (overrides->realm_set == 0) ? base->realm : overrides->realm;
+        dc->realm_set = overrides->realm_set || base->realm_set;
+
+        dc->user = (overrides->user_set == 0) ? base->user : overrides->user;
+        dc->user_set = overrides->user_set || base->user_set;
+
+        dc->authname = (overrides->authname_set == 0) ? base->authname : overrides->authname;
+        dc->authname_set = overrides->authname_set || base->authname_set;
+
+        dc->pass = (overrides->pass_set == 0) ? base->pass : overrides->pass;
+        dc->pass_set = overrides->pass_set || base->pass_set;
+    }
+    else {
+        dc->client_certs = overrides->client_certs;
+        dc->ChaseReferrals = overrides->ChaseReferrals;
+        dc->ReferralHopLimit = overrides->ReferralHopLimit;
+        dc->mech = overrides->mech;
+        dc->realm = overrides->realm;
+        dc->user = overrides->user;
+        dc->authname = overrides->authname;
+        dc->pass = overrides->pass;
+    }
+
+    return dc;
+}
+
 static const char *util_ldap_set_op_timeout(cmd_parms *cmd,
                                             void *dummy,
                                             const char *val)
@@ -3184,6 +3584,10 @@
                   "Limit the number of referral hops that LDAP can follow. "
                   "(Integer value, Consult LDAP SDK documentation for applicability and defaults"),
 
+    AP_INIT_TAKE2("LDAPBind", util_ldap_set_bind,
+                  NULL, OR_AUTHCFG,
+                  "Set the SASL bind parameters. One of 'mechanism', 'user', 'authname', 'pass'"),
+
     AP_INIT_TAKE1("LDAPLibraryDebug", util_ldap_set_debug_level,
                   NULL, RSRC_CONF,
                   "Enable debugging in LDAP SDK (Default: off, values: SDK specific"),
@@ -3206,6 +3610,8 @@
                   "Specify the delay between retries of a failed LDAP operation "
                   "(0 = no delay). Default: 0"),
 
+    AP_INIT_FLAG("LDAPInherit", util_ldap_set_inherit, NULL, OR_AUTHCFG,
+                 "on if this server should inherit all LDAP directives defined in the main server"),
 
     {NULL}
 };
@@ -3216,6 +3622,7 @@
     APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
     APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
     APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
+    APR_REGISTER_OPTIONAL_FN(uldap_connection_find_ex);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
@@ -3232,7 +3639,7 @@
 AP_DECLARE_MODULE(ldap) = {
    STANDARD20_MODULE_STUFF,
    util_ldap_create_dir_config, /* create dir config */
-   NULL,                        /* merge dir config */
+   util_ldap_merge_dir_config,  /* merge dir config */
    util_ldap_create_config,     /* create server config */
    util_ldap_merge_config,      /* merge server config */
    util_ldap_cmds,              /* command table */



Re: POC: updated ldapi:// support for mod_ldap

Posted by Graham Leggett via dev <de...@httpd.apache.org>.
On 18 Apr 2023, at 13:47, Graham Leggett <mi...@sharp.fm> wrote:

> This is a patch for httpd that adds support for ldapi:// URLs to mod_ldap and friends.
> 
> It depends on a patch for apr-util posted to the dev@apr list.

This patch adds limited support for SASL binds. Depends on updated POC patch at the dev@apr list.

Right now only EXTERNAL is supported, as the supporting structure for passing other SASL parameters isn't yet there. Further work to follow.

You can now do this:

    <If "%{SSL_CLIENT_VERIFY} == 'SUCCESS' || %{SSL_CLIENT_VERIFY} == 'GENEROUS'">

      SSLUserName SSL_CLIENT_CERT_RFC4523_CEA

      LDAPBindMechanism EXTERNAL
      AuthLDAPURL ldapi://%2frun%2fslapd-seawitch.socket/dc=xxx,dc=xx,dc=xx?uid,inetSubscriberAccountId?sub?inetSubscriberAccountId=*
      require ldap-group cn=https://xxx.xxx.xx.xx/xxx,ou=xxx,ou=xxx,dc=xxx,dc=xx,dc=xx

    </If>

Regards,
Graham
—



Index: include/util_ldap.h
===================================================================
--- include/util_ldap.h	(revision 1909117)
+++ include/util_ldap.h	(working copy)
@@ -45,6 +45,10 @@
 /* this whole thing disappears if LDAP is not enabled */
 #if APR_HAS_LDAP
 
+#ifndef APR_HAS_LDAP_INITIALIZE
+#define APR_HAS_LDAP_INITIALIZE 0
+#endif
+
 #if defined(LDAP_UNAVAILABLE) || APR_HAS_MICROSOFT_LDAPSDK
 #define AP_LDAP_IS_SERVER_DOWN(s)                ((s) == LDAP_SERVER_DOWN \
                 ||(s) == LDAP_UNAVAILABLE)
@@ -126,8 +130,8 @@
     const char *reason;                 /* Reason for an error failure */
 
     struct util_ldap_connection_t *next;
-    struct util_ldap_state_t *st;        /* The LDAP vhost config this connection belongs to */
-    int keep;                            /* Will this connection be kept when it's unlocked */
+    struct util_ldap_state_t *st;       /* The LDAP vhost config this connection belongs to */
+    int keep;                           /* Will this connection be kept when it's unlocked */
 
     int ChaseReferrals;                 /* [on|off] (default = AP_LDAP_CHASEREFERRALS_ON)*/
     int ReferralHopLimit;               /* # of referral hops to follow (default = AP_LDAP_DEFAULT_HOPLIMIT) */
@@ -136,6 +140,14 @@
     int must_rebind;                    /* The connection was last bound with other then binddn/bindpw */
     request_rec *r;                     /* request_rec used to find this util_ldap_connection_t */
     apr_time_t last_backend_conn;       /* the approximate time of the last backend LDAP request */
+
+#if APR_HAS_LDAP_INITIALIZE
+    apr_pool_t *init_pool;              /* Pool from which this connection is initialised */
+    apu_err_t result;                   /* result of prior operations on this connection */
+    const char *url;                    /* URL of the LDAP server (or space separated list) */
+    apr_ldap_t *ld;
+    const char *mech;                   /* SASL bind mechanism */
+#endif
 } util_ldap_connection_t;
 
 typedef struct util_ldap_config_t {
@@ -142,6 +154,13 @@
     int ChaseReferrals;
     int ReferralHopLimit;
     apr_array_header_t *client_certs;  /* Client certificates */
+    const char *mech;                  /* SASL mechanism */
+    unsigned int inherit:1;
+    unsigned int inherit_set:1;
+    unsigned int mech_set:1;
+    unsigned int client_certs_set:1;
+    unsigned int ReferralHopLimit_set:1;
+    unsigned int ChaseReferrals_set:1;
 } util_ldap_config_t;
 
 /* LDAP cache state information */
@@ -241,6 +260,7 @@
  * @fn util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
  *                                                           const char *binddn, const char *bindpw, deref_options deref,
  *                                                           int netscapessl, int starttls)
+ * @deprecated Replaced by uldap_connection_find_ex()
  */
 APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find,(request_rec *r, const char *host, int port,
                                                   const char *binddn, const char *bindpw, deref_options deref,
@@ -247,6 +267,26 @@
                                                   int secure));
 
 /**
+ * Find a connection in a list of connections
+ * @param r The request record
+ * @param url The URL to connect to (multiple URLs space separated)
+ * @param binddn The DN to bind with
+ * @param bindpw The password to bind with
+ * @param deref The dereferencing behavior
+ * @param secure use SSL on the connection
+ * @tip Once a connection is found and returned, a lock will be acquired to
+ *      lock that particular connection, so that another thread does not try and
+ *      use this connection while it is busy. Once you are finished with a connection,
+ *      apr_ldap_connection_close() must be called to release this connection.
+ * @fn util_ldap_connection_t *util_ldap_connection_find_ex(request_rec *r, const char *url,
+ *                                                           const char *binddn, const char *bindpw, deref_options deref,
+ *                                                           int netscapessl, int starttls)
+ */
+APR_DECLARE_OPTIONAL_FN(util_ldap_connection_t *,uldap_connection_find_ex,(request_rec *r, const char *url,
+                                                  const char *binddn, const char *bindpw, deref_options deref,
+                                                  int secure));
+
+/**
  * Compare two DNs for sameness
  * @param r The request record
  * @param ldc The LDAP connection being used.
Index: modules/aaa/mod_authnz_ldap.c
===================================================================
--- modules/aaa/mod_authnz_ldap.c	(revision 1909117)
+++ modules/aaa/mod_authnz_ldap.c	(working copy)
@@ -104,7 +104,7 @@
 module AP_MODULE_DECLARE_DATA authnz_ldap_module;
 
 static APR_OPTIONAL_FN_TYPE(uldap_connection_close) *util_ldap_connection_close;
-static APR_OPTIONAL_FN_TYPE(uldap_connection_find) *util_ldap_connection_find;
+static APR_OPTIONAL_FN_TYPE(uldap_connection_find_ex) *util_ldap_connection_find_ex;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_comparedn) *util_ldap_cache_comparedn;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_compare) *util_ldap_cache_compare;
 static APR_OPTIONAL_FN_TYPE(uldap_cache_check_subgroups) *util_ldap_cache_check_subgroups;
@@ -453,9 +453,7 @@
             bindpw = req->password;
     }
 
-    return util_ldap_connection_find(r, sec->host, sec->port,
-                                     binddn, bindpw,
-                                     sec->deref, sec->secure);
+    return util_ldap_connection_find_ex(r, sec->url, binddn, bindpw, sec->deref, sec->secure);
 }
 /*
  * Authentication Phase
@@ -527,7 +525,7 @@
     }
 
     /* There is a good AuthLDAPURL, right? */
-    if (sec->host) {
+    if (sec->url) {
         const char *binddn = sec->binddn;
         const char *bindpw = sec->bindpw;
         if (sec->initial_bind_as_user) {
@@ -535,13 +533,13 @@
             binddn = ldap_determine_binddn(r, user);
         }
 
-        ldc = util_ldap_connection_find(r, sec->host, sec->port,
-                                       binddn, bindpw,
-                                       sec->deref, sec->secure);
+        ldc = util_ldap_connection_find_ex(r, sec->url,
+                                           binddn, bindpw,
+                                           sec->deref, sec->secure);
     }
     else {
         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01690)
-                      "auth_ldap authenticate: no sec->host - weird...?");
+                      "auth_ldap authenticate: no sec->url - weird...?");
         return AUTH_GENERAL_ERROR;
     }
 
@@ -1436,13 +1434,14 @@
  * host and port.
  */
 static const char *mod_auth_ldap_parse_url(cmd_parms *cmd,
-                                    void *config,
-                                    const char *url,
-                                    const char *mode)
+                                           void *config,
+                                           const char *url,
+                                           const char *mode)
 {
-    int rc;
+    int rc, i;
     apr_ldap_url_desc_t *urld;
     apr_ldap_err_t *result;
+    const char *end = url;
 
     authn_ldap_config_t *sec = config;
 
@@ -1450,8 +1449,18 @@
     if (rc != APR_SUCCESS) {
         return result->reason;
     }
-    sec->url = apr_pstrdup(cmd->pool, url);
 
+    /* isolate the host/port part of the URL */
+    for (i = 0; end && i < 3; i++) {
+        end = strchr((char *)end + 1, '/');
+    }
+    if (end) {
+        sec->url = apr_pstrndup(cmd->pool, url, end - url);
+    }
+    else {
+        sec->url = apr_pstrdup(cmd->pool, url);
+    }
+
     /* Set all the values, or at least some sane defaults */
     if (sec->host) {
         sec->host = apr_pstrcat(cmd->pool, urld->lud_host, " ", sec->host, NULL);
@@ -1528,9 +1537,10 @@
     sec->have_ldap_url = 1;
 
     ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, cmd->server,
-                 "auth_ldap url parse: `%s', Host: %s, Port: %d, DN: %s, "
+                 "auth_ldap url parse: `%s', Url: %s, Host: %s, Port: %d, DN: %s, "
                  "attrib: %s, scope: %s, filter: %s, connection mode: %s",
                  url,
+                 sec->url,
                  urld->lud_host,
                  urld->lud_port,
                  urld->lud_dn,
@@ -1921,13 +1931,13 @@
 
 static void ImportULDAPOptFn(void)
 {
-    util_ldap_connection_close  = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
-    util_ldap_connection_find   = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_find);
-    util_ldap_cache_comparedn   = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_comparedn);
-    util_ldap_cache_compare     = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_compare);
-    util_ldap_cache_checkuserid = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid);
-    util_ldap_cache_getuserdn   = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn);
-    util_ldap_ssl_supported     = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported);
+    util_ldap_connection_close      = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
+    util_ldap_connection_find_ex    = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_find_ex);
+    util_ldap_cache_comparedn       = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_comparedn);
+    util_ldap_cache_compare         = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_compare);
+    util_ldap_cache_checkuserid     = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid);
+    util_ldap_cache_getuserdn       = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn);
+    util_ldap_ssl_supported         = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported);
     util_ldap_cache_check_subgroups = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_check_subgroups);
 }
 
Index: modules/ldap/util_ldap.c
===================================================================
--- modules/ldap/util_ldap.c	(revision 1909117)
+++ modules/ldap/util_ldap.c	(working copy)
@@ -199,9 +199,19 @@
             if (ldc->r) { 
                 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp unbind", ldc); 
             }
+#if !APR_HAS_LDAP_INITIALIZE
             ldap_unbind_s(ldc->ldap);
+#endif
             ldc->ldap = NULL;
         }
+
+#if APR_HAS_LDAP_INITIALIZE
+        if (ldc->ld) {
+            apr_pool_clear(ldc->init_pool);
+            ldc->ld = NULL;
+        }
+#endif
+
         ldc->bound = 0;
 
         /* forget the rebind info for this conn */
@@ -285,6 +295,27 @@
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
     int have_client_certs = !apr_is_empty_array(ldc->client_certs);
+
+#if APR_HAS_LDAP_INITIALIZE
+
+    apr_ldap_initialize(ldc->init_pool, ldc->url, &(ldc->ld), &(ldc->result));
+
+    if (ldc->ld) {
+        apr_ldap_opt_t opt;
+
+        apr_ldap_get_option_ex(ldc->ld, APR_LDAP_OPT_HANDLE, &opt, &(ldc->result));
+
+        ldc->ldap = opt.handle;
+    }
+
+    result = (apr_ldap_err_t *)&ldc->result;
+
+#else
+
+    /* remove after apr-util v1.7 */
+
+    apr_ldap_err_t *result = NULL;
+
 #if !APR_HAS_SOLARIS_LDAPSDK
     /*
      * Normally we enable SSL/TLS with apr_ldap_set_option(), except
@@ -322,7 +353,9 @@
         return(APR_EGENERAL);
     }
 
-    if (result->rc) {
+#endif
+
+    if (result && result->rc) {
         ldc->reason = result->reason;
         ldc->bound = 0;
         return result->rc;
@@ -508,6 +541,7 @@
     return LDAP_OTHER;
 }
 
+
 /*
  * Replacement function for ldap_simple_bind_s() with a timeout.
  * To do this in a portable way, we have to use ldap_simple_bind() and
@@ -546,6 +580,15 @@
     return rc;
 }
 
+
+#if APR_HAS_LDAP_INITIALIZE
+static apr_status_t bind_interact(apr_ldap_t *ld, unsigned int flags, apr_ldap_bind_interact_t *interact, void *ctx)
+{
+    return APR_SUCCESS;
+}
+#endif
+
+
 /*
  * Connect to the LDAP server and binds. Does not connect if already
  * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
@@ -559,6 +602,10 @@
     int failures = 0;
     int new_connection = 0;
     util_ldap_state_t *st;
+#if APR_HAS_LDAP_INITIALIZE
+    apr_status_t status = APR_SUCCESS;
+    apu_err_t result = { 0 };
+#endif
 
     /* sanity check for NULL */
     if (!ldc) {
@@ -601,10 +648,126 @@
      * the error condition.
      */
 
+#if APR_HAS_LDAP_INITIALIZE
+
     while (failures <= st->retries) {
         if (failures > 0 && st->retry_delay > 0) {
             apr_sleep(st->retry_delay);
         }
+
+        apr_ldap_message_t *msg = NULL;
+
+        do {
+// FIXME: pass pwd
+            status = apr_ldap_bind(ldc->ld, NULL, ldc->mech, bind_interact, NULL,
+                                   apr_time_from_sec(st->opTimeout ?
+                                   st->opTimeout->tv_sec : -1), &msg, &result);
+
+            if (APR_SUCCESS == status) {
+                break;
+            }
+            else if (APR_INCOMPLETE == status) {
+                /* keep going */
+            }
+            else {
+                break;
+            }
+
+            status = apr_ldap_result(msg, apr_time_from_sec(st->opTimeout ?
+                                   st->opTimeout->tv_sec : -1), &result);
+
+            if (APR_SUCCESS == status) {
+                /* no more messages */
+                break;
+            }
+            else if (APR_INCOMPLETE == status) {
+                /* keep going */
+            }
+            else {
+                break;
+            }
+
+        } while (APR_INCOMPLETE == status);
+
+        if (APR_SUCCESS != status) {
+            failures++;
+        }
+
+        if (APR_STATUS_IS_EDOWN(status)) {
+             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+                          "LDAP bind failed with server down "
+                          "(try %d)", failures);
+        }
+        else if (APR_STATUS_IS_ETIMEDOUT(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01284)
+                          "LDAP bind timed out on %s "
+                          "connection, dropped by firewall?",
+                          new_connection ? "new" : "reused");
+        }
+        else if (APR_STATUS_IS_AUTH_UNKNOWN(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, %s not supported for this user (auth unknown)",
+                          new_connection ? "new" : "reused", ldc->mech);
+            break;
+        }
+        else if (APR_STATUS_IS_PROXY_AUTH(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, proxy auth failed for this user",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else if (APR_STATUS_IS_INAPPROPRIATE_AUTH(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, type of authentication not valid for this user (inappropriate auth)",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else if (APR_STATUS_IS_INVALID_CREDENTIALS(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+                          "LDAP bind failed on %s "
+                          "connection, invalid credentials for this user (wrong password?)",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else if (APR_STATUS_IS_INSUFFICIENT_ACCESS(status)) {
+            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()    
+                          "LDAP bind failed on %s "           
+                          "connection, user does not have permission to bind (unsufficient access)",
+                          new_connection ? "new" : "reused"); 
+            break;
+        }
+        else {
+            /* Other errors not retryable */
+            break;
+        }
+
+    }
+
+    /* free the handle if there was an error
+    */
+    if (APR_SUCCESS != status)
+    {
+        uldap_connection_unbind(ldc);
+        ldc->reason = "LDAP: bind failed";
+    }
+    else {
+        ldc->bound = 1;
+        ldc->must_rebind = 0;
+        ldc->reason = "LDAP: connection open successful";
+    }
+
+    return(result.rc);
+
+#else
+
+    while (failures <= st->retries) {
+        if (failures > 0 && st->retry_delay > 0) {
+            apr_sleep(st->retry_delay);
+        }
+
         rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
                                st->opTimeout);
 
@@ -637,6 +800,7 @@
                 break;
             }
         }
+
     }
 
     /* free the handle if there was an error
@@ -653,6 +817,7 @@
     }
 
     return(rc);
+#endif
 }
 
 
@@ -697,19 +862,12 @@
 }
 
 
-/*
- * Find an existing ldap connection struct that matches the
- * provided ldap connection parameters.
- *
- * If not found in the cache, a new ldc structure will be allocated
- * from st->pool and returned to the caller.  If found in the cache,
- * a pointer to the existing ldc structure will be returned.
- */
 static util_ldap_connection_t *
-            uldap_connection_find(request_rec *r,
-                                  const char *host, int port,
-                                  const char *binddn, const char *bindpw,
-                                  deref_options deref, int secure)
+            connection_find(request_rec *r,
+                            const char *url,
+                            const char *host, int port,
+                            const char *binddn, const char *bindpw,
+                            deref_options deref, int secure)
 {
     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
     int secureflag = secure;
@@ -737,13 +895,19 @@
 #if APR_HAS_THREADS
         if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
 #endif
-        if (   (l->port == port) && (strcmp(l->host, host) == 0)
-            && ((!l->binddn && !binddn) || (l->binddn && binddn
-                                             && !strcmp(l->binddn, binddn)))
-            && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
-                                             && !strcmp(l->bindpw, bindpw)))
-            && (l->deref == deref) && (l->secure == secureflag)
-            && !compare_client_certs(dc->client_certs, l->client_certs))
+        if (   (l->port == port)
+            && ((!url && !l->url) || (url && l->url
+                                             && !strcmp(url, l->url)))
+            && ((!host && !l->host) || (host && l->host
+                                             && !strcmp(l->host, host)))
+            && ((!binddn && !l->binddn) || (binddn && l->binddn
+                                             && !strcmp(binddn, l->binddn)))
+            && ((!bindpw && !l->bindpw) || (bindpw && l->bindpw
+                                             && !strcmp(bindpw, l->bindpw)))
+            && (deref == l->deref) && (secureflag == l->secure)
+            && !compare_client_certs(dc->client_certs, l->client_certs)
+            && ((!dc->mech && !l->mech) || (dc->mech && l->mech
+                                             && !strcmp(dc->mech, l->mech))) )
         {
             if (st->connection_pool_ttl > 0) {
                 if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
@@ -779,9 +943,15 @@
             if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
 
 #endif
-            if ((l->port == port) && (strcmp(l->host, host) == 0) &&
-                (l->deref == deref) && (l->secure == secureflag) &&
-                !compare_client_certs(dc->client_certs, l->client_certs))
+            if ((port == l->port)
+                && ((!url && !l->url) || (url && l->url
+                                             && !strcmp(url, l->url)))
+                && ((!host && !l->host) || (host && l->host
+                                             && !strcmp(host, l->host)))
+                && (deref == l->deref) && (secureflag == l->secure)
+                && !compare_client_certs(dc->client_certs, l->client_certs)
+                && ((!dc->mech && !l->mech) || (dc->mech && l->mech
+                                             && !strcmp(dc->mech, l->mech))) )
             {
                 if (st->connection_pool_ttl > 0) {
                     if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
@@ -849,6 +1019,7 @@
         apr_thread_mutex_lock(l->lock);
 #endif
         l->bound = 0;
+        l->url = apr_pstrdup(l->pool, url);
         l->host = apr_pstrdup(l->pool, host);
         l->port = port;
         l->deref = deref;
@@ -882,6 +1053,20 @@
             apr_pool_tag(l->rebind_pool, "util_ldap_rebind");
         }
 
+#if APR_HAS_LDAP_INITIALIZE
+        if (apr_pool_create(&(l->init_pool), l->pool) != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01286)
+                          "util_ldap: Failed to create memory pool");
+#if APR_HAS_THREADS
+            apr_thread_mutex_unlock(st->mutex);
+#endif
+            return NULL;
+        }
+        apr_pool_tag(l->init_pool, "util_ldap_init");
+
+        l->mech = dc->mech;
+#endif
+
         if (p) {
             p->next = l;
         }
@@ -897,6 +1082,42 @@
     return l;
 }
 
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated
+ * from st->pool and returned to the caller.  If found in the cache,
+ * a pointer to the existing ldc structure will be returned.
+ *
+ * Deprecated: replaced by uldap_connection_find_ex()
+ */
+static util_ldap_connection_t *
+            uldap_connection_find(request_rec *r,
+                                  const char *host, int port,
+                                  const char *binddn, const char *bindpw,
+                                  deref_options deref, int secure)
+{
+    return connection_find(r, NULL, host, port, binddn, bindpw, deref, secure);
+}
+
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated
+ * from st->pool and returned to the caller.  If found in the cache,
+ * a pointer to the existing ldc structure will be returned.
+ */
+static util_ldap_connection_t *
+            uldap_connection_find_ex(request_rec *r,
+                                     const char *url,
+                                     const char *binddn, const char *bindpw,
+                                     deref_options deref, int secure)
+{
+    return connection_find(r, url, NULL, 0, binddn, bindpw, deref, secure);
+}
+
 /* ------------------------------------------------------------------ */
 
 /*
@@ -2525,6 +2746,8 @@
 
     }
 
+    dc->client_certs_set = 1;
+
     return(NULL);
 }
 
@@ -2643,6 +2866,8 @@
         return "LDAPReferrals must be 'on', 'off', or 'default'";
     }
 
+    dc->ChaseReferrals_set = 1;
+
     return(NULL);
 }
 
@@ -2684,9 +2909,33 @@
                  "LDAP: Limit chased referrals to maximum of %d hops.",
                  dc->ReferralHopLimit);
 
+    dc->ReferralHopLimit_set = 1;
+
     return NULL;
 }
 
+static const char *util_ldap_set_bind_mechanism(cmd_parms *cmd,
+                                                void *config,
+                                                const char *mech)
+{
+    util_ldap_config_t *dc = config;
+
+    dc->mech = mech;
+    dc->mech_set = 1;
+
+    return NULL;
+}
+
+static const char *util_ldap_set_inherit(cmd_parms *parms, void *config, int flag)
+{
+    util_ldap_config_t *dc = config;
+
+    dc->inherit = flag;
+    dc->inherit_set = 1;
+
+    return NULL;
+}
+
 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d)
 {
     util_ldap_config_t *dc =
@@ -2697,9 +2946,51 @@
     dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
     dc->ReferralHopLimit = AP_LDAP_HOPLIMIT_UNSET;
 
+    dc->inherit = 0;
+    dc->inherit_set = 0;
+
     return dc;
 }
 
+static void *util_ldap_merge_dir_config(apr_pool_t *p, void *basev,
+                                        void *overridesv)
+{
+    util_ldap_config_t *dc = apr_pcalloc(p, sizeof(util_ldap_config_t));
+    util_ldap_config_t *base = (util_ldap_config_t *) basev;
+    util_ldap_config_t *overrides = (util_ldap_config_t *) overridesv;
+
+    dc->inherit = (overrides->inherit_set == 0) ? base->inherit : overrides->inherit;
+    dc->inherit_set = overrides->inherit_set || base->inherit_set;
+
+    if (dc->inherit) {
+
+        dc->client_certs = (overrides->client_certs_set == 0) ? base->client_certs :
+                            overrides->client_certs;
+        dc->client_certs_set = overrides->client_certs_set || base->client_certs_set;
+
+        dc->ChaseReferrals = (overrides->ChaseReferrals_set == 0) ? base->ChaseReferrals :
+                            overrides->ChaseReferrals;
+        dc->ChaseReferrals_set = overrides->ChaseReferrals_set || base->ChaseReferrals_set;
+
+        dc->ReferralHopLimit = (overrides->ReferralHopLimit_set == 0) ? base->ReferralHopLimit :
+                            overrides->ReferralHopLimit;
+        dc->ReferralHopLimit_set = overrides->ReferralHopLimit_set || base->ReferralHopLimit_set;
+
+        dc->mech = (overrides->mech_set == 0) ? base->mech :
+                            overrides->mech;
+        dc->mech_set = overrides->mech_set || base->mech_set;
+
+    }
+    else {
+        dc->client_certs = overrides->client_certs;
+        dc->ChaseReferrals = overrides->ChaseReferrals;
+        dc->ReferralHopLimit = overrides->ReferralHopLimit;
+        dc->mech = overrides->mech;
+    }
+
+    return dc;
+}
+
 static const char *util_ldap_set_op_timeout(cmd_parms *cmd,
                                             void *dummy,
                                             const char *val)
@@ -3184,6 +3475,10 @@
                   "Limit the number of referral hops that LDAP can follow. "
                   "(Integer value, Consult LDAP SDK documentation for applicability and defaults"),
 
+    AP_INIT_TAKE1("LDAPBindMechanism", util_ldap_set_bind_mechanism,
+                  NULL, OR_AUTHCFG,
+                  "Set the SASL bind mechanism."),
+
     AP_INIT_TAKE1("LDAPLibraryDebug", util_ldap_set_debug_level,
                   NULL, RSRC_CONF,
                   "Enable debugging in LDAP SDK (Default: off, values: SDK specific"),
@@ -3206,6 +3501,8 @@
                   "Specify the delay between retries of a failed LDAP operation "
                   "(0 = no delay). Default: 0"),
 
+    AP_INIT_FLAG("LDAPInherit", util_ldap_set_inherit, NULL, OR_AUTHCFG,
+                 "on if this server should inherit all LDAP directives defined in the main server"),
 
     {NULL}
 };
@@ -3216,6 +3513,7 @@
     APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
     APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
     APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
+    APR_REGISTER_OPTIONAL_FN(uldap_connection_find_ex);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
     APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
@@ -3232,7 +3530,7 @@
 AP_DECLARE_MODULE(ldap) = {
    STANDARD20_MODULE_STUFF,
    util_ldap_create_dir_config, /* create dir config */
-   NULL,                        /* merge dir config */
+   util_ldap_merge_dir_config,  /* merge dir config */
    util_ldap_create_config,     /* create server config */
    util_ldap_merge_config,      /* merge server config */
    util_ldap_cmds,              /* command table */