# This is the second of two patches that provide chasing of LDAP referrals # in the httpd-2.0.x codebase when those referrals require authentication # (as is the case with MS Active Directory). # # This patch add the following Apache config file options: # LDAPReferrals # LDAPReferralHopLimit # and makes use of the apr_util/apr_ldap_rebind functionality provided in # the first patch. # # This patch is a direct backport of the code in the httpd 2.2.x trunk for # use in the 2.0.x code. The original code was written by Paul J. Reder and # Graham Leggett (see bugzilla link below). # # This patch was generated against the sources from a stock httpd-2.0.63 # tarball. It has been tested using libldap from OpenLDAP 2.3.41. # # See: # "windows 2003 active directory - [ldap_search_ext_s() for user failed][Referral]" # http://issues.apache.org/bugzilla/show_bug.cgi?id=26538 # # This patch will not work without the first (apr-util) patch in the set. # # To apply this patch: # # $ gzip -dc httpd-2.0.63.tar.gz | tar xf - # $ cd httpd-2.0.63 # $ patch -p1 < /path/to/this/patch/file # # diff -Naur httpd-2.0.63-orig/include/util_ldap.h httpd-2.0.63-chng/include/util_ldap.h --- httpd-2.0.63-orig/include/util_ldap.h 2006-07-12 07:40:55.000000000 +0000 +++ httpd-2.0.63-chng/include/util_ldap.h 2008-02-22 09:16:55.000000000 +0000 @@ -95,9 +95,17 @@ 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 */ diff -Naur httpd-2.0.63-orig/modules/experimental/util_ldap.c httpd-2.0.63-chng/modules/experimental/util_ldap.c --- httpd-2.0.63-orig/modules/experimental/util_ldap.c 2008-01-02 19:29:59.000000000 +0000 +++ httpd-2.0.63-chng/modules/experimental/util_ldap.c 2008-02-22 09:15:14.000000000 +0000 @@ -213,6 +213,8 @@ util_ldap_connection_t *ldc = param; if (ldc) { + /* Release the rebind info for this connection. No more referral rebinds required. */ + apr_ldap_rebind_remove(ldc->ldap); /* unbind and disconnect from the LDAP server */ util_ldap_connection_unbind(ldc); @@ -247,7 +249,9 @@ int failures = 0; int version = LDAP_VERSION3; int rc = LDAP_SUCCESS; +#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); @@ -318,6 +322,16 @@ return(-1); } + /* Now that we have an ldap struct, add it to the referral list for rebinds. */ + rc = apr_ldap_rebind_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 add rebind cross reference entry. Out of memory?"); + util_ldap_connection_unbind(ldc); + ldc->reason = "LDAP: Unable to add rebind cross reference entry."; + return(rc); + } + /* Set the alias dereferencing option */ ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref)); @@ -337,8 +351,45 @@ } } #endif - } + /* 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 = ldap_set_option(ldc->ldap, LDAP_OPT_REFERRALS, + (void *)(ldc->ChaseReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF)); + if (result != 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); + util_ldap_connection_unbind(ldc); + ldc->reason = "Unable to set LDAP_OPT_REFERRALS."; + return(result); + } + +#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 = ldap_set_option(ldc->ldap, LDAP_OPT_REFHOPLIMIT, + (void *)&ldc->ReferralHopLimit); + if (result != 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); + util_ldap_connection_unbind(ldc); + ldc->reason = "Unable to set LDAP_OPT_REFHOPLIMIT."; + return(result); + } + } +#endif + } /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is * returned. Break out of the loop on Success or any other error. @@ -391,6 +442,8 @@ (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 */ @@ -476,6 +529,10 @@ 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; + l->secure = secure; /* add the cleanup to the pool */ @@ -1379,6 +1436,46 @@ 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; +} + void *util_ldap_create_config(apr_pool_t *p, server_rec *s) { util_ldap_state_t *st = @@ -1403,6 +1500,9 @@ st->ssl_support = 0; st->connectionTimeout = 10; + /* Initialize the rebind callback's cross reference list */ + apr_ldap_rebind_init (p); + return st; } @@ -1737,6 +1837,15 @@ "Specifies the LDAP socket connection timeout in seconds. " "Default is 10 seconds. "), + 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} }; @@ -1749,7 +1858,7 @@ module ldap_module = { STANDARD20_MODULE_STUFF, - NULL, /* dir config creater */ + util_ldap_create_dir_config, /* create dir config */ NULL, /* dir merger --- default is to override */ util_ldap_create_config, /* server config */ NULL, /* merge server config */