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 void util_ldap_strdup (char **str, const char *newstr)
77 *str = strdup(newstr);
85 * This handler generates a status page about the current performance of
86 * the LDAP cache. It is enabled as follows:
88 * <Location /ldap-status>
89 * SetHandler ldap-status
93 static int util_ldap_handler(request_rec *r)
95 util_ldap_state_t *st = (util_ldap_state_t *)
96 ap_get_module_config(r->server->module_config,
99 r->allowed |= (1 << M_GET);
100 if (r->method_number != M_GET)
103 if (strcmp(r->handler, "ldap-status")) {
107 ap_set_content_type(r, "text/html");
112 ap_rputs(DOCTYPE_HTML_3_2
113 "<html><head><title>LDAP Cache Information</title></head>\n", r);
114 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
117 util_ald_cache_display(r, st);
122 /* ------------------------------------------------------------------ */
126 * Closes an LDAP connection by unlocking it. The next time
127 * uldap_connection_find() is called this connection will be
128 * available for reuse.
130 static void uldap_connection_close(util_ldap_connection_t *ldc)
136 * Is it safe leaving bound connections floating around between the
137 * different modules? Keeping the user bound is a performance boost,
138 * but it is also a potential security problem - maybe.
140 * For now we unbind the user when we finish with a connection, but
141 * we don't have to...
144 /* mark our connection as available for reuse */
147 apr_thread_mutex_unlock(ldc->lock);
153 * Destroys an LDAP connection by unbinding and closing the connection to
154 * the LDAP server. It is used to bring the connection back to a known
155 * state after an error, and during pool cleanup.
157 static apr_status_t uldap_connection_unbind(void *param)
159 util_ldap_connection_t *ldc = param;
163 ldap_unbind_s(ldc->ldap);
174 * Clean up an LDAP connection by unbinding and unlocking the connection.
176 static apr_status_t uldap_connection_cleanup(void *param)
178 util_ldap_connection_t *ldc = param;
182 /* unbind and disconnect from the LDAP server */
183 uldap_connection_unbind(ldc);
185 /* free the username and password */
187 free((void*)ldc->bindpw);
190 free((void*)ldc->binddn);
193 /* unlock this entry */
194 uldap_connection_close(ldc);
201 static int uldap_connection_init(request_rec *r,
202 util_ldap_connection_t *ldc )
204 int rc = 0, ldap_option = 0;
205 int version = LDAP_VERSION3;
206 apr_ldap_err_t *result = NULL;
207 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
208 util_ldap_state_t *st =
209 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
212 /* Since the host will include a port if the default port is not used,
213 * always specify the default ports for the port parameter. This will
214 * allow a host string that contains multiple hosts the ability to mix
215 * some hosts with ports and some without. All hosts which do not
216 * specify a port will use the default port.
218 apr_ldap_init(r->pool, &(ldc->ldap),
220 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
225 if (result != NULL && result->rc) {
226 ldc->reason = result->reason;
229 if (NULL == ldc->ldap)
232 if (NULL == ldc->reason) {
233 ldc->reason = "LDAP: ldap initialization failed";
236 ldc->reason = result->reason;
241 /* always default to LDAP V3 */
242 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
244 /* set client certificates */
245 if (!apr_is_empty_array(ldc->client_certs)) {
246 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
247 ldc->client_certs, &(result));
248 if (LDAP_SUCCESS != result->rc) {
249 uldap_connection_unbind( ldc );
250 ldc->reason = result->reason;
255 /* switch on SSL/TLS */
256 if (APR_LDAP_NONE != ldc->secure) {
257 apr_ldap_set_option(r->pool, ldc->ldap,
258 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
259 if (LDAP_SUCCESS != result->rc) {
260 uldap_connection_unbind( ldc );
261 ldc->reason = result->reason;
266 /* Set the alias dereferencing option */
267 ldap_option = ldc->deref;
268 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
270 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
271 #ifdef APR_LDAP_OPT_VERIFY_CERT
272 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
273 &(st->verify_svr_cert), &(result));
275 #if defined(LDAPSSL_VERIFY_SERVER)
276 if (st->verify_svr_cert) {
277 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
280 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
282 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
283 /* This is not a per-connection setting so just pass NULL for the
284 Ldap connection handle */
285 if (st->verify_svr_cert) {
286 int i = LDAP_OPT_X_TLS_DEMAND;
287 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
290 int i = LDAP_OPT_X_TLS_NEVER;
291 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
296 #ifdef LDAP_OPT_NETWORK_TIMEOUT
297 if (st->connectionTimeout > 0) {
298 timeOut.tv_sec = st->connectionTimeout;
301 if (st->connectionTimeout >= 0) {
302 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
303 (void *)&timeOut, &(result));
304 if (APR_SUCCESS != rc) {
305 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
306 "LDAP: Could not set the connection timeout");
315 * Connect to the LDAP server and binds. Does not connect if already
316 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
318 * Returns LDAP_SUCCESS on success; and an error code on failure
320 static int uldap_connection_open(request_rec *r,
321 util_ldap_connection_t *ldc)
326 /* sanity check for NULL */
331 /* If the connection is already bound, return
335 ldc->reason = "LDAP: connection open successful (already bound)";
339 /* create the ldap session handle
341 if (NULL == ldc->ldap)
343 rc = uldap_connection_init( r, ldc );
344 if (LDAP_SUCCESS != rc)
351 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
352 * returned. Break out of the loop on Success or any other error.
354 * NOTE: Looping is probably not a great idea. If the server isn't
355 * responding the chances it will respond after a few tries are poor.
356 * However, the original code looped and it only happens on
357 * the error condition.
359 for (failures=0; failures<10; failures++)
361 rc = ldap_simple_bind_s(ldc->ldap,
363 (char *)ldc->bindpw);
364 if (LDAP_SERVER_DOWN != rc) {
366 } else if (failures == 5) {
367 /* attempt to init the connection once again */
368 uldap_connection_unbind( ldc );
369 rc = uldap_connection_init( r, ldc );
370 if (LDAP_SUCCESS != rc)
377 /* free the handle if there was an error
379 if (LDAP_SUCCESS != rc)
381 uldap_connection_unbind(ldc);
382 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
386 ldc->reason = "LDAP: connection open successful";
394 * Compare client certificate arrays.
396 * Returns 1 on compare failure, 0 otherwise.
398 static int compare_client_certs(apr_array_header_t *srcs,
399 apr_array_header_t *dests)
402 struct apr_ldap_opt_tls_cert_t *src, *dest;
404 /* arrays both NULL? if so, then equal */
405 if (srcs == NULL && dests == NULL) {
409 /* arrays different length or either NULL? If so, then not equal */
410 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
414 /* run an actual comparison */
415 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
416 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
417 for (i = 0; i < srcs->nelts; i++) {
418 if (strcmp(src[i].path, dest[i].path) ||
419 strcmp(src[i].password, dest[i].password) ||
420 src[i].type != dest[i].type) {
425 /* if we got here, the cert arrays were identical */
432 * Find an existing ldap connection struct that matches the
433 * provided ldap connection parameters.
435 * If not found in the cache, a new ldc structure will be allocated
436 * from st->pool and returned to the caller. If found in the cache,
437 * a pointer to the existing ldc structure will be returned.
439 static util_ldap_connection_t *
440 uldap_connection_find(request_rec *r,
441 const char *host, int port,
442 const char *binddn, const char *bindpw,
443 deref_options deref, int secure)
445 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
446 int secureflag = secure;
448 util_ldap_state_t *st =
449 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
454 /* mutex lock this function */
455 apr_thread_mutex_lock(st->mutex);
458 if (secure < APR_LDAP_NONE) {
459 secureflag = st->secure;
462 /* Search for an exact connection match in the list that is not
465 for (l=st->connections,p=NULL; l; l=l->next) {
467 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
469 if ( (l->port == port) && (strcmp(l->host, host) == 0)
470 && ((!l->binddn && !binddn) || (l->binddn && binddn
471 && !strcmp(l->binddn, binddn)))
472 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
473 && !strcmp(l->bindpw, bindpw)))
474 && (l->deref == deref) && (l->secure == secureflag)
475 && !compare_client_certs(st->client_certs, l->client_certs))
480 /* If this connection didn't match the criteria, then we
481 * need to unlock the mutex so it is available to be reused.
483 apr_thread_mutex_unlock(l->lock);
489 /* If nothing found, search again, but we don't care about the
490 * binddn and bindpw this time.
493 for (l=st->connections,p=NULL; l; l=l->next) {
495 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
498 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
499 (l->deref == deref) && (l->secure == secureflag) &&
500 !compare_client_certs(st->client_certs, l->client_certs))
502 /* the bind credentials have changed */
504 util_ldap_strdup((char**)&(l->binddn), binddn);
505 util_ldap_strdup((char**)&(l->bindpw), bindpw);
509 /* If this connection didn't match the criteria, then we
510 * need to unlock the mutex so it is available to be reused.
512 apr_thread_mutex_unlock(l->lock);
519 /* artificially disable cache */
522 /* If no connection what found after the second search, we
528 * Add the new connection entry to the linked list. Note that we
529 * don't actually establish an LDAP connection yet; that happens
530 * the first time authentication is requested.
532 /* create the details to the pool in st */
533 l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
534 if (apr_pool_create(&l->pool, st->pool) != APR_SUCCESS) {
535 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
536 "util_ldap: Failed to create memory pool");
538 apr_thread_mutex_unlock(st->mutex);
544 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
545 apr_thread_mutex_lock(l->lock);
548 l->host = apr_pstrdup(st->pool, host);
551 util_ldap_strdup((char**)&(l->binddn), binddn);
552 util_ldap_strdup((char**)&(l->bindpw), bindpw);
554 /* The security mode after parsing the URL will always be either
555 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
556 * If the security setting is NONE, override it to the security
557 * setting optionally supplied by the admin using LDAPTrustedMode
559 l->secure = secureflag;
561 /* save away a copy of the client cert list that is presently valid */
562 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
573 apr_thread_mutex_unlock(st->mutex);
578 /* ------------------------------------------------------------------ */
581 * Compares two DNs to see if they're equal. The only way to do this correctly
582 * is to search for the dn and then do ldap_get_dn() on the result. This should
583 * match the initial dn, since it would have been also retrieved with
584 * ldap_get_dn(). This is expensive, so if the configuration value
585 * compare_dn_on_server is false, just does an ordinary strcmp.
587 * The lock for the ldap cache should already be acquired.
589 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
590 const char *url, const char *dn,
591 const char *reqdn, int compare_dn_on_server)
594 util_url_node_t *curl;
595 util_url_node_t curnode;
596 util_dn_compare_node_t *node;
597 util_dn_compare_node_t newnode;
599 LDAPMessage *res, *entry;
602 util_ldap_state_t *st = (util_ldap_state_t *)
603 ap_get_module_config(r->server->module_config,
606 /* get cache entry (or create one) */
610 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
612 curl = util_ald_create_caches(st, url);
616 /* a simple compare? */
617 if (!compare_dn_on_server) {
618 /* unlock this read lock */
619 if (strcmp(dn, reqdn)) {
620 ldc->reason = "DN Comparison FALSE (direct strcmp())";
621 return LDAP_COMPARE_FALSE;
624 ldc->reason = "DN Comparison TRUE (direct strcmp())";
625 return LDAP_COMPARE_TRUE;
630 /* no - it's a server side compare */
633 /* is it in the compare cache? */
634 newnode.reqdn = (char *)reqdn;
635 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
637 /* If it's in the cache, it's good */
638 /* unlock this read lock */
640 ldc->reason = "DN Comparison TRUE (cached)";
641 return LDAP_COMPARE_TRUE;
644 /* unlock this read lock */
649 if (failures++ > 10) {
650 /* too many failures */
654 /* make a server connection */
655 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
656 /* connect to server failed */
660 /* search for reqdn */
661 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
662 "(objectclass=*)", NULL, 1,
663 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
666 ldc->reason = "DN Comparison ldap_search_ext_s() "
667 "failed with server down";
668 uldap_connection_unbind(ldc);
671 if (result != LDAP_SUCCESS) {
672 /* search for reqdn failed - no match */
673 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
677 entry = ldap_first_entry(ldc->ldap, res);
678 searchdn = ldap_get_dn(ldc->ldap, entry);
681 if (strcmp(dn, searchdn) != 0) {
682 /* compare unsuccessful */
683 ldc->reason = "DN Comparison FALSE (checked on server)";
684 result = LDAP_COMPARE_FALSE;
688 /* compare successful - add to the compare cache */
690 newnode.reqdn = (char *)reqdn;
691 newnode.dn = (char *)dn;
693 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
695 || (strcmp(reqdn, node->reqdn) != 0)
696 || (strcmp(dn, node->dn) != 0))
698 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
702 ldc->reason = "DN Comparison TRUE (checked on server)";
703 result = LDAP_COMPARE_TRUE;
705 ldap_memfree(searchdn);
711 * Does an generic ldap_compare operation. It accepts a cache that it will use
712 * to lookup the compare in the cache. We cache two kinds of compares
713 * (require group compares) and (require user compares). Each compare has a
714 * different cache node: require group includes the DN; require user does not
715 * because the require user cache is owned by the
718 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
719 const char *url, const char *dn,
720 const char *attrib, const char *value)
723 util_url_node_t *curl;
724 util_url_node_t curnode;
725 util_compare_node_t *compare_nodep;
726 util_compare_node_t the_compare_node;
727 apr_time_t curtime = 0; /* silence gcc -Wall */
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) */
737 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
739 curl = util_ald_create_caches(st, url);
744 /* make a comparison to the cache */
746 curtime = apr_time_now();
748 the_compare_node.dn = (char *)dn;
749 the_compare_node.attrib = (char *)attrib;
750 the_compare_node.value = (char *)value;
751 the_compare_node.result = 0;
752 the_compare_node.sgl_processed = 0;
753 the_compare_node.subgroupList = NULL;
755 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
758 if (compare_nodep != NULL) {
760 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
761 /* ...but it is too old */
762 util_ald_cache_remove(curl->compare_cache, compare_nodep);
765 /* ...and it is good */
766 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
767 ldc->reason = "Comparison true (cached)";
769 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
770 ldc->reason = "Comparison false (cached)";
772 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
773 ldc->reason = "Comparison no such attribute (cached)";
776 ldc->reason = "Comparison undefined (cached)";
779 /* record the result code to return with the reason... */
780 result = compare_nodep->result;
781 /* and unlock this read lock */
786 /* unlock this read lock */
791 if (failures++ > 10) {
792 /* too many failures */
796 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
801 if ((result = ldap_compare_s(ldc->ldap,
805 == LDAP_SERVER_DOWN) {
806 /* connection failed - try again */
807 ldc->reason = "ldap_compare_s() failed with server down";
808 uldap_connection_unbind(ldc);
812 ldc->reason = "Comparison complete";
813 if ((LDAP_COMPARE_TRUE == result) ||
814 (LDAP_COMPARE_FALSE == result) ||
815 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
817 /* compare completed; caching result */
819 the_compare_node.lastcompare = curtime;
820 the_compare_node.result = result;
821 the_compare_node.sgl_processed = 0;
822 the_compare_node.subgroupList = NULL;
824 /* If the node doesn't exist then insert it, otherwise just update
825 * it with the last results
827 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
829 if ( (compare_nodep == NULL)
830 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
831 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
832 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
836 junk = util_ald_cache_insert(curl->compare_cache,
839 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
840 "[%" APR_PID_T_FMT "] cache_compare: Cache"
841 " insertion failure.", getpid());
845 compare_nodep->lastcompare = curtime;
846 compare_nodep->result = result;
850 if (LDAP_COMPARE_TRUE == result) {
851 ldc->reason = "Comparison true (adding to cache)";
852 return LDAP_COMPARE_TRUE;
854 else if (LDAP_COMPARE_FALSE == result) {
855 ldc->reason = "Comparison false (adding to cache)";
856 return LDAP_COMPARE_FALSE;
859 ldc->reason = "Comparison no such attribute (adding to cache)";
860 return LDAP_NO_SUCH_ATTRIBUTE;
867 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
868 util_ldap_connection_t *ldc,
871 char **subgroupAttrs,
872 apr_array_header_t *subgroupclasses)
875 int result = LDAP_COMPARE_FALSE;
876 util_compare_subgroup_t *res = NULL;
877 LDAPMessage *sga_res, *entry;
878 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
879 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
881 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
883 if (!subgroupAttrs) {
889 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
891 if (failures++ > 10) {
892 /* too many failures */
896 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
901 /* try to do the search */
902 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
903 (char *)"cn=*", subgroupAttrs, 0,
904 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
905 if (result == LDAP_SERVER_DOWN) {
906 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
908 uldap_connection_unbind(ldc);
912 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
913 if (result != LDAP_SUCCESS) {
914 ldc->reason = "ldap_search_ext_s() for subgroups failed";
918 entry = ldap_first_entry(ldc->ldap, sga_res);
921 * Get values for the provided sub-group attributes.
924 int indx = 0, tmp_sgcIndex;
926 while (subgroupAttrs[indx]) {
930 /* Get *all* matching "member" values from this group. */
931 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
936 * Now we are going to pare the subgroup members of this group
937 * to *just* the subgroups, add them to the compare_nodep, and
938 * then proceed to check the new level of subgroups.
940 while (values[val_index]) {
941 /* Check if this entry really is a group. */
943 result = LDAP_COMPARE_FALSE;
944 while ((tmp_sgcIndex < subgroupclasses->nelts)
945 && (result != LDAP_COMPARE_TRUE)) {
946 result = uldap_cache_compare(r, ldc, url,
949 sgc_ents[tmp_sgcIndex].name
952 if (result != LDAP_COMPARE_TRUE) {
956 /* It's a group, so add it to the array. */
957 if (result == LDAP_COMPARE_TRUE) {
958 char **newgrp = (char **) apr_array_push(subgroups);
959 *newgrp = apr_pstrdup(r->pool, values[val_index]);
963 ldap_value_free(values);
969 ldap_msgfree(sga_res);
971 if (subgroups->nelts > 0) {
972 /* We need to fill in tmp_local_subgroups using the data from LDAP */
975 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
976 res->subgroupDNs = apr_pcalloc(r->pool,
977 sizeof(char *) * (subgroups->nelts));
978 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
979 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
989 * Does a recursive lookup operation to try to find a user within (cached)
990 * nested groups. It accepts a cache that it will use to lookup previous
991 * compare attempts. We cache two kinds of compares (require group compares)
992 * and (require user compares). Each compare has a different cache node:
993 * require group includes the DN; require user does not because the require
994 * user cache is owned by the
996 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
999 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1000 * generic, user-agnostic, cached group entry. This will create a new generic
1001 * cache entry if there
1002 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1004 * 2. Lock The cache and get the generic cache entry.
1005 * 3. Check if there is already a subgrouplist in this generic group's cache
1007 * A. If there is, go to step 4.
1008 * B. If there isn't:
1009 * i) Use ldap_search to get the full list
1010 * of subgroup "members" (which may include non-group "members").
1011 * ii) Use uldap_cache_compare to strip the list down to just groups.
1012 * iii) Lock and add this stripped down list to the cache of the generic
1014 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1016 * subgroup to see if the subgroup contains the user and to get the subgroups
1018 * cache (with user-afinity, if they aren't already there).
1019 * A. If the user is in the subgroup, then we'll be returning
1020 * LDAP_COMPARE_TRUE.
1021 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1022 * uldap_cache_compare) then recursively call this function to get the
1023 * sub-subgroups added...
1024 * 5. Cleanup local allocations.
1025 * 6. Return the final result.
1028 static int uldap_cache_check_subgroups(request_rec *r,
1029 util_ldap_connection_t *ldc,
1030 const char *url, const char *dn,
1031 const char *attrib, const char *value,
1032 char **subgroupAttrs,
1033 apr_array_header_t *subgroupclasses,
1034 int cur_subgroup_depth,
1035 int max_subgroup_depth)
1037 int result = LDAP_COMPARE_FALSE;
1038 util_url_node_t *curl;
1039 util_url_node_t curnode;
1040 util_compare_node_t *compare_nodep;
1041 util_compare_node_t the_compare_node;
1042 util_compare_subgroup_t *tmp_local_sgl = NULL;
1043 int lcl_sgl_processedFlag = 0, sgindex = 0, base_sgcIndex = 0;
1044 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
1046 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1048 util_ldap_state_t *st = (util_ldap_state_t *)
1049 ap_get_module_config(r->server->module_config,
1053 * Stop looking at deeper levels of nested groups if we have reached the
1054 * max. Since we already checked the top-level group in uldap_cache_compare,
1055 * we don't need to check it again here - so if max_subgroup_depth is set
1056 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1057 * We'll be calling uldap_cache_compare from here to check if the user is
1058 * in the next level before we recurse into that next level looking for
1061 if (cur_subgroup_depth >= max_subgroup_depth) {
1062 return LDAP_COMPARE_FALSE;
1066 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1069 while ((base_sgcIndex < subgroupclasses->nelts)
1070 && (result != LDAP_COMPARE_TRUE)) {
1071 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1072 sgc_ents[base_sgcIndex].name);
1073 if (result != LDAP_COMPARE_TRUE) {
1078 if (result != LDAP_COMPARE_TRUE) {
1079 ldc->reason = "DN failed group verification.";
1084 * 2. Find previously created cache entry and check if there is already a
1089 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1090 LDAP_CACHE_UNLOCK();
1092 if (curl && curl->compare_cache) {
1093 /* make a comparison to the cache */
1096 the_compare_node.dn = (char *)dn;
1097 the_compare_node.attrib = (char *)"objectClass";
1098 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1099 the_compare_node.result = 0;
1100 the_compare_node.sgl_processed = 0;
1101 the_compare_node.subgroupList = NULL;
1103 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1106 if (compare_nodep != NULL) {
1108 * Found the generic group entry... but the user isn't in this
1109 * group or we wouldn't be here.
1111 lcl_sgl_processedFlag = compare_nodep->sgl_processed;
1112 if(compare_nodep->sgl_processed && compare_nodep->subgroupList) {
1113 /* Make a local copy of the subgroup list */
1115 tmp_local_sgl = apr_pcalloc(r->pool,
1116 sizeof(util_compare_subgroup_t));
1117 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1118 tmp_local_sgl->subgroupDNs =
1119 apr_pcalloc(r->pool,
1120 sizeof(char *) * compare_nodep->subgroupList->len);
1121 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1122 tmp_local_sgl->subgroupDNs[i] =
1123 apr_pstrdup(r->pool,
1124 compare_nodep->subgroupList->subgroupDNs[i]);
1128 LDAP_CACHE_UNLOCK();
1132 * If we get here, something is wrong. Caches should have been
1133 * created and this group entry should be found in the cache.
1135 ldc->reason = "check_subgroups failed to find any caches.";
1136 return LDAP_COMPARE_FALSE;
1139 result = LDAP_COMPARE_FALSE;
1141 if ((lcl_sgl_processedFlag == 0) && (!tmp_local_sgl)) {
1142 /* No Cached SGL, retrieve from LDAP */
1143 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1144 "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s,"
1145 " retrieving from LDAP" , getpid(), dn);
1146 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1148 if (!tmp_local_sgl) {
1149 /* No SGL aailable via LDAP either */
1150 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1151 " util_ldap: no subgroups for %s" , getpid(), dn);
1153 lcl_sgl_processedFlag = 1;
1156 * Find the generic group cache entry and add the sgl we just retrieved.
1160 the_compare_node.dn = (char *)dn;
1161 the_compare_node.attrib = (char *)"objectClass";
1162 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1163 the_compare_node.result = 0;
1164 the_compare_node.sgl_processed = 0;
1165 the_compare_node.subgroupList = NULL;
1167 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1170 if (compare_nodep == NULL) {
1172 * Didn't find it. This shouldn't happen since we just called
1173 * uldap_cache_compare.
1175 LDAP_CACHE_UNLOCK();
1176 ldc->reason = "check_subgroups failed to find the cache entry to"
1177 " add sub-group list to.";
1178 return LDAP_COMPARE_FALSE;
1181 * overwrite SGL if it was previously updated between the last
1182 * two times we looked at the cache
1184 compare_nodep->sgl_processed = 1;
1185 if (tmp_local_sgl) {
1186 compare_nodep->subgroupList = util_ald_sgl_dup(curl->compare_cache,
1191 * We didn't find a single subgroup, next time save us from looking
1193 compare_nodep->subgroupList = NULL;
1195 LDAP_CACHE_UNLOCK();
1199 * tmp_local_sgl has either been created, or copied out of the cache
1200 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1203 result = LDAP_COMPARE_FALSE;
1204 if (!tmp_local_sgl) {
1208 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1209 const char *group = NULL;
1210 group = tmp_local_sgl->subgroupDNs[sgindex];
1212 * 4. Now loop through the subgroupList and call uldap_cache_compare
1213 * to check for the user.
1215 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1216 if (result == LDAP_COMPARE_TRUE) {
1218 * 4.A. We found the user in the subgroup. Return
1219 * LDAP_COMPARE_TRUE.
1221 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1222 " util_ldap: Found user %s in a subgroup (%s) at"
1223 " level %d of %d.", getpid(), r->user, group,
1224 cur_subgroup_depth+1, max_subgroup_depth);
1228 * 4.B. We didn't find the user in this subgroup, so recurse into
1229 * it and keep looking.
1231 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1232 " util_ldap: user %s not found in subgroup (%s) at"
1233 " level %d of %d.", getpid(), r->user, group,
1234 cur_subgroup_depth+1, max_subgroup_depth);
1235 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1236 value, subgroupAttrs,
1238 cur_subgroup_depth+1,
1239 max_subgroup_depth);
1248 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1249 const char *url, const char *basedn,
1250 int scope, char **attrs, const char *filter,
1251 const char *bindpw, const char **binddn,
1252 const char ***retvals)
1254 const char **vals = NULL;
1257 LDAPMessage *res, *entry;
1261 util_url_node_t *curl; /* Cached URL node */
1262 util_url_node_t curnode;
1263 util_search_node_t *search_nodep; /* Cached search node */
1264 util_search_node_t the_search_node;
1267 util_ldap_state_t *st =
1268 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1271 /* Get the cache node for this url */
1274 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1277 curl = util_ald_create_caches(st, url);
1279 LDAP_CACHE_UNLOCK();
1283 the_search_node.username = filter;
1284 search_nodep = util_ald_cache_fetch(curl->search_cache,
1286 if (search_nodep != NULL) {
1288 /* found entry in search cache... */
1289 curtime = apr_time_now();
1292 * Remove this item from the cache if its expired. If the sent
1293 * password doesn't match the storepassword, the entry will
1294 * be removed and readded later if the credentials pass
1297 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1298 /* ...but entry is too old */
1299 util_ald_cache_remove(curl->search_cache, search_nodep);
1301 else if ( (search_nodep->bindpw)
1302 && (search_nodep->bindpw[0] != '\0')
1303 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1305 /* ...and entry is valid */
1306 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1310 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1311 while (search_nodep->vals[i]) {
1312 (*retvals)[i] = apr_pstrdup(r->pool,
1313 search_nodep->vals[i]);
1317 LDAP_CACHE_UNLOCK();
1318 ldc->reason = "Authentication successful (cached)";
1319 return LDAP_SUCCESS;
1322 /* unlock this read lock */
1323 LDAP_CACHE_UNLOCK();
1327 * At this point, there is no valid cached search, so lets do the search.
1331 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1334 if (failures++ > 10) {
1337 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1341 /* try do the search */
1342 if ((result = ldap_search_ext_s(ldc->ldap,
1343 (char *)basedn, scope,
1344 (char *)filter, attrs, 0,
1345 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1346 == LDAP_SERVER_DOWN)
1348 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1349 uldap_connection_unbind(ldc);
1353 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1354 if (result != LDAP_SUCCESS) {
1355 ldc->reason = "ldap_search_ext_s() for user failed";
1360 * We should have found exactly one entry; to find a different
1361 * number is an error.
1363 count = ldap_count_entries(ldc->ldap, res);
1367 ldc->reason = "User not found";
1369 ldc->reason = "User is not unique (search found two "
1372 return LDAP_NO_SUCH_OBJECT;
1375 entry = ldap_first_entry(ldc->ldap, res);
1377 /* Grab the dn, copy it into the pool, and free it again */
1378 dn = ldap_get_dn(ldc->ldap, entry);
1379 *binddn = apr_pstrdup(r->pool, dn);
1383 * A bind to the server with an empty password always succeeds, so
1384 * we check to ensure that the password is not empty. This implies
1385 * that users who actually do have empty passwords will never be
1386 * able to authenticate with this module. I don't see this as a big
1389 if (!bindpw || strlen(bindpw) <= 0) {
1391 ldc->reason = "Empty password not allowed";
1392 return LDAP_INVALID_CREDENTIALS;
1396 * Attempt to bind with the retrieved dn and the password. If the bind
1397 * fails, it means that the password is wrong (the dn obviously
1398 * exists, since we just retrieved it)
1400 if ((result = ldap_simple_bind_s(ldc->ldap,
1402 (char *)bindpw)) == LDAP_SERVER_DOWN) {
1403 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1404 "failed with server down";
1406 uldap_connection_unbind(ldc);
1410 /* failure? if so - return */
1411 if (result != LDAP_SUCCESS) {
1412 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1414 uldap_connection_unbind(ldc);
1419 * We have just bound the connection to a different user and password
1420 * combination, which might be reused unintentionally next time this
1421 * connection is used from the connection pool. To ensure no confusion,
1422 * we mark the connection as unbound.
1428 * Get values for the provided attributes.
1434 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1441 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1442 while (values && values[j]) {
1443 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1444 : apr_pstrdup(r->pool, values[j]);
1447 ldap_value_free(values);
1455 * Add the new username to the search cache.
1459 the_search_node.username = filter;
1460 the_search_node.dn = *binddn;
1461 the_search_node.bindpw = bindpw;
1462 the_search_node.lastbind = apr_time_now();
1463 the_search_node.vals = vals;
1464 the_search_node.numvals = numvals;
1466 /* Search again to make sure that another thread didn't ready insert
1467 * this node into the cache before we got here. If it does exist then
1468 * update the lastbind
1470 search_nodep = util_ald_cache_fetch(curl->search_cache,
1472 if ((search_nodep == NULL) ||
1473 (strcmp(*binddn, search_nodep->dn) != 0)) {
1475 /* Nothing in cache, insert new entry */
1476 util_ald_cache_insert(curl->search_cache, &the_search_node);
1478 else if ((!search_nodep->bindpw) ||
1479 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1481 /* Entry in cache is invalid, remove it and insert new one */
1482 util_ald_cache_remove(curl->search_cache, search_nodep);
1483 util_ald_cache_insert(curl->search_cache, &the_search_node);
1486 /* Cache entry is valid, update lastbind */
1487 search_nodep->lastbind = the_search_node.lastbind;
1489 LDAP_CACHE_UNLOCK();
1493 ldc->reason = "Authentication successful";
1494 return LDAP_SUCCESS;
1498 * This function will return the DN of the entry matching userid.
1499 * It is used to get the DN in case some other module than mod_auth_ldap
1500 * has authenticated the user.
1501 * The function is basically a copy of uldap_cache_checkuserid
1502 * with password checking removed.
1504 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1505 const char *url, const char *basedn,
1506 int scope, char **attrs, const char *filter,
1507 const char **binddn, const char ***retvals)
1509 const char **vals = NULL;
1512 LDAPMessage *res, *entry;
1516 util_url_node_t *curl; /* Cached URL node */
1517 util_url_node_t curnode;
1518 util_search_node_t *search_nodep; /* Cached search node */
1519 util_search_node_t the_search_node;
1522 util_ldap_state_t *st =
1523 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1526 /* Get the cache node for this url */
1529 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1532 curl = util_ald_create_caches(st, url);
1534 LDAP_CACHE_UNLOCK();
1538 the_search_node.username = filter;
1539 search_nodep = util_ald_cache_fetch(curl->search_cache,
1541 if (search_nodep != NULL) {
1543 /* found entry in search cache... */
1544 curtime = apr_time_now();
1547 * Remove this item from the cache if its expired.
1549 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1550 /* ...but entry is too old */
1551 util_ald_cache_remove(curl->search_cache, search_nodep);
1554 /* ...and entry is valid */
1555 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1559 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1560 while (search_nodep->vals[i]) {
1561 (*retvals)[i] = apr_pstrdup(r->pool,
1562 search_nodep->vals[i]);
1566 LDAP_CACHE_UNLOCK();
1567 ldc->reason = "Search successful (cached)";
1568 return LDAP_SUCCESS;
1571 /* unlock this read lock */
1572 LDAP_CACHE_UNLOCK();
1576 * At this point, there is no valid cached search, so lets do the search.
1580 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1583 if (failures++ > 10) {
1586 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1590 /* try do the search */
1591 if ((result = ldap_search_ext_s(ldc->ldap,
1592 (char *)basedn, scope,
1593 (char *)filter, attrs, 0,
1594 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1595 == LDAP_SERVER_DOWN)
1597 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1598 uldap_connection_unbind(ldc);
1602 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1603 if (result != LDAP_SUCCESS) {
1604 ldc->reason = "ldap_search_ext_s() for user failed";
1609 * We should have found exactly one entry; to find a different
1610 * number is an error.
1612 count = ldap_count_entries(ldc->ldap, res);
1616 ldc->reason = "User not found";
1618 ldc->reason = "User is not unique (search found two "
1621 return LDAP_NO_SUCH_OBJECT;
1624 entry = ldap_first_entry(ldc->ldap, res);
1626 /* Grab the dn, copy it into the pool, and free it again */
1627 dn = ldap_get_dn(ldc->ldap, entry);
1628 *binddn = apr_pstrdup(r->pool, dn);
1632 * Get values for the provided attributes.
1638 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1645 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1646 while (values && values[j]) {
1647 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1648 : apr_pstrdup(r->pool, values[j]);
1651 ldap_value_free(values);
1659 * Add the new username to the search cache.
1663 the_search_node.username = filter;
1664 the_search_node.dn = *binddn;
1665 the_search_node.bindpw = NULL;
1666 the_search_node.lastbind = apr_time_now();
1667 the_search_node.vals = vals;
1668 the_search_node.numvals = numvals;
1670 /* Search again to make sure that another thread didn't ready insert
1671 * this node into the cache before we got here. If it does exist then
1672 * update the lastbind
1674 search_nodep = util_ald_cache_fetch(curl->search_cache,
1676 if ((search_nodep == NULL) ||
1677 (strcmp(*binddn, search_nodep->dn) != 0)) {
1679 /* Nothing in cache, insert new entry */
1680 util_ald_cache_insert(curl->search_cache, &the_search_node);
1683 * Don't update lastbind on entries with bindpw because
1684 * we haven't verified that password. It's OK to update
1685 * the entry if there is no password in it.
1687 else if (!search_nodep->bindpw) {
1688 /* Cache entry is valid, update lastbind */
1689 search_nodep->lastbind = the_search_node.lastbind;
1691 LDAP_CACHE_UNLOCK();
1696 ldc->reason = "Search successful";
1697 return LDAP_SUCCESS;
1701 * Reports if ssl support is enabled
1703 * 1 = enabled, 0 = not enabled
1705 static int uldap_ssl_supported(request_rec *r)
1707 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1708 r->server->module_config, &ldap_module);
1710 return(st->ssl_supported);
1714 /* ---------------------------------------- */
1715 /* config directives */
1718 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1721 util_ldap_state_t *st =
1722 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1724 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1730 st->cache_bytes = atol(bytes);
1732 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1733 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1734 " cache size to %" APR_SIZE_T_FMT " bytes.",
1735 getpid(), st->cache_bytes);
1740 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1743 util_ldap_state_t *st =
1744 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1746 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1753 st->cache_file = ap_server_root_relative(st->pool, file);
1756 st->cache_file = NULL;
1759 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1760 "LDAP cache: Setting shared memory cache file to %s bytes.",
1766 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1769 util_ldap_state_t *st =
1770 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1772 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1778 st->search_cache_ttl = atol(ttl) * 1000000;
1780 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1781 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1782 " microseconds.", getpid(), st->search_cache_ttl);
1787 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1790 util_ldap_state_t *st =
1791 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1793 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1799 st->search_cache_size = atol(size);
1800 if (st->search_cache_size < 0) {
1801 st->search_cache_size = 0;
1804 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1805 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1806 " to %ld entries.", getpid(), st->search_cache_size);
1811 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1814 util_ldap_state_t *st =
1815 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1817 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1823 st->compare_cache_ttl = atol(ttl) * 1000000;
1825 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1826 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1827 " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1832 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1835 util_ldap_state_t *st =
1836 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1838 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1844 st->compare_cache_size = atol(size);
1845 if (st->compare_cache_size < 0) {
1846 st->compare_cache_size = 0;
1849 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1850 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
1851 " to %ld entries.", getpid(), st->compare_cache_size);
1858 * Parse the certificate type.
1860 * The type can be one of the following:
1861 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1862 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1864 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1866 static int util_ldap_parse_cert_type(const char *type)
1868 /* Authority file in binary DER format */
1869 if (0 == strcasecmp("CA_DER", type)) {
1870 return APR_LDAP_CA_TYPE_DER;
1873 /* Authority file in Base64 format */
1874 else if (0 == strcasecmp("CA_BASE64", type)) {
1875 return APR_LDAP_CA_TYPE_BASE64;
1878 /* Netscape certificate database file/directory */
1879 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1880 return APR_LDAP_CA_TYPE_CERT7_DB;
1883 /* Netscape secmod file/directory */
1884 else if (0 == strcasecmp("CA_SECMOD", type)) {
1885 return APR_LDAP_CA_TYPE_SECMOD;
1888 /* Client cert file in DER format */
1889 else if (0 == strcasecmp("CERT_DER", type)) {
1890 return APR_LDAP_CERT_TYPE_DER;
1893 /* Client cert file in Base64 format */
1894 else if (0 == strcasecmp("CERT_BASE64", type)) {
1895 return APR_LDAP_CERT_TYPE_BASE64;
1898 /* Client cert file in PKCS#12 format */
1899 else if (0 == strcasecmp("CERT_PFX", type)) {
1900 return APR_LDAP_CERT_TYPE_PFX;
1903 /* Netscape client cert database file/directory */
1904 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1905 return APR_LDAP_CERT_TYPE_KEY3_DB;
1908 /* Netscape client cert nickname */
1909 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1910 return APR_LDAP_CERT_TYPE_NICKNAME;
1913 /* Client cert key file in DER format */
1914 else if (0 == strcasecmp("KEY_DER", type)) {
1915 return APR_LDAP_KEY_TYPE_DER;
1918 /* Client cert key file in Base64 format */
1919 else if (0 == strcasecmp("KEY_BASE64", type)) {
1920 return APR_LDAP_KEY_TYPE_BASE64;
1923 /* Client cert key file in PKCS#12 format */
1924 else if (0 == strcasecmp("KEY_PFX", type)) {
1925 return APR_LDAP_KEY_TYPE_PFX;
1929 return APR_LDAP_CA_TYPE_UNKNOWN;
1936 * Set LDAPTrustedGlobalCert.
1938 * This directive takes either two or three arguments:
1939 * - certificate type
1940 * - certificate file / directory / nickname
1941 * - certificate password (optional)
1943 * This directive may only be used globally.
1945 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
1949 const char *password)
1951 util_ldap_state_t *st =
1952 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1954 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1958 apr_ldap_opt_tls_cert_t *cert;
1964 /* handle the certificate type */
1966 cert_type = util_ldap_parse_cert_type(type);
1967 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1968 return apr_psprintf(cmd->pool, "The certificate type %s is "
1969 "not recognised. It should be one "
1970 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1971 "CA_SECMOD, CERT_DER, CERT_BASE64, "
1972 "CERT_KEY3_DB, CERT_NICKNAME, "
1973 "KEY_DER, KEY_BASE64", type);
1977 return "Certificate type was not specified.";
1980 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1981 "LDAP: SSL trusted global cert - %s (type %s)",
1984 /* add the certificate to the global array */
1985 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1986 cert->type = cert_type;
1988 cert->password = password;
1990 /* if file is a file or path, fix the path */
1991 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1992 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1994 cert->path = ap_server_root_relative(cmd->pool, file);
1996 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1999 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2000 "LDAP: Could not open SSL trusted certificate "
2001 "authority file - %s",
2002 cert->path == NULL ? file : cert->path);
2003 return "Invalid global certificate file path";
2012 * Set LDAPTrustedClientCert.
2014 * This directive takes either two or three arguments:
2015 * - certificate type
2016 * - certificate file / directory / nickname
2017 * - certificate password (optional)
2019 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2023 const char *password)
2025 util_ldap_state_t *st =
2026 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2031 apr_ldap_opt_tls_cert_t *cert;
2033 /* handle the certificate type */
2035 cert_type = util_ldap_parse_cert_type(type);
2036 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2037 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2038 "not recognised. It should be one "
2039 "of CERT_DER, CERT_BASE64, "
2040 "CERT_NICKNAME, CERT_PFX,"
2041 "KEY_DER, KEY_BASE64, KEY_PFX",
2044 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2045 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2046 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2047 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2048 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2049 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2050 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2051 "only valid within a "
2052 "LDAPTrustedGlobalCert directive. "
2053 "Only CERT_DER, CERT_BASE64, "
2054 "CERT_NICKNAME, KEY_DER, and "
2055 "KEY_BASE64 may be used.", type);
2059 return "Certificate type was not specified.";
2062 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2063 "LDAP: SSL trusted client cert - %s (type %s)",
2066 /* add the certificate to the global array */
2067 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2068 cert->type = cert_type;
2070 cert->password = password;
2072 /* if file is a file or path, fix the path */
2073 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2074 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2076 cert->path = ap_server_root_relative(cmd->pool, file);
2078 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2081 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2082 "LDAP: Could not open SSL client certificate "
2084 cert->path == NULL ? file : cert->path);
2085 return "Invalid client certificate file path";
2095 * Set LDAPTrustedMode.
2097 * This directive sets what encryption mode to use on a connection:
2098 * - None (No encryption)
2099 * - SSL (SSL encryption)
2100 * - STARTTLS (TLS encryption)
2102 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2105 util_ldap_state_t *st =
2106 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2109 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2110 "LDAP: SSL trusted mode - %s",
2113 if (0 == strcasecmp("NONE", mode)) {
2114 st->secure = APR_LDAP_NONE;
2116 else if (0 == strcasecmp("SSL", mode)) {
2117 st->secure = APR_LDAP_SSL;
2119 else if ( (0 == strcasecmp("TLS", mode))
2120 || (0 == strcasecmp("STARTTLS", mode))) {
2121 st->secure = APR_LDAP_STARTTLS;
2124 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2125 "SSL, or TLS/STARTTLS";
2132 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2136 util_ldap_state_t *st =
2137 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2139 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2145 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2146 "LDAP: SSL verify server certificate - %s",
2147 mode?"TRUE":"FALSE");
2149 st->verify_svr_cert = mode;
2155 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2159 util_ldap_state_t *st =
2160 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2162 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2168 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2169 st->connectionTimeout = atol(ttl);
2171 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2172 "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2173 " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2175 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2176 "LDAP: Connection timout option not supported by the "
2177 "LDAP SDK in use." );
2184 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2186 util_ldap_state_t *st =
2187 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2189 /* Create a per vhost pool for mod_ldap to use, serialized with
2190 * st->mutex (also one per vhost). both are replicated by fork(),
2191 * no shared memory managed by either.
2193 apr_pool_create(&st->pool, p);
2195 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2198 st->cache_bytes = 100000;
2199 st->search_cache_ttl = 600000000;
2200 st->search_cache_size = 1024;
2201 st->compare_cache_ttl = 600000000;
2202 st->compare_cache_size = 1024;
2203 st->connections = NULL;
2204 st->ssl_supported = 0;
2205 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2206 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2207 st->secure = APR_LDAP_NONE;
2209 st->connectionTimeout = 10;
2210 st->verify_svr_cert = 1;
2215 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2218 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2219 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2220 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2222 st->pool = overrides->pool;
2224 st->mutex = overrides->mutex;
2227 /* The cache settings can not be modified in a
2228 virtual host since all server use the same
2229 shared memory cache. */
2230 st->cache_bytes = base->cache_bytes;
2231 st->search_cache_ttl = base->search_cache_ttl;
2232 st->search_cache_size = base->search_cache_size;
2233 st->compare_cache_ttl = base->compare_cache_ttl;
2234 st->compare_cache_size = base->compare_cache_size;
2235 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2237 st->connections = NULL;
2238 st->ssl_supported = 0;
2239 st->global_certs = apr_array_append(p, base->global_certs,
2240 overrides->global_certs);
2241 st->client_certs = apr_array_append(p, base->client_certs,
2242 overrides->client_certs);
2243 st->secure = (overrides->secure_set == 0) ? base->secure
2244 : overrides->secure;
2246 /* These LDAP connection settings can not be overwritten in
2247 a virtual host. Once set in the base server, they must
2248 remain the same. None of the LDAP SDKs seem to be able
2249 to handle setting the verify_svr_cert flag on a
2250 per-connection basis. The OpenLDAP client appears to be
2251 able to handle the connection timeout per-connection
2252 but the Novell SDK cannot. Allowing the timeout to
2253 be set by each vhost is of little value so rather than
2254 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2255 is being enforced on this setting as well. */
2256 st->connectionTimeout = base->connectionTimeout;
2257 st->verify_svr_cert = base->verify_svr_cert;
2262 static apr_status_t util_ldap_cleanup_module(void *data)
2265 server_rec *s = data;
2266 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2267 s->module_config, &ldap_module);
2269 if (st->ssl_supported) {
2270 apr_ldap_ssl_deinit();
2277 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2278 apr_pool_t *ptemp, server_rec *s)
2280 apr_status_t result;
2281 server_rec *s_vhost;
2282 util_ldap_state_t *st_vhost;
2284 util_ldap_state_t *st = (util_ldap_state_t *)
2285 ap_get_module_config(s->module_config,
2289 const char *userdata_key = "util_ldap_init";
2290 apr_ldap_err_t *result_err = NULL;
2293 /* util_ldap_post_config() will be called twice. Don't bother
2294 * going through all of the initialization on the first call
2295 * because it will just be thrown away.*/
2296 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2298 apr_pool_userdata_set((const void *)1, userdata_key,
2299 apr_pool_cleanup_null, s->process->pool);
2301 #if APR_HAS_SHARED_MEMORY
2302 /* If the cache file already exists then delete it. Otherwise we are
2303 * going to run into problems creating the shared memory. */
2304 if (st->cache_file) {
2305 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2307 apr_file_remove(lck_file, ptemp);
2313 #if APR_HAS_SHARED_MEMORY
2314 /* initializing cache if shared memory size is not zero and we already
2315 * don't have shm address
2317 if (!st->cache_shm && st->cache_bytes > 0) {
2319 result = util_ldap_cache_init(p, st);
2320 if (result != APR_SUCCESS) {
2321 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2322 "LDAP cache: could not create shared memory segment");
2327 #if APR_HAS_SHARED_MEMORY
2328 if (st->cache_file) {
2329 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2334 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2335 st->lock_file, APR_LOCK_DEFAULT,
2337 if (result != APR_SUCCESS) {
2341 #ifdef AP_NEED_SET_MUTEX_PERMS
2342 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2343 if (result != APR_SUCCESS) {
2344 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2345 "LDAP cache: failed to set mutex permissions");
2350 /* merge config in all vhost */
2353 st_vhost = (util_ldap_state_t *)
2354 ap_get_module_config(s_vhost->module_config,
2357 #if APR_HAS_SHARED_MEMORY
2358 st_vhost->cache_shm = st->cache_shm;
2359 st_vhost->cache_rmm = st->cache_rmm;
2360 st_vhost->cache_file = st->cache_file;
2361 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2362 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2363 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2364 s_vhost->server_hostname);
2366 st_vhost->lock_file = st->lock_file;
2367 s_vhost = s_vhost->next;
2369 #if APR_HAS_SHARED_MEMORY
2372 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2373 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2374 "shared memory cache");
2378 /* log the LDAP SDK used
2381 apr_ldap_err_t *result = NULL;
2382 apr_ldap_info(p, &(result));
2383 if (result != NULL) {
2384 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2388 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2389 util_ldap_cleanup_module);
2392 * Initialize SSL support, and log the result for the benefit of the admin.
2394 * If SSL is not supported it is not necessarily an error, as the
2395 * application may not want to use it.
2397 rc = apr_ldap_ssl_init(p,
2401 if (APR_SUCCESS == rc) {
2402 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2403 (void *)st->global_certs, &(result_err));
2406 if (APR_SUCCESS == rc) {
2407 st->ssl_supported = 1;
2408 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2409 "LDAP: SSL support available" );
2412 st->ssl_supported = 0;
2413 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2414 "LDAP: SSL support unavailable%s%s",
2415 result_err ? ": " : "",
2416 result_err ? result_err->reason : "");
2422 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2425 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2428 if (!st->util_ldap_cache_lock) return;
2430 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2432 if (sts != APR_SUCCESS) {
2433 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2434 "Failed to initialise global mutex %s in child process %"
2436 st->lock_file, getpid());
2440 static const command_rec util_ldap_cmds[] = {
2441 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2443 "Set the size of the shared memory cache (in bytes). Use "
2444 "0 to disable the shared memory cache. (default: 100000)"),
2446 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2448 "Set the file name for the shared memory cache."),
2450 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2452 "Set the maximum number of entries that are possible in the "
2453 "LDAP search cache. Use 0 for no limit. "
2454 "-1 disables the cache. (default: 1024)"),
2456 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2458 "Set the maximum time (in seconds) that an item can be "
2459 "cached in the LDAP search cache. Use 0 for no limit. "
2462 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2464 "Set the maximum number of entries that are possible "
2465 "in the LDAP compare cache. Use 0 for no limit. "
2466 "Use -1 to disable the cache. (default: 1024)"),
2468 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2470 "Set the maximum time (in seconds) that an item is cached "
2471 "in the LDAP operation cache. Use 0 for no limit. "
2474 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2476 "Takes three args; the file and/or directory containing "
2477 "the trusted CA certificates (and global client certs "
2478 "for Netware) used to validate the LDAP server. Second "
2479 "arg is the cert type for the first arg, one of CA_DER, "
2480 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2481 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2482 "Third arg is an optional passphrase if applicable."),
2484 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2486 "Takes three args; the file and/or directory containing "
2487 "the client certificate, or certificate ID used to "
2488 "validate this LDAP client. Second arg is the cert type "
2489 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2490 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2491 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2492 "optional passphrase if applicable."),
2494 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2496 "Specify the type of security that should be applied to "
2497 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2499 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2501 "Set to 'ON' requires that the server certificate be verified"
2502 " before a secure LDAP connection can be establish. Default"
2505 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2507 "Specify the LDAP socket connection timeout in seconds "
2513 static void util_ldap_register_hooks(apr_pool_t *p)
2515 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2516 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2517 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2518 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2519 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2520 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2521 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2522 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2523 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2524 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2525 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2527 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2528 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2529 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2532 module AP_MODULE_DECLARE_DATA ldap_module = {
2533 STANDARD20_MODULE_STUFF,
2534 NULL, /* create dir config */
2535 NULL, /* merge dir config */
2536 util_ldap_create_config, /* create server config */
2537 util_ldap_merge_config, /* merge server config */
2538 util_ldap_cmds, /* command table */
2539 util_ldap_register_hooks, /* set up request processing hooks */