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 module AP_MODULE_DECLARE_DATA ldap_module;
58 #define LDAP_CACHE_LOCK() do { \
59 if (st->util_ldap_cache_lock) \
60 apr_global_mutex_lock(st->util_ldap_cache_lock); \
63 #define LDAP_CACHE_UNLOCK() do { \
64 if (st->util_ldap_cache_lock) \
65 apr_global_mutex_unlock(st->util_ldap_cache_lock); \
68 static void util_ldap_strdup (char **str, const char *newstr)
76 *str = strdup(newstr);
84 * This handler generates a status page about the current performance of
85 * the LDAP cache. It is enabled as follows:
87 * <Location /ldap-status>
88 * SetHandler ldap-status
92 static int util_ldap_handler(request_rec *r)
94 util_ldap_state_t *st = (util_ldap_state_t *)
95 ap_get_module_config(r->server->module_config,
98 r->allowed |= (1 << M_GET);
99 if (r->method_number != M_GET)
102 if (strcmp(r->handler, "ldap-status")) {
106 ap_set_content_type(r, "text/html");
111 ap_rputs(DOCTYPE_HTML_3_2
112 "<html><head><title>LDAP Cache Information</title></head>\n", r);
113 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
116 util_ald_cache_display(r, st);
121 /* ------------------------------------------------------------------ */
125 * Closes an LDAP connection by unlocking it. The next time
126 * uldap_connection_find() is called this connection will be
127 * available for reuse.
129 static void uldap_connection_close(util_ldap_connection_t *ldc)
135 * Is it safe leaving bound connections floating around between the
136 * different modules? Keeping the user bound is a performance boost,
137 * but it is also a potential security problem - maybe.
139 * For now we unbind the user when we finish with a connection, but
140 * we don't have to...
143 /* mark our connection as available for reuse */
146 apr_thread_mutex_unlock(ldc->lock);
152 * Destroys an LDAP connection by unbinding and closing the connection to
153 * the LDAP server. It is used to bring the connection back to a known
154 * state after an error, and during pool cleanup.
156 static apr_status_t uldap_connection_unbind(void *param)
158 util_ldap_connection_t *ldc = param;
162 ldap_unbind_s(ldc->ldap);
173 * Clean up an LDAP connection by unbinding and unlocking the connection.
174 * This function is registered with the pool cleanup function - causing
175 * the LDAP connections to be shut down cleanly on graceful restart.
177 static apr_status_t uldap_connection_cleanup(void *param)
179 util_ldap_connection_t *ldc = param;
183 /* unbind and disconnect from the LDAP server */
184 uldap_connection_unbind(ldc);
186 /* free the username and password */
188 free((void*)ldc->bindpw);
191 free((void*)ldc->binddn);
194 /* unlock this entry */
195 uldap_connection_close(ldc);
202 static int uldap_connection_init(request_rec *r,
203 util_ldap_connection_t *ldc )
206 int version = LDAP_VERSION3;
207 apr_ldap_err_t *result = NULL;
208 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
209 util_ldap_state_t *st =
210 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
213 /* Since the host will include a port if the default port is not used,
214 * always specify the default ports for the port parameter. This will
215 * allow a host string that contains multiple hosts the ability to mix
216 * some hosts with ports and some without. All hosts which do not
217 * specify a port will use the default port.
219 apr_ldap_init(ldc->pool, &(ldc->ldap),
221 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
226 if (result != NULL && result->rc) {
227 ldc->reason = result->reason;
230 if (NULL == ldc->ldap)
233 if (NULL == ldc->reason) {
234 ldc->reason = "LDAP: ldap initialization failed";
237 ldc->reason = result->reason;
242 /* always default to LDAP V3 */
243 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
245 /* set client certificates */
246 if (!apr_is_empty_array(ldc->client_certs)) {
247 apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
248 ldc->client_certs, &(result));
249 if (LDAP_SUCCESS != result->rc) {
250 uldap_connection_unbind( ldc );
251 ldc->reason = result->reason;
256 /* switch on SSL/TLS */
257 if (APR_LDAP_NONE != ldc->secure) {
258 apr_ldap_set_option(ldc->pool, ldc->ldap,
259 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
260 if (LDAP_SUCCESS != result->rc) {
261 uldap_connection_unbind( ldc );
262 ldc->reason = result->reason;
267 /* Set the alias dereferencing option */
268 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
270 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
271 #ifdef APR_LDAP_OPT_VERIFY_CERT
272 apr_ldap_set_option(ldc->pool, ldc->ldap,
273 APR_LDAP_OPT_VERIFY_CERT, &(st->verify_svr_cert), &(result));
275 #if defined(LDAPSSL_VERIFY_SERVER)
276 if (st->verify_svr_cert) {
277 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
280 result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
282 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
283 /* This is not a per-connection setting so just pass NULL for the
284 Ldap connection handle */
285 if (st->verify_svr_cert) {
286 int i = LDAP_OPT_X_TLS_DEMAND;
287 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
290 int i = LDAP_OPT_X_TLS_NEVER;
291 result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
296 #ifdef LDAP_OPT_NETWORK_TIMEOUT
297 if (st->connectionTimeout > 0) {
298 timeOut.tv_sec = st->connectionTimeout;
301 if (st->connectionTimeout >= 0) {
302 rc = apr_ldap_set_option(ldc->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
303 (void *)&timeOut, &(result));
304 if (APR_SUCCESS != rc) {
305 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
306 "LDAP: Could not set the connection timeout");
315 * Connect to the LDAP server and binds. Does not connect if already
316 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
318 * Returns LDAP_SUCCESS on success; and an error code on failure
320 static int uldap_connection_open(request_rec *r,
321 util_ldap_connection_t *ldc)
326 /* sanity check for NULL */
331 /* If the connection is already bound, return
335 ldc->reason = "LDAP: connection open successful (already bound)";
339 /* create the ldap session handle
341 if (NULL == ldc->ldap)
343 rc = uldap_connection_init( r, ldc );
344 if (LDAP_SUCCESS != rc)
351 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
352 * returned. Break out of the loop on Success or any other error.
354 * NOTE: Looping is probably not a great idea. If the server isn't
355 * responding the chances it will respond after a few tries are poor.
356 * However, the original code looped and it only happens on
357 * the error condition.
359 for (failures=0; failures<10; failures++)
361 rc = ldap_simple_bind_s(ldc->ldap,
363 (char *)ldc->bindpw);
364 if (LDAP_SERVER_DOWN != rc) {
366 } else if (failures == 5) {
367 /* attempt to init the connection once again */
368 uldap_connection_unbind( ldc );
369 rc = uldap_connection_init( r, ldc );
370 if (LDAP_SUCCESS != rc)
377 /* free the handle if there was an error
379 if (LDAP_SUCCESS != rc)
381 uldap_connection_unbind(ldc);
382 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
386 ldc->reason = "LDAP: connection open successful";
394 * Compare client certificate arrays.
396 * Returns 1 on compare failure, 0 otherwise.
398 static int compare_client_certs(apr_array_header_t *srcs,
399 apr_array_header_t *dests)
402 struct apr_ldap_opt_tls_cert_t *src, *dest;
404 /* arrays both NULL? if so, then equal */
405 if (srcs == NULL && dests == NULL) {
409 /* arrays different length or either NULL? If so, then not equal */
410 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
414 /* run an actual comparison */
415 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
416 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
417 for (i = 0; i < srcs->nelts; i++) {
418 if (strcmp(src[i].path, dest[i].path) ||
419 strcmp(src[i].password, dest[i].password) ||
420 src[i].type != dest[i].type) {
425 /* if we got here, the cert arrays were identical */
432 * Find an existing ldap connection struct that matches the
433 * provided ldap connection parameters.
435 * If not found in the cache, a new ldc structure will be allocated
436 * from st->pool and returned to the caller. If found in the cache,
437 * a pointer to the existing ldc structure will be returned.
439 static util_ldap_connection_t *
440 uldap_connection_find(request_rec *r,
441 const char *host, int port,
442 const char *binddn, const char *bindpw,
443 deref_options deref, int secure)
445 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
446 int secureflag = secure;
448 util_ldap_state_t *st =
449 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
454 /* mutex lock this function */
455 apr_thread_mutex_lock(st->mutex);
458 if (secure < APR_LDAP_NONE) {
459 secureflag = st->secure;
462 /* Search for an exact connection match in the list that is not
465 for (l=st->connections,p=NULL; l; l=l->next) {
467 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
469 if ( (l->port == port) && (strcmp(l->host, host) == 0)
470 && ((!l->binddn && !binddn) || (l->binddn && binddn
471 && !strcmp(l->binddn, binddn)))
472 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
473 && !strcmp(l->bindpw, bindpw)))
474 && (l->deref == deref) && (l->secure == secureflag)
475 && !compare_client_certs(st->client_certs, l->client_certs))
480 /* If this connection didn't match the criteria, then we
481 * need to unlock the mutex so it is available to be reused.
483 apr_thread_mutex_unlock(l->lock);
489 /* If nothing found, search again, but we don't care about the
490 * binddn and bindpw this time.
493 for (l=st->connections,p=NULL; l; l=l->next) {
495 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
498 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
499 (l->deref == deref) && (l->secure == secureflag) &&
500 !compare_client_certs(st->client_certs, l->client_certs))
502 /* the bind credentials have changed */
504 util_ldap_strdup((char**)&(l->binddn), binddn);
505 util_ldap_strdup((char**)&(l->bindpw), bindpw);
509 /* If this connection didn't match the criteria, then we
510 * need to unlock the mutex so it is available to be reused.
512 apr_thread_mutex_unlock(l->lock);
519 /* artificially disable cache */
522 /* If no connection what found after the second search, we
528 * Add the new connection entry to the linked list. Note that we
529 * don't actually establish an LDAP connection yet; that happens
530 * the first time authentication is requested.
532 /* create the details to the pool in st */
533 l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
535 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
536 apr_thread_mutex_lock(l->lock);
540 l->host = apr_pstrdup(st->pool, host);
543 util_ldap_strdup((char**)&(l->binddn), binddn);
544 util_ldap_strdup((char**)&(l->bindpw), bindpw);
546 /* The security mode after parsing the URL will always be either
547 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
548 * If the security setting is NONE, override it to the security
549 * setting optionally supplied by the admin using LDAPTrustedMode
551 l->secure = secureflag;
553 /* save away a copy of the client cert list that is presently valid */
554 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
556 /* add the cleanup to the pool */
557 apr_pool_cleanup_register(l->pool, l,
558 uldap_connection_cleanup,
559 apr_pool_cleanup_null);
570 apr_thread_mutex_unlock(st->mutex);
575 /* ------------------------------------------------------------------ */
578 * Compares two DNs to see if they're equal. The only way to do this correctly
579 * is to search for the dn and then do ldap_get_dn() on the result. This should
580 * match the initial dn, since it would have been also retrieved with
581 * ldap_get_dn(). This is expensive, so if the configuration value
582 * compare_dn_on_server is false, just does an ordinary strcmp.
584 * The lock for the ldap cache should already be acquired.
586 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
587 const char *url, const char *dn,
588 const char *reqdn, int compare_dn_on_server)
591 util_url_node_t *curl;
592 util_url_node_t curnode;
593 util_dn_compare_node_t *node;
594 util_dn_compare_node_t newnode;
596 LDAPMessage *res, *entry;
599 util_ldap_state_t *st = (util_ldap_state_t *)
600 ap_get_module_config(r->server->module_config,
603 /* get cache entry (or create one) */
607 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
609 curl = util_ald_create_caches(st, url);
613 /* a simple compare? */
614 if (!compare_dn_on_server) {
615 /* unlock this read lock */
616 if (strcmp(dn, reqdn)) {
617 ldc->reason = "DN Comparison FALSE (direct strcmp())";
618 return LDAP_COMPARE_FALSE;
621 ldc->reason = "DN Comparison TRUE (direct strcmp())";
622 return LDAP_COMPARE_TRUE;
627 /* no - it's a server side compare */
630 /* is it in the compare cache? */
631 newnode.reqdn = (char *)reqdn;
632 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
634 /* If it's in the cache, it's good */
635 /* unlock this read lock */
637 ldc->reason = "DN Comparison TRUE (cached)";
638 return LDAP_COMPARE_TRUE;
641 /* unlock this read lock */
646 if (failures++ > 10) {
647 /* too many failures */
651 /* make a server connection */
652 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
653 /* connect to server failed */
657 /* search for reqdn */
658 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
659 "(objectclass=*)", NULL, 1,
660 NULL, NULL, NULL, -1, &res))
663 ldc->reason = "DN Comparison ldap_search_ext_s() "
664 "failed with server down";
665 uldap_connection_unbind(ldc);
668 if (result != LDAP_SUCCESS) {
669 /* search for reqdn failed - no match */
670 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
674 entry = ldap_first_entry(ldc->ldap, res);
675 searchdn = ldap_get_dn(ldc->ldap, entry);
678 if (strcmp(dn, searchdn) != 0) {
679 /* compare unsuccessful */
680 ldc->reason = "DN Comparison FALSE (checked on server)";
681 result = LDAP_COMPARE_FALSE;
685 /* compare successful - add to the compare cache */
687 newnode.reqdn = (char *)reqdn;
688 newnode.dn = (char *)dn;
690 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
692 || (strcmp(reqdn, node->reqdn) != 0)
693 || (strcmp(dn, node->dn) != 0))
695 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
699 ldc->reason = "DN Comparison TRUE (checked on server)";
700 result = LDAP_COMPARE_TRUE;
702 ldap_memfree(searchdn);
708 * Does an generic ldap_compare operation. It accepts a cache that it will use
709 * to lookup the compare in the cache. We cache two kinds of compares
710 * (require group compares) and (require user compares). Each compare has a different
711 * cache node: require group includes the DN; require user does not because the
712 * require user cache is owned by the
715 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
716 const char *url, const char *dn,
717 const char *attrib, const char *value)
720 util_url_node_t *curl;
721 util_url_node_t curnode;
722 util_compare_node_t *compare_nodep;
723 util_compare_node_t the_compare_node;
724 apr_time_t curtime = 0; /* silence gcc -Wall */
727 util_ldap_state_t *st = (util_ldap_state_t *)
728 ap_get_module_config(r->server->module_config,
731 /* get cache entry (or create one) */
734 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
736 curl = util_ald_create_caches(st, url);
741 /* make a comparison to the cache */
743 curtime = apr_time_now();
745 the_compare_node.dn = (char *)dn;
746 the_compare_node.attrib = (char *)attrib;
747 the_compare_node.value = (char *)value;
748 the_compare_node.result = 0;
750 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
753 if (compare_nodep != NULL) {
755 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
756 /* ...but it is too old */
757 util_ald_cache_remove(curl->compare_cache, compare_nodep);
760 /* ...and it is good */
761 /* unlock this read lock */
763 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
764 ldc->reason = "Comparison true (cached)";
765 return compare_nodep->result;
767 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
768 ldc->reason = "Comparison false (cached)";
769 return compare_nodep->result;
771 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
772 ldc->reason = "Comparison no such attribute (cached)";
773 return compare_nodep->result;
776 ldc->reason = "Comparison undefined (cached)";
777 return compare_nodep->result;
781 /* unlock this read lock */
786 if (failures++ > 10) {
787 /* too many failures */
790 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
795 if ((result = ldap_compare_s(ldc->ldap,
799 == LDAP_SERVER_DOWN) {
800 /* connection failed - try again */
801 ldc->reason = "ldap_compare_s() failed with server down";
802 uldap_connection_unbind(ldc);
806 ldc->reason = "Comparison complete";
807 if ((LDAP_COMPARE_TRUE == result) ||
808 (LDAP_COMPARE_FALSE == result) ||
809 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
811 /* compare completed; caching result */
813 the_compare_node.lastcompare = curtime;
814 the_compare_node.result = result;
816 /* If the node doesn't exist then insert it, otherwise just update
817 * it with the last results
819 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
821 if ( (compare_nodep == NULL)
822 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
823 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
824 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
826 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
829 compare_nodep->lastcompare = curtime;
830 compare_nodep->result = result;
834 if (LDAP_COMPARE_TRUE == result) {
835 ldc->reason = "Comparison true (adding to cache)";
836 return LDAP_COMPARE_TRUE;
838 else if (LDAP_COMPARE_FALSE == result) {
839 ldc->reason = "Comparison false (adding to cache)";
840 return LDAP_COMPARE_FALSE;
843 ldc->reason = "Comparison no such attribute (adding to cache)";
844 return LDAP_NO_SUCH_ATTRIBUTE;
850 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
851 const char *url, const char *basedn,
852 int scope, char **attrs, const char *filter,
853 const char *bindpw, const char **binddn,
854 const char ***retvals)
856 const char **vals = NULL;
859 LDAPMessage *res, *entry;
863 util_url_node_t *curl; /* Cached URL node */
864 util_url_node_t curnode;
865 util_search_node_t *search_nodep; /* Cached search node */
866 util_search_node_t the_search_node;
869 util_ldap_state_t *st =
870 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
873 /* Get the cache node for this url */
876 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
879 curl = util_ald_create_caches(st, url);
885 the_search_node.username = filter;
886 search_nodep = util_ald_cache_fetch(curl->search_cache,
888 if (search_nodep != NULL) {
890 /* found entry in search cache... */
891 curtime = apr_time_now();
894 * Remove this item from the cache if its expired. If the sent
895 * password doesn't match the storepassword, the entry will
896 * be removed and readded later if the credentials pass
899 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
900 /* ...but entry is too old */
901 util_ald_cache_remove(curl->search_cache, search_nodep);
903 else if ( (search_nodep->bindpw)
904 && (search_nodep->bindpw[0] != '\0')
905 && (strcmp(search_nodep->bindpw, bindpw) == 0))
907 /* ...and entry is valid */
908 *binddn = search_nodep->dn;
909 *retvals = search_nodep->vals;
911 ldc->reason = "Authentication successful (cached)";
915 /* unlock this read lock */
920 * At this point, there is no valid cached search, so lets do the search.
924 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
927 if (failures++ > 10) {
930 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
934 /* try do the search */
935 if ((result = ldap_search_ext_s(ldc->ldap,
936 (char *)basedn, scope,
937 (char *)filter, attrs, 0,
938 NULL, NULL, NULL, -1, &res))
941 ldc->reason = "ldap_search_ext_s() for user failed with server down";
942 uldap_connection_unbind(ldc);
946 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
947 if (result != LDAP_SUCCESS) {
948 ldc->reason = "ldap_search_ext_s() for user failed";
953 * We should have found exactly one entry; to find a different
954 * number is an error.
956 count = ldap_count_entries(ldc->ldap, res);
960 ldc->reason = "User not found";
962 ldc->reason = "User is not unique (search found two "
965 return LDAP_NO_SUCH_OBJECT;
968 entry = ldap_first_entry(ldc->ldap, res);
970 /* Grab the dn, copy it into the pool, and free it again */
971 dn = ldap_get_dn(ldc->ldap, entry);
972 *binddn = apr_pstrdup(r->pool, dn);
976 * A bind to the server with an empty password always succeeds, so
977 * we check to ensure that the password is not empty. This implies
978 * that users who actually do have empty passwords will never be
979 * able to authenticate with this module. I don't see this as a big
982 if (!bindpw || strlen(bindpw) <= 0) {
984 ldc->reason = "Empty password not allowed";
985 return LDAP_INVALID_CREDENTIALS;
989 * Attempt to bind with the retrieved dn and the password. If the bind
990 * fails, it means that the password is wrong (the dn obviously
991 * exists, since we just retrieved it)
993 if ((result = ldap_simple_bind_s(ldc->ldap,
995 (char *)bindpw)) == LDAP_SERVER_DOWN) {
996 ldc->reason = "ldap_simple_bind_s() to check user credentials "
997 "failed with server down";
999 uldap_connection_unbind(ldc);
1003 /* failure? if so - return */
1004 if (result != LDAP_SUCCESS) {
1005 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1007 uldap_connection_unbind(ldc);
1012 * We have just bound the connection to a different user and password
1013 * combination, which might be reused unintentionally next time this
1014 * connection is used from the connection pool. To ensure no confusion,
1015 * we mark the connection as unbound.
1021 * Get values for the provided attributes.
1027 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1034 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1035 while (values && values[j]) {
1036 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1037 : apr_pstrdup(r->pool, values[j]);
1040 ldap_value_free(values);
1048 * Add the new username to the search cache.
1052 the_search_node.username = filter;
1053 the_search_node.dn = *binddn;
1054 the_search_node.bindpw = bindpw;
1055 the_search_node.lastbind = apr_time_now();
1056 the_search_node.vals = vals;
1057 the_search_node.numvals = numvals;
1059 /* Search again to make sure that another thread didn't ready insert
1060 * this node into the cache before we got here. If it does exist then
1061 * update the lastbind
1063 search_nodep = util_ald_cache_fetch(curl->search_cache,
1065 if ((search_nodep == NULL) ||
1066 (strcmp(*binddn, search_nodep->dn) != 0)) {
1068 /* Nothing in cache, insert new entry */
1069 util_ald_cache_insert(curl->search_cache, &the_search_node);
1071 else if ((!search_nodep->bindpw) ||
1072 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1074 /* Entry in cache is invalid, remove it and insert new one */
1075 util_ald_cache_remove(curl->search_cache, search_nodep);
1076 util_ald_cache_insert(curl->search_cache, &the_search_node);
1079 /* Cache entry is valid, update lastbind */
1080 search_nodep->lastbind = the_search_node.lastbind;
1082 LDAP_CACHE_UNLOCK();
1086 ldc->reason = "Authentication successful";
1087 return LDAP_SUCCESS;
1091 * This function will return the DN of the entry matching userid.
1092 * It is used to get the DN in case some other module than mod_auth_ldap
1093 * has authenticated the user.
1094 * The function is basically a copy of uldap_cache_checkuserid
1095 * with password checking removed.
1097 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1098 const char *url, const char *basedn,
1099 int scope, char **attrs, const char *filter,
1100 const char **binddn, const char ***retvals)
1102 const char **vals = NULL;
1105 LDAPMessage *res, *entry;
1109 util_url_node_t *curl; /* Cached URL node */
1110 util_url_node_t curnode;
1111 util_search_node_t *search_nodep; /* Cached search node */
1112 util_search_node_t the_search_node;
1115 util_ldap_state_t *st =
1116 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1119 /* Get the cache node for this url */
1122 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1125 curl = util_ald_create_caches(st, url);
1127 LDAP_CACHE_UNLOCK();
1131 the_search_node.username = filter;
1132 search_nodep = util_ald_cache_fetch(curl->search_cache,
1134 if (search_nodep != NULL) {
1136 /* found entry in search cache... */
1137 curtime = apr_time_now();
1140 * Remove this item from the cache if its expired.
1142 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1143 /* ...but entry is too old */
1144 util_ald_cache_remove(curl->search_cache, search_nodep);
1147 /* ...and entry is valid */
1148 *binddn = search_nodep->dn;
1149 *retvals = search_nodep->vals;
1150 LDAP_CACHE_UNLOCK();
1151 ldc->reason = "Search successful (cached)";
1152 return LDAP_SUCCESS;
1155 /* unlock this read lock */
1156 LDAP_CACHE_UNLOCK();
1160 * At this point, there is no valid cached search, so lets do the search.
1164 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1167 if (failures++ > 10) {
1170 if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1174 /* try do the search */
1175 if ((result = ldap_search_ext_s(ldc->ldap,
1176 (char *)basedn, scope,
1177 (char *)filter, attrs, 0,
1178 NULL, NULL, NULL, -1, &res))
1179 == LDAP_SERVER_DOWN)
1181 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1182 uldap_connection_unbind(ldc);
1186 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1187 if (result != LDAP_SUCCESS) {
1188 ldc->reason = "ldap_search_ext_s() for user failed";
1193 * We should have found exactly one entry; to find a different
1194 * number is an error.
1196 count = ldap_count_entries(ldc->ldap, res);
1200 ldc->reason = "User not found";
1202 ldc->reason = "User is not unique (search found two "
1205 return LDAP_NO_SUCH_OBJECT;
1208 entry = ldap_first_entry(ldc->ldap, res);
1210 /* Grab the dn, copy it into the pool, and free it again */
1211 dn = ldap_get_dn(ldc->ldap, entry);
1212 *binddn = apr_pstrdup(r->pool, dn);
1216 * Get values for the provided attributes.
1222 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1229 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1230 while (values && values[j]) {
1231 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1232 : apr_pstrdup(r->pool, values[j]);
1235 ldap_value_free(values);
1243 * Add the new username to the search cache.
1247 the_search_node.username = filter;
1248 the_search_node.dn = *binddn;
1249 the_search_node.bindpw = NULL;
1250 the_search_node.lastbind = apr_time_now();
1251 the_search_node.vals = vals;
1252 the_search_node.numvals = numvals;
1254 /* Search again to make sure that another thread didn't ready insert
1255 * this node into the cache before we got here. If it does exist then
1256 * update the lastbind
1258 search_nodep = util_ald_cache_fetch(curl->search_cache,
1260 if ((search_nodep == NULL) ||
1261 (strcmp(*binddn, search_nodep->dn) != 0)) {
1263 /* Nothing in cache, insert new entry */
1264 util_ald_cache_insert(curl->search_cache, &the_search_node);
1267 * Don't update lastbind on entries with bindpw because
1268 * we haven't verified that password. It's OK to update
1269 * the entry if there is no password in it.
1271 else if (!search_nodep->bindpw) {
1272 /* Cache entry is valid, update lastbind */
1273 search_nodep->lastbind = the_search_node.lastbind;
1275 LDAP_CACHE_UNLOCK();
1280 ldc->reason = "Search successful";
1281 return LDAP_SUCCESS;
1285 * Reports if ssl support is enabled
1287 * 1 = enabled, 0 = not enabled
1289 static int uldap_ssl_supported(request_rec *r)
1291 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1292 r->server->module_config, &ldap_module);
1294 return(st->ssl_supported);
1298 /* ---------------------------------------- */
1299 /* config directives */
1302 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1305 util_ldap_state_t *st =
1306 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1308 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1314 st->cache_bytes = atol(bytes);
1316 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1317 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1318 " cache size to %" APR_SIZE_T_FMT " bytes.",
1319 getpid(), st->cache_bytes);
1324 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1327 util_ldap_state_t *st =
1328 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1330 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1337 st->cache_file = ap_server_root_relative(st->pool, file);
1340 st->cache_file = NULL;
1343 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1344 "LDAP cache: Setting shared memory cache file to %s bytes.",
1350 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1353 util_ldap_state_t *st =
1354 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1356 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1362 st->search_cache_ttl = atol(ttl) * 1000000;
1364 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1365 "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld microseconds.",
1366 getpid(), st->search_cache_ttl);
1371 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1374 util_ldap_state_t *st =
1375 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1377 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1383 st->search_cache_size = atol(size);
1384 if (st->search_cache_size < 0) {
1385 st->search_cache_size = 0;
1388 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1389 "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size to %ld entries.",
1390 getpid(), st->search_cache_size);
1395 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1398 util_ldap_state_t *st =
1399 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1401 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1407 st->compare_cache_ttl = atol(ttl) * 1000000;
1409 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1410 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL to %ld microseconds.",
1411 getpid(), st->compare_cache_ttl);
1416 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1419 util_ldap_state_t *st =
1420 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1422 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1428 st->compare_cache_size = atol(size);
1429 if (st->compare_cache_size < 0) {
1430 st->compare_cache_size = 0;
1433 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1434 "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size to %ld "
1435 "entries.", getpid(), st->compare_cache_size);
1442 * Parse the certificate type.
1444 * The type can be one of the following:
1445 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1446 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1448 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1450 static int util_ldap_parse_cert_type(const char *type)
1452 /* Authority file in binary DER format */
1453 if (0 == strcasecmp("CA_DER", type)) {
1454 return APR_LDAP_CA_TYPE_DER;
1457 /* Authority file in Base64 format */
1458 else if (0 == strcasecmp("CA_BASE64", type)) {
1459 return APR_LDAP_CA_TYPE_BASE64;
1462 /* Netscape certificate database file/directory */
1463 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1464 return APR_LDAP_CA_TYPE_CERT7_DB;
1467 /* Netscape secmod file/directory */
1468 else if (0 == strcasecmp("CA_SECMOD", type)) {
1469 return APR_LDAP_CA_TYPE_SECMOD;
1472 /* Client cert file in DER format */
1473 else if (0 == strcasecmp("CERT_DER", type)) {
1474 return APR_LDAP_CERT_TYPE_DER;
1477 /* Client cert file in Base64 format */
1478 else if (0 == strcasecmp("CERT_BASE64", type)) {
1479 return APR_LDAP_CERT_TYPE_BASE64;
1482 /* Client cert file in PKCS#12 format */
1483 else if (0 == strcasecmp("CERT_PFX", type)) {
1484 return APR_LDAP_CERT_TYPE_PFX;
1487 /* Netscape client cert database file/directory */
1488 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1489 return APR_LDAP_CERT_TYPE_KEY3_DB;
1492 /* Netscape client cert nickname */
1493 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1494 return APR_LDAP_CERT_TYPE_NICKNAME;
1497 /* Client cert key file in DER format */
1498 else if (0 == strcasecmp("KEY_DER", type)) {
1499 return APR_LDAP_KEY_TYPE_DER;
1502 /* Client cert key file in Base64 format */
1503 else if (0 == strcasecmp("KEY_BASE64", type)) {
1504 return APR_LDAP_KEY_TYPE_BASE64;
1507 /* Client cert key file in PKCS#12 format */
1508 else if (0 == strcasecmp("KEY_PFX", type)) {
1509 return APR_LDAP_KEY_TYPE_PFX;
1513 return APR_LDAP_CA_TYPE_UNKNOWN;
1520 * Set LDAPTrustedGlobalCert.
1522 * This directive takes either two or three arguments:
1523 * - certificate type
1524 * - certificate file / directory / nickname
1525 * - certificate password (optional)
1527 * This directive may only be used globally.
1529 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
1533 const char *password)
1535 util_ldap_state_t *st =
1536 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1538 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1542 apr_ldap_opt_tls_cert_t *cert;
1548 /* handle the certificate type */
1550 cert_type = util_ldap_parse_cert_type(type);
1551 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1552 return apr_psprintf(cmd->pool, "The certificate type %s is "
1553 "not recognised. It should be one "
1554 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1555 "CA_SECMOD, CERT_DER, CERT_BASE64, "
1556 "CERT_KEY3_DB, CERT_NICKNAME, "
1557 "KEY_DER, KEY_BASE64", type);
1561 return "Certificate type was not specified.";
1564 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1565 "LDAP: SSL trusted global cert - %s (type %s)",
1568 /* add the certificate to the global array */
1569 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1570 cert->type = cert_type;
1572 cert->password = password;
1574 /* if file is a file or path, fix the path */
1575 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1576 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1578 cert->path = ap_server_root_relative(cmd->pool, file);
1580 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1583 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1584 "LDAP: Could not open SSL trusted certificate "
1585 "authority file - %s",
1586 cert->path == NULL ? file : cert->path);
1587 return "Invalid global certificate file path";
1596 * Set LDAPTrustedClientCert.
1598 * This directive takes either two or three arguments:
1599 * - certificate type
1600 * - certificate file / directory / nickname
1601 * - certificate password (optional)
1603 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
1607 const char *password)
1609 util_ldap_state_t *st =
1610 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1615 apr_ldap_opt_tls_cert_t *cert;
1617 /* handle the certificate type */
1619 cert_type = util_ldap_parse_cert_type(type);
1620 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1621 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1622 "not recognised. It should be one "
1623 "of CERT_DER, CERT_BASE64, "
1624 "CERT_NICKNAME, CERT_PFX,"
1625 "KEY_DER, KEY_BASE64, KEY_PFX",
1628 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1629 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1630 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1631 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1632 APR_LDAP_CERT_TYPE_PFX == cert_type ||
1633 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1634 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1635 "only valid within a "
1636 "LDAPTrustedGlobalCert directive. "
1637 "Only CERT_DER, CERT_BASE64, "
1638 "CERT_NICKNAME, KEY_DER, and "
1639 "KEY_BASE64 may be used.", type);
1643 return "Certificate type was not specified.";
1646 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1647 "LDAP: SSL trusted client cert - %s (type %s)",
1650 /* add the certificate to the global array */
1651 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1652 cert->type = cert_type;
1654 cert->password = password;
1656 /* if file is a file or path, fix the path */
1657 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1658 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1660 cert->path = ap_server_root_relative(cmd->pool, file);
1662 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1665 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1666 "LDAP: Could not open SSL client certificate "
1668 cert->path == NULL ? file : cert->path);
1669 return "Invalid client certificate file path";
1679 * Set LDAPTrustedMode.
1681 * This directive sets what encryption mode to use on a connection:
1682 * - None (No encryption)
1683 * - SSL (SSL encryption)
1684 * - STARTTLS (TLS encryption)
1686 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
1689 util_ldap_state_t *st =
1690 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1693 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1694 "LDAP: SSL trusted mode - %s",
1697 if (0 == strcasecmp("NONE", mode)) {
1698 st->secure = APR_LDAP_NONE;
1700 else if (0 == strcasecmp("SSL", mode)) {
1701 st->secure = APR_LDAP_SSL;
1703 else if ( (0 == strcasecmp("TLS", mode))
1704 || (0 == strcasecmp("STARTTLS", mode))) {
1705 st->secure = APR_LDAP_STARTTLS;
1708 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1709 "SSL, or TLS/STARTTLS";
1716 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
1720 util_ldap_state_t *st =
1721 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1723 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1729 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1730 "LDAP: SSL verify server certificate - %s",
1731 mode?"TRUE":"FALSE");
1733 st->verify_svr_cert = mode;
1739 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
1743 util_ldap_state_t *st =
1744 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1746 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1752 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1753 st->connectionTimeout = atol(ttl);
1755 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1756 "[%" APR_PID_T_FMT "] ldap connection: Setting connection timeout to "
1757 "%ld seconds.", getpid(), st->connectionTimeout);
1759 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
1760 "LDAP: Connection timout option not supported by the "
1761 "LDAP SDK in use." );
1768 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1770 util_ldap_state_t *st =
1771 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1773 /* Create a per vhost pool for mod_ldap to use, serialized with
1774 * st->mutex (also one per vhost). both are replicated by fork(),
1775 * no shared memory managed by either.
1777 apr_pool_create(&st->pool, p);
1779 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
1782 st->cache_bytes = 100000;
1783 st->search_cache_ttl = 600000000;
1784 st->search_cache_size = 1024;
1785 st->compare_cache_ttl = 600000000;
1786 st->compare_cache_size = 1024;
1787 st->connections = NULL;
1788 st->ssl_supported = 0;
1789 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1790 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1791 st->secure = APR_LDAP_NONE;
1793 st->connectionTimeout = 10;
1794 st->verify_svr_cert = 1;
1799 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
1802 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
1803 util_ldap_state_t *base = (util_ldap_state_t *) basev;
1804 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
1806 st->pool = overrides->pool;
1808 st->mutex = overrides->mutex;
1811 /* The cache settings can not be modified in a
1812 virtual host since all server use the same
1813 shared memory cache. */
1814 st->cache_bytes = base->cache_bytes;
1815 st->search_cache_ttl = base->search_cache_ttl;
1816 st->search_cache_size = base->search_cache_size;
1817 st->compare_cache_ttl = base->compare_cache_ttl;
1818 st->compare_cache_size = base->compare_cache_size;
1820 st->connections = NULL;
1821 st->ssl_supported = 0;
1822 st->global_certs = apr_array_append(p, base->global_certs,
1823 overrides->global_certs);
1824 st->client_certs = apr_array_append(p, base->client_certs,
1825 overrides->client_certs);
1826 st->secure = (overrides->secure_set == 0) ? base->secure
1827 : overrides->secure;
1829 /* These LDAP connection settings can not be overwritten in
1830 a virtual host. Once set in the base server, they must
1831 remain the same. None of the LDAP SDKs seem to be able
1832 to handle setting the verify_svr_cert flag on a
1833 per-connection basis. The OpenLDAP client appears to be
1834 able to handle the connection timeout per-connection
1835 but the Novell SDK cannot. Allowing the timeout to
1836 be set by each vhost is of little value so rather than
1837 trying to make special expections for one LDAP SDK, GLOBAL_ONLY
1838 is being enforced on this setting as well. */
1839 st->connectionTimeout = base->connectionTimeout;
1840 st->verify_svr_cert = base->verify_svr_cert;
1845 static apr_status_t util_ldap_cleanup_module(void *data)
1848 server_rec *s = data;
1849 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1850 s->module_config, &ldap_module);
1852 if (st->ssl_supported) {
1853 apr_ldap_ssl_deinit();
1860 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
1861 apr_pool_t *ptemp, server_rec *s)
1863 apr_status_t result;
1864 server_rec *s_vhost;
1865 util_ldap_state_t *st_vhost;
1867 util_ldap_state_t *st = (util_ldap_state_t *)
1868 ap_get_module_config(s->module_config,
1872 const char *userdata_key = "util_ldap_init";
1873 apr_ldap_err_t *result_err = NULL;
1876 /* util_ldap_post_config() will be called twice. Don't bother
1877 * going through all of the initialization on the first call
1878 * because it will just be thrown away.*/
1879 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1881 apr_pool_userdata_set((const void *)1, userdata_key,
1882 apr_pool_cleanup_null, s->process->pool);
1884 #if APR_HAS_SHARED_MEMORY
1885 /* If the cache file already exists then delete it. Otherwise we are
1886 * going to run into problems creating the shared memory. */
1887 if (st->cache_file) {
1888 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
1890 apr_file_remove(lck_file, ptemp);
1896 #if APR_HAS_SHARED_MEMORY
1897 /* initializing cache if shared memory size is not zero and we already
1898 * don't have shm address
1900 if (!st->cache_shm && st->cache_bytes > 0) {
1902 result = util_ldap_cache_init(p, st);
1903 if (result != APR_SUCCESS) {
1904 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1905 "LDAP cache: could not create shared memory segment");
1910 #if APR_HAS_SHARED_MEMORY
1911 if (st->cache_file) {
1912 st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
1917 result = apr_global_mutex_create(&st->util_ldap_cache_lock,
1918 st->lock_file, APR_LOCK_DEFAULT,
1920 if (result != APR_SUCCESS) {
1924 #ifdef AP_NEED_SET_MUTEX_PERMS
1925 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1926 if (result != APR_SUCCESS) {
1927 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
1928 "LDAP cache: failed to set mutex permissions");
1933 /* merge config in all vhost */
1936 st_vhost = (util_ldap_state_t *)
1937 ap_get_module_config(s_vhost->module_config,
1940 #if APR_HAS_SHARED_MEMORY
1941 st_vhost->cache_shm = st->cache_shm;
1942 st_vhost->cache_rmm = st->cache_rmm;
1943 st_vhost->cache_file = st->cache_file;
1944 ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
1945 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
1946 "for VHOST: %s", st->cache_shm, st->cache_rmm,
1947 s_vhost->server_hostname);
1949 st_vhost->lock_file = st->lock_file;
1950 s_vhost = s_vhost->next;
1952 #if APR_HAS_SHARED_MEMORY
1955 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1956 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
1957 "shared memory cache");
1961 /* log the LDAP SDK used
1964 apr_ldap_err_t *result = NULL;
1965 apr_ldap_info(p, &(result));
1966 if (result != NULL) {
1967 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
1971 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1972 util_ldap_cleanup_module);
1975 * Initialize SSL support, and log the result for the benefit of the admin.
1977 * If SSL is not supported it is not necessarily an error, as the
1978 * application may not want to use it.
1980 rc = apr_ldap_ssl_init(p,
1984 if (APR_SUCCESS == rc) {
1985 rc = apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT,
1986 (void *)st->global_certs, &(result_err));
1989 if (APR_SUCCESS == rc) {
1990 st->ssl_supported = 1;
1991 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1992 "LDAP: SSL support available" );
1995 st->ssl_supported = 0;
1996 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1997 "LDAP: SSL support unavailable%s%s",
1998 result_err ? ": " : "",
1999 result_err ? result_err->reason : "");
2005 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2008 util_ldap_state_t *st = ap_get_module_config(s->module_config,
2011 if (!st->util_ldap_cache_lock) return;
2013 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2015 if (sts != APR_SUCCESS) {
2016 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2017 "Failed to initialise global mutex %s in child process %"
2019 st->lock_file, getpid());
2023 static const command_rec util_ldap_cmds[] = {
2024 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2026 "Set the size of the shared memory cache (in bytes). Use "
2027 "0 to disable the shared memory cache. (default: 100000)"),
2029 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2031 "Set the file name for the shared memory cache."),
2033 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2035 "Set the maximum number of entries that are possible in the "
2036 "LDAP search cache. Use 0 for no limit. "
2037 "-1 disables the cache. (default: 1024)"),
2039 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2041 "Set the maximum time (in seconds) that an item can be "
2042 "cached in the LDAP search cache. Use 0 for no limit. "
2045 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2047 "Set the maximum number of entries that are possible "
2048 "in the LDAP compare cache. Use 0 for no limit. "
2049 "Use -1 to disable the cache. (default: 1024)"),
2051 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2053 "Set the maximum time (in seconds) that an item is cached "
2054 "in the LDAP operation cache. Use 0 for no limit. "
2057 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2059 "Takes three args; the file and/or directory containing "
2060 "the trusted CA certificates (and global client certs "
2061 "for Netware) used to validate the LDAP server. Second "
2062 "arg is the cert type for the first arg, one of CA_DER, "
2063 "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2064 "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2065 "Third arg is an optional passphrase if applicable."),
2067 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2069 "Takes three args; the file and/or directory containing "
2070 "the client certificate, or certificate ID used to "
2071 "validate this LDAP client. Second arg is the cert type "
2072 "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2073 "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2074 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2075 "optional passphrase if applicable."),
2077 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2079 "Specify the type of security that should be applied to "
2080 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2082 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2084 "Set to 'ON' requires that the server certificate be verified "
2085 "before a secure LDAP connection can be establish. Default 'ON'"),
2087 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2089 "Specify the LDAP socket connection timeout in seconds "
2095 static void util_ldap_register_hooks(apr_pool_t *p)
2097 APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2098 APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2099 APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2100 APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2101 APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2102 APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2103 APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2104 APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2105 APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2106 APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2108 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2109 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2110 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2113 module AP_MODULE_DECLARE_DATA ldap_module = {
2114 STANDARD20_MODULE_STUFF,
2115 NULL, /* create dir config */
2116 NULL, /* merge dir config */
2117 util_ldap_create_config, /* create server config */
2118 util_ldap_merge_config, /* merge server config */
2119 util_ldap_cmds, /* command table */
2120 util_ldap_register_hooks, /* set up request processing hooks */