util_ldap.c revision 54091ac5c596337658fc568231ca1a900abdc5fe
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* Licensed to the Apache Software Foundation (ASF) under one or more
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * contributor license agreements. See the NOTICE file distributed with
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * this work for additional information regarding copyright ownership.
bc8fd1b0b1afdf89b8d28eefa8cd74e26ba97986fielding * The ASF licenses this file to You under the Apache License, Version 2.0
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * (the "License"); you may not use this file except in compliance with
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * the License. You may obtain a copy of the License at
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Unless required by applicable law or agreed to in writing, software
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * distributed under the License is distributed on an "AS IS" BASIS,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * See the License for the specific language governing permissions and
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * limitations under the License.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * util_ldap.c: LDAP things
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Original code from auth_ldap module for Apache v1.3:
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Copyright 1998, 1999 Enbridge Pipelines Inc.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Copyright 1999-2001 Dave Carrigan
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#error mod_ldap requires APR-util to have LDAP support built in
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* Default define for ldap functions that need a SIZELIMIT but
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * do not have the define
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * XXX This should be removed once a supporting #define is
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * released through APR-Util.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic apr_status_t uldap_connection_unbind(void *param);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#define LDAP_CACHE_LOCK() do { \
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb} while (0)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#define LDAP_CACHE_UNLOCK() do { \
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb} while (0)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic void util_ldap_strdup (char **str, const char *newstr)
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * Status Handler
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * --------------
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * This handler generates a status page about the current performance of
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * the LDAP cache. It is enabled as follows:
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * <Location /ldap-status>
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * SetHandler ldap-status
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * </Location>
4f9c22c4f27571d54197be9674e1fc0d528192aestriker st = (util_ldap_state_t *) ap_get_module_config(r->server->module_config,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker ap_set_content_type(r, "text/html; charset=ISO-8859-1");
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick "<html><head><title>LDAP Cache Information</title></head>\n", r);
c2cf53a40a9814eb91db2cdf820f97d943f21628coar ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe "</h1>\n", r);
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe/* ------------------------------------------------------------------ */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * Closes an LDAP connection by unlocking it. The next time
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * uldap_connection_find() is called this connection will be
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * available for reuse.
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowestatic void uldap_connection_close(util_ldap_connection_t *ldc)
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe /* We leave bound LDAP connections floating around in our pool,
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * but always check/fix the binddn/bindpw when we take them out
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * of the pool
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe /* mark our connection as available for reuse */
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * Destroys an LDAP connection by unbinding and closing the connection to
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * the LDAP server. It is used to bring the connection back to a known
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * state after an error.
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowestatic apr_status_t uldap_connection_unbind(void *param)
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe /* forget the rebind info for this conn */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe/* not presently used, not part of the API */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * util_ldap_connection_remove frees all storage associated with the LDAP
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * connection and removes it completely from the per-virtualhost list of
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * connections
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * The caller should hold the lock for this connection
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowestatic apr_status_t util_ldap_connection_remove (void *param) {
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe /* Remove ldc from the list */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe if (l == ldc) {
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe /* Destory the pool associated with this connection */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar struct timeval connectionTimeout = {10,0}; /* 10 second connection timeout */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe /* Since the host will include a port if the default port is not used,
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * always specify the default ports for the port parameter. This will
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * allow a host string that contains multiple hosts the ability to mix
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * some hosts with ports and some without. All hosts which do not
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * specify a port will use the default port.
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe /* something really bad happened */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* Now that we have an ldap struct, add it to the referral list for rebinds. */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker rc = apr_ldap_rebind_add(ldc->rebind_pool, ldc->ldap, ldc->binddn, ldc->bindpw);
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp "LDAP: Unable to add rebind cross reference entry. Out of memory?");
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe /* always default to LDAP V3 */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe /* set client certificates */
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* switch on SSL/TLS */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe /* Set the alias dereferencing option */
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe /* Set options for rebind and referrals. */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "LDAP: Setting referrals to %s.",
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"));
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"),
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if ((ldc->ReferralHopLimit != AP_LDAP_HOPLIMIT_UNSET) && ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* Referral hop limit - only if referrals are enabled and a hop limit is explicitly requested */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "Setting referral hop limit to %d.",
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe/*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
7c301a1818939f85da8f3629cc3e9b5588610ef0jerenkrantz /* This is not a per-connection setting so just pass NULL for the
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe Ldap connection handle */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
7c301a1818939f85da8f3629cc3e9b5588610ef0jerenkrantz result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
7c301a1818939f85da8f3629cc3e9b5588610ef0jerenkrantz rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe "LDAP: Could not set the connection timeout");
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * LDAP_OPT_TIMEOUT is not portable, but it influences all synchronous ldap
2f1949bb0e3c209db94c8d521cba7380b9d11421trawick * function calls and not just ldap_search_ext_s(), which accepts a timeout
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * parameter.
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * XXX: It would be possible to simulate LDAP_OPT_TIMEOUT by replacing all
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * XXX: synchronous ldap function calls with asynchronous calls and using
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * XXX: ldap_result() with a timeout.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_TIMEOUT,
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar "LDAP: Could not set LDAP_OPT_TIMEOUT");
7c301a1818939f85da8f3629cc3e9b5588610ef0jerenkrantz * Replacement function for ldap_simple_bind_s() with a timeout.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * To do this in a portable way, we have to use ldap_simple_bind() and
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * ldap_result().
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Returns LDAP_SUCCESS on success; and an error code on failure
8aefbd756763807188d2e3ce336a8680e4893066wrowestatic int uldap_simple_bind(util_ldap_connection_t *ldc, char *binddn,
8aefbd756763807188d2e3ce336a8680e4893066wrowe int msgid = ldap_simple_bind(ldc->ldap, binddn, bindpw);
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
8aefbd756763807188d2e3ce336a8680e4893066wrowe rc = ldap_result(ldc->ldap, msgid, 0, timeout, &result);
8aefbd756763807188d2e3ce336a8680e4893066wrowe ldc->reason = "LDAP: ldap_simple_bind() result retrieval failed";
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (rc == 0) {
2fa5b5878e7567e2875807c3e2a2b3b0d3ef74bewrowe } else if (ldap_parse_result(ldc->ldap, result, &rc, NULL, NULL, NULL,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ldc->reason = "LDAP: ldap_simple_bind() parse result failed";
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * Connect to the LDAP server and binds. Does not connect if already
23c6309e36a63b13b61c35999c978017521993d6wrowe * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * Returns LDAP_SUCCESS on success; and an error code on failure
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* sanity check for NULL */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* If the connection is already bound, return
4f9c22c4f27571d54197be9674e1fc0d528192aestriker ldc->reason = "LDAP: connection open successful (already bound)";
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* create the ldap session handle
cf6ef072483172309861d06e85b1aeff4573c060wrowe st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
cf6ef072483172309861d06e85b1aeff4573c060wrowe * returned. If LDAP_TIMEOUT is returned on the first try, maybe the
cf6ef072483172309861d06e85b1aeff4573c060wrowe * connection was idle for a long time and has been dropped by a firewall.
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe * In this case close the connection immediately and try again.
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick * On Success or any other error, break out of the loop.
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick * NOTE: Looping is probably not a great idea. If the server isn't
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick * responding the chances it will respond after a few tries are poor.
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick * However, the original code looped and it only happens on
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick * the error condition.
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
d75626f0952c6152a99acd013a4f127d46f0f9edtrawick if ((AP_LDAP_IS_SERVER_DOWN(rc) && failures == 5) ||
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar "ldap_simple_bind() timed out on reused "
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "connection, dropped by firewall?");
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "attempt to re-init the connection");
c2cf53a40a9814eb91db2cdf820f97d943f21628coar "ldap_simple_bind() failed with server down "
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* free the handle if there was an error
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * Compare client certificate arrays.
8aefbd756763807188d2e3ce336a8680e4893066wrowe * Returns 1 on compare failure, 0 otherwise.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic int compare_client_certs(apr_array_header_t *srcs,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* arrays both NULL? if so, then equal */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* arrays different length or either NULL? If so, then not equal */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz /* run an actual comparison */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* One is passwordless? If so, then not equal */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker ((src[i].password == NULL) ^ (dest[i].password == NULL)) ||
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe (src[i].password != NULL && dest[i].password != NULL &&
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* if we got here, the cert arrays were identical */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * Find an existing ldap connection struct that matches the
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * provided ldap connection parameters.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * If not found in the cache, a new ldc structure will be allocated
59513b1275fdc2021d4949ee03ae8229469abb86wrowe * from st->pool and returned to the caller. If found in the cache,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * a pointer to the existing ldc structure will be returned.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* mutex lock this function */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* Search for an exact connection match in the list that is not
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * being used.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if ( (l->port == port) && (strcmp(l->host, host) == 0)
4f9c22c4f27571d54197be9674e1fc0d528192aestriker && !compare_client_certs(dc->client_certs, l->client_certs))
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (l->bound && (now - l->freed) > st->connection_pool_ttl) {
4f9c22c4f27571d54197be9674e1fc0d528192aestriker "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* If this connection didn't match the criteria, then we
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * need to unlock the mutex so it is available to be reused.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* If nothing found, search again, but we don't care about the
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * binddn and bindpw this time.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if ((l->port == port) && (strcmp(l->host, host) == 0) &&
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe !compare_client_certs(dc->client_certs, l->client_certs))
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* the bind credentials have changed */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* no check for connection_pool_ttl, since we are unbinding any way */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* If this connection didn't match the criteria, then we
cf6ef072483172309861d06e85b1aeff4573c060wrowe * need to unlock the mutex so it is available to be reused.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe/* artificially disable cache */
cf6ef072483172309861d06e85b1aeff4573c060wrowe/* l = NULL; */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* If no connection was found after the second search, we
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * must create one.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "util_ldap: Failed to create memory pool");
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * Add the new connection entry to the linked list. Note that we
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * don't actually establish an LDAP connection yet; that happens
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * the first time authentication is requested.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* create the details of this connection in the new pool */
4fca95918a9c0ae93593806544b425d0adc2fcc3wrowe l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* The security mode after parsing the URL will always be either
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * If the security setting is NONE, override it to the security
f08810eff40a2bddd2bc0103453c4ae775ea62bewrowe * setting optionally supplied by the admin using LDAPTrustedMode
f08810eff40a2bddd2bc0103453c4ae775ea62bewrowe /* save away a copy of the client cert list that is presently valid */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe l->client_certs = apr_array_copy_hdr(l->pool, dc->client_certs);
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* whether or not to keep this connection in the pool when it's returned */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (apr_pool_create(&(l->rebind_pool), l->pool) != APR_SUCCESS) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "util_ldap: Failed to create memory pool");
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe/* ------------------------------------------------------------------ */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * Compares two DNs to see if they're equal. The only way to do this correctly
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * is to search for the dn and then do ldap_get_dn() on the result. This should
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * match the initial dn, since it would have been also retrieved with
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * ldap_get_dn(). This is expensive, so if the configuration value
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * compare_dn_on_server is false, just does an ordinary strcmp.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * The lock for the ldap cache should already be acquired.
8aefbd756763807188d2e3ce336a8680e4893066wrowestatic int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* get cache entry (or create one) */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* a simple compare? */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* unlock this read lock */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ldc->reason = "DN Comparison FALSE (direct strcmp())";
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* no - it's a server side compare */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* is it in the compare cache? */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* If it's in the cache, it's good */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* unlock this read lock */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* unlock this read lock */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* too many failures */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* make a server connection */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* connect to server failed */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* search for reqdn */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "failed with server down";
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe * we are reusing a connection that doesn't seem to be active anymore
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * (firewall state drop?), let's try a new connection.
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz ldc->reason = "DN Comparison ldap_search_ext_s() "
4f9c22c4f27571d54197be9674e1fc0d528192aestriker "failed with timeout";
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz /* search for reqdn failed - no match */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz ldc->reason = "DN Comparison ldap_search_ext_s() failed";
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* compare unsuccessful */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ldc->reason = "DN Comparison FALSE (checked on server)";
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* compare successful - add to the compare cache */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
8aefbd756763807188d2e3ce336a8680e4893066wrowe util_ald_cache_insert(curl->dn_compare_cache, &newnode);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ldc->reason = "DN Comparison TRUE (checked on server)";
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * Does an generic ldap_compare operation. It accepts a cache that it will use
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * to lookup the compare in the cache. We cache two kinds of compares
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * (require group compares) and (require user compares). Each compare has a
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * different cache node: require group includes the DN; require user does not
8aefbd756763807188d2e3ce336a8680e4893066wrowe * because the require user cache is owned by the
4f9c22c4f27571d54197be9674e1fc0d528192aestrikerstatic int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* get cache entry (or create one) */
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* make a comparison to the cache */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar compare_nodep = util_ald_cache_fetch(curl->compare_cache,
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* found it... */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* ...but it is too old */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe util_ald_cache_remove(curl->compare_cache, compare_nodep);
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* ...and it is good */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ldc->reason = "Comparison no such attribute (cached)";
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* record the result code to return with the reason... */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* and unlock this read lock */
cadddb2c31d24d48f4017db4df0a29687432326cwrowe /* unlock this read lock */
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* too many failures */
cf6ef072483172309861d06e85b1aeff4573c060wrowe if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick /* connect failed */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* connection failed - try again */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ldc->reason = "ldap_compare_s() failed with server down";
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * we are reusing a connection that doesn't seem to be active anymore
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * (firewall state drop?), let's try a new connection.
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* compare completed; caching result */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* If the node doesn't exist then insert it, otherwise just update
cf6ef072483172309861d06e85b1aeff4573c060wrowe * it with the last results
cf6ef072483172309861d06e85b1aeff4573c060wrowe compare_nodep = util_ald_cache_fetch(curl->compare_cache,
cf6ef072483172309861d06e85b1aeff4573c060wrowe || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "cache_compare: Cache insertion failure.");
4f9c22c4f27571d54197be9674e1fc0d528192aestriker ldc->reason = "Comparison no such attribute (adding to cache)";
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoarstatic util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe const char *url,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe const char *dn,
8aefbd756763807188d2e3ce336a8680e4893066wrowe apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
c2cf53a40a9814eb91db2cdf820f97d943f21628coar /* too many failures */
8aefbd756763807188d2e3ce336a8680e4893066wrowe if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* connect failed */
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* try to do the search */
8aefbd756763807188d2e3ce336a8680e4893066wrowe result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * we are reusing a connection that doesn't seem to be active anymore
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * (firewall state drop?), let's try a new connection.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe ldc->reason = "ldap_search_ext_s() for subgroups failed with timeout";
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ldc->reason = "ldap_search_ext_s() for subgroups failed";
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe * Get values for the provided sub-group attributes.
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* Get *all* matching "member" values from this group. */
8aefbd756763807188d2e3ce336a8680e4893066wrowe values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Now we are going to pare the subgroup members of this group
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * to *just* the subgroups, add them to the compare_nodep, and
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * then proceed to check the new level of subgroups.
948096a99010fccf648814fecf38f75c689172d7wrowe /* Check if this entry really is a group. */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz "objectClass",
948096a99010fccf648814fecf38f75c689172d7wrowe /* It's a group, so add it to the array. */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz *newgrp = apr_pstrdup(r->pool, values[val_index]);
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* We need to fill in tmp_local_subgroups using the data from LDAP */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
053497224246c4dbef9af594cacf5c00ed271e6cwrowe for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
a9a4544168a37b43bd180b3703ccee995f27a80awrowe res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
948096a99010fccf648814fecf38f75c689172d7wrowe * Does a recursive lookup operation to try to find a user within (cached)
948096a99010fccf648814fecf38f75c689172d7wrowe * nested groups. It accepts a cache that it will use to lookup previous
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * compare attempts. We cache two kinds of compares (require group compares)
948096a99010fccf648814fecf38f75c689172d7wrowe * and (require user compares). Each compare has a different cache node:
948096a99010fccf648814fecf38f75c689172d7wrowe * require group includes the DN; require user does not because the require
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * user cache is owned by the
948096a99010fccf648814fecf38f75c689172d7wrowe * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * 1. Call uldap_cache_compare for each subgroupclass value to check the
948096a99010fccf648814fecf38f75c689172d7wrowe * generic, user-agnostic, cached group entry. This will create a new generic
948096a99010fccf648814fecf38f75c689172d7wrowe * cache entry if there
948096a99010fccf648814fecf38f75c689172d7wrowe * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
948096a99010fccf648814fecf38f75c689172d7wrowe * have no groups.
948096a99010fccf648814fecf38f75c689172d7wrowe * 2. Lock The cache and get the generic cache entry.
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * 3. Check if there is already a subgrouplist in this generic group's cache
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * A. If there is, go to step 4.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * B. If there isn't:
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * i) Use ldap_search to get the full list
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * of subgroup "members" (which may include non-group "members").
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * ii) Use uldap_cache_compare to strip the list down to just groups.
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * iii) Lock and add this stripped down list to the cache of the generic
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * subgroup to see if the subgroup contains the user and to get the subgroups
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * added to the
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * cache (with user-afinity, if they aren't already there).
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * A. If the user is in the subgroup, then we'll be returning
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * LDAP_COMPARE_TRUE.
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * uldap_cache_compare) then recursively call this function to get the
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * sub-subgroups added...
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * 5. Cleanup local allocations.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * 6. Return the final result.
948096a99010fccf648814fecf38f75c689172d7wrowe int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
948096a99010fccf648814fecf38f75c689172d7wrowe (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * Stop looking at deeper levels of nested groups if we have reached the
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * max. Since we already checked the top-level group in uldap_cache_compare,
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * we don't need to check it again here - so if max_subgroup_depth is set
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * to 0, we won't check it (i.e. that is why we check < rather than <=).
696218c49632c863d18b25fa52ab63617088cb38wrowe * We'll be calling uldap_cache_compare from here to check if the user is
948096a99010fccf648814fecf38f75c689172d7wrowe * in the next level before we recurse into that next level looking for
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * more subgroups.
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * 1. Check the "groupiness" of the specified basedn. Stopping at the first
948096a99010fccf648814fecf38f75c689172d7wrowe * TRUE return.
a9a4544168a37b43bd180b3703ccee995f27a80awrowe result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 2. Find previously created cache entry and check if there is already a
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * subgrouplist.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
0540a0b469147b52e858587270dba31c2aaa9e09wrowe /* make a comparison to the cache */
4f9c22c4f27571d54197be9674e1fc0d528192aestriker the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe compare_nodep = util_ald_cache_fetch(curl->compare_cache,
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * Found the generic group entry... but the user isn't in this
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * group or we wouldn't be here.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe /* Make a local copy of the subgroup list */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe "Making local copy of SGL for "
aa047239dedf0d26e8efecfade32e7337f35df19wrowe "group (%s)(objectClass=%s) ",
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb for (i = 0; i < compare_nodep->subgroupList->len; i++) {
a9a4544168a37b43bd180b3703ccee995f27a80awrowe /* No Cached SGL, retrieve from LDAP */
a9a4544168a37b43bd180b3703ccee995f27a80awrowe tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* No SGL aailable via LDAP either */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "no subgroups for %s",
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * Find the generic group cache entry and add the sgl we just retrieved.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb compare_nodep = util_ald_cache_fetch(curl->compare_cache,
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * The group entry we want to attach our SGL to doesn't exist.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * We only got here if we verified this DN was actually a group
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * based on the objectClass, but we can't call the compare function
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * while we already hold the cache lock -- only the insert.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe util_ald_cache_insert(curl->compare_cache, &the_compare_node);
aa047239dedf0d26e8efecfade32e7337f35df19wrowe compare_nodep = util_ald_cache_fetch(curl->compare_cache,
aa047239dedf0d26e8efecfade32e7337f35df19wrowe "util_ldap: Couldn't retrieve group entry "
aa047239dedf0d26e8efecfade32e7337f35df19wrowe "for %s from cache",
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * We have a valid cache entry and a locally generated SGL.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * Attach the SGL to the cache entry
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* We looked up an SGL for a group and found it to be empty */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe "Copying local SGL of len %d for group %s into cache",
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "Copy of SGL failed to obtain shared memory, "
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe "couldn't update cache");
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * tmp_local_sgl has either been created, or copied out of the cache
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * return false
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 4. Now loop through the subgroupList and call uldap_cache_compare
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * to check for the user.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb result = uldap_cache_compare(r, ldc, url, group, attrib, value);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 4.A. We found the user in the subgroup. Return
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * LDAP_COMPARE_TRUE.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "Found user %s in a subgroup (%s) at level %d of %d.",
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * 4.B. We didn't find the user in this subgroup, so recurse into
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * it and keep looking.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "User %s not found in subgroup (%s) at level %d of "
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
4f9c22c4f27571d54197be9674e1fc0d528192aestrikerstatic int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe const char ***retvals)
4f9c22c4f27571d54197be9674e1fc0d528192aestriker util_search_node_t *search_nodep; /* Cached search node */
e57e920838f31508f1418aa4c25ce55b345b2cebrbb (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
e57e920838f31508f1418aa4c25ce55b345b2cebrbb /* Get the cache node for this url */
e57e920838f31508f1418aa4c25ce55b345b2cebrbb curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker search_nodep = util_ald_cache_fetch(curl->search_cache,
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe /* found entry in search cache... */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Remove this item from the cache if its expired. If the sent
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * password doesn't match the storepassword, the entry will
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * be removed and readded later if the credentials pass
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * authentication.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* ...but entry is too old */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb util_ald_cache_remove(curl->search_cache, search_nodep);
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* ...and entry is valid */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* unlock this read lock */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * At this point, there is no valid cached search, so lets do the search.
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe /* try do the search */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ldc->reason = "ldap_search_ext_s() for user failed with server down";
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
30b4a330a5f651eb5198fa93dbb9f3d3594564c9stoddard * We should have found exactly one entry; to find a different
30b4a330a5f651eb5198fa93dbb9f3d3594564c9stoddard * number is an error.
30b4a330a5f651eb5198fa93dbb9f3d3594564c9stoddard ldc->reason = "User is not unique (search found two "
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "or more matches)";
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* Grab the dn, copy it into the pool, and free it again */
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe * A bind to the server with an empty password always succeeds, so
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * we check to ensure that the password is not empty. This implies
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * that users who actually do have empty passwords will never be
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * able to authenticate with this module. I don't see this as a big
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames * Attempt to bind with the retrieved dn and the password. If the bind
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames * fails, it means that the password is wrong (the dn obviously
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe * exists, since we just retrieved it)
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe result = uldap_simple_bind(ldc, (char *)*binddn, (char *)bindpw,
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames ldc->reason = "ldap_simple_bind() to check user credentials "
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames "failed with server down";
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe ldc->reason = "ldap_simple_bind() to check user credentials "
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe "timed out";
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames /* failure? if so - return */
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames ldc->reason = "ldap_simple_bind() to check user credentials failed";
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * We have just bound the connection to a different user and password
cadddb2c31d24d48f4017db4df0a29687432326cwrowe * combination, which might be reused unintentionally next time this
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * connection is used from the connection pool. To ensure no confusion,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker * we mark the connection as unbound.
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * Get values for the provided attributes.
beda1fb2f11c52ca4612460a5d5ba47398143efbwrowe while (attrs[k++]);
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe while (attrs[i]) {
c2cf53a40a9814eb91db2cdf820f97d943f21628coar int j = 0;
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe /* get values */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
cadddb2c31d24d48f4017db4df0a29687432326cwrowe * Add the new username to the search cache.
cadddb2c31d24d48f4017db4df0a29687432326cwrowe /* Search again to make sure that another thread didn't ready insert
cadddb2c31d24d48f4017db4df0a29687432326cwrowe * this node into the cache before we got here. If it does exist then
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe * update the lastbind
cadddb2c31d24d48f4017db4df0a29687432326cwrowe search_nodep = util_ald_cache_fetch(curl->search_cache,
cadddb2c31d24d48f4017db4df0a29687432326cwrowe /* Nothing in cache, insert new entry */
cadddb2c31d24d48f4017db4df0a29687432326cwrowe util_ald_cache_insert(curl->search_cache, &the_search_node);
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* Entry in cache is invalid, remove it and insert new one */
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe util_ald_cache_remove(curl->search_cache, search_nodep);
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe util_ald_cache_insert(curl->search_cache, &the_search_node);
cadddb2c31d24d48f4017db4df0a29687432326cwrowe /* Cache entry is valid, update lastbind */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * This function will return the DN of the entry matching userid.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * It is used to get the DN in case some other module than mod_auth_ldap
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * has authenticated the user.
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe * The function is basically a copy of uldap_cache_checkuserid
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * with password checking removed.
b45c1c292ff1fa635004ae81fa691f8cb3cdda85rbbstatic int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe util_search_node_t *search_nodep; /* Cached search node */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
4f9c22c4f27571d54197be9674e1fc0d528192aestriker /* Get the cache node for this url */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
c2cf53a40a9814eb91db2cdf820f97d943f21628coar search_nodep = util_ald_cache_fetch(curl->search_cache,
0540a0b469147b52e858587270dba31c2aaa9e09wrowe /* found entry in search cache... */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * Remove this item from the cache if its expired.
0540a0b469147b52e858587270dba31c2aaa9e09wrowe if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
0540a0b469147b52e858587270dba31c2aaa9e09wrowe /* ...but entry is too old */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar util_ald_cache_remove(curl->search_cache, search_nodep);
0540a0b469147b52e858587270dba31c2aaa9e09wrowe /* ...and entry is valid */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* unlock this read lock */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * At this point, there is no valid cached search, so lets do the search.
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
30b4a330a5f651eb5198fa93dbb9f3d3594564c9stoddard if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
30b4a330a5f651eb5198fa93dbb9f3d3594564c9stoddard /* try do the search */
30b4a330a5f651eb5198fa93dbb9f3d3594564c9stoddard NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ldc->reason = "ldap_search_ext_s() for user failed with server down";
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * We should have found exactly one entry; to find a different
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * number is an error.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (count == 0 )
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "or more matches)";
if (attrs) {
while (attrs[k++]);
numvals = k;
while (attrs[i]) {
char **values;
if (curl) {
return LDAP_SUCCESS;
const char *bytes)
&ldap_module);
return err;
return NULL;
const char *file)
&ldap_module);
return err;
if (file) {
return NULL;
const char *ttl)
&ldap_module);
return err;
return NULL;
const char *size)
&ldap_module);
return err;
return NULL;
const char *ttl)
&ldap_module);
return err;
return NULL;
const char *size)
&ldap_module);
return err;
return NULL;
return APR_LDAP_CA_TYPE_DER;
return APR_LDAP_CA_TYPE_BASE64;
return APR_LDAP_CA_TYPE_CERT7_DB;
return APR_LDAP_CA_TYPE_SECMOD;
return APR_LDAP_CERT_TYPE_DER;
return APR_LDAP_CERT_TYPE_BASE64;
return APR_LDAP_CERT_TYPE_PFX;
return APR_LDAP_CERT_TYPE_KEY3_DB;
return APR_LDAP_CERT_TYPE_NICKNAME;
return APR_LDAP_KEY_TYPE_DER;
return APR_LDAP_KEY_TYPE_BASE64;
return APR_LDAP_KEY_TYPE_PFX;
return APR_LDAP_CA_TYPE_UNKNOWN;
void *dummy,
const char *type,
const char *file,
const char *password)
&ldap_module);
int cert_type = 0;
return err;
if (type) {
!= APR_SUCCESS))
return(NULL);
void *config,
const char *type,
const char *file,
const char *password)
int cert_type = 0;
if (type) {
type);
!= APR_SUCCESS))
return(NULL);
const char *mode)
&ldap_module);
mode);
return(NULL);
void *dummy,
int mode)
&ldap_module);
return err;
return(NULL);
void *dummy,
const char *ttl)
#ifdef LDAP_OPT_NETWORK_TIMEOUT
&ldap_module);
return err;
#ifdef LDAP_OPT_NETWORK_TIMEOUT
return NULL;
void *config,
int mode)
return(NULL);
void *config,
const char *arg) {
&ldap_module);
return err;
#ifndef AP_LDAP_OPT_DEBUG
return NULL;
void *config,
const char *hop_limit)
return "LDAPReferralHopLimit must be greater than zero (Use 'LDAPReferrals Off' to disable referral chasing)";
return NULL;
return dc;
void *dummy,
const char *val)
long timeout;
char *endptr;
&ldap_module);
return err;
if (timeout < 0) {
if (timeout) {
timeout);
#ifndef LDAP_OPT_TIMEOUT
return NULL;
void *dummy,
const char *val)
&ldap_module);
if (timeout < 0) {
return NULL;
#if APR_HAS_THREADS
return st;
void *overridesv)
#if APR_HAS_THREADS
return st;
return APR_SUCCESS;
APR_LOCK_DEFAULT, 0);
return result;
return OK;
&ldap_module);
int rc;
NULL);
return OK;
return DONE;
return result;
while (s_vhost) {
&ldap_module);
NULL,
&(result_err));
apr_ldap_rebind_init (p);
#ifdef AP_LDAP_OPT_DEBUG
return(OK);
&ldap_module);
{NULL}