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 ? "On" : "Off"));
354 apr_ldap_set_option(r->pool, ldc->ldap,
355 APR_LDAP_OPT_REFERRALS,
356 (void *)(ldc->ChaseReferrals ? LDAP_OPT_ON : LDAP_OPT_OFF),
358 if (result->rc != LDAP_SUCCESS) {
359 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
360 "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
361 (ldc->ChaseReferrals ? "On" : "Off"),
363 result->reason = "Unable to set LDAP_OPT_REFERRALS.";
364 uldap_connection_unbind(ldc);
368 if (ldc->ChaseReferrals) {
369 /* Referral hop limit - only if referrals are enabled */
370 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
371 "Setting referral hop limit to %d.",
372 ldc->ReferralHopLimit);
373 apr_ldap_set_option(r->pool, ldc->ldap,
374 APR_LDAP_OPT_REFHOPLIMIT,
375 (void *)&ldc->ReferralHopLimit,
377 if (result->rc != LDAP_SUCCESS) {
378 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
379 "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
380 ldc->ReferralHopLimit,
382 result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
383 uldap_connection_unbind(ldc);
388 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
389 #ifdef APR_LDAP_OPT_VERIFY_CERT
390 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
391 &(st->verify_svr_cert), &(result));
393 #if defined(LDAPSSL_VERIFY_SERVER)
394 if (st->verify_svr_cert) {
395 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
398 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
400 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
401 /* This is not a per-connection setting so just pass NULL for the
402 Ldap connection handle */
403 if (st->verify_svr_cert) {
404 int i = LDAP_OPT_X_TLS_DEMAND;
405 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
408 int i = LDAP_OPT_X_TLS_NEVER;
409 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
414 #ifdef LDAP_OPT_NETWORK_TIMEOUT
415 if (st->connectionTimeout > 0) {
416 timeOut.tv_sec = st->connectionTimeout;
419 if (st->connectionTimeout >= 0) {
420 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
421 (void *)&timeOut, &(result));
422 if (APR_SUCCESS != rc) {
423 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
424 "LDAP: Could not set the connection timeout");
433 * Connect to the LDAP server and binds. Does not connect if already
434 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
436 * Returns LDAP_SUCCESS on success; and an error code on failure
438 static int uldap_connection_open(request_rec *r,
439 util_ldap_connection_t *ldc)
444 /* sanity check for NULL */
449 /* If the connection is already bound, return
453 ldc->reason = "LDAP: connection open successful (already bound)";
457 /* create the ldap session handle
459 if (NULL == ldc->ldap)
461 rc = uldap_connection_init( r, ldc );
462 if (LDAP_SUCCESS != rc)
469 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
470 * returned. Break out of the loop on Success or any other error.
472 * NOTE: Looping is probably not a great idea. If the server isn't
473 * responding the chances it will respond after a few tries are poor.
474 * However, the original code looped and it only happens on
475 * the error condition.
477 for (failures=0; failures<10; failures++)
479 rc = ldap_simple_bind_s(ldc->ldap,
481 (char *)ldc->bindpw);
482 if (!AP_LDAP_IS_SERVER_DOWN(rc)) {
484 } else if (failures == 5) {
485 /* attempt to init the connection once again */
486 uldap_connection_unbind( ldc );
487 rc = uldap_connection_init( r, ldc );
488 if (LDAP_SUCCESS != rc)
495 /* free the handle if there was an error
497 if (LDAP_SUCCESS != rc)
499 uldap_connection_unbind(ldc);
500 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
504 ldc->reason = "LDAP: connection open successful";
512 * Compare client certificate arrays.
514 * Returns 1 on compare failure, 0 otherwise.
516 static int compare_client_certs(apr_array_header_t *srcs,
517 apr_array_header_t *dests)
520 struct apr_ldap_opt_tls_cert_t *src, *dest;
522 /* arrays both NULL? if so, then equal */
523 if (srcs == NULL && dests == NULL) {
527 /* arrays different length or either NULL? If so, then not equal */
528 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
532 /* run an actual comparison */
533 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
534 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
535 for (i = 0; i < srcs->nelts; i++) {
536 if (strcmp(src[i].path, dest[i].path) ||
537 strcmp(src[i].password, dest[i].password) ||
538 src[i].type != dest[i].type) {
543 /* if we got here, the cert arrays were identical */
550 * Find an existing ldap connection struct that matches the
551 * provided ldap connection parameters.
553 * If not found in the cache, a new ldc structure will be allocated
554 * from st->pool and returned to the caller. If found in the cache,
555 * a pointer to the existing ldc structure will be returned.
557 static util_ldap_connection_t *
558 uldap_connection_find(request_rec *r,
559 const char *host, int port,
560 const char *binddn, const char *bindpw,
561 deref_options deref, int secure)
563 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
564 int secureflag = secure;
566 util_ldap_state_t *st =
567 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
569 util_ldap_config_t *dc =
570 (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
573 /* mutex lock this function */
574 apr_thread_mutex_lock(st->mutex);
577 if (secure < APR_LDAP_NONE) {
578 secureflag = st->secure;
581 /* Search for an exact connection match in the list that is not
584 for (l=st->connections,p=NULL; l; l=l->next) {
586 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
588 if ( (l->port == port) && (strcmp(l->host, host) == 0)
589 && ((!l->binddn && !binddn) || (l->binddn && binddn
590 && !strcmp(l->binddn, binddn)))
591 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
592 && !strcmp(l->bindpw, bindpw)))
593 && (l->deref == deref) && (l->secure == secureflag)
594 && !compare_client_certs(st->client_certs, l->client_certs))
599 /* If this connection didn't match the criteria, then we
600 * need to unlock the mutex so it is available to be reused.
602 apr_thread_mutex_unlock(l->lock);
608 /* If nothing found, search again, but we don't care about the
609 * binddn and bindpw this time.
612 for (l=st->connections,p=NULL; l; l=l->next) {
614 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
617 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
618 (l->deref == deref) && (l->secure == secureflag) &&
619 !compare_client_certs(st->client_certs, l->client_certs))
621 /* the bind credentials have changed */
623 util_ldap_strdup((char**)&(l->binddn), binddn);
624 util_ldap_strdup((char**)&(l->bindpw), bindpw);
628 /* If this connection didn't match the criteria, then we
629 * need to unlock the mutex so it is available to be reused.
631 apr_thread_mutex_unlock(l->lock);
638 /* artificially disable cache */
641 /* If no connection was found after the second search, we
646 if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
647 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
648 "util_ldap: Failed to create memory pool");
650 apr_thread_mutex_unlock(st->mutex);
656 * Add the new connection entry to the linked list. Note that we
657 * don't actually establish an LDAP connection yet; that happens
658 * the first time authentication is requested.
661 /* create the details of this connection in the new pool */
662 l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
667 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
668 apr_thread_mutex_lock(l->lock);
671 l->host = apr_pstrdup(l->pool, host);
674 util_ldap_strdup((char**)&(l->binddn), binddn);
675 util_ldap_strdup((char**)&(l->bindpw), bindpw);
676 l->ChaseReferrals = dc->ChaseReferrals;
677 l->ReferralHopLimit = dc->ReferralHopLimit;
679 /* The security mode after parsing the URL will always be either
680 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
681 * If the security setting is NONE, override it to the security
682 * setting optionally supplied by the admin using LDAPTrustedMode
684 l->secure = secureflag;
686 /* save away a copy of the client cert list that is presently valid */
687 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
700 apr_thread_mutex_unlock(st->mutex);
705 /* ------------------------------------------------------------------ */
708 * Compares two DNs to see if they're equal. The only way to do this correctly
709 * is to search for the dn and then do ldap_get_dn() on the result. This should
710 * match the initial dn, since it would have been also retrieved with
711 * ldap_get_dn(). This is expensive, so if the configuration value
712 * compare_dn_on_server is false, just does an ordinary strcmp.
714 * The lock for the ldap cache should already be acquired.
716 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
717 const char *url, const char *dn,
718 const char *reqdn, int compare_dn_on_server)
721 util_url_node_t *curl;
722 util_url_node_t curnode;
723 util_dn_compare_node_t *node;
724 util_dn_compare_node_t newnode;
726 LDAPMessage *res, *entry;
729 util_ldap_state_t *st = (util_ldap_state_t *)
730 ap_get_module_config(r->server->module_config,
733 /* get cache entry (or create one) */
737 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
739 curl = util_ald_create_caches(st, url);
743 /* a simple compare? */
744 if (!compare_dn_on_server) {
745 /* unlock this read lock */
746 if (strcmp(dn, reqdn)) {
747 ldc->reason = "DN Comparison FALSE (direct strcmp())";
748 return LDAP_COMPARE_FALSE;
751 ldc->reason = "DN Comparison TRUE (direct strcmp())";
752 return LDAP_COMPARE_TRUE;
757 /* no - it's a server side compare */
760 /* is it in the compare cache? */
761 newnode.reqdn = (char *)reqdn;
762 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
764 /* If it's in the cache, it's good */
765 /* unlock this read lock */
767 ldc->reason = "DN Comparison TRUE (cached)";
768 return LDAP_COMPARE_TRUE;
771 /* unlock this read lock */
776 if (failures++ > 10) {
777 /* too many failures */
781 /* make a server connection */
782 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
783 /* connect to server failed */
787 /* search for reqdn */
788 result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
789 "(objectclass=*)", NULL, 1,
790 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
791 if (AP_LDAP_IS_SERVER_DOWN(result))
793 ldc->reason = "DN Comparison ldap_search_ext_s() "
794 "failed with server down";
795 uldap_connection_unbind(ldc);
798 if (result != LDAP_SUCCESS) {
799 /* search for reqdn failed - no match */
800 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
804 entry = ldap_first_entry(ldc->ldap, res);
805 searchdn = ldap_get_dn(ldc->ldap, entry);
808 if (strcmp(dn, searchdn) != 0) {
809 /* compare unsuccessful */
810 ldc->reason = "DN Comparison FALSE (checked on server)";
811 result = LDAP_COMPARE_FALSE;
815 /* compare successful - add to the compare cache */
817 newnode.reqdn = (char *)reqdn;
818 newnode.dn = (char *)dn;
820 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
822 || (strcmp(reqdn, node->reqdn) != 0)
823 || (strcmp(dn, node->dn) != 0))
825 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
829 ldc->reason = "DN Comparison TRUE (checked on server)";
830 result = LDAP_COMPARE_TRUE;
832 ldap_memfree(searchdn);
838 * Does an generic ldap_compare operation. It accepts a cache that it will use
839 * to lookup the compare in the cache. We cache two kinds of compares
840 * (require group compares) and (require user compares). Each compare has a
841 * different cache node: require group includes the DN; require user does not
842 * because the require user cache is owned by the
845 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
846 const char *url, const char *dn,
847 const char *attrib, const char *value)
850 util_url_node_t *curl;
851 util_url_node_t curnode;
852 util_compare_node_t *compare_nodep;
853 util_compare_node_t the_compare_node;
854 apr_time_t curtime = 0; /* silence gcc -Wall */
857 util_ldap_state_t *st = (util_ldap_state_t *)
858 ap_get_module_config(r->server->module_config,
861 /* get cache entry (or create one) */
864 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
866 curl = util_ald_create_caches(st, url);
871 /* make a comparison to the cache */
873 curtime = apr_time_now();
875 the_compare_node.dn = (char *)dn;
876 the_compare_node.attrib = (char *)attrib;
877 the_compare_node.value = (char *)value;
878 the_compare_node.result = 0;
879 the_compare_node.sgl_processed = 0;
880 the_compare_node.subgroupList = NULL;
882 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
885 if (compare_nodep != NULL) {
887 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
888 /* ...but it is too old */
889 util_ald_cache_remove(curl->compare_cache, compare_nodep);
892 /* ...and it is good */
893 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
894 ldc->reason = "Comparison true (cached)";
896 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
897 ldc->reason = "Comparison false (cached)";
899 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
900 ldc->reason = "Comparison no such attribute (cached)";
903 ldc->reason = "Comparison undefined (cached)";
906 /* record the result code to return with the reason... */
907 result = compare_nodep->result;
908 /* and unlock this read lock */
913 /* unlock this read lock */
918 if (failures++ > 10) {
919 /* too many failures */
923 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
928 result = ldap_compare_s(ldc->ldap,
932 if (AP_LDAP_IS_SERVER_DOWN(result)) {
933 /* connection failed - try again */
934 ldc->reason = "ldap_compare_s() failed with server down";
935 uldap_connection_unbind(ldc);
939 ldc->reason = "Comparison complete";
940 if ((LDAP_COMPARE_TRUE == result) ||
941 (LDAP_COMPARE_FALSE == result) ||
942 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
944 /* compare completed; caching result */
946 the_compare_node.lastcompare = curtime;
947 the_compare_node.result = result;
948 the_compare_node.sgl_processed = 0;
949 the_compare_node.subgroupList = NULL;
951 /* If the node doesn't exist then insert it, otherwise just update
952 * it with the last results
954 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
956 if ( (compare_nodep == NULL)
957 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
958 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
959 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
963 junk = util_ald_cache_insert(curl->compare_cache,
966 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
967 "[%" APR_PID_T_FMT "] cache_compare: Cache"
968 " insertion failure.", getpid());
972 compare_nodep->lastcompare = curtime;
973 compare_nodep->result = result;
977 if (LDAP_COMPARE_TRUE == result) {
978 ldc->reason = "Comparison true (adding to cache)";
979 return LDAP_COMPARE_TRUE;
981 else if (LDAP_COMPARE_FALSE == result) {
982 ldc->reason = "Comparison false (adding to cache)";
983 return LDAP_COMPARE_FALSE;
986 ldc->reason = "Comparison no such attribute (adding to cache)";
987 return LDAP_NO_SUCH_ATTRIBUTE;
994 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
995 util_ldap_connection_t *ldc,
998 char **subgroupAttrs,
999 apr_array_header_t *subgroupclasses)
1002 int result = LDAP_COMPARE_FALSE;
1003 util_compare_subgroup_t *res = NULL;
1004 LDAPMessage *sga_res, *entry;
1005 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
1006 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
1008 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1010 if (!subgroupAttrs) {
1016 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
1018 if (failures++ > 10) {
1019 /* too many failures */
1023 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1024 /* connect failed */
1028 /* try to do the search */
1029 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
1030 (char *)"cn=*", subgroupAttrs, 0,
1031 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
1032 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1033 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
1035 uldap_connection_unbind(ldc);
1039 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1040 if (result != LDAP_SUCCESS) {
1041 ldc->reason = "ldap_search_ext_s() for subgroups failed";
1045 entry = ldap_first_entry(ldc->ldap, sga_res);
1048 * Get values for the provided sub-group attributes.
1050 if (subgroupAttrs) {
1051 int indx = 0, tmp_sgcIndex;
1053 while (subgroupAttrs[indx]) {
1057 /* Get *all* matching "member" values from this group. */
1058 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1063 * Now we are going to pare the subgroup members of this group
1064 * to *just* the subgroups, add them to the compare_nodep, and
1065 * then proceed to check the new level of subgroups.
1067 while (values[val_index]) {
1068 /* Check if this entry really is a group. */
1070 result = LDAP_COMPARE_FALSE;
1071 while ((tmp_sgcIndex < subgroupclasses->nelts)
1072 && (result != LDAP_COMPARE_TRUE)) {
1073 result = uldap_cache_compare(r, ldc, url,
1076 sgc_ents[tmp_sgcIndex].name
1079 if (result != LDAP_COMPARE_TRUE) {
1083 /* It's a group, so add it to the array. */
1084 if (result == LDAP_COMPARE_TRUE) {
1085 char **newgrp = (char **) apr_array_push(subgroups);
1086 *newgrp = apr_pstrdup(r->pool, values[val_index]);
1090 ldap_value_free(values);
1096 ldap_msgfree(sga_res);
1098 if (subgroups->nelts > 0) {
1099 /* We need to fill in tmp_local_subgroups using the data from LDAP */
1102 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1103 res->subgroupDNs = apr_pcalloc(r->pool,
1104 sizeof(char *) * (subgroups->nelts));
1105 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1106 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1116 * Does a recursive lookup operation to try to find a user within (cached)
1117 * nested groups. It accepts a cache that it will use to lookup previous
1118 * compare attempts. We cache two kinds of compares (require group compares)
1119 * and (require user compares). Each compare has a different cache node:
1120 * require group includes the DN; require user does not because the require
1121 * user cache is owned by the
1123 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1126 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1127 * generic, user-agnostic, cached group entry. This will create a new generic
1128 * cache entry if there
1129 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1131 * 2. Lock The cache and get the generic cache entry.
1132 * 3. Check if there is already a subgrouplist in this generic group's cache
1134 * A. If there is, go to step 4.
1135 * B. If there isn't:
1136 * i) Use ldap_search to get the full list
1137 * of subgroup "members" (which may include non-group "members").
1138 * ii) Use uldap_cache_compare to strip the list down to just groups.
1139 * iii) Lock and add this stripped down list to the cache of the generic
1141 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1143 * subgroup to see if the subgroup contains the user and to get the subgroups
1145 * cache (with user-afinity, if they aren't already there).
1146 * A. If the user is in the subgroup, then we'll be returning
1147 * LDAP_COMPARE_TRUE.
1148 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1149 * uldap_cache_compare) then recursively call this function to get the
1150 * sub-subgroups added...
1151 * 5. Cleanup local allocations.
1152 * 6. Return the final result.
1155 static int uldap_cache_check_subgroups(request_rec *r,
1156 util_ldap_connection_t *ldc,
1157 const char *url, const char *dn,
1158 const char *attrib, const char *value,
1159 char **subgroupAttrs,
1160 apr_array_header_t *subgroupclasses,
1161 int cur_subgroup_depth,
1162 int max_subgroup_depth)
1164 int result = LDAP_COMPARE_FALSE;
1165 util_url_node_t *curl;
1166 util_url_node_t curnode;
1167 util_compare_node_t *compare_nodep;
1168 util_compare_node_t the_compare_node;
1169 util_compare_subgroup_t *tmp_local_sgl = NULL;
1170 int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
1171 struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
1172 (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1173 util_ldap_state_t *st = (util_ldap_state_t *)
1174 ap_get_module_config(r->server->module_config,
1178 * Stop looking at deeper levels of nested groups if we have reached the
1179 * max. Since we already checked the top-level group in uldap_cache_compare,
1180 * we don't need to check it again here - so if max_subgroup_depth is set
1181 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1182 * We'll be calling uldap_cache_compare from here to check if the user is
1183 * in the next level before we recurse into that next level looking for
1186 if (cur_subgroup_depth >= max_subgroup_depth) {
1187 return LDAP_COMPARE_FALSE;
1191 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1194 while ((base_sgcIndex < subgroupclasses->nelts)
1195 && (result != LDAP_COMPARE_TRUE)) {
1196 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1197 sgc_ents[base_sgcIndex].name);
1198 if (result != LDAP_COMPARE_TRUE) {
1203 if (result != LDAP_COMPARE_TRUE) {
1204 ldc->reason = "DN failed group verification.";
1209 * 2. Find previously created cache entry and check if there is already a
1214 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1215 LDAP_CACHE_UNLOCK();
1217 if (curl && curl->compare_cache) {
1218 /* make a comparison to the cache */
1221 the_compare_node.dn = (char *)dn;
1222 the_compare_node.attrib = (char *)"objectClass";
1223 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1224 the_compare_node.result = 0;
1225 the_compare_node.sgl_processed = 0;
1226 the_compare_node.subgroupList = NULL;
1228 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1231 if (compare_nodep != NULL) {
1233 * Found the generic group entry... but the user isn't in this
1234 * group or we wouldn't be here.
1236 if (compare_nodep->sgl_processed) {
1237 if (compare_nodep->subgroupList) {
1238 /* Make a local copy of the subgroup list */
1240 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1241 "[%" APR_PID_T_FMT "] util_ldap:"
1242 " Making local copy of SGL for "
1243 "group (%s)(objectClass=%s) ",
1245 (char *)sgc_ents[base_sgcIndex].name);
1246 tmp_local_sgl = apr_pcalloc(r->pool,
1247 sizeof(util_compare_subgroup_t));
1248 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1249 tmp_local_sgl->subgroupDNs =
1250 apr_pcalloc(r->pool,
1251 sizeof(char *) * compare_nodep->subgroupList->len);
1252 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1253 tmp_local_sgl->subgroupDNs[i] =
1254 apr_pstrdup(r->pool,
1255 compare_nodep->subgroupList->subgroupDNs[i]);
1259 sgl_cached_empty = 1;
1263 LDAP_CACHE_UNLOCK();
1266 if (!tmp_local_sgl && !sgl_cached_empty) {
1267 /* No Cached SGL, retrieve from LDAP */
1268 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1269 "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s,"
1270 " retrieving from LDAP" , getpid(), dn);
1271 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1273 if (!tmp_local_sgl) {
1274 /* No SGL aailable via LDAP either */
1275 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1276 " util_ldap: no subgroups for %s" , getpid(), dn);
1279 if (curl && curl->compare_cache) {
1281 * Find the generic group cache entry and add the sgl we just retrieved.
1285 the_compare_node.dn = (char *)dn;
1286 the_compare_node.attrib = (char *)"objectClass";
1287 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1288 the_compare_node.result = 0;
1289 the_compare_node.sgl_processed = 0;
1290 the_compare_node.subgroupList = NULL;
1292 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1295 if (compare_nodep == NULL) {
1297 * The group entry we want to attach our SGL to doesn't exist.
1298 * We only got here if we verified this DN was actually a group
1299 * based on the objectClass, but we can't call the compare function
1300 * while we already hold the cache lock -- only the insert.
1302 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1303 "[%" APR_PID_T_FMT "] util_ldap: Cache entry "
1304 "for %s doesn't exist",
1306 the_compare_node.result = LDAP_COMPARE_TRUE;
1307 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
1308 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1310 if (compare_nodep == NULL) {
1311 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1312 "[%" APR_PID_T_FMT "] util_ldap: Couldn't "
1313 "retrieve group entry for %s from cache",
1319 * We have a valid cache entry and a locally generated SGL.
1320 * Attach the SGL to the cache entry
1322 if (compare_nodep && !compare_nodep->sgl_processed) {
1323 if (!tmp_local_sgl) {
1324 /* We looked up an SGL for a group and found it to be empty */
1325 if (compare_nodep->subgroupList == NULL) {
1326 compare_nodep->sgl_processed = 1;
1330 util_compare_subgroup_t *sgl_copy =
1331 util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1332 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1333 "Copying local SGL of len %d for group %s into cache",
1334 tmp_local_sgl->len, dn);
1336 if (compare_nodep->subgroupList) {
1337 util_ald_sgl_free(curl->compare_cache,
1338 &(compare_nodep->subgroupList));
1340 compare_nodep->subgroupList = sgl_copy;
1341 compare_nodep->sgl_processed = 1;
1344 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1345 "Copy of SGL failed to obtain shared memory, "
1346 "couldn't update cache");
1350 LDAP_CACHE_UNLOCK();
1355 * tmp_local_sgl has either been created, or copied out of the cache
1356 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1359 result = LDAP_COMPARE_FALSE;
1360 if (!tmp_local_sgl) {
1364 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1365 const char *group = NULL;
1366 group = tmp_local_sgl->subgroupDNs[sgindex];
1368 * 4. Now loop through the subgroupList and call uldap_cache_compare
1369 * to check for the user.
1371 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1372 if (result == LDAP_COMPARE_TRUE) {
1374 * 4.A. We found the user in the subgroup. Return
1375 * LDAP_COMPARE_TRUE.
1377 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1378 " util_ldap: Found user %s in a subgroup (%s) at"
1379 " level %d of %d.", getpid(), r->user, group,
1380 cur_subgroup_depth+1, max_subgroup_depth);
1384 * 4.B. We didn't find the user in this subgroup, so recurse into
1385 * it and keep looking.
1387 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1388 " util_ldap: user %s not found in subgroup (%s) at"
1389 " level %d of %d.", getpid(), r->user, group,
1390 cur_subgroup_depth+1, max_subgroup_depth);
1391 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1392 value, subgroupAttrs,
1394 cur_subgroup_depth+1,
1395 max_subgroup_depth);
1404 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1405 const char *url, const char *basedn,
1406 int scope, char **attrs, const char *filter,
1407 const char *bindpw, const char **binddn,
1408 const char ***retvals)
1410 const char **vals = NULL;
1413 LDAPMessage *res, *entry;
1417 util_url_node_t *curl; /* Cached URL node */
1418 util_url_node_t curnode;
1419 util_search_node_t *search_nodep; /* Cached search node */
1420 util_search_node_t the_search_node;
1423 util_ldap_state_t *st =
1424 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1427 /* Get the cache node for this url */
1430 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1433 curl = util_ald_create_caches(st, url);
1435 LDAP_CACHE_UNLOCK();
1439 the_search_node.username = filter;
1440 search_nodep = util_ald_cache_fetch(curl->search_cache,
1442 if (search_nodep != NULL) {
1444 /* found entry in search cache... */
1445 curtime = apr_time_now();
1448 * Remove this item from the cache if its expired. If the sent
1449 * password doesn't match the storepassword, the entry will
1450 * be removed and readded later if the credentials pass
1453 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1454 /* ...but entry is too old */
1455 util_ald_cache_remove(curl->search_cache, search_nodep);
1457 else if ( (search_nodep->bindpw)
1458 && (search_nodep->bindpw[0] != '\0')
1459 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1461 /* ...and entry is valid */
1462 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1466 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1467 while (search_nodep->vals[i]) {
1468 (*retvals)[i] = apr_pstrdup(r->pool,
1469 search_nodep->vals[i]);
1473 LDAP_CACHE_UNLOCK();
1474 ldc->reason = "Authentication successful (cached)";
1475 return LDAP_SUCCESS;
1478 /* unlock this read lock */
1479 LDAP_CACHE_UNLOCK();
1483 * At this point, there is no valid cached search, so lets do the search.
1487 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1490 if (failures++ > 10) {
1493 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1497 /* try do the search */
1498 result = ldap_search_ext_s(ldc->ldap,
1499 (char *)basedn, scope,
1500 (char *)filter, attrs, 0,
1501 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1502 if (AP_LDAP_IS_SERVER_DOWN(result))
1504 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1505 uldap_connection_unbind(ldc);
1509 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1510 if (result != LDAP_SUCCESS) {
1511 ldc->reason = "ldap_search_ext_s() for user failed";
1516 * We should have found exactly one entry; to find a different
1517 * number is an error.
1519 count = ldap_count_entries(ldc->ldap, res);
1523 ldc->reason = "User not found";
1525 ldc->reason = "User is not unique (search found two "
1528 return LDAP_NO_SUCH_OBJECT;
1531 entry = ldap_first_entry(ldc->ldap, res);
1533 /* Grab the dn, copy it into the pool, and free it again */
1534 dn = ldap_get_dn(ldc->ldap, entry);
1535 *binddn = apr_pstrdup(r->pool, dn);
1539 * A bind to the server with an empty password always succeeds, so
1540 * we check to ensure that the password is not empty. This implies
1541 * that users who actually do have empty passwords will never be
1542 * able to authenticate with this module. I don't see this as a big
1545 if (!bindpw || strlen(bindpw) <= 0) {
1547 ldc->reason = "Empty password not allowed";
1548 return LDAP_INVALID_CREDENTIALS;
1552 * Attempt to bind with the retrieved dn and the password. If the bind
1553 * fails, it means that the password is wrong (the dn obviously
1554 * exists, since we just retrieved it)
1556 result = ldap_simple_bind_s(ldc->ldap,
1559 if (AP_LDAP_IS_SERVER_DOWN(result)) {
1560 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1561 "failed with server down";
1563 uldap_connection_unbind(ldc);
1567 /* failure? if so - return */
1568 if (result != LDAP_SUCCESS) {
1569 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1571 uldap_connection_unbind(ldc);
1576 * We have just bound the connection to a different user and password
1577 * combination, which might be reused unintentionally next time this
1578 * connection is used from the connection pool. To ensure no confusion,
1579 * we mark the connection as unbound.
1585 * Get values for the provided attributes.
1591 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1598 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1599 while (values && values[j]) {
1600 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1601 : apr_pstrdup(r->pool, values[j]);
1604 ldap_value_free(values);
1612 * Add the new username to the search cache.
1616 the_search_node.username = filter;
1617 the_search_node.dn = *binddn;
1618 the_search_node.bindpw = bindpw;
1619 the_search_node.lastbind = apr_time_now();
1620 the_search_node.vals = vals;
1621 the_search_node.numvals = numvals;
1623 /* Search again to make sure that another thread didn't ready insert
1624 * this node into the cache before we got here. If it does exist then
1625 * update the lastbind
1627 search_nodep = util_ald_cache_fetch(curl->search_cache,
1629 if ((search_nodep == NULL) ||
1630 (strcmp(*binddn, search_nodep->dn) != 0)) {
1632 /* Nothing in cache, insert new entry */
1633 util_ald_cache_insert(curl->search_cache, &the_search_node);
1635 else if ((!search_nodep->bindpw) ||
1636 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1638 /* Entry in cache is invalid, remove it and insert new one */
1639 util_ald_cache_remove(curl->search_cache, search_nodep);
1640 util_ald_cache_insert(curl->search_cache, &the_search_node);
1643 /* Cache entry is valid, update lastbind */
1644 search_nodep->lastbind = the_search_node.lastbind;
1646 LDAP_CACHE_UNLOCK();
1650 ldc->reason = "Authentication successful";
1651 return LDAP_SUCCESS;
1655 * This function will return the DN of the entry matching userid.
1656 * It is used to get the DN in case some other module than mod_auth_ldap
1657 * has authenticated the user.
1658 * The function is basically a copy of uldap_cache_checkuserid
1659 * with password checking removed.
1661 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1662 const char *url, const char *basedn,
1663 int scope, char **attrs, const char *filter,
1664 const char **binddn, const char ***retvals)
1666 const char **vals = NULL;
1669 LDAPMessage *res, *entry;
1673 util_url_node_t *curl; /* Cached URL node */
1674 util_url_node_t curnode;
1675 util_search_node_t *search_nodep; /* Cached search node */
1676 util_search_node_t the_search_node;
1679 util_ldap_state_t *st =
1680 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1683 /* Get the cache node for this url */
1686 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1689 curl = util_ald_create_caches(st, url);
1691 LDAP_CACHE_UNLOCK();
1695 the_search_node.username = filter;
1696 search_nodep = util_ald_cache_fetch(curl->search_cache,
1698 if (search_nodep != NULL) {
1700 /* found entry in search cache... */
1701 curtime = apr_time_now();
1704 * Remove this item from the cache if its expired.
1706 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1707 /* ...but entry is too old */
1708 util_ald_cache_remove(curl->search_cache, search_nodep);
1711 /* ...and entry is valid */
1712 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1716 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1717 while (search_nodep->vals[i]) {
1718 (*retvals)[i] = apr_pstrdup(r->pool,
1719 search_nodep->vals[i]);
1723 LDAP_CACHE_UNLOCK();
1724 ldc->reason = "Search successful (cached)";
1725 return LDAP_SUCCESS;
1728 /* unlock this read lock */
1729 LDAP_CACHE_UNLOCK();
1733 * At this point, there is no valid cached search, so lets do the search.
1737 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1740 if (failures++ > 10) {
1743 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1747 /* try do the search */
1748 result = ldap_search_ext_s(ldc->ldap,
1749 (char *)basedn, scope,
1750 (char *)filter, attrs, 0,
1751 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1752 if (AP_LDAP_IS_SERVER_DOWN(result))
1754 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1755 uldap_connection_unbind(ldc);
1759 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1760 if (result != LDAP_SUCCESS) {
1761 ldc->reason = "ldap_search_ext_s() for user failed";
1766 * We should have found exactly one entry; to find a different
1767 * number is an error.
1769 count = ldap_count_entries(ldc->ldap, res);
1773 ldc->reason = "User not found";
1775 ldc->reason = "User is not unique (search found two "
1778 return LDAP_NO_SUCH_OBJECT;
1781 entry = ldap_first_entry(ldc->ldap, res);
1783 /* Grab the dn, copy it into the pool, and free it again */
1784 dn = ldap_get_dn(ldc->ldap, entry);
1785 *binddn = apr_pstrdup(r->pool, dn);
1789 * Get values for the provided attributes.
1795 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1802 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1803 while (values && values[j]) {
1804 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1805 : apr_pstrdup(r->pool, values[j]);
1808 ldap_value_free(values);
1816 * Add the new username to the search cache.
1820 the_search_node.username = filter;
1821 the_search_node.dn = *binddn;
1822 the_search_node.bindpw = NULL;
1823 the_search_node.lastbind = apr_time_now();
1824 the_search_node.vals = vals;
1825 the_search_node.numvals = numvals;
1827 /* Search again to make sure that another thread didn't ready insert
1828 * this node into the cache before we got here. If it does exist then
1829 * update the lastbind
1831 search_nodep = util_ald_cache_fetch(curl->search_cache,
1833 if ((search_nodep == NULL) ||
1834 (strcmp(*binddn, search_nodep->dn) != 0)) {
1836 /* Nothing in cache, insert new entry */
1837 util_ald_cache_insert(curl->search_cache, &the_search_node);
1840 * Don't update lastbind on entries with bindpw because
1841 * we haven't verified that password. It's OK to update
1842 * the entry if there is no password in it.
1844 else if (!search_nodep->bindpw) {
1845 /* Cache entry is valid, update lastbind */
1846 search_nodep->lastbind = the_search_node.lastbind;
1848 LDAP_CACHE_UNLOCK();
1853 ldc->reason = "Search successful";
1854 return LDAP_SUCCESS;
1858 * Reports if ssl support is enabled
1860 * 1 = enabled, 0 = not enabled
1862 static int uldap_ssl_supported(request_rec *r)
1864 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1865 r->server->module_config, &ldap_module);
1867 return(st->ssl_supported);
1871 /* ---------------------------------------- */
1872 /* config directives */
1875 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1878 util_ldap_state_t *st =
1879 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1881 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1887 st->cache_bytes = atol(bytes);
1889 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1890 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1891 " cache size to %" APR_SIZE_T_FMT " bytes.",
1892 getpid(), st->cache_bytes);
1897 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1900 util_ldap_state_t *st =
1901 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1903 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1910 st->cache_file = ap_server_root_relative(st->pool, file);
1913 st->cache_file = NULL;
1916 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1917 "LDAP cache: Setting shared memory cache file to %s bytes.",
1923 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1926 util_ldap_state_t *st =
1927 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1929 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1935 st->search_cache_ttl = atol(ttl) * 1000000;
1937 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1938 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1939 " microseconds.", getpid(), st->search_cache_ttl);
1944 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1947 util_ldap_state_t *st =
1948 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1950 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1956 st->search_cache_size = atol(size);
1957 if (st->search_cache_size < 0) {
1958 st->search_cache_size = 0;
1961 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1962 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1963 " to %ld entries.", getpid(), st->search_cache_size);
1968 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1971 util_ldap_state_t *st =
1972 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1974 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1980 st->compare_cache_ttl = atol(ttl) * 1000000;
1982 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1983 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1984 " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1989 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1992 util_ldap_state_t *st =
1993 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1995 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2001 st->compare_cache_size = atol(size);
2002 if (st->compare_cache_size < 0) {
2003 st->compare_cache_size = 0;
2006 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2007 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
2008 " to %ld entries.", getpid(), st->compare_cache_size);
2015 * Parse the certificate type.
2017 * The type can be one of the following:
2018 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
2019 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
2021 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
2023 static int util_ldap_parse_cert_type(const char *type)
2025 /* Authority file in binary DER format */
2026 if (0 == strcasecmp("CA_DER", type)) {
2027 return APR_LDAP_CA_TYPE_DER;
2030 /* Authority file in Base64 format */
2031 else if (0 == strcasecmp("CA_BASE64", type)) {
2032 return APR_LDAP_CA_TYPE_BASE64;
2035 /* Netscape certificate database file/directory */
2036 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2037 return APR_LDAP_CA_TYPE_CERT7_DB;
2040 /* Netscape secmod file/directory */
2041 else if (0 == strcasecmp("CA_SECMOD", type)) {
2042 return APR_LDAP_CA_TYPE_SECMOD;
2045 /* Client cert file in DER format */
2046 else if (0 == strcasecmp("CERT_DER", type)) {
2047 return APR_LDAP_CERT_TYPE_DER;
2050 /* Client cert file in Base64 format */
2051 else if (0 == strcasecmp("CERT_BASE64", type)) {
2052 return APR_LDAP_CERT_TYPE_BASE64;
2055 /* Client cert file in PKCS#12 format */
2056 else if (0 == strcasecmp("CERT_PFX", type)) {
2057 return APR_LDAP_CERT_TYPE_PFX;
2060 /* Netscape client cert database file/directory */
2061 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2062 return APR_LDAP_CERT_TYPE_KEY3_DB;
2065 /* Netscape client cert nickname */
2066 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2067 return APR_LDAP_CERT_TYPE_NICKNAME;
2070 /* Client cert key file in DER format */
2071 else if (0 == strcasecmp("KEY_DER", type)) {
2072 return APR_LDAP_KEY_TYPE_DER;
2075 /* Client cert key file in Base64 format */
2076 else if (0 == strcasecmp("KEY_BASE64", type)) {
2077 return APR_LDAP_KEY_TYPE_BASE64;
2080 /* Client cert key file in PKCS#12 format */
2081 else if (0 == strcasecmp("KEY_PFX", type)) {
2082 return APR_LDAP_KEY_TYPE_PFX;
2086 return APR_LDAP_CA_TYPE_UNKNOWN;
2093 * Set LDAPTrustedGlobalCert.
2095 * This directive takes either two or three arguments:
2096 * - certificate type
2097 * - certificate file / directory / nickname
2098 * - certificate password (optional)
2100 * This directive may only be used globally.
2102 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2106 const char *password)
2108 util_ldap_state_t *st =
2109 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2111 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2115 apr_ldap_opt_tls_cert_t *cert;
2121 /* handle the certificate type */
2123 cert_type = util_ldap_parse_cert_type(type);
2124 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2125 return apr_psprintf(cmd->pool, "The certificate type %s is "
2126 "not recognised. It should be one "
2127 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2128 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2129 "CERT_KEY3_DB, CERT_NICKNAME, "
2130 "KEY_DER, KEY_BASE64", type);
2134 return "Certificate type was not specified.";
2137 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2138 "LDAP: SSL trusted global cert - %s (type %s)",
2141 /* add the certificate to the global array */
2142 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2143 cert->type = cert_type;
2145 cert->password = password;
2147 /* if file is a file or path, fix the path */
2148 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2149 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2151 cert->path = ap_server_root_relative(cmd->pool, file);
2153 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2156 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2157 "LDAP: Could not open SSL trusted certificate "
2158 "authority file - %s",
2159 cert->path == NULL ? file : cert->path);
2160 return "Invalid global certificate file path";
2169 * Set LDAPTrustedClientCert.
2171 * This directive takes either two or three arguments:
2172 * - certificate type
2173 * - certificate file / directory / nickname
2174 * - certificate password (optional)
2176 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2180 const char *password)
2182 util_ldap_state_t *st =
2183 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2188 apr_ldap_opt_tls_cert_t *cert;
2190 /* handle the certificate type */
2192 cert_type = util_ldap_parse_cert_type(type);
2193 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2194 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2195 "not recognised. It should be one "
2196 "of CERT_DER, CERT_BASE64, "
2197 "CERT_NICKNAME, CERT_PFX,"
2198 "KEY_DER, KEY_BASE64, KEY_PFX",
2201 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2202 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2203 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2204 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2205 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2206 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2207 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2208 "only valid within a "
2209 "LDAPTrustedGlobalCert directive. "
2210 "Only CERT_DER, CERT_BASE64, "
2211 "CERT_NICKNAME, KEY_DER, and "
2212 "KEY_BASE64 may be used.", type);
2216 return "Certificate type was not specified.";
2219 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2220 "LDAP: SSL trusted client cert - %s (type %s)",
2223 /* add the certificate to the global array */
2224 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2225 cert->type = cert_type;
2227 cert->password = password;
2229 /* if file is a file or path, fix the path */
2230 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2231 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2233 cert->path = ap_server_root_relative(cmd->pool, file);
2235 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2238 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2239 "LDAP: Could not open SSL client certificate "
2241 cert->path == NULL ? file : cert->path);
2242 return "Invalid client certificate file path";
2252 * Set LDAPTrustedMode.
2254 * This directive sets what encryption mode to use on a connection:
2255 * - None (No encryption)
2256 * - SSL (SSL encryption)
2257 * - STARTTLS (TLS encryption)
2259 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2262 util_ldap_state_t *st =
2263 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2266 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2267 "LDAP: SSL trusted mode - %s",
2270 if (0 == strcasecmp("NONE", mode)) {
2271 st->secure = APR_LDAP_NONE;
2273 else if (0 == strcasecmp("SSL", mode)) {
2274 st->secure = APR_LDAP_SSL;
2276 else if ( (0 == strcasecmp("TLS", mode))
2277 || (0 == strcasecmp("STARTTLS", mode))) {
2278 st->secure = APR_LDAP_STARTTLS;
2281 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2282 "SSL, or TLS/STARTTLS";
2289 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2293 util_ldap_state_t *st =
2294 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2296 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2302 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2303 "LDAP: SSL verify server certificate - %s",
2304 mode?"TRUE":"FALSE");
2306 st->verify_svr_cert = mode;
2312 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2316 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2317 util_ldap_state_t *st =
2318 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2321 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2327 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2328 st->connectionTimeout = atol(ttl);
2330 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2331 "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2332 " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2334 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2335 "LDAP: Connection timout option not supported by the "
2336 "LDAP SDK in use." );
2343 static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
2347 util_ldap_config_t *dc = config;
2349 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2350 "LDAP: Setting refferal chasing %s",
2353 dc->ChaseReferrals = mode;
2358 static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
2360 const char *hop_limit)
2362 util_ldap_config_t *dc = config;
2364 dc->ReferralHopLimit = atol(hop_limit);
2366 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2367 "LDAP: Limit chased referrals to maximum of %d hops.",
2368 dc->ReferralHopLimit);
2373 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
2374 util_ldap_config_t *dc =
2375 (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
2377 dc->ChaseReferrals = 1; /* default is to turn referral chasing on. */
2378 dc->ReferralHopLimit = 5; /* default is to chase a max of 5 hops. */
2384 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2386 util_ldap_state_t *st =
2387 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2389 /* Create a per vhost pool for mod_ldap to use, serialized with
2390 * st->mutex (also one per vhost). both are replicated by fork(),
2391 * no shared memory managed by either.
2393 apr_pool_create(&st->pool, p);
2395 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2398 st->cache_bytes = 100000;
2399 st->search_cache_ttl = 600000000;
2400 st->search_cache_size = 1024;
2401 st->compare_cache_ttl = 600000000;
2402 st->compare_cache_size = 1024;
2403 st->connections = NULL;
2404 st->ssl_supported = 0;
2405 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2406 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2407 st->secure = APR_LDAP_NONE;
2409 st->connectionTimeout = 10;
2410 st->verify_svr_cert = 1;
2412 /* Initialize the rebind callback's cross reference list. */
2413 apr_ldap_rebind_init (p);
2418 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2421 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2422 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2423 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2425 st->pool = overrides->pool;
2427 st->mutex = overrides->mutex;
2430 /* The cache settings can not be modified in a
2431 virtual host since all server use the same
2432 shared memory cache. */
2433 st->cache_bytes = base->cache_bytes;
2434 st->search_cache_ttl = base->search_cache_ttl;
2435 st->search_cache_size = base->search_cache_size;
2436 st->compare_cache_ttl = base->compare_cache_ttl;
2437 st->compare_cache_size = base->compare_cache_size;
2438 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2440 st->connections = NULL;
2441 st->ssl_supported = 0;
2442 st->global_certs = apr_array_append(p, base->global_certs,
2443 overrides->global_certs);
2444 st->client_certs = apr_array_append(p, base->client_certs,
2445 overrides->client_certs);
2446 st->secure = (overrides->secure_set == 0) ? base->secure
2447 : overrides->secure;
2449 /* These LDAP connection settings can not be overwritten in
2450 a virtual host. Once set in the base server, they must
2451 remain the same. None of the LDAP SDKs seem to be able
2452 to handle setting the verify_svr_cert flag on a
2453 per-connection basis. The OpenLDAP client appears to be
2454 able to handle the connection timeout per-connection
2455 but the Novell SDK cannot. Allowing the timeout to
2456 be set by each vhost is of little value so rather than
2457 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2458 is being enforced on this setting as well. */
2459 st->connectionTimeout = base->connectionTimeout;
2460 st->verify_svr_cert = base->verify_svr_cert;
2465 static apr_status_t util_ldap_cleanup_module(void *data)
2468 server_rec *s = data;
2469 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2470 s->module_config, &ldap_module);
2472 if (st->ssl_supported) {
2473 apr_ldap_ssl_deinit();
2480 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2481 apr_pool_t *ptemp, server_rec *s)
2483 apr_status_t result;
2484 server_rec *s_vhost;
2485 util_ldap_state_t *st_vhost;
2487 util_ldap_state_t *st = (util_ldap_state_t *)
2488 ap_get_module_config(s->module_config,
2492 const char *userdata_key = "util_ldap_init";
2493 apr_ldap_err_t *result_err = NULL;
2496 /* util_ldap_post_config() will be called twice. Don't bother
2497 * going through all of the initialization on the first call
2498 * because it will just be thrown away.*/
2499 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2501 apr_pool_userdata_set((const void *)1, userdata_key,
2502 apr_pool_cleanup_null, s->process->pool);
2504 #if APR_HAS_SHARED_MEMORY
2505 /* If the cache file already exists then delete it. Otherwise we are
2506 * going to run into problems creating the shared memory. */
2507 if (st->cache_file) {
2508 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2510 apr_file_remove(lck_file, ptemp);
2516 #if APR_HAS_SHARED_MEMORY
2517 /* initializing cache if shared memory size is not zero and we already
2518 * don't have shm address
2520 if (!st->cache_shm && st->cache_bytes > 0) {
2522 result = util_ldap_cache_init(p, st);
2523 if (result != APR_SUCCESS) {
2524 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2525 "LDAP cache: could not create shared memory segment");
2530 #if APR_HAS_SHARED_MEMORY
2531 if (st->cache_file) {
2532 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2537 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2538 st->lock_file, APR_LOCK_DEFAULT,
2540 if (result != APR_SUCCESS) {
2544 #ifdef AP_NEED_SET_MUTEX_PERMS
2545 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2546 if (result != APR_SUCCESS) {
2547 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2548 "LDAP cache: failed to set mutex permissions");
2553 /* merge config in all vhost */
2556 st_vhost = (util_ldap_state_t *)
2557 ap_get_module_config(s_vhost->module_config,
2560 #if APR_HAS_SHARED_MEMORY
2561 st_vhost->cache_shm = st->cache_shm;
2562 st_vhost->cache_rmm = st->cache_rmm;
2563 st_vhost->cache_file = st->cache_file;
2564 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2565 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2566 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2567 s_vhost->server_hostname);
2569 st_vhost->lock_file = st->lock_file;
2570 s_vhost = s_vhost->next;
2572 #if APR_HAS_SHARED_MEMORY
2575 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2576 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2577 "shared memory cache");
2581 /* log the LDAP SDK used
2584 apr_ldap_err_t *result = NULL;
2585 apr_ldap_info(p, &(result));
2586 if (result != NULL) {
2587 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2591 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2592 util_ldap_cleanup_module);
2595 * Initialize SSL support, and log the result for the benefit of the admin.
2597 * If SSL is not supported it is not necessarily an error, as the
2598 * application may not want to use it.
2600 rc = apr_ldap_ssl_init(p,
2604 if (APR_SUCCESS == rc) {
2605 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2606 (void *)st->global_certs, &(result_err));
2609 if (APR_SUCCESS == rc) {
2610 st->ssl_supported = 1;
2611 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2612 "LDAP: SSL support available" );
2615 st->ssl_supported = 0;
2616 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2617 "LDAP: SSL support unavailable%s%s",
2618 result_err ? ": " : "",
2619 result_err ? result_err->reason : "");
2625 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2628 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2631 if (!st->util_ldap_cache_lock) return;
2633 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2635 if (sts != APR_SUCCESS) {
2636 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2637 "Failed to initialise global mutex %s in child process %"
2639 st->lock_file, getpid());
2643 static const command_rec util_ldap_cmds[] = {
2644 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2646 "Set the size of the shared memory cache (in bytes). Use "
2647 "0 to disable the shared memory cache. (default: 100000)"),
2649 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2651 "Set the file name for the shared memory cache."),
2653 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2655 "Set the maximum number of entries that are possible in the "
2656 "LDAP search cache. Use 0 for no limit. "
2657 "-1 disables the cache. (default: 1024)"),
2659 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2661 "Set the maximum time (in seconds) that an item can be "
2662 "cached in the LDAP search cache. Use 0 for no limit. "
2665 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2667 "Set the maximum number of entries that are possible "
2668 "in the LDAP compare cache. Use 0 for no limit. "
2669 "Use -1 to disable the cache. (default: 1024)"),
2671 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2673 "Set the maximum time (in seconds) that an item is cached "
2674 "in the LDAP operation cache. Use 0 for no limit. "
2677 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2679 "Takes three args; the file and/or directory containing "
2680 "the trusted CA certificates (and global client certs "
2681 "for Netware) used to validate the LDAP server. Second "
2682 "arg is the cert type for the first arg, one of CA_DER, "
2683 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2684 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2685 "Third arg is an optional passphrase if applicable."),
2687 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2689 "Takes three args; the file and/or directory containing "
2690 "the client certificate, or certificate ID used to "
2691 "validate this LDAP client. Second arg is the cert type "
2692 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2693 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2694 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2695 "optional passphrase if applicable."),
2697 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2699 "Specify the type of security that should be applied to "
2700 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2702 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2704 "Set to 'ON' requires that the server certificate be verified"
2705 " before a secure LDAP connection can be establish. Default"
2708 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2710 "Specify the LDAP socket connection timeout in seconds "
2713 AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
2715 "Choose whether referrals are chased ['ON'|'OFF']. Default 'ON'"),
2717 AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
2719 "Limit the number of referral hops that LDAP can follow. "
2720 "(Integer value, default=5)"),
2725 static void util_ldap_register_hooks(apr_pool_t *p)
2727 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2728 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2729 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2730 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2731 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2732 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2733 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2734 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2735 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2736 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2737 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2739 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2740 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2741 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2744 module AP_MODULE_DECLARE_DATA ldap_module = {
2745 STANDARD20_MODULE_STUFF,
2746 util_ldap_create_dir_config, /* create dir config */
2747 NULL, /* merge dir config */
2748 util_ldap_create_config, /* create server config */
2749 util_ldap_merge_config, /* merge server config */
2750 util_ldap_cmds, /* command table */
2751 util_ldap_register_hooks, /* set up request processing hooks */