1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * util_ldap.c: LDAP things
20 * Original code from auth_ldap module for Apache v1.3:
21 * Copyright 1998, 1999 Enbridge Pipelines Inc.
22 * Copyright 1999-2001 Dave Carrigan
26 #include "http_config.h"
27 #include "http_core.h"
29 #include "http_protocol.h"
30 #include "http_request.h"
31 #include "util_ldap.h"
32 #include "util_ldap_cache.h"
34 #include <apr_strings.h>
41 #error mod_ldap requires APR-util to have LDAP support built in
44 #ifdef AP_NEED_SET_MUTEX_PERMS
48 /* Default define for ldap functions that need a SIZELIMIT but
49 * do not have the define
50 * XXX This should be removed once a supporting #define is
51 * released through APR-Util.
53 #ifndef APR_LDAP_SIZELIMIT
54 #define APR_LDAP_SIZELIMIT -1
57 module AP_MODULE_DECLARE_DATA ldap_module;
59 #define LDAP_CACHE_LOCK() do { \
60 if (st->util_ldap_cache_lock) \
61 apr_global_mutex_lock(st->util_ldap_cache_lock); \
64 #define LDAP_CACHE_UNLOCK() do { \
65 if (st->util_ldap_cache_lock) \
66 apr_global_mutex_unlock(st->util_ldap_cache_lock); \
69 static apr_status_t util_ldap_connection_remove (void *param);
71 static void util_ldap_strdup (char **str, const char *newstr)
79 *str = strdup(newstr);
87 * This handler generates a status page about the current performance of
88 * the LDAP cache. It is enabled as follows:
90 * <Location /ldap-status>
91 * SetHandler ldap-status
95 static int util_ldap_handler(request_rec *r)
97 util_ldap_state_t *st = (util_ldap_state_t *)
98 ap_get_module_config(r->server->module_config,
101 r->allowed |= (1 << M_GET);
102 if (r->method_number != M_GET)
105 if (strcmp(r->handler, "ldap-status")) {
109 ap_set_content_type(r, "text/html");
114 ap_rputs(DOCTYPE_HTML_3_2
115 "<html><head><title>LDAP Cache Information</title></head>\n", r);
116 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
119 util_ald_cache_display(r, st);
126 /* ------------------------------------------------------------------ */
128 * Closes an LDAP connection by unlocking it. The next time
129 * uldap_connection_find() is called this connection will be
130 * available for reuse.
132 static void uldap_connection_close(util_ldap_connection_t *ldc)
138 * Is it safe leaving bound connections floating around between the
139 * different modules? Keeping the user bound is a performance boost,
140 * but it is also a potential security problem - maybe.
142 * For now we unbind the user when we finish with a connection, but
143 * we don't have to...
147 util_ldap_connection_remove(ldc);
150 /* mark our connection as available for reuse */
152 apr_thread_mutex_unlock(ldc->lock);
159 * Destroys an LDAP connection by unbinding and closing the connection to
160 * the LDAP server. It is used to bring the connection back to a known
161 * state after an error.
163 static apr_status_t uldap_connection_unbind(void *param)
165 util_ldap_connection_t *ldc = param;
169 ldap_unbind_s(ldc->ldap);
180 * Clean up an LDAP connection by unbinding and unlocking the connection.
181 * This cleanup does not remove the util_ldap_connection_t from the
182 * per-virtualhost list of connections, does not remove the storage
183 * for the util_ldap_connection_t or it's data, and is NOT run automatically.
185 static apr_status_t uldap_connection_cleanup(void *param)
187 util_ldap_connection_t *ldc = param;
191 /* unbind and disconnect from the LDAP server */
192 uldap_connection_unbind(ldc);
194 /* free the username and password */
196 free((void*)ldc->bindpw);
199 free((void*)ldc->binddn);
202 /* unlock this entry */
203 uldap_connection_close(ldc);
211 * util_ldap_connection_remove frees all storage associated with the LDAP
212 * connection and removes it completely from the per-virtualhost list of
215 * The caller should hold the lock for this connection
217 static apr_status_t util_ldap_connection_remove (void *param) {
218 util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
219 util_ldap_state_t *st = ldc->st;
221 if (!ldc) return APR_SUCCESS;
223 uldap_connection_unbind(ldc);
226 apr_thread_mutex_lock(st->mutex);
229 /* Remove ldc from the list */
230 for (l=st->connections; l; l=l->next) {
233 prev->next = l->next;
236 st->connections = l->next;
243 /* Some unfortunate duplication between this method
244 * and uldap_connection_cleanup()
247 free((void*)ldc->bindpw);
250 free((void*)ldc->binddn);
254 apr_thread_mutex_unlock(ldc->lock);
255 apr_thread_mutex_unlock(st->mutex);
258 /* Destory the pool associated with this connection */
260 apr_pool_destroy(ldc->pool);
265 static int uldap_connection_init(request_rec *r,
266 util_ldap_connection_t *ldc )
268 int rc = 0, ldap_option = 0;
269 int version = LDAP_VERSION3;
270 apr_ldap_err_t *result = NULL;
271 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
272 util_ldap_state_t *st =
273 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
276 /* Since the host will include a port if the default port is not used,
277 * always specify the default ports for the port parameter. This will
278 * allow a host string that contains multiple hosts the ability to mix
279 * some hosts with ports and some without. All hosts which do not
280 * specify a port will use the default port.
282 apr_ldap_init(r->pool, &(ldc->ldap),
284 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
289 if (result != NULL && result->rc) {
290 ldc->reason = result->reason;
293 if (NULL == ldc->ldap)
296 if (NULL == ldc->reason) {
297 ldc->reason = "LDAP: ldap initialization failed";
300 ldc->reason = result->reason;
305 /* always default to LDAP V3 */
306 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
308 /* set client certificates */
309 if (!apr_is_empty_array(ldc->client_certs)) {
310 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
311 ldc->client_certs, &(result));
312 if (LDAP_SUCCESS != result->rc) {
313 uldap_connection_unbind( ldc );
314 ldc->reason = result->reason;
319 /* switch on SSL/TLS */
320 if (APR_LDAP_NONE != ldc->secure) {
321 apr_ldap_set_option(r->pool, ldc->ldap,
322 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
323 if (LDAP_SUCCESS != result->rc) {
324 uldap_connection_unbind( ldc );
325 ldc->reason = result->reason;
330 /* Set the alias dereferencing option */
331 ldap_option = ldc->deref;
332 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
334 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
335 #ifdef APR_LDAP_OPT_VERIFY_CERT
336 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
337 &(st->verify_svr_cert), &(result));
339 #if defined(LDAPSSL_VERIFY_SERVER)
340 if (st->verify_svr_cert) {
341 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
344 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
346 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
347 /* This is not a per-connection setting so just pass NULL for the
348 Ldap connection handle */
349 if (st->verify_svr_cert) {
350 int i = LDAP_OPT_X_TLS_DEMAND;
351 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
354 int i = LDAP_OPT_X_TLS_NEVER;
355 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
360 #ifdef LDAP_OPT_NETWORK_TIMEOUT
361 if (st->connectionTimeout > 0) {
362 timeOut.tv_sec = st->connectionTimeout;
365 if (st->connectionTimeout >= 0) {
366 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
367 (void *)&timeOut, &(result));
368 if (APR_SUCCESS != rc) {
369 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
370 "LDAP: Could not set the connection timeout");
379 * Connect to the LDAP server and binds. Does not connect if already
380 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
382 * Returns LDAP_SUCCESS on success; and an error code on failure
384 static int uldap_connection_open(request_rec *r,
385 util_ldap_connection_t *ldc)
390 /* sanity check for NULL */
395 /* If the connection is already bound, return
399 ldc->reason = "LDAP: connection open successful (already bound)";
403 /* create the ldap session handle
405 if (NULL == ldc->ldap)
407 rc = uldap_connection_init( r, ldc );
408 if (LDAP_SUCCESS != rc)
415 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
416 * returned. Break out of the loop on Success or any other error.
418 * NOTE: Looping is probably not a great idea. If the server isn't
419 * responding the chances it will respond after a few tries are poor.
420 * However, the original code looped and it only happens on
421 * the error condition.
423 for (failures=0; failures<10; failures++)
425 rc = ldap_simple_bind_s(ldc->ldap,
427 (char *)ldc->bindpw);
428 if (LDAP_SERVER_DOWN != rc) {
430 } else if (failures == 5) {
431 /* attempt to init the connection once again */
432 uldap_connection_unbind( ldc );
433 rc = uldap_connection_init( r, ldc );
434 if (LDAP_SUCCESS != rc)
441 /* free the handle if there was an error
443 if (LDAP_SUCCESS != rc)
445 uldap_connection_unbind(ldc);
446 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
450 ldc->reason = "LDAP: connection open successful";
458 * Compare client certificate arrays.
460 * Returns 1 on compare failure, 0 otherwise.
462 static int compare_client_certs(apr_array_header_t *srcs,
463 apr_array_header_t *dests)
466 struct apr_ldap_opt_tls_cert_t *src, *dest;
468 /* arrays both NULL? if so, then equal */
469 if (srcs == NULL && dests == NULL) {
473 /* arrays different length or either NULL? If so, then not equal */
474 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
478 /* run an actual comparison */
479 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
480 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
481 for (i = 0; i < srcs->nelts; i++) {
482 if (strcmp(src[i].path, dest[i].path) ||
483 strcmp(src[i].password, dest[i].password) ||
484 src[i].type != dest[i].type) {
489 /* if we got here, the cert arrays were identical */
496 * Find an existing ldap connection struct that matches the
497 * provided ldap connection parameters.
499 * If not found in the cache, a new ldc structure will be allocated
500 * from st->pool and returned to the caller. If found in the cache,
501 * a pointer to the existing ldc structure will be returned.
503 static util_ldap_connection_t *
504 uldap_connection_find(request_rec *r,
505 const char *host, int port,
506 const char *binddn, const char *bindpw,
507 deref_options deref, int secure)
509 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
510 int secureflag = secure;
512 util_ldap_state_t *st =
513 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
518 /* mutex lock this function */
519 apr_thread_mutex_lock(st->mutex);
522 if (secure < APR_LDAP_NONE) {
523 secureflag = st->secure;
526 /* Search for an exact connection match in the list that is not
529 for (l=st->connections,p=NULL; l; l=l->next) {
531 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
533 if ( (l->port == port) && (strcmp(l->host, host) == 0)
534 && ((!l->binddn && !binddn) || (l->binddn && binddn
535 && !strcmp(l->binddn, binddn)))
536 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
537 && !strcmp(l->bindpw, bindpw)))
538 && (l->deref == deref) && (l->secure == secureflag)
539 && !compare_client_certs(st->client_certs, l->client_certs))
544 /* If this connection didn't match the criteria, then we
545 * need to unlock the mutex so it is available to be reused.
547 apr_thread_mutex_unlock(l->lock);
553 /* If nothing found, search again, but we don't care about the
554 * binddn and bindpw this time.
557 for (l=st->connections,p=NULL; l; l=l->next) {
559 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
562 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
563 (l->deref == deref) && (l->secure == secureflag) &&
564 !compare_client_certs(st->client_certs, l->client_certs))
566 /* the bind credentials have changed */
568 util_ldap_strdup((char**)&(l->binddn), binddn);
569 util_ldap_strdup((char**)&(l->bindpw), bindpw);
573 /* If this connection didn't match the criteria, then we
574 * need to unlock the mutex so it is available to be reused.
576 apr_thread_mutex_unlock(l->lock);
583 /* artificially disable cache */
586 /* If no connection what found after the second search, we
591 if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
592 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
593 "util_ldap: Failed to create memory pool");
595 apr_thread_mutex_unlock(st->mutex);
602 * Add the new connection entry to the linked list. Note that we
603 * don't actually establish an LDAP connection yet; that happens
604 * the first time authentication is requested.
607 /* create the details of this connection in the new pool */
608 l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
613 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
614 apr_thread_mutex_lock(l->lock);
617 l->host = apr_pstrdup(l->pool, host);
620 util_ldap_strdup((char**)&(l->binddn), binddn);
621 util_ldap_strdup((char**)&(l->bindpw), bindpw);
623 /* The security mode after parsing the URL will always be either
624 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
625 * If the security setting is NONE, override it to the security
626 * setting optionally supplied by the admin using LDAPTrustedMode
628 l->secure = secureflag;
630 /* save away a copy of the client cert list that is presently valid */
631 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
644 apr_thread_mutex_unlock(st->mutex);
649 /* ------------------------------------------------------------------ */
652 * Compares two DNs to see if they're equal. The only way to do this correctly
653 * is to search for the dn and then do ldap_get_dn() on the result. This should
654 * match the initial dn, since it would have been also retrieved with
655 * ldap_get_dn(). This is expensive, so if the configuration value
656 * compare_dn_on_server is false, just does an ordinary strcmp.
658 * The lock for the ldap cache should already be acquired.
660 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
661 const char *url, const char *dn,
662 const char *reqdn, int compare_dn_on_server)
665 util_url_node_t *curl;
666 util_url_node_t curnode;
667 util_dn_compare_node_t *node;
668 util_dn_compare_node_t newnode;
670 LDAPMessage *res, *entry;
673 util_ldap_state_t *st = (util_ldap_state_t *)
674 ap_get_module_config(r->server->module_config,
677 /* get cache entry (or create one) */
681 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
683 curl = util_ald_create_caches(st, url);
687 /* a simple compare? */
688 if (!compare_dn_on_server) {
689 /* unlock this read lock */
690 if (strcmp(dn, reqdn)) {
691 ldc->reason = "DN Comparison FALSE (direct strcmp())";
692 return LDAP_COMPARE_FALSE;
695 ldc->reason = "DN Comparison TRUE (direct strcmp())";
696 return LDAP_COMPARE_TRUE;
701 /* no - it's a server side compare */
704 /* is it in the compare cache? */
705 newnode.reqdn = (char *)reqdn;
706 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
708 /* If it's in the cache, it's good */
709 /* unlock this read lock */
711 ldc->reason = "DN Comparison TRUE (cached)";
712 return LDAP_COMPARE_TRUE;
715 /* unlock this read lock */
720 if (failures++ > 10) {
721 /* too many failures */
725 /* make a server connection */
726 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
727 /* connect to server failed */
731 /* search for reqdn */
732 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
733 "(objectclass=*)", NULL, 1,
734 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
737 ldc->reason = "DN Comparison ldap_search_ext_s() "
738 "failed with server down";
739 uldap_connection_unbind(ldc);
742 if (result != LDAP_SUCCESS) {
743 /* search for reqdn failed - no match */
744 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
748 entry = ldap_first_entry(ldc->ldap, res);
749 searchdn = ldap_get_dn(ldc->ldap, entry);
752 if (strcmp(dn, searchdn) != 0) {
753 /* compare unsuccessful */
754 ldc->reason = "DN Comparison FALSE (checked on server)";
755 result = LDAP_COMPARE_FALSE;
759 /* compare successful - add to the compare cache */
761 newnode.reqdn = (char *)reqdn;
762 newnode.dn = (char *)dn;
764 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
766 || (strcmp(reqdn, node->reqdn) != 0)
767 || (strcmp(dn, node->dn) != 0))
769 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
773 ldc->reason = "DN Comparison TRUE (checked on server)";
774 result = LDAP_COMPARE_TRUE;
776 ldap_memfree(searchdn);
782 * Does an generic ldap_compare operation. It accepts a cache that it will use
783 * to lookup the compare in the cache. We cache two kinds of compares
784 * (require group compares) and (require user compares). Each compare has a
785 * different cache node: require group includes the DN; require user does not
786 * because the require user cache is owned by the
789 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
790 const char *url, const char *dn,
791 const char *attrib, const char *value)
794 util_url_node_t *curl;
795 util_url_node_t curnode;
796 util_compare_node_t *compare_nodep;
797 util_compare_node_t the_compare_node;
798 apr_time_t curtime = 0; /* silence gcc -Wall */
801 util_ldap_state_t *st = (util_ldap_state_t *)
802 ap_get_module_config(r->server->module_config,
805 /* get cache entry (or create one) */
808 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
810 curl = util_ald_create_caches(st, url);
815 /* make a comparison to the cache */
817 curtime = apr_time_now();
819 the_compare_node.dn = (char *)dn;
820 the_compare_node.attrib = (char *)attrib;
821 the_compare_node.value = (char *)value;
822 the_compare_node.result = 0;
823 the_compare_node.sgl_processed = 0;
824 the_compare_node.subgroupList = NULL;
826 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
829 if (compare_nodep != NULL) {
831 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
832 /* ...but it is too old */
833 util_ald_cache_remove(curl->compare_cache, compare_nodep);
836 /* ...and it is good */
837 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
838 ldc->reason = "Comparison true (cached)";
840 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
841 ldc->reason = "Comparison false (cached)";
843 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
844 ldc->reason = "Comparison no such attribute (cached)";
847 ldc->reason = "Comparison undefined (cached)";
850 /* record the result code to return with the reason... */
851 result = compare_nodep->result;
852 /* and unlock this read lock */
857 /* unlock this read lock */
862 if (failures++ > 10) {
863 /* too many failures */
867 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
872 if ((result = ldap_compare_s(ldc->ldap,
876 == LDAP_SERVER_DOWN) {
877 /* connection failed - try again */
878 ldc->reason = "ldap_compare_s() failed with server down";
879 uldap_connection_unbind(ldc);
883 ldc->reason = "Comparison complete";
884 if ((LDAP_COMPARE_TRUE == result) ||
885 (LDAP_COMPARE_FALSE == result) ||
886 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
888 /* compare completed; caching result */
890 the_compare_node.lastcompare = curtime;
891 the_compare_node.result = result;
892 the_compare_node.sgl_processed = 0;
893 the_compare_node.subgroupList = NULL;
895 /* If the node doesn't exist then insert it, otherwise just update
896 * it with the last results
898 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
900 if ( (compare_nodep == NULL)
901 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
902 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
903 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
907 junk = util_ald_cache_insert(curl->compare_cache,
910 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
911 "[%" APR_PID_T_FMT "] cache_compare: Cache"
912 " insertion failure.", getpid());
916 compare_nodep->lastcompare = curtime;
917 compare_nodep->result = result;
921 if (LDAP_COMPARE_TRUE == result) {
922 ldc->reason = "Comparison true (adding to cache)";
923 return LDAP_COMPARE_TRUE;
925 else if (LDAP_COMPARE_FALSE == result) {
926 ldc->reason = "Comparison false (adding to cache)";
927 return LDAP_COMPARE_FALSE;
930 ldc->reason = "Comparison no such attribute (adding to cache)";
931 return LDAP_NO_SUCH_ATTRIBUTE;
938 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
939 util_ldap_connection_t *ldc,
942 char **subgroupAttrs,
943 apr_array_header_t *subgroupclasses)
946 int result = LDAP_COMPARE_FALSE;
947 util_compare_subgroup_t *res = NULL;
948 LDAPMessage *sga_res, *entry;
949 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
950 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
952 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
954 if (!subgroupAttrs) {
960 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
962 if (failures++ > 10) {
963 /* too many failures */
967 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
972 /* try to do the search */
973 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
974 (char *)"cn=*", subgroupAttrs, 0,
975 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
976 if (result == LDAP_SERVER_DOWN) {
977 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
979 uldap_connection_unbind(ldc);
983 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
984 if (result != LDAP_SUCCESS) {
985 ldc->reason = "ldap_search_ext_s() for subgroups failed";
989 entry = ldap_first_entry(ldc->ldap, sga_res);
992 * Get values for the provided sub-group attributes.
995 int indx = 0, tmp_sgcIndex;
997 while (subgroupAttrs[indx]) {
1001 /* Get *all* matching "member" values from this group. */
1002 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1007 * Now we are going to pare the subgroup members of this group
1008 * to *just* the subgroups, add them to the compare_nodep, and
1009 * then proceed to check the new level of subgroups.
1011 while (values[val_index]) {
1012 /* Check if this entry really is a group. */
1014 result = LDAP_COMPARE_FALSE;
1015 while ((tmp_sgcIndex < subgroupclasses->nelts)
1016 && (result != LDAP_COMPARE_TRUE)) {
1017 result = uldap_cache_compare(r, ldc, url,
1020 sgc_ents[tmp_sgcIndex].name
1023 if (result != LDAP_COMPARE_TRUE) {
1027 /* It's a group, so add it to the array. */
1028 if (result == LDAP_COMPARE_TRUE) {
1029 char **newgrp = (char **) apr_array_push(subgroups);
1030 *newgrp = apr_pstrdup(r->pool, values[val_index]);
1034 ldap_value_free(values);
1040 ldap_msgfree(sga_res);
1042 if (subgroups->nelts > 0) {
1043 /* We need to fill in tmp_local_subgroups using the data from LDAP */
1046 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1047 res->subgroupDNs = apr_pcalloc(r->pool,
1048 sizeof(char *) * (subgroups->nelts));
1049 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1050 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1060 * Does a recursive lookup operation to try to find a user within (cached)
1061 * nested groups. It accepts a cache that it will use to lookup previous
1062 * compare attempts. We cache two kinds of compares (require group compares)
1063 * and (require user compares). Each compare has a different cache node:
1064 * require group includes the DN; require user does not because the require
1065 * user cache is owned by the
1067 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1070 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1071 * generic, user-agnostic, cached group entry. This will create a new generic
1072 * cache entry if there
1073 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1075 * 2. Lock The cache and get the generic cache entry.
1076 * 3. Check if there is already a subgrouplist in this generic group's cache
1078 * A. If there is, go to step 4.
1079 * B. If there isn't:
1080 * i) Use ldap_search to get the full list
1081 * of subgroup "members" (which may include non-group "members").
1082 * ii) Use uldap_cache_compare to strip the list down to just groups.
1083 * iii) Lock and add this stripped down list to the cache of the generic
1085 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1087 * subgroup to see if the subgroup contains the user and to get the subgroups
1089 * cache (with user-afinity, if they aren't already there).
1090 * A. If the user is in the subgroup, then we'll be returning
1091 * LDAP_COMPARE_TRUE.
1092 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1093 * uldap_cache_compare) then recursively call this function to get the
1094 * sub-subgroups added...
1095 * 5. Cleanup local allocations.
1096 * 6. Return the final result.
1099 static int uldap_cache_check_subgroups(request_rec *r,
1100 util_ldap_connection_t *ldc,
1101 const char *url, const char *dn,
1102 const char *attrib, const char *value,
1103 char **subgroupAttrs,
1104 apr_array_header_t *subgroupclasses,
1105 int cur_subgroup_depth,
1106 int max_subgroup_depth)
1108 int result = LDAP_COMPARE_FALSE;
1109 util_url_node_t *curl;
1110 util_url_node_t curnode;
1111 util_compare_node_t *compare_nodep;
1112 util_compare_node_t the_compare_node;
1113 util_compare_subgroup_t *tmp_local_sgl = NULL;
1114 int lcl_sgl_processedFlag = 0, sgindex = 0, base_sgcIndex = 0;
1115 struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
1116 (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1117 util_ldap_state_t *st = (util_ldap_state_t *)
1118 ap_get_module_config(r->server->module_config,
1122 * Stop looking at deeper levels of nested groups if we have reached the
1123 * max. Since we already checked the top-level group in uldap_cache_compare,
1124 * we don't need to check it again here - so if max_subgroup_depth is set
1125 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1126 * We'll be calling uldap_cache_compare from here to check if the user is
1127 * in the next level before we recurse into that next level looking for
1130 if (cur_subgroup_depth >= max_subgroup_depth) {
1131 return LDAP_COMPARE_FALSE;
1135 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1138 while ((base_sgcIndex < subgroupclasses->nelts)
1139 && (result != LDAP_COMPARE_TRUE)) {
1140 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1141 sgc_ents[base_sgcIndex].name);
1142 if (result != LDAP_COMPARE_TRUE) {
1147 if (result != LDAP_COMPARE_TRUE) {
1148 ldc->reason = "DN failed group verification.";
1153 * 2. Find previously created cache entry and check if there is already a
1158 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1159 LDAP_CACHE_UNLOCK();
1161 if (curl && curl->compare_cache) {
1162 /* make a comparison to the cache */
1165 the_compare_node.dn = (char *)dn;
1166 the_compare_node.attrib = (char *)"objectClass";
1167 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1168 the_compare_node.result = 0;
1169 the_compare_node.sgl_processed = 0;
1170 the_compare_node.subgroupList = NULL;
1172 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1175 if (compare_nodep != NULL) {
1177 * Found the generic group entry... but the user isn't in this
1178 * group or we wouldn't be here.
1180 lcl_sgl_processedFlag = compare_nodep->sgl_processed;
1181 if(compare_nodep->sgl_processed && compare_nodep->subgroupList) {
1182 /* Make a local copy of the subgroup list */
1184 tmp_local_sgl = apr_pcalloc(r->pool,
1185 sizeof(util_compare_subgroup_t));
1186 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1187 tmp_local_sgl->subgroupDNs =
1188 apr_pcalloc(r->pool,
1189 sizeof(char *) * compare_nodep->subgroupList->len);
1190 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1191 tmp_local_sgl->subgroupDNs[i] =
1192 apr_pstrdup(r->pool,
1193 compare_nodep->subgroupList->subgroupDNs[i]);
1197 LDAP_CACHE_UNLOCK();
1201 * If we get here, something is wrong. Caches should have been
1202 * created and this group entry should be found in the cache.
1204 ldc->reason = "check_subgroups failed to find any caches.";
1205 return LDAP_COMPARE_FALSE;
1208 result = LDAP_COMPARE_FALSE;
1210 if ((lcl_sgl_processedFlag == 0) && (!tmp_local_sgl)) {
1211 /* No Cached SGL, retrieve from LDAP */
1212 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1213 "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s,"
1214 " retrieving from LDAP" , getpid(), dn);
1215 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1217 if (!tmp_local_sgl) {
1218 /* No SGL aailable via LDAP either */
1219 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1220 " util_ldap: no subgroups for %s" , getpid(), dn);
1222 lcl_sgl_processedFlag = 1;
1225 * Find the generic group cache entry and add the sgl we just retrieved.
1229 the_compare_node.dn = (char *)dn;
1230 the_compare_node.attrib = (char *)"objectClass";
1231 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1232 the_compare_node.result = 0;
1233 the_compare_node.sgl_processed = 0;
1234 the_compare_node.subgroupList = NULL;
1236 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1239 if (compare_nodep == NULL) {
1241 * Didn't find it. This shouldn't happen since we just called
1242 * uldap_cache_compare.
1244 LDAP_CACHE_UNLOCK();
1245 ldc->reason = "check_subgroups failed to find the cache entry to"
1246 " add sub-group list to.";
1247 return LDAP_COMPARE_FALSE;
1250 * overwrite SGL if it was previously updated between the last
1251 * two times we looked at the cache
1253 compare_nodep->sgl_processed = 1;
1254 if (tmp_local_sgl) {
1255 compare_nodep->subgroupList = util_ald_sgl_dup(curl->compare_cache,
1260 * We didn't find a single subgroup, next time save us from looking
1262 compare_nodep->subgroupList = NULL;
1264 LDAP_CACHE_UNLOCK();
1268 * tmp_local_sgl has either been created, or copied out of the cache
1269 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1272 result = LDAP_COMPARE_FALSE;
1273 if (!tmp_local_sgl) {
1277 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1278 const char *group = NULL;
1279 group = tmp_local_sgl->subgroupDNs[sgindex];
1281 * 4. Now loop through the subgroupList and call uldap_cache_compare
1282 * to check for the user.
1284 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1285 if (result == LDAP_COMPARE_TRUE) {
1287 * 4.A. We found the user in the subgroup. Return
1288 * LDAP_COMPARE_TRUE.
1290 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1291 " util_ldap: Found user %s in a subgroup (%s) at"
1292 " level %d of %d.", getpid(), r->user, group,
1293 cur_subgroup_depth+1, max_subgroup_depth);
1297 * 4.B. We didn't find the user in this subgroup, so recurse into
1298 * it and keep looking.
1300 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1301 " util_ldap: user %s not found in subgroup (%s) at"
1302 " level %d of %d.", getpid(), r->user, group,
1303 cur_subgroup_depth+1, max_subgroup_depth);
1304 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1305 value, subgroupAttrs,
1307 cur_subgroup_depth+1,
1308 max_subgroup_depth);
1317 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1318 const char *url, const char *basedn,
1319 int scope, char **attrs, const char *filter,
1320 const char *bindpw, const char **binddn,
1321 const char ***retvals)
1323 const char **vals = NULL;
1326 LDAPMessage *res, *entry;
1330 util_url_node_t *curl; /* Cached URL node */
1331 util_url_node_t curnode;
1332 util_search_node_t *search_nodep; /* Cached search node */
1333 util_search_node_t the_search_node;
1336 util_ldap_state_t *st =
1337 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1340 /* Get the cache node for this url */
1343 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1346 curl = util_ald_create_caches(st, url);
1348 LDAP_CACHE_UNLOCK();
1352 the_search_node.username = filter;
1353 search_nodep = util_ald_cache_fetch(curl->search_cache,
1355 if (search_nodep != NULL) {
1357 /* found entry in search cache... */
1358 curtime = apr_time_now();
1361 * Remove this item from the cache if its expired. If the sent
1362 * password doesn't match the storepassword, the entry will
1363 * be removed and readded later if the credentials pass
1366 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1367 /* ...but entry is too old */
1368 util_ald_cache_remove(curl->search_cache, search_nodep);
1370 else if ( (search_nodep->bindpw)
1371 && (search_nodep->bindpw[0] != '\0')
1372 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1374 /* ...and entry is valid */
1375 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1379 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1380 while (search_nodep->vals[i]) {
1381 (*retvals)[i] = apr_pstrdup(r->pool,
1382 search_nodep->vals[i]);
1386 LDAP_CACHE_UNLOCK();
1387 ldc->reason = "Authentication successful (cached)";
1388 return LDAP_SUCCESS;
1391 /* unlock this read lock */
1392 LDAP_CACHE_UNLOCK();
1396 * At this point, there is no valid cached search, so lets do the search.
1400 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1403 if (failures++ > 10) {
1406 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1410 /* try do the search */
1411 if ((result = ldap_search_ext_s(ldc->ldap,
1412 (char *)basedn, scope,
1413 (char *)filter, attrs, 0,
1414 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1415 == LDAP_SERVER_DOWN)
1417 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1418 uldap_connection_unbind(ldc);
1422 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1423 if (result != LDAP_SUCCESS) {
1424 ldc->reason = "ldap_search_ext_s() for user failed";
1429 * We should have found exactly one entry; to find a different
1430 * number is an error.
1432 count = ldap_count_entries(ldc->ldap, res);
1436 ldc->reason = "User not found";
1438 ldc->reason = "User is not unique (search found two "
1441 return LDAP_NO_SUCH_OBJECT;
1444 entry = ldap_first_entry(ldc->ldap, res);
1446 /* Grab the dn, copy it into the pool, and free it again */
1447 dn = ldap_get_dn(ldc->ldap, entry);
1448 *binddn = apr_pstrdup(r->pool, dn);
1452 * A bind to the server with an empty password always succeeds, so
1453 * we check to ensure that the password is not empty. This implies
1454 * that users who actually do have empty passwords will never be
1455 * able to authenticate with this module. I don't see this as a big
1458 if (!bindpw || strlen(bindpw) <= 0) {
1460 ldc->reason = "Empty password not allowed";
1461 return LDAP_INVALID_CREDENTIALS;
1465 * Attempt to bind with the retrieved dn and the password. If the bind
1466 * fails, it means that the password is wrong (the dn obviously
1467 * exists, since we just retrieved it)
1469 if ((result = ldap_simple_bind_s(ldc->ldap,
1471 (char *)bindpw)) == LDAP_SERVER_DOWN) {
1472 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1473 "failed with server down";
1475 uldap_connection_unbind(ldc);
1479 /* failure? if so - return */
1480 if (result != LDAP_SUCCESS) {
1481 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1483 uldap_connection_unbind(ldc);
1488 * We have just bound the connection to a different user and password
1489 * combination, which might be reused unintentionally next time this
1490 * connection is used from the connection pool. To ensure no confusion,
1491 * we mark the connection as unbound.
1497 * Get values for the provided attributes.
1503 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1510 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1511 while (values && values[j]) {
1512 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1513 : apr_pstrdup(r->pool, values[j]);
1516 ldap_value_free(values);
1524 * Add the new username to the search cache.
1528 the_search_node.username = filter;
1529 the_search_node.dn = *binddn;
1530 the_search_node.bindpw = bindpw;
1531 the_search_node.lastbind = apr_time_now();
1532 the_search_node.vals = vals;
1533 the_search_node.numvals = numvals;
1535 /* Search again to make sure that another thread didn't ready insert
1536 * this node into the cache before we got here. If it does exist then
1537 * update the lastbind
1539 search_nodep = util_ald_cache_fetch(curl->search_cache,
1541 if ((search_nodep == NULL) ||
1542 (strcmp(*binddn, search_nodep->dn) != 0)) {
1544 /* Nothing in cache, insert new entry */
1545 util_ald_cache_insert(curl->search_cache, &the_search_node);
1547 else if ((!search_nodep->bindpw) ||
1548 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1550 /* Entry in cache is invalid, remove it and insert new one */
1551 util_ald_cache_remove(curl->search_cache, search_nodep);
1552 util_ald_cache_insert(curl->search_cache, &the_search_node);
1555 /* Cache entry is valid, update lastbind */
1556 search_nodep->lastbind = the_search_node.lastbind;
1558 LDAP_CACHE_UNLOCK();
1562 ldc->reason = "Authentication successful";
1563 return LDAP_SUCCESS;
1567 * This function will return the DN of the entry matching userid.
1568 * It is used to get the DN in case some other module than mod_auth_ldap
1569 * has authenticated the user.
1570 * The function is basically a copy of uldap_cache_checkuserid
1571 * with password checking removed.
1573 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1574 const char *url, const char *basedn,
1575 int scope, char **attrs, const char *filter,
1576 const char **binddn, const char ***retvals)
1578 const char **vals = NULL;
1581 LDAPMessage *res, *entry;
1585 util_url_node_t *curl; /* Cached URL node */
1586 util_url_node_t curnode;
1587 util_search_node_t *search_nodep; /* Cached search node */
1588 util_search_node_t the_search_node;
1591 util_ldap_state_t *st =
1592 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1595 /* Get the cache node for this url */
1598 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1601 curl = util_ald_create_caches(st, url);
1603 LDAP_CACHE_UNLOCK();
1607 the_search_node.username = filter;
1608 search_nodep = util_ald_cache_fetch(curl->search_cache,
1610 if (search_nodep != NULL) {
1612 /* found entry in search cache... */
1613 curtime = apr_time_now();
1616 * Remove this item from the cache if its expired.
1618 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1619 /* ...but entry is too old */
1620 util_ald_cache_remove(curl->search_cache, search_nodep);
1623 /* ...and entry is valid */
1624 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1628 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1629 while (search_nodep->vals[i]) {
1630 (*retvals)[i] = apr_pstrdup(r->pool,
1631 search_nodep->vals[i]);
1635 LDAP_CACHE_UNLOCK();
1636 ldc->reason = "Search successful (cached)";
1637 return LDAP_SUCCESS;
1640 /* unlock this read lock */
1641 LDAP_CACHE_UNLOCK();
1645 * At this point, there is no valid cached search, so lets do the search.
1649 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1652 if (failures++ > 10) {
1655 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1659 /* try do the search */
1660 if ((result = ldap_search_ext_s(ldc->ldap,
1661 (char *)basedn, scope,
1662 (char *)filter, attrs, 0,
1663 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1664 == LDAP_SERVER_DOWN)
1666 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1667 uldap_connection_unbind(ldc);
1671 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1672 if (result != LDAP_SUCCESS) {
1673 ldc->reason = "ldap_search_ext_s() for user failed";
1678 * We should have found exactly one entry; to find a different
1679 * number is an error.
1681 count = ldap_count_entries(ldc->ldap, res);
1685 ldc->reason = "User not found";
1687 ldc->reason = "User is not unique (search found two "
1690 return LDAP_NO_SUCH_OBJECT;
1693 entry = ldap_first_entry(ldc->ldap, res);
1695 /* Grab the dn, copy it into the pool, and free it again */
1696 dn = ldap_get_dn(ldc->ldap, entry);
1697 *binddn = apr_pstrdup(r->pool, dn);
1701 * Get values for the provided attributes.
1707 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1714 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1715 while (values && values[j]) {
1716 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1717 : apr_pstrdup(r->pool, values[j]);
1720 ldap_value_free(values);
1728 * Add the new username to the search cache.
1732 the_search_node.username = filter;
1733 the_search_node.dn = *binddn;
1734 the_search_node.bindpw = NULL;
1735 the_search_node.lastbind = apr_time_now();
1736 the_search_node.vals = vals;
1737 the_search_node.numvals = numvals;
1739 /* Search again to make sure that another thread didn't ready insert
1740 * this node into the cache before we got here. If it does exist then
1741 * update the lastbind
1743 search_nodep = util_ald_cache_fetch(curl->search_cache,
1745 if ((search_nodep == NULL) ||
1746 (strcmp(*binddn, search_nodep->dn) != 0)) {
1748 /* Nothing in cache, insert new entry */
1749 util_ald_cache_insert(curl->search_cache, &the_search_node);
1752 * Don't update lastbind on entries with bindpw because
1753 * we haven't verified that password. It's OK to update
1754 * the entry if there is no password in it.
1756 else if (!search_nodep->bindpw) {
1757 /* Cache entry is valid, update lastbind */
1758 search_nodep->lastbind = the_search_node.lastbind;
1760 LDAP_CACHE_UNLOCK();
1765 ldc->reason = "Search successful";
1766 return LDAP_SUCCESS;
1770 * Reports if ssl support is enabled
1772 * 1 = enabled, 0 = not enabled
1774 static int uldap_ssl_supported(request_rec *r)
1776 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1777 r->server->module_config, &ldap_module);
1779 return(st->ssl_supported);
1783 /* ---------------------------------------- */
1784 /* config directives */
1787 static const char *util_ldap_set_cache_bytes(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->cache_bytes = atol(bytes);
1801 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1802 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1803 " cache size to %" APR_SIZE_T_FMT " bytes.",
1804 getpid(), st->cache_bytes);
1809 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1812 util_ldap_state_t *st =
1813 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1815 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1822 st->cache_file = ap_server_root_relative(st->pool, file);
1825 st->cache_file = NULL;
1828 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1829 "LDAP cache: Setting shared memory cache file to %s bytes.",
1835 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1838 util_ldap_state_t *st =
1839 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1841 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1847 st->search_cache_ttl = atol(ttl) * 1000000;
1849 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1850 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1851 " microseconds.", getpid(), st->search_cache_ttl);
1856 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1859 util_ldap_state_t *st =
1860 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1862 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1868 st->search_cache_size = atol(size);
1869 if (st->search_cache_size < 0) {
1870 st->search_cache_size = 0;
1873 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1874 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1875 " to %ld entries.", getpid(), st->search_cache_size);
1880 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1883 util_ldap_state_t *st =
1884 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1886 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1892 st->compare_cache_ttl = atol(ttl) * 1000000;
1894 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1895 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1896 " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1901 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1904 util_ldap_state_t *st =
1905 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1907 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1913 st->compare_cache_size = atol(size);
1914 if (st->compare_cache_size < 0) {
1915 st->compare_cache_size = 0;
1918 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1919 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
1920 " to %ld entries.", getpid(), st->compare_cache_size);
1927 * Parse the certificate type.
1929 * The type can be one of the following:
1930 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1931 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1933 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1935 static int util_ldap_parse_cert_type(const char *type)
1937 /* Authority file in binary DER format */
1938 if (0 == strcasecmp("CA_DER", type)) {
1939 return APR_LDAP_CA_TYPE_DER;
1942 /* Authority file in Base64 format */
1943 else if (0 == strcasecmp("CA_BASE64", type)) {
1944 return APR_LDAP_CA_TYPE_BASE64;
1947 /* Netscape certificate database file/directory */
1948 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1949 return APR_LDAP_CA_TYPE_CERT7_DB;
1952 /* Netscape secmod file/directory */
1953 else if (0 == strcasecmp("CA_SECMOD", type)) {
1954 return APR_LDAP_CA_TYPE_SECMOD;
1957 /* Client cert file in DER format */
1958 else if (0 == strcasecmp("CERT_DER", type)) {
1959 return APR_LDAP_CERT_TYPE_DER;
1962 /* Client cert file in Base64 format */
1963 else if (0 == strcasecmp("CERT_BASE64", type)) {
1964 return APR_LDAP_CERT_TYPE_BASE64;
1967 /* Client cert file in PKCS#12 format */
1968 else if (0 == strcasecmp("CERT_PFX", type)) {
1969 return APR_LDAP_CERT_TYPE_PFX;
1972 /* Netscape client cert database file/directory */
1973 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1974 return APR_LDAP_CERT_TYPE_KEY3_DB;
1977 /* Netscape client cert nickname */
1978 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1979 return APR_LDAP_CERT_TYPE_NICKNAME;
1982 /* Client cert key file in DER format */
1983 else if (0 == strcasecmp("KEY_DER", type)) {
1984 return APR_LDAP_KEY_TYPE_DER;
1987 /* Client cert key file in Base64 format */
1988 else if (0 == strcasecmp("KEY_BASE64", type)) {
1989 return APR_LDAP_KEY_TYPE_BASE64;
1992 /* Client cert key file in PKCS#12 format */
1993 else if (0 == strcasecmp("KEY_PFX", type)) {
1994 return APR_LDAP_KEY_TYPE_PFX;
1998 return APR_LDAP_CA_TYPE_UNKNOWN;
2005 * Set LDAPTrustedGlobalCert.
2007 * This directive takes either two or three arguments:
2008 * - certificate type
2009 * - certificate file / directory / nickname
2010 * - certificate password (optional)
2012 * This directive may only be used globally.
2014 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2018 const char *password)
2020 util_ldap_state_t *st =
2021 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2023 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2027 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 CA_DER, CA_BASE64, CA_CERT7_DB, "
2040 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2041 "CERT_KEY3_DB, CERT_NICKNAME, "
2042 "KEY_DER, KEY_BASE64", type);
2046 return "Certificate type was not specified.";
2049 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2050 "LDAP: SSL trusted global cert - %s (type %s)",
2053 /* add the certificate to the global array */
2054 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2055 cert->type = cert_type;
2057 cert->password = password;
2059 /* if file is a file or path, fix the path */
2060 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2061 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2063 cert->path = ap_server_root_relative(cmd->pool, file);
2065 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2068 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2069 "LDAP: Could not open SSL trusted certificate "
2070 "authority file - %s",
2071 cert->path == NULL ? file : cert->path);
2072 return "Invalid global certificate file path";
2081 * Set LDAPTrustedClientCert.
2083 * This directive takes either two or three arguments:
2084 * - certificate type
2085 * - certificate file / directory / nickname
2086 * - certificate password (optional)
2088 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2092 const char *password)
2094 util_ldap_state_t *st =
2095 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2100 apr_ldap_opt_tls_cert_t *cert;
2102 /* handle the certificate type */
2104 cert_type = util_ldap_parse_cert_type(type);
2105 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2106 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2107 "not recognised. It should be one "
2108 "of CERT_DER, CERT_BASE64, "
2109 "CERT_NICKNAME, CERT_PFX,"
2110 "KEY_DER, KEY_BASE64, KEY_PFX",
2113 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2114 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2115 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2116 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2117 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2118 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2119 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2120 "only valid within a "
2121 "LDAPTrustedGlobalCert directive. "
2122 "Only CERT_DER, CERT_BASE64, "
2123 "CERT_NICKNAME, KEY_DER, and "
2124 "KEY_BASE64 may be used.", type);
2128 return "Certificate type was not specified.";
2131 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2132 "LDAP: SSL trusted client cert - %s (type %s)",
2135 /* add the certificate to the global array */
2136 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2137 cert->type = cert_type;
2139 cert->password = password;
2141 /* if file is a file or path, fix the path */
2142 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2143 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2145 cert->path = ap_server_root_relative(cmd->pool, file);
2147 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2150 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2151 "LDAP: Could not open SSL client certificate "
2153 cert->path == NULL ? file : cert->path);
2154 return "Invalid client certificate file path";
2164 * Set LDAPTrustedMode.
2166 * This directive sets what encryption mode to use on a connection:
2167 * - None (No encryption)
2168 * - SSL (SSL encryption)
2169 * - STARTTLS (TLS encryption)
2171 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2174 util_ldap_state_t *st =
2175 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2178 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2179 "LDAP: SSL trusted mode - %s",
2182 if (0 == strcasecmp("NONE", mode)) {
2183 st->secure = APR_LDAP_NONE;
2185 else if (0 == strcasecmp("SSL", mode)) {
2186 st->secure = APR_LDAP_SSL;
2188 else if ( (0 == strcasecmp("TLS", mode))
2189 || (0 == strcasecmp("STARTTLS", mode))) {
2190 st->secure = APR_LDAP_STARTTLS;
2193 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2194 "SSL, or TLS/STARTTLS";
2201 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2205 util_ldap_state_t *st =
2206 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2208 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2214 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2215 "LDAP: SSL verify server certificate - %s",
2216 mode?"TRUE":"FALSE");
2218 st->verify_svr_cert = mode;
2224 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2228 util_ldap_state_t *st =
2229 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2231 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2237 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2238 st->connectionTimeout = atol(ttl);
2240 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2241 "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2242 " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2244 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2245 "LDAP: Connection timout option not supported by the "
2246 "LDAP SDK in use." );
2253 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2255 util_ldap_state_t *st =
2256 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2258 /* Create a per vhost pool for mod_ldap to use, serialized with
2259 * st->mutex (also one per vhost). both are replicated by fork(),
2260 * no shared memory managed by either.
2262 apr_pool_create(&st->pool, p);
2264 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2267 st->cache_bytes = 100000;
2268 st->search_cache_ttl = 600000000;
2269 st->search_cache_size = 1024;
2270 st->compare_cache_ttl = 600000000;
2271 st->compare_cache_size = 1024;
2272 st->connections = NULL;
2273 st->ssl_supported = 0;
2274 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2275 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2276 st->secure = APR_LDAP_NONE;
2278 st->connectionTimeout = 10;
2279 st->verify_svr_cert = 1;
2284 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2287 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2288 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2289 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2291 st->pool = overrides->pool;
2293 st->mutex = overrides->mutex;
2296 /* The cache settings can not be modified in a
2297 virtual host since all server use the same
2298 shared memory cache. */
2299 st->cache_bytes = base->cache_bytes;
2300 st->search_cache_ttl = base->search_cache_ttl;
2301 st->search_cache_size = base->search_cache_size;
2302 st->compare_cache_ttl = base->compare_cache_ttl;
2303 st->compare_cache_size = base->compare_cache_size;
2304 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2306 st->connections = NULL;
2307 st->ssl_supported = 0;
2308 st->global_certs = apr_array_append(p, base->global_certs,
2309 overrides->global_certs);
2310 st->client_certs = apr_array_append(p, base->client_certs,
2311 overrides->client_certs);
2312 st->secure = (overrides->secure_set == 0) ? base->secure
2313 : overrides->secure;
2315 /* These LDAP connection settings can not be overwritten in
2316 a virtual host. Once set in the base server, they must
2317 remain the same. None of the LDAP SDKs seem to be able
2318 to handle setting the verify_svr_cert flag on a
2319 per-connection basis. The OpenLDAP client appears to be
2320 able to handle the connection timeout per-connection
2321 but the Novell SDK cannot. Allowing the timeout to
2322 be set by each vhost is of little value so rather than
2323 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2324 is being enforced on this setting as well. */
2325 st->connectionTimeout = base->connectionTimeout;
2326 st->verify_svr_cert = base->verify_svr_cert;
2331 static apr_status_t util_ldap_cleanup_module(void *data)
2334 server_rec *s = data;
2335 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2336 s->module_config, &ldap_module);
2338 if (st->ssl_supported) {
2339 apr_ldap_ssl_deinit();
2346 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2347 apr_pool_t *ptemp, server_rec *s)
2349 apr_status_t result;
2350 server_rec *s_vhost;
2351 util_ldap_state_t *st_vhost;
2353 util_ldap_state_t *st = (util_ldap_state_t *)
2354 ap_get_module_config(s->module_config,
2358 const char *userdata_key = "util_ldap_init";
2359 apr_ldap_err_t *result_err = NULL;
2362 /* util_ldap_post_config() will be called twice. Don't bother
2363 * going through all of the initialization on the first call
2364 * because it will just be thrown away.*/
2365 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2367 apr_pool_userdata_set((const void *)1, userdata_key,
2368 apr_pool_cleanup_null, s->process->pool);
2370 #if APR_HAS_SHARED_MEMORY
2371 /* If the cache file already exists then delete it. Otherwise we are
2372 * going to run into problems creating the shared memory. */
2373 if (st->cache_file) {
2374 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2376 apr_file_remove(lck_file, ptemp);
2382 #if APR_HAS_SHARED_MEMORY
2383 /* initializing cache if shared memory size is not zero and we already
2384 * don't have shm address
2386 if (!st->cache_shm && st->cache_bytes > 0) {
2388 result = util_ldap_cache_init(p, st);
2389 if (result != APR_SUCCESS) {
2390 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2391 "LDAP cache: could not create shared memory segment");
2396 #if APR_HAS_SHARED_MEMORY
2397 if (st->cache_file) {
2398 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2403 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2404 st->lock_file, APR_LOCK_DEFAULT,
2406 if (result != APR_SUCCESS) {
2410 #ifdef AP_NEED_SET_MUTEX_PERMS
2411 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2412 if (result != APR_SUCCESS) {
2413 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2414 "LDAP cache: failed to set mutex permissions");
2419 /* merge config in all vhost */
2422 st_vhost = (util_ldap_state_t *)
2423 ap_get_module_config(s_vhost->module_config,
2426 #if APR_HAS_SHARED_MEMORY
2427 st_vhost->cache_shm = st->cache_shm;
2428 st_vhost->cache_rmm = st->cache_rmm;
2429 st_vhost->cache_file = st->cache_file;
2430 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2431 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2432 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2433 s_vhost->server_hostname);
2435 st_vhost->lock_file = st->lock_file;
2436 s_vhost = s_vhost->next;
2438 #if APR_HAS_SHARED_MEMORY
2441 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2442 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2443 "shared memory cache");
2447 /* log the LDAP SDK used
2450 apr_ldap_err_t *result = NULL;
2451 apr_ldap_info(p, &(result));
2452 if (result != NULL) {
2453 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2457 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2458 util_ldap_cleanup_module);
2461 * Initialize SSL support, and log the result for the benefit of the admin.
2463 * If SSL is not supported it is not necessarily an error, as the
2464 * application may not want to use it.
2466 rc = apr_ldap_ssl_init(p,
2470 if (APR_SUCCESS == rc) {
2471 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2472 (void *)st->global_certs, &(result_err));
2475 if (APR_SUCCESS == rc) {
2476 st->ssl_supported = 1;
2477 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2478 "LDAP: SSL support available" );
2481 st->ssl_supported = 0;
2482 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2483 "LDAP: SSL support unavailable%s%s",
2484 result_err ? ": " : "",
2485 result_err ? result_err->reason : "");
2491 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2494 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2497 if (!st->util_ldap_cache_lock) return;
2499 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2501 if (sts != APR_SUCCESS) {
2502 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2503 "Failed to initialise global mutex %s in child process %"
2505 st->lock_file, getpid());
2509 static const command_rec util_ldap_cmds[] = {
2510 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2512 "Set the size of the shared memory cache (in bytes). Use "
2513 "0 to disable the shared memory cache. (default: 100000)"),
2515 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2517 "Set the file name for the shared memory cache."),
2519 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2521 "Set the maximum number of entries that are possible in the "
2522 "LDAP search cache. Use 0 for no limit. "
2523 "-1 disables the cache. (default: 1024)"),
2525 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2527 "Set the maximum time (in seconds) that an item can be "
2528 "cached in the LDAP search cache. Use 0 for no limit. "
2531 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2533 "Set the maximum number of entries that are possible "
2534 "in the LDAP compare cache. Use 0 for no limit. "
2535 "Use -1 to disable the cache. (default: 1024)"),
2537 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2539 "Set the maximum time (in seconds) that an item is cached "
2540 "in the LDAP operation cache. Use 0 for no limit. "
2543 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2545 "Takes three args; the file and/or directory containing "
2546 "the trusted CA certificates (and global client certs "
2547 "for Netware) used to validate the LDAP server. Second "
2548 "arg is the cert type for the first arg, one of CA_DER, "
2549 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2550 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2551 "Third arg is an optional passphrase if applicable."),
2553 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2555 "Takes three args; the file and/or directory containing "
2556 "the client certificate, or certificate ID used to "
2557 "validate this LDAP client. Second arg is the cert type "
2558 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2559 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2560 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2561 "optional passphrase if applicable."),
2563 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2565 "Specify the type of security that should be applied to "
2566 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2568 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2570 "Set to 'ON' requires that the server certificate be verified"
2571 " before a secure LDAP connection can be establish. Default"
2574 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2576 "Specify the LDAP socket connection timeout in seconds "
2582 static void util_ldap_register_hooks(apr_pool_t *p)
2584 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2585 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2586 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2587 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2588 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2589 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2590 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2591 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2592 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2593 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2594 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2596 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2597 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2598 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2601 module AP_MODULE_DECLARE_DATA ldap_module = {
2602 STANDARD20_MODULE_STUFF,
2603 NULL, /* create dir config */
2604 NULL, /* merge dir config */
2605 util_ldap_create_config, /* create server config */
2606 util_ldap_merge_config, /* merge server config */
2607 util_ldap_cmds, /* command table */
2608 util_ldap_register_hooks, /* set up request processing hooks */