1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * util_ldap.c: LDAP things
20 * Original code from auth_ldap module for Apache v1.3:
21 * Copyright 1998, 1999 Enbridge Pipelines Inc.
22 * Copyright 1999-2001 Dave Carrigan
26 #include "http_config.h"
27 #include "http_core.h"
29 #include "http_protocol.h"
30 #include "http_request.h"
31 #include "util_mutex.h"
32 #include "util_ldap.h"
33 #include "util_ldap_cache.h"
35 #include <apr_strings.h>
42 #error mod_ldap requires APR-util to have LDAP support built in
45 /* Default define for ldap functions that need a SIZELIMIT but
46 * do not have the define
47 * XXX This should be removed once a supporting #define is
48 * released through APR-Util.
50 #ifndef APR_LDAP_SIZELIMIT
51 #define APR_LDAP_SIZELIMIT -1
54 #ifdef LDAP_OPT_DEBUG_LEVEL
55 #define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG_LEVEL
58 #define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG
62 #define AP_LDAP_HOPLIMIT_UNSET -1
63 #define AP_LDAP_CHASEREFERRALS_OFF 0
64 #define AP_LDAP_CHASEREFERRALS_ON 1
66 #define AP_LDAP_CONNPOOL_DEFAULT -1
67 #define AP_LDAP_CONNPOOL_INFINITE -2
69 module AP_MODULE_DECLARE_DATA ldap_module;
70 static const char *ldap_cache_mutex_type = "ldap-cache";
71 static apr_status_t uldap_connection_unbind(void *param);
73 #define LDAP_CACHE_LOCK() do { \
74 if (st->util_ldap_cache_lock) \
75 apr_global_mutex_lock(st->util_ldap_cache_lock); \
78 #define LDAP_CACHE_UNLOCK() do { \
79 if (st->util_ldap_cache_lock) \
80 apr_global_mutex_unlock(st->util_ldap_cache_lock); \
83 static void util_ldap_strdup (char **str, const char *newstr)
91 *str = strdup(newstr);
99 * This handler generates a status page about the current performance of
100 * the LDAP cache. It is enabled as follows:
102 * <Location /ldap-status>
103 * SetHandler ldap-status
107 static int util_ldap_handler(request_rec *r)
109 util_ldap_state_t *st;
111 r->allowed |= (1 << M_GET);
112 if (r->method_number != M_GET) {
116 if (strcmp(r->handler, "ldap-status")) {
120 st = (util_ldap_state_t *) ap_get_module_config(r->server->module_config,
123 ap_set_content_type(r, "text/html; charset=ISO-8859-1");
128 ap_rputs(DOCTYPE_HTML_3_2
129 "<html><head><title>LDAP Cache Information</title></head>\n", r);
130 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
133 util_ald_cache_display(r, st);
140 /* ------------------------------------------------------------------ */
142 * Closes an LDAP connection by unlocking it. The next time
143 * uldap_connection_find() is called this connection will be
144 * available for reuse.
146 static void uldap_connection_close(util_ldap_connection_t *ldc)
149 /* We leave bound LDAP connections floating around in our pool,
150 * but always check/fix the binddn/bindpw when we take them out
154 uldap_connection_unbind(ldc);
157 /* mark our connection as available for reuse */
158 ldc->freed = apr_time_now();
160 apr_thread_mutex_unlock(ldc->lock);
167 * Destroys an LDAP connection by unbinding and closing the connection to
168 * the LDAP server. It is used to bring the connection back to a known
169 * state after an error.
171 static apr_status_t uldap_connection_unbind(void *param)
173 util_ldap_connection_t *ldc = param;
177 ldap_unbind_s(ldc->ldap);
182 /* forget the rebind info for this conn */
183 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
184 apr_ldap_rebind_remove(ldc->ldap);
185 apr_pool_clear(ldc->rebind_pool);
192 /* not presently used, not part of the API */
195 * util_ldap_connection_remove frees all storage associated with the LDAP
196 * connection and removes it completely from the per-virtualhost list of
199 * The caller should hold the lock for this connection
201 static apr_status_t util_ldap_connection_remove (void *param) {
202 util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
203 util_ldap_state_t *st;
205 if (!ldc) return APR_SUCCESS;
209 uldap_connection_unbind(ldc);
212 apr_thread_mutex_lock(st->mutex);
215 /* Remove ldc from the list */
216 for (l=st->connections; l; l=l->next) {
219 prev->next = l->next;
222 st->connections = l->next;
230 free((void*)ldc->bindpw);
233 free((void*)ldc->binddn);
237 apr_thread_mutex_unlock(ldc->lock);
238 apr_thread_mutex_unlock(st->mutex);
241 /* Destory the pool associated with this connection */
243 apr_pool_destroy(ldc->pool);
249 static int uldap_connection_init(request_rec *r,
250 util_ldap_connection_t *ldc)
252 int rc = 0, ldap_option = 0;
253 int version = LDAP_VERSION3;
254 apr_ldap_err_t *result = NULL;
255 #ifdef LDAP_OPT_NETWORK_TIMEOUT
256 struct timeval connectionTimeout = {10,0}; /* 10 second connection timeout */
258 util_ldap_state_t *st =
259 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
262 /* Since the host will include a port if the default port is not used,
263 * always specify the default ports for the port parameter. This will
264 * allow a host string that contains multiple hosts the ability to mix
265 * some hosts with ports and some without. All hosts which do not
266 * specify a port will use the default port.
268 apr_ldap_init(r->pool, &(ldc->ldap),
270 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
274 if (NULL == result) {
275 /* something really bad happened */
277 if (NULL == ldc->reason) {
278 ldc->reason = "LDAP: ldap initialization failed";
280 return(APR_EGENERAL);
284 ldc->reason = result->reason;
289 if (NULL == ldc->ldap)
292 if (NULL == ldc->reason) {
293 ldc->reason = "LDAP: ldap initialization failed";
296 ldc->reason = result->reason;
301 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
302 /* Now that we have an ldap struct, add it to the referral list for rebinds. */
303 rc = apr_ldap_rebind_add(ldc->rebind_pool, ldc->ldap, ldc->binddn, ldc->bindpw);
304 if (rc != APR_SUCCESS) {
305 ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,
306 "LDAP: Unable to add rebind cross reference entry. Out of memory?");
307 uldap_connection_unbind(ldc);
308 ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
313 /* always default to LDAP V3 */
314 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
316 /* set client certificates */
317 if (!apr_is_empty_array(ldc->client_certs)) {
318 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
319 ldc->client_certs, &(result));
320 if (LDAP_SUCCESS != result->rc) {
321 uldap_connection_unbind( ldc );
322 ldc->reason = result->reason;
327 /* switch on SSL/TLS */
328 if (APR_LDAP_NONE != ldc->secure) {
329 apr_ldap_set_option(r->pool, ldc->ldap,
330 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
331 if (LDAP_SUCCESS != result->rc) {
332 uldap_connection_unbind( ldc );
333 ldc->reason = result->reason;
338 /* Set the alias dereferencing option */
339 ldap_option = ldc->deref;
340 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
342 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
343 /* Set options for rebind and referrals. */
344 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
345 "LDAP: Setting referrals to %s.",
346 ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"));
347 apr_ldap_set_option(r->pool, ldc->ldap,
348 APR_LDAP_OPT_REFERRALS,
349 (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
350 LDAP_OPT_ON : LDAP_OPT_OFF),
352 if (result->rc != LDAP_SUCCESS) {
353 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
354 "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
355 ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"),
357 result->reason = "Unable to set LDAP_OPT_REFERRALS.";
358 ldc->reason = result->reason;
359 uldap_connection_unbind(ldc);
363 if ((ldc->ReferralHopLimit != AP_LDAP_HOPLIMIT_UNSET) && ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
364 /* Referral hop limit - only if referrals are enabled and a hop limit is explicitly requested */
365 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
366 "Setting referral hop limit to %d.",
367 ldc->ReferralHopLimit);
368 apr_ldap_set_option(r->pool, ldc->ldap,
369 APR_LDAP_OPT_REFHOPLIMIT,
370 (void *)&ldc->ReferralHopLimit,
372 if (result->rc != LDAP_SUCCESS) {
373 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
374 "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
375 ldc->ReferralHopLimit,
377 result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
378 ldc->reason = result->reason;
379 uldap_connection_unbind(ldc);
385 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
386 #ifdef APR_LDAP_OPT_VERIFY_CERT
387 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
388 &(st->verify_svr_cert), &(result));
390 #if defined(LDAPSSL_VERIFY_SERVER)
391 if (st->verify_svr_cert) {
392 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
395 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
397 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
398 /* This is not a per-connection setting so just pass NULL for the
399 Ldap connection handle */
400 if (st->verify_svr_cert) {
401 int i = LDAP_OPT_X_TLS_DEMAND;
402 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
405 int i = LDAP_OPT_X_TLS_NEVER;
406 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
411 #ifdef LDAP_OPT_NETWORK_TIMEOUT
412 if (st->connectionTimeout > 0) {
413 connectionTimeout.tv_sec = st->connectionTimeout;
416 if (st->connectionTimeout >= 0) {
417 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
418 (void *)&connectionTimeout, &(result));
419 if (APR_SUCCESS != rc) {
420 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
421 "LDAP: Could not set the connection timeout");
426 #ifdef LDAP_OPT_TIMEOUT
428 * LDAP_OPT_TIMEOUT is not portable, but it influences all synchronous ldap
429 * function calls and not just ldap_search_ext_s(), which accepts a timeout
431 * XXX: It would be possible to simulate LDAP_OPT_TIMEOUT by replacing all
432 * XXX: synchronous ldap function calls with asynchronous calls and using
433 * XXX: ldap_result() with a timeout.
436 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_TIMEOUT,
437 st->opTimeout, &(result));
438 if (APR_SUCCESS != rc) {
439 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
440 "LDAP: Could not set LDAP_OPT_TIMEOUT");
449 * Replacement function for ldap_simple_bind_s() with a timeout.
450 * To do this in a portable way, we have to use ldap_simple_bind() and
453 * Returns LDAP_SUCCESS on success; and an error code on failure
455 static int uldap_simple_bind(util_ldap_connection_t *ldc, char *binddn,
456 char* bindpw, struct timeval *timeout)
460 int msgid = ldap_simple_bind(ldc->ldap, binddn, bindpw);
462 ldc->reason = "LDAP: ldap_simple_bind() failed";
463 /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
466 rc = ldap_result(ldc->ldap, msgid, 0, timeout, &result);
468 ldc->reason = "LDAP: ldap_simple_bind() result retrieval failed";
469 /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
473 ldc->reason = "LDAP: ldap_simple_bind() timed out";
475 } else if (ldap_parse_result(ldc->ldap, result, &rc, NULL, NULL, NULL,
477 ldc->reason = "LDAP: ldap_simple_bind() parse result failed";
483 * Connect to the LDAP server and binds. Does not connect if already
484 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
486 * Returns LDAP_SUCCESS on success; and an error code on failure
488 static int uldap_connection_open(request_rec *r,
489 util_ldap_connection_t *ldc)
493 int new_connection = 0;
494 util_ldap_state_t *st;
496 /* sanity check for NULL */
501 /* If the connection is already bound, return
505 ldc->reason = "LDAP: connection open successful (already bound)";
509 /* create the ldap session handle
511 if (NULL == ldc->ldap)
514 rc = uldap_connection_init( r, ldc );
515 if (LDAP_SUCCESS != rc)
522 st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
525 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
526 * returned. If LDAP_TIMEOUT is returned on the first try, maybe the
527 * connection was idle for a long time and has been dropped by a firewall.
528 * In this case close the connection immediately and try again.
530 * On Success or any other error, break out of the loop.
532 * NOTE: Looping is probably not a great idea. If the server isn't
533 * responding the chances it will respond after a few tries are poor.
534 * However, the original code looped and it only happens on
535 * the error condition.
537 for (failures=0; failures<10; failures++)
539 rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
541 if ((AP_LDAP_IS_SERVER_DOWN(rc) && failures == 5) ||
542 (rc == LDAP_TIMEOUT && failures == 0))
544 if (rc == LDAP_TIMEOUT && !new_connection) {
545 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
546 "ldap_simple_bind() timed out on reused "
547 "connection, dropped by firewall?");
549 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
550 "attempt to re-init the connection");
551 uldap_connection_unbind( ldc );
552 rc = uldap_connection_init( r, ldc );
553 if (LDAP_SUCCESS != rc)
558 else if (!AP_LDAP_IS_SERVER_DOWN(rc)) {
561 ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
562 "ldap_simple_bind() failed with server down "
563 "(try %d)", failures + 1);
566 /* free the handle if there was an error
568 if (LDAP_SUCCESS != rc)
570 uldap_connection_unbind(ldc);
571 ldc->reason = "LDAP: ldap_simple_bind() failed";
575 ldc->reason = "LDAP: connection open successful";
583 * Compare client certificate arrays.
585 * Returns 1 on compare failure, 0 otherwise.
587 static int compare_client_certs(apr_array_header_t *srcs,
588 apr_array_header_t *dests)
591 struct apr_ldap_opt_tls_cert_t *src, *dest;
593 /* arrays both NULL? if so, then equal */
594 if (srcs == NULL && dests == NULL) {
598 /* arrays different length or either NULL? If so, then not equal */
599 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
603 /* run an actual comparison */
604 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
605 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
606 for (i = 0; i < srcs->nelts; i++) {
607 if ((strcmp(src[i].path, dest[i].path)) ||
608 (src[i].type != dest[i].type) ||
609 /* One is passwordless? If so, then not equal */
610 ((src[i].password == NULL) ^ (dest[i].password == NULL)) ||
611 (src[i].password != NULL && dest[i].password != NULL &&
612 strcmp(src[i].password, dest[i].password))) {
617 /* if we got here, the cert arrays were identical */
624 * Find an existing ldap connection struct that matches the
625 * provided ldap connection parameters.
627 * If not found in the cache, a new ldc structure will be allocated
628 * from st->pool and returned to the caller. If found in the cache,
629 * a pointer to the existing ldc structure will be returned.
631 static util_ldap_connection_t *
632 uldap_connection_find(request_rec *r,
633 const char *host, int port,
634 const char *binddn, const char *bindpw,
635 deref_options deref, int secure)
637 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
638 int secureflag = secure;
639 apr_time_t now = apr_time_now();
641 util_ldap_state_t *st =
642 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
644 util_ldap_config_t *dc =
645 (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
648 /* mutex lock this function */
649 apr_thread_mutex_lock(st->mutex);
652 if (secure < APR_LDAP_NONE) {
653 secureflag = st->secure;
656 /* Search for an exact connection match in the list that is not
659 for (l=st->connections,p=NULL; l; l=l->next) {
661 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
663 if ( (l->port == port) && (strcmp(l->host, host) == 0)
664 && ((!l->binddn && !binddn) || (l->binddn && binddn
665 && !strcmp(l->binddn, binddn)))
666 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
667 && !strcmp(l->bindpw, bindpw)))
668 && (l->deref == deref) && (l->secure == secureflag)
669 && !compare_client_certs(dc->client_certs, l->client_certs))
671 if (st->connectionPoolTTL > 0) {
672 if (l->bound && (now - l->freed) > st->connectionPoolTTL) {
673 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
674 "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
675 (now - l->freed) / APR_USEC_PER_SEC);
676 uldap_connection_unbind(l);
677 /* Go ahead and use it, so we don't create more just to unbind some other old ones */
686 /* If this connection didn't match the criteria, then we
687 * need to unlock the mutex so it is available to be reused.
689 apr_thread_mutex_unlock(l->lock);
695 /* If nothing found, search again, but we don't care about the
696 * binddn and bindpw this time.
699 for (l=st->connections,p=NULL; l; l=l->next) {
701 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
704 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
705 (l->deref == deref) && (l->secure == secureflag) &&
706 !compare_client_certs(dc->client_certs, l->client_certs))
708 /* the bind credentials have changed */
709 /* no check for connectionPoolTTL, since we are unbinding any way */
710 uldap_connection_unbind(l);
712 util_ldap_strdup((char**)&(l->binddn), binddn);
713 util_ldap_strdup((char**)&(l->bindpw), bindpw);
717 /* If this connection didn't match the criteria, then we
718 * need to unlock the mutex so it is available to be reused.
720 apr_thread_mutex_unlock(l->lock);
727 /* artificially disable cache */
730 /* If no connection was found after the second search, we
735 if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
736 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
737 "util_ldap: Failed to create memory pool");
739 apr_thread_mutex_unlock(st->mutex);
745 * Add the new connection entry to the linked list. Note that we
746 * don't actually establish an LDAP connection yet; that happens
747 * the first time authentication is requested.
750 /* create the details of this connection in the new pool */
751 l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
756 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
757 apr_thread_mutex_lock(l->lock);
760 l->host = apr_pstrdup(l->pool, host);
763 util_ldap_strdup((char**)&(l->binddn), binddn);
764 util_ldap_strdup((char**)&(l->bindpw), bindpw);
765 l->ChaseReferrals = dc->ChaseReferrals;
766 l->ReferralHopLimit = dc->ReferralHopLimit;
768 /* The security mode after parsing the URL will always be either
769 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
770 * If the security setting is NONE, override it to the security
771 * setting optionally supplied by the admin using LDAPTrustedMode
773 l->secure = secureflag;
775 /* save away a copy of the client cert list that is presently valid */
776 l->client_certs = apr_array_copy_hdr(l->pool, dc->client_certs);
778 /* whether or not to keep this connection in the pool when it's returned */
779 l->keep = (st->connectionPoolTTL == 0) ? 0 : 1;
781 if (l->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
782 if (apr_pool_create(&(l->rebind_pool), l->pool) != APR_SUCCESS) {
783 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
784 "util_ldap: Failed to create memory pool");
786 apr_thread_mutex_unlock(st->mutex);
801 apr_thread_mutex_unlock(st->mutex);
806 /* ------------------------------------------------------------------ */
809 * Compares two DNs to see if they're equal. The only way to do this correctly
810 * is to search for the dn and then do ldap_get_dn() on the result. This should
811 * match the initial dn, since it would have been also retrieved with
812 * ldap_get_dn(). This is expensive, so if the configuration value
813 * compare_dn_on_server is false, just does an ordinary strcmp.
815 * The lock for the ldap cache should already be acquired.
817 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
818 const char *url, const char *dn,
819 const char *reqdn, int compare_dn_on_server)
822 util_url_node_t *curl;
823 util_url_node_t curnode;
824 util_dn_compare_node_t *node;
825 util_dn_compare_node_t newnode;
827 LDAPMessage *res, *entry;
830 util_ldap_state_t *st = (util_ldap_state_t *)
831 ap_get_module_config(r->server->module_config,
834 /* get cache entry (or create one) */
838 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
840 curl = util_ald_create_caches(st, url);
844 /* a simple compare? */
845 if (!compare_dn_on_server) {
846 /* unlock this read lock */
847 if (strcmp(dn, reqdn)) {
848 ldc->reason = "DN Comparison FALSE (direct strcmp())";
849 return LDAP_COMPARE_FALSE;
852 ldc->reason = "DN Comparison TRUE (direct strcmp())";
853 return LDAP_COMPARE_TRUE;
858 /* no - it's a server side compare */
861 /* is it in the compare cache? */
862 newnode.reqdn = (char *)reqdn;
863 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
865 /* If it's in the cache, it's good */
866 /* unlock this read lock */
868 ldc->reason = "DN Comparison TRUE (cached)";
869 return LDAP_COMPARE_TRUE;
872 /* unlock this read lock */
877 if (failures++ > 10) {
878 /* too many failures */
882 /* make a server connection */
883 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
884 /* connect to server failed */
888 /* search for reqdn */
889 result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
890 "(objectclass=*)", NULL, 1,
891 NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
892 if (AP_LDAP_IS_SERVER_DOWN(result))
894 ldc->reason = "DN Comparison ldap_search_ext_s() "
895 "failed with server down";
896 uldap_connection_unbind(ldc);
899 if (result == LDAP_TIMEOUT && failures == 0) {
901 * we are reusing a connection that doesn't seem to be active anymore
902 * (firewall state drop?), let's try a new connection.
904 ldc->reason = "DN Comparison ldap_search_ext_s() "
905 "failed with timeout";
906 uldap_connection_unbind(ldc);
909 if (result != LDAP_SUCCESS) {
910 /* search for reqdn failed - no match */
911 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
915 entry = ldap_first_entry(ldc->ldap, res);
916 searchdn = ldap_get_dn(ldc->ldap, entry);
919 if (strcmp(dn, searchdn) != 0) {
920 /* compare unsuccessful */
921 ldc->reason = "DN Comparison FALSE (checked on server)";
922 result = LDAP_COMPARE_FALSE;
926 /* compare successful - add to the compare cache */
928 newnode.reqdn = (char *)reqdn;
929 newnode.dn = (char *)dn;
931 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
933 || (strcmp(reqdn, node->reqdn) != 0)
934 || (strcmp(dn, node->dn) != 0))
936 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
940 ldc->reason = "DN Comparison TRUE (checked on server)";
941 result = LDAP_COMPARE_TRUE;
943 ldap_memfree(searchdn);
949 * Does an generic ldap_compare operation. It accepts a cache that it will use
950 * to lookup the compare in the cache. We cache two kinds of compares
951 * (require group compares) and (require user compares). Each compare has a
952 * different cache node: require group includes the DN; require user does not
953 * because the require user cache is owned by the
956 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
957 const char *url, const char *dn,
958 const char *attrib, const char *value)
961 util_url_node_t *curl;
962 util_url_node_t curnode;
963 util_compare_node_t *compare_nodep;
964 util_compare_node_t the_compare_node;
965 apr_time_t curtime = 0; /* silence gcc -Wall */
968 util_ldap_state_t *st = (util_ldap_state_t *)
969 ap_get_module_config(r->server->module_config,
972 /* get cache entry (or create one) */
975 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
977 curl = util_ald_create_caches(st, url);
982 /* make a comparison to the cache */
984 curtime = apr_time_now();
986 the_compare_node.dn = (char *)dn;
987 the_compare_node.attrib = (char *)attrib;
988 the_compare_node.value = (char *)value;
989 the_compare_node.result = 0;
990 the_compare_node.sgl_processed = 0;
991 the_compare_node.subgroupList = NULL;
993 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
996 if (compare_nodep != NULL) {
998 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
999 /* ...but it is too old */
1000 util_ald_cache_remove(curl->compare_cache, compare_nodep);
1003 /* ...and it is good */
1004 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
1005 ldc->reason = "Comparison true (cached)";
1007 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
1008 ldc->reason = "Comparison false (cached)";
1010 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
1011 ldc->reason = "Comparison no such attribute (cached)";
1014 ldc->reason = "Comparison undefined (cached)";
1017 /* record the result code to return with the reason... */
1018 result = compare_nodep->result;
1019 /* and unlock this read lock */
1020 LDAP_CACHE_UNLOCK();
1024 /* unlock this read lock */
1025 LDAP_CACHE_UNLOCK();
1029 if (failures++ > 10) {
1030 /* too many failures */
1034 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1035 /* connect failed */
1039 result = ldap_compare_s(ldc->ldap,
1043 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1044 /* connection failed - try again */
1045 ldc->reason = "ldap_compare_s() failed with server down";
1046 uldap_connection_unbind(ldc);
1049 if (result == LDAP_TIMEOUT && failures == 0) {
1051 * we are reusing a connection that doesn't seem to be active anymore
1052 * (firewall state drop?), let's try a new connection.
1054 ldc->reason = "ldap_compare_s() failed with timeout";
1055 uldap_connection_unbind(ldc);
1059 ldc->reason = "Comparison complete";
1060 if ((LDAP_COMPARE_TRUE == result) ||
1061 (LDAP_COMPARE_FALSE == result) ||
1062 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
1064 /* compare completed; caching result */
1066 the_compare_node.lastcompare = curtime;
1067 the_compare_node.result = result;
1068 the_compare_node.sgl_processed = 0;
1069 the_compare_node.subgroupList = NULL;
1071 /* If the node doesn't exist then insert it, otherwise just update
1072 * it with the last results
1074 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1076 if ( (compare_nodep == NULL)
1077 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
1078 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
1079 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
1083 junk = util_ald_cache_insert(curl->compare_cache,
1086 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1087 "cache_compare: Cache insertion failure.");
1091 compare_nodep->lastcompare = curtime;
1092 compare_nodep->result = result;
1094 LDAP_CACHE_UNLOCK();
1096 if (LDAP_COMPARE_TRUE == result) {
1097 ldc->reason = "Comparison true (adding to cache)";
1098 return LDAP_COMPARE_TRUE;
1100 else if (LDAP_COMPARE_FALSE == result) {
1101 ldc->reason = "Comparison false (adding to cache)";
1102 return LDAP_COMPARE_FALSE;
1105 ldc->reason = "Comparison no such attribute (adding to cache)";
1106 return LDAP_NO_SUCH_ATTRIBUTE;
1113 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
1114 util_ldap_connection_t *ldc,
1117 char **subgroupAttrs,
1118 apr_array_header_t *subgroupclasses)
1121 int result = LDAP_COMPARE_FALSE;
1122 util_compare_subgroup_t *res = NULL;
1123 LDAPMessage *sga_res, *entry;
1124 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
1125 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
1127 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1129 if (!subgroupAttrs) {
1135 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
1137 if (failures++ > 10) {
1138 /* too many failures */
1142 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1143 /* connect failed */
1147 /* try to do the search */
1148 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
1149 (char *)"cn=*", subgroupAttrs, 0,
1150 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
1151 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1152 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
1154 uldap_connection_unbind(ldc);
1157 if (result == LDAP_TIMEOUT && failures == 0) {
1159 * we are reusing a connection that doesn't seem to be active anymore
1160 * (firewall state drop?), let's try a new connection.
1162 ldc->reason = "ldap_search_ext_s() for subgroups failed with timeout";
1163 uldap_connection_unbind(ldc);
1167 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1168 if (result != LDAP_SUCCESS) {
1169 ldc->reason = "ldap_search_ext_s() for subgroups failed";
1173 entry = ldap_first_entry(ldc->ldap, sga_res);
1176 * Get values for the provided sub-group attributes.
1178 if (subgroupAttrs) {
1179 int indx = 0, tmp_sgcIndex;
1181 while (subgroupAttrs[indx]) {
1185 /* Get *all* matching "member" values from this group. */
1186 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1191 * Now we are going to pare the subgroup members of this group
1192 * to *just* the subgroups, add them to the compare_nodep, and
1193 * then proceed to check the new level of subgroups.
1195 while (values[val_index]) {
1196 /* Check if this entry really is a group. */
1198 result = LDAP_COMPARE_FALSE;
1199 while ((tmp_sgcIndex < subgroupclasses->nelts)
1200 && (result != LDAP_COMPARE_TRUE)) {
1201 result = uldap_cache_compare(r, ldc, url,
1204 sgc_ents[tmp_sgcIndex].name
1207 if (result != LDAP_COMPARE_TRUE) {
1211 /* It's a group, so add it to the array. */
1212 if (result == LDAP_COMPARE_TRUE) {
1213 char **newgrp = (char **) apr_array_push(subgroups);
1214 *newgrp = apr_pstrdup(r->pool, values[val_index]);
1218 ldap_value_free(values);
1224 ldap_msgfree(sga_res);
1226 if (subgroups->nelts > 0) {
1227 /* We need to fill in tmp_local_subgroups using the data from LDAP */
1230 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1231 res->subgroupDNs = apr_pcalloc(r->pool,
1232 sizeof(char *) * (subgroups->nelts));
1233 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1234 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1244 * Does a recursive lookup operation to try to find a user within (cached)
1245 * nested groups. It accepts a cache that it will use to lookup previous
1246 * compare attempts. We cache two kinds of compares (require group compares)
1247 * and (require user compares). Each compare has a different cache node:
1248 * require group includes the DN; require user does not because the require
1249 * user cache is owned by the
1251 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1254 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1255 * generic, user-agnostic, cached group entry. This will create a new generic
1256 * cache entry if there
1257 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1259 * 2. Lock The cache and get the generic cache entry.
1260 * 3. Check if there is already a subgrouplist in this generic group's cache
1262 * A. If there is, go to step 4.
1263 * B. If there isn't:
1264 * i) Use ldap_search to get the full list
1265 * of subgroup "members" (which may include non-group "members").
1266 * ii) Use uldap_cache_compare to strip the list down to just groups.
1267 * iii) Lock and add this stripped down list to the cache of the generic
1269 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1271 * subgroup to see if the subgroup contains the user and to get the subgroups
1273 * cache (with user-afinity, if they aren't already there).
1274 * A. If the user is in the subgroup, then we'll be returning
1275 * LDAP_COMPARE_TRUE.
1276 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1277 * uldap_cache_compare) then recursively call this function to get the
1278 * sub-subgroups added...
1279 * 5. Cleanup local allocations.
1280 * 6. Return the final result.
1283 static int uldap_cache_check_subgroups(request_rec *r,
1284 util_ldap_connection_t *ldc,
1285 const char *url, const char *dn,
1286 const char *attrib, const char *value,
1287 char **subgroupAttrs,
1288 apr_array_header_t *subgroupclasses,
1289 int cur_subgroup_depth,
1290 int max_subgroup_depth)
1292 int result = LDAP_COMPARE_FALSE;
1293 util_url_node_t *curl;
1294 util_url_node_t curnode;
1295 util_compare_node_t *compare_nodep;
1296 util_compare_node_t the_compare_node;
1297 util_compare_subgroup_t *tmp_local_sgl = NULL;
1298 int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
1299 struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
1300 (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1301 util_ldap_state_t *st = (util_ldap_state_t *)
1302 ap_get_module_config(r->server->module_config,
1306 * Stop looking at deeper levels of nested groups if we have reached the
1307 * max. Since we already checked the top-level group in uldap_cache_compare,
1308 * we don't need to check it again here - so if max_subgroup_depth is set
1309 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1310 * We'll be calling uldap_cache_compare from here to check if the user is
1311 * in the next level before we recurse into that next level looking for
1314 if (cur_subgroup_depth >= max_subgroup_depth) {
1315 return LDAP_COMPARE_FALSE;
1319 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1322 while ((base_sgcIndex < subgroupclasses->nelts)
1323 && (result != LDAP_COMPARE_TRUE)) {
1324 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1325 sgc_ents[base_sgcIndex].name);
1326 if (result != LDAP_COMPARE_TRUE) {
1331 if (result != LDAP_COMPARE_TRUE) {
1332 ldc->reason = "DN failed group verification.";
1337 * 2. Find previously created cache entry and check if there is already a
1342 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1343 LDAP_CACHE_UNLOCK();
1345 if (curl && curl->compare_cache) {
1346 /* make a comparison to the cache */
1349 the_compare_node.dn = (char *)dn;
1350 the_compare_node.attrib = (char *)"objectClass";
1351 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1352 the_compare_node.result = 0;
1353 the_compare_node.sgl_processed = 0;
1354 the_compare_node.subgroupList = NULL;
1356 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1359 if (compare_nodep != NULL) {
1361 * Found the generic group entry... but the user isn't in this
1362 * group or we wouldn't be here.
1364 if (compare_nodep->sgl_processed) {
1365 if (compare_nodep->subgroupList) {
1366 /* Make a local copy of the subgroup list */
1368 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1369 "Making local copy of SGL for "
1370 "group (%s)(objectClass=%s) ",
1371 dn, (char *)sgc_ents[base_sgcIndex].name);
1372 tmp_local_sgl = apr_pcalloc(r->pool,
1373 sizeof(util_compare_subgroup_t));
1374 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1375 tmp_local_sgl->subgroupDNs =
1376 apr_pcalloc(r->pool,
1377 sizeof(char *) * compare_nodep->subgroupList->len);
1378 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1379 tmp_local_sgl->subgroupDNs[i] =
1380 apr_pstrdup(r->pool,
1381 compare_nodep->subgroupList->subgroupDNs[i]);
1385 sgl_cached_empty = 1;
1389 LDAP_CACHE_UNLOCK();
1392 if (!tmp_local_sgl && !sgl_cached_empty) {
1393 /* No Cached SGL, retrieve from LDAP */
1394 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1395 "no cached SGL for %s, retrieving from LDAP", dn);
1396 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1398 if (!tmp_local_sgl) {
1399 /* No SGL aailable via LDAP either */
1400 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "no subgroups for %s",
1404 if (curl && curl->compare_cache) {
1406 * Find the generic group cache entry and add the sgl we just retrieved.
1410 the_compare_node.dn = (char *)dn;
1411 the_compare_node.attrib = (char *)"objectClass";
1412 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1413 the_compare_node.result = 0;
1414 the_compare_node.sgl_processed = 0;
1415 the_compare_node.subgroupList = NULL;
1417 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1420 if (compare_nodep == NULL) {
1422 * The group entry we want to attach our SGL to doesn't exist.
1423 * We only got here if we verified this DN was actually a group
1424 * based on the objectClass, but we can't call the compare function
1425 * while we already hold the cache lock -- only the insert.
1427 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1428 "Cache entry for %s doesn't exist", dn);
1429 the_compare_node.result = LDAP_COMPARE_TRUE;
1430 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
1431 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1433 if (compare_nodep == NULL) {
1434 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1435 "util_ldap: Couldn't retrieve group entry "
1436 "for %s from cache",
1442 * We have a valid cache entry and a locally generated SGL.
1443 * Attach the SGL to the cache entry
1445 if (compare_nodep && !compare_nodep->sgl_processed) {
1446 if (!tmp_local_sgl) {
1447 /* We looked up an SGL for a group and found it to be empty */
1448 if (compare_nodep->subgroupList == NULL) {
1449 compare_nodep->sgl_processed = 1;
1453 util_compare_subgroup_t *sgl_copy =
1454 util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1455 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1456 "Copying local SGL of len %d for group %s into cache",
1457 tmp_local_sgl->len, dn);
1459 if (compare_nodep->subgroupList) {
1460 util_ald_sgl_free(curl->compare_cache,
1461 &(compare_nodep->subgroupList));
1463 compare_nodep->subgroupList = sgl_copy;
1464 compare_nodep->sgl_processed = 1;
1467 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1468 "Copy of SGL failed to obtain shared memory, "
1469 "couldn't update cache");
1473 LDAP_CACHE_UNLOCK();
1478 * tmp_local_sgl has either been created, or copied out of the cache
1479 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1482 result = LDAP_COMPARE_FALSE;
1483 if (!tmp_local_sgl) {
1487 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1488 const char *group = NULL;
1489 group = tmp_local_sgl->subgroupDNs[sgindex];
1491 * 4. Now loop through the subgroupList and call uldap_cache_compare
1492 * to check for the user.
1494 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1495 if (result == LDAP_COMPARE_TRUE) {
1497 * 4.A. We found the user in the subgroup. Return
1498 * LDAP_COMPARE_TRUE.
1500 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1501 "Found user %s in a subgroup (%s) at level %d of %d.",
1502 r->user, group, cur_subgroup_depth+1,
1503 max_subgroup_depth);
1507 * 4.B. We didn't find the user in this subgroup, so recurse into
1508 * it and keep looking.
1510 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1511 "User %s not found in subgroup (%s) at level %d of "
1512 "%d.", r->user, group, cur_subgroup_depth+1,
1513 max_subgroup_depth);
1514 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1515 value, subgroupAttrs,
1517 cur_subgroup_depth+1,
1518 max_subgroup_depth);
1527 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1528 const char *url, const char *basedn,
1529 int scope, char **attrs, const char *filter,
1530 const char *bindpw, const char **binddn,
1531 const char ***retvals)
1533 const char **vals = NULL;
1536 LDAPMessage *res, *entry;
1540 util_url_node_t *curl; /* Cached URL node */
1541 util_url_node_t curnode;
1542 util_search_node_t *search_nodep; /* Cached search node */
1543 util_search_node_t the_search_node;
1546 util_ldap_state_t *st =
1547 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1550 /* Get the cache node for this url */
1553 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1556 curl = util_ald_create_caches(st, url);
1558 LDAP_CACHE_UNLOCK();
1562 the_search_node.username = filter;
1563 search_nodep = util_ald_cache_fetch(curl->search_cache,
1565 if (search_nodep != NULL) {
1567 /* found entry in search cache... */
1568 curtime = apr_time_now();
1571 * Remove this item from the cache if its expired. If the sent
1572 * password doesn't match the storepassword, the entry will
1573 * be removed and readded later if the credentials pass
1576 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1577 /* ...but entry is too old */
1578 util_ald_cache_remove(curl->search_cache, search_nodep);
1580 else if ( (search_nodep->bindpw)
1581 && (search_nodep->bindpw[0] != '\0')
1582 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1584 /* ...and entry is valid */
1585 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1588 *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
1589 for (i = 0; i < search_nodep->numvals; i++) {
1590 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1593 LDAP_CACHE_UNLOCK();
1594 ldc->reason = "Authentication successful (cached)";
1595 return LDAP_SUCCESS;
1598 /* unlock this read lock */
1599 LDAP_CACHE_UNLOCK();
1603 * At this point, there is no valid cached search, so lets do the search.
1607 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1610 if (failures++ > 10) {
1613 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1617 /* try do the search */
1618 result = ldap_search_ext_s(ldc->ldap,
1619 (char *)basedn, scope,
1620 (char *)filter, attrs, 0,
1621 NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
1622 if (AP_LDAP_IS_SERVER_DOWN(result))
1624 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1625 uldap_connection_unbind(ldc);
1629 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1630 if (result != LDAP_SUCCESS) {
1631 ldc->reason = "ldap_search_ext_s() for user failed";
1636 * We should have found exactly one entry; to find a different
1637 * number is an error.
1639 count = ldap_count_entries(ldc->ldap, res);
1643 ldc->reason = "User not found";
1645 ldc->reason = "User is not unique (search found two "
1648 return LDAP_NO_SUCH_OBJECT;
1651 entry = ldap_first_entry(ldc->ldap, res);
1653 /* Grab the dn, copy it into the pool, and free it again */
1654 dn = ldap_get_dn(ldc->ldap, entry);
1655 *binddn = apr_pstrdup(r->pool, dn);
1659 * A bind to the server with an empty password always succeeds, so
1660 * we check to ensure that the password is not empty. This implies
1661 * that users who actually do have empty passwords will never be
1662 * able to authenticate with this module. I don't see this as a big
1665 if (!bindpw || strlen(bindpw) <= 0) {
1667 ldc->reason = "Empty password not allowed";
1668 return LDAP_INVALID_CREDENTIALS;
1672 * Attempt to bind with the retrieved dn and the password. If the bind
1673 * fails, it means that the password is wrong (the dn obviously
1674 * exists, since we just retrieved it)
1676 result = uldap_simple_bind(ldc, (char *)*binddn, (char *)bindpw,
1678 if (AP_LDAP_IS_SERVER_DOWN(result) ||
1679 (result == LDAP_TIMEOUT && failures == 0)) {
1680 if (AP_LDAP_IS_SERVER_DOWN(result))
1681 ldc->reason = "ldap_simple_bind() to check user credentials "
1682 "failed with server down";
1684 ldc->reason = "ldap_simple_bind() to check user credentials "
1687 uldap_connection_unbind(ldc);
1691 /* failure? if so - return */
1692 if (result != LDAP_SUCCESS) {
1693 ldc->reason = "ldap_simple_bind() to check user credentials failed";
1695 uldap_connection_unbind(ldc);
1700 * We have just bound the connection to a different user and password
1701 * combination, which might be reused unintentionally next time this
1702 * connection is used from the connection pool. To ensure no confusion,
1703 * we mark the connection as unbound.
1709 * Get values for the provided attributes.
1715 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1722 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1723 while (values && values[j]) {
1724 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1725 : apr_pstrdup(r->pool, values[j]);
1728 ldap_value_free(values);
1736 * Add the new username to the search cache.
1740 the_search_node.username = filter;
1741 the_search_node.dn = *binddn;
1742 the_search_node.bindpw = bindpw;
1743 the_search_node.lastbind = apr_time_now();
1744 the_search_node.vals = vals;
1745 the_search_node.numvals = numvals;
1747 /* Search again to make sure that another thread didn't ready insert
1748 * this node into the cache before we got here. If it does exist then
1749 * update the lastbind
1751 search_nodep = util_ald_cache_fetch(curl->search_cache,
1753 if ((search_nodep == NULL) ||
1754 (strcmp(*binddn, search_nodep->dn) != 0)) {
1756 /* Nothing in cache, insert new entry */
1757 util_ald_cache_insert(curl->search_cache, &the_search_node);
1759 else if ((!search_nodep->bindpw) ||
1760 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1762 /* Entry in cache is invalid, remove it and insert new one */
1763 util_ald_cache_remove(curl->search_cache, search_nodep);
1764 util_ald_cache_insert(curl->search_cache, &the_search_node);
1767 /* Cache entry is valid, update lastbind */
1768 search_nodep->lastbind = the_search_node.lastbind;
1770 LDAP_CACHE_UNLOCK();
1774 ldc->reason = "Authentication successful";
1775 return LDAP_SUCCESS;
1779 * This function will return the DN of the entry matching userid.
1780 * It is used to get the DN in case some other module than mod_auth_ldap
1781 * has authenticated the user.
1782 * The function is basically a copy of uldap_cache_checkuserid
1783 * with password checking removed.
1785 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1786 const char *url, const char *basedn,
1787 int scope, char **attrs, const char *filter,
1788 const char **binddn, const char ***retvals)
1790 const char **vals = NULL;
1793 LDAPMessage *res, *entry;
1797 util_url_node_t *curl; /* Cached URL node */
1798 util_url_node_t curnode;
1799 util_search_node_t *search_nodep; /* Cached search node */
1800 util_search_node_t the_search_node;
1803 util_ldap_state_t *st =
1804 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1807 /* Get the cache node for this url */
1810 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1813 curl = util_ald_create_caches(st, url);
1815 LDAP_CACHE_UNLOCK();
1819 the_search_node.username = filter;
1820 search_nodep = util_ald_cache_fetch(curl->search_cache,
1822 if (search_nodep != NULL) {
1824 /* found entry in search cache... */
1825 curtime = apr_time_now();
1828 * Remove this item from the cache if its expired.
1830 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1831 /* ...but entry is too old */
1832 util_ald_cache_remove(curl->search_cache, search_nodep);
1835 /* ...and entry is valid */
1836 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1839 *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
1840 for (i = 0; i < search_nodep->numvals; i++) {
1841 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1844 LDAP_CACHE_UNLOCK();
1845 ldc->reason = "Search successful (cached)";
1846 return LDAP_SUCCESS;
1849 /* unlock this read lock */
1850 LDAP_CACHE_UNLOCK();
1854 * At this point, there is no valid cached search, so lets do the search.
1858 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1861 if (failures++ > 10) {
1864 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1868 /* try do the search */
1869 result = ldap_search_ext_s(ldc->ldap,
1870 (char *)basedn, scope,
1871 (char *)filter, attrs, 0,
1872 NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
1873 if (AP_LDAP_IS_SERVER_DOWN(result))
1875 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1876 uldap_connection_unbind(ldc);
1880 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1881 if (result != LDAP_SUCCESS) {
1882 ldc->reason = "ldap_search_ext_s() for user failed";
1887 * We should have found exactly one entry; to find a different
1888 * number is an error.
1890 count = ldap_count_entries(ldc->ldap, res);
1894 ldc->reason = "User not found";
1896 ldc->reason = "User is not unique (search found two "
1899 return LDAP_NO_SUCH_OBJECT;
1902 entry = ldap_first_entry(ldc->ldap, res);
1904 /* Grab the dn, copy it into the pool, and free it again */
1905 dn = ldap_get_dn(ldc->ldap, entry);
1906 *binddn = apr_pstrdup(r->pool, dn);
1910 * Get values for the provided attributes.
1916 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1923 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1924 while (values && values[j]) {
1925 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1926 : apr_pstrdup(r->pool, values[j]);
1929 ldap_value_free(values);
1937 * Add the new username to the search cache.
1941 the_search_node.username = filter;
1942 the_search_node.dn = *binddn;
1943 the_search_node.bindpw = NULL;
1944 the_search_node.lastbind = apr_time_now();
1945 the_search_node.vals = vals;
1946 the_search_node.numvals = numvals;
1948 /* Search again to make sure that another thread didn't ready insert
1949 * this node into the cache before we got here. If it does exist then
1950 * update the lastbind
1952 search_nodep = util_ald_cache_fetch(curl->search_cache,
1954 if ((search_nodep == NULL) ||
1955 (strcmp(*binddn, search_nodep->dn) != 0)) {
1957 /* Nothing in cache, insert new entry */
1958 util_ald_cache_insert(curl->search_cache, &the_search_node);
1961 * Don't update lastbind on entries with bindpw because
1962 * we haven't verified that password. It's OK to update
1963 * the entry if there is no password in it.
1965 else if (!search_nodep->bindpw) {
1966 /* Cache entry is valid, update lastbind */
1967 search_nodep->lastbind = the_search_node.lastbind;
1969 LDAP_CACHE_UNLOCK();
1974 ldc->reason = "Search successful";
1975 return LDAP_SUCCESS;
1979 * Reports if ssl support is enabled
1981 * 1 = enabled, 0 = not enabled
1983 static int uldap_ssl_supported(request_rec *r)
1985 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1986 r->server->module_config, &ldap_module);
1988 return(st->ssl_supported);
1992 /* ---------------------------------------- */
1993 /* config directives */
1996 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1999 util_ldap_state_t *st =
2000 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2002 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2008 st->cache_bytes = atol(bytes);
2010 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2011 "ldap cache: Setting shared memory cache size to "
2012 "%" APR_SIZE_T_FMT " bytes.",
2018 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
2021 util_ldap_state_t *st =
2022 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2024 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2031 st->cache_file = ap_server_root_relative(st->pool, file);
2034 st->cache_file = NULL;
2037 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2038 "LDAP cache: Setting shared memory cache file to %s bytes.",
2044 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
2047 util_ldap_state_t *st =
2048 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2050 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2056 st->search_cache_ttl = atol(ttl) * 1000000;
2058 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2059 "ldap cache: Setting cache TTL to %ld microseconds.",
2060 st->search_cache_ttl);
2065 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
2068 util_ldap_state_t *st =
2069 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2071 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2077 st->search_cache_size = atol(size);
2078 if (st->search_cache_size < 0) {
2079 st->search_cache_size = 0;
2082 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2083 "ldap cache: Setting search cache size to %ld entries.",
2084 st->search_cache_size);
2089 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
2092 util_ldap_state_t *st =
2093 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2095 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2101 st->compare_cache_ttl = atol(ttl) * 1000000;
2103 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2104 "ldap cache: Setting operation cache TTL to %ld microseconds.",
2105 st->compare_cache_ttl);
2110 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
2113 util_ldap_state_t *st =
2114 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2116 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2122 st->compare_cache_size = atol(size);
2123 if (st->compare_cache_size < 0) {
2124 st->compare_cache_size = 0;
2127 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2128 "ldap cache: Setting operation cache size to %ld entries.",
2129 st->compare_cache_size);
2136 * Parse the certificate type.
2138 * The type can be one of the following:
2139 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
2140 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
2142 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
2144 static int util_ldap_parse_cert_type(const char *type)
2146 /* Authority file in binary DER format */
2147 if (0 == strcasecmp("CA_DER", type)) {
2148 return APR_LDAP_CA_TYPE_DER;
2151 /* Authority file in Base64 format */
2152 else if (0 == strcasecmp("CA_BASE64", type)) {
2153 return APR_LDAP_CA_TYPE_BASE64;
2156 /* Netscape certificate database file/directory */
2157 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2158 return APR_LDAP_CA_TYPE_CERT7_DB;
2161 /* Netscape secmod file/directory */
2162 else if (0 == strcasecmp("CA_SECMOD", type)) {
2163 return APR_LDAP_CA_TYPE_SECMOD;
2166 /* Client cert file in DER format */
2167 else if (0 == strcasecmp("CERT_DER", type)) {
2168 return APR_LDAP_CERT_TYPE_DER;
2171 /* Client cert file in Base64 format */
2172 else if (0 == strcasecmp("CERT_BASE64", type)) {
2173 return APR_LDAP_CERT_TYPE_BASE64;
2176 /* Client cert file in PKCS#12 format */
2177 else if (0 == strcasecmp("CERT_PFX", type)) {
2178 return APR_LDAP_CERT_TYPE_PFX;
2181 /* Netscape client cert database file/directory */
2182 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2183 return APR_LDAP_CERT_TYPE_KEY3_DB;
2186 /* Netscape client cert nickname */
2187 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2188 return APR_LDAP_CERT_TYPE_NICKNAME;
2191 /* Client cert key file in DER format */
2192 else if (0 == strcasecmp("KEY_DER", type)) {
2193 return APR_LDAP_KEY_TYPE_DER;
2196 /* Client cert key file in Base64 format */
2197 else if (0 == strcasecmp("KEY_BASE64", type)) {
2198 return APR_LDAP_KEY_TYPE_BASE64;
2201 /* Client cert key file in PKCS#12 format */
2202 else if (0 == strcasecmp("KEY_PFX", type)) {
2203 return APR_LDAP_KEY_TYPE_PFX;
2207 return APR_LDAP_CA_TYPE_UNKNOWN;
2214 * Set LDAPTrustedGlobalCert.
2216 * This directive takes either two or three arguments:
2217 * - certificate type
2218 * - certificate file / directory / nickname
2219 * - certificate password (optional)
2221 * This directive may only be used globally.
2223 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2227 const char *password)
2229 util_ldap_state_t *st =
2230 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2232 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2236 apr_ldap_opt_tls_cert_t *cert;
2242 /* handle the certificate type */
2244 cert_type = util_ldap_parse_cert_type(type);
2245 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2246 return apr_psprintf(cmd->pool, "The certificate type %s is "
2247 "not recognised. It should be one "
2248 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2249 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2250 "CERT_KEY3_DB, CERT_NICKNAME, "
2251 "KEY_DER, KEY_BASE64", type);
2255 return "Certificate type was not specified.";
2258 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2259 "LDAP: SSL trusted global cert - %s (type %s)",
2262 /* add the certificate to the global array */
2263 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2264 cert->type = cert_type;
2266 cert->password = password;
2268 /* if file is a file or path, fix the path */
2269 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2270 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2272 cert->path = ap_server_root_relative(cmd->pool, file);
2274 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2277 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2278 "LDAP: Could not open SSL trusted certificate "
2279 "authority file - %s",
2280 cert->path == NULL ? file : cert->path);
2281 return "Invalid global certificate file path";
2290 * Set LDAPTrustedClientCert.
2292 * This directive takes either two or three arguments:
2293 * - certificate type
2294 * - certificate file / directory / nickname
2295 * - certificate password (optional)
2297 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2301 const char *password)
2303 util_ldap_config_t *dc = config;
2307 apr_ldap_opt_tls_cert_t *cert;
2309 /* handle the certificate type */
2311 cert_type = util_ldap_parse_cert_type(type);
2312 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2313 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2314 "not recognised. It should be one "
2315 "of CA_DER, CA_BASE64, "
2316 "CERT_DER, CERT_BASE64, "
2317 "CERT_NICKNAME, CERT_PFX, "
2318 "KEY_DER, KEY_BASE64, KEY_PFX",
2321 else if ( APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2322 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2323 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2324 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2325 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2326 "only valid within a "
2327 "LDAPTrustedGlobalCert directive. "
2328 "Only CA_DER, CA_BASE64, "
2329 "CERT_DER, CERT_BASE64, "
2330 "CERT_NICKNAME, KEY_DER, and "
2331 "KEY_BASE64 may be used.", type);
2335 return "Certificate type was not specified.";
2338 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2339 "LDAP: SSL trusted client cert - %s (type %s)",
2342 /* add the certificate to the client array */
2343 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(dc->client_certs);
2344 cert->type = cert_type;
2346 cert->password = password;
2348 /* if file is a file or path, fix the path */
2349 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2350 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2352 cert->path = ap_server_root_relative(cmd->pool, file);
2354 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2357 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2358 "LDAP: Could not open SSL client certificate "
2360 cert->path == NULL ? file : cert->path);
2361 return "Invalid client certificate file path";
2371 * Set LDAPTrustedMode.
2373 * This directive sets what encryption mode to use on a connection:
2374 * - None (No encryption)
2375 * - SSL (SSL encryption)
2376 * - STARTTLS (TLS encryption)
2378 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2381 util_ldap_state_t *st =
2382 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2385 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2386 "LDAP: SSL trusted mode - %s",
2389 if (0 == strcasecmp("NONE", mode)) {
2390 st->secure = APR_LDAP_NONE;
2392 else if (0 == strcasecmp("SSL", mode)) {
2393 st->secure = APR_LDAP_SSL;
2395 else if ( (0 == strcasecmp("TLS", mode))
2396 || (0 == strcasecmp("STARTTLS", mode))) {
2397 st->secure = APR_LDAP_STARTTLS;
2400 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2401 "SSL, or TLS/STARTTLS";
2408 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2412 util_ldap_state_t *st =
2413 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2415 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2421 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2422 "LDAP: SSL verify server certificate - %s",
2423 mode?"TRUE":"FALSE");
2425 st->verify_svr_cert = mode;
2431 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2435 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2436 util_ldap_state_t *st =
2437 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2440 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2446 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2447 st->connectionTimeout = atol(ttl);
2449 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2450 "ldap connection: Setting connection timeout to %ld seconds.",
2451 st->connectionTimeout);
2453 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2454 "LDAP: Connection timeout option not supported by the "
2455 "LDAP SDK in use." );
2462 static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
2466 util_ldap_config_t *dc = config;
2468 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2469 "LDAP: Setting referral chasing %s",
2470 (mode == AP_LDAP_CHASEREFERRALS_ON) ? "ON" : "OFF");
2472 dc->ChaseReferrals = mode;
2477 static const char *util_ldap_set_debug_level(cmd_parms *cmd,
2480 util_ldap_state_t *st =
2481 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2484 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2489 #ifndef AP_LDAP_OPT_DEBUG
2490 return "This directive is not supported with the currently linked LDAP library";
2493 st->debug_level = atoi(arg);
2497 static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
2499 const char *hop_limit)
2501 util_ldap_config_t *dc = config;
2503 dc->ReferralHopLimit = atol(hop_limit);
2505 if (dc->ReferralHopLimit <= 0) {
2506 return "LDAPReferralHopLimit must be greater than zero (Use 'LDAPReferrals Off' to disable referral chasing)";
2509 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2510 "LDAP: Limit chased referrals to maximum of %d hops.",
2511 dc->ReferralHopLimit);
2516 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
2517 util_ldap_config_t *dc =
2518 (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
2520 /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
2521 dc->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2522 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2523 dc->ReferralHopLimit = AP_LDAP_HOPLIMIT_UNSET;
2528 static const char *util_ldap_set_op_timeout(cmd_parms *cmd,
2534 util_ldap_state_t *st =
2535 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2537 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2543 timeout = strtol(val, &endptr, 10);
2544 if ((val == endptr) || (*endptr != '\0')) {
2545 return "Timeout not numerical";
2548 return "Timeout must be non-negative";
2552 if (!st->opTimeout) {
2553 st->opTimeout = apr_pcalloc(cmd->pool, sizeof(struct timeval));
2555 st->opTimeout->tv_sec = timeout;
2558 st->opTimeout = NULL;
2561 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2562 "ldap connection: Setting op timeout to %ld seconds.",
2565 #ifndef LDAP_OPT_TIMEOUT
2567 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2568 "LDAP: LDAP_OPT_TIMEOUT option not supported by the "
2569 "LDAP library in use. Using LDAPTimeout value as search "
2576 static const char *util_ldap_set_conn_ttl(cmd_parms *cmd,
2580 apr_interval_time_t timeout;
2581 util_ldap_state_t *st =
2582 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2585 if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS) {
2586 return "LDAPConnPoolTTL has wrong format";
2590 /* reserve -1 for default value */
2591 timeout = AP_LDAP_CONNPOOL_INFINITE;
2593 st->connectionPoolTTL = timeout;
2599 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2601 util_ldap_state_t *st =
2602 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2604 /* Create a per vhost pool for mod_ldap to use, serialized with
2605 * st->mutex (also one per vhost). both are replicated by fork(),
2606 * no shared memory managed by either.
2608 apr_pool_create(&st->pool, p);
2610 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2613 st->cache_bytes = 500000;
2614 st->search_cache_ttl = 600000000;
2615 st->search_cache_size = 1024;
2616 st->compare_cache_ttl = 600000000;
2617 st->compare_cache_size = 1024;
2618 st->connections = NULL;
2619 st->ssl_supported = 0;
2620 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2621 st->secure = APR_LDAP_NONE;
2623 st->connectionTimeout = 10;
2624 st->opTimeout = apr_pcalloc(p, sizeof(struct timeval));
2625 st->opTimeout->tv_sec = 60;
2626 st->verify_svr_cert = 1;
2627 st->connectionPoolTTL = AP_LDAP_CONNPOOL_DEFAULT; /* no limit */
2632 /* cache-related settings are not merged here, but in the post_config hook,
2633 * since the cache has not yet sprung to life
2635 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2638 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2639 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2640 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2642 st->pool = overrides->pool;
2644 st->mutex = overrides->mutex;
2647 /* The cache settings can not be modified in a
2648 virtual host since all server use the same
2649 shared memory cache. */
2650 st->cache_bytes = base->cache_bytes;
2651 st->search_cache_ttl = base->search_cache_ttl;
2652 st->search_cache_size = base->search_cache_size;
2653 st->compare_cache_ttl = base->compare_cache_ttl;
2654 st->compare_cache_size = base->compare_cache_size;
2655 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2657 st->connections = NULL;
2658 st->ssl_supported = 0;
2659 st->global_certs = apr_array_append(p, base->global_certs,
2660 overrides->global_certs);
2661 st->secure = (overrides->secure_set == 0) ? base->secure
2662 : overrides->secure;
2664 /* These LDAP connection settings can not be overwritten in
2665 a virtual host. Once set in the base server, they must
2666 remain the same. None of the LDAP SDKs seem to be able
2667 to handle setting the verify_svr_cert flag on a
2668 per-connection basis. The OpenLDAP client appears to be
2669 able to handle the connection timeout per-connection
2670 but the Novell SDK cannot. Allowing the timeout to
2671 be set by each vhost is of little value so rather than
2672 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2673 is being enforced on this setting as well. */
2674 st->connectionTimeout = base->connectionTimeout;
2675 st->opTimeout = base->opTimeout;
2676 st->verify_svr_cert = base->verify_svr_cert;
2677 st->debug_level = base->debug_level;
2679 st->connectionPoolTTL = (overrides->connectionPoolTTL == AP_LDAP_CONNPOOL_DEFAULT) ?
2680 base->connectionPoolTTL : overrides->connectionPoolTTL;
2685 static apr_status_t util_ldap_cleanup_module(void *data)
2688 server_rec *s = data;
2689 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2690 s->module_config, &ldap_module);
2692 if (st->ssl_supported) {
2693 apr_ldap_ssl_deinit();
2700 static int util_ldap_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
2703 apr_status_t result;
2705 result = ap_mutex_register(pconf, ldap_cache_mutex_type, NULL,
2706 APR_LOCK_DEFAULT, 0);
2707 if (result != APR_SUCCESS) {
2714 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2715 apr_pool_t *ptemp, server_rec *s)
2717 apr_status_t result;
2718 server_rec *s_vhost;
2719 util_ldap_state_t *st_vhost;
2721 util_ldap_state_t *st = (util_ldap_state_t *)
2722 ap_get_module_config(s->module_config,
2725 apr_ldap_err_t *result_err = NULL;
2728 /* util_ldap_post_config() will be called twice. Don't bother
2729 * going through all of the initialization on the first call
2730 * because it will just be thrown away.*/
2731 if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
2733 #if APR_HAS_SHARED_MEMORY
2734 /* If the cache file already exists then delete it. Otherwise we are
2735 * going to run into problems creating the shared memory. */
2736 if (st->cache_file) {
2737 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2739 apr_file_remove(lck_file, ptemp);
2745 #if APR_HAS_SHARED_MEMORY
2746 /* initializing cache if shared memory size is not zero and we already
2747 * don't have shm address
2749 if (!st->cache_shm && st->cache_bytes > 0) {
2751 result = util_ldap_cache_init(p, st);
2752 if (result != APR_SUCCESS) {
2753 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2754 "LDAP cache: could not create shared memory segment");
2758 result = ap_global_mutex_create(&st->util_ldap_cache_lock, NULL,
2759 ldap_cache_mutex_type, NULL, s, p, 0);
2760 if (result != APR_SUCCESS) {
2764 /* merge config in all vhost */
2767 st_vhost = (util_ldap_state_t *)
2768 ap_get_module_config(s_vhost->module_config,
2771 #if APR_HAS_SHARED_MEMORY
2772 st_vhost->cache_shm = st->cache_shm;
2773 st_vhost->cache_rmm = st->cache_rmm;
2774 st_vhost->cache_file = st->cache_file;
2775 st_vhost->util_ldap_cache = st->util_ldap_cache;
2776 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2777 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2778 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2779 s_vhost->server_hostname);
2781 s_vhost = s_vhost->next;
2783 #if APR_HAS_SHARED_MEMORY
2786 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2787 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2788 "shared memory cache");
2792 /* log the LDAP SDK used
2795 apr_ldap_err_t *result = NULL;
2796 apr_ldap_info(p, &(result));
2797 if (result != NULL) {
2798 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2802 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2803 util_ldap_cleanup_module);
2806 * Initialize SSL support, and log the result for the benefit of the admin.
2808 * If SSL is not supported it is not necessarily an error, as the
2809 * application may not want to use it.
2811 rc = apr_ldap_ssl_init(p,
2815 if (APR_SUCCESS == rc) {
2816 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2817 (void *)st->global_certs, &(result_err));
2820 if (APR_SUCCESS == rc) {
2821 st->ssl_supported = 1;
2822 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2823 "LDAP: SSL support available" );
2826 st->ssl_supported = 0;
2827 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2828 "LDAP: SSL support unavailable%s%s",
2829 result_err ? ": " : "",
2830 result_err ? result_err->reason : "");
2833 /* Initialize the rebind callback's cross reference list. */
2834 apr_ldap_rebind_init (p);
2836 #ifdef AP_LDAP_OPT_DEBUG
2837 if (st->debug_level > 0) {
2838 result = ldap_set_option(NULL, AP_LDAP_OPT_DEBUG, &st->debug_level);
2839 if (result != LDAP_SUCCESS) {
2840 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
2841 "LDAP: Could not set the LDAP library debug level to %d:(%d) %s",
2842 st->debug_level, result, ldap_err2string(result));
2850 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2853 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2856 if (!st->util_ldap_cache_lock) return;
2858 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2859 apr_global_mutex_lockfile(st->util_ldap_cache_lock), p);
2860 if (sts != APR_SUCCESS) {
2861 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2862 "Failed to initialise global mutex %s in child process",
2863 ldap_cache_mutex_type);
2867 static const command_rec util_ldap_cmds[] = {
2868 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2870 "Set the size of the shared memory cache (in bytes). Use "
2871 "0 to disable the shared memory cache. (default: 100000)"),
2873 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2875 "Set the file name for the shared memory cache."),
2877 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2879 "Set the maximum number of entries that are possible in the "
2880 "LDAP search cache. Use 0 or -1 to disable the search cache "
2883 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2885 "Set the maximum time (in seconds) that an item can be "
2886 "cached in the LDAP search cache. Use 0 for no limit. "
2889 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2891 "Set the maximum number of entries that are possible "
2892 "in the LDAP compare cache. Use 0 or -1 to disable the compare cache "
2895 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2897 "Set the maximum time (in seconds) that an item is cached "
2898 "in the LDAP operation cache. Use 0 for no limit. "
2901 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2903 "Takes three arguments; the first argument is the cert "
2904 "type of the second argument, one of CA_DER, CA_BASE64, "
2905 "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2906 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
2907 "specifes the file and/or directory containing the trusted CA "
2908 "certificates (and global client certs for Netware) used to "
2909 "validate the LDAP server. The third argument is an optional "
2910 "passphrase if applicable."),
2912 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2914 "Takes three arguments: the first argument is the certificate "
2915 "type of the second argument, one of CA_DER, CA_BASE64, "
2916 "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2917 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
2918 "specifies the file and/or directory containing the client "
2919 "certificate, or certificate ID used to validate this LDAP "
2920 "client. The third argument is an optional passphrase if "
2923 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2925 "Specify the type of security that should be applied to "
2926 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2928 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2930 "Set to 'ON' requires that the server certificate be verified"
2931 " before a secure LDAP connection can be establish. Default"
2934 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2936 "Specify the LDAP socket connection timeout in seconds "
2939 AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
2941 "Choose whether referrals are chased ['ON'|'OFF']. Default 'ON'"),
2943 AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
2945 "Limit the number of referral hops that LDAP can follow. "
2946 "(Integer value, Consult LDAP SDK documentation for applicability and defaults"),
2948 AP_INIT_TAKE1("LDAPLibraryDebug", util_ldap_set_debug_level,
2950 "Enable debugging in LDAP SDK (Default: off, values: SDK specific"),
2952 AP_INIT_TAKE1("LDAPTimeout", util_ldap_set_op_timeout,
2954 "Specify the LDAP bind/search timeout in seconds "
2955 "(0 = no limit). Default: 60"),
2956 AP_INIT_TAKE1("LDAPConnectionPoolTTL", util_ldap_set_conn_ttl,
2958 "Specify the maximum amount of time a bound connection can sit "
2959 "idle and still be considered valid for reuse"
2960 "(0 = no pool, -1 = no limit, n = time in seconds). Default: -1"),
2965 static void util_ldap_register_hooks(apr_pool_t *p)
2967 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2968 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2969 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2970 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2971 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2972 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2973 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2974 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2975 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2976 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2978 ap_hook_pre_config(util_ldap_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
2979 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2980 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2981 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2984 AP_DECLARE_MODULE(ldap) = {
2985 STANDARD20_MODULE_STUFF,
2986 util_ldap_create_dir_config, /* create dir config */
2987 NULL, /* merge dir config */
2988 util_ldap_create_config, /* create server config */
2989 util_ldap_merge_config, /* merge server config */
2990 util_ldap_cmds, /* command table */
2991 util_ldap_register_hooks, /* set up request processing hooks */