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,
273 APR_LDAP_OPT_VERIFY_CERT, &(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 different
714 * cache node: require group includes the DN; require user does not because the
715 * 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, &the_compare_node);
838 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] cache_compare: Cache insertion failure.", getpid());
842 compare_nodep->lastcompare = curtime;
843 compare_nodep->result = result;
847 if (LDAP_COMPARE_TRUE == result) {
848 ldc->reason = "Comparison true (adding to cache)";
849 return LDAP_COMPARE_TRUE;
851 else if (LDAP_COMPARE_FALSE == result) {
852 ldc->reason = "Comparison false (adding to cache)";
853 return LDAP_COMPARE_FALSE;
856 ldc->reason = "Comparison no such attribute (adding to cache)";
857 return LDAP_NO_SUCH_ATTRIBUTE;
864 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r, util_ldap_connection_t *ldc,
865 const char *url, const char *dn,
866 char **subgroupAttrs, apr_array_header_t *subgroupclasses) {
868 int result = LDAP_COMPARE_FALSE;
869 util_compare_subgroup_t *res = NULL;
870 LDAPMessage *sga_res, *entry;
871 struct mod_auth_ldap_groupattr_entry_t *sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
872 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
874 if (!subgroupAttrs) {
879 /* 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups. */
880 if (failures++ > 10) {
881 /* too many failures */
885 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
890 /* try to do the search */
891 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
892 (char *)"cn=*", subgroupAttrs, 0,
893 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
894 if (result == LDAP_SERVER_DOWN) {
895 ldc->reason = "ldap_search_ext_s() for subgroups failed with server down";
896 uldap_connection_unbind(ldc);
900 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
901 if (result != LDAP_SUCCESS) {
902 ldc->reason = "ldap_search_ext_s() for subgroups failed";
906 entry = ldap_first_entry(ldc->ldap, sga_res);
909 * Get values for the provided sub-group attributes.
912 int indx = 0, tmp_sgcIndex;
914 while (subgroupAttrs[indx]) {
918 /* Get *all* matching "member" values from this group. */
919 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
924 * Now we are going to pare the subgroup members of this group to *just*
925 * the subgroups, add them to the compare_nodep, and then proceed to check
926 * the new level of subgroups.
928 while (values[val_index]) {
929 /* Check if this entry really is a group. */
931 result = LDAP_COMPARE_FALSE;
932 while ((tmp_sgcIndex < subgroupclasses->nelts) && (result != LDAP_COMPARE_TRUE)) {
933 result = uldap_cache_compare(r, ldc, url, values[val_index], "objectClass",
934 sgc_ents[tmp_sgcIndex].name);
936 if (result != LDAP_COMPARE_TRUE) {
940 /* It's a group, so add it to the array. */
941 if (result == LDAP_COMPARE_TRUE) {
942 char **newgrp = (char **) apr_array_push(subgroups);
943 *newgrp = apr_pstrdup(r->pool, values[val_index]);
947 ldap_value_free(values);
953 ldap_msgfree(sga_res);
955 if (subgroups->nelts > 0) {
956 /* We need to fill in tmp_local_subgroups using the data from LDAP */
959 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
960 res->subgroupDNs = apr_pcalloc(r->pool, sizeof(char *) * (subgroups->nelts));
961 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
962 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
972 * Does a recursive lookup operation to try to find a user within (cached) nested
973 * groups. It accepts a cache that it will use to lookup previous compare attempts.
974 * We cache two kinds of compares (require group compares) and (require user
975 * compares). Each compare has a different cache node: require group includes the DN;
976 * require user does not because the require user cache is owned by the
978 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
982 * 1. Call uldap_cache_compare for each subgroupclass value to check the generic,
983 * user-agnostic, cached group entry. This will create a new generic cache entry if there
984 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we have no groups.
985 * 2. Lock The cache and get the generic cache entry.
986 * 3. Check if there is already a subgrouplist in this generic group's cache entry.
987 * A. If there is, go to step 4.
989 * i) Use ldap_search to get the full list
990 * of subgroup "members" (which may include non-group "members").
991 * ii) Use uldap_cache_compare to strip the list down to just groups.
992 * iii) Lock and add this stripped down list to the cache of the generic group.
993 * 4. Loop through the sgl and call uldap_cache_compare (using the user info) for each
994 * subgroup to see if the subgroup contains the user and to get the subgroups added to the
995 * cache (with user-afinity, if they aren't already there).
996 * A. If the user is in the subgroup, then we'll be returning LDAP_COMPARE_TRUE.
997 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via uldap_cache_compare) then
998 * recursively call this function to get the sub-subgroups added...
999 * 5. Cleanup local allocations.
1000 * 6. Return the final result.
1003 static int uldap_cache_check_subgroups(request_rec *r, util_ldap_connection_t *ldc,
1004 const char *url, const char *dn,
1005 const char *attrib, const char *value,
1006 char **subgroupAttrs, apr_array_header_t *subgroupclasses,
1007 int cur_subgroup_depth, int max_subgroup_depth)
1009 int result = LDAP_COMPARE_FALSE;
1010 util_url_node_t *curl;
1011 util_url_node_t curnode;
1012 util_compare_node_t *compare_nodep;
1013 util_compare_node_t the_compare_node;
1014 util_compare_subgroup_t *tmp_local_sgl = NULL;
1015 int lcl_sgl_processedFlag = 0, failures = 0, sgindex = 0, base_sgcIndex = 0;
1016 struct mod_auth_ldap_groupattr_entry_t *sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1018 util_ldap_state_t *st = (util_ldap_state_t *)
1019 ap_get_module_config(r->server->module_config,
1022 /* Stop looking at deeper levels of nested groups if we have reached the max.
1023 * Since we already checked the top-level group in uldap_cache_compare, we don't
1024 * need to check it again here - so if max_subgroup_depth is set to 0, we won't
1025 * check it (i.e. that is why we check < rather than <=).
1026 * We'll be calling uldap_cache_compare from here to check if the user is in the
1027 * next level before we recurse into that next level looking for more subgroups.
1029 if (cur_subgroup_depth >= max_subgroup_depth) {
1030 return LDAP_COMPARE_FALSE;
1033 /* 1. Check the "groupiness" of the specified basedn. Stopping at the first TRUE return. */
1034 while ((base_sgcIndex < subgroupclasses->nelts) && (result != LDAP_COMPARE_TRUE)) {
1035 result = uldap_cache_compare(r, ldc, url, dn, "objectClass", sgc_ents[base_sgcIndex].name);
1036 if (result != LDAP_COMPARE_TRUE) {
1041 if (result != LDAP_COMPARE_TRUE) {
1042 ldc->reason = "DN failed group verification.";
1046 /* 2. Find previously created cache entry and check if there is already a subgrouplist. */
1049 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1050 LDAP_CACHE_UNLOCK();
1052 if (curl && curl->compare_cache) {
1053 /* make a comparison to the cache */
1056 the_compare_node.dn = (char *)dn;
1057 the_compare_node.attrib = (char *)"objectClass";
1058 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1059 the_compare_node.result = 0;
1060 the_compare_node.sgl_processed = 0;
1061 the_compare_node.subgroupList = NULL;
1063 compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
1065 if (compare_nodep != NULL) {
1066 /* Found the generic group entry... but the user isn't in this group or we wouldn't be here. */
1067 lcl_sgl_processedFlag = compare_nodep->sgl_processed;
1068 if(compare_nodep->sgl_processed && compare_nodep->subgroupList) {
1069 /* Make a local copy of the subgroup list */
1071 tmp_local_sgl = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1072 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1073 tmp_local_sgl->subgroupDNs = apr_pcalloc(r->pool, sizeof(char *) * compare_nodep->subgroupList->len);
1074 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1075 tmp_local_sgl->subgroupDNs[i] = apr_pstrdup(r->pool, compare_nodep->subgroupList->subgroupDNs[i]);
1079 LDAP_CACHE_UNLOCK();
1082 /* If we get here, something is wrong. Caches should have been created and
1083 this group entry should be found in the cache. */
1084 ldc->reason = "check_subgroups failed to find any caches.";
1085 return LDAP_COMPARE_FALSE;
1088 result = LDAP_COMPARE_FALSE;
1090 if ((lcl_sgl_processedFlag == 0) && (!tmp_local_sgl)) {
1091 /* No Cached SGL, retrieve from LDAP */
1092 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s, retrieving from LDAP" , getpid(), dn);
1093 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs, subgroupclasses);
1094 if (!tmp_local_sgl) {
1095 /* No SGL aailable via LDAP either */
1096 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] util_ldap: no subgroups for %s" , getpid(), dn);
1098 lcl_sgl_processedFlag = 1;
1100 /* Find the generic group cache entry and add the sgl we just retrieved. */
1103 the_compare_node.dn = (char *)dn;
1104 the_compare_node.attrib = (char *)"objectClass";
1105 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1106 the_compare_node.result = 0;
1107 the_compare_node.sgl_processed = 0;
1108 the_compare_node.subgroupList = NULL;
1110 compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
1112 if (compare_nodep == NULL) {
1113 /* Didn't find it. This shouldn't happen since we just called uldap_cache_compare. */
1114 LDAP_CACHE_UNLOCK();
1115 ldc->reason = "check_subgroups failed to find the cache entry to add sub-group list to.";
1116 return LDAP_COMPARE_FALSE;
1119 * overwrite SGL if it was previously updated between the last
1120 * two times we looked at the cache
1122 compare_nodep->sgl_processed = 1;
1123 if (tmp_local_sgl) {
1124 compare_nodep->subgroupList = util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1127 /* We didn't find a single subgroup, next time save us from looking */
1128 compare_nodep->subgroupList = NULL;
1130 LDAP_CACHE_UNLOCK();
1133 /* tmp_local_sgl has either been created, or copied out of the cache */
1134 /* If tmp_local_sgl is NULL, there are no subgroups to process and we'll return false */
1135 result = LDAP_COMPARE_FALSE;
1136 if (!tmp_local_sgl) {
1140 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1141 const char *group = NULL;
1142 group = tmp_local_sgl->subgroupDNs[sgindex];
1143 /* 4. Now loop through the subgroupList and call uldap_cache_compare to check for the user. */
1144 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1145 if (result == LDAP_COMPARE_TRUE) {
1146 /* 4.A. We found the user in the subgroup. Return LDAP_COMPARE_TRUE. */
1147 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] util_ldap:"
1148 " Found user %s in a subgroup (%s) at level %d of %d.",
1149 getpid(), r->user, group, cur_subgroup_depth+1, max_subgroup_depth);
1152 /* 4.B. We didn't find the user in this subgroup, so recurse into it and keep looking. */
1153 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] util_ldap:"
1154 " user %s not found in subgroup (%s) at level %d of %d.",
1155 getpid(), r->user, group, cur_subgroup_depth+1, max_subgroup_depth);
1156 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1157 value, subgroupAttrs, subgroupclasses,
1158 cur_subgroup_depth+1, max_subgroup_depth);
1167 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1168 const char *url, const char *basedn,
1169 int scope, char **attrs, const char *filter,
1170 const char *bindpw, const char **binddn,
1171 const char ***retvals)
1173 const char **vals = NULL;
1176 LDAPMessage *res, *entry;
1180 util_url_node_t *curl; /* Cached URL node */
1181 util_url_node_t curnode;
1182 util_search_node_t *search_nodep; /* Cached search node */
1183 util_search_node_t the_search_node;
1186 util_ldap_state_t *st =
1187 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1190 /* Get the cache node for this url */
1193 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1196 curl = util_ald_create_caches(st, url);
1198 LDAP_CACHE_UNLOCK();
1202 the_search_node.username = filter;
1203 search_nodep = util_ald_cache_fetch(curl->search_cache,
1205 if (search_nodep != NULL) {
1207 /* found entry in search cache... */
1208 curtime = apr_time_now();
1211 * Remove this item from the cache if its expired. If the sent
1212 * password doesn't match the storepassword, the entry will
1213 * be removed and readded later if the credentials pass
1216 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1217 /* ...but entry is too old */
1218 util_ald_cache_remove(curl->search_cache, search_nodep);
1220 else if ( (search_nodep->bindpw)
1221 && (search_nodep->bindpw[0] != '\0')
1222 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1224 /* ...and entry is valid */
1225 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1229 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1230 while (search_nodep->vals[i]) {
1231 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1235 LDAP_CACHE_UNLOCK();
1236 ldc->reason = "Authentication successful (cached)";
1237 return LDAP_SUCCESS;
1240 /* unlock this read lock */
1241 LDAP_CACHE_UNLOCK();
1245 * At this point, there is no valid cached search, so lets do the search.
1249 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1252 if (failures++ > 10) {
1255 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1259 /* try do the search */
1260 if ((result = ldap_search_ext_s(ldc->ldap,
1261 (char *)basedn, scope,
1262 (char *)filter, attrs, 0,
1263 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1264 == LDAP_SERVER_DOWN)
1266 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1267 uldap_connection_unbind(ldc);
1271 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1272 if (result != LDAP_SUCCESS) {
1273 ldc->reason = "ldap_search_ext_s() for user failed";
1278 * We should have found exactly one entry; to find a different
1279 * number is an error.
1281 count = ldap_count_entries(ldc->ldap, res);
1285 ldc->reason = "User not found";
1287 ldc->reason = "User is not unique (search found two "
1290 return LDAP_NO_SUCH_OBJECT;
1293 entry = ldap_first_entry(ldc->ldap, res);
1295 /* Grab the dn, copy it into the pool, and free it again */
1296 dn = ldap_get_dn(ldc->ldap, entry);
1297 *binddn = apr_pstrdup(r->pool, dn);
1301 * A bind to the server with an empty password always succeeds, so
1302 * we check to ensure that the password is not empty. This implies
1303 * that users who actually do have empty passwords will never be
1304 * able to authenticate with this module. I don't see this as a big
1307 if (!bindpw || strlen(bindpw) <= 0) {
1309 ldc->reason = "Empty password not allowed";
1310 return LDAP_INVALID_CREDENTIALS;
1314 * Attempt to bind with the retrieved dn and the password. If the bind
1315 * fails, it means that the password is wrong (the dn obviously
1316 * exists, since we just retrieved it)
1318 if ((result = ldap_simple_bind_s(ldc->ldap,
1320 (char *)bindpw)) == LDAP_SERVER_DOWN) {
1321 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1322 "failed with server down";
1324 uldap_connection_unbind(ldc);
1328 /* failure? if so - return */
1329 if (result != LDAP_SUCCESS) {
1330 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1332 uldap_connection_unbind(ldc);
1337 * We have just bound the connection to a different user and password
1338 * combination, which might be reused unintentionally next time this
1339 * connection is used from the connection pool. To ensure no confusion,
1340 * we mark the connection as unbound.
1346 * Get values for the provided attributes.
1352 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1359 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1360 while (values && values[j]) {
1361 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1362 : apr_pstrdup(r->pool, values[j]);
1365 ldap_value_free(values);
1373 * Add the new username to the search cache.
1377 the_search_node.username = filter;
1378 the_search_node.dn = *binddn;
1379 the_search_node.bindpw = bindpw;
1380 the_search_node.lastbind = apr_time_now();
1381 the_search_node.vals = vals;
1382 the_search_node.numvals = numvals;
1384 /* Search again to make sure that another thread didn't ready insert
1385 * this node into the cache before we got here. If it does exist then
1386 * update the lastbind
1388 search_nodep = util_ald_cache_fetch(curl->search_cache,
1390 if ((search_nodep == NULL) ||
1391 (strcmp(*binddn, search_nodep->dn) != 0)) {
1393 /* Nothing in cache, insert new entry */
1394 util_ald_cache_insert(curl->search_cache, &the_search_node);
1396 else if ((!search_nodep->bindpw) ||
1397 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1399 /* Entry in cache is invalid, remove it and insert new one */
1400 util_ald_cache_remove(curl->search_cache, search_nodep);
1401 util_ald_cache_insert(curl->search_cache, &the_search_node);
1404 /* Cache entry is valid, update lastbind */
1405 search_nodep->lastbind = the_search_node.lastbind;
1407 LDAP_CACHE_UNLOCK();
1411 ldc->reason = "Authentication successful";
1412 return LDAP_SUCCESS;
1416 * This function will return the DN of the entry matching userid.
1417 * It is used to get the DN in case some other module than mod_auth_ldap
1418 * has authenticated the user.
1419 * The function is basically a copy of uldap_cache_checkuserid
1420 * with password checking removed.
1422 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1423 const char *url, const char *basedn,
1424 int scope, char **attrs, const char *filter,
1425 const char **binddn, const char ***retvals)
1427 const char **vals = NULL;
1430 LDAPMessage *res, *entry;
1434 util_url_node_t *curl; /* Cached URL node */
1435 util_url_node_t curnode;
1436 util_search_node_t *search_nodep; /* Cached search node */
1437 util_search_node_t the_search_node;
1440 util_ldap_state_t *st =
1441 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1444 /* Get the cache node for this url */
1447 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1450 curl = util_ald_create_caches(st, url);
1452 LDAP_CACHE_UNLOCK();
1456 the_search_node.username = filter;
1457 search_nodep = util_ald_cache_fetch(curl->search_cache,
1459 if (search_nodep != NULL) {
1461 /* found entry in search cache... */
1462 curtime = apr_time_now();
1465 * Remove this item from the cache if its expired.
1467 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1468 /* ...but entry is too old */
1469 util_ald_cache_remove(curl->search_cache, search_nodep);
1472 /* ...and entry is valid */
1473 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1477 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1478 while (search_nodep->vals[i]) {
1479 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1483 LDAP_CACHE_UNLOCK();
1484 ldc->reason = "Search successful (cached)";
1485 return LDAP_SUCCESS;
1488 /* unlock this read lock */
1489 LDAP_CACHE_UNLOCK();
1493 * At this point, there is no valid cached search, so lets do the search.
1497 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1500 if (failures++ > 10) {
1503 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1507 /* try do the search */
1508 if ((result = ldap_search_ext_s(ldc->ldap,
1509 (char *)basedn, scope,
1510 (char *)filter, attrs, 0,
1511 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1512 == LDAP_SERVER_DOWN)
1514 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1515 uldap_connection_unbind(ldc);
1519 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1520 if (result != LDAP_SUCCESS) {
1521 ldc->reason = "ldap_search_ext_s() for user failed";
1526 * We should have found exactly one entry; to find a different
1527 * number is an error.
1529 count = ldap_count_entries(ldc->ldap, res);
1533 ldc->reason = "User not found";
1535 ldc->reason = "User is not unique (search found two "
1538 return LDAP_NO_SUCH_OBJECT;
1541 entry = ldap_first_entry(ldc->ldap, res);
1543 /* Grab the dn, copy it into the pool, and free it again */
1544 dn = ldap_get_dn(ldc->ldap, entry);
1545 *binddn = apr_pstrdup(r->pool, dn);
1549 * Get values for the provided attributes.
1555 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1562 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1563 while (values && values[j]) {
1564 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1565 : apr_pstrdup(r->pool, values[j]);
1568 ldap_value_free(values);
1576 * Add the new username to the search cache.
1580 the_search_node.username = filter;
1581 the_search_node.dn = *binddn;
1582 the_search_node.bindpw = NULL;
1583 the_search_node.lastbind = apr_time_now();
1584 the_search_node.vals = vals;
1585 the_search_node.numvals = numvals;
1587 /* Search again to make sure that another thread didn't ready insert
1588 * this node into the cache before we got here. If it does exist then
1589 * update the lastbind
1591 search_nodep = util_ald_cache_fetch(curl->search_cache,
1593 if ((search_nodep == NULL) ||
1594 (strcmp(*binddn, search_nodep->dn) != 0)) {
1596 /* Nothing in cache, insert new entry */
1597 util_ald_cache_insert(curl->search_cache, &the_search_node);
1600 * Don't update lastbind on entries with bindpw because
1601 * we haven't verified that password. It's OK to update
1602 * the entry if there is no password in it.
1604 else if (!search_nodep->bindpw) {
1605 /* Cache entry is valid, update lastbind */
1606 search_nodep->lastbind = the_search_node.lastbind;
1608 LDAP_CACHE_UNLOCK();
1613 ldc->reason = "Search successful";
1614 return LDAP_SUCCESS;
1618 * Reports if ssl support is enabled
1620 * 1 = enabled, 0 = not enabled
1622 static int uldap_ssl_supported(request_rec *r)
1624 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1625 r->server->module_config, &ldap_module);
1627 return(st->ssl_supported);
1631 /* ---------------------------------------- */
1632 /* config directives */
1635 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1638 util_ldap_state_t *st =
1639 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1641 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1647 st->cache_bytes = atol(bytes);
1649 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1650 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1651 " cache size to %" APR_SIZE_T_FMT " bytes.",
1652 getpid(), st->cache_bytes);
1657 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1660 util_ldap_state_t *st =
1661 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1663 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1670 st->cache_file = ap_server_root_relative(st->pool, file);
1673 st->cache_file = NULL;
1676 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1677 "LDAP cache: Setting shared memory cache file to %s bytes.",
1683 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1686 util_ldap_state_t *st =
1687 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1689 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1695 st->search_cache_ttl = atol(ttl) * 1000000;
1697 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1698 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld microseconds.",
1699 getpid(), st->search_cache_ttl);
1704 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1707 util_ldap_state_t *st =
1708 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1710 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1716 st->search_cache_size = atol(size);
1717 if (st->search_cache_size < 0) {
1718 st->search_cache_size = 0;
1721 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1722 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size to %ld entries.",
1723 getpid(), st->search_cache_size);
1728 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1731 util_ldap_state_t *st =
1732 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1734 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1740 st->compare_cache_ttl = atol(ttl) * 1000000;
1742 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1743 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL to %ld microseconds.",
1744 getpid(), st->compare_cache_ttl);
1749 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1752 util_ldap_state_t *st =
1753 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1755 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1761 st->compare_cache_size = atol(size);
1762 if (st->compare_cache_size < 0) {
1763 st->compare_cache_size = 0;
1766 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1767 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size to %ld "
1768 "entries.", getpid(), st->compare_cache_size);
1775 * Parse the certificate type.
1777 * The type can be one of the following:
1778 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1779 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1781 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1783 static int util_ldap_parse_cert_type(const char *type)
1785 /* Authority file in binary DER format */
1786 if (0 == strcasecmp("CA_DER", type)) {
1787 return APR_LDAP_CA_TYPE_DER;
1790 /* Authority file in Base64 format */
1791 else if (0 == strcasecmp("CA_BASE64", type)) {
1792 return APR_LDAP_CA_TYPE_BASE64;
1795 /* Netscape certificate database file/directory */
1796 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1797 return APR_LDAP_CA_TYPE_CERT7_DB;
1800 /* Netscape secmod file/directory */
1801 else if (0 == strcasecmp("CA_SECMOD", type)) {
1802 return APR_LDAP_CA_TYPE_SECMOD;
1805 /* Client cert file in DER format */
1806 else if (0 == strcasecmp("CERT_DER", type)) {
1807 return APR_LDAP_CERT_TYPE_DER;
1810 /* Client cert file in Base64 format */
1811 else if (0 == strcasecmp("CERT_BASE64", type)) {
1812 return APR_LDAP_CERT_TYPE_BASE64;
1815 /* Client cert file in PKCS#12 format */
1816 else if (0 == strcasecmp("CERT_PFX", type)) {
1817 return APR_LDAP_CERT_TYPE_PFX;
1820 /* Netscape client cert database file/directory */
1821 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1822 return APR_LDAP_CERT_TYPE_KEY3_DB;
1825 /* Netscape client cert nickname */
1826 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1827 return APR_LDAP_CERT_TYPE_NICKNAME;
1830 /* Client cert key file in DER format */
1831 else if (0 == strcasecmp("KEY_DER", type)) {
1832 return APR_LDAP_KEY_TYPE_DER;
1835 /* Client cert key file in Base64 format */
1836 else if (0 == strcasecmp("KEY_BASE64", type)) {
1837 return APR_LDAP_KEY_TYPE_BASE64;
1840 /* Client cert key file in PKCS#12 format */
1841 else if (0 == strcasecmp("KEY_PFX", type)) {
1842 return APR_LDAP_KEY_TYPE_PFX;
1846 return APR_LDAP_CA_TYPE_UNKNOWN;
1853 * Set LDAPTrustedGlobalCert.
1855 * This directive takes either two or three arguments:
1856 * - certificate type
1857 * - certificate file / directory / nickname
1858 * - certificate password (optional)
1860 * This directive may only be used globally.
1862 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
1866 const char *password)
1868 util_ldap_state_t *st =
1869 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1871 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1875 apr_ldap_opt_tls_cert_t *cert;
1881 /* handle the certificate type */
1883 cert_type = util_ldap_parse_cert_type(type);
1884 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1885 return apr_psprintf(cmd->pool, "The certificate type %s is "
1886 "not recognised. It should be one "
1887 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1888 "CA_SECMOD, CERT_DER, CERT_BASE64, "
1889 "CERT_KEY3_DB, CERT_NICKNAME, "
1890 "KEY_DER, KEY_BASE64", type);
1894 return "Certificate type was not specified.";
1897 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1898 "LDAP: SSL trusted global cert - %s (type %s)",
1901 /* add the certificate to the global array */
1902 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1903 cert->type = cert_type;
1905 cert->password = password;
1907 /* if file is a file or path, fix the path */
1908 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1909 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1911 cert->path = ap_server_root_relative(cmd->pool, file);
1913 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1916 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1917 "LDAP: Could not open SSL trusted certificate "
1918 "authority file - %s",
1919 cert->path == NULL ? file : cert->path);
1920 return "Invalid global certificate file path";
1929 * Set LDAPTrustedClientCert.
1931 * This directive takes either two or three arguments:
1932 * - certificate type
1933 * - certificate file / directory / nickname
1934 * - certificate password (optional)
1936 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
1940 const char *password)
1942 util_ldap_state_t *st =
1943 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1948 apr_ldap_opt_tls_cert_t *cert;
1950 /* handle the certificate type */
1952 cert_type = util_ldap_parse_cert_type(type);
1953 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1954 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1955 "not recognised. It should be one "
1956 "of CERT_DER, CERT_BASE64, "
1957 "CERT_NICKNAME, CERT_PFX,"
1958 "KEY_DER, KEY_BASE64, KEY_PFX",
1961 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1962 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1963 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1964 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1965 APR_LDAP_CERT_TYPE_PFX == cert_type ||
1966 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1967 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1968 "only valid within a "
1969 "LDAPTrustedGlobalCert directive. "
1970 "Only CERT_DER, CERT_BASE64, "
1971 "CERT_NICKNAME, KEY_DER, and "
1972 "KEY_BASE64 may be used.", type);
1976 return "Certificate type was not specified.";
1979 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1980 "LDAP: SSL trusted client cert - %s (type %s)",
1983 /* add the certificate to the global array */
1984 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1985 cert->type = cert_type;
1987 cert->password = password;
1989 /* if file is a file or path, fix the path */
1990 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1991 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1993 cert->path = ap_server_root_relative(cmd->pool, file);
1995 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1998 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1999 "LDAP: Could not open SSL client certificate "
2001 cert->path == NULL ? file : cert->path);
2002 return "Invalid client certificate file path";
2012 * Set LDAPTrustedMode.
2014 * This directive sets what encryption mode to use on a connection:
2015 * - None (No encryption)
2016 * - SSL (SSL encryption)
2017 * - STARTTLS (TLS encryption)
2019 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2022 util_ldap_state_t *st =
2023 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2026 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2027 "LDAP: SSL trusted mode - %s",
2030 if (0 == strcasecmp("NONE", mode)) {
2031 st->secure = APR_LDAP_NONE;
2033 else if (0 == strcasecmp("SSL", mode)) {
2034 st->secure = APR_LDAP_SSL;
2036 else if ( (0 == strcasecmp("TLS", mode))
2037 || (0 == strcasecmp("STARTTLS", mode))) {
2038 st->secure = APR_LDAP_STARTTLS;
2041 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2042 "SSL, or TLS/STARTTLS";
2049 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2053 util_ldap_state_t *st =
2054 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2056 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2062 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2063 "LDAP: SSL verify server certificate - %s",
2064 mode?"TRUE":"FALSE");
2066 st->verify_svr_cert = mode;
2072 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2076 util_ldap_state_t *st =
2077 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2079 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2085 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2086 st->connectionTimeout = atol(ttl);
2088 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2089 "[%" APR_PID_T_FMT "] ldap connection: Setting connection timeout to "
2090 "%ld seconds.", getpid(), st->connectionTimeout);
2092 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2093 "LDAP: Connection timout option not supported by the "
2094 "LDAP SDK in use." );
2101 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2103 util_ldap_state_t *st =
2104 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2106 /* Create a per vhost pool for mod_ldap to use, serialized with
2107 * st->mutex (also one per vhost). both are replicated by fork(),
2108 * no shared memory managed by either.
2110 apr_pool_create(&st->pool, p);
2112 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2115 st->cache_bytes = 100000;
2116 st->search_cache_ttl = 600000000;
2117 st->search_cache_size = 1024;
2118 st->compare_cache_ttl = 600000000;
2119 st->compare_cache_size = 1024;
2120 st->connections = NULL;
2121 st->ssl_supported = 0;
2122 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2123 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2124 st->secure = APR_LDAP_NONE;
2126 st->connectionTimeout = 10;
2127 st->verify_svr_cert = 1;
2132 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2135 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2136 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2137 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2139 st->pool = overrides->pool;
2141 st->mutex = overrides->mutex;
2144 /* The cache settings can not be modified in a
2145 virtual host since all server use the same
2146 shared memory cache. */
2147 st->cache_bytes = base->cache_bytes;
2148 st->search_cache_ttl = base->search_cache_ttl;
2149 st->search_cache_size = base->search_cache_size;
2150 st->compare_cache_ttl = base->compare_cache_ttl;
2151 st->compare_cache_size = base->compare_cache_size;
2152 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2154 st->connections = NULL;
2155 st->ssl_supported = 0;
2156 st->global_certs = apr_array_append(p, base->global_certs,
2157 overrides->global_certs);
2158 st->client_certs = apr_array_append(p, base->client_certs,
2159 overrides->client_certs);
2160 st->secure = (overrides->secure_set == 0) ? base->secure
2161 : overrides->secure;
2163 /* These LDAP connection settings can not be overwritten in
2164 a virtual host. Once set in the base server, they must
2165 remain the same. None of the LDAP SDKs seem to be able
2166 to handle setting the verify_svr_cert flag on a
2167 per-connection basis. The OpenLDAP client appears to be
2168 able to handle the connection timeout per-connection
2169 but the Novell SDK cannot. Allowing the timeout to
2170 be set by each vhost is of little value so rather than
2171 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2172 is being enforced on this setting as well. */
2173 st->connectionTimeout = base->connectionTimeout;
2174 st->verify_svr_cert = base->verify_svr_cert;
2179 static apr_status_t util_ldap_cleanup_module(void *data)
2182 server_rec *s = data;
2183 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2184 s->module_config, &ldap_module);
2186 if (st->ssl_supported) {
2187 apr_ldap_ssl_deinit();
2194 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2195 apr_pool_t *ptemp, server_rec *s)
2197 apr_status_t result;
2198 server_rec *s_vhost;
2199 util_ldap_state_t *st_vhost;
2201 util_ldap_state_t *st = (util_ldap_state_t *)
2202 ap_get_module_config(s->module_config,
2206 const char *userdata_key = "util_ldap_init";
2207 apr_ldap_err_t *result_err = NULL;
2210 /* util_ldap_post_config() will be called twice. Don't bother
2211 * going through all of the initialization on the first call
2212 * because it will just be thrown away.*/
2213 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2215 apr_pool_userdata_set((const void *)1, userdata_key,
2216 apr_pool_cleanup_null, s->process->pool);
2218 #if APR_HAS_SHARED_MEMORY
2219 /* If the cache file already exists then delete it. Otherwise we are
2220 * going to run into problems creating the shared memory. */
2221 if (st->cache_file) {
2222 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2224 apr_file_remove(lck_file, ptemp);
2230 #if APR_HAS_SHARED_MEMORY
2231 /* initializing cache if shared memory size is not zero and we already
2232 * don't have shm address
2234 if (!st->cache_shm && st->cache_bytes > 0) {
2236 result = util_ldap_cache_init(p, st);
2237 if (result != APR_SUCCESS) {
2238 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2239 "LDAP cache: could not create shared memory segment");
2244 #if APR_HAS_SHARED_MEMORY
2245 if (st->cache_file) {
2246 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2251 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2252 st->lock_file, APR_LOCK_DEFAULT,
2254 if (result != APR_SUCCESS) {
2258 #ifdef AP_NEED_SET_MUTEX_PERMS
2259 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2260 if (result != APR_SUCCESS) {
2261 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2262 "LDAP cache: failed to set mutex permissions");
2267 /* merge config in all vhost */
2270 st_vhost = (util_ldap_state_t *)
2271 ap_get_module_config(s_vhost->module_config,
2274 #if APR_HAS_SHARED_MEMORY
2275 st_vhost->cache_shm = st->cache_shm;
2276 st_vhost->cache_rmm = st->cache_rmm;
2277 st_vhost->cache_file = st->cache_file;
2278 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2279 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2280 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2281 s_vhost->server_hostname);
2283 st_vhost->lock_file = st->lock_file;
2284 s_vhost = s_vhost->next;
2286 #if APR_HAS_SHARED_MEMORY
2289 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2290 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2291 "shared memory cache");
2295 /* log the LDAP SDK used
2298 apr_ldap_err_t *result = NULL;
2299 apr_ldap_info(p, &(result));
2300 if (result != NULL) {
2301 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2305 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2306 util_ldap_cleanup_module);
2309 * Initialize SSL support, and log the result for the benefit of the admin.
2311 * If SSL is not supported it is not necessarily an error, as the
2312 * application may not want to use it.
2314 rc = apr_ldap_ssl_init(p,
2318 if (APR_SUCCESS == rc) {
2319 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2320 (void *)st->global_certs, &(result_err));
2323 if (APR_SUCCESS == rc) {
2324 st->ssl_supported = 1;
2325 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2326 "LDAP: SSL support available" );
2329 st->ssl_supported = 0;
2330 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2331 "LDAP: SSL support unavailable%s%s",
2332 result_err ? ": " : "",
2333 result_err ? result_err->reason : "");
2339 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2342 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2345 if (!st->util_ldap_cache_lock) return;
2347 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2349 if (sts != APR_SUCCESS) {
2350 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2351 "Failed to initialise global mutex %s in child process %"
2353 st->lock_file, getpid());
2357 static const command_rec util_ldap_cmds[] = {
2358 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2360 "Set the size of the shared memory cache (in bytes). Use "
2361 "0 to disable the shared memory cache. (default: 100000)"),
2363 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2365 "Set the file name for the shared memory cache."),
2367 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2369 "Set the maximum number of entries that are possible in the "
2370 "LDAP search cache. Use 0 for no limit. "
2371 "-1 disables the cache. (default: 1024)"),
2373 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2375 "Set the maximum time (in seconds) that an item can be "
2376 "cached in the LDAP search cache. Use 0 for no limit. "
2379 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2381 "Set the maximum number of entries that are possible "
2382 "in the LDAP compare cache. Use 0 for no limit. "
2383 "Use -1 to disable the cache. (default: 1024)"),
2385 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2387 "Set the maximum time (in seconds) that an item is cached "
2388 "in the LDAP operation cache. Use 0 for no limit. "
2391 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2393 "Takes three args; the file and/or directory containing "
2394 "the trusted CA certificates (and global client certs "
2395 "for Netware) used to validate the LDAP server. Second "
2396 "arg is the cert type for the first arg, one of CA_DER, "
2397 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2398 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2399 "Third arg is an optional passphrase if applicable."),
2401 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2403 "Takes three args; the file and/or directory containing "
2404 "the client certificate, or certificate ID used to "
2405 "validate this LDAP client. Second arg is the cert type "
2406 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2407 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2408 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2409 "optional passphrase if applicable."),
2411 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2413 "Specify the type of security that should be applied to "
2414 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2416 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2418 "Set to 'ON' requires that the server certificate be verified "
2419 "before a secure LDAP connection can be establish. Default 'ON'"),
2421 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2423 "Specify the LDAP socket connection timeout in seconds "
2429 static void util_ldap_register_hooks(apr_pool_t *p)
2431 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2432 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2433 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2434 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2435 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2436 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2437 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2438 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2439 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2440 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2441 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2443 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2444 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2445 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2448 module AP_MODULE_DECLARE_DATA ldap_module = {
2449 STANDARD20_MODULE_STUFF,
2450 NULL, /* create dir config */
2451 NULL, /* merge dir config */
2452 util_ldap_create_config, /* create server config */
2453 util_ldap_merge_config, /* merge server config */
2454 util_ldap_cmds, /* command table */
2455 util_ldap_register_hooks, /* set up request processing hooks */