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
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * http://www.apache.org/licenses/LICENSE-2.0
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
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.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh/*
e8f95a682820a599fe41b22977010636be5c2717jim * util_ldap.c: LDAP things
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh *
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh * Original code from auth_ldap module for Apache v1.3:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Copyright 1998, 1999 Enbridge Pipelines Inc.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Copyright 1999-2001 Dave Carrigan
1b21d7b3d97def358b2e923655edeb16613a1c31gstein */
1b21d7b3d97def358b2e923655edeb16613a1c31gstein
1b21d7b3d97def358b2e923655edeb16613a1c31gstein#include "httpd.h"
1b21d7b3d97def358b2e923655edeb16613a1c31gstein#include "http_config.h"
1b21d7b3d97def358b2e923655edeb16613a1c31gstein#include "http_core.h"
1b21d7b3d97def358b2e923655edeb16613a1c31gstein#include "http_log.h"
1b21d7b3d97def358b2e923655edeb16613a1c31gstein#include "http_protocol.h"
2d71630471d1c23f0137309e3c3957c633ecbfd6rbb#include "http_request.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "util_mutex.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "util_ldap.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "util_ldap_cache.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include <apr_strings.h>
51af95bb51b5084e883bad250b2afa2838e9ceebfielding
d4f1d9c1ff112a8ab9bee31f196973761329b236rbb#if APR_HAVE_UNISTD_H
7fae9cc4639013f3c04c085547256c68814aee8ftrawick#include <unistd.h>
7fae9cc4639013f3c04c085547256c68814aee8ftrawick#endif
7fae9cc4639013f3c04c085547256c68814aee8ftrawick
7fae9cc4639013f3c04c085547256c68814aee8ftrawick#if !APR_HAS_LDAP
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#error mod_ldap requires APR-util to have LDAP support built in
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#ifndef APR_LDAP_SIZELIMIT
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define APR_LDAP_SIZELIMIT -1
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#ifdef LDAP_OPT_DEBUG_LEVEL
785be1b6298010956622771c870ab3cd8ca57a2faaron#define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG_LEVEL
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#else
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#ifdef LDAP_OPT_DEBUG
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define AP_LDAP_HOPLIMIT_UNSET -1
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define AP_LDAP_CHASEREFERRALS_OFF 0
785be1b6298010956622771c870ab3cd8ca57a2faaron#define AP_LDAP_CHASEREFERRALS_ON 1
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#define AP_LDAP_CONNPOOL_DEFAULT -1
785be1b6298010956622771c870ab3cd8ca57a2faaron#define AP_LDAP_CONNPOOL_INFINITE -2
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingmodule AP_MODULE_DECLARE_DATA ldap_module;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *ldap_cache_mutex_type = "ldap-cache";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic apr_status_t uldap_connection_unbind(void *param);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define LDAP_CACHE_LOCK() do { \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (st->util_ldap_cache_lock) \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_global_mutex_lock(st->util_ldap_cache_lock); \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding} while (0)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define LDAP_CACHE_UNLOCK() do { \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (st->util_ldap_cache_lock) \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_global_mutex_unlock(st->util_ldap_cache_lock); \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding} while (0)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic void util_ldap_strdup (char **str, const char *newstr)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (*str) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding free(*str);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *str = NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (newstr) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *str = strdup(newstr);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Status Handler
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * --------------
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This handler generates a status page about the current performance of
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the LDAP cache. It is enabled as follows:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * <Location /ldap-status>
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * SetHandler ldap-status
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * </Location>
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int util_ldap_handler(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_state_t *st;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->allowed |= (1 << M_GET);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->method_number != M_GET) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return DECLINED;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (strcmp(r->handler, "ldap-status")) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return DECLINED;
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding st = (util_ldap_state_t *) ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding &ldap_module);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_set_content_type(r, "text/html; charset=ISO-8859-1");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->header_only)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return OK;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_rputs(DOCTYPE_HTML_3_2
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);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ald_cache_display(r, st);
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm
e8f95a682820a599fe41b22977010636be5c2717jim return OK;
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/* ------------------------------------------------------------------ */
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.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawickstatic void uldap_connection_close(util_ldap_connection_t *ldc)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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 */
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames if (!ldc->keep) {
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames uldap_connection_unbind(ldc);
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames ldc->r = NULL;
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames }
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames else {
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames /* mark our connection as available for reuse */
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames ldc->freed = apr_time_now();
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames ldc->r = NULL;
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick#if APR_HAS_THREADS
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick apr_thread_mutex_unlock(ldc->lock);
560f6ac786d611b858b2bad932713d9e971f0716trawick#endif
560f6ac786d611b858b2bad932713d9e971f0716trawick }
560f6ac786d611b858b2bad932713d9e971f0716trawick}
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/*
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.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick */
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawickstatic apr_status_t uldap_connection_unbind(void *param)
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick{
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick util_ldap_connection_t *ldc = param;
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (ldc) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (ldc->ldap) {
eae32ab3fb398ca408bc2d45b22adf1b67a75471rbb if (ldc->r) {
39b76a07959a0a332366c735a23894d9e8ed6872trawick ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp unbind", ldc);
de00ec7378227d05be63ecd2053ebbb01b940023jorton }
f3aa436e29aa30e29695a18b7f469dd66b39b7e4jorton ldap_unbind_s(ldc->ldap);
de00ec7378227d05be63ecd2053ebbb01b940023jorton ldc->ldap = NULL;
de00ec7378227d05be63ecd2053ebbb01b940023jorton }
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->bound = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick /* forget the rebind info for this conn */
39b76a07959a0a332366c735a23894d9e8ed6872trawick if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
de00ec7378227d05be63ecd2053ebbb01b940023jorton apr_ldap_rebind_remove(ldc->ldap);
f3aa436e29aa30e29695a18b7f469dd66b39b7e4jorton apr_pool_clear(ldc->rebind_pool);
de00ec7378227d05be63ecd2053ebbb01b940023jorton }
de00ec7378227d05be63ecd2053ebbb01b940023jorton }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron return APR_SUCCESS;
066877f1a045103acfdd376d48cdd473c33f409bdougm}
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/* not presently used, not part of the API */
f3aa436e29aa30e29695a18b7f469dd66b39b7e4jorton#if 0
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid/*
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 *
97c78987224dcd037076d393aad1867c26b2c8cftrawick * The caller should hold the lock for this connection
97c78987224dcd037076d393aad1867c26b2c8cftrawick */
97c78987224dcd037076d393aad1867c26b2c8cftrawickstatic apr_status_t util_ldap_connection_remove (void *param) {
97c78987224dcd037076d393aad1867c26b2c8cftrawick util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
97c78987224dcd037076d393aad1867c26b2c8cftrawick util_ldap_state_t *st;
97c78987224dcd037076d393aad1867c26b2c8cftrawick
97c78987224dcd037076d393aad1867c26b2c8cftrawick if (!ldc) return APR_SUCCESS;
97c78987224dcd037076d393aad1867c26b2c8cftrawick
97c78987224dcd037076d393aad1867c26b2c8cftrawick st = ldc->st;
97c78987224dcd037076d393aad1867c26b2c8cftrawick
97c78987224dcd037076d393aad1867c26b2c8cftrawick uldap_connection_unbind(ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#if APR_HAS_THREADS
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_lock(st->mutex);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Remove ldc from the list */
785be1b6298010956622771c870ab3cd8ca57a2faaron for (l=st->connections; l; l=l->next) {
785be1b6298010956622771c870ab3cd8ca57a2faaron if (l == ldc) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (prev) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding prev->next = l->next;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding st->connections = l->next;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding break;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron prev = l;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if (ldc->bindpw) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding free((void*)ldc->bindpw);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (ldc->binddn) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding free((void*)ldc->binddn);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#if APR_HAS_THREADS
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_thread_mutex_unlock(ldc->lock);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_unlock(st->mutex);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Destory the pool associated with this connection */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_pool_destroy(ldc->pool);
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return APR_SUCCESS;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int uldap_connection_init(request_rec *r,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_connection_t *ldc)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton int rc = 0, ldap_option = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int version = LDAP_VERSION3;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_ldap_err_t *result = NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#ifdef LDAP_OPT_NETWORK_TIMEOUT
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding struct timeval connectionTimeout = {0};
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_state_t *st =
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding &ldap_module);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int have_client_certs = !apr_is_empty_array(ldc->client_certs);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#if !APR_HAS_SOLARIS_LDAPSDK
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Normally we enable SSL/TLS with apr_ldap_set_option(), except
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * with Solaris LDAP, where this is broken.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int secure = APR_LDAP_NONE;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#else
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /*
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.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
785be1b6298010956622771c870ab3cd8ca57a2faaron * Just in case client certificates ever get supported, we
785be1b6298010956622771c870ab3cd8ca57a2faaron * handle those as with the other LDAP SDKs.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron int secure = have_client_certs ? APR_LDAP_NONE : ldc->secure;
785be1b6298010956622771c870ab3cd8ca57a2faaron#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron
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.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_ldap_init(r->pool, &(ldc->ldap),
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->host,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding secure, &(result));
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if (NULL == result) {
785be1b6298010956622771c870ab3cd8ca57a2faaron /* something really bad happened */
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->bound = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (NULL == ldc->reason) {
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->reason = "LDAP: ldap initialization failed";
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron return(APR_EGENERAL);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm
785be1b6298010956622771c870ab3cd8ca57a2faaron if (result->rc) {
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick ldc->reason = result->reason;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->bound = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return result->rc;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (NULL == ldc->ldap)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->bound = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (NULL == ldc->reason) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "LDAP: ldap initialization failed";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = result->reason;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return(result->rc);
a7ed9c525f9460187f327cea953bf90ecf1bdc51gstein }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp init", ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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 if (rc != APR_SUCCESS) {
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 uldap_connection_unbind(ldc);
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick return(rc);
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick }
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick }
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick /* always default to LDAP V3 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm
785be1b6298010956622771c870ab3cd8ca57a2faaron /* set client certificates */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (have_client_certs) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->client_certs, &(result));
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm if (LDAP_SUCCESS != result->rc) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uldap_connection_unbind( ldc );
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = result->reason;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return(result->rc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* switch on SSL/TLS */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (APR_LDAP_NONE != ldc->secure
785be1b6298010956622771c870ab3cd8ca57a2faaron#if APR_HAS_SOLARIS_LDAPSDK
785be1b6298010956622771c870ab3cd8ca57a2faaron /* See comments near apr_ldap_init() above */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding && have_client_certs
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ) {
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm apr_ldap_set_option(r->pool, ldc->ldap,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding APR_LDAP_OPT_TLS, &ldc->secure, &(result));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (LDAP_SUCCESS != result->rc) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uldap_connection_unbind( ldc );
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = result->reason;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return(result->rc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
a7ed9c525f9460187f327cea953bf90ecf1bdc51gstein
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Set the alias dereferencing option */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldap_option = ldc->deref;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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"));
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick apr_ldap_set_option(r->pool, ldc->ldap,
39b76a07959a0a332366c735a23894d9e8ed6872trawick APR_LDAP_OPT_REFERRALS,
8fd7c5046d164fb0959222497e5925dfc6a52ff3trawick (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick LDAP_OPT_ON : LDAP_OPT_OFF),
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick &(result));
e8f95a682820a599fe41b22977010636be5c2717jim if (result->rc != LDAP_SUCCESS) {
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 result->rc);
8f8ec0957334f50b7ac11359f90490ee467258eedreid result->reason = "Unable to set LDAP_OPT_REFERRALS.";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = result->reason;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uldap_connection_unbind(ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return(result->rc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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 ldc->ReferralHopLimit);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_ldap_set_option(r->pool, ldc->ldap,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding APR_LDAP_OPT_REFHOPLIMIT,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (void *)&ldc->ReferralHopLimit,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding &(result));
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (result->rc != LDAP_SUCCESS) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01281)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ldc->ReferralHopLimit,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding result->rc);
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = result->reason;
39b76a07959a0a332366c735a23894d9e8ed6872trawick uldap_connection_unbind(ldc);
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick return(result->rc);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
39b76a07959a0a332366c735a23894d9e8ed6872trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick/*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
785be1b6298010956622771c870ab3cd8ca57a2faaron#ifdef APR_LDAP_OPT_VERIFY_CERT
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf &(st->verify_svr_cert), &(result));
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf#else
785be1b6298010956622771c870ab3cd8ca57a2faaron#if defined(LDAPSSL_VERIFY_SERVER)
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (st->verify_svr_cert) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf else {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron#elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
785be1b6298010956622771c870ab3cd8ca57a2faaron /* This is not a per-connection setting so just pass NULL for the
785be1b6298010956622771c870ab3cd8ca57a2faaron Ldap connection handle */
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (st->verify_svr_cert) {
066877f1a045103acfdd376d48cdd473c33f409bdougm int i = LDAP_OPT_X_TLS_DEMAND;
785be1b6298010956622771c870ab3cd8ca57a2faaron result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron else {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf int i = LDAP_OPT_X_TLS_NEVER;
785be1b6298010956622771c870ab3cd8ca57a2faaron result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#ifdef LDAP_OPT_NETWORK_TIMEOUT
785be1b6298010956622771c870ab3cd8ca57a2faaron if (st->connectionTimeout > 0) {
785be1b6298010956622771c870ab3cd8ca57a2faaron connectionTimeout.tv_sec = st->connectionTimeout;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (connectionTimeout.tv_sec > 0) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf (void *)&connectionTimeout, &(result));
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (APR_SUCCESS != rc) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01282)
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf "LDAP: Could not set the connection timeout");
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
066877f1a045103acfdd376d48cdd473c33f409bdougm }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#ifdef LDAP_OPT_TIMEOUT
785be1b6298010956622771c870ab3cd8ca57a2faaron /*
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 */
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (st->opTimeout) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_TIMEOUT,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf st->opTimeout, &(result));
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (APR_SUCCESS != rc) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01283)
785be1b6298010956622771c870ab3cd8ca57a2faaron "LDAP: Could not set LDAP_OPT_TIMEOUT");
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf#endif
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return(rc);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf}
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanfstatic int uldap_ld_errno(util_ldap_connection_t *ldc)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
785be1b6298010956622771c870ab3cd8ca57a2faaron int ldaprc;
785be1b6298010956622771c870ab3cd8ca57a2faaron#ifdef LDAP_OPT_ERROR_NUMBER
785be1b6298010956622771c870ab3cd8ca57a2faaron if (LDAP_SUCCESS == ldap_get_option(ldc->ldap, LDAP_OPT_ERROR_NUMBER, &ldaprc)) return ldaprc;
785be1b6298010956622771c870ab3cd8ca57a2faaron#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron#ifdef LDAP_OPT_RESULT_CODE
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick if (LDAP_SUCCESS == ldap_get_option(ldc->ldap, LDAP_OPT_RESULT_CODE, &ldaprc)) return ldaprc;
785be1b6298010956622771c870ab3cd8ca57a2faaron#endif
785be1b6298010956622771c870ab3cd8ca57a2faaron return LDAP_OTHER;
785be1b6298010956622771c870ab3cd8ca57a2faaron}
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron/*
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().
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf *
785be1b6298010956622771c870ab3cd8ca57a2faaron * Returns LDAP_SUCCESS on success; and an error code on failure
785be1b6298010956622771c870ab3cd8ca57a2faaron */
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanfstatic int uldap_simple_bind(util_ldap_connection_t *ldc, char *binddn,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf char* bindpw, struct timeval *timeout)
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf{
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf LDAPMessage *result;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf int rc;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf int msgid = ldap_simple_bind(ldc->ldap, binddn, bindpw);
785be1b6298010956622771c870ab3cd8ca57a2faaron if (msgid == -1) {
e8f95a682820a599fe41b22977010636be5c2717jim ldc->reason = "LDAP: ldap_simple_bind() failed";
785be1b6298010956622771c870ab3cd8ca57a2faaron return uldap_ld_errno(ldc);
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick }
785be1b6298010956622771c870ab3cd8ca57a2faaron rc = ldap_result(ldc->ldap, msgid, 0, timeout, &result);
785be1b6298010956622771c870ab3cd8ca57a2faaron if (rc == -1) {
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->reason = "LDAP: ldap_simple_bind() result retrieval failed";
785be1b6298010956622771c870ab3cd8ca57a2faaron /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
785be1b6298010956622771c870ab3cd8ca57a2faaron return uldap_ld_errno(ldc);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron else if (rc == 0) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "LDAP: ldap_simple_bind() timed out";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding rc = LDAP_TIMEOUT;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding } else if (ldap_parse_result(ldc->ldap, result, &rc, NULL, NULL, NULL,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding NULL, 1) == -1) {
ebc18d48bea83ee5ed7a1b4e30007e5192539829wrowe ldc->reason = "LDAP: ldap_simple_bind() parse result failed";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return uldap_ld_errno(ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp bind", ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return rc;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*
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 *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Returns LDAP_SUCCESS on success; and an error code on failure
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic int uldap_connection_open(request_rec *r,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_connection_t *ldc)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int rc = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int failures = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron int new_connection = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_state_t *st;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton /* sanity check for NULL */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!ldc) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return -1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
5f08a022a210f4e511561e89f500621a15e6177dtrawick /* If the connection is already bound, return
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (ldc->bound && !ldc->must_rebind)
785be1b6298010956622771c870ab3cd8ca57a2faaron {
e8f95a682820a599fe41b22977010636be5c2717jim ldc->reason = "LDAP: connection open successful (already bound)";
5f08a022a210f4e511561e89f500621a15e6177dtrawick return LDAP_SUCCESS;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* create the ldap session handle
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (NULL == ldc->ldap)
785be1b6298010956622771c870ab3cd8ca57a2faaron {
785be1b6298010956622771c870ab3cd8ca57a2faaron new_connection = 1;
785be1b6298010956622771c870ab3cd8ca57a2faaron rc = uldap_connection_init( r, ldc );
785be1b6298010956622771c870ab3cd8ca57a2faaron if (LDAP_SUCCESS != rc)
785be1b6298010956622771c870ab3cd8ca57a2faaron {
785be1b6298010956622771c870ab3cd8ca57a2faaron return rc;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton &ldap_module);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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 *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * On Success or any other error, break out of the loop.
785be1b6298010956622771c870ab3cd8ca57a2faaron *
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.
900127764fb985c340ee4979cac97146a330c694trawick */
900127764fb985c340ee4979cac97146a330c694trawick
900127764fb985c340ee4979cac97146a330c694trawick while (failures <= st->retries) {
785be1b6298010956622771c870ab3cd8ca57a2faaron if (failures > 0 && st->retry_delay > 0) {
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_sleep(st->retry_delay);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
785be1b6298010956622771c870ab3cd8ca57a2faaron st->opTimeout);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if (rc == LDAP_SUCCESS) break;
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron failures++;
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if (AP_LDAP_IS_SERVER_DOWN(rc)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
785be1b6298010956622771c870ab3cd8ca57a2faaron "ldap_simple_bind() failed with server down "
785be1b6298010956622771c870ab3cd8ca57a2faaron "(try %d)", failures);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron else if (rc == LDAP_TIMEOUT) {
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 new_connection ? "new" : "reused");
785be1b6298010956622771c870ab3cd8ca57a2faaron if (new_connection) break;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron else {
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Other errors not retryable */
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick
785be1b6298010956622771c870ab3cd8ca57a2faaron if (!(failures % 2)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
785be1b6298010956622771c870ab3cd8ca57a2faaron "attempt to re-init the connection");
785be1b6298010956622771c870ab3cd8ca57a2faaron uldap_connection_unbind(ldc);
785be1b6298010956622771c870ab3cd8ca57a2faaron if (LDAP_SUCCESS != uldap_connection_init(r, ldc)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron /* leave rc as the initial bind return code */
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* free the handle if there was an error
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (LDAP_SUCCESS != rc)
785be1b6298010956622771c870ab3cd8ca57a2faaron {
785be1b6298010956622771c870ab3cd8ca57a2faaron uldap_connection_unbind(ldc);
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->reason = "LDAP: ldap_simple_bind() failed";
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron else {
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->bound = 1;
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->must_rebind = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron ldc->reason = "LDAP: connection open successful";
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron return(rc);
785be1b6298010956622771c870ab3cd8ca57a2faaron}
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron/*
785be1b6298010956622771c870ab3cd8ca57a2faaron * Compare client certificate arrays.
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Returns 1 on compare failure, 0 otherwise.
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick */
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawickstatic int compare_client_certs(apr_array_header_t *srcs,
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick apr_array_header_t *dests)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
785be1b6298010956622771c870ab3cd8ca57a2faaron int i = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron struct apr_ldap_opt_tls_cert_t *src, *dest;
785be1b6298010956622771c870ab3cd8ca57a2faaron
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick /* arrays both NULL? if so, then equal */
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (srcs == NULL && dests == NULL) {
066877f1a045103acfdd376d48cdd473c33f409bdougm return 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick /* arrays different length or either NULL? If so, then not equal */
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick return 1;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* run an actual comparison */
785be1b6298010956622771c870ab3cd8ca57a2faaron src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
785be1b6298010956622771c870ab3cd8ca57a2faaron dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding for (i = 0; i < srcs->nelts; i++) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((strcmp(src[i].path, dest[i].path)) ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (src[i].type != dest[i].type) ||
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 &&
785be1b6298010956622771c870ab3cd8ca57a2faaron strcmp(src[i].password, dest[i].password))) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 1;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* if we got here, the cert arrays were identical */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
bbbf8f0e622ad5a37ccf70f35660fc755575278arbb
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm}
2659802f083b95856f571b49e4ec087838b53039rederpj
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Find an existing ldap connection struct that matches the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * provided ldap connection parameters.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
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.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanfstatic util_ldap_connection_t *
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf uldap_connection_find(request_rec *r,
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf const char *host, int port,
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf const char *binddn, const char *bindpw,
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf deref_options deref, int secure)
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf{
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf int secureflag = secure;
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf apr_time_t now = apr_time_now();
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf util_ldap_state_t *st =
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding &ldap_module);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_config_t *dc =
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf#if APR_HAS_THREADS
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick /* mutex lock this function */
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick apr_thread_mutex_lock(st->mutex);
4f133508c93204c06e1acba9774ff184e5812606niq#endif
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz if (secure < APR_LDAP_NONE) {
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz secureflag = st->secure;
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz }
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz /* Search for an exact connection match in the list that is not
4f133508c93204c06e1acba9774ff184e5812606niq * being used.
4f133508c93204c06e1acba9774ff184e5812606niq */
4f133508c93204c06e1acba9774ff184e5812606niq for (l=st->connections,p=NULL; l; l=l->next) {
4f133508c93204c06e1acba9774ff184e5812606niq#if APR_HAS_THREADS
4f133508c93204c06e1acba9774ff184e5812606niq if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
4f133508c93204c06e1acba9774ff184e5812606niq#endif
4f133508c93204c06e1acba9774ff184e5812606niq if ( (l->port == port) && (strcmp(l->host, host) == 0)
4f133508c93204c06e1acba9774ff184e5812606niq && ((!l->binddn && !binddn) || (l->binddn && binddn
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick && !strcmp(l->binddn, binddn)))
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick && !strcmp(l->bindpw, bindpw)))
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding && (l->deref == deref) && (l->secure == secureflag)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding && !compare_client_certs(dc->client_certs, l->client_certs))
4f133508c93204c06e1acba9774ff184e5812606niq {
644be6f54749d2d9950d2c4d2ac448f7af016d26martin if (st->connection_pool_ttl > 0) {
43c3e6a4b559b76b750c245ee95e2782c15b4296jim if (l->bound && (now - l->freed) > st->connection_pool_ttl) {
644be6f54749d2d9950d2c4d2ac448f7af016d26martin ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
43c3e6a4b559b76b750c245ee95e2782c15b4296jim "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
43c3e6a4b559b76b750c245ee95e2782c15b4296jim (now - l->freed) / APR_USEC_PER_SEC);
644be6f54749d2d9950d2c4d2ac448f7af016d26martin l->r = r;
43c3e6a4b559b76b750c245ee95e2782c15b4296jim uldap_connection_unbind(l);
644be6f54749d2d9950d2c4d2ac448f7af016d26martin /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick }
e8f95a682820a599fe41b22977010636be5c2717jim ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r,
e8f95a682820a599fe41b22977010636be5c2717jim "Reuse %s LDC %pp",
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick l->bound ? "bound" : "unbound", l);
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick }
71715c646d5231de578431f8961e711764b899d3fanf break;
3c8b3749225668f06abbb2b023a833a2cef46931brianp }
3c8b3749225668f06abbb2b023a833a2cef46931brianp#if APR_HAS_THREADS
3c8b3749225668f06abbb2b023a833a2cef46931brianp /* If this connection didn't match the criteria, then we
3c8b3749225668f06abbb2b023a833a2cef46931brianp * need to unlock the mutex so it is available to be reused.
ccad4fdc6c75a352157a413bd3bbaf4d0c8a6f72brianp */
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_thread_mutex_unlock(l->lock);
ccad4fdc6c75a352157a413bd3bbaf4d0c8a6f72brianp }
785be1b6298010956622771c870ab3cd8ca57a2faaron#endif
3c8b3749225668f06abbb2b023a833a2cef46931brianp p = l;
3c8b3749225668f06abbb2b023a833a2cef46931brianp }
3c8b3749225668f06abbb2b023a833a2cef46931brianp
785be1b6298010956622771c870ab3cd8ca57a2faaron /* If nothing found, search again, but we don't care about the
785be1b6298010956622771c870ab3cd8ca57a2faaron * binddn and bindpw this time.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick if (!l) {
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick for (l=st->connections,p=NULL; l; l=l->next) {
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick#if APR_HAS_THREADS
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((l->port == port) && (strcmp(l->host, host) == 0) &&
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf (l->deref == deref) && (l->secure == secureflag) &&
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf !compare_client_certs(dc->client_certs, l->client_certs))
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf {
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf if (st->connection_pool_ttl > 0) {
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick if (l->bound && (now - l->freed) > st->connection_pool_ttl) {
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (now - l->freed) / APR_USEC_PER_SEC);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->r = r;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uldap_connection_unbind(l);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "Reuse %s LDC %pp (will rebind)",
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm l->bound ? "bound" : "unbound", l);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* the bind credentials have changed */
785be1b6298010956622771c870ab3cd8ca57a2faaron l->must_rebind = 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_strdup((char**)&(l->binddn), binddn);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_strdup((char**)&(l->bindpw), bindpw);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding break;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
785be1b6298010956622771c870ab3cd8ca57a2faaron#if APR_HAS_THREADS
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 */
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_thread_mutex_unlock(l->lock);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding p = l;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron/* artificially disable cache */
785be1b6298010956622771c870ab3cd8ca57a2faaron/* l = NULL; */
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* If no connection was found after the second search, we
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * must create one.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!l) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_pool_t *newpool;
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#if APR_HAS_THREADS
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_unlock(st->mutex);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
3d96ee83babeec32482c9082c9426340cee8c44dwrowe
785be1b6298010956622771c870ab3cd8ca57a2faaron /*
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 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* create the details of this connection in the new pool */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->pool = newpool;
e8f95a682820a599fe41b22977010636be5c2717jim l->st = st;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#if APR_HAS_THREADS
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_lock(l->lock);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->bound = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->host = apr_pstrdup(l->pool, host);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->port = port;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->deref = deref;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_strdup((char**)&(l->binddn), binddn);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_ldap_strdup((char**)&(l->bindpw), bindpw);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->ChaseReferrals = dc->ChaseReferrals;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->ReferralHopLimit = dc->ReferralHopLimit;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->secure = secureflag;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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);
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* whether or not to keep this connection in the pool when it's returned */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->keep = (st->connection_pool_ttl == 0) ? 0 : 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
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");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#if APR_HAS_THREADS
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_unlock(st->mutex);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (p) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding p->next = l;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding st->connections = l;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
dad234382d8424e1c5a30af2838e172aec9d6d1bdreid#if APR_HAS_THREADS
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_thread_mutex_unlock(st->mutex);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding l->r = r;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return l;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb
c1b808d160bfb5c849263be8d4acff600853a328trawick/* ------------------------------------------------------------------ */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*
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 *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * The lock for the ldap cache should already be acquired.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *url, const char *dn,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *reqdn, int compare_dn_on_server)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
785be1b6298010956622771c870ab3cd8ca57a2faaron int result = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron util_url_node_t *curl;
785be1b6298010956622771c870ab3cd8ca57a2faaron util_url_node_t curnode;
785be1b6298010956622771c870ab3cd8ca57a2faaron util_dn_compare_node_t *node;
785be1b6298010956622771c870ab3cd8ca57a2faaron util_dn_compare_node_t newnode;
785be1b6298010956622771c870ab3cd8ca57a2faaron int failures = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding LDAPMessage *res, *entry;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char *searchdn;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
785be1b6298010956622771c870ab3cd8ca57a2faaron util_ldap_state_t *st = (util_ldap_state_t *)
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_get_module_config(r->server->module_config,
785be1b6298010956622771c870ab3cd8ca57a2faaron &ldap_module);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* get cache entry (or create one) */
785be1b6298010956622771c870ab3cd8ca57a2faaron LDAP_CACHE_LOCK();
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron curnode.url = url;
785be1b6298010956622771c870ab3cd8ca57a2faaron curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
785be1b6298010956622771c870ab3cd8ca57a2faaron if (curl == NULL) {
785be1b6298010956622771c870ab3cd8ca57a2faaron curl = util_ald_create_caches(st, url);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron LDAP_CACHE_UNLOCK();
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* a simple compare? */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (!compare_dn_on_server) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* unlock this read lock */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (strcmp(dn, reqdn)) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison FALSE (direct strcmp())";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return LDAP_COMPARE_FALSE;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
51af95bb51b5084e883bad250b2afa2838e9ceebfielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison TRUE (direct strcmp())";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return LDAP_COMPARE_TRUE;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (curl) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* no - it's a server side compare */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding LDAP_CACHE_LOCK();
dad234382d8424e1c5a30af2838e172aec9d6d1bdreid
382fa07a63096c4a1aabfed36433ea5ac9c40ad0trawick /* is it in the compare cache? */
c1b808d160bfb5c849263be8d4acff600853a328trawick newnode.reqdn = (char *)reqdn;
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (node != NULL) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* If it's in the cache, it's good */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* unlock this read lock */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding LDAP_CACHE_UNLOCK();
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison TRUE (cached)";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return LDAP_COMPARE_TRUE;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* unlock this read lock */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding LDAP_CACHE_UNLOCK();
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaronstart_over:
785be1b6298010956622771c870ab3cd8ca57a2faaron if (failures > st->retries) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return result;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (failures > 0 && st->retry_delay > 0) {
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_sleep(st->retry_delay);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* make a server connection */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* connect to server failed */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return result;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
51af95bb51b5084e883bad250b2afa2838e9ceebfielding /* search for reqdn */
785be1b6298010956622771c870ab3cd8ca57a2faaron result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
785be1b6298010956622771c870ab3cd8ca57a2faaron "(objectclass=*)", NULL, 1,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (AP_LDAP_IS_SERVER_DOWN(result))
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison ldap_search_ext_s() "
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard "failed with server down";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uldap_connection_unbind(ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding failures++;
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
785be1b6298010956622771c870ab3cd8ca57a2faaron goto start_over;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron if (result == LDAP_TIMEOUT && failures == 0) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /*
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 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ldc->reason = "DN Comparison ldap_search_ext_s() "
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "failed with timeout";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uldap_connection_unbind(ldc);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding failures++;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding goto start_over;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (result != LDAP_SUCCESS) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* search for reqdn failed - no match */
e8f95a682820a599fe41b22977010636be5c2717jim ldc->reason = "DN Comparison ldap_search_ext_s() failed";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return result;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna entry = ldap_first_entry(ldc->ldap, res);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna searchdn = ldap_get_dn(ldc->ldap, entry);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ldap_msgfree(res);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (strcmp(dn, searchdn) != 0) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* compare unsuccessful */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ldc->reason = "DN Comparison FALSE (checked on server)";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna result = LDAP_COMPARE_FALSE;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna else {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (curl) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* compare successful - add to the compare cache */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna LDAP_CACHE_LOCK();
e8f95a682820a599fe41b22977010636be5c2717jim newnode.reqdn = (char *)reqdn;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna newnode.dn = (char *)dn;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if ( (node == NULL)
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna || (strcmp(reqdn, node->reqdn) != 0)
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna || (strcmp(dn, node->dn) != 0))
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna util_ald_cache_insert(curl->dn_compare_cache, &newnode);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna LDAP_CACHE_UNLOCK();
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ldc->reason = "DN Comparison TRUE (checked on server)";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna result = LDAP_COMPARE_TRUE;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ldap_memfree(searchdn);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return result;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna}
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna/*
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
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna *
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernastatic int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna const char *url, const char *dn,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *attrib, const char *value)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int result = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_url_node_t *curl;
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard util_url_node_t curnode;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_compare_node_t *compare_nodep;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding util_compare_node_t the_compare_node;
8f8ec0957334f50b7ac11359f90490ee467258eedreid apr_time_t curtime = 0; /* silence gcc -Wall */
382fa07a63096c4a1aabfed36433ea5ac9c40ad0trawick int failures = 0;
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick util_ldap_state_t *st = (util_ldap_state_t *)
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding &ldap_module);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* get cache entry (or create one) */
785be1b6298010956622771c870ab3cd8ca57a2faaron LDAP_CACHE_LOCK();
785be1b6298010956622771c870ab3cd8ca57a2faaron curnode.url = url;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (curl == NULL) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf curl = util_ald_create_caches(st, url);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding LDAP_CACHE_UNLOCK();
c1b808d160bfb5c849263be8d4acff600853a328trawick
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb if (curl) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* make a comparison to the cache */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding LDAP_CACHE_LOCK();
785be1b6298010956622771c870ab3cd8ca57a2faaron curtime = apr_time_now();
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron the_compare_node.dn = (char *)dn;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding the_compare_node.attrib = (char *)attrib;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding the_compare_node.value = (char *)value;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf the_compare_node.result = 0;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf the_compare_node.sgl_processed = 0;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf the_compare_node.subgroupList = NULL;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding compare_nodep = util_ald_cache_fetch(curl->compare_cache,
&the_compare_node);
if (compare_nodep != NULL) {
/* found it... */
if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
/* ...but it is too old */
util_ald_cache_remove(curl->compare_cache, compare_nodep);
}
else {
/* ...and it is good */
if (LDAP_COMPARE_TRUE == compare_nodep->result) {
ldc->reason = "Comparison true (cached)";
}
else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
ldc->reason = "Comparison false (cached)";
}
else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
ldc->reason = "Comparison no such attribute (cached)";
}
else {
ldc->reason = "Comparison undefined (cached)";
}
/* record the result code to return with the reason... */
result = compare_nodep->result;
/* and unlock this read lock */
LDAP_CACHE_UNLOCK();
return result;
}
}
/* unlock this read lock */
LDAP_CACHE_UNLOCK();
}
start_over:
if (failures > st->retries) {
return result;
}
if (failures > 0 && st->retry_delay > 0) {
apr_sleep(st->retry_delay);
}
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
/* uldap_connection_open() retried already */
return result;
}
result = ldap_compare_s(ldc->ldap,
(char *)dn,
(char *)attrib,
(char *)value);
if (AP_LDAP_IS_SERVER_DOWN(result)) {
/* connection failed - try again */
ldc->reason = "ldap_compare_s() failed with server down";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
if (result == LDAP_TIMEOUT && failures == 0) {
/*
* we are reusing a connection that doesn't seem to be active anymore
* (firewall state drop?), let's try a new connection.
*/
ldc->reason = "ldap_compare_s() failed with timeout";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
ldc->reason = "Comparison complete";
if ((LDAP_COMPARE_TRUE == result) ||
(LDAP_COMPARE_FALSE == result) ||
(LDAP_NO_SUCH_ATTRIBUTE == result)) {
if (curl) {
/* compare completed; caching result */
LDAP_CACHE_LOCK();
the_compare_node.lastcompare = curtime;
the_compare_node.result = result;
the_compare_node.sgl_processed = 0;
the_compare_node.subgroupList = NULL;
/* If the node doesn't exist then insert it, otherwise just update
* it with the last results
*/
compare_nodep = util_ald_cache_fetch(curl->compare_cache,
&the_compare_node);
if ( (compare_nodep == NULL)
|| (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
|| (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
|| (strcmp(the_compare_node.value, compare_nodep->value) != 0))
{
void *junk;
junk = util_ald_cache_insert(curl->compare_cache,
&the_compare_node);
if (junk == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01287)
"cache_compare: Cache insertion failure.");
}
}
else {
compare_nodep->lastcompare = curtime;
compare_nodep->result = result;
}
LDAP_CACHE_UNLOCK();
}
if (LDAP_COMPARE_TRUE == result) {
ldc->reason = "Comparison true (adding to cache)";
return LDAP_COMPARE_TRUE;
}
else if (LDAP_COMPARE_FALSE == result) {
ldc->reason = "Comparison false (adding to cache)";
return LDAP_COMPARE_FALSE;
}
else {
ldc->reason = "Comparison no such attribute (adding to cache)";
return LDAP_NO_SUCH_ATTRIBUTE;
}
}
return result;
}
static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
util_ldap_connection_t *ldc,
const char *url,
const char *dn,
char **subgroupAttrs,
apr_array_header_t *subgroupclasses)
{
int failures = 0;
int result = LDAP_COMPARE_FALSE;
util_compare_subgroup_t *res = NULL;
LDAPMessage *sga_res, *entry;
struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
util_ldap_state_t *st = (util_ldap_state_t *)
ap_get_module_config(r->server->module_config,
&ldap_module);
sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
if (!subgroupAttrs) {
return res;
}
start_over:
/*
* 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
*/
if (failures > st->retries) {
return res;
}
if (failures > 0 && st->retry_delay > 0) {
apr_sleep(st->retry_delay);
}
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
/* uldap_connection_open() retried already */
return res;
}
/* try to do the search */
result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
(char *)"cn=*", subgroupAttrs, 0,
NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
if (AP_LDAP_IS_SERVER_DOWN(result)) {
ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
" down";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
if (result == LDAP_TIMEOUT && failures == 0) {
/*
* we are reusing a connection that doesn't seem to be active anymore
* (firewall state drop?), let's try a new connection.
*/
ldc->reason = "ldap_search_ext_s() for subgroups failed with timeout";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
/* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
if (result != LDAP_SUCCESS) {
ldc->reason = "ldap_search_ext_s() for subgroups failed";
return res;
}
entry = ldap_first_entry(ldc->ldap, sga_res);
/*
* Get values for the provided sub-group attributes.
*/
if (subgroupAttrs) {
int indx = 0, tmp_sgcIndex;
while (subgroupAttrs[indx]) {
char **values;
int val_index = 0;
/* Get *all* matching "member" values from this group. */
values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
if (values) {
val_index = 0;
/*
* Now we are going to pare the subgroup members of this group
* to *just* the subgroups, add them to the compare_nodep, and
* then proceed to check the new level of subgroups.
*/
while (values[val_index]) {
/* Check if this entry really is a group. */
tmp_sgcIndex = 0;
result = LDAP_COMPARE_FALSE;
while ((tmp_sgcIndex < subgroupclasses->nelts)
&& (result != LDAP_COMPARE_TRUE)) {
result = uldap_cache_compare(r, ldc, url,
values[val_index],
"objectClass",
sgc_ents[tmp_sgcIndex].name
);
if (result != LDAP_COMPARE_TRUE) {
tmp_sgcIndex++;
}
}
/* It's a group, so add it to the array. */
if (result == LDAP_COMPARE_TRUE) {
char **newgrp = (char **) apr_array_push(subgroups);
*newgrp = apr_pstrdup(r->pool, values[val_index]);
}
val_index++;
}
ldap_value_free(values);
}
indx++;
}
}
ldap_msgfree(sga_res);
if (subgroups->nelts > 0) {
/* We need to fill in tmp_local_subgroups using the data from LDAP */
int sgindex;
char **group;
res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
res->subgroupDNs = apr_palloc(r->pool,
sizeof(char *) * (subgroups->nelts));
for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
}
res->len = sgindex;
}
return res;
}
/*
* Does a recursive lookup operation to try to find a user within (cached)
* nested groups. It accepts a cache that it will use to lookup previous
* compare attempts. We cache two kinds of compares (require group compares)
* and (require user compares). Each compare has a different cache node:
* require group includes the DN; require user does not because the require
* user cache is owned by the
*
* DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
*
*
* 1. Call uldap_cache_compare for each subgroupclass value to check the
* generic, user-agnostic, cached group entry. This will create a new generic
* cache entry if there
* wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
* have no groups.
* 2. Lock The cache and get the generic cache entry.
* 3. Check if there is already a subgrouplist in this generic group's cache
* entry.
* A. If there is, go to step 4.
* B. If there isn't:
* i) Use ldap_search to get the full list
* of subgroup "members" (which may include non-group "members").
* ii) Use uldap_cache_compare to strip the list down to just groups.
* iii) Lock and add this stripped down list to the cache of the generic
* group.
* 4. Loop through the sgl and call uldap_cache_compare (using the user info)
* for each
* subgroup to see if the subgroup contains the user and to get the subgroups
* added to the
* cache (with user-afinity, if they aren't already there).
* A. If the user is in the subgroup, then we'll be returning
* LDAP_COMPARE_TRUE.
* B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
* uldap_cache_compare) then recursively call this function to get the
* sub-subgroups added...
* 5. Cleanup local allocations.
* 6. Return the final result.
*/
static int uldap_cache_check_subgroups(request_rec *r,
util_ldap_connection_t *ldc,
const char *url, const char *dn,
const char *attrib, const char *value,
char **subgroupAttrs,
apr_array_header_t *subgroupclasses,
int cur_subgroup_depth,
int max_subgroup_depth)
{
int result = LDAP_COMPARE_FALSE;
util_url_node_t *curl;
util_url_node_t curnode;
util_compare_node_t *compare_nodep;
util_compare_node_t the_compare_node;
util_compare_subgroup_t *tmp_local_sgl = NULL;
int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
(struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
util_ldap_state_t *st = (util_ldap_state_t *)
ap_get_module_config(r->server->module_config,
&ldap_module);
/*
* Stop looking at deeper levels of nested groups if we have reached the
* max. Since we already checked the top-level group in uldap_cache_compare,
* we don't need to check it again here - so if max_subgroup_depth is set
* to 0, we won't check it (i.e. that is why we check < rather than <=).
* We'll be calling uldap_cache_compare from here to check if the user is
* in the next level before we recurse into that next level looking for
* more subgroups.
*/
if (cur_subgroup_depth >= max_subgroup_depth) {
return LDAP_COMPARE_FALSE;
}
/*
* 1. Check the "groupiness" of the specified basedn. Stopping at the first
* TRUE return.
*/
while ((base_sgcIndex < subgroupclasses->nelts)
&& (result != LDAP_COMPARE_TRUE)) {
result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
sgc_ents[base_sgcIndex].name);
if (result != LDAP_COMPARE_TRUE) {
base_sgcIndex++;
}
}
if (result != LDAP_COMPARE_TRUE) {
ldc->reason = "DN failed group verification.";
return result;
}
/*
* 2. Find previously created cache entry and check if there is already a
* subgrouplist.
*/
LDAP_CACHE_LOCK();
curnode.url = url;
curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
LDAP_CACHE_UNLOCK();
if (curl && curl->compare_cache) {
/* make a comparison to the cache */
LDAP_CACHE_LOCK();
the_compare_node.dn = (char *)dn;
the_compare_node.attrib = (char *)"objectClass";
the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
the_compare_node.result = 0;
the_compare_node.sgl_processed = 0;
the_compare_node.subgroupList = NULL;
compare_nodep = util_ald_cache_fetch(curl->compare_cache,
&the_compare_node);
if (compare_nodep != NULL) {
/*
* Found the generic group entry... but the user isn't in this
* group or we wouldn't be here.
*/
if (compare_nodep->sgl_processed) {
if (compare_nodep->subgroupList) {
/* Make a local copy of the subgroup list */
int i;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01288)
"Making local copy of SGL for "
"group (%s)(objectClass=%s) ",
dn, (char *)sgc_ents[base_sgcIndex].name);
tmp_local_sgl = apr_pcalloc(r->pool,
sizeof(util_compare_subgroup_t));
tmp_local_sgl->len = compare_nodep->subgroupList->len;
tmp_local_sgl->subgroupDNs =
apr_palloc(r->pool,
sizeof(char *) * compare_nodep->subgroupList->len);
for (i = 0; i < compare_nodep->subgroupList->len; i++) {
tmp_local_sgl->subgroupDNs[i] =
apr_pstrdup(r->pool,
compare_nodep->subgroupList->subgroupDNs[i]);
}
}
else {
sgl_cached_empty = 1;
}
}
}
LDAP_CACHE_UNLOCK();
}
if (!tmp_local_sgl && !sgl_cached_empty) {
/* No Cached SGL, retrieve from LDAP */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01289)
"no cached SGL for %s, retrieving from LDAP", dn);
tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
subgroupclasses);
if (!tmp_local_sgl) {
/* No SGL aailable via LDAP either */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01290) "no subgroups for %s",
dn);
}
if (curl && curl->compare_cache) {
/*
* Find the generic group cache entry and add the sgl we just retrieved.
*/
LDAP_CACHE_LOCK();
the_compare_node.dn = (char *)dn;
the_compare_node.attrib = (char *)"objectClass";
the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
the_compare_node.result = 0;
the_compare_node.sgl_processed = 0;
the_compare_node.subgroupList = NULL;
compare_nodep = util_ald_cache_fetch(curl->compare_cache,
&the_compare_node);
if (compare_nodep == NULL) {
/*
* The group entry we want to attach our SGL to doesn't exist.
* We only got here if we verified this DN was actually a group
* based on the objectClass, but we can't call the compare function
* while we already hold the cache lock -- only the insert.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01291)
"Cache entry for %s doesn't exist", dn);
the_compare_node.result = LDAP_COMPARE_TRUE;
util_ald_cache_insert(curl->compare_cache, &the_compare_node);
compare_nodep = util_ald_cache_fetch(curl->compare_cache,
&the_compare_node);
if (compare_nodep == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01292)
"util_ldap: Couldn't retrieve group entry "
"for %s from cache",
dn);
}
}
/*
* We have a valid cache entry and a locally generated SGL.
* Attach the SGL to the cache entry
*/
if (compare_nodep && !compare_nodep->sgl_processed) {
if (!tmp_local_sgl) {
/* We looked up an SGL for a group and found it to be empty */
if (compare_nodep->subgroupList == NULL) {
compare_nodep->sgl_processed = 1;
}
}
else {
util_compare_subgroup_t *sgl_copy =
util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(01293)
"Copying local SGL of len %d for group %s into cache",
tmp_local_sgl->len, dn);
if (sgl_copy) {
if (compare_nodep->subgroupList) {
util_ald_sgl_free(curl->compare_cache,
&(compare_nodep->subgroupList));
}
compare_nodep->subgroupList = sgl_copy;
compare_nodep->sgl_processed = 1;
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, APLOGNO(01294)
"Copy of SGL failed to obtain shared memory, "
"couldn't update cache");
}
}
}
LDAP_CACHE_UNLOCK();
}
}
/*
* tmp_local_sgl has either been created, or copied out of the cache
* If tmp_local_sgl is NULL, there are no subgroups to process and we'll
* return false
*/
result = LDAP_COMPARE_FALSE;
if (!tmp_local_sgl) {
return result;
}
while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
const char *group = NULL;
group = tmp_local_sgl->subgroupDNs[sgindex];
/*
* 4. Now loop through the subgroupList and call uldap_cache_compare
* to check for the user.
*/
result = uldap_cache_compare(r, ldc, url, group, attrib, value);
if (result == LDAP_COMPARE_TRUE) {
/*
* 4.A. We found the user in the subgroup. Return
* LDAP_COMPARE_TRUE.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01295)
"Found user %s in a subgroup (%s) at level %d of %d.",
r->user, group, cur_subgroup_depth+1,
max_subgroup_depth);
}
else {
/*
* 4.B. We didn't find the user in this subgroup, so recurse into
* it and keep looking.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01296)
"User %s not found in subgroup (%s) at level %d of "
"%d.", r->user, group, cur_subgroup_depth+1,
max_subgroup_depth);
result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
value, subgroupAttrs,
subgroupclasses,
cur_subgroup_depth+1,
max_subgroup_depth);
}
sgindex++;
}
return result;
}
static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
const char *url, const char *basedn,
int scope, char **attrs, const char *filter,
const char *bindpw, const char **binddn,
const char ***retvals)
{
const char **vals = NULL;
int numvals = 0;
int result = 0;
LDAPMessage *res, *entry;
char *dn;
int count;
int failures = 0;
util_url_node_t *curl; /* Cached URL node */
util_url_node_t curnode;
util_search_node_t *search_nodep; /* Cached search node */
util_search_node_t the_search_node;
apr_time_t curtime;
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(r->server->module_config,
&ldap_module);
/* Get the cache node for this url */
LDAP_CACHE_LOCK();
curnode.url = url;
curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
&curnode);
if (curl == NULL) {
curl = util_ald_create_caches(st, url);
}
LDAP_CACHE_UNLOCK();
if (curl) {
LDAP_CACHE_LOCK();
the_search_node.username = filter;
search_nodep = util_ald_cache_fetch(curl->search_cache,
&the_search_node);
if (search_nodep != NULL) {
/* found entry in search cache... */
curtime = apr_time_now();
/*
* Remove this item from the cache if its expired. If the sent
* password doesn't match the storepassword, the entry will
* be removed and readded later if the credentials pass
* authentication.
*/
if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
/* ...but entry is too old */
util_ald_cache_remove(curl->search_cache, search_nodep);
}
else if ( (search_nodep->bindpw)
&& (search_nodep->bindpw[0] != '\0')
&& (strcmp(search_nodep->bindpw, bindpw) == 0))
{
/* ...and entry is valid */
*binddn = apr_pstrdup(r->pool, search_nodep->dn);
if (attrs) {
int i;
*retvals = apr_palloc(r->pool, sizeof(char *) * search_nodep->numvals);
for (i = 0; i < search_nodep->numvals; i++) {
(*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
}
}
LDAP_CACHE_UNLOCK();
ldc->reason = "Authentication successful (cached)";
return LDAP_SUCCESS;
}
}
/* unlock this read lock */
LDAP_CACHE_UNLOCK();
}
/*
* At this point, there is no valid cached search, so lets do the search.
*/
/*
* If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
*/
start_over:
if (failures > st->retries) {
return result;
}
if (failures > 0 && st->retry_delay > 0) {
apr_sleep(st->retry_delay);
}
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
return result;
}
/* try do the search */
result = ldap_search_ext_s(ldc->ldap,
(char *)basedn, scope,
(char *)filter, attrs, 0,
NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
if (AP_LDAP_IS_SERVER_DOWN(result))
{
ldc->reason = "ldap_search_ext_s() for user failed with server down";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
if (result == LDAP_TIMEOUT) {
ldc->reason = "ldap_search_ext_s() for user failed with timeout";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
/* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
if (result != LDAP_SUCCESS) {
ldc->reason = "ldap_search_ext_s() for user failed";
return result;
}
/*
* We should have found exactly one entry; to find a different
* number is an error.
*/
count = ldap_count_entries(ldc->ldap, res);
if (count != 1)
{
if (count == 0 )
ldc->reason = "User not found";
else
ldc->reason = "User is not unique (search found two "
"or more matches)";
ldap_msgfree(res);
return LDAP_NO_SUCH_OBJECT;
}
entry = ldap_first_entry(ldc->ldap, res);
/* Grab the dn, copy it into the pool, and free it again */
dn = ldap_get_dn(ldc->ldap, entry);
*binddn = apr_pstrdup(r->pool, dn);
ldap_memfree(dn);
/*
* A bind to the server with an empty password always succeeds, so
* we check to ensure that the password is not empty. This implies
* that users who actually do have empty passwords will never be
* able to authenticate with this module. I don't see this as a big
* problem.
*/
if (!bindpw || strlen(bindpw) <= 0) {
ldap_msgfree(res);
ldc->reason = "Empty password not allowed";
return LDAP_INVALID_CREDENTIALS;
}
/*
* Attempt to bind with the retrieved dn and the password. If the bind
* fails, it means that the password is wrong (the dn obviously
* exists, since we just retrieved it)
*/
result = uldap_simple_bind(ldc, (char *)*binddn, (char *)bindpw,
st->opTimeout);
if (AP_LDAP_IS_SERVER_DOWN(result) ||
(result == LDAP_TIMEOUT && failures == 0)) {
if (AP_LDAP_IS_SERVER_DOWN(result))
ldc->reason = "ldap_simple_bind() to check user credentials "
"failed with server down";
else
ldc->reason = "ldap_simple_bind() to check user credentials "
"timed out";
ldap_msgfree(res);
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
/* failure? if so - return */
if (result != LDAP_SUCCESS) {
ldc->reason = "ldap_simple_bind() to check user credentials failed";
ldap_msgfree(res);
uldap_connection_unbind(ldc);
return result;
}
else {
/*
* We have just bound the connection to a different user and password
* combination, which might be reused unintentionally next time this
* connection is used from the connection pool.
*/
ldc->must_rebind = 0;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp used for authn, must be rebound", ldc);
}
/*
* Get values for the provided attributes.
*/
if (attrs) {
int k = 0;
int i = 0;
while (attrs[k++]);
vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
numvals = k;
while (attrs[i]) {
char **values;
int j = 0;
char *str = NULL;
/* get values */
values = ldap_get_values(ldc->ldap, entry, attrs[i]);
while (values && values[j]) {
str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
: apr_pstrdup(r->pool, values[j]);
j++;
}
ldap_value_free(values);
vals[i] = str;
i++;
}
*retvals = vals;
}
/*
* Add the new username to the search cache.
*/
if (curl) {
LDAP_CACHE_LOCK();
the_search_node.username = filter;
the_search_node.dn = *binddn;
the_search_node.bindpw = bindpw;
the_search_node.lastbind = apr_time_now();
the_search_node.vals = vals;
the_search_node.numvals = numvals;
/* Search again to make sure that another thread didn't ready insert
* this node into the cache before we got here. If it does exist then
* update the lastbind
*/
search_nodep = util_ald_cache_fetch(curl->search_cache,
&the_search_node);
if ((search_nodep == NULL) ||
(strcmp(*binddn, search_nodep->dn) != 0)) {
/* Nothing in cache, insert new entry */
util_ald_cache_insert(curl->search_cache, &the_search_node);
}
else if ((!search_nodep->bindpw) ||
(strcmp(bindpw, search_nodep->bindpw) != 0)) {
/* Entry in cache is invalid, remove it and insert new one */
util_ald_cache_remove(curl->search_cache, search_nodep);
util_ald_cache_insert(curl->search_cache, &the_search_node);
}
else {
/* Cache entry is valid, update lastbind */
search_nodep->lastbind = the_search_node.lastbind;
}
LDAP_CACHE_UNLOCK();
}
ldap_msgfree(res);
ldc->reason = "Authentication successful";
return LDAP_SUCCESS;
}
/*
* This function will return the DN of the entry matching userid.
* It is used to get the DN in case some other module than mod_auth_ldap
* has authenticated the user.
* The function is basically a copy of uldap_cache_checkuserid
* with password checking removed.
*/
static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
const char *url, const char *basedn,
int scope, char **attrs, const char *filter,
const char **binddn, const char ***retvals)
{
const char **vals = NULL;
int numvals = 0;
int result = 0;
LDAPMessage *res, *entry;
char *dn;
int count;
int failures = 0;
util_url_node_t *curl; /* Cached URL node */
util_url_node_t curnode;
util_search_node_t *search_nodep; /* Cached search node */
util_search_node_t the_search_node;
apr_time_t curtime;
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(r->server->module_config,
&ldap_module);
/* Get the cache node for this url */
LDAP_CACHE_LOCK();
curnode.url = url;
curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
&curnode);
if (curl == NULL) {
curl = util_ald_create_caches(st, url);
}
LDAP_CACHE_UNLOCK();
if (curl) {
LDAP_CACHE_LOCK();
the_search_node.username = filter;
search_nodep = util_ald_cache_fetch(curl->search_cache,
&the_search_node);
if (search_nodep != NULL) {
/* found entry in search cache... */
curtime = apr_time_now();
/*
* Remove this item from the cache if its expired.
*/
if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
/* ...but entry is too old */
util_ald_cache_remove(curl->search_cache, search_nodep);
}
else {
/* ...and entry is valid */
*binddn = apr_pstrdup(r->pool, search_nodep->dn);
if (attrs) {
int i;
*retvals = apr_palloc(r->pool, sizeof(char *) * search_nodep->numvals);
for (i = 0; i < search_nodep->numvals; i++) {
(*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
}
}
LDAP_CACHE_UNLOCK();
ldc->reason = "Search successful (cached)";
return LDAP_SUCCESS;
}
}
/* unlock this read lock */
LDAP_CACHE_UNLOCK();
}
/*
* At this point, there is no valid cached search, so lets do the search.
*/
/*
* If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
*/
start_over:
if (failures > st->retries) {
return result;
}
if (failures > 0 && st->retry_delay > 0) {
apr_sleep(st->retry_delay);
}
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
return result;
}
/* try do the search */
result = ldap_search_ext_s(ldc->ldap,
(char *)basedn, scope,
(char *)filter, attrs, 0,
NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
if (AP_LDAP_IS_SERVER_DOWN(result))
{
ldc->reason = "ldap_search_ext_s() for user failed with server down";
uldap_connection_unbind(ldc);
failures++;
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
goto start_over;
}
/* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
if (result != LDAP_SUCCESS) {
ldc->reason = "ldap_search_ext_s() for user failed";
return result;
}
/*
* We should have found exactly one entry; to find a different
* number is an error.
*/
count = ldap_count_entries(ldc->ldap, res);
if (count != 1)
{
if (count == 0 )
ldc->reason = "User not found";
else
ldc->reason = "User is not unique (search found two "
"or more matches)";
ldap_msgfree(res);
return LDAP_NO_SUCH_OBJECT;
}
entry = ldap_first_entry(ldc->ldap, res);
/* Grab the dn, copy it into the pool, and free it again */
dn = ldap_get_dn(ldc->ldap, entry);
*binddn = apr_pstrdup(r->pool, dn);
ldap_memfree(dn);
/*
* Get values for the provided attributes.
*/
if (attrs) {
int k = 0;
int i = 0;
while (attrs[k++]);
vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
numvals = k;
while (attrs[i]) {
char **values;
int j = 0;
char *str = NULL;
/* get values */
values = ldap_get_values(ldc->ldap, entry, attrs[i]);
while (values && values[j]) {
str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
: apr_pstrdup(r->pool, values[j]);
j++;
}
ldap_value_free(values);
vals[i] = str;
i++;
}
*retvals = vals;
}
/*
* Add the new username to the search cache.
*/
if (curl) {
LDAP_CACHE_LOCK();
the_search_node.username = filter;
the_search_node.dn = *binddn;
the_search_node.bindpw = NULL;
the_search_node.lastbind = apr_time_now();
the_search_node.vals = vals;
the_search_node.numvals = numvals;
/* Search again to make sure that another thread didn't ready insert
* this node into the cache before we got here. If it does exist then
* update the lastbind
*/
search_nodep = util_ald_cache_fetch(curl->search_cache,
&the_search_node);
if ((search_nodep == NULL) ||
(strcmp(*binddn, search_nodep->dn) != 0)) {
/* Nothing in cache, insert new entry */
util_ald_cache_insert(curl->search_cache, &the_search_node);
}
/*
* Don't update lastbind on entries with bindpw because
* we haven't verified that password. It's OK to update
* the entry if there is no password in it.
*/
else if (!search_nodep->bindpw) {
/* Cache entry is valid, update lastbind */
search_nodep->lastbind = the_search_node.lastbind;
}
LDAP_CACHE_UNLOCK();
}
ldap_msgfree(res);
ldc->reason = "Search successful";
return LDAP_SUCCESS;
}
/*
* Reports if ssl support is enabled
*
* 1 = enabled, 0 = not enabled
*/
static int uldap_ssl_supported(request_rec *r)
{
util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
r->server->module_config, &ldap_module);
return(st->ssl_supported);
}
/* ---------------------------------------- */
/* config directives */
static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
const char *bytes)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
st->cache_bytes = atol(bytes);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01297)
"ldap cache: Setting shared memory cache size to "
"%" APR_SIZE_T_FMT " bytes.",
st->cache_bytes);
return NULL;
}
static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
const char *file)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (file) {
st->cache_file = ap_runtime_dir_relative(st->pool, file);
}
else {
st->cache_file = NULL;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01298)
"LDAP cache: Setting shared memory cache file to %s.",
st->cache_file);
return NULL;
}
static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
const char *ttl)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
st->search_cache_ttl = atol(ttl) * 1000000;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01299)
"ldap cache: Setting cache TTL to %ld microseconds.",
st->search_cache_ttl);
return NULL;
}
static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
const char *size)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
st->search_cache_size = atol(size);
if (st->search_cache_size < 0) {
st->search_cache_size = 0;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01300)
"ldap cache: Setting search cache size to %ld entries.",
st->search_cache_size);
return NULL;
}
static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
const char *ttl)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
st->compare_cache_ttl = atol(ttl) * 1000000;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01301)
"ldap cache: Setting operation cache TTL to %ld microseconds.",
st->compare_cache_ttl);
return NULL;
}
static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
const char *size)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
st->compare_cache_size = atol(size);
if (st->compare_cache_size < 0) {
st->compare_cache_size = 0;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01302)
"ldap cache: Setting operation cache size to %ld entries.",
st->compare_cache_size);
return NULL;
}
/**
* Parse the certificate type.
*
* The type can be one of the following:
* CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
* CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
*
* If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
*/
static int util_ldap_parse_cert_type(const char *type)
{
/* Authority file in binary DER format */
if (0 == strcasecmp("CA_DER", type)) {
return APR_LDAP_CA_TYPE_DER;
}
/* Authority file in Base64 format */
else if (0 == strcasecmp("CA_BASE64", type)) {
return APR_LDAP_CA_TYPE_BASE64;
}
/* Netscape certificate database file/directory */
else if (0 == strcasecmp("CA_CERT7_DB", type)) {
return APR_LDAP_CA_TYPE_CERT7_DB;
}
/* Netscape secmod file/directory */
else if (0 == strcasecmp("CA_SECMOD", type)) {
return APR_LDAP_CA_TYPE_SECMOD;
}
/* Client cert file in DER format */
else if (0 == strcasecmp("CERT_DER", type)) {
return APR_LDAP_CERT_TYPE_DER;
}
/* Client cert file in Base64 format */
else if (0 == strcasecmp("CERT_BASE64", type)) {
return APR_LDAP_CERT_TYPE_BASE64;
}
/* Client cert file in PKCS#12 format */
else if (0 == strcasecmp("CERT_PFX", type)) {
return APR_LDAP_CERT_TYPE_PFX;
}
/* Netscape client cert database file/directory */
else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
return APR_LDAP_CERT_TYPE_KEY3_DB;
}
/* Netscape client cert nickname */
else if (0 == strcasecmp("CERT_NICKNAME", type)) {
return APR_LDAP_CERT_TYPE_NICKNAME;
}
/* Client cert key file in DER format */
else if (0 == strcasecmp("KEY_DER", type)) {
return APR_LDAP_KEY_TYPE_DER;
}
/* Client cert key file in Base64 format */
else if (0 == strcasecmp("KEY_BASE64", type)) {
return APR_LDAP_KEY_TYPE_BASE64;
}
/* Client cert key file in PKCS#12 format */
else if (0 == strcasecmp("KEY_PFX", type)) {
return APR_LDAP_KEY_TYPE_PFX;
}
else {
return APR_LDAP_CA_TYPE_UNKNOWN;
}
}
/**
* Set LDAPTrustedGlobalCert.
*
* This directive takes either two or three arguments:
* - certificate type
* - certificate file / directory / nickname
* - certificate password (optional)
*
* This directive may only be used globally.
*/
static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
void *dummy,
const char *type,
const char *file,
const char *password)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
apr_finfo_t finfo;
apr_status_t rv;
int cert_type = 0;
apr_ldap_opt_tls_cert_t *cert;
if (err != NULL) {
return err;
}
/* handle the certificate type */
if (type) {
cert_type = util_ldap_parse_cert_type(type);
if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
return apr_psprintf(cmd->pool, "The certificate type %s is "
"not recognised. It should be one "
"of CA_DER, CA_BASE64, CA_CERT7_DB, "
"CA_SECMOD, CERT_DER, CERT_BASE64, "
"CERT_KEY3_DB, CERT_NICKNAME, "
"KEY_DER, KEY_BASE64", type);
}
}
else {
return "Certificate type was not specified.";
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01303)
"LDAP: SSL trusted global cert - %s (type %s)",
file, type);
/* add the certificate to the global array */
cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
cert->type = cert_type;
cert->path = file;
cert->password = password;
/* if file is a file or path, fix the path */
if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
cert->path = ap_server_root_relative(cmd->pool, file);
if (cert->path &&
((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
!= APR_SUCCESS))
{
ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server, APLOGNO(01304)
"LDAP: Could not open SSL trusted certificate "
"authority file - %s",
cert->path == NULL ? file : cert->path);
return "Invalid global certificate file path";
}
}
return(NULL);
}
/**
* Set LDAPTrustedClientCert.
*
* This directive takes either two or three arguments:
* - certificate type
* - certificate file / directory / nickname
* - certificate password (optional)
*/
static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
void *config,
const char *type,
const char *file,
const char *password)
{
util_ldap_config_t *dc = config;
apr_finfo_t finfo;
apr_status_t rv;
int cert_type = 0;
apr_ldap_opt_tls_cert_t *cert;
/* handle the certificate type */
if (type) {
cert_type = util_ldap_parse_cert_type(type);
if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
"not recognised. It should be one "
"of CA_DER, CA_BASE64, "
"CERT_DER, CERT_BASE64, "
"CERT_NICKNAME, CERT_PFX, "
"KEY_DER, KEY_BASE64, KEY_PFX",
type);
}
else if ( APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
APR_LDAP_CA_TYPE_SECMOD == cert_type ||
APR_LDAP_CERT_TYPE_PFX == cert_type ||
APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
"only valid within a "
"LDAPTrustedGlobalCert directive. "
"Only CA_DER, CA_BASE64, "
"CERT_DER, CERT_BASE64, "
"CERT_NICKNAME, KEY_DER, and "
"KEY_BASE64 may be used.", type);
}
}
else {
return "Certificate type was not specified.";
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01305)
"LDAP: SSL trusted client cert - %s (type %s)",
file, type);
/* add the certificate to the client array */
cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(dc->client_certs);
cert->type = cert_type;
cert->path = file;
cert->password = password;
/* if file is a file or path, fix the path */
if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
cert->path = ap_server_root_relative(cmd->pool, file);
if (cert->path &&
((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
!= APR_SUCCESS))
{
ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server, APLOGNO(01306)
"LDAP: Could not open SSL client certificate "
"file - %s",
cert->path == NULL ? file : cert->path);
return "Invalid client certificate file path";
}
}
return(NULL);
}
/**
* Set LDAPTrustedMode.
*
* This directive sets what encryption mode to use on a connection:
* - None (No encryption)
* - SSL (SSL encryption)
* - STARTTLS (TLS encryption)
*/
static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
const char *mode)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01307)
"LDAP: SSL trusted mode - %s",
mode);
if (0 == strcasecmp("NONE", mode)) {
st->secure = APR_LDAP_NONE;
}
else if (0 == strcasecmp("SSL", mode)) {
st->secure = APR_LDAP_SSL;
}
else if ( (0 == strcasecmp("TLS", mode))
|| (0 == strcasecmp("STARTTLS", mode))) {
st->secure = APR_LDAP_STARTTLS;
}
else {
return "Invalid LDAPTrustedMode setting: must be one of NONE, "
"SSL, or TLS/STARTTLS";
}
st->secure_set = 1;
return(NULL);
}
static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
void *dummy,
int mode)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01308)
"LDAP: SSL verify server certificate - %s",
mode?"TRUE":"FALSE");
st->verify_svr_cert = mode;
return(NULL);
}
static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
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) {
return err;
}
#ifdef LDAP_OPT_NETWORK_TIMEOUT
st->connectionTimeout = atol(ttl);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01309)
"ldap connection: Setting connection timeout to %ld seconds.",
st->connectionTimeout);
#else
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, APLOGNO(01310)
"LDAP: Connection timeout option not supported by the "
"LDAP SDK in use." );
#endif
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, APLOGNO(01311)
"LDAP: Setting referral chasing %s",
(mode == AP_LDAP_CHASEREFERRALS_ON) ? "ON" : "OFF");
dc->ChaseReferrals = mode;
return(NULL);
}
static const char *util_ldap_set_debug_level(cmd_parms *cmd,
void *config,
const char *arg) {
#ifdef AP_LDAP_OPT_DEBUG
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) {
return err;
}
#ifndef AP_LDAP_OPT_DEBUG
return "This directive is not supported with the currently linked LDAP library";
#else
st->debug_level = atoi(arg);
return NULL;
#endif
}
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);
if (dc->ReferralHopLimit <= 0) {
return "LDAPReferralHopLimit must be greater than zero (Use 'LDAPReferrals Off' to disable referral chasing)";
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01312)
"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));
/* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
dc->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
dc->ReferralHopLimit = AP_LDAP_HOPLIMIT_UNSET;
return dc;
}
static const char *util_ldap_set_op_timeout(cmd_parms *cmd,
void *dummy,
const char *val)
{
long timeout;
char *endptr;
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
timeout = strtol(val, &endptr, 10);
if ((val == endptr) || (*endptr != '\0')) {
return "Timeout not numerical";
}
if (timeout < 0) {
return "Timeout must be non-negative";
}
if (timeout) {
if (!st->opTimeout) {
st->opTimeout = apr_pcalloc(cmd->pool, sizeof(struct timeval));
}
st->opTimeout->tv_sec = timeout;
}
else {
st->opTimeout = NULL;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01313)
"ldap connection: Setting op timeout to %ld seconds.",
timeout);
return NULL;
}
static const char *util_ldap_set_conn_ttl(cmd_parms *cmd,
void *dummy,
const char *val)
{
apr_interval_time_t timeout;
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) {
return "LDAPConnPoolTTL has wrong format";
}
if (timeout < 0) {
/* reserve -1 for default value */
timeout = AP_LDAP_CONNPOOL_INFINITE;
}
st->connection_pool_ttl = timeout;
return NULL;
}
static const char *util_ldap_set_retry_delay(cmd_parms *cmd,
void *dummy,
const char *val)
{
apr_interval_time_t timeout;
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) {
return "LDAPRetryDelay has wrong format";
}
if (timeout < 0) {
return "LDAPRetryDelay must be >= 0";
}
st->retry_delay = timeout;
return NULL;
}
static const char *util_ldap_set_retries(cmd_parms *cmd,
void *dummy,
const char *val)
{
util_ldap_state_t *st =
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
&ldap_module);
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
st->retries = atoi(val);
if (st->retries < 0) {
return "LDAPRetries must be >= 0";
}
return NULL;
}
static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
{
util_ldap_state_t *st =
(util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
/* Create a per vhost pool for mod_ldap to use, serialized with
* st->mutex (also one per vhost). both are replicated by fork(),
* no shared memory managed by either.
*/
apr_pool_create(&st->pool, p);
#if APR_HAS_THREADS
apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
#endif
st->cache_bytes = 500000;
st->search_cache_ttl = 600000000;
st->search_cache_size = 1024;
st->compare_cache_ttl = 600000000;
st->compare_cache_size = 1024;
st->connections = NULL;
st->ssl_supported = 0;
st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
st->secure = APR_LDAP_NONE;
st->secure_set = 0;
st->connectionTimeout = 10;
st->opTimeout = apr_pcalloc(p, sizeof(struct timeval));
st->opTimeout->tv_sec = 60;
st->verify_svr_cert = 1;
st->connection_pool_ttl = AP_LDAP_CONNPOOL_DEFAULT; /* no limit */
st->retries = 3;
st->retry_delay = 0; /* no delay */
return st;
}
/* cache-related settings are not merged here, but in the post_config hook,
* since the cache has not yet sprung to life
*/
static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
void *overridesv)
{
util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
util_ldap_state_t *base = (util_ldap_state_t *) basev;
util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
st->pool = overrides->pool;
#if APR_HAS_THREADS
st->mutex = overrides->mutex;
#endif
/* The cache settings can not be modified in a
virtual host since all server use the same
shared memory cache. */
st->cache_bytes = base->cache_bytes;
st->search_cache_ttl = base->search_cache_ttl;
st->search_cache_size = base->search_cache_size;
st->compare_cache_ttl = base->compare_cache_ttl;
st->compare_cache_size = base->compare_cache_size;
st->util_ldap_cache_lock = base->util_ldap_cache_lock;
st->connections = NULL;
st->ssl_supported = 0; /* not known until post-config and re-merged */
st->global_certs = apr_array_append(p, base->global_certs,
overrides->global_certs);
st->secure = (overrides->secure_set == 0) ? base->secure
: overrides->secure;
/* These LDAP connection settings can not be overwritten in
a virtual host. Once set in the base server, they must
remain the same. None of the LDAP SDKs seem to be able
to handle setting the verify_svr_cert flag on a
per-connection basis. The OpenLDAP client appears to be
able to handle the connection timeout per-connection
but the Novell SDK cannot. Allowing the timeout to
be set by each vhost is of little value so rather than
trying to make special expections for one LDAP SDK, GLOBAL_ONLY
is being enforced on this setting as well. */
st->connectionTimeout = base->connectionTimeout;
st->opTimeout = base->opTimeout;
st->verify_svr_cert = base->verify_svr_cert;
st->debug_level = base->debug_level;
st->connection_pool_ttl = (overrides->connection_pool_ttl == AP_LDAP_CONNPOOL_DEFAULT) ?
base->connection_pool_ttl : overrides->connection_pool_ttl;
st->retries = base->retries;
st->retry_delay = base->retry_delay;
return st;
}
static apr_status_t util_ldap_cleanup_module(void *data)
{
server_rec *s = data;
util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
s->module_config, &ldap_module);
if (st->ssl_supported) {
apr_ldap_ssl_deinit();
}
return APR_SUCCESS;
}
static int util_ldap_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp)
{
apr_status_t result;
result = ap_mutex_register(pconf, ldap_cache_mutex_type, NULL,
APR_LOCK_DEFAULT, 0);
if (result != APR_SUCCESS) {
return result;
}
return OK;
}
static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
apr_status_t result;
server_rec *s_vhost;
util_ldap_state_t *st_vhost;
util_ldap_state_t *st = (util_ldap_state_t *)
ap_get_module_config(s->module_config,
&ldap_module);
apr_ldap_err_t *result_err = NULL;
int rc;
/* util_ldap_post_config() will be called twice. Don't bother
* going through all of the initialization on the first call
* because it will just be thrown away.*/
if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
#if APR_HAS_SHARED_MEMORY
/*
* If we are using shared memory caching and the cache file already
* exists then delete it. Otherwise we are going to run into problems
* creating the shared memory.
*/
if (st->cache_file && st->cache_bytes > 0) {
char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
NULL);
apr_file_remove(lck_file, ptemp);
}
#endif
return OK;
}
#if APR_HAS_SHARED_MEMORY
/*
* initializing cache if we don't already have a shm address
*/
if (!st->cache_shm) {
#endif
result = util_ldap_cache_init(p, st);
if (result != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, result, s, APLOGNO(01315)
"LDAP cache: could not create shared memory segment");
return DONE;
}
result = ap_global_mutex_create(&st->util_ldap_cache_lock, NULL,
ldap_cache_mutex_type, NULL, s, p, 0);
if (result != APR_SUCCESS) {
return result;
}
/* merge config in all vhost */
s_vhost = s->next;
while (s_vhost) {
st_vhost = (util_ldap_state_t *)
ap_get_module_config(s_vhost->module_config,
&ldap_module);
#if APR_HAS_SHARED_MEMORY
st_vhost->cache_shm = st->cache_shm;
st_vhost->cache_rmm = st->cache_rmm;
st_vhost->cache_file = st->cache_file;
st_vhost->util_ldap_cache = st->util_ldap_cache;
ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s, APLOGNO(01316)
"LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
"for VHOST: %s", st->cache_shm, st->cache_rmm,
s_vhost->server_hostname);
#endif
s_vhost = s_vhost->next;
}
#if APR_HAS_SHARED_MEMORY
}
else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01317)
"LDAP cache: LDAPSharedCacheSize is zero, disabling "
"shared memory cache");
}
#endif
/* log the LDAP SDK used
*/
{
apr_ldap_err_t *result = NULL;
apr_ldap_info(p, &(result));
if (result != NULL) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01318) "%s", result->reason);
}
}
apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
util_ldap_cleanup_module);
/*
* Initialize SSL support, and log the result for the benefit of the admin.
*
* If SSL is not supported it is not necessarily an error, as the
* application may not want to use it.
*/
rc = apr_ldap_ssl_init(p,
NULL,
0,
&(result_err));
if (APR_SUCCESS == rc) {
rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
(void *)st->global_certs, &(result_err));
}
if (APR_SUCCESS == rc) {
st->ssl_supported = 1;
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01319)
"LDAP: SSL support available" );
}
else {
st->ssl_supported = 0;
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01320)
"LDAP: SSL support unavailable%s%s",
result_err ? ": " : "",
result_err ? result_err->reason : "");
}
/* ssl_supported is really a global setting */
s_vhost = s->next;
while (s_vhost) {
st_vhost = (util_ldap_state_t *)
ap_get_module_config(s_vhost->module_config,
&ldap_module);
st_vhost->ssl_supported = st->ssl_supported;
s_vhost = s_vhost->next;
}
/* Initialize the rebind callback's cross reference list. */
apr_ldap_rebind_init (p);
#ifdef AP_LDAP_OPT_DEBUG
if (st->debug_level > 0) {
result = ldap_set_option(NULL, AP_LDAP_OPT_DEBUG, &st->debug_level);
if (result != LDAP_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01321)
"LDAP: Could not set the LDAP library debug level to %d:(%d) %s",
st->debug_level, result, ldap_err2string(result));
}
}
#endif
return(OK);
}
static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
{
apr_status_t sts;
util_ldap_state_t *st = ap_get_module_config(s->module_config,
&ldap_module);
if (!st->util_ldap_cache_lock) return;
sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
apr_global_mutex_lockfile(st->util_ldap_cache_lock), p);
if (sts != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s, APLOGNO(01322)
"Failed to initialise global mutex %s in child process",
ldap_cache_mutex_type);
}
}
static const command_rec util_ldap_cmds[] = {
AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
NULL, RSRC_CONF,
"Set the size of the shared memory cache (in bytes). Use "
"0 to disable the shared memory cache. (default: 500000)"),
AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
NULL, RSRC_CONF,
"Set the file name for the shared memory cache."),
AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
NULL, RSRC_CONF,
"Set the maximum number of entries that are possible in the "
"LDAP search cache. Use 0 or -1 to disable the search cache "
"(default: 1024)"),
AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
NULL, RSRC_CONF,
"Set the maximum time (in seconds) that an item can be "
"cached in the LDAP search cache. Use 0 for no limit. "
"(default 600)"),
AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
NULL, RSRC_CONF,
"Set the maximum number of entries that are possible "
"in the LDAP compare cache. Use 0 or -1 to disable the compare cache "
"(default: 1024)"),
AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
NULL, RSRC_CONF,
"Set the maximum time (in seconds) that an item is cached "
"in the LDAP operation cache. Use 0 for no limit. "
"(default: 600)"),
AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
NULL, RSRC_CONF,
"Takes three arguments; the first argument is the cert "
"type of the second argument, one of CA_DER, CA_BASE64, "
"CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
"CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
"specifes the file and/or directory containing the trusted CA "
"certificates (and global client certs for Netware) used to "
"validate the LDAP server. The third argument is an optional "
"passphrase if applicable."),
AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
NULL, OR_AUTHCFG,
"Takes three arguments: the first argument is the certificate "
"type of the second argument, one of CA_DER, CA_BASE64, "
"CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
"CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
"specifies the file and/or directory containing the client "
"certificate, or certificate ID used to validate this LDAP "
"client. The third argument is an optional passphrase if "
"applicable."),
AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
NULL, RSRC_CONF,
"Specify the type of security that should be applied to "
"an LDAP connection. One of; NONE, SSL or STARTTLS."),
AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
NULL, RSRC_CONF,
"Set to 'ON' requires that the server certificate be verified"
" before a secure LDAP connection can be establish. Default"
" 'ON'"),
AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
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, Consult LDAP SDK documentation for applicability and defaults"),
AP_INIT_TAKE1("LDAPLibraryDebug", util_ldap_set_debug_level,
NULL, RSRC_CONF,
"Enable debugging in LDAP SDK (Default: off, values: SDK specific"),
AP_INIT_TAKE1("LDAPTimeout", util_ldap_set_op_timeout,
NULL, RSRC_CONF,
"Specify the LDAP bind/search timeout in seconds "
"(0 = no limit). Default: 60"),
AP_INIT_TAKE1("LDAPConnectionPoolTTL", util_ldap_set_conn_ttl,
NULL, RSRC_CONF,
"Specify the maximum amount of time a bound connection can sit "
"idle and still be considered valid for reuse"
"(0 = no pool, -1 = no limit, n = time in seconds). Default: -1"),
AP_INIT_TAKE1("LDAPRetries", util_ldap_set_retries,
NULL, RSRC_CONF,
"Specify the number of times a failed LDAP operation should be retried "
"(0 = no retries). Default: 3"),
AP_INIT_TAKE1("LDAPRetryDelay", util_ldap_set_retry_delay,
NULL, RSRC_CONF,
"Specify the delay between retries of a failed LDAP operation "
"(0 = no delay). Default: 0"),
{NULL}
};
static void util_ldap_register_hooks(apr_pool_t *p)
{
APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
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_cache_comparedn);
APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
ap_hook_pre_config(util_ldap_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}
AP_DECLARE_MODULE(ldap) = {
STANDARD20_MODULE_STUFF,
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 */
util_ldap_cmds, /* command table */
util_ldap_register_hooks, /* set up request processing hooks */
};