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 /* defines for certificate file types
50 #define LDAP_CA_TYPE_UNKNOWN 0
51 #define LDAP_CA_TYPE_DER 1
52 #define LDAP_CA_TYPE_BASE64 2
53 #define LDAP_CA_TYPE_CERT7_DB 3
56 #define LDAP_NO_LIMIT -1
59 module AP_MODULE_DECLARE_DATA ldap_module;
61 #define LDAP_CACHE_LOCK() do { \
62 if (st->util_ldap_cache_lock) \
63 apr_global_mutex_lock(st->util_ldap_cache_lock); \
66 #define LDAP_CACHE_UNLOCK() do { \
67 if (st->util_ldap_cache_lock) \
68 apr_global_mutex_unlock(st->util_ldap_cache_lock); \
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);
124 /* ------------------------------------------------------------------ */
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...
146 /* mark our connection as available for reuse */
149 apr_thread_mutex_unlock(ldc->lock);
155 * Destroys an LDAP connection by unbinding and closing the connection to
156 * the LDAP server. It is used to bring the connection back to a known
157 * state after an error, and during pool cleanup.
159 static apr_status_t uldap_connection_unbind(void *param)
161 util_ldap_connection_t *ldc = param;
165 ldap_unbind_s(ldc->ldap);
176 * Clean up an LDAP connection by unbinding and unlocking the connection.
177 * This function is registered with the pool cleanup function - causing
178 * the LDAP connections to be shut down cleanly on graceful restart.
180 static apr_status_t uldap_connection_cleanup(void *param)
182 util_ldap_connection_t *ldc = param;
186 /* unbind and disconnect from the LDAP server */
187 uldap_connection_unbind(ldc);
189 /* free the username and password */
191 free((void*)ldc->bindpw);
194 free((void*)ldc->binddn);
197 /* unlock this entry */
198 uldap_connection_close(ldc);
205 static int uldap_connection_init(request_rec *r,
206 util_ldap_connection_t *ldc )
209 int version = LDAP_VERSION3;
210 apr_ldap_err_t *result = NULL;
211 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
212 util_ldap_state_t *st =
213 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
216 /* Since the host will include a port if the default port is not used,
217 * always specify the default ports for the port parameter. This will
218 * allow a host string that contains multiple hosts the ability to mix
219 * some hosts with ports and some without. All hosts which do not
220 * specify a port will use the default port.
222 apr_ldap_init(ldc->pool, &(ldc->ldap),
224 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
229 if (result != NULL && result->rc) {
230 ldc->reason = result->reason;
233 if (NULL == ldc->ldap)
236 if (NULL == ldc->reason) {
237 ldc->reason = "LDAP: ldap initialization failed";
240 ldc->reason = result->reason;
245 /* always default to LDAP V3 */
246 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
248 /* set client certificates */
249 if (!apr_is_empty_array(ldc->client_certs)) {
250 apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
251 ldc->client_certs, &(result));
252 if (LDAP_SUCCESS != result->rc) {
253 uldap_connection_unbind( ldc );
254 ldc->reason = result->reason;
259 /* switch on SSL/TLS */
260 if (APR_LDAP_NONE != ldc->secure) {
261 apr_ldap_set_option(ldc->pool, ldc->ldap,
262 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
263 if (LDAP_SUCCESS != result->rc) {
264 uldap_connection_unbind( ldc );
265 ldc->reason = result->reason;
270 /* Set the alias dereferencing option */
271 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
273 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
274 #ifdef APR_LDAP_OPT_VERIFY_CERT
275 apr_ldap_set_option(ldc->pool, ldc->ldap,
276 APR_LDAP_OPT_VERIFY_CERT, &(st->verify_svr_cert), &(result));
278 #if defined(LDAPSSL_VERIFY_SERVER)
279 if (st->verify_svr_cert) {
280 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
283 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
285 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
286 /* This is not a per-connection setting so just pass NULL for the
287 Ldap connection handle */
288 if (st->verify_svr_cert) {
289 int i = LDAP_OPT_X_TLS_DEMAND;
290 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
293 int i = LDAP_OPT_X_TLS_NEVER;
294 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
299 #ifdef LDAP_OPT_NETWORK_TIMEOUT
300 if (st->connectionTimeout > 0) {
301 timeOut.tv_sec = st->connectionTimeout;
304 if (st->connectionTimeout >= 0) {
305 rc = apr_ldap_set_option(ldc->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
306 (void *)&timeOut, &(result));
307 if (APR_SUCCESS != rc) {
308 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
309 "LDAP: Could not set the connection timeout");
318 * Connect to the LDAP server and binds. Does not connect if already
319 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
321 * Returns LDAP_SUCCESS on success; and an error code on failure
323 static int uldap_connection_open(request_rec *r,
324 util_ldap_connection_t *ldc)
329 /* sanity check for NULL */
334 /* If the connection is already bound, return
338 ldc->reason = "LDAP: connection open successful (already bound)";
342 /* create the ldap session handle
344 if (NULL == ldc->ldap)
346 rc = uldap_connection_init( r, ldc );
347 if (LDAP_SUCCESS != rc)
354 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
355 * returned. Break out of the loop on Success or any other error.
357 * NOTE: Looping is probably not a great idea. If the server isn't
358 * responding the chances it will respond after a few tries are poor.
359 * However, the original code looped and it only happens on
360 * the error condition.
362 for (failures=0; failures<10; failures++)
364 rc = ldap_simple_bind_s(ldc->ldap,
366 (char *)ldc->bindpw);
367 if (LDAP_SERVER_DOWN != rc) {
369 } else if (failures == 5) {
370 /* attempt to init the connection once again */
371 uldap_connection_unbind( ldc );
372 rc = uldap_connection_init( r, ldc );
373 if (LDAP_SUCCESS != rc)
380 /* free the handle if there was an error
382 if (LDAP_SUCCESS != rc)
384 uldap_connection_unbind(ldc);
385 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
389 ldc->reason = "LDAP: connection open successful";
397 * Compare client certificate arrays.
399 * Returns 1 on compare failure, 0 otherwise.
401 static int compare_client_certs(apr_array_header_t *srcs,
402 apr_array_header_t *dests)
405 struct apr_ldap_opt_tls_cert_t *src, *dest;
407 /* arrays both NULL? if so, then equal */
408 if (srcs == NULL && dests == NULL) {
412 /* arrays different length or either NULL? If so, then not equal */
413 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
417 /* run an actual comparison */
418 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
419 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
420 for (i = 0; i < srcs->nelts; i++) {
421 if (strcmp(src[i].path, dest[i].path) ||
422 strcmp(src[i].password, dest[i].password) ||
423 src[i].type != dest[i].type) {
428 /* if we got here, the cert arrays were identical */
435 * Find an existing ldap connection struct that matches the
436 * provided ldap connection parameters.
438 * If not found in the cache, a new ldc structure will be allocated
439 * from st->pool and returned to the caller. If found in the cache,
440 * a pointer to the existing ldc structure will be returned.
442 static util_ldap_connection_t *
443 uldap_connection_find(request_rec *r,
444 const char *host, int port,
445 const char *binddn, const char *bindpw,
446 deref_options deref, int secure)
448 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
449 int secureflag = secure;
451 util_ldap_state_t *st =
452 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
457 /* mutex lock this function */
458 apr_thread_mutex_lock(st->mutex);
461 if (secure < APR_LDAP_NONE) {
462 secureflag = st->secure;
465 /* Search for an exact connection match in the list that is not
468 for (l=st->connections,p=NULL; l; l=l->next) {
470 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
472 if ( (l->port == port) && (strcmp(l->host, host) == 0)
473 && ((!l->binddn && !binddn) || (l->binddn && binddn
474 && !strcmp(l->binddn, binddn)))
475 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
476 && !strcmp(l->bindpw, bindpw)))
477 && (l->deref == deref) && (l->secure == secureflag)
478 && !compare_client_certs(st->client_certs, l->client_certs))
483 /* If this connection didn't match the criteria, then we
484 * need to unlock the mutex so it is available to be reused.
486 apr_thread_mutex_unlock(l->lock);
492 /* If nothing found, search again, but we don't care about the
493 * binddn and bindpw this time.
496 for (l=st->connections,p=NULL; l; l=l->next) {
498 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
501 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
502 (l->deref == deref) && (l->secure == secureflag) &&
503 !compare_client_certs(st->client_certs, l->client_certs))
505 /* the bind credentials have changed */
507 util_ldap_strdup((char**)&(l->binddn), binddn);
508 util_ldap_strdup((char**)&(l->bindpw), bindpw);
512 /* If this connection didn't match the criteria, then we
513 * need to unlock the mutex so it is available to be reused.
515 apr_thread_mutex_unlock(l->lock);
522 /* artificially disable cache */
525 /* If no connection what found after the second search, we
531 * Add the new connection entry to the linked list. Note that we
532 * don't actually establish an LDAP connection yet; that happens
533 * the first time authentication is requested.
535 /* create the details to the pool in st */
536 l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
538 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
539 apr_thread_mutex_lock(l->lock);
543 l->host = apr_pstrdup(st->pool, host);
546 util_ldap_strdup((char**)&(l->binddn), binddn);
547 util_ldap_strdup((char**)&(l->bindpw), bindpw);
549 /* The security mode after parsing the URL will always be either
550 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
551 * If the security setting is NONE, override it to the security
552 * setting optionally supplied by the admin using LDAPTrustedMode
554 l->secure = secureflag;
556 /* save away a copy of the client cert list that is presently valid */
557 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
559 /* add the cleanup to the pool */
560 apr_pool_cleanup_register(l->pool, l,
561 uldap_connection_cleanup,
562 apr_pool_cleanup_null);
573 apr_thread_mutex_unlock(st->mutex);
578 /* ------------------------------------------------------------------ */
581 * Compares two DNs to see if they're equal. The only way to do this correctly
582 * is to search for the dn and then do ldap_get_dn() on the result. This should
583 * match the initial dn, since it would have been also retrieved with
584 * ldap_get_dn(). This is expensive, so if the configuration value
585 * compare_dn_on_server is false, just does an ordinary strcmp.
587 * The lock for the ldap cache should already be acquired.
589 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
590 const char *url, const char *dn,
591 const char *reqdn, int compare_dn_on_server)
594 util_url_node_t *curl;
595 util_url_node_t curnode;
596 util_dn_compare_node_t *node;
597 util_dn_compare_node_t newnode;
599 LDAPMessage *res, *entry;
602 util_ldap_state_t *st = (util_ldap_state_t *)
603 ap_get_module_config(r->server->module_config,
606 /* get cache entry (or create one) */
610 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
612 curl = util_ald_create_caches(st, url);
616 /* a simple compare? */
617 if (!compare_dn_on_server) {
618 /* unlock this read lock */
619 if (strcmp(dn, reqdn)) {
620 ldc->reason = "DN Comparison FALSE (direct strcmp())";
621 return LDAP_COMPARE_FALSE;
624 ldc->reason = "DN Comparison TRUE (direct strcmp())";
625 return LDAP_COMPARE_TRUE;
630 /* no - it's a server side compare */
633 /* is it in the compare cache? */
634 newnode.reqdn = (char *)reqdn;
635 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
637 /* If it's in the cache, it's good */
638 /* unlock this read lock */
640 ldc->reason = "DN Comparison TRUE (cached)";
641 return LDAP_COMPARE_TRUE;
644 /* unlock this read lock */
649 if (failures++ > 10) {
650 /* too many failures */
654 /* make a server connection */
655 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
656 /* connect to server failed */
660 /* search for reqdn */
661 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
662 "(objectclass=*)", NULL, 1,
663 NULL, NULL, NULL, LDAP_NO_LIMIT, &res))
666 ldc->reason = "DN Comparison ldap_search_ext_s() "
667 "failed with server down";
668 uldap_connection_unbind(ldc);
671 if (result != LDAP_SUCCESS) {
672 /* search for reqdn failed - no match */
673 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
677 entry = ldap_first_entry(ldc->ldap, res);
678 searchdn = ldap_get_dn(ldc->ldap, entry);
681 if (strcmp(dn, searchdn) != 0) {
682 /* compare unsuccessful */
683 ldc->reason = "DN Comparison FALSE (checked on server)";
684 result = LDAP_COMPARE_FALSE;
688 /* compare successful - add to the compare cache */
690 newnode.reqdn = (char *)reqdn;
691 newnode.dn = (char *)dn;
693 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
695 || (strcmp(reqdn, node->reqdn) != 0)
696 || (strcmp(dn, node->dn) != 0))
698 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
702 ldc->reason = "DN Comparison TRUE (checked on server)";
703 result = LDAP_COMPARE_TRUE;
705 ldap_memfree(searchdn);
711 * Does an generic ldap_compare operation. It accepts a cache that it will use
712 * to lookup the compare in the cache. We cache two kinds of compares
713 * (require group compares) and (require user compares). Each compare has a different
714 * cache node: require group includes the DN; require user does not because the
715 * require user cache is owned by the
718 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
719 const char *url, const char *dn,
720 const char *attrib, const char *value)
723 util_url_node_t *curl;
724 util_url_node_t curnode;
725 util_compare_node_t *compare_nodep;
726 util_compare_node_t the_compare_node;
727 apr_time_t curtime = 0; /* silence gcc -Wall */
730 util_ldap_state_t *st = (util_ldap_state_t *)
731 ap_get_module_config(r->server->module_config,
734 /* get cache entry (or create one) */
737 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
739 curl = util_ald_create_caches(st, url);
744 /* make a comparison to the cache */
746 curtime = apr_time_now();
748 the_compare_node.dn = (char *)dn;
749 the_compare_node.attrib = (char *)attrib;
750 the_compare_node.value = (char *)value;
751 the_compare_node.result = 0;
753 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
756 if (compare_nodep != NULL) {
758 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
759 /* ...but it is too old */
760 util_ald_cache_remove(curl->compare_cache, compare_nodep);
763 /* ...and it is good */
764 /* unlock this read lock */
766 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
767 ldc->reason = "Comparison true (cached)";
768 return compare_nodep->result;
770 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
771 ldc->reason = "Comparison false (cached)";
772 return compare_nodep->result;
774 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
775 ldc->reason = "Comparison no such attribute (cached)";
776 return compare_nodep->result;
779 ldc->reason = "Comparison undefined (cached)";
780 return compare_nodep->result;
784 /* unlock this read lock */
789 if (failures++ > 10) {
790 /* too many failures */
793 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
798 if ((result = ldap_compare_s(ldc->ldap,
802 == LDAP_SERVER_DOWN) {
803 /* connection failed - try again */
804 ldc->reason = "ldap_compare_s() failed with server down";
805 uldap_connection_unbind(ldc);
809 ldc->reason = "Comparison complete";
810 if ((LDAP_COMPARE_TRUE == result) ||
811 (LDAP_COMPARE_FALSE == result) ||
812 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
814 /* compare completed; caching result */
816 the_compare_node.lastcompare = curtime;
817 the_compare_node.result = result;
819 /* If the node doesn't exist then insert it, otherwise just update
820 * it with the last results
822 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
824 if ( (compare_nodep == NULL)
825 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
826 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
827 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
829 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
832 compare_nodep->lastcompare = curtime;
833 compare_nodep->result = result;
837 if (LDAP_COMPARE_TRUE == result) {
838 ldc->reason = "Comparison true (adding to cache)";
839 return LDAP_COMPARE_TRUE;
841 else if (LDAP_COMPARE_FALSE == result) {
842 ldc->reason = "Comparison false (adding to cache)";
843 return LDAP_COMPARE_FALSE;
846 ldc->reason = "Comparison no such attribute (adding to cache)";
847 return LDAP_NO_SUCH_ATTRIBUTE;
853 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
854 const char *url, const char *basedn,
855 int scope, char **attrs, const char *filter,
856 const char *bindpw, const char **binddn,
857 const char ***retvals)
859 const char **vals = NULL;
862 LDAPMessage *res, *entry;
866 util_url_node_t *curl; /* Cached URL node */
867 util_url_node_t curnode;
868 util_search_node_t *search_nodep; /* Cached search node */
869 util_search_node_t the_search_node;
872 util_ldap_state_t *st =
873 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
876 /* Get the cache node for this url */
879 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
882 curl = util_ald_create_caches(st, url);
888 the_search_node.username = filter;
889 search_nodep = util_ald_cache_fetch(curl->search_cache,
891 if (search_nodep != NULL) {
893 /* found entry in search cache... */
894 curtime = apr_time_now();
897 * Remove this item from the cache if its expired. If the sent
898 * password doesn't match the storepassword, the entry will
899 * be removed and readded later if the credentials pass
902 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
903 /* ...but entry is too old */
904 util_ald_cache_remove(curl->search_cache, search_nodep);
906 else if ( (search_nodep->bindpw)
907 && (search_nodep->bindpw[0] != '\0')
908 && (strcmp(search_nodep->bindpw, bindpw) == 0))
910 /* ...and entry is valid */
911 *binddn = search_nodep->dn;
912 *retvals = search_nodep->vals;
914 ldc->reason = "Authentication successful (cached)";
918 /* unlock this read lock */
923 * At this point, there is no valid cached search, so lets do the search.
927 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
930 if (failures++ > 10) {
933 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
937 /* try do the search */
938 if ((result = ldap_search_ext_s(ldc->ldap,
939 (char *)basedn, scope,
940 (char *)filter, attrs, 0,
941 NULL, NULL, NULL, LDAP_NO_LIMIT, &res))
944 ldc->reason = "ldap_search_ext_s() for user failed with server down";
945 uldap_connection_unbind(ldc);
949 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
950 if (result != LDAP_SUCCESS) {
951 ldc->reason = "ldap_search_ext_s() for user failed";
956 * We should have found exactly one entry; to find a different
957 * number is an error.
959 count = ldap_count_entries(ldc->ldap, res);
963 ldc->reason = "User not found";
965 ldc->reason = "User is not unique (search found two "
968 return LDAP_NO_SUCH_OBJECT;
971 entry = ldap_first_entry(ldc->ldap, res);
973 /* Grab the dn, copy it into the pool, and free it again */
974 dn = ldap_get_dn(ldc->ldap, entry);
975 *binddn = apr_pstrdup(r->pool, dn);
979 * A bind to the server with an empty password always succeeds, so
980 * we check to ensure that the password is not empty. This implies
981 * that users who actually do have empty passwords will never be
982 * able to authenticate with this module. I don't see this as a big
985 if (!bindpw || strlen(bindpw) <= 0) {
987 ldc->reason = "Empty password not allowed";
988 return LDAP_INVALID_CREDENTIALS;
992 * Attempt to bind with the retrieved dn and the password. If the bind
993 * fails, it means that the password is wrong (the dn obviously
994 * exists, since we just retrieved it)
996 if ((result = ldap_simple_bind_s(ldc->ldap,
998 (char *)bindpw)) == LDAP_SERVER_DOWN) {
999 ldc->reason = "ldap_simple_bind_s() to check user credentials "
1000 "failed with server down";
1002 uldap_connection_unbind(ldc);
1006 /* failure? if so - return */
1007 if (result != LDAP_SUCCESS) {
1008 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1010 uldap_connection_unbind(ldc);
1015 * We have just bound the connection to a different user and password
1016 * combination, which might be reused unintentionally next time this
1017 * connection is used from the connection pool. To ensure no confusion,
1018 * we mark the connection as unbound.
1024 * Get values for the provided attributes.
1030 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1037 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1038 while (values && values[j]) {
1039 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1040 : apr_pstrdup(r->pool, values[j]);
1043 ldap_value_free(values);
1051 * Add the new username to the search cache.
1055 the_search_node.username = filter;
1056 the_search_node.dn = *binddn;
1057 the_search_node.bindpw = bindpw;
1058 the_search_node.lastbind = apr_time_now();
1059 the_search_node.vals = vals;
1060 the_search_node.numvals = numvals;
1062 /* Search again to make sure that another thread didn't ready insert
1063 * this node into the cache before we got here. If it does exist then
1064 * update the lastbind
1066 search_nodep = util_ald_cache_fetch(curl->search_cache,
1068 if ((search_nodep == NULL) ||
1069 (strcmp(*binddn, search_nodep->dn) != 0)) {
1071 /* Nothing in cache, insert new entry */
1072 util_ald_cache_insert(curl->search_cache, &the_search_node);
1074 else if ((!search_nodep->bindpw) ||
1075 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1077 /* Entry in cache is invalid, remove it and insert new one */
1078 util_ald_cache_remove(curl->search_cache, search_nodep);
1079 util_ald_cache_insert(curl->search_cache, &the_search_node);
1082 /* Cache entry is valid, update lastbind */
1083 search_nodep->lastbind = the_search_node.lastbind;
1085 LDAP_CACHE_UNLOCK();
1089 ldc->reason = "Authentication successful";
1090 return LDAP_SUCCESS;
1094 * This function will return the DN of the entry matching userid.
1095 * It is used to get the DN in case some other module than mod_auth_ldap
1096 * has authenticated the user.
1097 * The function is basically a copy of uldap_cache_checkuserid
1098 * with password checking removed.
1100 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1101 const char *url, const char *basedn,
1102 int scope, char **attrs, const char *filter,
1103 const char **binddn, const char ***retvals)
1105 const char **vals = NULL;
1108 LDAPMessage *res, *entry;
1112 util_url_node_t *curl; /* Cached URL node */
1113 util_url_node_t curnode;
1114 util_search_node_t *search_nodep; /* Cached search node */
1115 util_search_node_t the_search_node;
1118 util_ldap_state_t *st =
1119 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1122 /* Get the cache node for this url */
1125 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1128 curl = util_ald_create_caches(st, url);
1130 LDAP_CACHE_UNLOCK();
1134 the_search_node.username = filter;
1135 search_nodep = util_ald_cache_fetch(curl->search_cache,
1137 if (search_nodep != NULL) {
1139 /* found entry in search cache... */
1140 curtime = apr_time_now();
1143 * Remove this item from the cache if its expired.
1145 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1146 /* ...but entry is too old */
1147 util_ald_cache_remove(curl->search_cache, search_nodep);
1150 /* ...and entry is valid */
1151 *binddn = search_nodep->dn;
1152 *retvals = search_nodep->vals;
1153 LDAP_CACHE_UNLOCK();
1154 ldc->reason = "Search successful (cached)";
1155 return LDAP_SUCCESS;
1158 /* unlock this read lock */
1159 LDAP_CACHE_UNLOCK();
1163 * At this point, there is no valid cached search, so lets do the search.
1167 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1170 if (failures++ > 10) {
1173 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1177 /* try do the search */
1178 if ((result = ldap_search_ext_s(ldc->ldap,
1179 (char *)basedn, scope,
1180 (char *)filter, attrs, 0,
1181 NULL, NULL, NULL, LDAP_NO_LIMIT, &res))
1182 == LDAP_SERVER_DOWN)
1184 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1185 uldap_connection_unbind(ldc);
1189 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1190 if (result != LDAP_SUCCESS) {
1191 ldc->reason = "ldap_search_ext_s() for user failed";
1196 * We should have found exactly one entry; to find a different
1197 * number is an error.
1199 count = ldap_count_entries(ldc->ldap, res);
1203 ldc->reason = "User not found";
1205 ldc->reason = "User is not unique (search found two "
1208 return LDAP_NO_SUCH_OBJECT;
1211 entry = ldap_first_entry(ldc->ldap, res);
1213 /* Grab the dn, copy it into the pool, and free it again */
1214 dn = ldap_get_dn(ldc->ldap, entry);
1215 *binddn = apr_pstrdup(r->pool, dn);
1219 * Get values for the provided attributes.
1225 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1232 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1233 while (values && values[j]) {
1234 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1235 : apr_pstrdup(r->pool, values[j]);
1238 ldap_value_free(values);
1246 * Add the new username to the search cache.
1250 the_search_node.username = filter;
1251 the_search_node.dn = *binddn;
1252 the_search_node.bindpw = NULL;
1253 the_search_node.lastbind = apr_time_now();
1254 the_search_node.vals = vals;
1255 the_search_node.numvals = numvals;
1257 /* Search again to make sure that another thread didn't ready insert
1258 * this node into the cache before we got here. If it does exist then
1259 * update the lastbind
1261 search_nodep = util_ald_cache_fetch(curl->search_cache,
1263 if ((search_nodep == NULL) ||
1264 (strcmp(*binddn, search_nodep->dn) != 0)) {
1266 /* Nothing in cache, insert new entry */
1267 util_ald_cache_insert(curl->search_cache, &the_search_node);
1270 * Don't update lastbind on entries with bindpw because
1271 * we haven't verified that password. It's OK to update
1272 * the entry if there is no password in it.
1274 else if (!search_nodep->bindpw) {
1275 /* Cache entry is valid, update lastbind */
1276 search_nodep->lastbind = the_search_node.lastbind;
1278 LDAP_CACHE_UNLOCK();
1283 ldc->reason = "Search successful";
1284 return LDAP_SUCCESS;
1288 * Reports if ssl support is enabled
1290 * 1 = enabled, 0 = not enabled
1292 static int uldap_ssl_supported(request_rec *r)
1294 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1295 r->server->module_config, &ldap_module);
1297 return(st->ssl_supported);
1301 /* ---------------------------------------- */
1302 /* config directives */
1305 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1308 util_ldap_state_t *st =
1309 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1311 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1317 st->cache_bytes = atol(bytes);
1319 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1320 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1321 " cache size to %" APR_SIZE_T_FMT " bytes.",
1322 getpid(), st->cache_bytes);
1327 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1330 util_ldap_state_t *st =
1331 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1333 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1340 st->cache_file = ap_server_root_relative(st->pool, file);
1343 st->cache_file = NULL;
1346 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1347 "LDAP cache: Setting shared memory cache file to %s bytes.",
1353 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1356 util_ldap_state_t *st =
1357 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1359 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1365 st->search_cache_ttl = atol(ttl) * 1000000;
1367 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1368 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld microseconds.",
1369 getpid(), st->search_cache_ttl);
1374 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1377 util_ldap_state_t *st =
1378 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1380 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1386 st->search_cache_size = atol(size);
1387 if (st->search_cache_size < 0) {
1388 st->search_cache_size = 0;
1391 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1392 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size to %ld entries.",
1393 getpid(), st->search_cache_size);
1398 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1401 util_ldap_state_t *st =
1402 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1404 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1410 st->compare_cache_ttl = atol(ttl) * 1000000;
1412 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1413 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL to %ld microseconds.",
1414 getpid(), st->compare_cache_ttl);
1419 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1422 util_ldap_state_t *st =
1423 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1425 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1431 st->compare_cache_size = atol(size);
1432 if (st->compare_cache_size < 0) {
1433 st->compare_cache_size = 0;
1436 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1437 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size to %ld "
1438 "entries.", getpid(), st->compare_cache_size);
1445 * Parse the certificate type.
1447 * The type can be one of the following:
1448 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1449 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1451 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1453 static int util_ldap_parse_cert_type(const char *type)
1455 /* Authority file in binary DER format */
1456 if (0 == strcasecmp("CA_DER", type)) {
1457 return APR_LDAP_CA_TYPE_DER;
1460 /* Authority file in Base64 format */
1461 else if (0 == strcasecmp("CA_BASE64", type)) {
1462 return APR_LDAP_CA_TYPE_BASE64;
1465 /* Netscape certificate database file/directory */
1466 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1467 return APR_LDAP_CA_TYPE_CERT7_DB;
1470 /* Netscape secmod file/directory */
1471 else if (0 == strcasecmp("CA_SECMOD", type)) {
1472 return APR_LDAP_CA_TYPE_SECMOD;
1475 /* Client cert file in DER format */
1476 else if (0 == strcasecmp("CERT_DER", type)) {
1477 return APR_LDAP_CERT_TYPE_DER;
1480 /* Client cert file in Base64 format */
1481 else if (0 == strcasecmp("CERT_BASE64", type)) {
1482 return APR_LDAP_CERT_TYPE_BASE64;
1485 /* Client cert file in PKCS#12 format */
1486 else if (0 == strcasecmp("CERT_PFX", type)) {
1487 return APR_LDAP_CERT_TYPE_PFX;
1490 /* Netscape client cert database file/directory */
1491 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1492 return APR_LDAP_CERT_TYPE_KEY3_DB;
1495 /* Netscape client cert nickname */
1496 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1497 return APR_LDAP_CERT_TYPE_NICKNAME;
1500 /* Client cert key file in DER format */
1501 else if (0 == strcasecmp("KEY_DER", type)) {
1502 return APR_LDAP_KEY_TYPE_DER;
1505 /* Client cert key file in Base64 format */
1506 else if (0 == strcasecmp("KEY_BASE64", type)) {
1507 return APR_LDAP_KEY_TYPE_BASE64;
1510 /* Client cert key file in PKCS#12 format */
1511 else if (0 == strcasecmp("KEY_PFX", type)) {
1512 return APR_LDAP_KEY_TYPE_PFX;
1516 return APR_LDAP_CA_TYPE_UNKNOWN;
1523 * Set LDAPTrustedGlobalCert.
1525 * This directive takes either two or three arguments:
1526 * - certificate type
1527 * - certificate file / directory / nickname
1528 * - certificate password (optional)
1530 * This directive may only be used globally.
1532 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
1536 const char *password)
1538 util_ldap_state_t *st =
1539 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1541 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1545 apr_ldap_opt_tls_cert_t *cert;
1551 /* handle the certificate type */
1553 cert_type = util_ldap_parse_cert_type(type);
1554 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1555 return apr_psprintf(cmd->pool, "The certificate type %s is "
1556 "not recognised. It should be one "
1557 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1558 "CA_SECMOD, CERT_DER, CERT_BASE64, "
1559 "CERT_KEY3_DB, CERT_NICKNAME, "
1560 "KEY_DER, KEY_BASE64", type);
1564 return "Certificate type was not specified.";
1567 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1568 "LDAP: SSL trusted global cert - %s (type %s)",
1571 /* add the certificate to the global array */
1572 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1573 cert->type = cert_type;
1575 cert->password = password;
1577 /* if file is a file or path, fix the path */
1578 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1579 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1581 cert->path = ap_server_root_relative(cmd->pool, file);
1583 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1586 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1587 "LDAP: Could not open SSL trusted certificate "
1588 "authority file - %s",
1589 cert->path == NULL ? file : cert->path);
1590 return "Invalid global certificate file path";
1599 * Set LDAPTrustedClientCert.
1601 * This directive takes either two or three arguments:
1602 * - certificate type
1603 * - certificate file / directory / nickname
1604 * - certificate password (optional)
1606 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
1610 const char *password)
1612 util_ldap_state_t *st =
1613 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1618 apr_ldap_opt_tls_cert_t *cert;
1620 /* handle the certificate type */
1622 cert_type = util_ldap_parse_cert_type(type);
1623 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1624 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1625 "not recognised. It should be one "
1626 "of CERT_DER, CERT_BASE64, "
1627 "CERT_NICKNAME, CERT_PFX,"
1628 "KEY_DER, KEY_BASE64, KEY_PFX",
1631 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1632 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1633 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1634 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1635 APR_LDAP_CERT_TYPE_PFX == cert_type ||
1636 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1637 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1638 "only valid within a "
1639 "LDAPTrustedGlobalCert directive. "
1640 "Only CERT_DER, CERT_BASE64, "
1641 "CERT_NICKNAME, KEY_DER, and "
1642 "KEY_BASE64 may be used.", type);
1646 return "Certificate type was not specified.";
1649 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1650 "LDAP: SSL trusted client cert - %s (type %s)",
1653 /* add the certificate to the global array */
1654 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1655 cert->type = cert_type;
1657 cert->password = password;
1659 /* if file is a file or path, fix the path */
1660 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1661 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1663 cert->path = ap_server_root_relative(cmd->pool, file);
1665 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1668 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1669 "LDAP: Could not open SSL client certificate "
1671 cert->path == NULL ? file : cert->path);
1672 return "Invalid client certificate file path";
1682 * Set LDAPTrustedMode.
1684 * This directive sets what encryption mode to use on a connection:
1685 * - None (No encryption)
1686 * - SSL (SSL encryption)
1687 * - STARTTLS (TLS encryption)
1689 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
1692 util_ldap_state_t *st =
1693 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1696 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1697 "LDAP: SSL trusted mode - %s",
1700 if (0 == strcasecmp("NONE", mode)) {
1701 st->secure = APR_LDAP_NONE;
1703 else if (0 == strcasecmp("SSL", mode)) {
1704 st->secure = APR_LDAP_SSL;
1706 else if ( (0 == strcasecmp("TLS", mode))
1707 || (0 == strcasecmp("STARTTLS", mode))) {
1708 st->secure = APR_LDAP_STARTTLS;
1711 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1712 "SSL, or TLS/STARTTLS";
1719 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
1723 util_ldap_state_t *st =
1724 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1726 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1732 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1733 "LDAP: SSL verify server certificate - %s",
1734 mode?"TRUE":"FALSE");
1736 st->verify_svr_cert = mode;
1742 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
1746 util_ldap_state_t *st =
1747 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1749 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1755 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1756 st->connectionTimeout = atol(ttl);
1758 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1759 "[%" APR_PID_T_FMT "] ldap connection: Setting connection timeout to "
1760 "%ld seconds.", getpid(), st->connectionTimeout);
1762 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
1763 "LDAP: Connection timout option not supported by the "
1764 "LDAP SDK in use." );
1771 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1773 util_ldap_state_t *st =
1774 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1776 /* Create a per vhost pool for mod_ldap to use, serialized with
1777 * st->mutex (also one per vhost). both are replicated by fork(),
1778 * no shared memory managed by either.
1780 apr_pool_create(&st->pool, p);
1782 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
1785 st->cache_bytes = 100000;
1786 st->search_cache_ttl = 600000000;
1787 st->search_cache_size = 1024;
1788 st->compare_cache_ttl = 600000000;
1789 st->compare_cache_size = 1024;
1790 st->connections = NULL;
1791 st->ssl_supported = 0;
1792 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1793 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1794 st->secure = APR_LDAP_NONE;
1796 st->connectionTimeout = 10;
1797 st->verify_svr_cert = 1;
1802 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
1805 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
1806 util_ldap_state_t *base = (util_ldap_state_t *) basev;
1807 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
1809 st->pool = overrides->pool;
1811 st->mutex = overrides->mutex;
1814 /* The cache settings can not be modified in a
1815 virtual host since all server use the same
1816 shared memory cache. */
1817 st->cache_bytes = base->cache_bytes;
1818 st->search_cache_ttl = base->search_cache_ttl;
1819 st->search_cache_size = base->search_cache_size;
1820 st->compare_cache_ttl = base->compare_cache_ttl;
1821 st->compare_cache_size = base->compare_cache_size;
1823 st->connections = NULL;
1824 st->ssl_supported = 0;
1825 st->global_certs = apr_array_append(p, base->global_certs,
1826 overrides->global_certs);
1827 st->client_certs = apr_array_append(p, base->client_certs,
1828 overrides->client_certs);
1829 st->secure = (overrides->secure_set == 0) ? base->secure
1830 : overrides->secure;
1832 /* These LDAP connection settings can not be overwritten in
1833 a virtual host. Once set in the base server, they must
1834 remain the same. None of the LDAP SDKs seem to be able
1835 to handle setting the verify_svr_cert flag on a
1836 per-connection basis. The OpenLDAP client appears to be
1837 able to handle the connection timeout per-connection
1838 but the Novell SDK cannot. Allowing the timeout to
1839 be set by each vhost is of little value so rather than
1840 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
1841 is being enforced on this setting as well. */
1842 st->connectionTimeout = base->connectionTimeout;
1843 st->verify_svr_cert = base->verify_svr_cert;
1848 static apr_status_t util_ldap_cleanup_module(void *data)
1851 server_rec *s = data;
1852 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1853 s->module_config, &ldap_module);
1855 if (st->ssl_supported) {
1856 apr_ldap_ssl_deinit();
1863 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
1864 apr_pool_t *ptemp, server_rec *s)
1866 apr_status_t result;
1867 server_rec *s_vhost;
1868 util_ldap_state_t *st_vhost;
1870 util_ldap_state_t *st = (util_ldap_state_t *)
1871 ap_get_module_config(s->module_config,
1875 const char *userdata_key = "util_ldap_init";
1876 apr_ldap_err_t *result_err = NULL;
1879 /* util_ldap_post_config() will be called twice. Don't bother
1880 * going through all of the initialization on the first call
1881 * because it will just be thrown away.*/
1882 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1884 apr_pool_userdata_set((const void *)1, userdata_key,
1885 apr_pool_cleanup_null, s->process->pool);
1887 #if APR_HAS_SHARED_MEMORY
1888 /* If the cache file already exists then delete it. Otherwise we are
1889 * going to run into problems creating the shared memory. */
1890 if (st->cache_file) {
1891 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
1893 apr_file_remove(lck_file, ptemp);
1899 #if APR_HAS_SHARED_MEMORY
1900 /* initializing cache if shared memory size is not zero and we already
1901 * don't have shm address
1903 if (!st->cache_shm && st->cache_bytes > 0) {
1905 result = util_ldap_cache_init(p, st);
1906 if (result != APR_SUCCESS) {
1907 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1908 "LDAP cache: could not create shared memory segment");
1913 #if APR_HAS_SHARED_MEMORY
1914 if (st->cache_file) {
1915 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
1920 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
1921 st->lock_file, APR_LOCK_DEFAULT,
1923 if (result != APR_SUCCESS) {
1927 #ifdef AP_NEED_SET_MUTEX_PERMS
1928 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1929 if (result != APR_SUCCESS) {
1930 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
1931 "LDAP cache: failed to set mutex permissions");
1936 /* merge config in all vhost */
1939 st_vhost = (util_ldap_state_t *)
1940 ap_get_module_config(s_vhost->module_config,
1943 #if APR_HAS_SHARED_MEMORY
1944 st_vhost->cache_shm = st->cache_shm;
1945 st_vhost->cache_rmm = st->cache_rmm;
1946 st_vhost->cache_file = st->cache_file;
1947 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
1948 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
1949 "for VHOST: %s", st->cache_shm, st->cache_rmm,
1950 s_vhost->server_hostname);
1952 st_vhost->lock_file = st->lock_file;
1953 s_vhost = s_vhost->next;
1955 #if APR_HAS_SHARED_MEMORY
1958 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1959 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
1960 "shared memory cache");
1964 /* log the LDAP SDK used
1967 apr_ldap_err_t *result = NULL;
1968 apr_ldap_info(p, &(result));
1969 if (result != NULL) {
1970 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
1974 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1975 util_ldap_cleanup_module);
1978 * Initialize SSL support, and log the result for the benefit of the admin.
1980 * If SSL is not supported it is not necessarily an error, as the
1981 * application may not want to use it.
1983 rc = apr_ldap_ssl_init(p,
1987 if (APR_SUCCESS == rc) {
1988 rc = apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT,
1989 (void *)st->global_certs, &(result_err));
1992 if (APR_SUCCESS == rc) {
1993 st->ssl_supported = 1;
1994 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1995 "LDAP: SSL support available" );
1998 st->ssl_supported = 0;
1999 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2000 "LDAP: SSL support unavailable%s%s",
2001 result_err ? ": " : "",
2002 result_err ? result_err->reason : "");
2008 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2011 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2014 if (!st->util_ldap_cache_lock) return;
2016 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2018 if (sts != APR_SUCCESS) {
2019 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2020 "Failed to initialise global mutex %s in child process %"
2022 st->lock_file, getpid());
2026 static const command_rec util_ldap_cmds[] = {
2027 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2029 "Set the size of the shared memory cache (in bytes). Use "
2030 "0 to disable the shared memory cache. (default: 100000)"),
2032 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2034 "Set the file name for the shared memory cache."),
2036 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2038 "Set the maximum number of entries that are possible in the "
2039 "LDAP search cache. Use 0 for no limit. "
2040 "-1 disables the cache. (default: 1024)"),
2042 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2044 "Set the maximum time (in seconds) that an item can be "
2045 "cached in the LDAP search cache. Use 0 for no limit. "
2048 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2050 "Set the maximum number of entries that are possible "
2051 "in the LDAP compare cache. Use 0 for no limit. "
2052 "Use -1 to disable the cache. (default: 1024)"),
2054 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2056 "Set the maximum time (in seconds) that an item is cached "
2057 "in the LDAP operation cache. Use 0 for no limit. "
2060 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2062 "Takes three args; the file and/or directory containing "
2063 "the trusted CA certificates (and global client certs "
2064 "for Netware) used to validate the LDAP server. Second "
2065 "arg is the cert type for the first arg, one of CA_DER, "
2066 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2067 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2068 "Third arg is an optional passphrase if applicable."),
2070 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2072 "Takes three args; the file and/or directory containing "
2073 "the client certificate, or certificate ID used to "
2074 "validate this LDAP client. Second arg is the cert type "
2075 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2076 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2077 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2078 "optional passphrase if applicable."),
2080 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2082 "Specify the type of security that should be applied to "
2083 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2085 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2087 "Set to 'ON' requires that the server certificate be verified "
2088 "before a secure LDAP connection can be establish. Default 'ON'"),
2090 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2092 "Specify the LDAP socket connection timeout in seconds "
2098 static void util_ldap_register_hooks(apr_pool_t *p)
2100 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2101 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2102 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2103 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2104 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2105 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2106 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2107 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2108 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2109 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2111 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2112 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2113 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2116 module AP_MODULE_DECLARE_DATA ldap_module = {
2117 STANDARD20_MODULE_STUFF,
2118 NULL, /* create dir config */
2119 NULL, /* merge dir config */
2120 util_ldap_create_config, /* create server config */
2121 util_ldap_merge_config, /* merge server config */
2122 util_ldap_cmds, /* command table */
2123 util_ldap_register_hooks, /* set up request processing hooks */