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_ldap.h"
32 #include "util_ldap_cache.h"
34 #include <apr_strings.h>
41 #error mod_ldap requires APR-util to have LDAP support built in
44 #ifdef AP_NEED_SET_MUTEX_PERMS
48 /* Default define for ldap functions that need a SIZELIMIT but
49 * do not have the define
50 * XXX This should be removed once a supporting #define is
51 * released through APR-Util.
53 #ifndef APR_LDAP_SIZELIMIT
54 #define APR_LDAP_SIZELIMIT -1
57 module AP_MODULE_DECLARE_DATA ldap_module;
59 #define LDAP_CACHE_LOCK() do { \
60 if (st->util_ldap_cache_lock) \
61 apr_global_mutex_lock(st->util_ldap_cache_lock); \
64 #define LDAP_CACHE_UNLOCK() do { \
65 if (st->util_ldap_cache_lock) \
66 apr_global_mutex_unlock(st->util_ldap_cache_lock); \
69 static apr_status_t util_ldap_connection_remove (void *param);
71 static void util_ldap_strdup (char **str, const char *newstr)
79 *str = strdup(newstr);
87 * This handler generates a status page about the current performance of
88 * the LDAP cache. It is enabled as follows:
90 * <Location /ldap-status>
91 * SetHandler ldap-status
95 static int util_ldap_handler(request_rec *r)
97 util_ldap_state_t *st = (util_ldap_state_t *)
98 ap_get_module_config(r->server->module_config,
101 r->allowed |= (1 << M_GET);
102 if (r->method_number != M_GET)
105 if (strcmp(r->handler, "ldap-status")) {
109 ap_set_content_type(r, "text/html; charset=ISO-8859-1");
114 ap_rputs(DOCTYPE_HTML_3_2
115 "<html><head><title>LDAP Cache Information</title></head>\n", r);
116 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
119 util_ald_cache_display(r, st);
126 /* ------------------------------------------------------------------ */
128 * Closes an LDAP connection by unlocking it. The next time
129 * uldap_connection_find() is called this connection will be
130 * available for reuse.
132 static void uldap_connection_close(util_ldap_connection_t *ldc)
138 * Is it safe leaving bound connections floating around between the
139 * different modules? Keeping the user bound is a performance boost,
140 * but it is also a potential security problem - maybe.
142 * For now we unbind the user when we finish with a connection, but
143 * we don't have to...
147 util_ldap_connection_remove(ldc);
150 /* mark our connection as available for reuse */
152 apr_thread_mutex_unlock(ldc->lock);
159 * Destroys an LDAP connection by unbinding and closing the connection to
160 * the LDAP server. It is used to bring the connection back to a known
161 * state after an error.
163 static apr_status_t uldap_connection_unbind(void *param)
165 util_ldap_connection_t *ldc = param;
169 ldap_unbind_s(ldc->ldap);
180 * Clean up an LDAP connection by unbinding and unlocking the connection.
181 * This cleanup does not remove the util_ldap_connection_t from the
182 * per-virtualhost list of connections, does not remove the storage
183 * for the util_ldap_connection_t or it's data, and is NOT run automatically.
185 static apr_status_t uldap_connection_cleanup(void *param)
187 util_ldap_connection_t *ldc = param;
190 /* Release the rebind info for this connection. No more referral rebinds required. */
191 apr_ldap_rebind_remove(ldc->ldap);
193 /* unbind and disconnect from the LDAP server */
194 uldap_connection_unbind(ldc);
196 /* free the username and password */
198 free((void*)ldc->bindpw);
201 free((void*)ldc->binddn);
203 /* ldc->reason is allocated from r->pool */
207 /* unlock this entry */
208 uldap_connection_close(ldc);
216 * util_ldap_connection_remove frees all storage associated with the LDAP
217 * connection and removes it completely from the per-virtualhost list of
220 * The caller should hold the lock for this connection
222 static apr_status_t util_ldap_connection_remove (void *param) {
223 util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
224 util_ldap_state_t *st = ldc->st;
226 if (!ldc) return APR_SUCCESS;
228 uldap_connection_unbind(ldc);
231 apr_thread_mutex_lock(st->mutex);
234 /* Remove ldc from the list */
235 for (l=st->connections; l; l=l->next) {
238 prev->next = l->next;
241 st->connections = l->next;
248 /* Some unfortunate duplication between this method
249 * and uldap_connection_cleanup()
252 free((void*)ldc->bindpw);
255 free((void*)ldc->binddn);
259 apr_thread_mutex_unlock(ldc->lock);
260 apr_thread_mutex_unlock(st->mutex);
263 /* Destory the pool associated with this connection */
265 apr_pool_destroy(ldc->pool);
270 static int uldap_connection_init(request_rec *r,
271 util_ldap_connection_t *ldc)
273 int rc = 0, ldap_option = 0;
274 int version = LDAP_VERSION3;
275 apr_ldap_err_t *result = NULL;
276 #ifdef LDAP_OPT_NETWORK_TIMEOUT
277 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
279 util_ldap_state_t *st =
280 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
283 /* Since the host will include a port if the default port is not used,
284 * always specify the default ports for the port parameter. This will
285 * allow a host string that contains multiple hosts the ability to mix
286 * some hosts with ports and some without. All hosts which do not
287 * specify a port will use the default port.
289 apr_ldap_init(r->pool, &(ldc->ldap),
291 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
295 if (result != NULL && result->rc) {
296 ldc->reason = result->reason;
299 if (NULL == ldc->ldap)
302 if (NULL == ldc->reason) {
303 ldc->reason = "LDAP: ldap initialization failed";
306 ldc->reason = result->reason;
311 /* Now that we have an ldap struct, add it to the referral list for rebinds. */
312 rc = apr_ldap_rebind_add(ldc->pool, ldc->ldap, ldc->binddn, ldc->bindpw);
313 if (rc != APR_SUCCESS) {
314 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
315 "LDAP: Unable to add rebind cross reference entry. Out of memory?");
316 uldap_connection_unbind(ldc);
317 ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
321 /* always default to LDAP V3 */
322 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
324 /* set client certificates */
325 if (!apr_is_empty_array(ldc->client_certs)) {
326 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
327 ldc->client_certs, &(result));
328 if (LDAP_SUCCESS != result->rc) {
329 uldap_connection_unbind( ldc );
330 ldc->reason = result->reason;
335 /* switch on SSL/TLS */
336 if (APR_LDAP_NONE != ldc->secure) {
337 apr_ldap_set_option(r->pool, ldc->ldap,
338 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
339 if (LDAP_SUCCESS != result->rc) {
340 uldap_connection_unbind( ldc );
341 ldc->reason = result->reason;
346 /* Set the alias dereferencing option */
347 ldap_option = ldc->deref;
348 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
350 /* Set options for rebind and referrals. */
351 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
352 "LDAP: Setting referrals to %s.",
353 ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"));
354 apr_ldap_set_option(r->pool, ldc->ldap,
355 APR_LDAP_OPT_REFERRALS,
356 (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
357 LDAP_OPT_ON : LDAP_OPT_OFF),
359 if (result->rc != LDAP_SUCCESS) {
360 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
361 "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
362 ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"),
364 result->reason = "Unable to set LDAP_OPT_REFERRALS.";
365 uldap_connection_unbind(ldc);
369 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
370 /* Referral hop limit - only if referrals are enabled */
371 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
372 "Setting referral hop limit to %d.",
373 ldc->ReferralHopLimit);
374 apr_ldap_set_option(r->pool, ldc->ldap,
375 APR_LDAP_OPT_REFHOPLIMIT,
376 (void *)&ldc->ReferralHopLimit,
378 if (result->rc != LDAP_SUCCESS) {
379 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
380 "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
381 ldc->ReferralHopLimit,
383 result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
384 uldap_connection_unbind(ldc);
389 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
390 #ifdef APR_LDAP_OPT_VERIFY_CERT
391 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
392 &(st->verify_svr_cert), &(result));
394 #if defined(LDAPSSL_VERIFY_SERVER)
395 if (st->verify_svr_cert) {
396 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
399 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
401 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
402 /* This is not a per-connection setting so just pass NULL for the
403 Ldap connection handle */
404 if (st->verify_svr_cert) {
405 int i = LDAP_OPT_X_TLS_DEMAND;
406 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
409 int i = LDAP_OPT_X_TLS_NEVER;
410 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
415 #ifdef LDAP_OPT_NETWORK_TIMEOUT
416 if (st->connectionTimeout > 0) {
417 timeOut.tv_sec = st->connectionTimeout;
420 if (st->connectionTimeout >= 0) {
421 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
422 (void *)&timeOut, &(result));
423 if (APR_SUCCESS != rc) {
424 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
425 "LDAP: Could not set the connection timeout");
434 * Connect to the LDAP server and binds. Does not connect if already
435 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
437 * Returns LDAP_SUCCESS on success; and an error code on failure
439 static int uldap_connection_open(request_rec *r,
440 util_ldap_connection_t *ldc)
445 /* sanity check for NULL */
450 /* If the connection is already bound, return
454 ldc->reason = "LDAP: connection open successful (already bound)";
458 /* create the ldap session handle
460 if (NULL == ldc->ldap)
462 rc = uldap_connection_init( r, ldc );
463 if (LDAP_SUCCESS != rc)
470 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
471 * returned. Break out of the loop on Success or any other error.
473 * NOTE: Looping is probably not a great idea. If the server isn't
474 * responding the chances it will respond after a few tries are poor.
475 * However, the original code looped and it only happens on
476 * the error condition.
478 for (failures=0; failures<10; failures++)
480 rc = ldap_simple_bind_s(ldc->ldap,
482 (char *)ldc->bindpw);
483 if (!AP_LDAP_IS_SERVER_DOWN(rc)) {
485 } else if (failures == 5) {
486 /* attempt to init the connection once again */
487 uldap_connection_unbind( ldc );
488 rc = uldap_connection_init( r, ldc );
489 if (LDAP_SUCCESS != rc)
496 /* free the handle if there was an error
498 if (LDAP_SUCCESS != rc)
500 uldap_connection_unbind(ldc);
501 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
505 ldc->reason = "LDAP: connection open successful";
513 * Compare client certificate arrays.
515 * Returns 1 on compare failure, 0 otherwise.
517 static int compare_client_certs(apr_array_header_t *srcs,
518 apr_array_header_t *dests)
521 struct apr_ldap_opt_tls_cert_t *src, *dest;
523 /* arrays both NULL? if so, then equal */
524 if (srcs == NULL && dests == NULL) {
528 /* arrays different length or either NULL? If so, then not equal */
529 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
533 /* run an actual comparison */
534 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
535 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
536 for (i = 0; i < srcs->nelts; i++) {
537 if (strcmp(src[i].path, dest[i].path) ||
538 strcmp(src[i].password, dest[i].password) ||
539 src[i].type != dest[i].type) {
544 /* if we got here, the cert arrays were identical */
551 * Find an existing ldap connection struct that matches the
552 * provided ldap connection parameters.
554 * If not found in the cache, a new ldc structure will be allocated
555 * from st->pool and returned to the caller. If found in the cache,
556 * a pointer to the existing ldc structure will be returned.
558 static util_ldap_connection_t *
559 uldap_connection_find(request_rec *r,
560 const char *host, int port,
561 const char *binddn, const char *bindpw,
562 deref_options deref, int secure)
564 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
565 int secureflag = secure;
567 util_ldap_state_t *st =
568 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
570 util_ldap_config_t *dc =
571 (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
574 /* mutex lock this function */
575 apr_thread_mutex_lock(st->mutex);
578 if (secure < APR_LDAP_NONE) {
579 secureflag = st->secure;
582 /* Search for an exact connection match in the list that is not
585 for (l=st->connections,p=NULL; l; l=l->next) {
587 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
589 if ( (l->port == port) && (strcmp(l->host, host) == 0)
590 && ((!l->binddn && !binddn) || (l->binddn && binddn
591 && !strcmp(l->binddn, binddn)))
592 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
593 && !strcmp(l->bindpw, bindpw)))
594 && (l->deref == deref) && (l->secure == secureflag)
595 && !compare_client_certs(st->client_certs, l->client_certs))
600 /* If this connection didn't match the criteria, then we
601 * need to unlock the mutex so it is available to be reused.
603 apr_thread_mutex_unlock(l->lock);
609 /* If nothing found, search again, but we don't care about the
610 * binddn and bindpw this time.
613 for (l=st->connections,p=NULL; l; l=l->next) {
615 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
618 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
619 (l->deref == deref) && (l->secure == secureflag) &&
620 !compare_client_certs(st->client_certs, l->client_certs))
622 /* the bind credentials have changed */
624 util_ldap_strdup((char**)&(l->binddn), binddn);
625 util_ldap_strdup((char**)&(l->bindpw), bindpw);
629 /* If this connection didn't match the criteria, then we
630 * need to unlock the mutex so it is available to be reused.
632 apr_thread_mutex_unlock(l->lock);
639 /* artificially disable cache */
642 /* If no connection was found after the second search, we
647 if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
648 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
649 "util_ldap: Failed to create memory pool");
651 apr_thread_mutex_unlock(st->mutex);
657 * Add the new connection entry to the linked list. Note that we
658 * don't actually establish an LDAP connection yet; that happens
659 * the first time authentication is requested.
662 /* create the details of this connection in the new pool */
663 l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
668 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
669 apr_thread_mutex_lock(l->lock);
672 l->host = apr_pstrdup(l->pool, host);
675 util_ldap_strdup((char**)&(l->binddn), binddn);
676 util_ldap_strdup((char**)&(l->bindpw), bindpw);
677 l->ChaseReferrals = dc->ChaseReferrals;
678 l->ReferralHopLimit = dc->ReferralHopLimit;
680 /* The security mode after parsing the URL will always be either
681 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
682 * If the security setting is NONE, override it to the security
683 * setting optionally supplied by the admin using LDAPTrustedMode
685 l->secure = secureflag;
687 /* save away a copy of the client cert list that is presently valid */
688 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
701 apr_thread_mutex_unlock(st->mutex);
706 /* ------------------------------------------------------------------ */
709 * Compares two DNs to see if they're equal. The only way to do this correctly
710 * is to search for the dn and then do ldap_get_dn() on the result. This should
711 * match the initial dn, since it would have been also retrieved with
712 * ldap_get_dn(). This is expensive, so if the configuration value
713 * compare_dn_on_server is false, just does an ordinary strcmp.
715 * The lock for the ldap cache should already be acquired.
717 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
718 const char *url, const char *dn,
719 const char *reqdn, int compare_dn_on_server)
722 util_url_node_t *curl;
723 util_url_node_t curnode;
724 util_dn_compare_node_t *node;
725 util_dn_compare_node_t newnode;
727 LDAPMessage *res, *entry;
730 util_ldap_state_t *st = (util_ldap_state_t *)
731 ap_get_module_config(r->server->module_config,
734 /* get cache entry (or create one) */
738 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
740 curl = util_ald_create_caches(st, url);
744 /* a simple compare? */
745 if (!compare_dn_on_server) {
746 /* unlock this read lock */
747 if (strcmp(dn, reqdn)) {
748 ldc->reason = "DN Comparison FALSE (direct strcmp())";
749 return LDAP_COMPARE_FALSE;
752 ldc->reason = "DN Comparison TRUE (direct strcmp())";
753 return LDAP_COMPARE_TRUE;
758 /* no - it's a server side compare */
761 /* is it in the compare cache? */
762 newnode.reqdn = (char *)reqdn;
763 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
765 /* If it's in the cache, it's good */
766 /* unlock this read lock */
768 ldc->reason = "DN Comparison TRUE (cached)";
769 return LDAP_COMPARE_TRUE;
772 /* unlock this read lock */
777 if (failures++ > 10) {
778 /* too many failures */
782 /* make a server connection */
783 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
784 /* connect to server failed */
788 /* search for reqdn */
789 result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
790 "(objectclass=*)", NULL, 1,
791 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
792 if (AP_LDAP_IS_SERVER_DOWN(result))
794 ldc->reason = "DN Comparison ldap_search_ext_s() "
795 "failed with server down";
796 uldap_connection_unbind(ldc);
799 if (result != LDAP_SUCCESS) {
800 /* search for reqdn failed - no match */
801 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
805 entry = ldap_first_entry(ldc->ldap, res);
806 searchdn = ldap_get_dn(ldc->ldap, entry);
809 if (strcmp(dn, searchdn) != 0) {
810 /* compare unsuccessful */
811 ldc->reason = "DN Comparison FALSE (checked on server)";
812 result = LDAP_COMPARE_FALSE;
816 /* compare successful - add to the compare cache */
818 newnode.reqdn = (char *)reqdn;
819 newnode.dn = (char *)dn;
821 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
823 || (strcmp(reqdn, node->reqdn) != 0)
824 || (strcmp(dn, node->dn) != 0))
826 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
830 ldc->reason = "DN Comparison TRUE (checked on server)";
831 result = LDAP_COMPARE_TRUE;
833 ldap_memfree(searchdn);
839 * Does an generic ldap_compare operation. It accepts a cache that it will use
840 * to lookup the compare in the cache. We cache two kinds of compares
841 * (require group compares) and (require user compares). Each compare has a
842 * different cache node: require group includes the DN; require user does not
843 * because the require user cache is owned by the
846 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
847 const char *url, const char *dn,
848 const char *attrib, const char *value)
851 util_url_node_t *curl;
852 util_url_node_t curnode;
853 util_compare_node_t *compare_nodep;
854 util_compare_node_t the_compare_node;
855 apr_time_t curtime = 0; /* silence gcc -Wall */
858 util_ldap_state_t *st = (util_ldap_state_t *)
859 ap_get_module_config(r->server->module_config,
862 /* get cache entry (or create one) */
865 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
867 curl = util_ald_create_caches(st, url);
872 /* make a comparison to the cache */
874 curtime = apr_time_now();
876 the_compare_node.dn = (char *)dn;
877 the_compare_node.attrib = (char *)attrib;
878 the_compare_node.value = (char *)value;
879 the_compare_node.result = 0;
880 the_compare_node.sgl_processed = 0;
881 the_compare_node.subgroupList = NULL;
883 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
886 if (compare_nodep != NULL) {
888 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
889 /* ...but it is too old */
890 util_ald_cache_remove(curl->compare_cache, compare_nodep);
893 /* ...and it is good */
894 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
895 ldc->reason = "Comparison true (cached)";
897 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
898 ldc->reason = "Comparison false (cached)";
900 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
901 ldc->reason = "Comparison no such attribute (cached)";
904 ldc->reason = "Comparison undefined (cached)";
907 /* record the result code to return with the reason... */
908 result = compare_nodep->result;
909 /* and unlock this read lock */
914 /* unlock this read lock */
919 if (failures++ > 10) {
920 /* too many failures */
924 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
929 result = ldap_compare_s(ldc->ldap,
933 if (AP_LDAP_IS_SERVER_DOWN(result)) {
934 /* connection failed - try again */
935 ldc->reason = "ldap_compare_s() failed with server down";
936 uldap_connection_unbind(ldc);
940 ldc->reason = "Comparison complete";
941 if ((LDAP_COMPARE_TRUE == result) ||
942 (LDAP_COMPARE_FALSE == result) ||
943 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
945 /* compare completed; caching result */
947 the_compare_node.lastcompare = curtime;
948 the_compare_node.result = result;
949 the_compare_node.sgl_processed = 0;
950 the_compare_node.subgroupList = NULL;
952 /* If the node doesn't exist then insert it, otherwise just update
953 * it with the last results
955 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
957 if ( (compare_nodep == NULL)
958 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
959 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
960 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
964 junk = util_ald_cache_insert(curl->compare_cache,
967 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
968 "[%" APR_PID_T_FMT "] cache_compare: Cache"
969 " insertion failure.", getpid());
973 compare_nodep->lastcompare = curtime;
974 compare_nodep->result = result;
978 if (LDAP_COMPARE_TRUE == result) {
979 ldc->reason = "Comparison true (adding to cache)";
980 return LDAP_COMPARE_TRUE;
982 else if (LDAP_COMPARE_FALSE == result) {
983 ldc->reason = "Comparison false (adding to cache)";
984 return LDAP_COMPARE_FALSE;
987 ldc->reason = "Comparison no such attribute (adding to cache)";
988 return LDAP_NO_SUCH_ATTRIBUTE;
995 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
996 util_ldap_connection_t *ldc,
999 char **subgroupAttrs,
1000 apr_array_header_t *subgroupclasses)
1003 int result = LDAP_COMPARE_FALSE;
1004 util_compare_subgroup_t *res = NULL;
1005 LDAPMessage *sga_res, *entry;
1006 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
1007 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
1009 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1011 if (!subgroupAttrs) {
1017 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
1019 if (failures++ > 10) {
1020 /* too many failures */
1024 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1025 /* connect failed */
1029 /* try to do the search */
1030 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
1031 (char *)"cn=*", subgroupAttrs, 0,
1032 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
1033 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1034 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
1036 uldap_connection_unbind(ldc);
1040 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1041 if (result != LDAP_SUCCESS) {
1042 ldc->reason = "ldap_search_ext_s() for subgroups failed";
1046 entry = ldap_first_entry(ldc->ldap, sga_res);
1049 * Get values for the provided sub-group attributes.
1051 if (subgroupAttrs) {
1052 int indx = 0, tmp_sgcIndex;
1054 while (subgroupAttrs[indx]) {
1058 /* Get *all* matching "member" values from this group. */
1059 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1064 * Now we are going to pare the subgroup members of this group
1065 * to *just* the subgroups, add them to the compare_nodep, and
1066 * then proceed to check the new level of subgroups.
1068 while (values[val_index]) {
1069 /* Check if this entry really is a group. */
1071 result = LDAP_COMPARE_FALSE;
1072 while ((tmp_sgcIndex < subgroupclasses->nelts)
1073 && (result != LDAP_COMPARE_TRUE)) {
1074 result = uldap_cache_compare(r, ldc, url,
1077 sgc_ents[tmp_sgcIndex].name
1080 if (result != LDAP_COMPARE_TRUE) {
1084 /* It's a group, so add it to the array. */
1085 if (result == LDAP_COMPARE_TRUE) {
1086 char **newgrp = (char **) apr_array_push(subgroups);
1087 *newgrp = apr_pstrdup(r->pool, values[val_index]);
1091 ldap_value_free(values);
1097 ldap_msgfree(sga_res);
1099 if (subgroups->nelts > 0) {
1100 /* We need to fill in tmp_local_subgroups using the data from LDAP */
1103 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1104 res->subgroupDNs = apr_pcalloc(r->pool,
1105 sizeof(char *) * (subgroups->nelts));
1106 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1107 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1117 * Does a recursive lookup operation to try to find a user within (cached)
1118 * nested groups. It accepts a cache that it will use to lookup previous
1119 * compare attempts. We cache two kinds of compares (require group compares)
1120 * and (require user compares). Each compare has a different cache node:
1121 * require group includes the DN; require user does not because the require
1122 * user cache is owned by the
1124 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1127 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1128 * generic, user-agnostic, cached group entry. This will create a new generic
1129 * cache entry if there
1130 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1132 * 2. Lock The cache and get the generic cache entry.
1133 * 3. Check if there is already a subgrouplist in this generic group's cache
1135 * A. If there is, go to step 4.
1136 * B. If there isn't:
1137 * i) Use ldap_search to get the full list
1138 * of subgroup "members" (which may include non-group "members").
1139 * ii) Use uldap_cache_compare to strip the list down to just groups.
1140 * iii) Lock and add this stripped down list to the cache of the generic
1142 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1144 * subgroup to see if the subgroup contains the user and to get the subgroups
1146 * cache (with user-afinity, if they aren't already there).
1147 * A. If the user is in the subgroup, then we'll be returning
1148 * LDAP_COMPARE_TRUE.
1149 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1150 * uldap_cache_compare) then recursively call this function to get the
1151 * sub-subgroups added...
1152 * 5. Cleanup local allocations.
1153 * 6. Return the final result.
1156 static int uldap_cache_check_subgroups(request_rec *r,
1157 util_ldap_connection_t *ldc,
1158 const char *url, const char *dn,
1159 const char *attrib, const char *value,
1160 char **subgroupAttrs,
1161 apr_array_header_t *subgroupclasses,
1162 int cur_subgroup_depth,
1163 int max_subgroup_depth)
1165 int result = LDAP_COMPARE_FALSE;
1166 util_url_node_t *curl;
1167 util_url_node_t curnode;
1168 util_compare_node_t *compare_nodep;
1169 util_compare_node_t the_compare_node;
1170 util_compare_subgroup_t *tmp_local_sgl = NULL;
1171 int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
1172 struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
1173 (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1174 util_ldap_state_t *st = (util_ldap_state_t *)
1175 ap_get_module_config(r->server->module_config,
1179 * Stop looking at deeper levels of nested groups if we have reached the
1180 * max. Since we already checked the top-level group in uldap_cache_compare,
1181 * we don't need to check it again here - so if max_subgroup_depth is set
1182 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1183 * We'll be calling uldap_cache_compare from here to check if the user is
1184 * in the next level before we recurse into that next level looking for
1187 if (cur_subgroup_depth >= max_subgroup_depth) {
1188 return LDAP_COMPARE_FALSE;
1192 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1195 while ((base_sgcIndex < subgroupclasses->nelts)
1196 && (result != LDAP_COMPARE_TRUE)) {
1197 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1198 sgc_ents[base_sgcIndex].name);
1199 if (result != LDAP_COMPARE_TRUE) {
1204 if (result != LDAP_COMPARE_TRUE) {
1205 ldc->reason = "DN failed group verification.";
1210 * 2. Find previously created cache entry and check if there is already a
1215 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1216 LDAP_CACHE_UNLOCK();
1218 if (curl && curl->compare_cache) {
1219 /* make a comparison to the cache */
1222 the_compare_node.dn = (char *)dn;
1223 the_compare_node.attrib = (char *)"objectClass";
1224 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1225 the_compare_node.result = 0;
1226 the_compare_node.sgl_processed = 0;
1227 the_compare_node.subgroupList = NULL;
1229 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1232 if (compare_nodep != NULL) {
1234 * Found the generic group entry... but the user isn't in this
1235 * group or we wouldn't be here.
1237 if (compare_nodep->sgl_processed) {
1238 if (compare_nodep->subgroupList) {
1239 /* Make a local copy of the subgroup list */
1241 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1242 "[%" APR_PID_T_FMT "] util_ldap:"
1243 " Making local copy of SGL for "
1244 "group (%s)(objectClass=%s) ",
1246 (char *)sgc_ents[base_sgcIndex].name);
1247 tmp_local_sgl = apr_pcalloc(r->pool,
1248 sizeof(util_compare_subgroup_t));
1249 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1250 tmp_local_sgl->subgroupDNs =
1251 apr_pcalloc(r->pool,
1252 sizeof(char *) * compare_nodep->subgroupList->len);
1253 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1254 tmp_local_sgl->subgroupDNs[i] =
1255 apr_pstrdup(r->pool,
1256 compare_nodep->subgroupList->subgroupDNs[i]);
1260 sgl_cached_empty = 1;
1264 LDAP_CACHE_UNLOCK();
1267 if (!tmp_local_sgl && !sgl_cached_empty) {
1268 /* No Cached SGL, retrieve from LDAP */
1269 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1270 "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s,"
1271 " retrieving from LDAP" , getpid(), dn);
1272 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1274 if (!tmp_local_sgl) {
1275 /* No SGL aailable via LDAP either */
1276 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1277 " util_ldap: no subgroups for %s" , getpid(), dn);
1280 if (curl && curl->compare_cache) {
1282 * Find the generic group cache entry and add the sgl we just retrieved.
1286 the_compare_node.dn = (char *)dn;
1287 the_compare_node.attrib = (char *)"objectClass";
1288 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1289 the_compare_node.result = 0;
1290 the_compare_node.sgl_processed = 0;
1291 the_compare_node.subgroupList = NULL;
1293 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1296 if (compare_nodep == NULL) {
1298 * The group entry we want to attach our SGL to doesn't exist.
1299 * We only got here if we verified this DN was actually a group
1300 * based on the objectClass, but we can't call the compare function
1301 * while we already hold the cache lock -- only the insert.
1303 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1304 "[%" APR_PID_T_FMT "] util_ldap: Cache entry "
1305 "for %s doesn't exist",
1307 the_compare_node.result = LDAP_COMPARE_TRUE;
1308 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
1309 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1311 if (compare_nodep == NULL) {
1312 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1313 "[%" APR_PID_T_FMT "] util_ldap: Couldn't "
1314 "retrieve group entry for %s from cache",
1320 * We have a valid cache entry and a locally generated SGL.
1321 * Attach the SGL to the cache entry
1323 if (compare_nodep && !compare_nodep->sgl_processed) {
1324 if (!tmp_local_sgl) {
1325 /* We looked up an SGL for a group and found it to be empty */
1326 if (compare_nodep->subgroupList == NULL) {
1327 compare_nodep->sgl_processed = 1;
1331 util_compare_subgroup_t *sgl_copy =
1332 util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1333 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1334 "Copying local SGL of len %d for group %s into cache",
1335 tmp_local_sgl->len, dn);
1337 if (compare_nodep->subgroupList) {
1338 util_ald_sgl_free(curl->compare_cache,
1339 &(compare_nodep->subgroupList));
1341 compare_nodep->subgroupList = sgl_copy;
1342 compare_nodep->sgl_processed = 1;
1345 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1346 "Copy of SGL failed to obtain shared memory, "
1347 "couldn't update cache");
1351 LDAP_CACHE_UNLOCK();
1356 * tmp_local_sgl has either been created, or copied out of the cache
1357 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1360 result = LDAP_COMPARE_FALSE;
1361 if (!tmp_local_sgl) {
1365 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1366 const char *group = NULL;
1367 group = tmp_local_sgl->subgroupDNs[sgindex];
1369 * 4. Now loop through the subgroupList and call uldap_cache_compare
1370 * to check for the user.
1372 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1373 if (result == LDAP_COMPARE_TRUE) {
1375 * 4.A. We found the user in the subgroup. Return
1376 * LDAP_COMPARE_TRUE.
1378 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1379 " util_ldap: Found user %s in a subgroup (%s) at"
1380 " level %d of %d.", getpid(), r->user, group,
1381 cur_subgroup_depth+1, max_subgroup_depth);
1385 * 4.B. We didn't find the user in this subgroup, so recurse into
1386 * it and keep looking.
1388 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1389 " util_ldap: user %s not found in subgroup (%s) at"
1390 " level %d of %d.", getpid(), r->user, group,
1391 cur_subgroup_depth+1, max_subgroup_depth);
1392 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1393 value, subgroupAttrs,
1395 cur_subgroup_depth+1,
1396 max_subgroup_depth);
1405 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1406 const char *url, const char *basedn,
1407 int scope, char **attrs, const char *filter,
1408 const char *bindpw, const char **binddn,
1409 const char ***retvals)
1411 const char **vals = NULL;
1414 LDAPMessage *res, *entry;
1418 util_url_node_t *curl; /* Cached URL node */
1419 util_url_node_t curnode;
1420 util_search_node_t *search_nodep; /* Cached search node */
1421 util_search_node_t the_search_node;
1424 util_ldap_state_t *st =
1425 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1428 /* Get the cache node for this url */
1431 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1434 curl = util_ald_create_caches(st, url);
1436 LDAP_CACHE_UNLOCK();
1440 the_search_node.username = filter;
1441 search_nodep = util_ald_cache_fetch(curl->search_cache,
1443 if (search_nodep != NULL) {
1445 /* found entry in search cache... */
1446 curtime = apr_time_now();
1449 * Remove this item from the cache if its expired. If the sent
1450 * password doesn't match the storepassword, the entry will
1451 * be removed and readded later if the credentials pass
1454 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1455 /* ...but entry is too old */
1456 util_ald_cache_remove(curl->search_cache, search_nodep);
1458 else if ( (search_nodep->bindpw)
1459 && (search_nodep->bindpw[0] != '\0')
1460 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1462 /* ...and entry is valid */
1463 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1466 *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
1467 for (i = 0; i < search_nodep->numvals; i++) {
1468 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1471 LDAP_CACHE_UNLOCK();
1472 ldc->reason = "Authentication successful (cached)";
1473 return LDAP_SUCCESS;
1476 /* unlock this read lock */
1477 LDAP_CACHE_UNLOCK();
1481 * At this point, there is no valid cached search, so lets do the search.
1485 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1488 if (failures++ > 10) {
1491 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1495 /* try do the search */
1496 result = ldap_search_ext_s(ldc->ldap,
1497 (char *)basedn, scope,
1498 (char *)filter, attrs, 0,
1499 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1500 if (AP_LDAP_IS_SERVER_DOWN(result))
1502 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1503 uldap_connection_unbind(ldc);
1507 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1508 if (result != LDAP_SUCCESS) {
1509 ldc->reason = "ldap_search_ext_s() for user failed";
1514 * We should have found exactly one entry; to find a different
1515 * number is an error.
1517 count = ldap_count_entries(ldc->ldap, res);
1521 ldc->reason = "User not found";
1523 ldc->reason = "User is not unique (search found two "
1526 return LDAP_NO_SUCH_OBJECT;
1529 entry = ldap_first_entry(ldc->ldap, res);
1531 /* Grab the dn, copy it into the pool, and free it again */
1532 dn = ldap_get_dn(ldc->ldap, entry);
1533 *binddn = apr_pstrdup(r->pool, dn);
1537 * A bind to the server with an empty password always succeeds, so
1538 * we check to ensure that the password is not empty. This implies
1539 * that users who actually do have empty passwords will never be
1540 * able to authenticate with this module. I don't see this as a big
1543 if (!bindpw || strlen(bindpw) <= 0) {
1545 ldc->reason = "Empty password not allowed";
1546 return LDAP_INVALID_CREDENTIALS;
1550 * Attempt to bind with the retrieved dn and the password. If the bind
1551 * fails, it means that the password is wrong (the dn obviously
1552 * exists, since we just retrieved it)
1554 result = ldap_simple_bind_s(ldc->ldap,
1557 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1558 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1559 "failed with server down";
1561 uldap_connection_unbind(ldc);
1565 /* failure? if so - return */
1566 if (result != LDAP_SUCCESS) {
1567 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1569 uldap_connection_unbind(ldc);
1574 * We have just bound the connection to a different user and password
1575 * combination, which might be reused unintentionally next time this
1576 * connection is used from the connection pool. To ensure no confusion,
1577 * we mark the connection as unbound.
1583 * Get values for the provided attributes.
1589 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1596 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1597 while (values && values[j]) {
1598 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1599 : apr_pstrdup(r->pool, values[j]);
1602 ldap_value_free(values);
1610 * Add the new username to the search cache.
1614 the_search_node.username = filter;
1615 the_search_node.dn = *binddn;
1616 the_search_node.bindpw = bindpw;
1617 the_search_node.lastbind = apr_time_now();
1618 the_search_node.vals = vals;
1619 the_search_node.numvals = numvals;
1621 /* Search again to make sure that another thread didn't ready insert
1622 * this node into the cache before we got here. If it does exist then
1623 * update the lastbind
1625 search_nodep = util_ald_cache_fetch(curl->search_cache,
1627 if ((search_nodep == NULL) ||
1628 (strcmp(*binddn, search_nodep->dn) != 0)) {
1630 /* Nothing in cache, insert new entry */
1631 util_ald_cache_insert(curl->search_cache, &the_search_node);
1633 else if ((!search_nodep->bindpw) ||
1634 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1636 /* Entry in cache is invalid, remove it and insert new one */
1637 util_ald_cache_remove(curl->search_cache, search_nodep);
1638 util_ald_cache_insert(curl->search_cache, &the_search_node);
1641 /* Cache entry is valid, update lastbind */
1642 search_nodep->lastbind = the_search_node.lastbind;
1644 LDAP_CACHE_UNLOCK();
1648 ldc->reason = "Authentication successful";
1649 return LDAP_SUCCESS;
1653 * This function will return the DN of the entry matching userid.
1654 * It is used to get the DN in case some other module than mod_auth_ldap
1655 * has authenticated the user.
1656 * The function is basically a copy of uldap_cache_checkuserid
1657 * with password checking removed.
1659 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1660 const char *url, const char *basedn,
1661 int scope, char **attrs, const char *filter,
1662 const char **binddn, const char ***retvals)
1664 const char **vals = NULL;
1667 LDAPMessage *res, *entry;
1671 util_url_node_t *curl; /* Cached URL node */
1672 util_url_node_t curnode;
1673 util_search_node_t *search_nodep; /* Cached search node */
1674 util_search_node_t the_search_node;
1677 util_ldap_state_t *st =
1678 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1681 /* Get the cache node for this url */
1684 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1687 curl = util_ald_create_caches(st, url);
1689 LDAP_CACHE_UNLOCK();
1693 the_search_node.username = filter;
1694 search_nodep = util_ald_cache_fetch(curl->search_cache,
1696 if (search_nodep != NULL) {
1698 /* found entry in search cache... */
1699 curtime = apr_time_now();
1702 * Remove this item from the cache if its expired.
1704 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1705 /* ...but entry is too old */
1706 util_ald_cache_remove(curl->search_cache, search_nodep);
1709 /* ...and entry is valid */
1710 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1713 *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
1714 for (i = 0; i < search_nodep->numvals; i++) {
1715 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1718 LDAP_CACHE_UNLOCK();
1719 ldc->reason = "Search successful (cached)";
1720 return LDAP_SUCCESS;
1723 /* unlock this read lock */
1724 LDAP_CACHE_UNLOCK();
1728 * At this point, there is no valid cached search, so lets do the search.
1732 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1735 if (failures++ > 10) {
1738 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1742 /* try do the search */
1743 result = ldap_search_ext_s(ldc->ldap,
1744 (char *)basedn, scope,
1745 (char *)filter, attrs, 0,
1746 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1747 if (AP_LDAP_IS_SERVER_DOWN(result))
1749 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1750 uldap_connection_unbind(ldc);
1754 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1755 if (result != LDAP_SUCCESS) {
1756 ldc->reason = "ldap_search_ext_s() for user failed";
1761 * We should have found exactly one entry; to find a different
1762 * number is an error.
1764 count = ldap_count_entries(ldc->ldap, res);
1768 ldc->reason = "User not found";
1770 ldc->reason = "User is not unique (search found two "
1773 return LDAP_NO_SUCH_OBJECT;
1776 entry = ldap_first_entry(ldc->ldap, res);
1778 /* Grab the dn, copy it into the pool, and free it again */
1779 dn = ldap_get_dn(ldc->ldap, entry);
1780 *binddn = apr_pstrdup(r->pool, dn);
1784 * Get values for the provided attributes.
1790 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1797 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1798 while (values && values[j]) {
1799 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1800 : apr_pstrdup(r->pool, values[j]);
1803 ldap_value_free(values);
1811 * Add the new username to the search cache.
1815 the_search_node.username = filter;
1816 the_search_node.dn = *binddn;
1817 the_search_node.bindpw = NULL;
1818 the_search_node.lastbind = apr_time_now();
1819 the_search_node.vals = vals;
1820 the_search_node.numvals = numvals;
1822 /* Search again to make sure that another thread didn't ready insert
1823 * this node into the cache before we got here. If it does exist then
1824 * update the lastbind
1826 search_nodep = util_ald_cache_fetch(curl->search_cache,
1828 if ((search_nodep == NULL) ||
1829 (strcmp(*binddn, search_nodep->dn) != 0)) {
1831 /* Nothing in cache, insert new entry */
1832 util_ald_cache_insert(curl->search_cache, &the_search_node);
1835 * Don't update lastbind on entries with bindpw because
1836 * we haven't verified that password. It's OK to update
1837 * the entry if there is no password in it.
1839 else if (!search_nodep->bindpw) {
1840 /* Cache entry is valid, update lastbind */
1841 search_nodep->lastbind = the_search_node.lastbind;
1843 LDAP_CACHE_UNLOCK();
1848 ldc->reason = "Search successful";
1849 return LDAP_SUCCESS;
1853 * Reports if ssl support is enabled
1855 * 1 = enabled, 0 = not enabled
1857 static int uldap_ssl_supported(request_rec *r)
1859 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1860 r->server->module_config, &ldap_module);
1862 return(st->ssl_supported);
1866 /* ---------------------------------------- */
1867 /* config directives */
1870 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1873 util_ldap_state_t *st =
1874 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1876 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1882 st->cache_bytes = atol(bytes);
1884 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1885 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1886 " cache size to %" APR_SIZE_T_FMT " bytes.",
1887 getpid(), st->cache_bytes);
1892 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1895 util_ldap_state_t *st =
1896 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1898 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1905 st->cache_file = ap_server_root_relative(st->pool, file);
1908 st->cache_file = NULL;
1911 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1912 "LDAP cache: Setting shared memory cache file to %s bytes.",
1918 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1921 util_ldap_state_t *st =
1922 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1924 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1930 st->search_cache_ttl = atol(ttl) * 1000000;
1932 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1933 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1934 " microseconds.", getpid(), st->search_cache_ttl);
1939 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1942 util_ldap_state_t *st =
1943 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1945 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1951 st->search_cache_size = atol(size);
1952 if (st->search_cache_size < 0) {
1953 st->search_cache_size = 0;
1956 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1957 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1958 " to %ld entries.", getpid(), st->search_cache_size);
1963 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1966 util_ldap_state_t *st =
1967 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1969 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1975 st->compare_cache_ttl = atol(ttl) * 1000000;
1977 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1978 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1979 " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1984 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1987 util_ldap_state_t *st =
1988 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1990 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1996 st->compare_cache_size = atol(size);
1997 if (st->compare_cache_size < 0) {
1998 st->compare_cache_size = 0;
2001 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2002 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
2003 " to %ld entries.", getpid(), st->compare_cache_size);
2010 * Parse the certificate type.
2012 * The type can be one of the following:
2013 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
2014 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
2016 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
2018 static int util_ldap_parse_cert_type(const char *type)
2020 /* Authority file in binary DER format */
2021 if (0 == strcasecmp("CA_DER", type)) {
2022 return APR_LDAP_CA_TYPE_DER;
2025 /* Authority file in Base64 format */
2026 else if (0 == strcasecmp("CA_BASE64", type)) {
2027 return APR_LDAP_CA_TYPE_BASE64;
2030 /* Netscape certificate database file/directory */
2031 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2032 return APR_LDAP_CA_TYPE_CERT7_DB;
2035 /* Netscape secmod file/directory */
2036 else if (0 == strcasecmp("CA_SECMOD", type)) {
2037 return APR_LDAP_CA_TYPE_SECMOD;
2040 /* Client cert file in DER format */
2041 else if (0 == strcasecmp("CERT_DER", type)) {
2042 return APR_LDAP_CERT_TYPE_DER;
2045 /* Client cert file in Base64 format */
2046 else if (0 == strcasecmp("CERT_BASE64", type)) {
2047 return APR_LDAP_CERT_TYPE_BASE64;
2050 /* Client cert file in PKCS#12 format */
2051 else if (0 == strcasecmp("CERT_PFX", type)) {
2052 return APR_LDAP_CERT_TYPE_PFX;
2055 /* Netscape client cert database file/directory */
2056 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2057 return APR_LDAP_CERT_TYPE_KEY3_DB;
2060 /* Netscape client cert nickname */
2061 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2062 return APR_LDAP_CERT_TYPE_NICKNAME;
2065 /* Client cert key file in DER format */
2066 else if (0 == strcasecmp("KEY_DER", type)) {
2067 return APR_LDAP_KEY_TYPE_DER;
2070 /* Client cert key file in Base64 format */
2071 else if (0 == strcasecmp("KEY_BASE64", type)) {
2072 return APR_LDAP_KEY_TYPE_BASE64;
2075 /* Client cert key file in PKCS#12 format */
2076 else if (0 == strcasecmp("KEY_PFX", type)) {
2077 return APR_LDAP_KEY_TYPE_PFX;
2081 return APR_LDAP_CA_TYPE_UNKNOWN;
2088 * Set LDAPTrustedGlobalCert.
2090 * This directive takes either two or three arguments:
2091 * - certificate type
2092 * - certificate file / directory / nickname
2093 * - certificate password (optional)
2095 * This directive may only be used globally.
2097 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2101 const char *password)
2103 util_ldap_state_t *st =
2104 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2106 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2110 apr_ldap_opt_tls_cert_t *cert;
2116 /* handle the certificate type */
2118 cert_type = util_ldap_parse_cert_type(type);
2119 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2120 return apr_psprintf(cmd->pool, "The certificate type %s is "
2121 "not recognised. It should be one "
2122 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2123 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2124 "CERT_KEY3_DB, CERT_NICKNAME, "
2125 "KEY_DER, KEY_BASE64", type);
2129 return "Certificate type was not specified.";
2132 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2133 "LDAP: SSL trusted global cert - %s (type %s)",
2136 /* add the certificate to the global array */
2137 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2138 cert->type = cert_type;
2140 cert->password = password;
2142 /* if file is a file or path, fix the path */
2143 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2144 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2146 cert->path = ap_server_root_relative(cmd->pool, file);
2148 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2151 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2152 "LDAP: Could not open SSL trusted certificate "
2153 "authority file - %s",
2154 cert->path == NULL ? file : cert->path);
2155 return "Invalid global certificate file path";
2164 * Set LDAPTrustedClientCert.
2166 * This directive takes either two or three arguments:
2167 * - certificate type
2168 * - certificate file / directory / nickname
2169 * - certificate password (optional)
2171 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2175 const char *password)
2177 util_ldap_state_t *st =
2178 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2183 apr_ldap_opt_tls_cert_t *cert;
2185 /* handle the certificate type */
2187 cert_type = util_ldap_parse_cert_type(type);
2188 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2189 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2190 "not recognised. It should be one "
2191 "of CERT_DER, CERT_BASE64, "
2192 "CERT_NICKNAME, CERT_PFX,"
2193 "KEY_DER, KEY_BASE64, KEY_PFX",
2196 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2197 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2198 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2199 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2200 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2201 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2202 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2203 "only valid within a "
2204 "LDAPTrustedGlobalCert directive. "
2205 "Only CERT_DER, CERT_BASE64, "
2206 "CERT_NICKNAME, KEY_DER, and "
2207 "KEY_BASE64 may be used.", type);
2211 return "Certificate type was not specified.";
2214 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2215 "LDAP: SSL trusted client cert - %s (type %s)",
2218 /* add the certificate to the global array */
2219 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2220 cert->type = cert_type;
2222 cert->password = password;
2224 /* if file is a file or path, fix the path */
2225 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2226 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2228 cert->path = ap_server_root_relative(cmd->pool, file);
2230 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2233 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2234 "LDAP: Could not open SSL client certificate "
2236 cert->path == NULL ? file : cert->path);
2237 return "Invalid client certificate file path";
2247 * Set LDAPTrustedMode.
2249 * This directive sets what encryption mode to use on a connection:
2250 * - None (No encryption)
2251 * - SSL (SSL encryption)
2252 * - STARTTLS (TLS encryption)
2254 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2257 util_ldap_state_t *st =
2258 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2261 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2262 "LDAP: SSL trusted mode - %s",
2265 if (0 == strcasecmp("NONE", mode)) {
2266 st->secure = APR_LDAP_NONE;
2268 else if (0 == strcasecmp("SSL", mode)) {
2269 st->secure = APR_LDAP_SSL;
2271 else if ( (0 == strcasecmp("TLS", mode))
2272 || (0 == strcasecmp("STARTTLS", mode))) {
2273 st->secure = APR_LDAP_STARTTLS;
2276 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2277 "SSL, or TLS/STARTTLS";
2284 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2288 util_ldap_state_t *st =
2289 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2291 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2297 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2298 "LDAP: SSL verify server certificate - %s",
2299 mode?"TRUE":"FALSE");
2301 st->verify_svr_cert = mode;
2307 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2311 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2312 util_ldap_state_t *st =
2313 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2316 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2322 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2323 st->connectionTimeout = atol(ttl);
2325 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2326 "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2327 " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2329 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2330 "LDAP: Connection timeout option not supported by the "
2331 "LDAP SDK in use." );
2338 static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
2342 util_ldap_config_t *dc = config;
2344 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2345 "LDAP: Setting refferal chasing %s",
2346 (mode == AP_LDAP_CHASEREFERRALS_ON) ? "ON" : "OFF");
2348 dc->ChaseReferrals = mode;
2353 static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
2355 const char *hop_limit)
2357 util_ldap_config_t *dc = config;
2359 dc->ReferralHopLimit = atol(hop_limit);
2361 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2362 "LDAP: Limit chased referrals to maximum of %d hops.",
2363 dc->ReferralHopLimit);
2368 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
2369 util_ldap_config_t *dc =
2370 (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
2372 /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
2373 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2374 dc->ReferralHopLimit = AP_LDAP_DEFAULT_HOPLIMIT;
2380 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2382 util_ldap_state_t *st =
2383 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2385 /* Create a per vhost pool for mod_ldap to use, serialized with
2386 * st->mutex (also one per vhost). both are replicated by fork(),
2387 * no shared memory managed by either.
2389 apr_pool_create(&st->pool, p);
2391 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2394 st->cache_bytes = 100000;
2395 st->search_cache_ttl = 600000000;
2396 st->search_cache_size = 1024;
2397 st->compare_cache_ttl = 600000000;
2398 st->compare_cache_size = 1024;
2399 st->connections = NULL;
2400 st->ssl_supported = 0;
2401 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2402 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2403 st->secure = APR_LDAP_NONE;
2405 st->connectionTimeout = 10;
2406 st->verify_svr_cert = 1;
2411 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2414 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2415 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2416 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2418 st->pool = overrides->pool;
2420 st->mutex = overrides->mutex;
2423 /* The cache settings can not be modified in a
2424 virtual host since all server use the same
2425 shared memory cache. */
2426 st->cache_bytes = base->cache_bytes;
2427 st->search_cache_ttl = base->search_cache_ttl;
2428 st->search_cache_size = base->search_cache_size;
2429 st->compare_cache_ttl = base->compare_cache_ttl;
2430 st->compare_cache_size = base->compare_cache_size;
2431 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2433 st->connections = NULL;
2434 st->ssl_supported = 0;
2435 st->global_certs = apr_array_append(p, base->global_certs,
2436 overrides->global_certs);
2437 st->client_certs = apr_array_append(p, base->client_certs,
2438 overrides->client_certs);
2439 st->secure = (overrides->secure_set == 0) ? base->secure
2440 : overrides->secure;
2442 /* These LDAP connection settings can not be overwritten in
2443 a virtual host. Once set in the base server, they must
2444 remain the same. None of the LDAP SDKs seem to be able
2445 to handle setting the verify_svr_cert flag on a
2446 per-connection basis. The OpenLDAP client appears to be
2447 able to handle the connection timeout per-connection
2448 but the Novell SDK cannot. Allowing the timeout to
2449 be set by each vhost is of little value so rather than
2450 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2451 is being enforced on this setting as well. */
2452 st->connectionTimeout = base->connectionTimeout;
2453 st->verify_svr_cert = base->verify_svr_cert;
2458 static apr_status_t util_ldap_cleanup_module(void *data)
2461 server_rec *s = data;
2462 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2463 s->module_config, &ldap_module);
2465 if (st->ssl_supported) {
2466 apr_ldap_ssl_deinit();
2473 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2474 apr_pool_t *ptemp, server_rec *s)
2476 apr_status_t result;
2477 server_rec *s_vhost;
2478 util_ldap_state_t *st_vhost;
2480 util_ldap_state_t *st = (util_ldap_state_t *)
2481 ap_get_module_config(s->module_config,
2485 const char *userdata_key = "util_ldap_init";
2486 apr_ldap_err_t *result_err = NULL;
2489 /* util_ldap_post_config() will be called twice. Don't bother
2490 * going through all of the initialization on the first call
2491 * because it will just be thrown away.*/
2492 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2494 apr_pool_userdata_set((const void *)1, userdata_key,
2495 apr_pool_cleanup_null, s->process->pool);
2497 #if APR_HAS_SHARED_MEMORY
2498 /* If the cache file already exists then delete it. Otherwise we are
2499 * going to run into problems creating the shared memory. */
2500 if (st->cache_file) {
2501 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2503 apr_file_remove(lck_file, ptemp);
2509 #if APR_HAS_SHARED_MEMORY
2510 /* initializing cache if shared memory size is not zero and we already
2511 * don't have shm address
2513 if (!st->cache_shm && st->cache_bytes > 0) {
2515 result = util_ldap_cache_init(p, st);
2516 if (result != APR_SUCCESS) {
2517 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2518 "LDAP cache: could not create shared memory segment");
2523 #if APR_HAS_SHARED_MEMORY
2524 if (st->cache_file) {
2525 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2530 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2531 st->lock_file, APR_LOCK_DEFAULT,
2533 if (result != APR_SUCCESS) {
2537 #ifdef AP_NEED_SET_MUTEX_PERMS
2538 result = ap_unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2539 if (result != APR_SUCCESS) {
2540 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2541 "LDAP cache: failed to set mutex permissions");
2546 /* merge config in all vhost */
2549 st_vhost = (util_ldap_state_t *)
2550 ap_get_module_config(s_vhost->module_config,
2553 #if APR_HAS_SHARED_MEMORY
2554 st_vhost->cache_shm = st->cache_shm;
2555 st_vhost->cache_rmm = st->cache_rmm;
2556 st_vhost->cache_file = st->cache_file;
2557 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2558 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2559 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2560 s_vhost->server_hostname);
2562 st_vhost->lock_file = st->lock_file;
2563 s_vhost = s_vhost->next;
2565 #if APR_HAS_SHARED_MEMORY
2568 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2569 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2570 "shared memory cache");
2574 /* log the LDAP SDK used
2577 apr_ldap_err_t *result = NULL;
2578 apr_ldap_info(p, &(result));
2579 if (result != NULL) {
2580 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2584 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2585 util_ldap_cleanup_module);
2588 * Initialize SSL support, and log the result for the benefit of the admin.
2590 * If SSL is not supported it is not necessarily an error, as the
2591 * application may not want to use it.
2593 rc = apr_ldap_ssl_init(p,
2597 if (APR_SUCCESS == rc) {
2598 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2599 (void *)st->global_certs, &(result_err));
2602 if (APR_SUCCESS == rc) {
2603 st->ssl_supported = 1;
2604 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2605 "LDAP: SSL support available" );
2608 st->ssl_supported = 0;
2609 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2610 "LDAP: SSL support unavailable%s%s",
2611 result_err ? ": " : "",
2612 result_err ? result_err->reason : "");
2615 /* Initialize the rebind callback's cross reference list. */
2616 apr_ldap_rebind_init (p);
2621 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2624 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2627 if (!st->util_ldap_cache_lock) return;
2629 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2631 if (sts != APR_SUCCESS) {
2632 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2633 "Failed to initialise global mutex %s in child process %"
2635 st->lock_file, getpid());
2639 static const command_rec util_ldap_cmds[] = {
2640 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2642 "Set the size of the shared memory cache (in bytes). Use "
2643 "0 to disable the shared memory cache. (default: 100000)"),
2645 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2647 "Set the file name for the shared memory cache."),
2649 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2651 "Set the maximum number of entries that are possible in the "
2652 "LDAP search cache. Use 0 for no limit. "
2653 "-1 disables the cache. (default: 1024)"),
2655 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2657 "Set the maximum time (in seconds) that an item can be "
2658 "cached in the LDAP search cache. Use 0 for no limit. "
2661 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2663 "Set the maximum number of entries that are possible "
2664 "in the LDAP compare cache. Use 0 for no limit. "
2665 "Use -1 to disable the cache. (default: 1024)"),
2667 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2669 "Set the maximum time (in seconds) that an item is cached "
2670 "in the LDAP operation cache. Use 0 for no limit. "
2673 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2675 "Takes three arguments; the first argument is the cert "
2676 "type of the second argument, one of CA_DER, CA_BASE64, "
2677 "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2678 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
2679 "specifes the file and/or directory containing the trusted CA "
2680 "certificates (and global client certs for Netware) used to "
2681 "validate the LDAP server. The third argument is an optional "
2682 "passphrase if applicable."),
2684 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2686 "Takes three arguments: the first argument is the certificate "
2687 "type of the second argument, one of CA_DER, CA_BASE64, "
2688 "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2689 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
2690 "specifies the file and/or directory containing the client "
2691 "certificate, or certificate ID used to validate this LDAP "
2692 "client. The third argument is an optional passphrase if "
2695 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2697 "Specify the type of security that should be applied to "
2698 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2700 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2702 "Set to 'ON' requires that the server certificate be verified"
2703 " before a secure LDAP connection can be establish. Default"
2706 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2708 "Specify the LDAP socket connection timeout in seconds "
2711 AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
2713 "Choose whether referrals are chased ['ON'|'OFF']. Default 'ON'"),
2715 AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
2717 "Limit the number of referral hops that LDAP can follow. "
2718 "(Integer value, default=" AP_LDAP_DEFAULT_HOPLIMIT_STR ")"),
2723 static void util_ldap_register_hooks(apr_pool_t *p)
2725 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2726 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2727 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2728 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2729 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2730 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2731 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2732 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2733 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2734 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2735 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2737 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2738 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2739 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2742 module AP_MODULE_DECLARE_DATA ldap_module = {
2743 STANDARD20_MODULE_STUFF,
2744 util_ldap_create_dir_config, /* create dir config */
2745 NULL, /* merge dir config */
2746 util_ldap_create_config, /* create server config */
2747 util_ldap_merge_config, /* merge server config */
2748 util_ldap_cmds, /* command table */
2749 util_ldap_register_hooks, /* set up request processing hooks */