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);
201 /* ldc->reason is allocated from r->pool */
205 /* unlock this entry */
206 uldap_connection_close(ldc);
214 * util_ldap_connection_remove frees all storage associated with the LDAP
215 * connection and removes it completely from the per-virtualhost list of
218 * The caller should hold the lock for this connection
220 static apr_status_t util_ldap_connection_remove (void *param) {
221 util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
222 util_ldap_state_t *st = ldc->st;
224 if (!ldc) return APR_SUCCESS;
226 uldap_connection_unbind(ldc);
229 apr_thread_mutex_lock(st->mutex);
232 /* Remove ldc from the list */
233 for (l=st->connections; l; l=l->next) {
236 prev->next = l->next;
239 st->connections = l->next;
246 /* Some unfortunate duplication between this method
247 * and uldap_connection_cleanup()
250 free((void*)ldc->bindpw);
253 free((void*)ldc->binddn);
257 apr_thread_mutex_unlock(ldc->lock);
258 apr_thread_mutex_unlock(st->mutex);
261 /* Destory the pool associated with this connection */
263 apr_pool_destroy(ldc->pool);
268 static int uldap_connection_init(request_rec *r,
269 util_ldap_connection_t *ldc)
271 int rc = 0, ldap_option = 0;
272 int version = LDAP_VERSION3;
273 apr_ldap_err_t *result = NULL;
274 #ifdef LDAP_OPT_NETWORK_TIMEOUT
275 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
277 util_ldap_state_t *st =
278 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
281 /* Since the host will include a port if the default port is not used,
282 * always specify the default ports for the port parameter. This will
283 * allow a host string that contains multiple hosts the ability to mix
284 * some hosts with ports and some without. All hosts which do not
285 * specify a port will use the default port.
287 apr_ldap_init(r->pool, &(ldc->ldap),
289 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
294 if (result != NULL && result->rc) {
295 ldc->reason = result->reason;
298 if (NULL == ldc->ldap)
301 if (NULL == ldc->reason) {
302 ldc->reason = "LDAP: ldap initialization failed";
305 ldc->reason = result->reason;
310 /* always default to LDAP V3 */
311 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
313 /* set client certificates */
314 if (!apr_is_empty_array(ldc->client_certs)) {
315 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
316 ldc->client_certs, &(result));
317 if (LDAP_SUCCESS != result->rc) {
318 uldap_connection_unbind( ldc );
319 ldc->reason = result->reason;
324 /* switch on SSL/TLS */
325 if (APR_LDAP_NONE != ldc->secure) {
326 apr_ldap_set_option(r->pool, ldc->ldap,
327 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
328 if (LDAP_SUCCESS != result->rc) {
329 uldap_connection_unbind( ldc );
330 ldc->reason = result->reason;
335 /* Set the alias dereferencing option */
336 ldap_option = ldc->deref;
337 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
339 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
340 #ifdef APR_LDAP_OPT_VERIFY_CERT
341 apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
342 &(st->verify_svr_cert), &(result));
344 #if defined(LDAPSSL_VERIFY_SERVER)
345 if (st->verify_svr_cert) {
346 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
349 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
351 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
352 /* This is not a per-connection setting so just pass NULL for the
353 Ldap connection handle */
354 if (st->verify_svr_cert) {
355 int i = LDAP_OPT_X_TLS_DEMAND;
356 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
359 int i = LDAP_OPT_X_TLS_NEVER;
360 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
365 #ifdef LDAP_OPT_NETWORK_TIMEOUT
366 if (st->connectionTimeout > 0) {
367 timeOut.tv_sec = st->connectionTimeout;
370 if (st->connectionTimeout >= 0) {
371 rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
372 (void *)&timeOut, &(result));
373 if (APR_SUCCESS != rc) {
374 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
375 "LDAP: Could not set the connection timeout");
384 * Connect to the LDAP server and binds. Does not connect if already
385 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
387 * Returns LDAP_SUCCESS on success; and an error code on failure
389 static int uldap_connection_open(request_rec *r,
390 util_ldap_connection_t *ldc)
395 /* sanity check for NULL */
400 /* If the connection is already bound, return
404 ldc->reason = "LDAP: connection open successful (already bound)";
408 /* create the ldap session handle
410 if (NULL == ldc->ldap)
412 rc = uldap_connection_init( r, ldc );
413 if (LDAP_SUCCESS != rc)
420 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
421 * returned. Break out of the loop on Success or any other error.
423 * NOTE: Looping is probably not a great idea. If the server isn't
424 * responding the chances it will respond after a few tries are poor.
425 * However, the original code looped and it only happens on
426 * the error condition.
428 for (failures=0; failures<10; failures++)
430 rc = ldap_simple_bind_s(ldc->ldap,
432 (char *)ldc->bindpw);
433 if (LDAP_SERVER_DOWN != rc) {
435 } else if (failures == 5) {
436 /* attempt to init the connection once again */
437 uldap_connection_unbind( ldc );
438 rc = uldap_connection_init( r, ldc );
439 if (LDAP_SUCCESS != rc)
446 /* free the handle if there was an error
448 if (LDAP_SUCCESS != rc)
450 uldap_connection_unbind(ldc);
451 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
455 ldc->reason = "LDAP: connection open successful";
463 * Compare client certificate arrays.
465 * Returns 1 on compare failure, 0 otherwise.
467 static int compare_client_certs(apr_array_header_t *srcs,
468 apr_array_header_t *dests)
471 struct apr_ldap_opt_tls_cert_t *src, *dest;
473 /* arrays both NULL? if so, then equal */
474 if (srcs == NULL && dests == NULL) {
478 /* arrays different length or either NULL? If so, then not equal */
479 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
483 /* run an actual comparison */
484 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
485 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
486 for (i = 0; i < srcs->nelts; i++) {
487 if (strcmp(src[i].path, dest[i].path) ||
488 strcmp(src[i].password, dest[i].password) ||
489 src[i].type != dest[i].type) {
494 /* if we got here, the cert arrays were identical */
501 * Find an existing ldap connection struct that matches the
502 * provided ldap connection parameters.
504 * If not found in the cache, a new ldc structure will be allocated
505 * from st->pool and returned to the caller. If found in the cache,
506 * a pointer to the existing ldc structure will be returned.
508 static util_ldap_connection_t *
509 uldap_connection_find(request_rec *r,
510 const char *host, int port,
511 const char *binddn, const char *bindpw,
512 deref_options deref, int secure)
514 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
515 int secureflag = secure;
517 util_ldap_state_t *st =
518 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
523 /* mutex lock this function */
524 apr_thread_mutex_lock(st->mutex);
527 if (secure < APR_LDAP_NONE) {
528 secureflag = st->secure;
531 /* Search for an exact connection match in the list that is not
534 for (l=st->connections,p=NULL; l; l=l->next) {
536 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
538 if ( (l->port == port) && (strcmp(l->host, host) == 0)
539 && ((!l->binddn && !binddn) || (l->binddn && binddn
540 && !strcmp(l->binddn, binddn)))
541 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
542 && !strcmp(l->bindpw, bindpw)))
543 && (l->deref == deref) && (l->secure == secureflag)
544 && !compare_client_certs(st->client_certs, l->client_certs))
549 /* If this connection didn't match the criteria, then we
550 * need to unlock the mutex so it is available to be reused.
552 apr_thread_mutex_unlock(l->lock);
558 /* If nothing found, search again, but we don't care about the
559 * binddn and bindpw this time.
562 for (l=st->connections,p=NULL; l; l=l->next) {
564 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
567 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
568 (l->deref == deref) && (l->secure == secureflag) &&
569 !compare_client_certs(st->client_certs, l->client_certs))
571 /* the bind credentials have changed */
573 util_ldap_strdup((char**)&(l->binddn), binddn);
574 util_ldap_strdup((char**)&(l->bindpw), bindpw);
578 /* If this connection didn't match the criteria, then we
579 * need to unlock the mutex so it is available to be reused.
581 apr_thread_mutex_unlock(l->lock);
588 /* artificially disable cache */
591 /* If no connection was found after the second search, we
596 if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
597 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
598 "util_ldap: Failed to create memory pool");
600 apr_thread_mutex_unlock(st->mutex);
606 * Add the new connection entry to the linked list. Note that we
607 * don't actually establish an LDAP connection yet; that happens
608 * the first time authentication is requested.
611 /* create the details of this connection in the new pool */
612 l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
617 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
618 apr_thread_mutex_lock(l->lock);
621 l->host = apr_pstrdup(l->pool, host);
624 util_ldap_strdup((char**)&(l->binddn), binddn);
625 util_ldap_strdup((char**)&(l->bindpw), bindpw);
627 /* The security mode after parsing the URL will always be either
628 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
629 * If the security setting is NONE, override it to the security
630 * setting optionally supplied by the admin using LDAPTrustedMode
632 l->secure = secureflag;
634 /* save away a copy of the client cert list that is presently valid */
635 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
648 apr_thread_mutex_unlock(st->mutex);
653 /* ------------------------------------------------------------------ */
656 * Compares two DNs to see if they're equal. The only way to do this correctly
657 * is to search for the dn and then do ldap_get_dn() on the result. This should
658 * match the initial dn, since it would have been also retrieved with
659 * ldap_get_dn(). This is expensive, so if the configuration value
660 * compare_dn_on_server is false, just does an ordinary strcmp.
662 * The lock for the ldap cache should already be acquired.
664 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
665 const char *url, const char *dn,
666 const char *reqdn, int compare_dn_on_server)
669 util_url_node_t *curl;
670 util_url_node_t curnode;
671 util_dn_compare_node_t *node;
672 util_dn_compare_node_t newnode;
674 LDAPMessage *res, *entry;
677 util_ldap_state_t *st = (util_ldap_state_t *)
678 ap_get_module_config(r->server->module_config,
681 /* get cache entry (or create one) */
685 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
687 curl = util_ald_create_caches(st, url);
691 /* a simple compare? */
692 if (!compare_dn_on_server) {
693 /* unlock this read lock */
694 if (strcmp(dn, reqdn)) {
695 ldc->reason = "DN Comparison FALSE (direct strcmp())";
696 return LDAP_COMPARE_FALSE;
699 ldc->reason = "DN Comparison TRUE (direct strcmp())";
700 return LDAP_COMPARE_TRUE;
705 /* no - it's a server side compare */
708 /* is it in the compare cache? */
709 newnode.reqdn = (char *)reqdn;
710 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
712 /* If it's in the cache, it's good */
713 /* unlock this read lock */
715 ldc->reason = "DN Comparison TRUE (cached)";
716 return LDAP_COMPARE_TRUE;
719 /* unlock this read lock */
724 if (failures++ > 10) {
725 /* too many failures */
729 /* make a server connection */
730 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
731 /* connect to server failed */
735 /* search for reqdn */
736 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
737 "(objectclass=*)", NULL, 1,
738 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
741 ldc->reason = "DN Comparison ldap_search_ext_s() "
742 "failed with server down";
743 uldap_connection_unbind(ldc);
746 if (result != LDAP_SUCCESS) {
747 /* search for reqdn failed - no match */
748 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
752 entry = ldap_first_entry(ldc->ldap, res);
753 searchdn = ldap_get_dn(ldc->ldap, entry);
756 if (strcmp(dn, searchdn) != 0) {
757 /* compare unsuccessful */
758 ldc->reason = "DN Comparison FALSE (checked on server)";
759 result = LDAP_COMPARE_FALSE;
763 /* compare successful - add to the compare cache */
765 newnode.reqdn = (char *)reqdn;
766 newnode.dn = (char *)dn;
768 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
770 || (strcmp(reqdn, node->reqdn) != 0)
771 || (strcmp(dn, node->dn) != 0))
773 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
777 ldc->reason = "DN Comparison TRUE (checked on server)";
778 result = LDAP_COMPARE_TRUE;
780 ldap_memfree(searchdn);
786 * Does an generic ldap_compare operation. It accepts a cache that it will use
787 * to lookup the compare in the cache. We cache two kinds of compares
788 * (require group compares) and (require user compares). Each compare has a
789 * different cache node: require group includes the DN; require user does not
790 * because the require user cache is owned by the
793 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
794 const char *url, const char *dn,
795 const char *attrib, const char *value)
798 util_url_node_t *curl;
799 util_url_node_t curnode;
800 util_compare_node_t *compare_nodep;
801 util_compare_node_t the_compare_node;
802 apr_time_t curtime = 0; /* silence gcc -Wall */
805 util_ldap_state_t *st = (util_ldap_state_t *)
806 ap_get_module_config(r->server->module_config,
809 /* get cache entry (or create one) */
812 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
814 curl = util_ald_create_caches(st, url);
819 /* make a comparison to the cache */
821 curtime = apr_time_now();
823 the_compare_node.dn = (char *)dn;
824 the_compare_node.attrib = (char *)attrib;
825 the_compare_node.value = (char *)value;
826 the_compare_node.result = 0;
827 the_compare_node.sgl_processed = 0;
828 the_compare_node.subgroupList = NULL;
830 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
833 if (compare_nodep != NULL) {
835 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
836 /* ...but it is too old */
837 util_ald_cache_remove(curl->compare_cache, compare_nodep);
840 /* ...and it is good */
841 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
842 ldc->reason = "Comparison true (cached)";
844 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
845 ldc->reason = "Comparison false (cached)";
847 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
848 ldc->reason = "Comparison no such attribute (cached)";
851 ldc->reason = "Comparison undefined (cached)";
854 /* record the result code to return with the reason... */
855 result = compare_nodep->result;
856 /* and unlock this read lock */
861 /* unlock this read lock */
866 if (failures++ > 10) {
867 /* too many failures */
871 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
876 if ((result = ldap_compare_s(ldc->ldap,
880 == LDAP_SERVER_DOWN) {
881 /* connection failed - try again */
882 ldc->reason = "ldap_compare_s() failed with server down";
883 uldap_connection_unbind(ldc);
887 ldc->reason = "Comparison complete";
888 if ((LDAP_COMPARE_TRUE == result) ||
889 (LDAP_COMPARE_FALSE == result) ||
890 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
892 /* compare completed; caching result */
894 the_compare_node.lastcompare = curtime;
895 the_compare_node.result = result;
896 the_compare_node.sgl_processed = 0;
897 the_compare_node.subgroupList = NULL;
899 /* If the node doesn't exist then insert it, otherwise just update
900 * it with the last results
902 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
904 if ( (compare_nodep == NULL)
905 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
906 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
907 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
911 junk = util_ald_cache_insert(curl->compare_cache,
914 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
915 "[%" APR_PID_T_FMT "] cache_compare: Cache"
916 " insertion failure.", getpid());
920 compare_nodep->lastcompare = curtime;
921 compare_nodep->result = result;
925 if (LDAP_COMPARE_TRUE == result) {
926 ldc->reason = "Comparison true (adding to cache)";
927 return LDAP_COMPARE_TRUE;
929 else if (LDAP_COMPARE_FALSE == result) {
930 ldc->reason = "Comparison false (adding to cache)";
931 return LDAP_COMPARE_FALSE;
934 ldc->reason = "Comparison no such attribute (adding to cache)";
935 return LDAP_NO_SUCH_ATTRIBUTE;
942 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
943 util_ldap_connection_t *ldc,
946 char **subgroupAttrs,
947 apr_array_header_t *subgroupclasses)
950 int result = LDAP_COMPARE_FALSE;
951 util_compare_subgroup_t *res = NULL;
952 LDAPMessage *sga_res, *entry;
953 struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
954 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
956 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
958 if (!subgroupAttrs) {
964 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
966 if (failures++ > 10) {
967 /* too many failures */
971 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
976 /* try to do the search */
977 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
978 (char *)"cn=*", subgroupAttrs, 0,
979 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
980 if (result == LDAP_SERVER_DOWN) {
981 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
983 uldap_connection_unbind(ldc);
987 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
988 if (result != LDAP_SUCCESS) {
989 ldc->reason = "ldap_search_ext_s() for subgroups failed";
993 entry = ldap_first_entry(ldc->ldap, sga_res);
996 * Get values for the provided sub-group attributes.
999 int indx = 0, tmp_sgcIndex;
1001 while (subgroupAttrs[indx]) {
1005 /* Get *all* matching "member" values from this group. */
1006 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1011 * Now we are going to pare the subgroup members of this group
1012 * to *just* the subgroups, add them to the compare_nodep, and
1013 * then proceed to check the new level of subgroups.
1015 while (values[val_index]) {
1016 /* Check if this entry really is a group. */
1018 result = LDAP_COMPARE_FALSE;
1019 while ((tmp_sgcIndex < subgroupclasses->nelts)
1020 && (result != LDAP_COMPARE_TRUE)) {
1021 result = uldap_cache_compare(r, ldc, url,
1024 sgc_ents[tmp_sgcIndex].name
1027 if (result != LDAP_COMPARE_TRUE) {
1031 /* It's a group, so add it to the array. */
1032 if (result == LDAP_COMPARE_TRUE) {
1033 char **newgrp = (char **) apr_array_push(subgroups);
1034 *newgrp = apr_pstrdup(r->pool, values[val_index]);
1038 ldap_value_free(values);
1044 ldap_msgfree(sga_res);
1046 if (subgroups->nelts > 0) {
1047 /* We need to fill in tmp_local_subgroups using the data from LDAP */
1050 res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1051 res->subgroupDNs = apr_pcalloc(r->pool,
1052 sizeof(char *) * (subgroups->nelts));
1053 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1054 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1064 * Does a recursive lookup operation to try to find a user within (cached)
1065 * nested groups. It accepts a cache that it will use to lookup previous
1066 * compare attempts. We cache two kinds of compares (require group compares)
1067 * and (require user compares). Each compare has a different cache node:
1068 * require group includes the DN; require user does not because the require
1069 * user cache is owned by the
1071 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1074 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1075 * generic, user-agnostic, cached group entry. This will create a new generic
1076 * cache entry if there
1077 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1079 * 2. Lock The cache and get the generic cache entry.
1080 * 3. Check if there is already a subgrouplist in this generic group's cache
1082 * A. If there is, go to step 4.
1083 * B. If there isn't:
1084 * i) Use ldap_search to get the full list
1085 * of subgroup "members" (which may include non-group "members").
1086 * ii) Use uldap_cache_compare to strip the list down to just groups.
1087 * iii) Lock and add this stripped down list to the cache of the generic
1089 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1091 * subgroup to see if the subgroup contains the user and to get the subgroups
1093 * cache (with user-afinity, if they aren't already there).
1094 * A. If the user is in the subgroup, then we'll be returning
1095 * LDAP_COMPARE_TRUE.
1096 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1097 * uldap_cache_compare) then recursively call this function to get the
1098 * sub-subgroups added...
1099 * 5. Cleanup local allocations.
1100 * 6. Return the final result.
1103 static int uldap_cache_check_subgroups(request_rec *r,
1104 util_ldap_connection_t *ldc,
1105 const char *url, const char *dn,
1106 const char *attrib, const char *value,
1107 char **subgroupAttrs,
1108 apr_array_header_t *subgroupclasses,
1109 int cur_subgroup_depth,
1110 int max_subgroup_depth)
1112 int result = LDAP_COMPARE_FALSE;
1113 util_url_node_t *curl;
1114 util_url_node_t curnode;
1115 util_compare_node_t *compare_nodep;
1116 util_compare_node_t the_compare_node;
1117 util_compare_subgroup_t *tmp_local_sgl = NULL;
1118 int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
1119 struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
1120 (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1121 util_ldap_state_t *st = (util_ldap_state_t *)
1122 ap_get_module_config(r->server->module_config,
1126 * Stop looking at deeper levels of nested groups if we have reached the
1127 * max. Since we already checked the top-level group in uldap_cache_compare,
1128 * we don't need to check it again here - so if max_subgroup_depth is set
1129 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1130 * We'll be calling uldap_cache_compare from here to check if the user is
1131 * in the next level before we recurse into that next level looking for
1134 if (cur_subgroup_depth >= max_subgroup_depth) {
1135 return LDAP_COMPARE_FALSE;
1139 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1142 while ((base_sgcIndex < subgroupclasses->nelts)
1143 && (result != LDAP_COMPARE_TRUE)) {
1144 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1145 sgc_ents[base_sgcIndex].name);
1146 if (result != LDAP_COMPARE_TRUE) {
1151 if (result != LDAP_COMPARE_TRUE) {
1152 ldc->reason = "DN failed group verification.";
1157 * 2. Find previously created cache entry and check if there is already a
1162 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1163 LDAP_CACHE_UNLOCK();
1165 if (curl && curl->compare_cache) {
1166 /* make a comparison to the cache */
1169 the_compare_node.dn = (char *)dn;
1170 the_compare_node.attrib = (char *)"objectClass";
1171 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1172 the_compare_node.result = 0;
1173 the_compare_node.sgl_processed = 0;
1174 the_compare_node.subgroupList = NULL;
1176 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1179 if (compare_nodep != NULL) {
1181 * Found the generic group entry... but the user isn't in this
1182 * group or we wouldn't be here.
1184 if (compare_nodep->sgl_processed) {
1185 if (compare_nodep->subgroupList) {
1186 /* Make a local copy of the subgroup list */
1188 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1189 "[%" APR_PID_T_FMT "] util_ldap:"
1190 " Making local copy of SGL for "
1191 "group (%s)(objectClass=%s) ",
1193 (char *)sgc_ents[base_sgcIndex].name);
1194 tmp_local_sgl = apr_pcalloc(r->pool,
1195 sizeof(util_compare_subgroup_t));
1196 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1197 tmp_local_sgl->subgroupDNs =
1198 apr_pcalloc(r->pool,
1199 sizeof(char *) * compare_nodep->subgroupList->len);
1200 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1201 tmp_local_sgl->subgroupDNs[i] =
1202 apr_pstrdup(r->pool,
1203 compare_nodep->subgroupList->subgroupDNs[i]);
1207 sgl_cached_empty = 1;
1211 LDAP_CACHE_UNLOCK();
1214 if (!tmp_local_sgl && !sgl_cached_empty) {
1215 /* No Cached SGL, retrieve from LDAP */
1216 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1217 "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s,"
1218 " retrieving from LDAP" , getpid(), dn);
1219 tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1221 if (!tmp_local_sgl) {
1222 /* No SGL aailable via LDAP either */
1223 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1224 " util_ldap: no subgroups for %s" , getpid(), dn);
1227 if (curl && curl->compare_cache) {
1229 * Find the generic group cache entry and add the sgl we just retrieved.
1233 the_compare_node.dn = (char *)dn;
1234 the_compare_node.attrib = (char *)"objectClass";
1235 the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1236 the_compare_node.result = 0;
1237 the_compare_node.sgl_processed = 0;
1238 the_compare_node.subgroupList = NULL;
1240 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1243 if (compare_nodep == NULL) {
1245 * The group entry we want to attach our SGL to doesn't exist.
1246 * We only got here if we verified this DN was actually a group
1247 * based on the objectClass, but we can't call the compare function
1248 * while we already hold the cache lock -- only the insert.
1250 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1251 "[%" APR_PID_T_FMT "] util_ldap: Cache entry "
1252 "for %s doesn't exist",
1254 the_compare_node.result = LDAP_COMPARE_TRUE;
1255 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
1256 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1258 if (compare_nodep == NULL) {
1259 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1260 "[%" APR_PID_T_FMT "] util_ldap: Couldn't "
1261 "retrieve group entry for %s from cache",
1267 * We have a valid cache entry and a locally generated SGL.
1268 * Attach the SGL to the cache entry
1270 if (compare_nodep && !compare_nodep->sgl_processed) {
1271 if (!tmp_local_sgl) {
1272 /* We looked up an SGL for a group and found it to be empty */
1273 if (compare_nodep->subgroupList == NULL) {
1274 compare_nodep->sgl_processed = 1;
1278 util_compare_subgroup_t *sgl_copy =
1279 util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1280 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1281 "Copying local SGL of len %d for group %s into cache",
1282 tmp_local_sgl->len, dn);
1284 if (compare_nodep->subgroupList) {
1285 util_ald_sgl_free(curl->compare_cache,
1286 &(compare_nodep->subgroupList));
1288 compare_nodep->subgroupList = sgl_copy;
1289 compare_nodep->sgl_processed = 1;
1292 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1293 "Copy of SGL failed to obtain shared memory, "
1294 "couldn't update cache");
1298 LDAP_CACHE_UNLOCK();
1303 * tmp_local_sgl has either been created, or copied out of the cache
1304 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1307 result = LDAP_COMPARE_FALSE;
1308 if (!tmp_local_sgl) {
1312 while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1313 const char *group = NULL;
1314 group = tmp_local_sgl->subgroupDNs[sgindex];
1316 * 4. Now loop through the subgroupList and call uldap_cache_compare
1317 * to check for the user.
1319 result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1320 if (result == LDAP_COMPARE_TRUE) {
1322 * 4.A. We found the user in the subgroup. Return
1323 * LDAP_COMPARE_TRUE.
1325 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1326 " util_ldap: Found user %s in a subgroup (%s) at"
1327 " level %d of %d.", getpid(), r->user, group,
1328 cur_subgroup_depth+1, max_subgroup_depth);
1332 * 4.B. We didn't find the user in this subgroup, so recurse into
1333 * it and keep looking.
1335 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1336 " util_ldap: user %s not found in subgroup (%s) at"
1337 " level %d of %d.", getpid(), r->user, group,
1338 cur_subgroup_depth+1, max_subgroup_depth);
1339 result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1340 value, subgroupAttrs,
1342 cur_subgroup_depth+1,
1343 max_subgroup_depth);
1352 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1353 const char *url, const char *basedn,
1354 int scope, char **attrs, const char *filter,
1355 const char *bindpw, const char **binddn,
1356 const char ***retvals)
1358 const char **vals = NULL;
1361 LDAPMessage *res, *entry;
1365 util_url_node_t *curl; /* Cached URL node */
1366 util_url_node_t curnode;
1367 util_search_node_t *search_nodep; /* Cached search node */
1368 util_search_node_t the_search_node;
1371 util_ldap_state_t *st =
1372 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1375 /* Get the cache node for this url */
1378 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1381 curl = util_ald_create_caches(st, url);
1383 LDAP_CACHE_UNLOCK();
1387 the_search_node.username = filter;
1388 search_nodep = util_ald_cache_fetch(curl->search_cache,
1390 if (search_nodep != NULL) {
1392 /* found entry in search cache... */
1393 curtime = apr_time_now();
1396 * Remove this item from the cache if its expired. If the sent
1397 * password doesn't match the storepassword, the entry will
1398 * be removed and readded later if the credentials pass
1401 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1402 /* ...but entry is too old */
1403 util_ald_cache_remove(curl->search_cache, search_nodep);
1405 else if ( (search_nodep->bindpw)
1406 && (search_nodep->bindpw[0] != '\0')
1407 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1409 /* ...and entry is valid */
1410 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1414 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1415 while (search_nodep->vals[i]) {
1416 (*retvals)[i] = apr_pstrdup(r->pool,
1417 search_nodep->vals[i]);
1421 LDAP_CACHE_UNLOCK();
1422 ldc->reason = "Authentication successful (cached)";
1423 return LDAP_SUCCESS;
1426 /* unlock this read lock */
1427 LDAP_CACHE_UNLOCK();
1431 * At this point, there is no valid cached search, so lets do the search.
1435 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1438 if (failures++ > 10) {
1441 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1445 /* try do the search */
1446 if ((result = ldap_search_ext_s(ldc->ldap,
1447 (char *)basedn, scope,
1448 (char *)filter, attrs, 0,
1449 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1450 == LDAP_SERVER_DOWN)
1452 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1453 uldap_connection_unbind(ldc);
1457 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1458 if (result != LDAP_SUCCESS) {
1459 ldc->reason = "ldap_search_ext_s() for user failed";
1464 * We should have found exactly one entry; to find a different
1465 * number is an error.
1467 count = ldap_count_entries(ldc->ldap, res);
1471 ldc->reason = "User not found";
1473 ldc->reason = "User is not unique (search found two "
1476 return LDAP_NO_SUCH_OBJECT;
1479 entry = ldap_first_entry(ldc->ldap, res);
1481 /* Grab the dn, copy it into the pool, and free it again */
1482 dn = ldap_get_dn(ldc->ldap, entry);
1483 *binddn = apr_pstrdup(r->pool, dn);
1487 * A bind to the server with an empty password always succeeds, so
1488 * we check to ensure that the password is not empty. This implies
1489 * that users who actually do have empty passwords will never be
1490 * able to authenticate with this module. I don't see this as a big
1493 if (!bindpw || strlen(bindpw) <= 0) {
1495 ldc->reason = "Empty password not allowed";
1496 return LDAP_INVALID_CREDENTIALS;
1500 * Attempt to bind with the retrieved dn and the password. If the bind
1501 * fails, it means that the password is wrong (the dn obviously
1502 * exists, since we just retrieved it)
1504 if ((result = ldap_simple_bind_s(ldc->ldap,
1506 (char *)bindpw)) == LDAP_SERVER_DOWN) {
1507 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1508 "failed with server down";
1510 uldap_connection_unbind(ldc);
1514 /* failure? if so - return */
1515 if (result != LDAP_SUCCESS) {
1516 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1518 uldap_connection_unbind(ldc);
1523 * We have just bound the connection to a different user and password
1524 * combination, which might be reused unintentionally next time this
1525 * connection is used from the connection pool. To ensure no confusion,
1526 * we mark the connection as unbound.
1532 * Get values for the provided attributes.
1538 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1545 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1546 while (values && values[j]) {
1547 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1548 : apr_pstrdup(r->pool, values[j]);
1551 ldap_value_free(values);
1559 * Add the new username to the search cache.
1563 the_search_node.username = filter;
1564 the_search_node.dn = *binddn;
1565 the_search_node.bindpw = bindpw;
1566 the_search_node.lastbind = apr_time_now();
1567 the_search_node.vals = vals;
1568 the_search_node.numvals = numvals;
1570 /* Search again to make sure that another thread didn't ready insert
1571 * this node into the cache before we got here. If it does exist then
1572 * update the lastbind
1574 search_nodep = util_ald_cache_fetch(curl->search_cache,
1576 if ((search_nodep == NULL) ||
1577 (strcmp(*binddn, search_nodep->dn) != 0)) {
1579 /* Nothing in cache, insert new entry */
1580 util_ald_cache_insert(curl->search_cache, &the_search_node);
1582 else if ((!search_nodep->bindpw) ||
1583 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1585 /* Entry in cache is invalid, remove it and insert new one */
1586 util_ald_cache_remove(curl->search_cache, search_nodep);
1587 util_ald_cache_insert(curl->search_cache, &the_search_node);
1590 /* Cache entry is valid, update lastbind */
1591 search_nodep->lastbind = the_search_node.lastbind;
1593 LDAP_CACHE_UNLOCK();
1597 ldc->reason = "Authentication successful";
1598 return LDAP_SUCCESS;
1602 * This function will return the DN of the entry matching userid.
1603 * It is used to get the DN in case some other module than mod_auth_ldap
1604 * has authenticated the user.
1605 * The function is basically a copy of uldap_cache_checkuserid
1606 * with password checking removed.
1608 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1609 const char *url, const char *basedn,
1610 int scope, char **attrs, const char *filter,
1611 const char **binddn, const char ***retvals)
1613 const char **vals = NULL;
1616 LDAPMessage *res, *entry;
1620 util_url_node_t *curl; /* Cached URL node */
1621 util_url_node_t curnode;
1622 util_search_node_t *search_nodep; /* Cached search node */
1623 util_search_node_t the_search_node;
1626 util_ldap_state_t *st =
1627 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1630 /* Get the cache node for this url */
1633 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1636 curl = util_ald_create_caches(st, url);
1638 LDAP_CACHE_UNLOCK();
1642 the_search_node.username = filter;
1643 search_nodep = util_ald_cache_fetch(curl->search_cache,
1645 if (search_nodep != NULL) {
1647 /* found entry in search cache... */
1648 curtime = apr_time_now();
1651 * Remove this item from the cache if its expired.
1653 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1654 /* ...but entry is too old */
1655 util_ald_cache_remove(curl->search_cache, search_nodep);
1658 /* ...and entry is valid */
1659 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1663 *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1664 while (search_nodep->vals[i]) {
1665 (*retvals)[i] = apr_pstrdup(r->pool,
1666 search_nodep->vals[i]);
1670 LDAP_CACHE_UNLOCK();
1671 ldc->reason = "Search successful (cached)";
1672 return LDAP_SUCCESS;
1675 /* unlock this read lock */
1676 LDAP_CACHE_UNLOCK();
1680 * At this point, there is no valid cached search, so lets do the search.
1684 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1687 if (failures++ > 10) {
1690 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1694 /* try do the search */
1695 if ((result = ldap_search_ext_s(ldc->ldap,
1696 (char *)basedn, scope,
1697 (char *)filter, attrs, 0,
1698 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1699 == LDAP_SERVER_DOWN)
1701 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1702 uldap_connection_unbind(ldc);
1706 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1707 if (result != LDAP_SUCCESS) {
1708 ldc->reason = "ldap_search_ext_s() for user failed";
1713 * We should have found exactly one entry; to find a different
1714 * number is an error.
1716 count = ldap_count_entries(ldc->ldap, res);
1720 ldc->reason = "User not found";
1722 ldc->reason = "User is not unique (search found two "
1725 return LDAP_NO_SUCH_OBJECT;
1728 entry = ldap_first_entry(ldc->ldap, res);
1730 /* Grab the dn, copy it into the pool, and free it again */
1731 dn = ldap_get_dn(ldc->ldap, entry);
1732 *binddn = apr_pstrdup(r->pool, dn);
1736 * Get values for the provided attributes.
1742 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1749 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1750 while (values && values[j]) {
1751 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1752 : apr_pstrdup(r->pool, values[j]);
1755 ldap_value_free(values);
1763 * Add the new username to the search cache.
1767 the_search_node.username = filter;
1768 the_search_node.dn = *binddn;
1769 the_search_node.bindpw = NULL;
1770 the_search_node.lastbind = apr_time_now();
1771 the_search_node.vals = vals;
1772 the_search_node.numvals = numvals;
1774 /* Search again to make sure that another thread didn't ready insert
1775 * this node into the cache before we got here. If it does exist then
1776 * update the lastbind
1778 search_nodep = util_ald_cache_fetch(curl->search_cache,
1780 if ((search_nodep == NULL) ||
1781 (strcmp(*binddn, search_nodep->dn) != 0)) {
1783 /* Nothing in cache, insert new entry */
1784 util_ald_cache_insert(curl->search_cache, &the_search_node);
1787 * Don't update lastbind on entries with bindpw because
1788 * we haven't verified that password. It's OK to update
1789 * the entry if there is no password in it.
1791 else if (!search_nodep->bindpw) {
1792 /* Cache entry is valid, update lastbind */
1793 search_nodep->lastbind = the_search_node.lastbind;
1795 LDAP_CACHE_UNLOCK();
1800 ldc->reason = "Search successful";
1801 return LDAP_SUCCESS;
1805 * Reports if ssl support is enabled
1807 * 1 = enabled, 0 = not enabled
1809 static int uldap_ssl_supported(request_rec *r)
1811 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1812 r->server->module_config, &ldap_module);
1814 return(st->ssl_supported);
1818 /* ---------------------------------------- */
1819 /* config directives */
1822 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1825 util_ldap_state_t *st =
1826 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1828 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1834 st->cache_bytes = atol(bytes);
1836 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1837 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1838 " cache size to %" APR_SIZE_T_FMT " bytes.",
1839 getpid(), st->cache_bytes);
1844 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1847 util_ldap_state_t *st =
1848 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1850 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1857 st->cache_file = ap_server_root_relative(st->pool, file);
1860 st->cache_file = NULL;
1863 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1864 "LDAP cache: Setting shared memory cache file to %s bytes.",
1870 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1873 util_ldap_state_t *st =
1874 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1876 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1882 st->search_cache_ttl = atol(ttl) * 1000000;
1884 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1885 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1886 " microseconds.", getpid(), st->search_cache_ttl);
1891 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1894 util_ldap_state_t *st =
1895 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1897 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1903 st->search_cache_size = atol(size);
1904 if (st->search_cache_size < 0) {
1905 st->search_cache_size = 0;
1908 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1909 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1910 " to %ld entries.", getpid(), st->search_cache_size);
1915 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1918 util_ldap_state_t *st =
1919 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1921 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1927 st->compare_cache_ttl = atol(ttl) * 1000000;
1929 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1930 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1931 " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1936 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1939 util_ldap_state_t *st =
1940 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1942 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1948 st->compare_cache_size = atol(size);
1949 if (st->compare_cache_size < 0) {
1950 st->compare_cache_size = 0;
1953 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1954 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
1955 " to %ld entries.", getpid(), st->compare_cache_size);
1962 * Parse the certificate type.
1964 * The type can be one of the following:
1965 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1966 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1968 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1970 static int util_ldap_parse_cert_type(const char *type)
1972 /* Authority file in binary DER format */
1973 if (0 == strcasecmp("CA_DER", type)) {
1974 return APR_LDAP_CA_TYPE_DER;
1977 /* Authority file in Base64 format */
1978 else if (0 == strcasecmp("CA_BASE64", type)) {
1979 return APR_LDAP_CA_TYPE_BASE64;
1982 /* Netscape certificate database file/directory */
1983 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1984 return APR_LDAP_CA_TYPE_CERT7_DB;
1987 /* Netscape secmod file/directory */
1988 else if (0 == strcasecmp("CA_SECMOD", type)) {
1989 return APR_LDAP_CA_TYPE_SECMOD;
1992 /* Client cert file in DER format */
1993 else if (0 == strcasecmp("CERT_DER", type)) {
1994 return APR_LDAP_CERT_TYPE_DER;
1997 /* Client cert file in Base64 format */
1998 else if (0 == strcasecmp("CERT_BASE64", type)) {
1999 return APR_LDAP_CERT_TYPE_BASE64;
2002 /* Client cert file in PKCS#12 format */
2003 else if (0 == strcasecmp("CERT_PFX", type)) {
2004 return APR_LDAP_CERT_TYPE_PFX;
2007 /* Netscape client cert database file/directory */
2008 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2009 return APR_LDAP_CERT_TYPE_KEY3_DB;
2012 /* Netscape client cert nickname */
2013 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2014 return APR_LDAP_CERT_TYPE_NICKNAME;
2017 /* Client cert key file in DER format */
2018 else if (0 == strcasecmp("KEY_DER", type)) {
2019 return APR_LDAP_KEY_TYPE_DER;
2022 /* Client cert key file in Base64 format */
2023 else if (0 == strcasecmp("KEY_BASE64", type)) {
2024 return APR_LDAP_KEY_TYPE_BASE64;
2027 /* Client cert key file in PKCS#12 format */
2028 else if (0 == strcasecmp("KEY_PFX", type)) {
2029 return APR_LDAP_KEY_TYPE_PFX;
2033 return APR_LDAP_CA_TYPE_UNKNOWN;
2040 * Set LDAPTrustedGlobalCert.
2042 * This directive takes either two or three arguments:
2043 * - certificate type
2044 * - certificate file / directory / nickname
2045 * - certificate password (optional)
2047 * This directive may only be used globally.
2049 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2053 const char *password)
2055 util_ldap_state_t *st =
2056 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2058 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2062 apr_ldap_opt_tls_cert_t *cert;
2068 /* handle the certificate type */
2070 cert_type = util_ldap_parse_cert_type(type);
2071 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2072 return apr_psprintf(cmd->pool, "The certificate type %s is "
2073 "not recognised. It should be one "
2074 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2075 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2076 "CERT_KEY3_DB, CERT_NICKNAME, "
2077 "KEY_DER, KEY_BASE64", type);
2081 return "Certificate type was not specified.";
2084 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2085 "LDAP: SSL trusted global cert - %s (type %s)",
2088 /* add the certificate to the global array */
2089 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2090 cert->type = cert_type;
2092 cert->password = password;
2094 /* if file is a file or path, fix the path */
2095 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2096 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2098 cert->path = ap_server_root_relative(cmd->pool, file);
2100 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2103 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2104 "LDAP: Could not open SSL trusted certificate "
2105 "authority file - %s",
2106 cert->path == NULL ? file : cert->path);
2107 return "Invalid global certificate file path";
2116 * Set LDAPTrustedClientCert.
2118 * This directive takes either two or three arguments:
2119 * - certificate type
2120 * - certificate file / directory / nickname
2121 * - certificate password (optional)
2123 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2127 const char *password)
2129 util_ldap_state_t *st =
2130 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2135 apr_ldap_opt_tls_cert_t *cert;
2137 /* handle the certificate type */
2139 cert_type = util_ldap_parse_cert_type(type);
2140 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2141 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2142 "not recognised. It should be one "
2143 "of CERT_DER, CERT_BASE64, "
2144 "CERT_NICKNAME, CERT_PFX,"
2145 "KEY_DER, KEY_BASE64, KEY_PFX",
2148 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2149 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2150 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2151 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2152 APR_LDAP_CERT_TYPE_PFX == cert_type ||
2153 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2154 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2155 "only valid within a "
2156 "LDAPTrustedGlobalCert directive. "
2157 "Only CERT_DER, CERT_BASE64, "
2158 "CERT_NICKNAME, KEY_DER, and "
2159 "KEY_BASE64 may be used.", type);
2163 return "Certificate type was not specified.";
2166 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2167 "LDAP: SSL trusted client cert - %s (type %s)",
2170 /* add the certificate to the global array */
2171 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2172 cert->type = cert_type;
2174 cert->password = password;
2176 /* if file is a file or path, fix the path */
2177 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2178 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2180 cert->path = ap_server_root_relative(cmd->pool, file);
2182 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2185 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2186 "LDAP: Could not open SSL client certificate "
2188 cert->path == NULL ? file : cert->path);
2189 return "Invalid client certificate file path";
2199 * Set LDAPTrustedMode.
2201 * This directive sets what encryption mode to use on a connection:
2202 * - None (No encryption)
2203 * - SSL (SSL encryption)
2204 * - STARTTLS (TLS encryption)
2206 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2209 util_ldap_state_t *st =
2210 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2213 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2214 "LDAP: SSL trusted mode - %s",
2217 if (0 == strcasecmp("NONE", mode)) {
2218 st->secure = APR_LDAP_NONE;
2220 else if (0 == strcasecmp("SSL", mode)) {
2221 st->secure = APR_LDAP_SSL;
2223 else if ( (0 == strcasecmp("TLS", mode))
2224 || (0 == strcasecmp("STARTTLS", mode))) {
2225 st->secure = APR_LDAP_STARTTLS;
2228 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2229 "SSL, or TLS/STARTTLS";
2236 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2240 util_ldap_state_t *st =
2241 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2243 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2249 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2250 "LDAP: SSL verify server certificate - %s",
2251 mode?"TRUE":"FALSE");
2253 st->verify_svr_cert = mode;
2259 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2263 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2264 util_ldap_state_t *st =
2265 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2268 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2274 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2275 st->connectionTimeout = atol(ttl);
2277 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2278 "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2279 " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2281 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2282 "LDAP: Connection timout option not supported by the "
2283 "LDAP SDK in use." );
2290 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2292 util_ldap_state_t *st =
2293 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2295 /* Create a per vhost pool for mod_ldap to use, serialized with
2296 * st->mutex (also one per vhost). both are replicated by fork(),
2297 * no shared memory managed by either.
2299 apr_pool_create(&st->pool, p);
2301 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2304 st->cache_bytes = 100000;
2305 st->search_cache_ttl = 600000000;
2306 st->search_cache_size = 1024;
2307 st->compare_cache_ttl = 600000000;
2308 st->compare_cache_size = 1024;
2309 st->connections = NULL;
2310 st->ssl_supported = 0;
2311 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2312 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2313 st->secure = APR_LDAP_NONE;
2315 st->connectionTimeout = 10;
2316 st->verify_svr_cert = 1;
2321 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2324 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2325 util_ldap_state_t *base = (util_ldap_state_t *) basev;
2326 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2328 st->pool = overrides->pool;
2330 st->mutex = overrides->mutex;
2333 /* The cache settings can not be modified in a
2334 virtual host since all server use the same
2335 shared memory cache. */
2336 st->cache_bytes = base->cache_bytes;
2337 st->search_cache_ttl = base->search_cache_ttl;
2338 st->search_cache_size = base->search_cache_size;
2339 st->compare_cache_ttl = base->compare_cache_ttl;
2340 st->compare_cache_size = base->compare_cache_size;
2341 st->util_ldap_cache_lock = base->util_ldap_cache_lock;
2343 st->connections = NULL;
2344 st->ssl_supported = 0;
2345 st->global_certs = apr_array_append(p, base->global_certs,
2346 overrides->global_certs);
2347 st->client_certs = apr_array_append(p, base->client_certs,
2348 overrides->client_certs);
2349 st->secure = (overrides->secure_set == 0) ? base->secure
2350 : overrides->secure;
2352 /* These LDAP connection settings can not be overwritten in
2353 a virtual host. Once set in the base server, they must
2354 remain the same. None of the LDAP SDKs seem to be able
2355 to handle setting the verify_svr_cert flag on a
2356 per-connection basis. The OpenLDAP client appears to be
2357 able to handle the connection timeout per-connection
2358 but the Novell SDK cannot. Allowing the timeout to
2359 be set by each vhost is of little value so rather than
2360 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
2361 is being enforced on this setting as well. */
2362 st->connectionTimeout = base->connectionTimeout;
2363 st->verify_svr_cert = base->verify_svr_cert;
2368 static apr_status_t util_ldap_cleanup_module(void *data)
2371 server_rec *s = data;
2372 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2373 s->module_config, &ldap_module);
2375 if (st->ssl_supported) {
2376 apr_ldap_ssl_deinit();
2383 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2384 apr_pool_t *ptemp, server_rec *s)
2386 apr_status_t result;
2387 server_rec *s_vhost;
2388 util_ldap_state_t *st_vhost;
2390 util_ldap_state_t *st = (util_ldap_state_t *)
2391 ap_get_module_config(s->module_config,
2395 const char *userdata_key = "util_ldap_init";
2396 apr_ldap_err_t *result_err = NULL;
2399 /* util_ldap_post_config() will be called twice. Don't bother
2400 * going through all of the initialization on the first call
2401 * because it will just be thrown away.*/
2402 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2404 apr_pool_userdata_set((const void *)1, userdata_key,
2405 apr_pool_cleanup_null, s->process->pool);
2407 #if APR_HAS_SHARED_MEMORY
2408 /* If the cache file already exists then delete it. Otherwise we are
2409 * going to run into problems creating the shared memory. */
2410 if (st->cache_file) {
2411 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2413 apr_file_remove(lck_file, ptemp);
2419 #if APR_HAS_SHARED_MEMORY
2420 /* initializing cache if shared memory size is not zero and we already
2421 * don't have shm address
2423 if (!st->cache_shm && st->cache_bytes > 0) {
2425 result = util_ldap_cache_init(p, st);
2426 if (result != APR_SUCCESS) {
2427 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2428 "LDAP cache: could not create shared memory segment");
2433 #if APR_HAS_SHARED_MEMORY
2434 if (st->cache_file) {
2435 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2440 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2441 st->lock_file, APR_LOCK_DEFAULT,
2443 if (result != APR_SUCCESS) {
2447 #ifdef AP_NEED_SET_MUTEX_PERMS
2448 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2449 if (result != APR_SUCCESS) {
2450 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2451 "LDAP cache: failed to set mutex permissions");
2456 /* merge config in all vhost */
2459 st_vhost = (util_ldap_state_t *)
2460 ap_get_module_config(s_vhost->module_config,
2463 #if APR_HAS_SHARED_MEMORY
2464 st_vhost->cache_shm = st->cache_shm;
2465 st_vhost->cache_rmm = st->cache_rmm;
2466 st_vhost->cache_file = st->cache_file;
2467 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2468 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2469 "for VHOST: %s", st->cache_shm, st->cache_rmm,
2470 s_vhost->server_hostname);
2472 st_vhost->lock_file = st->lock_file;
2473 s_vhost = s_vhost->next;
2475 #if APR_HAS_SHARED_MEMORY
2478 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2479 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2480 "shared memory cache");
2484 /* log the LDAP SDK used
2487 apr_ldap_err_t *result = NULL;
2488 apr_ldap_info(p, &(result));
2489 if (result != NULL) {
2490 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2494 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2495 util_ldap_cleanup_module);
2498 * Initialize SSL support, and log the result for the benefit of the admin.
2500 * If SSL is not supported it is not necessarily an error, as the
2501 * application may not want to use it.
2503 rc = apr_ldap_ssl_init(p,
2507 if (APR_SUCCESS == rc) {
2508 rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2509 (void *)st->global_certs, &(result_err));
2512 if (APR_SUCCESS == rc) {
2513 st->ssl_supported = 1;
2514 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2515 "LDAP: SSL support available" );
2518 st->ssl_supported = 0;
2519 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2520 "LDAP: SSL support unavailable%s%s",
2521 result_err ? ": " : "",
2522 result_err ? result_err->reason : "");
2528 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2531 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2534 if (!st->util_ldap_cache_lock) return;
2536 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2538 if (sts != APR_SUCCESS) {
2539 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2540 "Failed to initialise global mutex %s in child process %"
2542 st->lock_file, getpid());
2546 static const command_rec util_ldap_cmds[] = {
2547 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2549 "Set the size of the shared memory cache (in bytes). Use "
2550 "0 to disable the shared memory cache. (default: 100000)"),
2552 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2554 "Set the file name for the shared memory cache."),
2556 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2558 "Set the maximum number of entries that are possible in the "
2559 "LDAP search cache. Use 0 for no limit. "
2560 "-1 disables the cache. (default: 1024)"),
2562 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2564 "Set the maximum time (in seconds) that an item can be "
2565 "cached in the LDAP search cache. Use 0 for no limit. "
2568 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2570 "Set the maximum number of entries that are possible "
2571 "in the LDAP compare cache. Use 0 for no limit. "
2572 "Use -1 to disable the cache. (default: 1024)"),
2574 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2576 "Set the maximum time (in seconds) that an item is cached "
2577 "in the LDAP operation cache. Use 0 for no limit. "
2580 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2582 "Takes three args; the file and/or directory containing "
2583 "the trusted CA certificates (and global client certs "
2584 "for Netware) used to validate the LDAP server. Second "
2585 "arg is the cert type for the first arg, one of CA_DER, "
2586 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2587 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2588 "Third arg is an optional passphrase if applicable."),
2590 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2592 "Takes three args; the file and/or directory containing "
2593 "the client certificate, or certificate ID used to "
2594 "validate this LDAP client. Second arg is the cert type "
2595 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2596 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2597 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2598 "optional passphrase if applicable."),
2600 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2602 "Specify the type of security that should be applied to "
2603 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2605 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2607 "Set to 'ON' requires that the server certificate be verified"
2608 " before a secure LDAP connection can be establish. Default"
2611 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2613 "Specify the LDAP socket connection timeout in seconds "
2619 static void util_ldap_register_hooks(apr_pool_t *p)
2621 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2622 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2623 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2624 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2625 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2626 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2627 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2628 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2629 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2630 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2631 APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2633 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2634 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2635 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2638 module AP_MODULE_DECLARE_DATA ldap_module = {
2639 STANDARD20_MODULE_STUFF,
2640 NULL, /* create dir config */
2641 NULL, /* merge dir config */
2642 util_ldap_create_config, /* create server config */
2643 util_ldap_merge_config, /* merge server config */
2644 util_ldap_cmds, /* command table */
2645 util_ldap_register_hooks, /* set up request processing hooks */