1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * util_ldap.c: LDAP things
20 * Original code from auth_ldap module for Apache v1.3:
21 * Copyright 1998, 1999 Enbridge Pipelines Inc.
22 * Copyright 1999-2001 Dave Carrigan
26 #include "http_config.h"
27 #include "http_core.h"
29 #include "http_protocol.h"
30 #include "http_request.h"
31 #include "util_ldap.h"
32 #include "util_ldap_cache.h"
34 #include <apr_strings.h>
41 #error mod_ldap requires APR-util to have LDAP support built in
44 #ifdef AP_NEED_SET_MUTEX_PERMS
48 /* Default define for ldap functions that need a SIZELIMIT but
49 * do not have the define
50 * XXX This should be removed once a supporting #define is
51 * released through APR-Util.
53 #ifndef APR_LDAP_SIZELIMIT
54 #define APR_LDAP_SIZELIMIT -1
57 module AP_MODULE_DECLARE_DATA ldap_module;
59 #define LDAP_CACHE_LOCK() do { \
60 if (st->util_ldap_cache_lock) \
61 apr_global_mutex_lock(st->util_ldap_cache_lock); \
64 #define LDAP_CACHE_UNLOCK() do { \
65 if (st->util_ldap_cache_lock) \
66 apr_global_mutex_unlock(st->util_ldap_cache_lock); \
69 static void util_ldap_strdup (char **str, const char *newstr)
77 *str = strdup(newstr);
85 * This handler generates a status page about the current performance of
86 * the LDAP cache. It is enabled as follows:
88 * <Location /ldap-status>
89 * SetHandler ldap-status
93 static int util_ldap_handler(request_rec *r)
95 util_ldap_state_t *st = (util_ldap_state_t *)
96 ap_get_module_config(r->server->module_config,
99 r->allowed |= (1 << M_GET);
100 if (r->method_number != M_GET)
103 if (strcmp(r->handler, "ldap-status")) {
107 ap_set_content_type(r, "text/html");
112 ap_rputs(DOCTYPE_HTML_3_2
113 "<html><head><title>LDAP Cache Information</title></head>\n", r);
114 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
117 util_ald_cache_display(r, st);
122 /* ------------------------------------------------------------------ */
126 * Closes an LDAP connection by unlocking it. The next time
127 * uldap_connection_find() is called this connection will be
128 * available for reuse.
130 static void uldap_connection_close(util_ldap_connection_t *ldc)
136 * Is it safe leaving bound connections floating around between the
137 * different modules? Keeping the user bound is a performance boost,
138 * but it is also a potential security problem - maybe.
140 * For now we unbind the user when we finish with a connection, but
141 * we don't have to...
144 /* mark our connection as available for reuse */
147 apr_thread_mutex_unlock(ldc->lock);
153 * Destroys an LDAP connection by unbinding and closing the connection to
154 * the LDAP server. It is used to bring the connection back to a known
155 * state after an error, and during pool cleanup.
157 static apr_status_t uldap_connection_unbind(void *param)
159 util_ldap_connection_t *ldc = param;
163 ldap_unbind_s(ldc->ldap);
174 * Clean up an LDAP connection by unbinding and unlocking the connection.
175 * This function is registered with the pool cleanup function - causing
176 * the LDAP connections to be shut down cleanly on graceful restart.
178 static apr_status_t uldap_connection_cleanup(void *param)
180 util_ldap_connection_t *ldc = param;
184 /* unbind and disconnect from the LDAP server */
185 uldap_connection_unbind(ldc);
187 /* free the username and password */
189 free((void*)ldc->bindpw);
192 free((void*)ldc->binddn);
195 /* unlock this entry */
196 uldap_connection_close(ldc);
203 static int uldap_connection_init(request_rec *r,
204 util_ldap_connection_t *ldc )
207 int version = LDAP_VERSION3;
208 apr_ldap_err_t *result = NULL;
209 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
210 util_ldap_state_t *st =
211 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
214 /* Since the host will include a port if the default port is not used,
215 * always specify the default ports for the port parameter. This will
216 * allow a host string that contains multiple hosts the ability to mix
217 * some hosts with ports and some without. All hosts which do not
218 * specify a port will use the default port.
220 apr_ldap_init(ldc->pool, &(ldc->ldap),
222 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
227 if (result != NULL && result->rc) {
228 ldc->reason = result->reason;
231 if (NULL == ldc->ldap)
234 if (NULL == ldc->reason) {
235 ldc->reason = "LDAP: ldap initialization failed";
238 ldc->reason = result->reason;
243 /* always default to LDAP V3 */
244 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
246 /* set client certificates */
247 if (!apr_is_empty_array(ldc->client_certs)) {
248 apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
249 ldc->client_certs, &(result));
250 if (LDAP_SUCCESS != result->rc) {
251 uldap_connection_unbind( ldc );
252 ldc->reason = result->reason;
257 /* switch on SSL/TLS */
258 if (APR_LDAP_NONE != ldc->secure) {
259 apr_ldap_set_option(ldc->pool, ldc->ldap,
260 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
261 if (LDAP_SUCCESS != result->rc) {
262 uldap_connection_unbind( ldc );
263 ldc->reason = result->reason;
268 /* Set the alias dereferencing option */
269 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
271 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
272 #ifdef APR_LDAP_OPT_VERIFY_CERT
273 apr_ldap_set_option(ldc->pool, ldc->ldap,
274 APR_LDAP_OPT_VERIFY_CERT, &(st->verify_svr_cert), &(result));
276 #if defined(LDAPSSL_VERIFY_SERVER)
277 if (st->verify_svr_cert) {
278 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
281 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
283 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
284 /* This is not a per-connection setting so just pass NULL for the
285 Ldap connection handle */
286 if (st->verify_svr_cert) {
287 int i = LDAP_OPT_X_TLS_DEMAND;
288 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
291 int i = LDAP_OPT_X_TLS_NEVER;
292 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
297 #ifdef LDAP_OPT_NETWORK_TIMEOUT
298 if (st->connectionTimeout > 0) {
299 timeOut.tv_sec = st->connectionTimeout;
302 if (st->connectionTimeout >= 0) {
303 rc = apr_ldap_set_option(ldc->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
304 (void *)&timeOut, &(result));
305 if (APR_SUCCESS != rc) {
306 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
307 "LDAP: Could not set the connection timeout");
316 * Connect to the LDAP server and binds. Does not connect if already
317 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
319 * Returns LDAP_SUCCESS on success; and an error code on failure
321 static int uldap_connection_open(request_rec *r,
322 util_ldap_connection_t *ldc)
327 /* sanity check for NULL */
332 /* If the connection is already bound, return
336 ldc->reason = "LDAP: connection open successful (already bound)";
340 /* create the ldap session handle
342 if (NULL == ldc->ldap)
344 rc = uldap_connection_init( r, ldc );
345 if (LDAP_SUCCESS != rc)
352 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
353 * returned. Break out of the loop on Success or any other error.
355 * NOTE: Looping is probably not a great idea. If the server isn't
356 * responding the chances it will respond after a few tries are poor.
357 * However, the original code looped and it only happens on
358 * the error condition.
360 for (failures=0; failures<10; failures++)
362 rc = ldap_simple_bind_s(ldc->ldap,
364 (char *)ldc->bindpw);
365 if (LDAP_SERVER_DOWN != rc) {
367 } else if (failures == 5) {
368 /* attempt to init the connection once again */
369 uldap_connection_unbind( ldc );
370 rc = uldap_connection_init( r, ldc );
371 if (LDAP_SUCCESS != rc)
378 /* free the handle if there was an error
380 if (LDAP_SUCCESS != rc)
382 uldap_connection_unbind(ldc);
383 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
387 ldc->reason = "LDAP: connection open successful";
395 * Compare client certificate arrays.
397 * Returns 1 on compare failure, 0 otherwise.
399 static int compare_client_certs(apr_array_header_t *srcs,
400 apr_array_header_t *dests)
403 struct apr_ldap_opt_tls_cert_t *src, *dest;
405 /* arrays both NULL? if so, then equal */
406 if (srcs == NULL && dests == NULL) {
410 /* arrays different length or either NULL? If so, then not equal */
411 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
415 /* run an actual comparison */
416 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
417 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
418 for (i = 0; i < srcs->nelts; i++) {
419 if (strcmp(src[i].path, dest[i].path) ||
420 strcmp(src[i].password, dest[i].password) ||
421 src[i].type != dest[i].type) {
426 /* if we got here, the cert arrays were identical */
433 * Find an existing ldap connection struct that matches the
434 * provided ldap connection parameters.
436 * If not found in the cache, a new ldc structure will be allocated
437 * from st->pool and returned to the caller. If found in the cache,
438 * a pointer to the existing ldc structure will be returned.
440 static util_ldap_connection_t *
441 uldap_connection_find(request_rec *r,
442 const char *host, int port,
443 const char *binddn, const char *bindpw,
444 deref_options deref, int secure)
446 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
447 int secureflag = secure;
449 util_ldap_state_t *st =
450 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
455 /* mutex lock this function */
456 apr_thread_mutex_lock(st->mutex);
459 if (secure < APR_LDAP_NONE) {
460 secureflag = st->secure;
463 /* Search for an exact connection match in the list that is not
466 for (l=st->connections,p=NULL; l; l=l->next) {
468 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
470 if ( (l->port == port) && (strcmp(l->host, host) == 0)
471 && ((!l->binddn && !binddn) || (l->binddn && binddn
472 && !strcmp(l->binddn, binddn)))
473 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
474 && !strcmp(l->bindpw, bindpw)))
475 && (l->deref == deref) && (l->secure == secureflag)
476 && !compare_client_certs(st->client_certs, l->client_certs))
481 /* If this connection didn't match the criteria, then we
482 * need to unlock the mutex so it is available to be reused.
484 apr_thread_mutex_unlock(l->lock);
490 /* If nothing found, search again, but we don't care about the
491 * binddn and bindpw this time.
494 for (l=st->connections,p=NULL; l; l=l->next) {
496 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
499 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
500 (l->deref == deref) && (l->secure == secureflag) &&
501 !compare_client_certs(st->client_certs, l->client_certs))
503 /* the bind credentials have changed */
505 util_ldap_strdup((char**)&(l->binddn), binddn);
506 util_ldap_strdup((char**)&(l->bindpw), bindpw);
510 /* If this connection didn't match the criteria, then we
511 * need to unlock the mutex so it is available to be reused.
513 apr_thread_mutex_unlock(l->lock);
520 /* artificially disable cache */
523 /* If no connection what found after the second search, we
529 * Add the new connection entry to the linked list. Note that we
530 * don't actually establish an LDAP connection yet; that happens
531 * the first time authentication is requested.
533 /* create the details to the pool in st */
534 l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
536 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
537 apr_thread_mutex_lock(l->lock);
541 l->host = apr_pstrdup(st->pool, host);
544 util_ldap_strdup((char**)&(l->binddn), binddn);
545 util_ldap_strdup((char**)&(l->bindpw), bindpw);
547 /* The security mode after parsing the URL will always be either
548 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
549 * If the security setting is NONE, override it to the security
550 * setting optionally supplied by the admin using LDAPTrustedMode
552 l->secure = secureflag;
554 /* save away a copy of the client cert list that is presently valid */
555 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
557 /* add the cleanup to the pool */
558 apr_pool_cleanup_register(l->pool, l,
559 uldap_connection_cleanup,
560 apr_pool_cleanup_null);
571 apr_thread_mutex_unlock(st->mutex);
576 /* ------------------------------------------------------------------ */
579 * Compares two DNs to see if they're equal. The only way to do this correctly
580 * is to search for the dn and then do ldap_get_dn() on the result. This should
581 * match the initial dn, since it would have been also retrieved with
582 * ldap_get_dn(). This is expensive, so if the configuration value
583 * compare_dn_on_server is false, just does an ordinary strcmp.
585 * The lock for the ldap cache should already be acquired.
587 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
588 const char *url, const char *dn,
589 const char *reqdn, int compare_dn_on_server)
592 util_url_node_t *curl;
593 util_url_node_t curnode;
594 util_dn_compare_node_t *node;
595 util_dn_compare_node_t newnode;
597 LDAPMessage *res, *entry;
600 util_ldap_state_t *st = (util_ldap_state_t *)
601 ap_get_module_config(r->server->module_config,
604 /* get cache entry (or create one) */
608 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
610 curl = util_ald_create_caches(st, url);
614 /* a simple compare? */
615 if (!compare_dn_on_server) {
616 /* unlock this read lock */
617 if (strcmp(dn, reqdn)) {
618 ldc->reason = "DN Comparison FALSE (direct strcmp())";
619 return LDAP_COMPARE_FALSE;
622 ldc->reason = "DN Comparison TRUE (direct strcmp())";
623 return LDAP_COMPARE_TRUE;
628 /* no - it's a server side compare */
631 /* is it in the compare cache? */
632 newnode.reqdn = (char *)reqdn;
633 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
635 /* If it's in the cache, it's good */
636 /* unlock this read lock */
638 ldc->reason = "DN Comparison TRUE (cached)";
639 return LDAP_COMPARE_TRUE;
642 /* unlock this read lock */
647 if (failures++ > 10) {
648 /* too many failures */
652 /* make a server connection */
653 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
654 /* connect to server failed */
658 /* search for reqdn */
659 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
660 "(objectclass=*)", NULL, 1,
661 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
664 ldc->reason = "DN Comparison ldap_search_ext_s() "
665 "failed with server down";
666 uldap_connection_unbind(ldc);
669 if (result != LDAP_SUCCESS) {
670 /* search for reqdn failed - no match */
671 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
675 entry = ldap_first_entry(ldc->ldap, res);
676 searchdn = ldap_get_dn(ldc->ldap, entry);
679 if (strcmp(dn, searchdn) != 0) {
680 /* compare unsuccessful */
681 ldc->reason = "DN Comparison FALSE (checked on server)";
682 result = LDAP_COMPARE_FALSE;
686 /* compare successful - add to the compare cache */
688 newnode.reqdn = (char *)reqdn;
689 newnode.dn = (char *)dn;
691 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
693 || (strcmp(reqdn, node->reqdn) != 0)
694 || (strcmp(dn, node->dn) != 0))
696 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
700 ldc->reason = "DN Comparison TRUE (checked on server)";
701 result = LDAP_COMPARE_TRUE;
703 ldap_memfree(searchdn);
709 * Does an generic ldap_compare operation. It accepts a cache that it will use
710 * to lookup the compare in the cache. We cache two kinds of compares
711 * (require group compares) and (require user compares). Each compare has a different
712 * cache node: require group includes the DN; require user does not because the
713 * require user cache is owned by the
716 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
717 const char *url, const char *dn,
718 const char *attrib, const char *value)
721 util_url_node_t *curl;
722 util_url_node_t curnode;
723 util_compare_node_t *compare_nodep;
724 util_compare_node_t the_compare_node;
725 apr_time_t curtime = 0; /* silence gcc -Wall */
728 util_ldap_state_t *st = (util_ldap_state_t *)
729 ap_get_module_config(r->server->module_config,
732 /* get cache entry (or create one) */
735 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
737 curl = util_ald_create_caches(st, url);
742 /* make a comparison to the cache */
744 curtime = apr_time_now();
746 the_compare_node.dn = (char *)dn;
747 the_compare_node.attrib = (char *)attrib;
748 the_compare_node.value = (char *)value;
749 the_compare_node.result = 0;
751 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
754 if (compare_nodep != NULL) {
756 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
757 /* ...but it is too old */
758 util_ald_cache_remove(curl->compare_cache, compare_nodep);
761 /* ...and it is good */
762 /* unlock this read lock */
764 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
765 ldc->reason = "Comparison true (cached)";
766 return compare_nodep->result;
768 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
769 ldc->reason = "Comparison false (cached)";
770 return compare_nodep->result;
772 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
773 ldc->reason = "Comparison no such attribute (cached)";
774 return compare_nodep->result;
777 ldc->reason = "Comparison undefined (cached)";
778 return compare_nodep->result;
782 /* unlock this read lock */
787 if (failures++ > 10) {
788 /* too many failures */
791 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
796 if ((result = ldap_compare_s(ldc->ldap,
800 == LDAP_SERVER_DOWN) {
801 /* connection failed - try again */
802 ldc->reason = "ldap_compare_s() failed with server down";
803 uldap_connection_unbind(ldc);
807 ldc->reason = "Comparison complete";
808 if ((LDAP_COMPARE_TRUE == result) ||
809 (LDAP_COMPARE_FALSE == result) ||
810 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
812 /* compare completed; caching result */
814 the_compare_node.lastcompare = curtime;
815 the_compare_node.result = result;
817 /* If the node doesn't exist then insert it, otherwise just update
818 * it with the last results
820 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
822 if ( (compare_nodep == NULL)
823 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
824 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
825 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
827 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
830 compare_nodep->lastcompare = curtime;
831 compare_nodep->result = result;
835 if (LDAP_COMPARE_TRUE == result) {
836 ldc->reason = "Comparison true (adding to cache)";
837 return LDAP_COMPARE_TRUE;
839 else if (LDAP_COMPARE_FALSE == result) {
840 ldc->reason = "Comparison false (adding to cache)";
841 return LDAP_COMPARE_FALSE;
844 ldc->reason = "Comparison no such attribute (adding to cache)";
845 return LDAP_NO_SUCH_ATTRIBUTE;
851 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
852 const char *url, const char *basedn,
853 int scope, char **attrs, const char *filter,
854 const char *bindpw, const char **binddn,
855 const char ***retvals)
857 const char **vals = NULL;
860 LDAPMessage *res, *entry;
864 util_url_node_t *curl; /* Cached URL node */
865 util_url_node_t curnode;
866 util_search_node_t *search_nodep; /* Cached search node */
867 util_search_node_t the_search_node;
870 util_ldap_state_t *st =
871 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
874 /* Get the cache node for this url */
877 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
880 curl = util_ald_create_caches(st, url);
886 the_search_node.username = filter;
887 search_nodep = util_ald_cache_fetch(curl->search_cache,
889 if (search_nodep != NULL) {
891 /* found entry in search cache... */
892 curtime = apr_time_now();
895 * Remove this item from the cache if its expired. If the sent
896 * password doesn't match the storepassword, the entry will
897 * be removed and readded later if the credentials pass
900 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
901 /* ...but entry is too old */
902 util_ald_cache_remove(curl->search_cache, search_nodep);
904 else if ( (search_nodep->bindpw)
905 && (search_nodep->bindpw[0] != '\0')
906 && (strcmp(search_nodep->bindpw, bindpw) == 0))
908 /* ...and entry is valid */
909 *binddn = search_nodep->dn;
910 *retvals = search_nodep->vals;
912 ldc->reason = "Authentication successful (cached)";
916 /* unlock this read lock */
921 * At this point, there is no valid cached search, so lets do the search.
925 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
928 if (failures++ > 10) {
931 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
935 /* try do the search */
936 if ((result = ldap_search_ext_s(ldc->ldap,
937 (char *)basedn, scope,
938 (char *)filter, attrs, 0,
939 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
942 ldc->reason = "ldap_search_ext_s() for user failed with server down";
943 uldap_connection_unbind(ldc);
947 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
948 if (result != LDAP_SUCCESS) {
949 ldc->reason = "ldap_search_ext_s() for user failed";
954 * We should have found exactly one entry; to find a different
955 * number is an error.
957 count = ldap_count_entries(ldc->ldap, res);
961 ldc->reason = "User not found";
963 ldc->reason = "User is not unique (search found two "
966 return LDAP_NO_SUCH_OBJECT;
969 entry = ldap_first_entry(ldc->ldap, res);
971 /* Grab the dn, copy it into the pool, and free it again */
972 dn = ldap_get_dn(ldc->ldap, entry);
973 *binddn = apr_pstrdup(r->pool, dn);
977 * A bind to the server with an empty password always succeeds, so
978 * we check to ensure that the password is not empty. This implies
979 * that users who actually do have empty passwords will never be
980 * able to authenticate with this module. I don't see this as a big
983 if (!bindpw || strlen(bindpw) <= 0) {
985 ldc->reason = "Empty password not allowed";
986 return LDAP_INVALID_CREDENTIALS;
990 * Attempt to bind with the retrieved dn and the password. If the bind
991 * fails, it means that the password is wrong (the dn obviously
992 * exists, since we just retrieved it)
994 if ((result = ldap_simple_bind_s(ldc->ldap,
996 (char *)bindpw)) == LDAP_SERVER_DOWN) {
997 ldc->reason = "ldap_simple_bind_s() to check user credentials "
998 "failed with server down";
1000 uldap_connection_unbind(ldc);
1004 /* failure? if so - return */
1005 if (result != LDAP_SUCCESS) {
1006 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1008 uldap_connection_unbind(ldc);
1013 * We have just bound the connection to a different user and password
1014 * combination, which might be reused unintentionally next time this
1015 * connection is used from the connection pool. To ensure no confusion,
1016 * we mark the connection as unbound.
1022 * Get values for the provided attributes.
1028 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1035 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1036 while (values && values[j]) {
1037 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1038 : apr_pstrdup(r->pool, values[j]);
1041 ldap_value_free(values);
1049 * Add the new username to the search cache.
1053 the_search_node.username = filter;
1054 the_search_node.dn = *binddn;
1055 the_search_node.bindpw = bindpw;
1056 the_search_node.lastbind = apr_time_now();
1057 the_search_node.vals = vals;
1058 the_search_node.numvals = numvals;
1060 /* Search again to make sure that another thread didn't ready insert
1061 * this node into the cache before we got here. If it does exist then
1062 * update the lastbind
1064 search_nodep = util_ald_cache_fetch(curl->search_cache,
1066 if ((search_nodep == NULL) ||
1067 (strcmp(*binddn, search_nodep->dn) != 0)) {
1069 /* Nothing in cache, insert new entry */
1070 util_ald_cache_insert(curl->search_cache, &the_search_node);
1072 else if ((!search_nodep->bindpw) ||
1073 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1075 /* Entry in cache is invalid, remove it and insert new one */
1076 util_ald_cache_remove(curl->search_cache, search_nodep);
1077 util_ald_cache_insert(curl->search_cache, &the_search_node);
1080 /* Cache entry is valid, update lastbind */
1081 search_nodep->lastbind = the_search_node.lastbind;
1083 LDAP_CACHE_UNLOCK();
1087 ldc->reason = "Authentication successful";
1088 return LDAP_SUCCESS;
1092 * This function will return the DN of the entry matching userid.
1093 * It is used to get the DN in case some other module than mod_auth_ldap
1094 * has authenticated the user.
1095 * The function is basically a copy of uldap_cache_checkuserid
1096 * with password checking removed.
1098 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1099 const char *url, const char *basedn,
1100 int scope, char **attrs, const char *filter,
1101 const char **binddn, const char ***retvals)
1103 const char **vals = NULL;
1106 LDAPMessage *res, *entry;
1110 util_url_node_t *curl; /* Cached URL node */
1111 util_url_node_t curnode;
1112 util_search_node_t *search_nodep; /* Cached search node */
1113 util_search_node_t the_search_node;
1116 util_ldap_state_t *st =
1117 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1120 /* Get the cache node for this url */
1123 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1126 curl = util_ald_create_caches(st, url);
1128 LDAP_CACHE_UNLOCK();
1132 the_search_node.username = filter;
1133 search_nodep = util_ald_cache_fetch(curl->search_cache,
1135 if (search_nodep != NULL) {
1137 /* found entry in search cache... */
1138 curtime = apr_time_now();
1141 * Remove this item from the cache if its expired.
1143 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1144 /* ...but entry is too old */
1145 util_ald_cache_remove(curl->search_cache, search_nodep);
1148 /* ...and entry is valid */
1149 *binddn = search_nodep->dn;
1150 *retvals = search_nodep->vals;
1151 LDAP_CACHE_UNLOCK();
1152 ldc->reason = "Search successful (cached)";
1153 return LDAP_SUCCESS;
1156 /* unlock this read lock */
1157 LDAP_CACHE_UNLOCK();
1161 * At this point, there is no valid cached search, so lets do the search.
1165 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1168 if (failures++ > 10) {
1171 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1175 /* try do the search */
1176 if ((result = ldap_search_ext_s(ldc->ldap,
1177 (char *)basedn, scope,
1178 (char *)filter, attrs, 0,
1179 NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res))
1180 == LDAP_SERVER_DOWN)
1182 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1183 uldap_connection_unbind(ldc);
1187 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1188 if (result != LDAP_SUCCESS) {
1189 ldc->reason = "ldap_search_ext_s() for user failed";
1194 * We should have found exactly one entry; to find a different
1195 * number is an error.
1197 count = ldap_count_entries(ldc->ldap, res);
1201 ldc->reason = "User not found";
1203 ldc->reason = "User is not unique (search found two "
1206 return LDAP_NO_SUCH_OBJECT;
1209 entry = ldap_first_entry(ldc->ldap, res);
1211 /* Grab the dn, copy it into the pool, and free it again */
1212 dn = ldap_get_dn(ldc->ldap, entry);
1213 *binddn = apr_pstrdup(r->pool, dn);
1217 * Get values for the provided attributes.
1223 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1230 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1231 while (values && values[j]) {
1232 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1233 : apr_pstrdup(r->pool, values[j]);
1236 ldap_value_free(values);
1244 * Add the new username to the search cache.
1248 the_search_node.username = filter;
1249 the_search_node.dn = *binddn;
1250 the_search_node.bindpw = NULL;
1251 the_search_node.lastbind = apr_time_now();
1252 the_search_node.vals = vals;
1253 the_search_node.numvals = numvals;
1255 /* Search again to make sure that another thread didn't ready insert
1256 * this node into the cache before we got here. If it does exist then
1257 * update the lastbind
1259 search_nodep = util_ald_cache_fetch(curl->search_cache,
1261 if ((search_nodep == NULL) ||
1262 (strcmp(*binddn, search_nodep->dn) != 0)) {
1264 /* Nothing in cache, insert new entry */
1265 util_ald_cache_insert(curl->search_cache, &the_search_node);
1268 * Don't update lastbind on entries with bindpw because
1269 * we haven't verified that password. It's OK to update
1270 * the entry if there is no password in it.
1272 else if (!search_nodep->bindpw) {
1273 /* Cache entry is valid, update lastbind */
1274 search_nodep->lastbind = the_search_node.lastbind;
1276 LDAP_CACHE_UNLOCK();
1281 ldc->reason = "Search successful";
1282 return LDAP_SUCCESS;
1286 * Reports if ssl support is enabled
1288 * 1 = enabled, 0 = not enabled
1290 static int uldap_ssl_supported(request_rec *r)
1292 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1293 r->server->module_config, &ldap_module);
1295 return(st->ssl_supported);
1299 /* ---------------------------------------- */
1300 /* config directives */
1303 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1306 util_ldap_state_t *st =
1307 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1309 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1315 st->cache_bytes = atol(bytes);
1317 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1318 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1319 " cache size to %" APR_SIZE_T_FMT " bytes.",
1320 getpid(), st->cache_bytes);
1325 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1328 util_ldap_state_t *st =
1329 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1331 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1338 st->cache_file = ap_server_root_relative(st->pool, file);
1341 st->cache_file = NULL;
1344 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1345 "LDAP cache: Setting shared memory cache file to %s bytes.",
1351 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1354 util_ldap_state_t *st =
1355 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1357 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1363 st->search_cache_ttl = atol(ttl) * 1000000;
1365 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1366 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld microseconds.",
1367 getpid(), st->search_cache_ttl);
1372 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1375 util_ldap_state_t *st =
1376 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1378 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1384 st->search_cache_size = atol(size);
1385 if (st->search_cache_size < 0) {
1386 st->search_cache_size = 0;
1389 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1390 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size to %ld entries.",
1391 getpid(), st->search_cache_size);
1396 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1399 util_ldap_state_t *st =
1400 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1402 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1408 st->compare_cache_ttl = atol(ttl) * 1000000;
1410 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1411 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL to %ld microseconds.",
1412 getpid(), st->compare_cache_ttl);
1417 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1420 util_ldap_state_t *st =
1421 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1423 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1429 st->compare_cache_size = atol(size);
1430 if (st->compare_cache_size < 0) {
1431 st->compare_cache_size = 0;
1434 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1435 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size to %ld "
1436 "entries.", getpid(), st->compare_cache_size);
1443 * Parse the certificate type.
1445 * The type can be one of the following:
1446 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1447 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1449 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1451 static int util_ldap_parse_cert_type(const char *type)
1453 /* Authority file in binary DER format */
1454 if (0 == strcasecmp("CA_DER", type)) {
1455 return APR_LDAP_CA_TYPE_DER;
1458 /* Authority file in Base64 format */
1459 else if (0 == strcasecmp("CA_BASE64", type)) {
1460 return APR_LDAP_CA_TYPE_BASE64;
1463 /* Netscape certificate database file/directory */
1464 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1465 return APR_LDAP_CA_TYPE_CERT7_DB;
1468 /* Netscape secmod file/directory */
1469 else if (0 == strcasecmp("CA_SECMOD", type)) {
1470 return APR_LDAP_CA_TYPE_SECMOD;
1473 /* Client cert file in DER format */
1474 else if (0 == strcasecmp("CERT_DER", type)) {
1475 return APR_LDAP_CERT_TYPE_DER;
1478 /* Client cert file in Base64 format */
1479 else if (0 == strcasecmp("CERT_BASE64", type)) {
1480 return APR_LDAP_CERT_TYPE_BASE64;
1483 /* Client cert file in PKCS#12 format */
1484 else if (0 == strcasecmp("CERT_PFX", type)) {
1485 return APR_LDAP_CERT_TYPE_PFX;
1488 /* Netscape client cert database file/directory */
1489 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1490 return APR_LDAP_CERT_TYPE_KEY3_DB;
1493 /* Netscape client cert nickname */
1494 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1495 return APR_LDAP_CERT_TYPE_NICKNAME;
1498 /* Client cert key file in DER format */
1499 else if (0 == strcasecmp("KEY_DER", type)) {
1500 return APR_LDAP_KEY_TYPE_DER;
1503 /* Client cert key file in Base64 format */
1504 else if (0 == strcasecmp("KEY_BASE64", type)) {
1505 return APR_LDAP_KEY_TYPE_BASE64;
1508 /* Client cert key file in PKCS#12 format */
1509 else if (0 == strcasecmp("KEY_PFX", type)) {
1510 return APR_LDAP_KEY_TYPE_PFX;
1514 return APR_LDAP_CA_TYPE_UNKNOWN;
1521 * Set LDAPTrustedGlobalCert.
1523 * This directive takes either two or three arguments:
1524 * - certificate type
1525 * - certificate file / directory / nickname
1526 * - certificate password (optional)
1528 * This directive may only be used globally.
1530 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
1534 const char *password)
1536 util_ldap_state_t *st =
1537 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1539 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1543 apr_ldap_opt_tls_cert_t *cert;
1549 /* handle the certificate type */
1551 cert_type = util_ldap_parse_cert_type(type);
1552 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1553 return apr_psprintf(cmd->pool, "The certificate type %s is "
1554 "not recognised. It should be one "
1555 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1556 "CA_SECMOD, CERT_DER, CERT_BASE64, "
1557 "CERT_KEY3_DB, CERT_NICKNAME, "
1558 "KEY_DER, KEY_BASE64", type);
1562 return "Certificate type was not specified.";
1565 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1566 "LDAP: SSL trusted global cert - %s (type %s)",
1569 /* add the certificate to the global array */
1570 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1571 cert->type = cert_type;
1573 cert->password = password;
1575 /* if file is a file or path, fix the path */
1576 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1577 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1579 cert->path = ap_server_root_relative(cmd->pool, file);
1581 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1584 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1585 "LDAP: Could not open SSL trusted certificate "
1586 "authority file - %s",
1587 cert->path == NULL ? file : cert->path);
1588 return "Invalid global certificate file path";
1597 * Set LDAPTrustedClientCert.
1599 * This directive takes either two or three arguments:
1600 * - certificate type
1601 * - certificate file / directory / nickname
1602 * - certificate password (optional)
1604 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
1608 const char *password)
1610 util_ldap_state_t *st =
1611 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1616 apr_ldap_opt_tls_cert_t *cert;
1618 /* handle the certificate type */
1620 cert_type = util_ldap_parse_cert_type(type);
1621 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1622 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1623 "not recognised. It should be one "
1624 "of CERT_DER, CERT_BASE64, "
1625 "CERT_NICKNAME, CERT_PFX,"
1626 "KEY_DER, KEY_BASE64, KEY_PFX",
1629 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1630 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1631 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1632 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1633 APR_LDAP_CERT_TYPE_PFX == cert_type ||
1634 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1635 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1636 "only valid within a "
1637 "LDAPTrustedGlobalCert directive. "
1638 "Only CERT_DER, CERT_BASE64, "
1639 "CERT_NICKNAME, KEY_DER, and "
1640 "KEY_BASE64 may be used.", type);
1644 return "Certificate type was not specified.";
1647 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1648 "LDAP: SSL trusted client cert - %s (type %s)",
1651 /* add the certificate to the global array */
1652 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1653 cert->type = cert_type;
1655 cert->password = password;
1657 /* if file is a file or path, fix the path */
1658 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1659 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1661 cert->path = ap_server_root_relative(cmd->pool, file);
1663 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1666 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1667 "LDAP: Could not open SSL client certificate "
1669 cert->path == NULL ? file : cert->path);
1670 return "Invalid client certificate file path";
1680 * Set LDAPTrustedMode.
1682 * This directive sets what encryption mode to use on a connection:
1683 * - None (No encryption)
1684 * - SSL (SSL encryption)
1685 * - STARTTLS (TLS encryption)
1687 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
1690 util_ldap_state_t *st =
1691 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1694 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1695 "LDAP: SSL trusted mode - %s",
1698 if (0 == strcasecmp("NONE", mode)) {
1699 st->secure = APR_LDAP_NONE;
1701 else if (0 == strcasecmp("SSL", mode)) {
1702 st->secure = APR_LDAP_SSL;
1704 else if ( (0 == strcasecmp("TLS", mode))
1705 || (0 == strcasecmp("STARTTLS", mode))) {
1706 st->secure = APR_LDAP_STARTTLS;
1709 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1710 "SSL, or TLS/STARTTLS";
1717 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
1721 util_ldap_state_t *st =
1722 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1724 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1730 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1731 "LDAP: SSL verify server certificate - %s",
1732 mode?"TRUE":"FALSE");
1734 st->verify_svr_cert = mode;
1740 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
1744 util_ldap_state_t *st =
1745 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1747 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1753 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1754 st->connectionTimeout = atol(ttl);
1756 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1757 "[%" APR_PID_T_FMT "] ldap connection: Setting connection timeout to "
1758 "%ld seconds.", getpid(), st->connectionTimeout);
1760 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
1761 "LDAP: Connection timout option not supported by the "
1762 "LDAP SDK in use." );
1769 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1771 util_ldap_state_t *st =
1772 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1774 /* Create a per vhost pool for mod_ldap to use, serialized with
1775 * st->mutex (also one per vhost). both are replicated by fork(),
1776 * no shared memory managed by either.
1778 apr_pool_create(&st->pool, p);
1780 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
1783 st->cache_bytes = 100000;
1784 st->search_cache_ttl = 600000000;
1785 st->search_cache_size = 1024;
1786 st->compare_cache_ttl = 600000000;
1787 st->compare_cache_size = 1024;
1788 st->connections = NULL;
1789 st->ssl_supported = 0;
1790 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1791 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1792 st->secure = APR_LDAP_NONE;
1794 st->connectionTimeout = 10;
1795 st->verify_svr_cert = 1;
1800 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
1803 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
1804 util_ldap_state_t *base = (util_ldap_state_t *) basev;
1805 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
1807 st->pool = overrides->pool;
1809 st->mutex = overrides->mutex;
1812 /* The cache settings can not be modified in a
1813 virtual host since all server use the same
1814 shared memory cache. */
1815 st->cache_bytes = base->cache_bytes;
1816 st->search_cache_ttl = base->search_cache_ttl;
1817 st->search_cache_size = base->search_cache_size;
1818 st->compare_cache_ttl = base->compare_cache_ttl;
1819 st->compare_cache_size = base->compare_cache_size;
1821 st->connections = NULL;
1822 st->ssl_supported = 0;
1823 st->global_certs = apr_array_append(p, base->global_certs,
1824 overrides->global_certs);
1825 st->client_certs = apr_array_append(p, base->client_certs,
1826 overrides->client_certs);
1827 st->secure = (overrides->secure_set == 0) ? base->secure
1828 : overrides->secure;
1830 /* These LDAP connection settings can not be overwritten in
1831 a virtual host. Once set in the base server, they must
1832 remain the same. None of the LDAP SDKs seem to be able
1833 to handle setting the verify_svr_cert flag on a
1834 per-connection basis. The OpenLDAP client appears to be
1835 able to handle the connection timeout per-connection
1836 but the Novell SDK cannot. Allowing the timeout to
1837 be set by each vhost is of little value so rather than
1838 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
1839 is being enforced on this setting as well. */
1840 st->connectionTimeout = base->connectionTimeout;
1841 st->verify_svr_cert = base->verify_svr_cert;
1846 static apr_status_t util_ldap_cleanup_module(void *data)
1849 server_rec *s = data;
1850 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1851 s->module_config, &ldap_module);
1853 if (st->ssl_supported) {
1854 apr_ldap_ssl_deinit();
1861 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
1862 apr_pool_t *ptemp, server_rec *s)
1864 apr_status_t result;
1865 server_rec *s_vhost;
1866 util_ldap_state_t *st_vhost;
1868 util_ldap_state_t *st = (util_ldap_state_t *)
1869 ap_get_module_config(s->module_config,
1873 const char *userdata_key = "util_ldap_init";
1874 apr_ldap_err_t *result_err = NULL;
1877 /* util_ldap_post_config() will be called twice. Don't bother
1878 * going through all of the initialization on the first call
1879 * because it will just be thrown away.*/
1880 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1882 apr_pool_userdata_set((const void *)1, userdata_key,
1883 apr_pool_cleanup_null, s->process->pool);
1885 #if APR_HAS_SHARED_MEMORY
1886 /* If the cache file already exists then delete it. Otherwise we are
1887 * going to run into problems creating the shared memory. */
1888 if (st->cache_file) {
1889 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
1891 apr_file_remove(lck_file, ptemp);
1897 #if APR_HAS_SHARED_MEMORY
1898 /* initializing cache if shared memory size is not zero and we already
1899 * don't have shm address
1901 if (!st->cache_shm && st->cache_bytes > 0) {
1903 result = util_ldap_cache_init(p, st);
1904 if (result != APR_SUCCESS) {
1905 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1906 "LDAP cache: could not create shared memory segment");
1911 #if APR_HAS_SHARED_MEMORY
1912 if (st->cache_file) {
1913 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
1918 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
1919 st->lock_file, APR_LOCK_DEFAULT,
1921 if (result != APR_SUCCESS) {
1925 #ifdef AP_NEED_SET_MUTEX_PERMS
1926 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1927 if (result != APR_SUCCESS) {
1928 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
1929 "LDAP cache: failed to set mutex permissions");
1934 /* merge config in all vhost */
1937 st_vhost = (util_ldap_state_t *)
1938 ap_get_module_config(s_vhost->module_config,
1941 #if APR_HAS_SHARED_MEMORY
1942 st_vhost->cache_shm = st->cache_shm;
1943 st_vhost->cache_rmm = st->cache_rmm;
1944 st_vhost->cache_file = st->cache_file;
1945 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
1946 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
1947 "for VHOST: %s", st->cache_shm, st->cache_rmm,
1948 s_vhost->server_hostname);
1950 st_vhost->lock_file = st->lock_file;
1951 s_vhost = s_vhost->next;
1953 #if APR_HAS_SHARED_MEMORY
1956 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1957 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
1958 "shared memory cache");
1962 /* log the LDAP SDK used
1965 apr_ldap_err_t *result = NULL;
1966 apr_ldap_info(p, &(result));
1967 if (result != NULL) {
1968 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
1972 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1973 util_ldap_cleanup_module);
1976 * Initialize SSL support, and log the result for the benefit of the admin.
1978 * If SSL is not supported it is not necessarily an error, as the
1979 * application may not want to use it.
1981 rc = apr_ldap_ssl_init(p,
1985 if (APR_SUCCESS == rc) {
1986 rc = apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT,
1987 (void *)st->global_certs, &(result_err));
1990 if (APR_SUCCESS == rc) {
1991 st->ssl_supported = 1;
1992 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1993 "LDAP: SSL support available" );
1996 st->ssl_supported = 0;
1997 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1998 "LDAP: SSL support unavailable%s%s",
1999 result_err ? ": " : "",
2000 result_err ? result_err->reason : "");
2006 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2009 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2012 if (!st->util_ldap_cache_lock) return;
2014 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2016 if (sts != APR_SUCCESS) {
2017 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2018 "Failed to initialise global mutex %s in child process %"
2020 st->lock_file, getpid());
2024 static const command_rec util_ldap_cmds[] = {
2025 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2027 "Set the size of the shared memory cache (in bytes). Use "
2028 "0 to disable the shared memory cache. (default: 100000)"),
2030 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2032 "Set the file name for the shared memory cache."),
2034 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2036 "Set the maximum number of entries that are possible in the "
2037 "LDAP search cache. Use 0 for no limit. "
2038 "-1 disables the cache. (default: 1024)"),
2040 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2042 "Set the maximum time (in seconds) that an item can be "
2043 "cached in the LDAP search cache. Use 0 for no limit. "
2046 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2048 "Set the maximum number of entries that are possible "
2049 "in the LDAP compare cache. Use 0 for no limit. "
2050 "Use -1 to disable the cache. (default: 1024)"),
2052 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2054 "Set the maximum time (in seconds) that an item is cached "
2055 "in the LDAP operation cache. Use 0 for no limit. "
2058 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2060 "Takes three args; the file and/or directory containing "
2061 "the trusted CA certificates (and global client certs "
2062 "for Netware) used to validate the LDAP server. Second "
2063 "arg is the cert type for the first arg, one of CA_DER, "
2064 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2065 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2066 "Third arg is an optional passphrase if applicable."),
2068 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2070 "Takes three args; the file and/or directory containing "
2071 "the client certificate, or certificate ID used to "
2072 "validate this LDAP client. Second arg is the cert type "
2073 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2074 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2075 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2076 "optional passphrase if applicable."),
2078 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2080 "Specify the type of security that should be applied to "
2081 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2083 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2085 "Set to 'ON' requires that the server certificate be verified "
2086 "before a secure LDAP connection can be establish. Default 'ON'"),
2088 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2090 "Specify the LDAP socket connection timeout in seconds "
2096 static void util_ldap_register_hooks(apr_pool_t *p)
2098 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2099 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2100 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2101 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2102 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2103 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2104 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2105 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2106 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2107 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2109 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2110 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2111 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2114 module AP_MODULE_DECLARE_DATA ldap_module = {
2115 STANDARD20_MODULE_STUFF,
2116 NULL, /* create dir config */
2117 NULL, /* merge dir config */
2118 util_ldap_create_config, /* create server config */
2119 util_ldap_merge_config, /* merge server config */
2120 util_ldap_cmds, /* command table */
2121 util_ldap_register_hooks, /* set up request processing hooks */