util_ldap.c revision 93f973142a3d6db34052694c0d2d5869f527fdc5
842ae4bd224140319ae7feec1872b93dfd491143fielding/* Licensed to the Apache Software Foundation (ASF) under one or more
842ae4bd224140319ae7feec1872b93dfd491143fielding * contributor license agreements. See the NOTICE file distributed with
842ae4bd224140319ae7feec1872b93dfd491143fielding * this work for additional information regarding copyright ownership.
842ae4bd224140319ae7feec1872b93dfd491143fielding * The ASF licenses this file to You under the Apache License, Version 2.0
842ae4bd224140319ae7feec1872b93dfd491143fielding * (the "License"); you may not use this file except in compliance with
842ae4bd224140319ae7feec1872b93dfd491143fielding * the License. You may obtain a copy of the License at
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * Unless required by applicable law or agreed to in writing, software
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * distributed under the License is distributed on an "AS IS" BASIS,
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * See the License for the specific language governing permissions and
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * limitations under the License.
e8f95a682820a599fe41b22977010636be5c2717jim * util_ldap.c: LDAP things
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh * Original code from auth_ldap module for Apache v1.3:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Copyright 1998, 1999 Enbridge Pipelines Inc.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Copyright 1999-2001 Dave Carrigan
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#error mod_ldap requires APR-util to have LDAP support built in
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/* Default define for ldap functions that need a SIZELIMIT but
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * do not have the define
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * XXX This should be removed once a supporting #define is
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * released through APR-Util.
785be1b6298010956622771c870ab3cd8ca57a2faaron#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *ldap_cache_mutex_type = "ldap-cache";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic apr_status_t uldap_connection_unbind(void *param);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define LDAP_CACHE_LOCK() do { \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define LDAP_CACHE_UNLOCK() do { \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_global_mutex_unlock(st->util_ldap_cache_lock); \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic void util_ldap_strdup (char **str, const char *newstr)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Status Handler
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * --------------
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This handler generates a status page about the current performance of
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the LDAP cache. It is enabled as follows:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * <Location /ldap-status>
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * SetHandler ldap-status
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * </Location>
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding st = (util_ldap_state_t *) ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_set_content_type(r, "text/html; charset=ISO-8859-1");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "<html><head><title>LDAP Cache Information</title></head>\n", r);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "</h1>\n", r);
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/* ------------------------------------------------------------------ */
58fd79b56eb624bf011772994e9761d3c2e228c1orlikowski * Closes an LDAP connection by unlocking it. The next time
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * uldap_connection_find() is called this connection will be
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick * available for reuse.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawickstatic void uldap_connection_close(util_ldap_connection_t *ldc)
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick /* We leave bound LDAP connections floating around in our pool,
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * but always check/fix the binddn/bindpw when we take them out
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * of the pool
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames /* mark our connection as available for reuse */
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp * Destroys an LDAP connection by unbinding and closing the connection to
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp * the LDAP server. It is used to bring the connection back to a known
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp * state after an error.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawickstatic apr_status_t uldap_connection_unbind(void *param)
39b76a07959a0a332366c735a23894d9e8ed6872trawick ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp unbind", ldc);
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick /* forget the rebind info for this conn */
39b76a07959a0a332366c735a23894d9e8ed6872trawick if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/* not presently used, not part of the API */
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid * util_ldap_connection_remove frees all storage associated with the LDAP
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid * connection and removes it completely from the per-virtualhost list of
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid * connections
97c78987224dcd037076d393aad1867c26b2c8cftrawick * The caller should hold the lock for this connection
97c78987224dcd037076d393aad1867c26b2c8cftrawickstatic apr_status_t util_ldap_connection_remove (void *param) {
97c78987224dcd037076d393aad1867c26b2c8cftrawick util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Remove ldc from the list */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (l == ldc) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Destory the pool associated with this connection */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int have_client_certs = !apr_is_empty_array(ldc->client_certs);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Normally we enable SSL/TLS with apr_ldap_set_option(), except
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * with Solaris LDAP, where this is broken.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * With Solaris LDAP, we enable TSL via the secure argument
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * to apr_ldap_init(). This requires a fix from apr-util >= 1.4.0.
785be1b6298010956622771c870ab3cd8ca57a2faaron * Just in case client certificates ever get supported, we
785be1b6298010956622771c870ab3cd8ca57a2faaron * handle those as with the other LDAP SDKs.
785be1b6298010956622771c870ab3cd8ca57a2faaron int secure = have_client_certs ? APR_LDAP_NONE : ldc->secure;
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Since the host will include a port if the default port is not used,
785be1b6298010956622771c870ab3cd8ca57a2faaron * always specify the default ports for the port parameter. This will
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * allow a host string that contains multiple hosts the ability to mix
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * some hosts with ports and some without. All hosts which do not
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * specify a port will use the default port.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
785be1b6298010956622771c870ab3cd8ca57a2faaron /* something really bad happened */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp init", ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Now that we have an ldap struct, add it to the referral list for rebinds. */
a7ed9c525f9460187f327cea953bf90ecf1bdc51gstein rc = apr_ldap_rebind_add(ldc->rebind_pool, ldc->ldap, ldc->binddn, ldc->bindpw);
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server, APLOGNO(01277)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "LDAP: Unable to add rebind cross reference entry. Out of memory?");
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick /* always default to LDAP V3 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
785be1b6298010956622771c870ab3cd8ca57a2faaron /* set client certificates */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* switch on SSL/TLS */
785be1b6298010956622771c870ab3cd8ca57a2faaron /* See comments near apr_ldap_init() above */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Set the alias dereferencing option */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick /* Set options for rebind and referrals. */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, r->server, APLOGNO(01278)
785be1b6298010956622771c870ab3cd8ca57a2faaron "LDAP: Setting referrals to %s.",
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"));
8fd7c5046d164fb0959222497e5925dfc6a52ff3trawick (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01279)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"),
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((ldc->ReferralHopLimit != AP_LDAP_HOPLIMIT_UNSET) && ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Referral hop limit - only if referrals are enabled and a hop limit is explicitly requested */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01280)
785be1b6298010956622771c870ab3cd8ca57a2faaron "Setting referral hop limit to %d.",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01281)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
785be1b6298010956622771c870ab3cd8ca57a2faaron /* This is not a per-connection setting so just pass NULL for the
785be1b6298010956622771c870ab3cd8ca57a2faaron Ldap connection handle */
785be1b6298010956622771c870ab3cd8ca57a2faaron result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
785be1b6298010956622771c870ab3cd8ca57a2faaron result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01282)
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf "LDAP: Could not set the connection timeout");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * LDAP_OPT_TIMEOUT is not portable, but it influences all synchronous ldap
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * function calls and not just ldap_search_ext_s(), which accepts a timeout
785be1b6298010956622771c870ab3cd8ca57a2faaron * parameter.
785be1b6298010956622771c870ab3cd8ca57a2faaron * XXX: It would be possible to simulate LDAP_OPT_TIMEOUT by replacing all
785be1b6298010956622771c870ab3cd8ca57a2faaron * XXX: synchronous ldap function calls with asynchronous calls and using
785be1b6298010956622771c870ab3cd8ca57a2faaron * XXX: ldap_result() with a timeout.
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_TIMEOUT,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01283)
785be1b6298010956622771c870ab3cd8ca57a2faaron "LDAP: Could not set LDAP_OPT_TIMEOUT");
785be1b6298010956622771c870ab3cd8ca57a2faaron if (LDAP_SUCCESS == ldap_get_option(ldc->ldap, LDAP_OPT_ERROR_NUMBER, &ldaprc)) return ldaprc;
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick if (LDAP_SUCCESS == ldap_get_option(ldc->ldap, LDAP_OPT_RESULT_CODE, &ldaprc)) return ldaprc;
785be1b6298010956622771c870ab3cd8ca57a2faaron * Replacement function for ldap_simple_bind_s() with a timeout.
785be1b6298010956622771c870ab3cd8ca57a2faaron * To do this in a portable way, we have to use ldap_simple_bind() and
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * ldap_result().
785be1b6298010956622771c870ab3cd8ca57a2faaron * Returns LDAP_SUCCESS on success; and an error code on failure
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanfstatic int uldap_simple_bind(util_ldap_connection_t *ldc, char *binddn,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf int msgid = ldap_simple_bind(ldc->ldap, binddn, bindpw);
785be1b6298010956622771c870ab3cd8ca57a2faaron rc = ldap_result(ldc->ldap, msgid, 0, timeout, &result);
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->reason = "LDAP: ldap_simple_bind() result retrieval failed";
785be1b6298010956622771c870ab3cd8ca57a2faaron /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
785be1b6298010956622771c870ab3cd8ca57a2faaron else if (rc == 0) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "LDAP: ldap_simple_bind() timed out";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding } else if (ldap_parse_result(ldc->ldap, result, &rc, NULL, NULL, NULL,
ebc18d48bea83ee5ed7a1b4e30007e5192539829wrowe ldc->reason = "LDAP: ldap_simple_bind() parse result failed";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp bind", ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Connect to the LDAP server and binds. Does not connect if already
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Returns LDAP_SUCCESS on success; and an error code on failure
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton /* sanity check for NULL */
5f08a022a210f4e511561e89f500621a15e6177dtrawick /* If the connection is already bound, return
e8f95a682820a599fe41b22977010636be5c2717jim ldc->reason = "LDAP: connection open successful (already bound)";
785be1b6298010956622771c870ab3cd8ca57a2faaron /* create the ldap session handle
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* loop trying to bind up to st->retries times if LDAP_SERVER_DOWN or LDAP_TIMEOUT
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * are returned. Close the connection before the first retry, and then on every
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * other retry.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * On Success or any other error, break out of the loop.
785be1b6298010956622771c870ab3cd8ca57a2faaron * NOTE: Looping is probably not a great idea. If the server isn't
785be1b6298010956622771c870ab3cd8ca57a2faaron * responding the chances it will respond after a few tries are poor.
900127764fb985c340ee4979cac97146a330c694trawick * However, the original code looped and it only happens on
785be1b6298010956622771c870ab3cd8ca57a2faaron * the error condition.
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
785be1b6298010956622771c870ab3cd8ca57a2faaron "ldap_simple_bind() failed with server down "
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01284)
785be1b6298010956622771c870ab3cd8ca57a2faaron "ldap_simple_bind() timed out on %s "
785be1b6298010956622771c870ab3cd8ca57a2faaron "connection, dropped by firewall?",
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Other errors not retryable */
785be1b6298010956622771c870ab3cd8ca57a2faaron "attempt to re-init the connection");
785be1b6298010956622771c870ab3cd8ca57a2faaron /* leave rc as the initial bind return code */
785be1b6298010956622771c870ab3cd8ca57a2faaron /* free the handle if there was an error
785be1b6298010956622771c870ab3cd8ca57a2faaron * Compare client certificate arrays.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Returns 1 on compare failure, 0 otherwise.
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawickstatic int compare_client_certs(apr_array_header_t *srcs,
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick /* arrays both NULL? if so, then equal */
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick /* arrays different length or either NULL? If so, then not equal */
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
785be1b6298010956622771c870ab3cd8ca57a2faaron /* run an actual comparison */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* One is passwordless? If so, then not equal */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ((src[i].password == NULL) ^ (dest[i].password == NULL)) ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (src[i].password != NULL && dest[i].password != NULL &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* if we got here, the cert arrays were identical */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Find an existing ldap connection struct that matches the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * provided ldap connection parameters.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * If not found in the cache, a new ldc structure will be allocated
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * from st->pool and returned to the caller. If found in the cache,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * a pointer to the existing ldc structure will be returned.
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick /* mutex lock this function */
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz /* Search for an exact connection match in the list that is not
4f133508c93204c06e1acba9774ff184e5812606niq * being used.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding && (l->deref == deref) && (l->secure == secureflag)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding && !compare_client_certs(dc->client_certs, l->client_certs))
43c3e6a4b559b76b750c245ee95e2782c15b4296jim if (l->bound && (now - l->freed) > st->connection_pool_ttl) {
43c3e6a4b559b76b750c245ee95e2782c15b4296jim "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
644be6f54749d2d9950d2c4d2ac448f7af016d26martin /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
e8f95a682820a599fe41b22977010636be5c2717jim "Reuse %s LDC %pp",
3c8b3749225668f06abbb2b023a833a2cef46931brianp /* If this connection didn't match the criteria, then we
3c8b3749225668f06abbb2b023a833a2cef46931brianp * need to unlock the mutex so it is available to be reused.
785be1b6298010956622771c870ab3cd8ca57a2faaron /* If nothing found, search again, but we don't care about the
785be1b6298010956622771c870ab3cd8ca57a2faaron * binddn and bindpw this time.
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((l->port == port) && (strcmp(l->host, host) == 0) &&
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf !compare_client_certs(dc->client_certs, l->client_certs))
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick if (l->bound && (now - l->freed) > st->connection_pool_ttl) {
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "Reuse %s LDC %pp (will rebind)",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* the bind credentials have changed */
785be1b6298010956622771c870ab3cd8ca57a2faaron /* If this connection didn't match the criteria, then we
785be1b6298010956622771c870ab3cd8ca57a2faaron * need to unlock the mutex so it is available to be reused.
785be1b6298010956622771c870ab3cd8ca57a2faaron/* artificially disable cache */
785be1b6298010956622771c870ab3cd8ca57a2faaron/* l = NULL; */
785be1b6298010956622771c870ab3cd8ca57a2faaron /* If no connection was found after the second search, we
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * must create one.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01285)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "util_ldap: Failed to create memory pool");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Add the new connection entry to the linked list. Note that we
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * don't actually establish an LDAP connection yet; that happens
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the first time authentication is requested.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* create the details of this connection in the new pool */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
785be1b6298010956622771c870ab3cd8ca57a2faaron /* The security mode after parsing the URL will always be either
785be1b6298010956622771c870ab3cd8ca57a2faaron * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
785be1b6298010956622771c870ab3cd8ca57a2faaron * If the security setting is NONE, override it to the security
785be1b6298010956622771c870ab3cd8ca57a2faaron * setting optionally supplied by the admin using LDAPTrustedMode
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* save away a copy of the client cert list that is presently valid */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->client_certs = apr_array_copy_hdr(l->pool, dc->client_certs);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* whether or not to keep this connection in the pool when it's returned */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (l->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (apr_pool_create(&(l->rebind_pool), l->pool) != APR_SUCCESS) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01286)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "util_ldap: Failed to create memory pool");
c1b808d160bfb5c849263be8d4acff600853a328trawick/* ------------------------------------------------------------------ */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Compares two DNs to see if they're equal. The only way to do this correctly
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * is to search for the dn and then do ldap_get_dn() on the result. This should
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * match the initial dn, since it would have been also retrieved with
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * ldap_get_dn(). This is expensive, so if the configuration value
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * compare_dn_on_server is false, just does an ordinary strcmp.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * The lock for the ldap cache should already be acquired.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
785be1b6298010956622771c870ab3cd8ca57a2faaron /* get cache entry (or create one) */
785be1b6298010956622771c870ab3cd8ca57a2faaron curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
785be1b6298010956622771c870ab3cd8ca57a2faaron /* a simple compare? */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* unlock this read lock */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison FALSE (direct strcmp())";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison TRUE (direct strcmp())";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* no - it's a server side compare */
382fa07a63096c4a1aabfed36433ea5ac9c40ad0trawick /* is it in the compare cache? */
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* If it's in the cache, it's good */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* unlock this read lock */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* unlock this read lock */
785be1b6298010956622771c870ab3cd8ca57a2faaron /* make a server connection */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* connect to server failed */
51af95bb51b5084e883bad250b2afa2838e9ceebfielding /* search for reqdn */
785be1b6298010956622771c870ab3cd8ca57a2faaron result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard "failed with server down";
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * we are reusing a connection that doesn't seem to be active anymore
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * (firewall state drop?), let's try a new connection.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "failed with timeout";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* search for reqdn failed - no match */
e8f95a682820a599fe41b22977010636be5c2717jim ldc->reason = "DN Comparison ldap_search_ext_s() failed";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* compare unsuccessful */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ldc->reason = "DN Comparison FALSE (checked on server)";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* compare successful - add to the compare cache */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna util_ald_cache_insert(curl->dn_compare_cache, &newnode);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ldc->reason = "DN Comparison TRUE (checked on server)";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * Does an generic ldap_compare operation. It accepts a cache that it will use
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * to lookup the compare in the cache. We cache two kinds of compares
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * (require group compares) and (require user compares). Each compare has a
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * different cache node: require group includes the DN; require user does not
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * because the require user cache is owned by the
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernastatic int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
785be1b6298010956622771c870ab3cd8ca57a2faaron /* get cache entry (or create one) */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* make a comparison to the cache */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding compare_nodep = util_ald_cache_fetch(curl->compare_cache,
return result;
return result;
return result;
(char *)dn,
(char *)attrib,
(char *)value);
failures++;
goto start_over;
failures++;
goto start_over;
if (curl) {
void *junk;
return LDAP_COMPARE_TRUE;
return LDAP_COMPARE_FALSE;
return LDAP_NO_SUCH_ATTRIBUTE;
return result;
const char *url,
const char *dn,
char **subgroupAttrs,
int failures = 0;
&ldap_module);
if (!subgroupAttrs) {
return res;
return res;
return res;
failures++;
goto start_over;
failures++;
goto start_over;
return res;
if (subgroupAttrs) {
char **values;
int val_index = 0;
if (values) {
val_index = 0;
tmp_sgcIndex = 0;
tmp_sgcIndex++;
val_index++;
indx++;
int sgindex;
char **group;
return res;
char **subgroupAttrs,
int cur_subgroup_depth,
int max_subgroup_depth)
&ldap_module);
return LDAP_COMPARE_FALSE;
return result;
sizeof(util_compare_subgroup_t));
if (!tmp_local_sgl) {
dn);
dn);
if (!tmp_local_sgl) {
if (sgl_copy) {
if (!tmp_local_sgl) {
return result;
sgindex++;
return result;
const char ***retvals)
int numvals = 0;
int result = 0;
char *dn;
int count;
int failures = 0;
&ldap_module);
&curnode);
if (curl) {
if (attrs) {
return LDAP_SUCCESS;
return result;
return result;
failures++;
goto start_over;
failures++;
goto start_over;
return result;
if (count == 0 )
return LDAP_NO_SUCH_OBJECT;
return LDAP_INVALID_CREDENTIALS;
failures++;
goto start_over;
return result;
if (attrs) {
while (attrs[k++]);
numvals = k;
while (attrs[i]) {
char **values;
if (curl) {
return LDAP_SUCCESS;
int numvals = 0;
int result = 0;
char *dn;
int count;
int failures = 0;
&ldap_module);
&curnode);
if (curl) {
if (attrs) {
return LDAP_SUCCESS;
return result;
return result;
failures++;
goto start_over;
return result;
if (count == 0 )
return LDAP_NO_SUCH_OBJECT;
if (attrs) {
while (attrs[k++]);
numvals = k;
while (attrs[i]) {
char **values;
if (curl) {
return LDAP_SUCCESS;
const char *bytes)
&ldap_module);
return err;
return NULL;
const char *file)
&ldap_module);
return err;
if (file) {
return NULL;
const char *ttl)
&ldap_module);
return err;
return NULL;
const char *size)
&ldap_module);
return err;
return NULL;
const char *ttl)
&ldap_module);
return err;
return NULL;
const char *size)
&ldap_module);
return err;
return NULL;
return APR_LDAP_CA_TYPE_DER;
return APR_LDAP_CA_TYPE_BASE64;
return APR_LDAP_CA_TYPE_CERT7_DB;
return APR_LDAP_CA_TYPE_SECMOD;
return APR_LDAP_CERT_TYPE_DER;
return APR_LDAP_CERT_TYPE_BASE64;
return APR_LDAP_CERT_TYPE_PFX;
return APR_LDAP_CERT_TYPE_KEY3_DB;
return APR_LDAP_CERT_TYPE_NICKNAME;
return APR_LDAP_KEY_TYPE_DER;
return APR_LDAP_KEY_TYPE_BASE64;
return APR_LDAP_KEY_TYPE_PFX;
return APR_LDAP_CA_TYPE_UNKNOWN;
void *dummy,
const char *type,
const char *file,
const char *password)
&ldap_module);
int cert_type = 0;
return err;
if (type) {
!= APR_SUCCESS))
return(NULL);
void *config,
const char *type,
const char *file,
const char *password)
int cert_type = 0;
if (type) {
type);
!= APR_SUCCESS))
return(NULL);
const char *mode)
&ldap_module);
mode);
return(NULL);
void *dummy,
int mode)
&ldap_module);
return err;
return(NULL);
void *dummy,
const char *ttl)
#ifdef LDAP_OPT_NETWORK_TIMEOUT
&ldap_module);
return err;
#ifdef LDAP_OPT_NETWORK_TIMEOUT
return NULL;
void *config,
int mode)
return(NULL);
void *config,
const char *arg) {
#ifdef AP_LDAP_OPT_DEBUG
&ldap_module);
return err;
#ifndef AP_LDAP_OPT_DEBUG
return NULL;
void *config,
const char *hop_limit)
return "LDAPReferralHopLimit must be greater than zero (Use 'LDAPReferrals Off' to disable referral chasing)";
return NULL;
return dc;
void *dummy,
const char *val)
long timeout;
char *endptr;
&ldap_module);
return err;
if (timeout < 0) {
if (timeout) {
timeout);
return NULL;
void *dummy,
const char *val)
&ldap_module);
if (timeout < 0) {
return NULL;
void *dummy,
const char *val)
&ldap_module);
return err;
if (timeout < 0) {
return NULL;
void *dummy,
const char *val)
&ldap_module);
return err;
return NULL;
#if APR_HAS_THREADS
return st;
void *overridesv)
#if APR_HAS_THREADS
return st;
return APR_SUCCESS;
APR_LOCK_DEFAULT, 0);
return result;
return OK;
&ldap_module);
int rc;
NULL);
return OK;
return DONE;
return result;
while (s_vhost) {
&ldap_module);
NULL,
&(result_err));
while (s_vhost) {
&ldap_module);
apr_ldap_rebind_init (p);
#ifdef AP_LDAP_OPT_DEBUG
return(OK);
&ldap_module);
{NULL}