1 /* Copyright 2001-2004 The Apache Software Foundation
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 * util_ldap.c: LDAP things
19 * Original code from auth_ldap module for Apache v1.3:
20 * Copyright 1998, 1999 Enbridge Pipelines Inc.
21 * Copyright 1999-2001 Dave Carrigan
25 #include "http_config.h"
26 #include "http_core.h"
28 #include "http_protocol.h"
29 #include "http_request.h"
30 #include "util_ldap.h"
31 #include "util_ldap_cache.h"
33 #include <apr_strings.h>
40 #error mod_ldap requires APR-util to have LDAP support built in
43 #ifdef AP_NEED_SET_MUTEX_PERMS
47 /* defines for certificate file types
49 #define LDAP_CA_TYPE_UNKNOWN 0
50 #define LDAP_CA_TYPE_DER 1
51 #define LDAP_CA_TYPE_BASE64 2
52 #define LDAP_CA_TYPE_CERT7_DB 3
55 module AP_MODULE_DECLARE_DATA ldap_module;
57 int util_ldap_handler(request_rec *r);
58 void *util_ldap_create_config(apr_pool_t *p, server_rec *s);
62 * Some definitions to help between various versions of apache.
65 #ifndef DOCTYPE_HTML_2_0
66 #define DOCTYPE_HTML_2_0 "<!DOCTYPE HTML PUBLIC \"-//IETF//" \
67 "DTD HTML 2.0//EN\">\n"
70 #ifndef DOCTYPE_HTML_3_2
71 #define DOCTYPE_HTML_3_2 "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
72 "DTD HTML 3.2 Final//EN\">\n"
75 #ifndef DOCTYPE_HTML_4_0S
76 #define DOCTYPE_HTML_4_0S "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
77 "DTD HTML 4.0//EN\"\n" \
78 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
81 #ifndef DOCTYPE_HTML_4_0T
82 #define DOCTYPE_HTML_4_0T "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
83 "DTD HTML 4.0 Transitional//EN\"\n" \
84 "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
87 #ifndef DOCTYPE_HTML_4_0F
88 #define DOCTYPE_HTML_4_0F "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
89 "DTD HTML 4.0 Frameset//EN\"\n" \
90 "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n"
93 #define LDAP_CACHE_LOCK() \
94 if (st->util_ldap_cache_lock) \
95 apr_global_mutex_lock(st->util_ldap_cache_lock)
96 #define LDAP_CACHE_UNLOCK() \
97 if (st->util_ldap_cache_lock) \
98 apr_global_mutex_unlock(st->util_ldap_cache_lock)
101 static void util_ldap_strdup (char **str, const char *newstr)
109 *str = calloc(1, strlen(newstr)+1);
110 strcpy (*str, newstr);
118 * This handler generates a status page about the current performance of
119 * the LDAP cache. It is enabled as follows:
121 * <Location /ldap-status>
122 * SetHandler ldap-status
126 int util_ldap_handler(request_rec *r)
128 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config, &ldap_module);
130 r->allowed |= (1 << M_GET);
131 if (r->method_number != M_GET)
134 if (strcmp(r->handler, "ldap-status")) {
138 r->content_type = "text/html";
142 ap_rputs(DOCTYPE_HTML_3_2
143 "<html><head><title>LDAP Cache Information</title></head>\n", r);
144 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information</h1>\n", r);
146 util_ald_cache_display(r, st);
151 /* ------------------------------------------------------------------ */
155 * Closes an LDAP connection by unlocking it. The next time
156 * util_ldap_connection_find() is called this connection will be
157 * available for reuse.
159 LDAP_DECLARE(void) util_ldap_connection_close(util_ldap_connection_t *ldc)
165 * Is it safe leaving bound connections floating around between the
166 * different modules? Keeping the user bound is a performance boost,
167 * but it is also a potential security problem - maybe.
169 * For now we unbind the user when we finish with a connection, but
170 * we don't have to...
173 /* mark our connection as available for reuse */
176 apr_thread_mutex_unlock(ldc->lock);
182 * Destroys an LDAP connection by unbinding and closing the connection to
183 * the LDAP server. It is used to bring the connection back to a known
184 * state after an error, and during pool cleanup.
186 LDAP_DECLARE_NONSTD(apr_status_t) util_ldap_connection_unbind(void *param)
188 util_ldap_connection_t *ldc = param;
192 ldap_unbind_s(ldc->ldap);
203 * Clean up an LDAP connection by unbinding and unlocking the connection.
204 * This function is registered with the pool cleanup function - causing
205 * the LDAP connections to be shut down cleanly on graceful restart.
207 LDAP_DECLARE_NONSTD(apr_status_t) util_ldap_connection_cleanup(void *param)
209 util_ldap_connection_t *ldc = param;
213 /* unbind and disconnect from the LDAP server */
214 util_ldap_connection_unbind(ldc);
216 /* free the username and password */
218 free((void*)ldc->bindpw);
221 free((void*)ldc->binddn);
224 /* unlock this entry */
225 util_ldap_connection_close(ldc);
234 * Connect to the LDAP server and binds. Does not connect if already
235 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
237 * Returns LDAP_SUCCESS on success; and an error code on failure
239 LDAP_DECLARE(int) util_ldap_connection_open(request_rec *r,
240 util_ldap_connection_t *ldc)
244 int version = LDAP_VERSION3;
245 apr_ldap_err_t *result = NULL;
247 /* sanity check for NULL */
252 /* If the connection is already bound, return
256 ldc->reason = "LDAP: connection open successful (already bound)";
260 /* create the ldap session handle
262 if (NULL == ldc->ldap)
264 /* Since the host will include a port if the default port is not used,
265 * always specify the default ports for the port parameter. This will
266 * allow a host string that contains multiple hosts the ability to mix
267 * some hosts with ports and some without. All hosts which do not
268 * specify a port will use the default port.
270 apr_ldap_init(ldc->pool, &(ldc->ldap),
272 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
277 if (result != NULL) {
278 ldc->reason = result->reason;
281 if (NULL == ldc->ldap)
284 if (NULL == ldc->reason) {
285 ldc->reason = "LDAP: ldap initialization failed";
288 ldc->reason = result->reason;
293 /* set client certificates */
294 if (!apr_is_empty_array(ldc->client_certs)) {
295 apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
296 ldc->client_certs, &(result));
297 if (LDAP_SUCCESS != result->rc) {
298 ldap_unbind_s(ldc->ldap);
301 ldc->reason = result->reason;
306 /* switch on SSL/TLS */
307 if (APR_LDAP_NONE != ldc->secure) {
308 apr_ldap_set_option(ldc->pool, ldc->ldap,
309 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
310 if (LDAP_SUCCESS != result->rc) {
311 ldap_unbind_s(ldc->ldap);
314 ldc->reason = result->reason;
319 /* Set the alias dereferencing option */
320 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
322 /* always default to LDAP V3 */
323 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
328 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
329 * returned. Break out of the loop on Success or any other error.
331 * NOTE: Looping is probably not a great idea. If the server isn't
332 * responding the chances it will respond after a few tries are poor.
333 * However, the original code looped and it only happens on
334 * the error condition.
336 for (failures=0; failures<10; failures++)
338 rc = ldap_simple_bind_s(ldc->ldap,
340 (char *)ldc->bindpw);
341 if (LDAP_SERVER_DOWN != rc) {
346 /* free the handle if there was an error
348 if (LDAP_SUCCESS != rc)
350 ldap_unbind_s(ldc->ldap);
353 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
357 ldc->reason = "LDAP: connection open successful";
365 * Compare client certificate arrays.
367 * Returns 1 on compare failure, 0 otherwise.
369 int compare_client_certs(apr_array_header_t *srcs, apr_array_header_t *dests) {
372 struct apr_ldap_opt_tls_cert_t *src, *dest;
374 /* arrays both NULL? if so, then equal */
375 if (srcs == NULL && dests == NULL) {
379 /* arrays different length or either NULL? If so, then not equal */
380 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
384 /* run an actual comparison */
385 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
386 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
387 for (i = 0; i < srcs->nelts; i++) {
388 if (apr_strnatcmp(src[i].path, dest[i].path) ||
389 apr_strnatcmp(src[i].password, dest[i].password) ||
390 src[i].type != dest[i].type) {
395 /* if we got here, the cert arrays were identical */
402 * Find an existing ldap connection struct that matches the
403 * provided ldap connection parameters.
405 * If not found in the cache, a new ldc structure will be allocated from st->pool
406 * and returned to the caller. If found in the cache, a pointer to the existing
407 * ldc structure will be returned.
409 LDAP_DECLARE(util_ldap_connection_t *)
410 util_ldap_connection_find(request_rec *r,
411 const char *host, int port,
412 const char *binddn, const char *bindpw,
413 deref_options deref, int secure) {
414 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
416 util_ldap_state_t *st =
417 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
422 /* mutex lock this function */
424 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
426 apr_thread_mutex_lock(st->mutex);
429 /* Search for an exact connection match in the list that is not
432 for (l=st->connections,p=NULL; l; l=l->next) {
434 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
436 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
437 ((!l->binddn && !binddn) || (l->binddn && binddn && !strcmp(l->binddn, binddn))) &&
438 ((!l->bindpw && !bindpw) || (l->bindpw && bindpw && !strcmp(l->bindpw, bindpw))) &&
439 (l->deref == deref) && (l->secure == secure) &&
440 !compare_client_certs(st->client_certs, l->client_certs)) {
445 /* If this connection didn't match the criteria, then we
446 * need to unlock the mutex so it is available to be reused.
448 apr_thread_mutex_unlock(l->lock);
454 /* If nothing found, search again, but we don't care about the
455 * binddn and bindpw this time.
458 for (l=st->connections,p=NULL; l; l=l->next) {
460 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
463 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
464 (l->deref == deref) && (l->secure == secure) &&
465 !compare_client_certs(st->client_certs, l->client_certs)) {
467 /* the bind credentials have changed */
469 util_ldap_strdup((char**)&(l->binddn), binddn);
470 util_ldap_strdup((char**)&(l->bindpw), bindpw);
474 /* If this connection didn't match the criteria, then we
475 * need to unlock the mutex so it is available to be reused.
477 apr_thread_mutex_unlock(l->lock);
484 /* artificially disable cache */
487 /* If no connection what found after the second search, we
493 * Add the new connection entry to the linked list. Note that we
494 * don't actually establish an LDAP connection yet; that happens
495 * the first time authentication is requested.
497 /* create the details to the pool in st */
498 l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
500 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
501 apr_thread_mutex_lock(l->lock);
505 l->host = apr_pstrdup(st->pool, host);
508 util_ldap_strdup((char**)&(l->binddn), binddn);
509 util_ldap_strdup((char**)&(l->bindpw), bindpw);
511 /* The security mode after parsing the URL will always be either
512 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
513 * If the security setting is NONE, override it to the security
514 * setting optionally supplied by the admin using LDAPTrustedMode
516 l->secure = (APR_LDAP_NONE == secure) ?
520 /* save away a copy of the client cert list that is presently valid */
521 l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
523 /* add the cleanup to the pool */
524 apr_pool_cleanup_register(l->pool, l,
525 util_ldap_connection_cleanup,
526 apr_pool_cleanup_null);
537 apr_thread_mutex_unlock(st->mutex);
542 /* ------------------------------------------------------------------ */
545 * Compares two DNs to see if they're equal. The only way to do this correctly is to
546 * search for the dn and then do ldap_get_dn() on the result. This should match the
547 * initial dn, since it would have been also retrieved with ldap_get_dn(). This is
548 * expensive, so if the configuration value compare_dn_on_server is
549 * false, just does an ordinary strcmp.
551 * The lock for the ldap cache should already be acquired.
553 LDAP_DECLARE(int) util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
554 const char *url, const char *dn, const char *reqdn,
555 int compare_dn_on_server)
558 util_url_node_t *curl;
559 util_url_node_t curnode;
560 util_dn_compare_node_t *node;
561 util_dn_compare_node_t newnode;
563 LDAPMessage *res, *entry;
566 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config, &ldap_module);
568 /* get cache entry (or create one) */
572 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
574 curl = util_ald_create_caches(st, url);
578 /* a simple compare? */
579 if (!compare_dn_on_server) {
580 /* unlock this read lock */
581 if (strcmp(dn, reqdn)) {
582 ldc->reason = "DN Comparison FALSE (direct strcmp())";
583 return LDAP_COMPARE_FALSE;
586 ldc->reason = "DN Comparison TRUE (direct strcmp())";
587 return LDAP_COMPARE_TRUE;
592 /* no - it's a server side compare */
595 /* is it in the compare cache? */
596 newnode.reqdn = (char *)reqdn;
597 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
599 /* If it's in the cache, it's good */
600 /* unlock this read lock */
602 ldc->reason = "DN Comparison TRUE (cached)";
603 return LDAP_COMPARE_TRUE;
606 /* unlock this read lock */
611 if (failures++ > 10) {
612 /* too many failures */
616 /* make a server connection */
617 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
618 /* connect to server failed */
622 /* search for reqdn */
623 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
624 "(objectclass=*)", NULL, 1,
625 NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
626 ldc->reason = "DN Comparison ldap_search_ext_s() failed with server down";
627 util_ldap_connection_unbind(ldc);
630 if (result != LDAP_SUCCESS) {
631 /* search for reqdn failed - no match */
632 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
636 entry = ldap_first_entry(ldc->ldap, res);
637 searchdn = ldap_get_dn(ldc->ldap, entry);
640 if (strcmp(dn, searchdn) != 0) {
641 /* compare unsuccessful */
642 ldc->reason = "DN Comparison FALSE (checked on server)";
643 result = LDAP_COMPARE_FALSE;
647 /* compare successful - add to the compare cache */
649 newnode.reqdn = (char *)reqdn;
650 newnode.dn = (char *)dn;
652 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
653 if ((node == NULL) ||
654 (strcmp(reqdn, node->reqdn) != 0) || (strcmp(dn, node->dn) != 0)) {
656 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
660 ldc->reason = "DN Comparison TRUE (checked on server)";
661 result = LDAP_COMPARE_TRUE;
663 ldap_memfree(searchdn);
669 * Does an generic ldap_compare operation. It accepts a cache that it will use
670 * to lookup the compare in the cache. We cache two kinds of compares
671 * (require group compares) and (require user compares). Each compare has a different
672 * cache node: require group includes the DN; require user does not because the
673 * require user cache is owned by the
676 LDAP_DECLARE(int) util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
677 const char *url, const char *dn,
678 const char *attrib, const char *value)
681 util_url_node_t *curl;
682 util_url_node_t curnode;
683 util_compare_node_t *compare_nodep;
684 util_compare_node_t the_compare_node;
685 apr_time_t curtime = 0; /* silence gcc -Wall */
688 util_ldap_state_t *st =
689 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
692 /* get cache entry (or create one) */
695 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
697 curl = util_ald_create_caches(st, url);
702 /* make a comparison to the cache */
704 curtime = apr_time_now();
706 the_compare_node.dn = (char *)dn;
707 the_compare_node.attrib = (char *)attrib;
708 the_compare_node.value = (char *)value;
709 the_compare_node.result = 0;
711 compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
713 if (compare_nodep != NULL) {
715 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
716 /* ...but it is too old */
717 util_ald_cache_remove(curl->compare_cache, compare_nodep);
720 /* ...and it is good */
721 /* unlock this read lock */
723 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
724 ldc->reason = "Comparison true (cached)";
725 return compare_nodep->result;
727 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
728 ldc->reason = "Comparison false (cached)";
729 return compare_nodep->result;
731 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
732 ldc->reason = "Comparison no such attribute (cached)";
733 return compare_nodep->result;
736 ldc->reason = "Comparison undefined (cached)";
737 return compare_nodep->result;
741 /* unlock this read lock */
746 if (failures++ > 10) {
747 /* too many failures */
750 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
755 if ((result = ldap_compare_s(ldc->ldap,
759 == LDAP_SERVER_DOWN) {
760 /* connection failed - try again */
761 ldc->reason = "ldap_compare_s() failed with server down";
762 util_ldap_connection_unbind(ldc);
766 ldc->reason = "Comparison complete";
767 if ((LDAP_COMPARE_TRUE == result) ||
768 (LDAP_COMPARE_FALSE == result) ||
769 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
771 /* compare completed; caching result */
773 the_compare_node.lastcompare = curtime;
774 the_compare_node.result = result;
776 /* If the node doesn't exist then insert it, otherwise just update it with
778 compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
779 if ((compare_nodep == NULL) ||
780 (strcmp(the_compare_node.dn, compare_nodep->dn) != 0) ||
781 (strcmp(the_compare_node.attrib, compare_nodep->attrib) != 0) ||
782 (strcmp(the_compare_node.value, compare_nodep->value) != 0)) {
784 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
787 compare_nodep->lastcompare = curtime;
788 compare_nodep->result = result;
792 if (LDAP_COMPARE_TRUE == result) {
793 ldc->reason = "Comparison true (adding to cache)";
794 return LDAP_COMPARE_TRUE;
796 else if (LDAP_COMPARE_FALSE == result) {
797 ldc->reason = "Comparison false (adding to cache)";
798 return LDAP_COMPARE_FALSE;
801 ldc->reason = "Comparison no such attribute (adding to cache)";
802 return LDAP_NO_SUCH_ATTRIBUTE;
808 LDAP_DECLARE(int) util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
809 const char *url, const char *basedn, int scope, char **attrs,
810 const char *filter, const char *bindpw, const char **binddn,
811 const char ***retvals)
813 const char **vals = NULL;
815 LDAPMessage *res, *entry;
819 util_url_node_t *curl; /* Cached URL node */
820 util_url_node_t curnode;
821 util_search_node_t *search_nodep; /* Cached search node */
822 util_search_node_t the_search_node;
825 util_ldap_state_t *st =
826 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
829 /* Get the cache node for this url */
832 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
834 curl = util_ald_create_caches(st, url);
840 the_search_node.username = filter;
841 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
842 if (search_nodep != NULL) {
844 /* found entry in search cache... */
845 curtime = apr_time_now();
848 * Remove this item from the cache if its expired. If the sent password
849 * doesn't match the storepassword, the entry will be removed and readded
850 * later if the credentials pass authentication.
852 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
853 /* ...but entry is too old */
854 util_ald_cache_remove(curl->search_cache, search_nodep);
856 else if ((search_nodep->bindpw) && (search_nodep->bindpw[0] != '\0') &&
857 (strcmp(search_nodep->bindpw, bindpw) == 0)) {
858 /* ...and entry is valid */
859 *binddn = search_nodep->dn;
860 *retvals = search_nodep->vals;
862 ldc->reason = "Authentication successful (cached)";
866 /* unlock this read lock */
871 * At this point, there is no valid cached search, so lets do the search.
875 * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
878 if (failures++ > 10) {
881 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
885 /* try do the search */
886 if ((result = ldap_search_ext_s(ldc->ldap,
887 (char *)basedn, scope,
888 (char *)filter, attrs, 0,
889 NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
890 ldc->reason = "ldap_search_ext_s() for user failed with server down";
891 util_ldap_connection_unbind(ldc);
895 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
896 if (result != LDAP_SUCCESS) {
897 ldc->reason = "ldap_search_ext_s() for user failed";
902 * We should have found exactly one entry; to find a different
903 * number is an error.
905 count = ldap_count_entries(ldc->ldap, res);
909 ldc->reason = "User not found";
911 ldc->reason = "User is not unique (search found two or more matches)";
913 return LDAP_NO_SUCH_OBJECT;
916 entry = ldap_first_entry(ldc->ldap, res);
918 /* Grab the dn, copy it into the pool, and free it again */
919 dn = ldap_get_dn(ldc->ldap, entry);
920 *binddn = apr_pstrdup(r->pool, dn);
924 * A bind to the server with an empty password always succeeds, so
925 * we check to ensure that the password is not empty. This implies
926 * that users who actually do have empty passwords will never be
927 * able to authenticate with this module. I don't see this as a big
930 if (!bindpw || strlen(bindpw) <= 0) {
932 ldc->reason = "Empty password not allowed";
933 return LDAP_INVALID_CREDENTIALS;
937 * Attempt to bind with the retrieved dn and the password. If the bind
938 * fails, it means that the password is wrong (the dn obviously
939 * exists, since we just retrieved it)
941 if ((result = ldap_simple_bind_s(ldc->ldap,
943 (char *)bindpw)) == LDAP_SERVER_DOWN) {
944 ldc->reason = "ldap_simple_bind_s() to check user credentials "
945 "failed with server down";
947 util_ldap_connection_unbind(ldc);
951 /* failure? if so - return */
952 if (result != LDAP_SUCCESS) {
953 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
955 util_ldap_connection_unbind(ldc);
960 * We have just bound the connection to a different user and password
961 * combination, which might be reused unintentionally next time this
962 * connection is used from the connection pool. To ensure no confusion,
963 * we mark the connection as unbound.
969 * Get values for the provided attributes.
975 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
981 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
982 while (values && values[j]) {
983 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
986 ldap_value_free(values);
994 * Add the new username to the search cache.
998 the_search_node.username = filter;
999 the_search_node.dn = *binddn;
1000 the_search_node.bindpw = bindpw;
1001 the_search_node.lastbind = apr_time_now();
1002 the_search_node.vals = vals;
1004 /* Search again to make sure that another thread didn't ready insert this node
1005 into the cache before we got here. If it does exist then update the lastbind */
1006 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1007 if ((search_nodep == NULL) ||
1008 (strcmp(*binddn, search_nodep->dn) != 0)) {
1010 /* Nothing in cache, insert new entry */
1011 util_ald_cache_insert(curl->search_cache, &the_search_node);
1013 else if ((!search_nodep->bindpw) ||
1014 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1016 /* Entry in cache is invalid, remove it and insert new one */
1017 util_ald_cache_remove(curl->search_cache, search_nodep);
1018 util_ald_cache_insert(curl->search_cache, &the_search_node);
1021 /* Cache entry is valid, update lastbind */
1022 search_nodep->lastbind = the_search_node.lastbind;
1024 LDAP_CACHE_UNLOCK();
1028 ldc->reason = "Authentication successful";
1029 return LDAP_SUCCESS;
1033 * This function will return the DN of the entry matching userid.
1034 * It is used to get the DN in case some other module than mod_auth_ldap
1035 * has authenticated the user.
1036 * The function is basically a copy of util_ldap_cache_checkuserid
1037 * with password checking removed.
1039 LDAP_DECLARE(int) util_ldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1040 const char *url, const char *basedn, int scope, char **attrs,
1041 const char *filter, const char **binddn,
1042 const char ***retvals)
1044 const char **vals = NULL;
1046 LDAPMessage *res, *entry;
1050 util_url_node_t *curl; /* Cached URL node */
1051 util_url_node_t curnode;
1052 util_search_node_t *search_nodep; /* Cached search node */
1053 util_search_node_t the_search_node;
1056 util_ldap_state_t *st =
1057 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1060 /* Get the cache node for this url */
1063 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1065 curl = util_ald_create_caches(st, url);
1067 LDAP_CACHE_UNLOCK();
1071 the_search_node.username = filter;
1072 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1073 if (search_nodep != NULL) {
1075 /* found entry in search cache... */
1076 curtime = apr_time_now();
1079 * Remove this item from the cache if its expired.
1081 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1082 /* ...but entry is too old */
1083 util_ald_cache_remove(curl->search_cache, search_nodep);
1086 /* ...and entry is valid */
1087 *binddn = search_nodep->dn;
1088 *retvals = search_nodep->vals;
1089 LDAP_CACHE_UNLOCK();
1090 ldc->reason = "Search successful (cached)";
1091 return LDAP_SUCCESS;
1094 /* unlock this read lock */
1095 LDAP_CACHE_UNLOCK();
1099 * At this point, there is no valid cached search, so lets do the search.
1103 * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1106 if (failures++ > 10) {
1109 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
1113 /* try do the search */
1114 if ((result = ldap_search_ext_s(ldc->ldap,
1115 (char *)basedn, scope,
1116 (char *)filter, attrs, 0,
1118 NULL, -1, &res)) == LDAP_SERVER_DOWN) {
1119 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1123 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1124 if (result != LDAP_SUCCESS) {
1125 ldc->reason = "ldap_search_ext_s() for user failed";
1130 * We should have found exactly one entry; to find a different
1131 * number is an error.
1133 count = ldap_count_entries(ldc->ldap, res);
1137 ldc->reason = "User not found";
1139 ldc->reason = "User is not unique (search found two or more matches)";
1141 return LDAP_NO_SUCH_OBJECT;
1144 entry = ldap_first_entry(ldc->ldap, res);
1146 /* Grab the dn, copy it into the pool, and free it again */
1147 dn = ldap_get_dn(ldc->ldap, entry);
1148 *binddn = apr_pstrdup(st->pool, dn);
1152 * Get values for the provided attributes.
1158 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1164 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1165 while (values && values[j]) {
1166 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
1169 ldap_value_free(values);
1177 * Add the new username to the search cache.
1181 the_search_node.username = filter;
1182 the_search_node.dn = *binddn;
1183 the_search_node.bindpw = NULL;
1184 the_search_node.lastbind = apr_time_now();
1185 the_search_node.vals = vals;
1187 /* Search again to make sure that another thread didn't ready insert this node
1188 into the cache before we got here. If it does exist then update the lastbind */
1189 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1190 if ((search_nodep == NULL) ||
1191 (strcmp(*binddn, search_nodep->dn) != 0)) {
1193 /* Nothing in cache, insert new entry */
1194 util_ald_cache_insert(curl->search_cache, &the_search_node);
1197 * Don't update lastbind on entries with bindpw because
1198 * we haven't verified that password. It's OK to update
1199 * the entry if there is no password in it.
1201 else if (!search_nodep->bindpw) {
1202 /* Cache entry is valid, update lastbind */
1203 search_nodep->lastbind = the_search_node.lastbind;
1205 LDAP_CACHE_UNLOCK();
1210 ldc->reason = "Search successful";
1211 return LDAP_SUCCESS;
1215 * Reports if ssl support is enabled
1217 * 1 = enabled, 0 = not enabled
1219 LDAP_DECLARE(int) util_ldap_ssl_supported(request_rec *r)
1221 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1222 r->server->module_config, &ldap_module);
1224 return(st->ssl_supported);
1228 /* ---------------------------------------- */
1229 /* config directives */
1232 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes)
1234 util_ldap_state_t *st =
1235 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1238 st->cache_bytes = atol(bytes);
1240 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1241 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1242 " cache size to %" APR_SIZE_T_FMT " bytes.",
1243 getpid(), st->cache_bytes);
1248 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy, const char *file)
1250 util_ldap_state_t *st =
1251 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1255 st->cache_file = ap_server_root_relative(st->pool, file);
1258 st->cache_file = NULL;
1261 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1262 "LDAP cache: Setting shared memory cache file to %s bytes.",
1268 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
1270 util_ldap_state_t *st =
1271 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1274 st->search_cache_ttl = atol(ttl) * 1000000;
1276 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1277 "[%d] ldap cache: Setting cache TTL to %ld microseconds.",
1278 getpid(), st->search_cache_ttl);
1283 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size)
1285 util_ldap_state_t *st =
1286 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1290 st->search_cache_size = atol(size);
1291 if (st->search_cache_size < 0) {
1292 st->search_cache_size = 0;
1295 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1296 "[%d] ldap cache: Setting search cache size to %ld entries.",
1297 getpid(), st->search_cache_size);
1302 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
1304 util_ldap_state_t *st =
1305 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1308 st->compare_cache_ttl = atol(ttl) * 1000000;
1310 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1311 "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.",
1312 getpid(), st->compare_cache_ttl);
1317 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size)
1319 util_ldap_state_t *st =
1320 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1323 st->compare_cache_size = atol(size);
1324 if (st->compare_cache_size < 0) {
1325 st->compare_cache_size = 0;
1328 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1329 "[%d] ldap cache: Setting operation cache size to %ld entries.",
1330 getpid(), st->compare_cache_size);
1337 * Parse the certificate type.
1339 * The type can be one of the following:
1340 * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1341 * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1343 * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1345 static const int util_ldap_parse_cert_type(const char *type) {
1347 /* Authority file in binary DER format */
1348 if (0 == strcasecmp("CA_DER", type)) {
1349 return APR_LDAP_CA_TYPE_DER;
1352 /* Authority file in Base64 format */
1353 else if (0 == strcasecmp("CA_BASE64", type)) {
1354 return APR_LDAP_CA_TYPE_BASE64;
1357 /* Netscape certificate database file/directory */
1358 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1359 return APR_LDAP_CA_TYPE_CERT7_DB;
1362 /* Netscape secmod file/directory */
1363 else if (0 == strcasecmp("CA_SECMOD", type)) {
1364 return APR_LDAP_CA_TYPE_SECMOD;
1367 /* Client cert file in DER format */
1368 else if (0 == strcasecmp("CERT_DER", type)) {
1369 return APR_LDAP_CERT_TYPE_DER;
1372 /* Client cert file in Base64 format */
1373 else if (0 == strcasecmp("CERT_BASE64", type)) {
1374 return APR_LDAP_CERT_TYPE_BASE64;
1377 /* Client cert file in PKCS#12 format */
1378 else if (0 == strcasecmp("CERT_PFX", type)) {
1379 return APR_LDAP_CERT_TYPE_PFX;
1382 /* Netscape client cert database file/directory */
1383 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1384 return APR_LDAP_CERT_TYPE_KEY3_DB;
1387 /* Netscape client cert nickname */
1388 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1389 return APR_LDAP_CERT_TYPE_NICKNAME;
1392 /* Client cert key file in DER format */
1393 else if (0 == strcasecmp("KEY_DER", type)) {
1394 return APR_LDAP_KEY_TYPE_DER;
1397 /* Client cert key file in Base64 format */
1398 else if (0 == strcasecmp("KEY_BASE64", type)) {
1399 return APR_LDAP_KEY_TYPE_BASE64;
1402 /* Client cert key file in PKCS#12 format */
1403 else if (0 == strcasecmp("KEY_PFX", type)) {
1404 return APR_LDAP_KEY_TYPE_PFX;
1408 return APR_LDAP_CA_TYPE_UNKNOWN;
1415 * Set LDAPTrustedGlobalCert.
1417 * This directive takes either two or three arguments:
1418 * - certificate type
1419 * - certificate file / directory / nickname
1420 * - certificate password (optional)
1422 * This directive may only be used globally.
1424 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd, void *dummy, const char *type, const char *file, const char *password)
1426 util_ldap_state_t *st =
1427 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1429 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1433 apr_ldap_opt_tls_cert_t *cert;
1439 /* handle the certificate type */
1441 cert_type = util_ldap_parse_cert_type(type);
1442 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1443 return apr_psprintf(cmd->pool, "The certificate type %s is "
1444 "not recognised. It should be one "
1445 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1446 "CA_SECMOD, CERT_DER, CERT_BASE64, "
1447 "CERT_KEY3_DB, CERT_NICKNAME, "
1448 "KEY_DER, KEY_BASE64", type);
1452 return "Certificate type was not specified.";
1455 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1456 "LDAP: SSL trusted global cert - %s (type %s)",
1459 /* add the certificate to the global array */
1460 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1461 cert->type = cert_type;
1463 cert->password = password;
1465 /* if file is a file or path, fix the path */
1466 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1467 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1469 cert->path = ap_server_root_relative(cmd->pool, file);
1471 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) != APR_SUCCESS)) {
1472 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1473 "LDAP: Could not open SSL trusted certificate "
1474 "authority file - %s",
1475 cert->path == NULL ? file : cert->path);
1476 return "Invalid global certificate file path";
1486 * Set LDAPTrustedClientCert.
1488 * This directive takes either two or three arguments:
1489 * - certificate type
1490 * - certificate file / directory / nickname
1491 * - certificate password (optional)
1493 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd, void *config, const char *type, const char *file, const char *password)
1495 util_ldap_state_t *st =
1496 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1501 apr_ldap_opt_tls_cert_t *cert;
1503 /* handle the certificate type */
1505 cert_type = util_ldap_parse_cert_type(type);
1506 if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1507 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1508 "not recognised. It should be one "
1509 "of CERT_DER, CERT_BASE64, "
1510 "CERT_NICKNAME, CERT_PFX,"
1511 "KEY_DER, KEY_BASE64, KEY_PFX", type);
1513 else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1514 APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1515 APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1516 APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1517 APR_LDAP_CERT_TYPE_PFX == cert_type ||
1518 APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1519 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1520 "only valid within a "
1521 "LDAPTrustedGlobalCert directive. "
1522 "Only CERT_DER, CERT_BASE64, "
1523 "CERT_NICKNAME, KEY_DER, and "
1524 "KEY_BASE64 may be used.", type);
1528 return "Certificate type was not specified.";
1531 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1532 "LDAP: SSL trusted client cert - %s (type %s)",
1535 /* add the certificate to the global array */
1536 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1537 cert->type = cert_type;
1539 cert->password = password;
1541 /* if file is a file or path, fix the path */
1542 if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1543 cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1545 cert->path = ap_server_root_relative(cmd->pool, file);
1547 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) != APR_SUCCESS)) {
1548 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1549 "LDAP: Could not open SSL client certificate "
1551 cert->path == NULL ? file : cert->path);
1552 return "Invalid client certificate file path";
1562 * Set LDAPTrustedMode.
1564 * This directive sets what encryption mode to use on a connection:
1565 * - None (No encryption)
1566 * - SSL (SSL encryption)
1567 * - STARTTLS (TLS encryption)
1569 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy, const char *mode)
1571 util_ldap_state_t *st =
1572 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1575 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1576 "LDAP: SSL trusted mode - %s",
1579 if (0 == strcasecmp("NONE", mode)) {
1580 st->secure = APR_LDAP_NONE;
1582 else if (0 == strcasecmp("SSL", mode)) {
1583 st->secure = APR_LDAP_SSL;
1585 else if (0 == strcasecmp("TLS", mode) || 0 == strcasecmp("STARTTLS", mode)) {
1586 st->secure = APR_LDAP_STARTTLS;
1589 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1590 "SSL, or TLS/STARTTLS";
1597 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd, void *dummy, const char *ttl)
1599 util_ldap_state_t *st =
1600 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1602 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1608 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1609 st->connectionTimeout = atol(ttl);
1611 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1612 "[%d] ldap connection: Setting connection timeout to %ld seconds.",
1613 getpid(), st->connectionTimeout);
1615 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1616 "LDAP: Connection timout option not supported by the LDAP SDK in use." );
1623 void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1625 util_ldap_state_t *st =
1626 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1630 st->cache_bytes = 100000;
1631 st->search_cache_ttl = 600000000;
1632 st->search_cache_size = 1024;
1633 st->compare_cache_ttl = 600000000;
1634 st->compare_cache_size = 1024;
1635 st->connections = NULL;
1636 st->ssl_supported = 0;
1637 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1638 st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1639 st->secure = APR_LDAP_NONE;
1641 st->connectionTimeout = 10;
1646 static void *util_ldap_merge_config(apr_pool_t *p, void *basev, void *overridesv)
1648 util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
1649 util_ldap_state_t *base = (util_ldap_state_t *) basev;
1650 util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
1654 st->cache_bytes = base->cache_bytes;
1655 st->search_cache_ttl = base->search_cache_ttl;
1656 st->search_cache_size = base->search_cache_size;
1657 st->compare_cache_ttl = base->compare_cache_ttl;
1658 st->compare_cache_size = base->compare_cache_size;
1659 st->connections = base->connections;
1660 st->ssl_supported = base->ssl_supported;
1661 st->global_certs = apr_array_append(p, base->global_certs, overrides->global_certs);
1662 st->client_certs = apr_array_append(p, base->client_certs, overrides->client_certs);
1663 st->secure = (overrides->secure_set == 0) ? base->secure : overrides->secure;
1668 static apr_status_t util_ldap_cleanup_module(void *data)
1671 server_rec *s = data;
1672 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1673 s->module_config, &ldap_module);
1675 if (st->ssl_supported) {
1676 apr_ldap_ssl_deinit();
1683 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
1684 apr_pool_t *ptemp, server_rec *s)
1686 apr_status_t result;
1687 char buf[MAX_STRING_LEN];
1688 server_rec *s_vhost;
1689 util_ldap_state_t *st_vhost;
1691 util_ldap_state_t *st =
1692 (util_ldap_state_t *)ap_get_module_config(s->module_config, &ldap_module);
1695 const char *userdata_key = "util_ldap_init";
1696 apr_ldap_err_t *result_err = NULL;
1698 struct timeval timeOut = {10,0}; /* 10 second connection timeout */
1700 /* util_ldap_post_config() will be called twice. Don't bother
1701 * going through all of the initialization on the first call
1702 * because it will just be thrown away.*/
1703 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1705 apr_pool_userdata_set((const void *)1, userdata_key,
1706 apr_pool_cleanup_null, s->process->pool);
1708 #if APR_HAS_SHARED_MEMORY
1709 /* If the cache file already exists then delete it. Otherwise we are
1710 * going to run into problems creating the shared memory. */
1711 if (st->cache_file) {
1712 char *lck_file = apr_pstrcat (st->pool, st->cache_file, ".lck", NULL);
1713 apr_file_remove(st->cache_file, ptemp);
1714 apr_file_remove(lck_file, ptemp);
1720 #if APR_HAS_SHARED_MEMORY
1721 /* initializing cache if shared memory size is not zero and we already don't have shm address */
1722 if (!st->cache_shm && st->cache_bytes > 0) {
1724 result = util_ldap_cache_init(p, st);
1725 if (result != APR_SUCCESS) {
1726 apr_strerror(result, buf, sizeof(buf));
1727 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1728 "LDAP cache: error while creating a shared memory segment: %s", buf);
1732 #if APR_HAS_SHARED_MEMORY
1733 if (st->cache_file) {
1734 st->lock_file = apr_pstrcat (st->pool, st->cache_file, ".lck", NULL);
1738 st->lock_file = ap_server_root_relative(st->pool, tmpnam(NULL));
1740 result = apr_global_mutex_create(&st->util_ldap_cache_lock, st->lock_file, APR_LOCK_DEFAULT, st->pool);
1741 if (result != APR_SUCCESS) {
1745 #ifdef AP_NEED_SET_MUTEX_PERMS
1746 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1747 if (result != APR_SUCCESS) {
1748 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
1749 "LDAP cache: failed to set mutex permissions");
1754 /* merge config in all vhost */
1757 st_vhost = (util_ldap_state_t *)ap_get_module_config(s_vhost->module_config, &ldap_module);
1759 #if APR_HAS_SHARED_MEMORY
1760 st_vhost->cache_shm = st->cache_shm;
1761 st_vhost->cache_rmm = st->cache_rmm;
1762 st_vhost->cache_file = st->cache_file;
1763 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s,
1764 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp for VHOST: %s",
1765 st->cache_shm, st->cache_rmm, s_vhost->server_hostname);
1767 st_vhost->lock_file = st->lock_file;
1768 s_vhost = s_vhost->next;
1770 #if APR_HAS_SHARED_MEMORY
1773 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "LDAP cache: LDAPSharedCacheSize is zero, disabling shared memory cache");
1777 /* log the LDAP SDK used
1780 apr_ldap_err_t *result = NULL;
1781 apr_ldap_info(p, &(result));
1782 if (result != NULL) {
1783 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "%s", result->reason);
1787 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1788 util_ldap_cleanup_module);
1791 * Initialize SSL support, and log the result for the benefit of the admin.
1793 * If SSL is not supported it is not necessarily an error, as the
1794 * application may not want to use it.
1796 rc = apr_ldap_ssl_init(p,
1800 if (APR_SUCCESS == rc) {
1801 rc = apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT,
1802 (void *)st->global_certs, &(result_err));
1805 if (APR_SUCCESS == rc) {
1806 st->ssl_supported = 1;
1807 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1808 "LDAP: SSL support available" );
1811 st->ssl_supported = 0;
1812 if (NULL != result_err) {
1813 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "%s", result_err->reason);
1815 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1816 "LDAP: SSL support unavailable" );
1819 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1820 if (st->connectionTimeout > 0) {
1821 timeOut.tv_sec = st->connectionTimeout;
1824 if (st->connectionTimeout >= 0) {
1825 rc = apr_ldap_set_option(p, NULL, LDAP_OPT_NETWORK_TIMEOUT,
1826 (void *)&timeOut, &(result_err));
1827 if (APR_SUCCESS != rc) {
1828 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1829 "LDAP: Could not set the connection timeout" );
1838 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
1841 util_ldap_state_t *st = ap_get_module_config(s->module_config, &ldap_module);
1843 if (!st->util_ldap_cache_lock) return;
1845 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock, st->lock_file, p);
1846 if (sts != APR_SUCCESS) {
1847 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
1848 "Failed to initialise global mutex %s in child process %"
1851 st->lock_file, getpid());
1855 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, s,
1856 "Initialisation of global mutex %s in child process %"
1859 st->lock_file, getpid());
1863 command_rec util_ldap_cmds[] = {
1864 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, NULL, RSRC_CONF,
1865 "Sets the size of the shared memory cache in bytes. "
1866 "Zero means disable the shared memory cache. Defaults to 100KB."),
1868 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file, NULL, RSRC_CONF,
1869 "Sets the file of the shared memory cache."
1870 "Nothing means disable the shared memory cache."),
1872 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, NULL, RSRC_CONF,
1873 "Sets the maximum number of entries that are possible in the LDAP "
1875 "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
1877 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, NULL, RSRC_CONF,
1878 "Sets the maximum time (in seconds) that an item can be cached in the LDAP "
1879 "search cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
1881 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, NULL, RSRC_CONF,
1882 "Sets the maximum number of entries that are possible in the LDAP "
1884 "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
1886 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, NULL, RSRC_CONF,
1887 "Sets the maximum time (in seconds) that an item is cached in the LDAP "
1888 "operation cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
1890 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert, NULL, RSRC_CONF,
1891 "Sets the file and/or directory containing the trusted "
1892 "certificate authority certificates, and global client "
1893 "certificates (Netware). Used to validate the LDAP server "
1894 "certificate for SSL/TLS connections. "
1895 "The following types are supported: "
1896 " CA_DER - Authority file in binary DER format "
1897 " CA_BASE64 - Authority file in Base64 format "
1898 " CA_CERT7_DB - Netscape certificate database file/directory "
1899 " CA_SECMOD - Netscape secmod file/directory "
1900 " CERT_DER - Client cert file in DER format "
1901 " CERT_BASE64 - Client cert file in Base64 format "
1902 " CERT_KEY3_DB - Netscape client cert database file/directory "
1903 " CERT_NICKNAME - Netscape client cert nickname "
1904 " KEY_DER - Client cert key file in DER format "
1905 " KEY_BASE64 - Client cert key file in Base64 format "),
1907 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert, NULL, OR_ALL,
1908 "Specifies a file containing a client certificate or private "
1909 "key, or the ID of the certificate to usethe type of the Certificate Authority file. "
1910 "The following types are supported: "
1911 " CA_DER - Authority file in binary DER format "
1912 " CA_BASE64 - Authority file in Base64 format "
1913 " CA_CERT7_DB - Netscape certificate database file/directory "
1914 " CA_SECMOD - Netscape secmod file/directory "
1915 " CERT_DER - Client cert file in DER format "
1916 " CERT_BASE64 - Client cert file in Base64 format "
1917 " CERT_KEY3_DB - Netscape client cert database file/directory "
1918 " CERT_NICKNAME - Netscape client cert nickname "
1919 " KEY_DER - Client cert key file in DER format "
1920 " KEY_BASE64 - Client cert key file in Base64 format "),
1922 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode, NULL, OR_ALL,
1923 "Specifies the type of security that should be applied to "
1924 "an LDAP connection. The types supported are: "
1925 " NONE - no encryption enabled "
1926 " SSL - SSL encryption enabled (forced by ldaps://) "
1927 " STARTTLS - STARTTLS MUST be enabled "),
1929 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout, NULL, RSRC_CONF,
1930 "Specifies the LDAP socket connection timeout in seconds. "
1931 "Default is 10 seconds. "),
1936 static void util_ldap_register_hooks(apr_pool_t *p)
1938 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
1939 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
1940 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1943 module ldap_module = {
1944 STANDARD20_MODULE_STUFF,
1945 NULL, /* dir config creater */
1946 NULL, /* dir merger --- default is to override */
1947 util_ldap_create_config, /* server config */
1948 util_ldap_merge_config, /* merge server config */
1949 util_ldap_cmds, /* command table */
1950 util_ldap_register_hooks, /* set up request processing hooks */