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 <apr_strings.h>
27 #include "ap_config.h"
29 #include "http_config.h"
30 #include "http_core.h"
32 #include "http_protocol.h"
33 #include "http_request.h"
34 #include "util_ldap.h"
35 #include "util_ldap_cache.h"
42 #error mod_ldap requires APR-util to have LDAP support built in
45 #ifdef AP_NEED_SET_MUTEX_PERMS
49 /* defines for certificate file types
51 #define LDAP_CA_TYPE_UNKNOWN 0
52 #define LDAP_CA_TYPE_DER 1
53 #define LDAP_CA_TYPE_BASE64 2
54 #define LDAP_CA_TYPE_CERT7_DB 3
57 module AP_MODULE_DECLARE_DATA ldap_module;
59 int util_ldap_handler(request_rec *r);
60 void *util_ldap_create_config(apr_pool_t *p, server_rec *s);
64 * Some definitions to help between various versions of apache.
67 #ifndef DOCTYPE_HTML_2_0
68 #define DOCTYPE_HTML_2_0 "<!DOCTYPE HTML PUBLIC \"-//IETF//" \
69 "DTD HTML 2.0//EN\">\n"
72 #ifndef DOCTYPE_HTML_3_2
73 #define DOCTYPE_HTML_3_2 "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
74 "DTD HTML 3.2 Final//EN\">\n"
77 #ifndef DOCTYPE_HTML_4_0S
78 #define DOCTYPE_HTML_4_0S "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
79 "DTD HTML 4.0//EN\"\n" \
80 "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
83 #ifndef DOCTYPE_HTML_4_0T
84 #define DOCTYPE_HTML_4_0T "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
85 "DTD HTML 4.0 Transitional//EN\"\n" \
86 "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
89 #ifndef DOCTYPE_HTML_4_0F
90 #define DOCTYPE_HTML_4_0F "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
91 "DTD HTML 4.0 Frameset//EN\"\n" \
92 "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n"
95 #define LDAP_CACHE_LOCK() \
96 if (st->util_ldap_cache_lock) \
97 apr_global_mutex_lock(st->util_ldap_cache_lock)
98 #define LDAP_CACHE_UNLOCK() \
99 if (st->util_ldap_cache_lock) \
100 apr_global_mutex_unlock(st->util_ldap_cache_lock)
103 static void util_ldap_strdup (char **str, const char *newstr)
111 *str = calloc(1, strlen(newstr)+1);
112 strcpy (*str, newstr);
120 * This handler generates a status page about the current performance of
121 * the LDAP cache. It is enabled as follows:
123 * <Location /ldap-status>
124 * SetHandler ldap-status
128 int util_ldap_handler(request_rec *r)
130 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config, &ldap_module);
132 r->allowed |= (1 << M_GET);
133 if (r->method_number != M_GET)
136 if (strcmp(r->handler, "ldap-status")) {
140 r->content_type = "text/html";
144 ap_rputs(DOCTYPE_HTML_3_2
145 "<html><head><title>LDAP Cache Information</title></head>\n", r);
146 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information</h1>\n", r);
148 util_ald_cache_display(r, st);
153 /* ------------------------------------------------------------------ */
157 * Closes an LDAP connection by unlocking it. The next time
158 * util_ldap_connection_find() is called this connection will be
159 * available for reuse.
161 LDAP_DECLARE(void) util_ldap_connection_close(util_ldap_connection_t *ldc)
167 * Is it safe leaving bound connections floating around between the
168 * different modules? Keeping the user bound is a performance boost,
169 * but it is also a potential security problem - maybe.
171 * For now we unbind the user when we finish with a connection, but
172 * we don't have to...
175 /* mark our connection as available for reuse */
178 apr_thread_mutex_unlock(ldc->lock);
184 * Destroys an LDAP connection by unbinding and closing the connection to
185 * the LDAP server. It is used to bring the connection back to a known
186 * state after an error, and during pool cleanup.
188 LDAP_DECLARE_NONSTD(apr_status_t) util_ldap_connection_unbind(void *param)
190 util_ldap_connection_t *ldc = param;
194 ldap_unbind_s(ldc->ldap);
205 * Clean up an LDAP connection by unbinding and unlocking the connection.
206 * This function is registered with the pool cleanup function - causing
207 * the LDAP connections to be shut down cleanly on graceful restart.
209 LDAP_DECLARE_NONSTD(apr_status_t) util_ldap_connection_cleanup(void *param)
211 util_ldap_connection_t *ldc = param;
215 /* unbind and disconnect from the LDAP server */
216 util_ldap_connection_unbind(ldc);
218 /* free the username and password */
220 free((void*)ldc->bindpw);
223 free((void*)ldc->binddn);
226 /* unlock this entry */
227 util_ldap_connection_close(ldc);
236 * Connect to the LDAP server and binds. Does not connect if already
237 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
239 * Returns LDAP_SUCCESS on success; and an error code on failure
241 LDAP_DECLARE(int) util_ldap_connection_open(request_rec *r,
242 util_ldap_connection_t *ldc)
246 int version = LDAP_VERSION3;
248 /* If the connection is already bound, return
252 ldc->reason = "LDAP: connection open successful (already bound)";
256 /* create the ldap session handle
258 if (NULL == ldc->ldap)
260 apr_ldap_err_t *result = NULL;
262 /* Since the host will include a port if the default port is not used,
263 always specify the default ports for the port parameter. This will allow
264 a host string that contains multiple hosts the ability to mix some
265 hosts with ports and some without. All hosts which do not specify
266 a port will use the default port.*/
267 apr_ldap_init(r->pool, &(ldc->ldap),
268 ldc->host, ldc->secure?LDAPS_PORT:LDAP_PORT,
269 ldc->secure, &(result));
271 if (result != NULL) {
272 ldc->reason = result->reason;
275 if (NULL == ldc->ldap)
278 if (NULL == ldc->reason)
279 ldc->reason = "LDAP: ldap initialization failed";
283 /* Set the alias dereferencing option */
284 ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
286 /* always default to LDAP V3 */
287 ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
292 /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
293 * returned. Break out of the loop on Success or any other error.
295 * NOTE: Looping is probably not a great idea. If the server isn't
296 * responding the chances it will respond after a few tries are poor.
297 * However, the original code looped and it only happens on
298 * the error condition.
300 for (failures=0; failures<10; failures++)
302 result = ldap_simple_bind_s(ldc->ldap,
304 (char *)ldc->bindpw);
305 if (LDAP_SERVER_DOWN != result)
309 /* free the handle if there was an error
311 if (LDAP_SUCCESS != result)
313 ldap_unbind_s(ldc->ldap);
316 ldc->reason = "LDAP: ldap_simple_bind_s() failed";
320 ldc->reason = "LDAP: connection open successful";
328 * Find an existing ldap connection struct that matches the
329 * provided ldap connection parameters.
331 * If not found in the cache, a new ldc structure will be allocated from st->pool
332 * and returned to the caller. If found in the cache, a pointer to the existing
333 * ldc structure will be returned.
335 LDAP_DECLARE(util_ldap_connection_t *)util_ldap_connection_find(request_rec *r, const char *host, int port,
336 const char *binddn, const char *bindpw, deref_options deref,
339 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
341 util_ldap_state_t *st =
342 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
347 /* mutex lock this function */
349 apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
351 apr_thread_mutex_lock(st->mutex);
354 /* Search for an exact connection match in the list that is not
357 for (l=st->connections,p=NULL; l; l=l->next) {
359 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
361 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
362 ((!l->binddn && !binddn) || (l->binddn && binddn && !strcmp(l->binddn, binddn))) &&
363 ((!l->bindpw && !bindpw) || (l->bindpw && bindpw && !strcmp(l->bindpw, bindpw))) &&
364 (l->deref == deref) && (l->secure == secure)) {
369 /* If this connection didn't match the criteria, then we
370 * need to unlock the mutex so it is available to be reused.
372 apr_thread_mutex_unlock(l->lock);
378 /* If nothing found, search again, but we don't care about the
379 * binddn and bindpw this time.
382 for (l=st->connections,p=NULL; l; l=l->next) {
384 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
387 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
388 (l->deref == deref) && (l->secure == secure)) {
390 /* the bind credentials have changed */
392 util_ldap_strdup((char**)&(l->binddn), binddn);
393 util_ldap_strdup((char**)&(l->bindpw), bindpw);
397 /* If this connection didn't match the criteria, then we
398 * need to unlock the mutex so it is available to be reused.
400 apr_thread_mutex_unlock(l->lock);
407 /* artificially disable cache */
410 /* If no connection what found after the second search, we
416 * Add the new connection entry to the linked list. Note that we
417 * don't actually establish an LDAP connection yet; that happens
418 * the first time authentication is requested.
420 /* create the details to the pool in st */
421 l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
423 apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
424 apr_thread_mutex_lock(l->lock);
428 l->host = apr_pstrdup(st->pool, host);
431 util_ldap_strdup((char**)&(l->binddn), binddn);
432 util_ldap_strdup((char**)&(l->bindpw), bindpw);
435 /* add the cleanup to the pool */
436 apr_pool_cleanup_register(l->pool, l,
437 util_ldap_connection_cleanup,
438 apr_pool_cleanup_null);
449 apr_thread_mutex_unlock(st->mutex);
454 /* ------------------------------------------------------------------ */
457 * Compares two DNs to see if they're equal. The only way to do this correctly is to
458 * search for the dn and then do ldap_get_dn() on the result. This should match the
459 * initial dn, since it would have been also retrieved with ldap_get_dn(). This is
460 * expensive, so if the configuration value compare_dn_on_server is
461 * false, just does an ordinary strcmp.
463 * The lock for the ldap cache should already be acquired.
465 LDAP_DECLARE(int) util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
466 const char *url, const char *dn, const char *reqdn,
467 int compare_dn_on_server)
470 util_url_node_t *curl;
471 util_url_node_t curnode;
472 util_dn_compare_node_t *node;
473 util_dn_compare_node_t newnode;
475 LDAPMessage *res, *entry;
478 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(r->server->module_config, &ldap_module);
480 /* get cache entry (or create one) */
484 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
486 curl = util_ald_create_caches(st, url);
490 /* a simple compare? */
491 if (!compare_dn_on_server) {
492 /* unlock this read lock */
493 if (strcmp(dn, reqdn)) {
494 ldc->reason = "DN Comparison FALSE (direct strcmp())";
495 return LDAP_COMPARE_FALSE;
498 ldc->reason = "DN Comparison TRUE (direct strcmp())";
499 return LDAP_COMPARE_TRUE;
504 /* no - it's a server side compare */
507 /* is it in the compare cache? */
508 newnode.reqdn = (char *)reqdn;
509 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
511 /* If it's in the cache, it's good */
512 /* unlock this read lock */
514 ldc->reason = "DN Comparison TRUE (cached)";
515 return LDAP_COMPARE_TRUE;
518 /* unlock this read lock */
523 if (failures++ > 10) {
524 /* too many failures */
528 /* make a server connection */
529 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
530 /* connect to server failed */
534 /* search for reqdn */
535 if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
536 "(objectclass=*)", NULL, 1,
537 NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
538 ldc->reason = "DN Comparison ldap_search_ext_s() failed with server down";
539 util_ldap_connection_unbind(ldc);
542 if (result != LDAP_SUCCESS) {
543 /* search for reqdn failed - no match */
544 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
548 entry = ldap_first_entry(ldc->ldap, res);
549 searchdn = ldap_get_dn(ldc->ldap, entry);
552 if (strcmp(dn, searchdn) != 0) {
553 /* compare unsuccessful */
554 ldc->reason = "DN Comparison FALSE (checked on server)";
555 result = LDAP_COMPARE_FALSE;
559 /* compare successful - add to the compare cache */
561 newnode.reqdn = (char *)reqdn;
562 newnode.dn = (char *)dn;
564 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
565 if ((node == NULL) ||
566 (strcmp(reqdn, node->reqdn) != 0) || (strcmp(dn, node->dn) != 0)) {
568 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
572 ldc->reason = "DN Comparison TRUE (checked on server)";
573 result = LDAP_COMPARE_TRUE;
575 ldap_memfree(searchdn);
581 * Does an generic ldap_compare operation. It accepts a cache that it will use
582 * to lookup the compare in the cache. We cache two kinds of compares
583 * (require group compares) and (require user compares). Each compare has a different
584 * cache node: require group includes the DN; require user does not because the
585 * require user cache is owned by the
588 LDAP_DECLARE(int) util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
589 const char *url, const char *dn,
590 const char *attrib, const char *value)
593 util_url_node_t *curl;
594 util_url_node_t curnode;
595 util_compare_node_t *compare_nodep;
596 util_compare_node_t the_compare_node;
597 apr_time_t curtime = 0; /* silence gcc -Wall */
600 util_ldap_state_t *st =
601 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
604 /* 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);
614 /* make a comparison to the cache */
616 curtime = apr_time_now();
618 the_compare_node.dn = (char *)dn;
619 the_compare_node.attrib = (char *)attrib;
620 the_compare_node.value = (char *)value;
621 the_compare_node.result = 0;
623 compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
625 if (compare_nodep != NULL) {
627 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
628 /* ...but it is too old */
629 util_ald_cache_remove(curl->compare_cache, compare_nodep);
632 /* ...and it is good */
633 /* unlock this read lock */
635 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
636 ldc->reason = "Comparison true (cached)";
637 return compare_nodep->result;
639 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
640 ldc->reason = "Comparison false (cached)";
641 return compare_nodep->result;
643 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
644 ldc->reason = "Comparison no such attribute (cached)";
645 return compare_nodep->result;
648 ldc->reason = "Comparison undefined (cached)";
649 return compare_nodep->result;
653 /* unlock this read lock */
658 if (failures++ > 10) {
659 /* too many failures */
662 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
667 if ((result = ldap_compare_s(ldc->ldap,
671 == LDAP_SERVER_DOWN) {
672 /* connection failed - try again */
673 ldc->reason = "ldap_compare_s() failed with server down";
674 util_ldap_connection_unbind(ldc);
678 ldc->reason = "Comparison complete";
679 if ((LDAP_COMPARE_TRUE == result) ||
680 (LDAP_COMPARE_FALSE == result) ||
681 (LDAP_NO_SUCH_ATTRIBUTE == result)) {
683 /* compare completed; caching result */
685 the_compare_node.lastcompare = curtime;
686 the_compare_node.result = result;
688 /* If the node doesn't exist then insert it, otherwise just update it with
690 compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
691 if ((compare_nodep == NULL) ||
692 (strcmp(the_compare_node.dn, compare_nodep->dn) != 0) ||
693 (strcmp(the_compare_node.attrib, compare_nodep->attrib) != 0) ||
694 (strcmp(the_compare_node.value, compare_nodep->value) != 0)) {
696 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
699 compare_nodep->lastcompare = curtime;
700 compare_nodep->result = result;
704 if (LDAP_COMPARE_TRUE == result) {
705 ldc->reason = "Comparison true (adding to cache)";
706 return LDAP_COMPARE_TRUE;
708 else if (LDAP_COMPARE_FALSE == result) {
709 ldc->reason = "Comparison false (adding to cache)";
710 return LDAP_COMPARE_FALSE;
713 ldc->reason = "Comparison no such attribute (adding to cache)";
714 return LDAP_NO_SUCH_ATTRIBUTE;
720 LDAP_DECLARE(int) util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
721 const char *url, const char *basedn, int scope, char **attrs,
722 const char *filter, const char *bindpw, const char **binddn,
723 const char ***retvals)
725 const char **vals = NULL;
727 LDAPMessage *res, *entry;
731 util_url_node_t *curl; /* Cached URL node */
732 util_url_node_t curnode;
733 util_search_node_t *search_nodep; /* Cached search node */
734 util_search_node_t the_search_node;
737 util_ldap_state_t *st =
738 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
741 /* Get the cache node for this url */
744 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
746 curl = util_ald_create_caches(st, url);
752 the_search_node.username = filter;
753 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
754 if (search_nodep != NULL) {
756 /* found entry in search cache... */
757 curtime = apr_time_now();
760 * Remove this item from the cache if its expired. If the sent password
761 * doesn't match the storepassword, the entry will be removed and readded
762 * later if the credentials pass authentication.
764 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
765 /* ...but entry is too old */
766 util_ald_cache_remove(curl->search_cache, search_nodep);
768 else if ((search_nodep->bindpw) && (search_nodep->bindpw[0] != '\0') &&
769 (strcmp(search_nodep->bindpw, bindpw) == 0)) {
770 /* ...and entry is valid */
771 *binddn = search_nodep->dn;
772 *retvals = search_nodep->vals;
774 ldc->reason = "Authentication successful (cached)";
778 /* unlock this read lock */
783 * At this point, there is no valid cached search, so lets do the search.
787 * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
790 if (failures++ > 10) {
793 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
797 /* try do the search */
798 if ((result = ldap_search_ext_s(ldc->ldap,
799 (char *)basedn, scope,
800 (char *)filter, attrs, 0,
801 NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
802 ldc->reason = "ldap_search_ext_s() for user failed with server down";
803 util_ldap_connection_unbind(ldc);
807 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
808 if (result != LDAP_SUCCESS) {
809 ldc->reason = "ldap_search_ext_s() for user failed";
814 * We should have found exactly one entry; to find a different
815 * number is an error.
817 count = ldap_count_entries(ldc->ldap, res);
821 ldc->reason = "User not found";
823 ldc->reason = "User is not unique (search found two or more matches)";
825 return LDAP_NO_SUCH_OBJECT;
828 entry = ldap_first_entry(ldc->ldap, res);
830 /* Grab the dn, copy it into the pool, and free it again */
831 dn = ldap_get_dn(ldc->ldap, entry);
832 *binddn = apr_pstrdup(r->pool, dn);
836 * A bind to the server with an empty password always succeeds, so
837 * we check to ensure that the password is not empty. This implies
838 * that users who actually do have empty passwords will never be
839 * able to authenticate with this module. I don't see this as a big
842 if (!bindpw || strlen(bindpw) <= 0) {
844 ldc->reason = "Empty password not allowed";
845 return LDAP_INVALID_CREDENTIALS;
849 * Attempt to bind with the retrieved dn and the password. If the bind
850 * fails, it means that the password is wrong (the dn obviously
851 * exists, since we just retrieved it)
853 if ((result = ldap_simple_bind_s(ldc->ldap,
855 (char *)bindpw)) == LDAP_SERVER_DOWN) {
856 ldc->reason = "ldap_simple_bind_s() to check user credentials "
857 "failed with server down";
859 util_ldap_connection_unbind(ldc);
863 /* failure? if so - return */
864 if (result != LDAP_SUCCESS) {
865 ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
867 util_ldap_connection_unbind(ldc);
872 * We have just bound the connection to a different user and password
873 * combination, which might be reused unintentionally next time this
874 * connection is used from the connection pool. To ensure no confusion,
875 * we mark the connection as unbound.
881 * Get values for the provided attributes.
887 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
893 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
894 while (values && values[j]) {
895 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
898 ldap_value_free(values);
906 * Add the new username to the search cache.
910 the_search_node.username = filter;
911 the_search_node.dn = *binddn;
912 the_search_node.bindpw = bindpw;
913 the_search_node.lastbind = apr_time_now();
914 the_search_node.vals = vals;
916 /* Search again to make sure that another thread didn't ready insert this node
917 into the cache before we got here. If it does exist then update the lastbind */
918 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
919 if ((search_nodep == NULL) ||
920 (strcmp(*binddn, search_nodep->dn) != 0)) {
922 /* Nothing in cache, insert new entry */
923 util_ald_cache_insert(curl->search_cache, &the_search_node);
925 else if ((!search_nodep->bindpw) ||
926 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
928 /* Entry in cache is invalid, remove it and insert new one */
929 util_ald_cache_remove(curl->search_cache, search_nodep);
930 util_ald_cache_insert(curl->search_cache, &the_search_node);
933 /* Cache entry is valid, update lastbind */
934 search_nodep->lastbind = the_search_node.lastbind;
940 ldc->reason = "Authentication successful";
945 * This function will return the DN of the entry matching userid.
946 * It is used to get the DN in case some other module than mod_auth_ldap
947 * has authenticated the user.
948 * The function is basically a copy of util_ldap_cache_checkuserid
949 * with password checking removed.
951 LDAP_DECLARE(int) util_ldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
952 const char *url, const char *basedn, int scope, char **attrs,
953 const char *filter, const char **binddn,
954 const char ***retvals)
956 const char **vals = NULL;
958 LDAPMessage *res, *entry;
962 util_url_node_t *curl; /* Cached URL node */
963 util_url_node_t curnode;
964 util_search_node_t *search_nodep; /* Cached search node */
965 util_search_node_t the_search_node;
968 util_ldap_state_t *st =
969 (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
972 /* Get the cache node for this url */
975 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
977 curl = util_ald_create_caches(st, url);
983 the_search_node.username = filter;
984 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
985 if (search_nodep != NULL) {
987 /* found entry in search cache... */
988 curtime = apr_time_now();
991 * Remove this item from the cache if its expired.
993 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
994 /* ...but entry is too old */
995 util_ald_cache_remove(curl->search_cache, search_nodep);
998 /* ...and entry is valid */
999 *binddn = search_nodep->dn;
1000 *retvals = search_nodep->vals;
1001 LDAP_CACHE_UNLOCK();
1002 ldc->reason = "Search successful (cached)";
1003 return LDAP_SUCCESS;
1006 /* unlock this read lock */
1007 LDAP_CACHE_UNLOCK();
1011 * At this point, there is no valid cached search, so lets do the search.
1015 * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1018 if (failures++ > 10) {
1021 if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
1025 /* try do the search */
1026 if ((result = ldap_search_ext_s(ldc->ldap,
1027 (char *)basedn, scope,
1028 (char *)filter, attrs, 0,
1030 NULL, -1, &res)) == LDAP_SERVER_DOWN) {
1031 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1035 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1036 if (result != LDAP_SUCCESS) {
1037 ldc->reason = "ldap_search_ext_s() for user failed";
1042 * We should have found exactly one entry; to find a different
1043 * number is an error.
1045 count = ldap_count_entries(ldc->ldap, res);
1049 ldc->reason = "User not found";
1051 ldc->reason = "User is not unique (search found two or more matches)";
1053 return LDAP_NO_SUCH_OBJECT;
1056 entry = ldap_first_entry(ldc->ldap, res);
1058 /* Grab the dn, copy it into the pool, and free it again */
1059 dn = ldap_get_dn(ldc->ldap, entry);
1060 *binddn = apr_pstrdup(st->pool, dn);
1064 * Get values for the provided attributes.
1070 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1076 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1077 while (values && values[j]) {
1078 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
1081 ldap_value_free(values);
1089 * Add the new username to the search cache.
1093 the_search_node.username = filter;
1094 the_search_node.dn = *binddn;
1095 the_search_node.bindpw = NULL;
1096 the_search_node.lastbind = apr_time_now();
1097 the_search_node.vals = vals;
1099 /* Search again to make sure that another thread didn't ready insert this node
1100 into the cache before we got here. If it does exist then update the lastbind */
1101 search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1102 if ((search_nodep == NULL) ||
1103 (strcmp(*binddn, search_nodep->dn) != 0)) {
1105 /* Nothing in cache, insert new entry */
1106 util_ald_cache_insert(curl->search_cache, &the_search_node);
1109 * Don't update lastbind on entries with bindpw because
1110 * we haven't verified that password. It's OK to update
1111 * the entry if there is no password in it.
1113 else if (!search_nodep->bindpw) {
1114 /* Cache entry is valid, update lastbind */
1115 search_nodep->lastbind = the_search_node.lastbind;
1117 LDAP_CACHE_UNLOCK();
1122 ldc->reason = "Search successful";
1123 return LDAP_SUCCESS;
1127 * Reports if ssl support is enabled
1129 * 1 = enabled, 0 = not enabled
1131 LDAP_DECLARE(int) util_ldap_ssl_supported(request_rec *r)
1133 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1134 r->server->module_config, &ldap_module);
1136 return(st->ssl_support);
1140 /* ---------------------------------------- */
1141 /* config directives */
1144 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes)
1146 util_ldap_state_t *st =
1147 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1150 st->cache_bytes = atol(bytes);
1152 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1153 "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1154 " cache size to %" APR_SIZE_T_FMT " bytes.",
1155 getpid(), st->cache_bytes);
1160 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy, const char *file)
1162 util_ldap_state_t *st =
1163 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1167 st->cache_file = ap_server_root_relative(st->pool, file);
1170 st->cache_file = NULL;
1173 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1174 "LDAP cache: Setting shared memory cache file to %s bytes.",
1180 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
1182 util_ldap_state_t *st =
1183 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1186 st->search_cache_ttl = atol(ttl) * 1000000;
1188 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1189 "[%d] ldap cache: Setting cache TTL to %ld microseconds.",
1190 getpid(), st->search_cache_ttl);
1195 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size)
1197 util_ldap_state_t *st =
1198 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1202 st->search_cache_size = atol(size);
1203 if (st->search_cache_size < 0) {
1204 st->search_cache_size = 0;
1207 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1208 "[%d] ldap cache: Setting search cache size to %ld entries.",
1209 getpid(), st->search_cache_size);
1214 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
1216 util_ldap_state_t *st =
1217 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1220 st->compare_cache_ttl = atol(ttl) * 1000000;
1222 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1223 "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.",
1224 getpid(), st->compare_cache_ttl);
1229 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size)
1231 util_ldap_state_t *st =
1232 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1235 st->compare_cache_size = atol(size);
1236 if (st->compare_cache_size < 0) {
1237 st->compare_cache_size = 0;
1240 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1241 "[%d] ldap cache: Setting operation cache size to %ld entries.",
1242 getpid(), st->compare_cache_size);
1247 static const char *util_ldap_set_cert_auth(cmd_parms *cmd, void *dummy, const char *file)
1249 util_ldap_state_t *st =
1250 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1252 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1260 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1261 "LDAP: SSL trusted certificate authority file - %s",
1264 st->cert_auth_file = ap_server_root_relative(cmd->pool, file);
1266 if (st->cert_auth_file &&
1267 ((rv = apr_stat (&finfo, st->cert_auth_file, APR_FINFO_MIN, cmd->pool)) != APR_SUCCESS))
1269 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1270 "LDAP: Could not open SSL trusted certificate authority file - %s",
1271 st->cert_auth_file == NULL ? file : st->cert_auth_file);
1272 return "Invalid file path";
1279 static const char *util_ldap_set_cert_type(cmd_parms *cmd, void *dummy, const char *Type)
1281 util_ldap_state_t *st =
1282 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1284 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1289 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1290 "LDAP: SSL trusted certificate authority file type - %s",
1293 if (0 == strcmp("DER_FILE", Type))
1294 st->cert_file_type = LDAP_CA_TYPE_DER;
1296 else if (0 == strcmp("BASE64_FILE", Type))
1297 st->cert_file_type = LDAP_CA_TYPE_BASE64;
1299 else if (0 == strcmp("CERT7_DB_PATH", Type))
1300 st->cert_file_type = LDAP_CA_TYPE_CERT7_DB;
1303 st->cert_file_type = LDAP_CA_TYPE_UNKNOWN;
1309 void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1311 util_ldap_state_t *st =
1312 (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1316 st->cache_bytes = 100000;
1317 st->search_cache_ttl = 600000000;
1318 st->search_cache_size = 1024;
1319 st->compare_cache_ttl = 600000000;
1320 st->compare_cache_size = 1024;
1321 st->connections = NULL;
1322 st->cert_auth_file = NULL;
1323 st->cert_file_type = LDAP_CA_TYPE_UNKNOWN;
1324 st->ssl_support = 0;
1329 static apr_status_t util_ldap_cleanup_module(void *data)
1332 server_rec *s = data;
1333 util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1334 s->module_config, &ldap_module);
1336 if (st->ssl_support) {
1337 apr_ldap_ssl_deinit();
1344 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
1345 apr_pool_t *ptemp, server_rec *s)
1347 apr_status_t result;
1348 char buf[MAX_STRING_LEN];
1349 server_rec *s_vhost;
1350 util_ldap_state_t *st_vhost;
1352 util_ldap_state_t *st =
1353 (util_ldap_state_t *)ap_get_module_config(s->module_config, &ldap_module);
1356 const char *userdata_key = "util_ldap_init";
1358 /* util_ldap_post_config() will be called twice. Don't bother
1359 * going through all of the initialization on the first call
1360 * because it will just be thrown away.*/
1361 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1363 apr_pool_userdata_set((const void *)1, userdata_key,
1364 apr_pool_cleanup_null, s->process->pool);
1366 #if APR_HAS_SHARED_MEMORY
1367 /* If the cache file already exists then delete it. Otherwise we are
1368 * going to run into problems creating the shared memory. */
1369 if (st->cache_file) {
1370 char *lck_file = apr_pstrcat (st->pool, st->cache_file, ".lck", NULL);
1371 apr_file_remove(st->cache_file, ptemp);
1372 apr_file_remove(lck_file, ptemp);
1378 #if APR_HAS_SHARED_MEMORY
1379 /* initializing cache if shared memory size is not zero and we already don't have shm address */
1380 if (!st->cache_shm && st->cache_bytes > 0) {
1382 result = util_ldap_cache_init(p, st);
1383 if (result != APR_SUCCESS) {
1384 apr_strerror(result, buf, sizeof(buf));
1385 ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1386 "LDAP cache: error while creating a shared memory segment: %s", buf);
1390 #if APR_HAS_SHARED_MEMORY
1391 if (st->cache_file) {
1392 st->lock_file = apr_pstrcat (st->pool, st->cache_file, ".lck", NULL);
1396 st->lock_file = ap_server_root_relative(st->pool, tmpnam(NULL));
1398 result = apr_global_mutex_create(&st->util_ldap_cache_lock, st->lock_file, APR_LOCK_DEFAULT, st->pool);
1399 if (result != APR_SUCCESS) {
1403 #ifdef AP_NEED_SET_MUTEX_PERMS
1404 result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1405 if (result != APR_SUCCESS) {
1406 ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
1407 "LDAP cache: failed to set mutex permissions");
1412 /* merge config in all vhost */
1415 st_vhost = (util_ldap_state_t *)ap_get_module_config(s_vhost->module_config, &ldap_module);
1417 #if APR_HAS_SHARED_MEMORY
1418 st_vhost->cache_shm = st->cache_shm;
1419 st_vhost->cache_rmm = st->cache_rmm;
1420 st_vhost->cache_file = st->cache_file;
1421 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s,
1422 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp for VHOST: %s",
1423 st->cache_shm, st->cache_rmm, s_vhost->server_hostname);
1425 st_vhost->lock_file = st->lock_file;
1426 s_vhost = s_vhost->next;
1428 #if APR_HAS_SHARED_MEMORY
1431 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "LDAP cache: LDAPSharedCacheSize is zero, disabling shared memory cache");
1435 /* log the LDAP SDK used
1438 apr_ldap_err_t *result = NULL;
1439 apr_ldap_info(p, &(result));
1440 if (result != NULL) {
1441 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "%s", result->reason);
1445 apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1446 util_ldap_cleanup_module);
1448 /* initialize SSL support if requested
1450 if (st->cert_auth_file) {
1452 apr_ldap_err_t *result = NULL;
1453 int rc = apr_ldap_ssl_init(p,
1458 if (LDAP_SUCCESS == rc) {
1459 st->ssl_support = 1;
1461 else if (NULL != result) {
1462 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "%s", result->reason);
1463 st->ssl_support = 0;
1468 /* log SSL status - If SSL isn't available it isn't necessarily
1469 * an error because the modules asking for LDAP connections
1470 * may not ask for SSL support
1472 if (st->ssl_support) {
1473 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1474 "LDAP: SSL support available" );
1477 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1478 "LDAP: SSL support unavailable" );
1484 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
1487 util_ldap_state_t *st = ap_get_module_config(s->module_config, &ldap_module);
1489 if (!st->util_ldap_cache_lock) return;
1491 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock, st->lock_file, p);
1492 if (sts != APR_SUCCESS) {
1493 ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
1494 "Failed to initialise global mutex %s in child process %"
1497 st->lock_file, getpid());
1501 ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, s,
1502 "Initialisation of global mutex %s in child process %"
1505 st->lock_file, getpid());
1509 command_rec util_ldap_cmds[] = {
1510 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, NULL, RSRC_CONF,
1511 "Sets the size of the shared memory cache in bytes. "
1512 "Zero means disable the shared memory cache. Defaults to 100KB."),
1514 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file, NULL, RSRC_CONF,
1515 "Sets the file of the shared memory cache."
1516 "Nothing means disable the shared memory cache."),
1518 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, NULL, RSRC_CONF,
1519 "Sets the maximum number of entries that are possible in the LDAP "
1521 "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
1523 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, NULL, RSRC_CONF,
1524 "Sets the maximum time (in seconds) that an item can be cached in the LDAP "
1525 "search cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
1527 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, NULL, RSRC_CONF,
1528 "Sets the maximum number of entries that are possible in the LDAP "
1530 "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
1532 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, NULL, RSRC_CONF,
1533 "Sets the maximum time (in seconds) that an item is cached in the LDAP "
1534 "operation cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
1536 AP_INIT_TAKE1("LDAPTrustedCA", util_ldap_set_cert_auth, NULL, RSRC_CONF,
1537 "Sets the file containing the trusted Certificate Authority certificate. "
1538 "Used to validate the LDAP server certificate for SSL connections."),
1540 AP_INIT_TAKE1("LDAPTrustedCAType", util_ldap_set_cert_type, NULL, RSRC_CONF,
1541 "Specifies the type of the Certificate Authority file. "
1542 "The following types are supported: "
1543 " DER_FILE - file in binary DER format "
1544 " BASE64_FILE - file in Base64 format "
1545 " CERT7_DB_PATH - Netscape certificate database file "),
1549 static void util_ldap_register_hooks(apr_pool_t *p)
1551 ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
1552 ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
1553 ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1556 module ldap_module = {
1557 STANDARD20_MODULE_STUFF,
1558 NULL, /* dir config creater */
1559 NULL, /* dir merger --- default is to override */
1560 util_ldap_create_config, /* server config */
1561 NULL, /* merge server config */
1562 util_ldap_cmds, /* command table */
1563 util_ldap_register_hooks, /* set up request processing hooks */