While I wait for sufficient karma to commit to apr(-util) trunk I figured I'd
start the discussion about this patch. It adds rebind callback support into
Apache/APR so that referrals can be chased in cases where the LDAP server does
not accept anonymous binds during referral chasing.

The intent is to get this backported into the 2.2 branch so, although I believe
it makes most sense to use the SplitRebind* patch below (in which much of the
code is in APR), I fear that that may limit backporting support. I provided
the AllApacheRebind patch as a way to (possibly) make backporting more 
palatable.
Either way, the code is pretty much the same except for which files contain
which code.

In evaluating/verifying these patches you would look at either:
  SplitRebind_Apache.diff + SplitRebind_apr-util.diff

          -- OR --
  AllApacheRebind.diff


--
Paul J. Reder
-----------------------------------------------------------
"The strength of the Constitution lies entirely in the determination of each
citizen to defend it.  Only if every single citizen feels duty bound to do
his share in this defense are the constitutional rights secure."
-- Albert Einstein




Index: httpd-trunk/modules/ldap/util_ldap.c
===================================================================
--- httpd-trunk/modules/ldap/util_ldap.c	(revision 558196)
+++ httpd-trunk/modules/ldap/util_ldap.c	(working copy)
@@ -54,6 +54,11 @@
 #define APR_LDAP_SIZELIMIT -1
 #endif
 
+#if APR_HAS_THREADS
+static apr_thread_mutex_t *LDAP_xref_lock = NULL;
+#endif
+static LDAP_xref_entry_t *xref_head = NULL;
+
 module AP_MODULE_DECLARE_DATA ldap_module;
 
 #define LDAP_CACHE_LOCK() do {                                  \
@@ -170,6 +175,174 @@
 }
 
 
+/*************************************************************************************/
+APU_DECLARE(int) LDAP_xref_add(apr_pool_t *pool, LDAP *ld, const char *bindDN, const char *bindPW)
+{
+    LDAP_xref_entry_t *new_xref;
+
+    new_xref = (LDAP_xref_entry_t *)apr_pcalloc(pool, sizeof(LDAP_xref_entry_t));
+    if (new_xref) {
+        new_xref->index = ld;
+        if (bindDN) {
+            new_xref->bindDN = apr_pstrdup(pool, bindDN);
+        }
+        if (bindPW) {
+            new_xref->bindPW = apr_pstrdup(pool, bindPW);
+        }
+
+#if APR_HAS_THREADS
+       apr_thread_mutex_lock(LDAP_xref_lock);
+#endif
+
+        new_xref->next = xref_head;
+        xref_head = new_xref;
+
+#if APR_HAS_THREADS
+        apr_thread_mutex_unlock(LDAP_xref_lock);
+#endif
+    }
+    else {
+        return(APR_ENOMEM);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "LDAP: xref_add: Added [%s] [%s]",
+                 (new_xref->bindDN ? new_xref->bindDN : "NULL"),
+                 (new_xref->bindPW ? new_xref->bindPW : "NULL"));
+
+    return(APR_SUCCESS);
+}
+
+/*************************************************************************************/
+APU_DECLARE(void) LDAP_xref_remove(LDAP *ld)
+{
+    LDAP_xref_entry_t *tmp_xref, *prev = NULL;
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_lock(LDAP_xref_lock);
+#endif
+    tmp_xref = xref_head;
+
+    while ((tmp_xref) && (tmp_xref->index != ld)) {
+        prev = tmp_xref;
+        tmp_xref = tmp_xref->next;
+    }
+
+    if (tmp_xref) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                     "LDAP: xref_remove: Removing [%s] [%s]",
+                     (tmp_xref->bindDN ? tmp_xref->bindDN : "NULL"),
+                     (tmp_xref->bindPW ? tmp_xref->bindPW : "NULL"));
+
+        if (tmp_xref == xref_head) {
+            xref_head = xref_head->next;
+        }
+        else {
+            prev->next = tmp_xref->next;
+        }
+        /* tmp_xref and its contents were pool allocated so they don't need to be freed here. */
+    }
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_unlock(LDAP_xref_lock);
+#endif
+}
+
+/*************************************************************************************/
+static LDAP_xref_entry_t *LDAP_xref_lookup(LDAP *ld)
+{
+    LDAP_xref_entry_t *tmp_xref, *match = NULL;
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_lock(LDAP_xref_lock);
+#endif
+    tmp_xref = xref_head;
+
+    while (tmp_xref) {
+        if (tmp_xref->index == ld) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                         "LDAP: xref_lookup: Found [%s] [%s]",
+                         (tmp_xref->bindDN ? tmp_xref->bindDN : "NULL"),
+                         (tmp_xref->bindPW ? tmp_xref->bindPW : "NULL"));
+
+            match = tmp_xref;
+            tmp_xref = NULL;
+        }
+        else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                         "LDAP: xref_lookup: Skipping [%s] [%s]",
+                         (tmp_xref->bindDN ? tmp_xref->bindDN : "NULL"),
+                         (tmp_xref->bindPW ? tmp_xref->bindPW : "NULL"));
+
+            tmp_xref = tmp_xref->next;
+        }
+    }
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_unlock(LDAP_xref_lock);
+#endif
+
+    return (match);
+}
+
+
+/* LDAP_rebindproc() ITDS style
+ *     Rebind callback function. Called when chasing referrals. See API docs.
+ * ON ENTRY:
+ *     ld       Pointer to an LDAP control structure. (input only)
+ *     binddnp  Pointer to an Application DName used for binding (in *or* out)
+ *     passwdp  Pointer to the password associated with the DName (in *or* out)
+ *     methodp  Pointer to the Auth method (output only)
+ *     freeit   Flag to indicate if this is a lookup or a free request (input only)
+ */
+#if APR_HAS_TIVOLI_LDAPSDK
+int LDAP_rebindproc(LDAP *ld, char **binddnp, char **passwdp, int *methodp, int freeit)
+{
+    if (!freeit) {
+        LDAP_xref_entry_t *my_conn;
+
+        *methodp = LDAP_AUTH_SIMPLE;
+        my_conn = apr_ldap_xref_lookup(ld);
+
+        if ((my_conn) && (my_conn->bindDN != NULL)) {
+            *binddnp = strdup(my_conn->bindDN);
+            *passwdp = strdup(my_conn->bindPW);
+        } else {
+            *binddnp = NULL;
+            *passwdp = NULL;
+        }
+    } else {
+        if (*binddnp) {
+            free(*binddnp);
+        }
+        if (*passwdp) {
+            free(*passwdp);
+        }
+    }
+
+    return LDAP_SUCCESS;
+}
+#elif APR_HAS_OPENLDAP_LDAPSDK
+
+/* LDAP_rebindproc() openLDAP V3 style */
+int LDAP_rebindproc(LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *params)
+{
+    LDAP_xref_entry_t *my_conn;
+    const char *bindDN = NULL;
+    const char *bindPW = NULL;
+
+    my_conn = LDAP_xref_lookup(ld);
+
+    if ((my_conn) && (my_conn->bindDN != NULL)) {
+        bindDN = my_conn->bindDN;
+        bindPW = my_conn->bindPW;
+    }
+
+    return (ldap_bind_s(ld, bindDN, bindPW, LDAP_AUTH_SIMPLE));
+}
+
+#endif
+
 /*
  * Clean up an LDAP connection by unbinding and unlocking the connection.
  * This function is registered with the pool cleanup function - causing
@@ -180,6 +353,8 @@
     util_ldap_connection_t *ldc = param;
 
     if (ldc) {
+        /* Release the xref info for this connection. No more referral rebinds required. */
+        LDAP_xref_remove(ldc->ldap);
 
         /* unbind and disconnect from the LDAP server */
         uldap_connection_unbind(ldc);
@@ -201,12 +376,14 @@
 }
 
 static int uldap_connection_init(request_rec *r,
-                                 util_ldap_connection_t *ldc )
+                                 util_ldap_connection_t *ldc)
 {
     int rc = 0, ldap_option = 0;
     int version  = LDAP_VERSION3;
     apr_ldap_err_t *result = NULL;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
     struct timeval timeOut = {10,0};    /* 10 second connection timeout */
+#endif
     util_ldap_state_t *st =
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
@@ -223,7 +400,6 @@
                   APR_LDAP_NONE,
                   &(result));
 
-
     if (result != NULL && result->rc) {
         ldc->reason = result->reason;
     }
@@ -240,6 +416,16 @@
         return(result->rc);
     }
 
+    /* Now that we have an ldap struct, add it to the referral xref list for rebinds. */
+    rc = LDAP_xref_add(ldc->pool, ldc->ldap, ldc->binddn, ldc->bindpw);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                         "LDAP: Unable to construct cross reference entry. Out of memory?");
+        uldap_connection_unbind(ldc);
+        ldc->reason = "LDAP: Unable to construct cross reference entry.";
+        return(rc);
+    }
+
     /* always default to LDAP V3 */
     ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
 
@@ -269,6 +455,59 @@
     ldap_option = ldc->deref;
     ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
 
+    /* Set options for rebind and referrals. */
+    /*   Should we chase referrals? */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "LDAP: Setting referrals to %s.",
+                 (ldc->ChaseReferrals ? "On" : "Off"));
+    result->rc = ldap_set_option(ldc->ldap,
+                                 LDAP_OPT_REFERRALS,
+                                 (void *)(ldc->ChaseReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF));
+    if (result->rc != LDAP_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
+                     (ldc->ChaseReferrals ? "On" : "Off"),
+                     result->rc);
+        result->reason = "Unable to set LDAP_OPT_REFERRALS.";
+        uldap_connection_unbind(ldc);
+        return(result->rc);
+    }
+
+#if APR_HAS_TIVOLI_LDAPSDK
+    /* This is not supported by current versions of OpenLDAP, OpenLDAP defaults to 5. */
+    if (ldc->ChaseReferrals) {
+        /* Referral hop limit - only if referrals are enabled */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "Setting referral hop limit to %d.",
+                     ldc->ReferralHopLimit);
+        result->rc = ldap_set_option(ldc->ldap,
+                                     LDAP_OPT_REFHOPLIMIT,
+                                     (void *)&ldc->ReferralHopLimit);
+        if (result->rc != LDAP_SUCCESS) {
+          ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                       "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
+                       ldc->ReferralHopLimit,
+                       result->rc);
+          result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
+          uldap_connection_unbind(ldc);
+          return(result->rc);
+        }
+    }
+#endif
+
+    /* set the rebind callback function for use when chasing referrals */
+    /* LDAP_set_rebind_proc returns void, so no post-call check. */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "LDAP: Setting rebind callback function.");
+#if APR_HAS_TIVOLI_LDAPSDK
+    ldap_set_rebind_proc(ldc->ldap, (LDAPRebindProc)LDAP_rebindproc);
+#elif APR_HAS_OPENLDAP_LDAPSDK
+    ldap_set_rebind_proc(ldc->ldap, LDAP_rebindproc, NULL);
+#else
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "LDAP: ldap_set_rebind_proc not supported with current LDAP client SDK. Proc not set.");    
+#endif
+
 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
 #ifdef APR_LDAP_OPT_VERIFY_CERT
     apr_ldap_set_option(ldc->pool, ldc->ldap,
@@ -450,8 +689,9 @@
     util_ldap_state_t *st =
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
+    util_ldap_config_t *dc =
+        (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
 
-
 #if APR_HAS_THREADS
     /* mutex lock this function */
     apr_thread_mutex_lock(st->mutex);
@@ -521,7 +761,7 @@
 /* artificially disable cache */
 /* l = NULL; */
 
-    /* If no connection what found after the second search, we
+    /* If no connection was found after the second search, we
      * must create one.
      */
     if (!l) {
@@ -544,6 +784,8 @@
         l->deref = deref;
         util_ldap_strdup((char**)&(l->binddn), binddn);
         util_ldap_strdup((char**)&(l->bindpw), bindpw);
+        l->ChaseReferrals = dc->ChaseReferrals;
+        l->ReferralHopLimit = dc->ReferralHopLimit;
 
         /* The security mode after parsing the URL will always be either
          * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
@@ -1742,9 +1984,11 @@
                                                     void *dummy,
                                                     const char *ttl)
 {
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
     util_ldap_state_t *st =
         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
                                                   &ldap_module);
+#endif
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 
     if (err != NULL) {
@@ -1766,7 +2010,47 @@
     return NULL;
 }
 
+static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
+                                                 void *config,
+                                                 int mode)
+{
+    util_ldap_config_t *dc =  config;
 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
+                      "LDAP: Setting refferal chasing %s",
+                      mode?"ON":"OFF");
+
+    dc->ChaseReferrals = mode;
+
+    return(NULL);
+}
+
+static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
+                                                    void *config,
+                                                    const char *hop_limit)
+{
+    util_ldap_config_t *dc =  config;
+
+    dc->ReferralHopLimit = atol(hop_limit);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
+                 "LDAP: Limit chased referrals to maximum of %d hops.",
+                 dc->ReferralHopLimit);
+
+    return NULL;
+}
+
+static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
+   util_ldap_config_t *dc =
+       (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
+
+   dc->ChaseReferrals = 1;    /* default is to turn referral chasing on. */
+   dc->ReferralHopLimit = 5; /* default is to chase a max of 5 hops. */
+
+   return dc;
+}
+
+
 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
 {
     util_ldap_state_t *st =
@@ -1795,6 +2079,13 @@
     st->connectionTimeout = 10;
     st->verify_svr_cert = 1;
 
+#if APR_HAS_THREADS
+    apr_thread_mutex_create( &LDAP_xref_lock, APR_THREAD_MUTEX_DEFAULT, p );
+    if( LDAP_xref_lock == NULL ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "LDAP: Can't create xref lock.");
+    }
+#endif
+
     return st;
 }
 
@@ -2090,7 +2381,15 @@
                   NULL, RSRC_CONF,
                   "Specify the LDAP socket connection timeout in seconds "
                   "(default: 10)"),
+    AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
+                  NULL, OR_AUTHCFG,
+                  "Choose whether referrals are chased ['ON'|'OFF'].  Default 'ON'"),
 
+    AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
+                  NULL, OR_AUTHCFG,
+                  "Limit the number of referral hops that LDAP can follow. "
+                  "(Integer value, default=5)"),
+
     {NULL}
 };
 
@@ -2114,7 +2413,7 @@
 
 module AP_MODULE_DECLARE_DATA ldap_module = {
    STANDARD20_MODULE_STUFF,
-   NULL,                        /* create dir config */
+   util_ldap_create_dir_config, /* create dir config */
    NULL,                        /* merge dir config */
    util_ldap_create_config,     /* create server config */
    util_ldap_merge_config,      /* merge server config */
Index: httpd-trunk/include/util_ldap.h
===================================================================
--- httpd-trunk/include/util_ldap.h	(revision 558196)
+++ httpd-trunk/include/util_ldap.h	(working copy)
@@ -105,10 +105,17 @@
     apr_array_header_t *client_certs;   /* Client certificates on this connection */
 
     const char *reason;                 /* Reason for an error failure */
+    int ChaseReferrals;                 /* [on|off] (on=1, off=0, default = On)*/
+    int ReferralHopLimit;               /* # of referral hops to follow (default = 5) */
 
     struct util_ldap_connection_t *next;
 } util_ldap_connection_t;
 
+typedef struct util_ldap_config_t {
+    int ChaseReferrals;
+    int ReferralHopLimit;
+} util_ldap_config_t;
+
 /* LDAP cache state information */ 
 typedef struct util_ldap_state_t {
     apr_pool_t *pool;           /* pool from which this state is allocated */
@@ -144,6 +151,14 @@
 
 } util_ldap_state_t;
 
+/* Used to store information about connections for use in the referral rebind callback. */
+struct LDAP_xref_entry {
+    LDAP *index;
+    const char *bindDN;
+    const char *bindPW;
+    struct LDAP_xref_entry *next;
+};
+typedef struct LDAP_xref_entry LDAP_xref_entry_t;
 
 /**
  * Open a connection to an LDAP server
Index: httpd-trunk/modules/ldap/util_ldap.c
===================================================================
--- httpd-trunk/modules/ldap/util_ldap.c	(revision 558855)
+++ httpd-trunk/modules/ldap/util_ldap.c	(working copy)
@@ -180,6 +180,8 @@
     util_ldap_connection_t *ldc = param;
 
     if (ldc) {
+        /* Release the xref info for this connection. No more referral rebinds required. */
+        apr_ldap_xref_remove(ldc->ldap);
 
         /* unbind and disconnect from the LDAP server */
         uldap_connection_unbind(ldc);
@@ -201,12 +203,14 @@
 }
 
 static int uldap_connection_init(request_rec *r,
-                                 util_ldap_connection_t *ldc )
+                                 util_ldap_connection_t *ldc)
 {
     int rc = 0, ldap_option = 0;
     int version  = LDAP_VERSION3;
     apr_ldap_err_t *result = NULL;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
     struct timeval timeOut = {10,0};    /* 10 second connection timeout */
+#endif
     util_ldap_state_t *st =
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
@@ -223,7 +227,6 @@
                   APR_LDAP_NONE,
                   &(result));
 
-
     if (result != NULL && result->rc) {
         ldc->reason = result->reason;
     }
@@ -240,6 +243,16 @@
         return(result->rc);
     }
 
+    /* Now that we have an ldap struct, add it to the referral xref list for rebinds. */
+    rc = apr_ldap_xref_add(ldc->pool, ldc->ldap, ldc->binddn, ldc->bindpw);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                         "LDAP: Unable to construct cross reference entry. Out of memory?");
+        uldap_connection_unbind(ldc);
+        ldc->reason = "LDAP: Unable to construct cross reference entry.";
+        return(rc);
+    }
+
     /* always default to LDAP V3 */
     ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
 
@@ -269,6 +282,55 @@
     ldap_option = ldc->deref;
     ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
 
+    /* Set options for rebind and referrals. */
+    /*   Should we chase referrals? */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "LDAP: Setting referrals to %s.",
+                 (ldc->ChaseReferrals ? "On" : "Off"));
+    result->rc = ldap_set_option(ldc->ldap,
+                                 LDAP_OPT_REFERRALS,
+                                 (void *)(ldc->ChaseReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF));
+    if (result->rc != LDAP_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
+                     (ldc->ChaseReferrals ? "On" : "Off"),
+                     result->rc);
+        result->reason = "Unable to set LDAP_OPT_REFERRALS.";
+        uldap_connection_unbind(ldc);
+        return(result->rc);
+    }
+
+#if APR_HAS_TIVOLI_LDAPSDK
+    /* This is not supported by current versions of OpenLDAP, OpenLDAP defaults to 5. */
+    if (ldc->ChaseReferrals) {
+        /* Referral hop limit - only if referrals are enabled */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "Setting referral hop limit to %d.",
+                     ldc->ReferralHopLimit);
+        result->rc = ldap_set_option(ldc->ldap,
+                                     LDAP_OPT_REFHOPLIMIT,
+                                     (void *)&ldc->ReferralHopLimit);
+        if (result->rc != LDAP_SUCCESS) {
+          ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                       "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
+                       ldc->ReferralHopLimit,
+                       result->rc);
+          result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
+          uldap_connection_unbind(ldc);
+          return(result->rc);
+        }
+    }
+#endif
+
+    /* set the rebind callback function for use when chasing referrals */
+    /* LDAP_set_rebind_proc returns void, so no post-call check. */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "LDAP: Setting rebind callback function.");
+    if (apr_ldap_set_rebind_callback(ldc->ldap) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "LDAP: Failed to set rebind callback. Current LDAP client SDK may not support it.");
+    }
+
 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
 #ifdef APR_LDAP_OPT_VERIFY_CERT
     apr_ldap_set_option(ldc->pool, ldc->ldap,
@@ -450,8 +512,9 @@
     util_ldap_state_t *st =
         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
         &ldap_module);
+    util_ldap_config_t *dc =
+        (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
 
-
 #if APR_HAS_THREADS
     /* mutex lock this function */
     apr_thread_mutex_lock(st->mutex);
@@ -521,7 +584,7 @@
 /* artificially disable cache */
 /* l = NULL; */
 
-    /* If no connection what found after the second search, we
+    /* If no connection was found after the second search, we
      * must create one.
      */
     if (!l) {
@@ -544,6 +607,8 @@
         l->deref = deref;
         util_ldap_strdup((char**)&(l->binddn), binddn);
         util_ldap_strdup((char**)&(l->bindpw), bindpw);
+        l->ChaseReferrals = dc->ChaseReferrals;
+        l->ReferralHopLimit = dc->ReferralHopLimit;
 
         /* The security mode after parsing the URL will always be either
          * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
@@ -1742,9 +1807,11 @@
                                                     void *dummy,
                                                     const char *ttl)
 {
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
     util_ldap_state_t *st =
         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
                                                   &ldap_module);
+#endif
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 
     if (err != NULL) {
@@ -1766,7 +1833,47 @@
     return NULL;
 }
 
+static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
+                                                 void *config,
+                                                 int mode)
+{
+    util_ldap_config_t *dc =  config;
 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
+                      "LDAP: Setting refferal chasing %s",
+                      mode?"ON":"OFF");
+
+    dc->ChaseReferrals = mode;
+
+    return(NULL);
+}
+
+static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
+                                                    void *config,
+                                                    const char *hop_limit)
+{
+    util_ldap_config_t *dc =  config;
+
+    dc->ReferralHopLimit = atol(hop_limit);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
+                 "LDAP: Limit chased referrals to maximum of %d hops.",
+                 dc->ReferralHopLimit);
+
+    return NULL;
+}
+
+static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
+   util_ldap_config_t *dc =
+       (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
+
+   dc->ChaseReferrals = 1;    /* default is to turn referral chasing on. */
+   dc->ReferralHopLimit = 5; /* default is to chase a max of 5 hops. */
+
+   return dc;
+}
+
+
 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
 {
     util_ldap_state_t *st =
@@ -1795,6 +1902,10 @@
     st->connectionTimeout = 10;
     st->verify_svr_cert = 1;
 
+#if APR_HAS_THREADS
+    apr_ldap_init_xref_lock (p);
+#endif
+
     return st;
 }
 
@@ -2091,6 +2202,15 @@
                   "Specify the LDAP socket connection timeout in seconds "
                   "(default: 10)"),
 
+    AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
+                  NULL, OR_AUTHCFG,
+                  "Choose whether referrals are chased ['ON'|'OFF'].  Default 'ON'"),
+
+    AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
+                  NULL, OR_AUTHCFG,
+                  "Limit the number of referral hops that LDAP can follow. "
+                  "(Integer value, default=5)"),
+
     {NULL}
 };
 
@@ -2114,7 +2234,7 @@
 
 module AP_MODULE_DECLARE_DATA ldap_module = {
    STANDARD20_MODULE_STUFF,
-   NULL,                        /* create dir config */
+   util_ldap_create_dir_config, /* create dir config */
    NULL,                        /* merge dir config */
    util_ldap_create_config,     /* create server config */
    util_ldap_merge_config,      /* merge server config */
Index: httpd-trunk/include/util_ldap.h
===================================================================
--- httpd-trunk/include/util_ldap.h	(revision 558855)
+++ httpd-trunk/include/util_ldap.h	(working copy)
@@ -29,6 +29,7 @@
 #include "apr_tables.h"
 #include "apr_time.h"
 #include "apr_ldap.h"
+#include "apr_ldap_rebind.h"
 
 #if APR_HAS_SHARED_MEMORY
 #include "apr_rmm.h"
@@ -105,10 +106,17 @@
     apr_array_header_t *client_certs;   /* Client certificates on this connection */
 
     const char *reason;                 /* Reason for an error failure */
+    int ChaseReferrals;                 /* [on|off] (on=1, off=0, default = On)*/
+    int ReferralHopLimit;               /* # of referral hops to follow (default = 5) */
 
     struct util_ldap_connection_t *next;
 } util_ldap_connection_t;
 
+typedef struct util_ldap_config_t {
+    int ChaseReferrals;
+    int ReferralHopLimit;
+} util_ldap_config_t;
+
 /* LDAP cache state information */ 
 typedef struct util_ldap_state_t {
     apr_pool_t *pool;           /* pool from which this state is allocated */
Index: apr-util-trunk/ldap/apr_ldap_rebind.c
===================================================================
--- apr-util-trunk/ldap/apr_ldap_rebind.c	(revision 0)
+++ apr-util-trunk/ldap/apr_ldap_rebind.c	(revision 0)
@@ -0,0 +1,211 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*  apr_ldap_option.c -- LDAP options
+ *
+ *  The LDAP SDK allows the getting and setting of options on an LDAP
+ *  connection.
+ *
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_ldap.h"
+#include "apr_errno.h"
+#include "apr_strings.h"
+#include "apr_ldap_rebind.h"
+
+#include "stdio.h"
+
+#if APR_HAS_THREADS
+static apr_thread_mutex_t *apr_ldap_xref_lock = NULL;
+#endif
+static LDAP_xref_entry_t *xref_head = NULL;
+
+
+/* APR utility routine used to create the xref_lock. */
+APU_DECLARE(apr_status_t) apr_ldap_init_xref_lock(apr_pool_t *pool)
+{
+    apr_status_t retcode = APR_SUCCESS;
+
+#if APR_HAS_THREADS
+    retcode = apr_thread_mutex_create(&apr_ldap_xref_lock, APR_THREAD_MUTEX_DEFAULT, pool);
+#endif
+
+    return(retcode);
+}
+
+
+/*************************************************************************************/
+APU_DECLARE(int) apr_ldap_xref_add(apr_pool_t *pool, LDAP *ld, const char *bindDN, const char *bindPW)
+{
+    LDAP_xref_entry_t *new_xref;
+
+    new_xref = (LDAP_xref_entry_t *)apr_pcalloc(pool, sizeof(LDAP_xref_entry_t));
+    if (new_xref) {
+        new_xref->index = ld;
+        if (bindDN) {
+            new_xref->bindDN = apr_pstrdup(pool, bindDN);
+        }
+        if (bindPW) {
+            new_xref->bindPW = apr_pstrdup(pool, bindPW);
+        }
+    
+#if APR_HAS_THREADS
+       apr_thread_mutex_lock(apr_ldap_xref_lock);
+#endif
+    
+        new_xref->next = xref_head;
+        xref_head = new_xref;
+    
+#if APR_HAS_THREADS
+        apr_thread_mutex_unlock(apr_ldap_xref_lock);
+#endif
+    }
+    else {
+        return(APR_ENOMEM);
+    }
+
+    return(APR_SUCCESS);
+}
+
+/*************************************************************************************/
+APU_DECLARE(void) apr_ldap_xref_remove(LDAP *ld)
+{
+    LDAP_xref_entry_t *tmp_xref, *prev = NULL;
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_lock(apr_ldap_xref_lock);
+#endif
+    tmp_xref = xref_head;
+
+    while ((tmp_xref) && (tmp_xref->index != ld)) {
+        prev = tmp_xref;
+        tmp_xref = tmp_xref->next;
+    }
+
+    if (tmp_xref) {
+        if (tmp_xref == xref_head) {
+            xref_head = xref_head->next;
+        }
+        else {
+            prev->next = tmp_xref->next;
+        }
+        /* tmp_xref and its contents were pool allocated so they don't need to be freed here. */
+    }
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_unlock(apr_ldap_xref_lock);
+#endif
+}
+
+/*************************************************************************************/
+static LDAP_xref_entry_t *apr_ldap_xref_lookup(LDAP *ld)
+{
+    LDAP_xref_entry_t *tmp_xref, *match = NULL;
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_lock(apr_ldap_xref_lock);
+#endif
+    tmp_xref = xref_head;
+
+    while (tmp_xref) {
+        if (tmp_xref->index == ld) {
+            match = tmp_xref;
+            tmp_xref = NULL;
+        }
+        else {
+            tmp_xref = tmp_xref->next;
+        }
+    }
+
+#if APR_HAS_THREADS
+    apr_thread_mutex_unlock(apr_ldap_xref_lock);
+#endif
+
+    return (match);
+}
+
+/* LDAP_rebindproc() ITDS style
+ *     Rebind callback function. Called when chasing referrals. See API docs.
+ * ON ENTRY:
+ *     ld       Pointer to an LDAP control structure. (input only)
+ *     binddnp  Pointer to an Application DName used for binding (in *or* out)
+ *     passwdp  Pointer to the password associated with the DName (in *or* out)
+ *     methodp  Pointer to the Auth method (output only)
+ *     freeit   Flag to indicate if this is a lookup or a free request (input only)
+ */
+#if APR_HAS_TIVOLI_LDAPSDK
+int LDAP_rebindproc(LDAP *ld, char **binddnp, char **passwdp, int *methodp, int freeit)
+{
+    if (!freeit) {
+        LDAP_xref_entry_t *my_conn;
+
+        *methodp = LDAP_AUTH_SIMPLE;
+        my_conn = apr_ldap_xref_lookup(ld);
+
+        if ((my_conn) && (my_conn->bindDN != NULL)) {
+            *binddnp = strdup(my_conn->bindDN);
+            *passwdp = strdup(my_conn->bindPW);
+        } else {
+            *binddnp = NULL;
+            *passwdp = NULL;
+        }
+    } else {
+        if (*binddnp) {
+            free(*binddnp);
+        }
+        if (*passwdp) {
+            free(*passwdp);
+        }
+    }
+
+    return LDAP_SUCCESS;
+}
+#elif APR_HAS_OPENLDAP_LDAPSDK
+
+/* LDAP_rebindproc() openLDAP V3 style */
+int LDAP_rebindproc(LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *params)
+{
+    LDAP_xref_entry_t *my_conn;
+    const char *bindDN = NULL;
+    const char *bindPW = NULL;
+
+    my_conn = apr_ldap_xref_lookup(ld);
+
+    if ((my_conn) && (my_conn->bindDN != NULL)) {
+        bindDN = my_conn->bindDN;
+        bindPW = my_conn->bindPW;
+    }
+
+    return (ldap_bind_s(ld, bindDN, bindPW, LDAP_AUTH_SIMPLE));
+}
+
+#endif
+
+/* APR utility routine used to set the rebind callback routine. */
+APU_DECLARE(apr_status_t) apr_ldap_set_rebind_callback(LDAP *ld)
+{
+#if APR_HAS_TIVOLI_LDAPSDK
+    ldap_set_rebind_proc(ld, (LDAPRebindProc)LDAP_rebindproc);
+    return (APR_SUCCESS);
+#elif APR_HAS_OPENLDAP_LDAPSDK
+    ldap_set_rebind_proc(ld, LDAP_rebindproc, NULL);
+    return (APR_SUCCESS);
+#else
+    return (APR_ENOTIMPL);
+#endif
+}
Index: apr-util-trunk/ldap/NWGNUmakefile
===================================================================
--- apr-util-trunk/ldap/NWGNUmakefile	(revision 558855)
+++ apr-util-trunk/ldap/NWGNUmakefile	(working copy)
@@ -231,6 +231,7 @@
 	$(OBJDIR)/apr_ldap_init.o \
 	$(OBJDIR)/apr_ldap_option.o \
 	$(OBJDIR)/apr_ldap_url.o \
+	$(OBJDIR)/apr_ldap_rebind.o \
 	$(EOLIST)
 
 #
Index: apr-util-trunk/include/apr_ldap_rebind.h
===================================================================
--- apr-util-trunk/include/apr_ldap_rebind.h	(revision 0)
+++ apr-util-trunk/include/apr_ldap_rebind.h	(revision 0)
@@ -0,0 +1,79 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file apu_ldap_rebind.h
+ * @brief Apache LDAP library
+ */
+
+#ifndef APU_LDAP_REBIND_H
+#define APU_LDAP_REBIND_H
+
+/* Used to store information about connections for use in the referral rebind callback. */
+struct LDAP_xref_entry {
+    LDAP *index;
+    const char *bindDN;
+    const char *bindPW;
+    struct LDAP_xref_entry *next;
+};
+typedef struct LDAP_xref_entry LDAP_xref_entry_t;
+
+#endif /* APU_LDAP_REBIND_H */
+
+
+/**
+ * APR LDAP initialize xref lock
+ *
+ * This function creates the lock for controlling access to the xref list..
+ * @param pool Pool to use when creating the xref_lock.
+ */
+APU_DECLARE(apr_status_t) apr_ldap_init_xref_lock(apr_pool_t *pool);
+
+
+/**
+ * APR LDAP xref_add function
+ *
+ * This function creates a cross reference entry for the specified ldap
+ * connection. The rebind callback function will look up this ldap 
+ * connection so it can retrieve the bindDN and bindPW for use in any 
+ * binds while referrals are being chased.
+ * @param pool The pool to use
+ * @param ld The LDAP connectionhandle
+ * @param bindDN The bind DN to be used for any binds while chasing 
+ *               referrals on this ldap connection.
+ * @param bindPW The bind Password to be used for any binds while 
+ *               chasing referrals on this ldap connection.
+ */
+APU_DECLARE(int) apr_ldap_xref_add(apr_pool_t *pool,
+                                   LDAP *ld,
+                                   const char *bindDN,
+                                   const char *bindPW);
+
+/**
+ * APR LDAP xref_remove function
+ *
+ * This function removes the rebind cross reference entry for the specified ldap connection.
+ * @param ld The LDAP connectionhandle
+ */
+APU_DECLARE(void) apr_ldap_xref_remove(LDAP *ld);
+
+/**
+ * APR LDAP set rebind callback function
+ *
+ * This function sets the rebind callback function for this ldap connection.
+ * @param ld The LDAP connectionhandle
+ */
+APU_DECLARE(apr_status_t) apr_ldap_set_rebind_callback(LDAP *ld);
Index: apr-util-trunk/include/apr_ldap.h.in
===================================================================
--- apr-util-trunk/include/apr_ldap.h.in	(revision 558855)
+++ apr-util-trunk/include/apr_ldap.h.in	(working copy)
@@ -149,6 +149,7 @@
 #include "apr_ldap_url.h"
 #include "apr_ldap_init.h"
 #include "apr_ldap_option.h"
+#include "apr_ldap_rebind.h"
 
 /** @} */
 #endif /* APR_HAS_LDAP */

Reply via email to