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);
1467 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1468 while (search_nodep->vals[i]) {
1469 (*retvals)[i] = apr_pstrdup(r->pool,
1470 search_nodep->vals[i]);
1474 LDAP_CACHE_UNLOCK();
1475 ldc->reason = "Authentication successful (cached)";
1476 return LDAP_SUCCESS;
1479 /* unlock this read lock */
1480 LDAP_CACHE_UNLOCK();
1484 * At this point, there is no valid cached search, so lets do the search.
1488 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1491 if (failures++ > 10) {
1494 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1498 /* try do the search */
1499 result = ldap_search_ext_s(ldc->ldap,
1500 (char *)basedn, scope,
1501 (char *)filter, attrs, 0,
1502 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1503 if (AP_LDAP_IS_SERVER_DOWN(result))
1505 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1506 uldap_connection_unbind(ldc);
1510 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1511 if (result != LDAP_SUCCESS) {
1512 ldc->reason = "ldap_search_ext_s() for user failed";
1517 * We should have found exactly one entry; to find a different
1518 * number is an error.
1520 count = ldap_count_entries(ldc->ldap, res);
1524 ldc->reason = "User not found";
1526 ldc->reason = "User is not unique (search found two "
1529 return LDAP_NO_SUCH_OBJECT;
1532 entry = ldap_first_entry(ldc->ldap, res);
1534 /* Grab the dn, copy it into the pool, and free it again */
1535 dn = ldap_get_dn(ldc->ldap, entry);
1536 *binddn = apr_pstrdup(r->pool, dn);
1540 * A bind to the server with an empty password always succeeds, so
1541 * we check to ensure that the password is not empty. This implies
1542 * that users who actually do have empty passwords will never be
1543 * able to authenticate with this module. I don't see this as a big
1546 if (!bindpw || strlen(bindpw) <= 0) {
1548 ldc->reason = "Empty password not allowed";
1549 return LDAP_INVALID_CREDENTIALS;
1553 * Attempt to bind with the retrieved dn and the password. If the bind
1554 * fails, it means that the password is wrong (the dn obviously
1555 * exists, since we just retrieved it)
1557 result = ldap_simple_bind_s(ldc->ldap,
1560 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1561 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1562 "failed with server down";
1564 uldap_connection_unbind(ldc);
1568 /* failure? if so - return */
1569 if (result != LDAP_SUCCESS) {
1570 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1572 uldap_connection_unbind(ldc);
1577 * We have just bound the connection to a different user and password
1578 * combination, which might be reused unintentionally next time this
1579 * connection is used from the connection pool. To ensure no confusion,
1580 * we mark the connection as unbound.
1586 * Get values for the provided attributes.
1592 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1599 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1600 while (values && values[j]) {
1601 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1602 : apr_pstrdup(r->pool, values[j]);
1605 ldap_value_free(values);
1613 * Add the new username to the search cache.
1617 the_search_node.username = filter;
1618 the_search_node.dn = *binddn;
1619 the_search_node.bindpw = bindpw;
1620 the_search_node.lastbind = apr_time_now();
1621 the_search_node.vals = vals;
1622 the_search_node.numvals = numvals;
1624 /* Search again to make sure that another thread didn't ready insert
1625 * this node into the cache before we got here. If it does exist then
1626 * update the lastbind
1628 search_nodep = util_ald_cache_fetch(curl->search_cache,
1630 if ((search_nodep == NULL) ||
1631 (strcmp(*binddn, search_nodep->dn) != 0)) {
1633 /* Nothing in cache, insert new entry */
1634 util_ald_cache_insert(curl->search_cache, &the_search_node);
1636 else if ((!search_nodep->bindpw) ||
1637 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1639 /* Entry in cache is invalid, remove it and insert new one */
1640 util_ald_cache_remove(curl->search_cache, search_nodep);
1641 util_ald_cache_insert(curl->search_cache, &the_search_node);
1644 /* Cache entry is valid, update lastbind */
1645 search_nodep->lastbind = the_search_node.lastbind;
1647 LDAP_CACHE_UNLOCK();
1651 ldc->reason = "Authentication successful";
1652 return LDAP_SUCCESS;
1656 * This function will return the DN of the entry matching userid.
1657 * It is used to get the DN in case some other module than mod_auth_ldap
1658 * has authenticated the user.
1659 * The function is basically a copy of uldap_cache_checkuserid
1660 * with password checking removed.
1662 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1663 const char *url, const char *basedn,
1664 int scope, char **attrs, const char *filter,
1665 const char **binddn, const char ***retvals)
1667 const char **vals = NULL;
1670 LDAPMessage *res, *entry;
1674 util_url_node_t *curl; /* Cached URL node */
1675 util_url_node_t curnode;
1676 util_search_node_t *search_nodep; /* Cached search node */
1677 util_search_node_t the_search_node;
1680 util_ldap_state_t *st =
1681 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1684 /* Get the cache node for this url */
1687 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1690 curl = util_ald_create_caches(st, url);
1692 LDAP_CACHE_UNLOCK();
1696 the_search_node.username = filter;
1697 search_nodep = util_ald_cache_fetch(curl->search_cache,
1699 if (search_nodep != NULL) {
1701 /* found entry in search cache... */
1702 curtime = apr_time_now();
1705 * Remove this item from the cache if its expired.
1707 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1708 /* ...but entry is too old */
1709 util_ald_cache_remove(curl->search_cache, search_nodep);
1712 /* ...and entry is valid */
1713 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1717 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1718 while (search_nodep->vals[i]) {
1719 (*retvals)[i] = apr_pstrdup(r->pool,
1720 search_nodep->vals[i]);
1724 LDAP_CACHE_UNLOCK();
1725 ldc->reason = "Search successful (cached)";
1726 return LDAP_SUCCESS;
1729 /* unlock this read lock */
1730 LDAP_CACHE_UNLOCK();
1734 * At this point, there is no valid cached search, so lets do the search.
1738 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1741 if (failures++ > 10) {
1744 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1748 /* try do the search */
1749 result = ldap_search_ext_s(ldc->ldap,
1750 (char *)basedn, scope,
1751 (char *)filter, attrs, 0,
1752 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1753 if (AP_LDAP_IS_SERVER_DOWN(result))
1755 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1756 uldap_connection_unbind(ldc);
1760 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1761 if (result != LDAP_SUCCESS) {
1762 ldc->reason = "ldap_search_ext_s() for user failed";
1767 * We should have found exactly one entry; to find a different
1768 * number is an error.
1770 count = ldap_count_entries(ldc->ldap, res);
1774 ldc->reason = "User not found";
1776 ldc->reason = "User is not unique (search found two "
1779 return LDAP_NO_SUCH_OBJECT;
1782 entry = ldap_first_entry(ldc->ldap, res);
1784 /* Grab the dn, copy it into the pool, and free it again */
1785 dn = ldap_get_dn(ldc->ldap, entry);
1786 *binddn = apr_pstrdup(r->pool, dn);
1790 * Get values for the provided attributes.
1796 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1803 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1804 while (values && values[j]) {
1805 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1806 : apr_pstrdup(r->pool, values[j]);
1809 ldap_value_free(values);
1817 * Add the new username to the search cache.
1821 the_search_node.username = filter;
1822 the_search_node.dn = *binddn;
1823 the_search_node.bindpw = NULL;
1824 the_search_node.lastbind = apr_time_now();
1825 the_search_node.vals = vals;
1826 the_search_node.numvals = numvals;
1828 /* Search again to make sure that another thread didn't ready insert
1829 * this node into the cache before we got here. If it does exist then
1830 * update the lastbind
1832 search_nodep = util_ald_cache_fetch(curl->search_cache,
1834 if ((search_nodep == NULL) ||
1835 (strcmp(*binddn, search_nodep->dn) != 0)) {
1837 /* Nothing in cache, insert new entry */
1838 util_ald_cache_insert(curl->search_cache, &the_search_node);
1841 * Don't update lastbind on entries with bindpw because
1842 * we haven't verified that password. It's OK to update
1843 * the entry if there is no password in it.
1845 else if (!search_nodep->bindpw) {
1846 /* Cache entry is valid, update lastbind */
1847 search_nodep->lastbind = the_search_node.lastbind;
1849 LDAP_CACHE_UNLOCK();
1854 ldc->reason = "Search successful";
1855 return LDAP_SUCCESS;
1859 * Reports if ssl support is enabled
1861 * 1 = enabled, 0 = not enabled
1863 static int uldap_ssl_supported(request_rec *r)
1865 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1866 r->server->module_config, &ldap_module);
1868 return(st->ssl_supported);
1872 /* ---------------------------------------- */
1873 /* config directives */
1876 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1879 util_ldap_state_t *st =
1880 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1882 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1888 st->cache_bytes = atol(bytes);
1890 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1891 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1892 " cache size to %" APR_SIZE_T_FMT " bytes.",
1893 getpid(), st->cache_bytes);
1898 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1901 util_ldap_state_t *st =
1902 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1904 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1911 st->cache_file = ap_server_root_relative(st->pool, file);
1914 st->cache_file = NULL;
1917 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1918 "LDAP cache: Setting shared memory cache file to %s bytes.",
1924 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1927 util_ldap_state_t *st =
1928 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1930 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1936 st->search_cache_ttl = atol(ttl) * 1000000;
1938 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1939 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1940 " microseconds.", getpid(), st->search_cache_ttl);
1945 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1948 util_ldap_state_t *st =
1949 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1951 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1957 st->search_cache_size = atol(size);
1958 if (st->search_cache_size < 0) {
1959 st->search_cache_size = 0;
1962 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1963 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1964 " to %ld entries.", getpid(), st->search_cache_size);
1969 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1972 util_ldap_state_t *st =
1973 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1975 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1981 st->compare_cache_ttl = atol(ttl) * 1000000;
1983 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1984 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1985 " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1990 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1993 util_ldap_state_t *st =
1994 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1996 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2002 st->compare_cache_size = atol(size);
2003 if (st->compare_cache_size < 0) {
2004 st->compare_cache_size = 0;
2007 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2008 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
2009 " to %ld entries.", getpid(), st->compare_cache_size);
2016 * Parse the certificate type.
2018 * The type can be one of the following:
2019 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
2020 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
2022 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
2024 static int util_ldap_parse_cert_type(const char *type)
2026 /* Authority file in binary DER format */
2027 if (0 == strcasecmp("CA_DER", type)) {
2028 return APR_LDAP_CA_TYPE_DER;
2031 /* Authority file in Base64 format */
2032 else if (0 == strcasecmp("CA_BASE64", type)) {
2033 return APR_LDAP_CA_TYPE_BASE64;
2036 /* Netscape certificate database file/directory */
2037 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2038 return APR_LDAP_CA_TYPE_CERT7_DB;
2041 /* Netscape secmod file/directory */
2042 else if (0 == strcasecmp("CA_SECMOD", type)) {
2043 return APR_LDAP_CA_TYPE_SECMOD;
2046 /* Client cert file in DER format */
2047 else if (0 == strcasecmp("CERT_DER", type)) {
2048 return APR_LDAP_CERT_TYPE_DER;
2051 /* Client cert file in Base64 format */
2052 else if (0 == strcasecmp("CERT_BASE64", type)) {
2053 return APR_LDAP_CERT_TYPE_BASE64;
2056 /* Client cert file in PKCS#12 format */
2057 else if (0 == strcasecmp("CERT_PFX", type)) {
2058 return APR_LDAP_CERT_TYPE_PFX;
2061 /* Netscape client cert database file/directory */
2062 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2063 return APR_LDAP_CERT_TYPE_KEY3_DB;
2066 /* Netscape client cert nickname */
2067 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2068 return APR_LDAP_CERT_TYPE_NICKNAME;
2071 /* Client cert key file in DER format */
2072 else if (0 == strcasecmp("KEY_DER", type)) {
2073 return APR_LDAP_KEY_TYPE_DER;
2076 /* Client cert key file in Base64 format */
2077 else if (0 == strcasecmp("KEY_BASE64", type)) {
2078 return APR_LDAP_KEY_TYPE_BASE64;
2081 /* Client cert key file in PKCS#12 format */
2082 else if (0 == strcasecmp("KEY_PFX", type)) {
2083 return APR_LDAP_KEY_TYPE_PFX;
2087 return APR_LDAP_CA_TYPE_UNKNOWN;
2094 * Set LDAPTrustedGlobalCert.
2096 * This directive takes either two or three arguments:
2097 * - certificate type
2098 * - certificate file / directory / nickname
2099 * - certificate password (optional)
2101 * This directive may only be used globally.
2103 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2107 const char *password)
2109 util_ldap_state_t *st =
2110 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2112 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2116 apr_ldap_opt_tls_cert_t *cert;
2122 /* handle the certificate type */
2124 cert_type = util_ldap_parse_cert_type(type);
2125 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2126 return apr_psprintf(cmd->pool, "The certificate type %s is "
2127 "not recognised. It should be one "
2128 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2129 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2130 "CERT_KEY3_DB, CERT_NICKNAME, "
2131 "KEY_DER, KEY_BASE64", type);
2135 return "Certificate type was not specified.";
2138 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2139 "LDAP: SSL trusted global cert - %s (type %s)",
2142 /* add the certificate to the global array */
2143 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2144 cert->type = cert_type;
2146 cert->password = password;
2148 /* if file is a file or path, fix the path */
2149 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2150 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2152 cert->path = ap_server_root_relative(cmd->pool, file);
2154 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2157 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2158 "LDAP: Could not open SSL trusted certificate "
2159 "authority file - %s",
2160 cert->path == NULL ? file : cert->path);
2161 return "Invalid global certificate file path";
2170 * Set LDAPTrustedClientCert.
2172 * This directive takes either two or three arguments:
2173 * - certificate type
2174 * - certificate file / directory / nickname
2175 * - certificate password (optional)
2177 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2181 const char *password)
2183 util_ldap_state_t *st =
2184 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2189 apr_ldap_opt_tls_cert_t *cert;
2191 /* handle the certificate type */
2193 cert_type = util_ldap_parse_cert_type(type);
2194 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2195 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2196 "not recognised. It should be one "
2197 "of CERT_DER, CERT_BASE64, "
2198 "CERT_NICKNAME, CERT_PFX,"
2199 "KEY_DER, KEY_BASE64, KEY_PFX",
2202 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2203 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2204 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2205 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2206 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2207 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2208 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2209 "only valid within a "
2210 "LDAPTrustedGlobalCert directive. "
2211 "Only CERT_DER, CERT_BASE64, "
2212 "CERT_NICKNAME, KEY_DER, and "
2213 "KEY_BASE64 may be used.", type);
2217 return "Certificate type was not specified.";
2220 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2221 "LDAP: SSL trusted client cert - %s (type %s)",
2224 /* add the certificate to the global array */
2225 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2226 cert->type = cert_type;
2228 cert->password = password;
2230 /* if file is a file or path, fix the path */
2231 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2232 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2234 cert->path = ap_server_root_relative(cmd->pool, file);
2236 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2239 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2240 "LDAP: Could not open SSL client certificate "
2242 cert->path == NULL ? file : cert->path);
2243 return "Invalid client certificate file path";
2253 * Set LDAPTrustedMode.
2255 * This directive sets what encryption mode to use on a connection:
2256 * - None (No encryption)
2257 * - SSL (SSL encryption)
2258 * - STARTTLS (TLS encryption)
2260 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2263 util_ldap_state_t *st =
2264 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2267 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2268 "LDAP: SSL trusted mode - %s",
2271 if (0 == strcasecmp("NONE", mode)) {
2272 st->secure = APR_LDAP_NONE;
2274 else if (0 == strcasecmp("SSL", mode)) {
2275 st->secure = APR_LDAP_SSL;
2277 else if ( (0 == strcasecmp("TLS", mode))
2278 || (0 == strcasecmp("STARTTLS", mode))) {
2279 st->secure = APR_LDAP_STARTTLS;
2282 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2283 "SSL, or TLS/STARTTLS";
2290 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2294 util_ldap_state_t *st =
2295 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2297 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2303 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2304 "LDAP: SSL verify server certificate - %s",
2305 mode?"TRUE":"FALSE");
2307 st->verify_svr_cert = mode;
2313 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2317 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2318 util_ldap_state_t *st =
2319 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2322 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2328 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2329 st->connectionTimeout = atol(ttl);
2331 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2332 "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2333 " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2335 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2336 "LDAP: Connection timeout option not supported by the "
2337 "LDAP SDK in use." );
2344 static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
2348 util_ldap_config_t *dc = config;
2350 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2351 "LDAP: Setting refferal chasing %s",
2352 (mode == AP_LDAP_CHASEREFERRALS_ON) ? "ON" : "OFF");
2354 dc->ChaseReferrals = mode;
2359 static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
2361 const char *hop_limit)
2363 util_ldap_config_t *dc = config;
2365 dc->ReferralHopLimit = atol(hop_limit);
2367 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2368 "LDAP: Limit chased referrals to maximum of %d hops.",
2369 dc->ReferralHopLimit);
2374 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
2375 util_ldap_config_t *dc =
2376 (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
2378 /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
2379 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2380 dc->ReferralHopLimit = AP_LDAP_DEFAULT_HOPLIMIT;
2386 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2388 util_ldap_state_t *st =
2389 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2391 /* Create a per vhost pool for mod_ldap to use, serialized with
2392 * st->mutex (also one per vhost). both are replicated by fork(),
2393 * no shared memory managed by either.
2395 apr_pool_create(&st->pool, p);
2397 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2400 st->cache_bytes = 100000;
2401 st->search_cache_ttl = 600000000;
2402 st->search_cache_size = 1024;
2403 st->compare_cache_ttl = 600000000;
2404 st->compare_cache_size = 1024;
2405 st->connections = NULL;
2406 st->ssl_supported = 0;
2407 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2408 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2409 st->secure = APR_LDAP_NONE;
2411 st->connectionTimeout = 10;
2412 st->verify_svr_cert = 1;
2414 /* Initialize the rebind callback's cross reference list. */
2415 apr_ldap_rebind_init (p);
2420 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2423 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2424 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2425 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2427 st->pool = overrides->pool;
2429 st->mutex = overrides->mutex;
2432 /* The cache settings can not be modified in a
2433 virtual host since all server use the same
2434 shared memory cache. */
2435 st->cache_bytes = base->cache_bytes;
2436 st->search_cache_ttl = base->search_cache_ttl;
2437 st->search_cache_size = base->search_cache_size;
2438 st->compare_cache_ttl = base->compare_cache_ttl;
2439 st->compare_cache_size = base->compare_cache_size;
2440 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2442 st->connections = NULL;
2443 st->ssl_supported = 0;
2444 st->global_certs = apr_array_append(p, base->global_certs,
2445 overrides->global_certs);
2446 st->client_certs = apr_array_append(p, base->client_certs,
2447 overrides->client_certs);
2448 st->secure = (overrides->secure_set == 0) ? base->secure
2449 : overrides->secure;
2451 /* These LDAP connection settings can not be overwritten in
2452 a virtual host. Once set in the base server, they must
2453 remain the same. None of the LDAP SDKs seem to be able
2454 to handle setting the verify_svr_cert flag on a
2455 per-connection basis. The OpenLDAP client appears to be
2456 able to handle the connection timeout per-connection
2457 but the Novell SDK cannot. Allowing the timeout to
2458 be set by each vhost is of little value so rather than
2459 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2460 is being enforced on this setting as well. */
2461 st->connectionTimeout = base->connectionTimeout;
2462 st->verify_svr_cert = base->verify_svr_cert;
2467 static apr_status_t util_ldap_cleanup_module(void *data)
2470 server_rec *s = data;
2471 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2472 s->module_config, &ldap_module);
2474 if (st->ssl_supported) {
2475 apr_ldap_ssl_deinit();
2482 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2483 apr_pool_t *ptemp, server_rec *s)
2485 apr_status_t result;
2486 server_rec *s_vhost;
2487 util_ldap_state_t *st_vhost;
2489 util_ldap_state_t *st = (util_ldap_state_t *)
2490 ap_get_module_config(s->module_config,
2494 const char *userdata_key = "util_ldap_init";
2495 apr_ldap_err_t *result_err = NULL;
2498 /* util_ldap_post_config() will be called twice. Don't bother
2499 * going through all of the initialization on the first call
2500 * because it will just be thrown away.*/
2501 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2503 apr_pool_userdata_set((const void *)1, userdata_key,
2504 apr_pool_cleanup_null, s->process->pool);
2506 #if APR_HAS_SHARED_MEMORY
2507 /* If the cache file already exists then delete it. Otherwise we are
2508 * going to run into problems creating the shared memory. */
2509 if (st->cache_file) {
2510 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2512 apr_file_remove(lck_file, ptemp);
2518 #if APR_HAS_SHARED_MEMORY
2519 /* initializing cache if shared memory size is not zero and we already
2520 * don't have shm address
2522 if (!st->cache_shm && st->cache_bytes > 0) {
2524 result = util_ldap_cache_init(p, st);
2525 if (result != APR_SUCCESS) {
2526 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2527 "LDAP cache: could not create shared memory segment");
2532 #if APR_HAS_SHARED_MEMORY
2533 if (st->cache_file) {
2534 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2539 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2540 st->lock_file, APR_LOCK_DEFAULT,
2542 if (result != APR_SUCCESS) {
2546 #ifdef AP_NEED_SET_MUTEX_PERMS
2547 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2548 if (result != APR_SUCCESS) {
2549 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2550 "LDAP cache: failed to set mutex permissions");
2555 /* merge config in all vhost */
2558 st_vhost = (util_ldap_state_t *)
2559 ap_get_module_config(s_vhost->module_config,
2562 #if APR_HAS_SHARED_MEMORY
2563 st_vhost->cache_shm = st->cache_shm;
2564 st_vhost->cache_rmm = st->cache_rmm;
2565 st_vhost->cache_file = st->cache_file;
2566 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2567 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2568 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2569 s_vhost->server_hostname);
2571 st_vhost->lock_file = st->lock_file;
2572 s_vhost = s_vhost->next;
2574 #if APR_HAS_SHARED_MEMORY
2577 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2578 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2579 "shared memory cache");
2583 /* log the LDAP SDK used
2586 apr_ldap_err_t *result = NULL;
2587 apr_ldap_info(p, &(result));
2588 if (result != NULL) {
2589 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2593 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2594 util_ldap_cleanup_module);
2597 * Initialize SSL support, and log the result for the benefit of the admin.
2599 * If SSL is not supported it is not necessarily an error, as the
2600 * application may not want to use it.
2602 rc = apr_ldap_ssl_init(p,
2606 if (APR_SUCCESS == rc) {
2607 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2608 (void *)st->global_certs, &(result_err));
2611 if (APR_SUCCESS == rc) {
2612 st->ssl_supported = 1;
2613 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2614 "LDAP: SSL support available" );
2617 st->ssl_supported = 0;
2618 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2619 "LDAP: SSL support unavailable%s%s",
2620 result_err ? ": " : "",
2621 result_err ? result_err->reason : "");
2627 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2630 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2633 if (!st->util_ldap_cache_lock) return;
2635 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2637 if (sts != APR_SUCCESS) {
2638 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2639 "Failed to initialise global mutex %s in child process %"
2641 st->lock_file, getpid());
2645 static const command_rec util_ldap_cmds[] = {
2646 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2648 "Set the size of the shared memory cache (in bytes). Use "
2649 "0 to disable the shared memory cache. (default: 100000)"),
2651 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2653 "Set the file name for the shared memory cache."),
2655 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2657 "Set the maximum number of entries that are possible in the "
2658 "LDAP search cache. Use 0 for no limit. "
2659 "-1 disables the cache. (default: 1024)"),
2661 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2663 "Set the maximum time (in seconds) that an item can be "
2664 "cached in the LDAP search cache. Use 0 for no limit. "
2667 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2669 "Set the maximum number of entries that are possible "
2670 "in the LDAP compare cache. Use 0 for no limit. "
2671 "Use -1 to disable the cache. (default: 1024)"),
2673 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2675 "Set the maximum time (in seconds) that an item is cached "
2676 "in the LDAP operation cache. Use 0 for no limit. "
2679 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2681 "Takes three args; the file and/or directory containing "
2682 "the trusted CA certificates (and global client certs "
2683 "for Netware) used to validate the LDAP server. Second "
2684 "arg is the cert type for the first arg, one of CA_DER, "
2685 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2686 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2687 "Third arg is an optional passphrase if applicable."),
2689 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2691 "Takes three args; the file and/or directory containing "
2692 "the client certificate, or certificate ID used to "
2693 "validate this LDAP client. Second arg is the cert type "
2694 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2695 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2696 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2697 "optional passphrase if applicable."),
2699 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2701 "Specify the type of security that should be applied to "
2702 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2704 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2706 "Set to 'ON' requires that the server certificate be verified"
2707 " before a secure LDAP connection can be establish. Default"
2710 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2712 "Specify the LDAP socket connection timeout in seconds "
2715 AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
2717 "Choose whether referrals are chased ['ON'|'OFF']. Default 'ON'"),
2719 AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
2721 "Limit the number of referral hops that LDAP can follow. "
2722 "(Integer value, default=" AP_LDAP_DEFAULT_HOPLIMIT_STR ")"),
2727 static void util_ldap_register_hooks(apr_pool_t *p)
2729 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2730 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2731 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2732 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2733 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2734 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2735 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2736 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2737 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2738 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2739 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2741 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2742 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2743 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2746 module AP_MODULE_DECLARE_DATA ldap_module = {
2747 STANDARD20_MODULE_STUFF,
2748 util_ldap_create_dir_config, /* create dir config */
2749 NULL, /* merge dir config */
2750 util_ldap_create_config, /* create server config */
2751 util_ldap_merge_config, /* merge server config */
2752 util_ldap_cmds, /* command table */
2753 util_ldap_register_hooks, /* set up request processing hooks */