Apache HTTPD
util_ldap.c
Go to the documentation of this file.
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17/*
18 * util_ldap.c: LDAP things
19 *
20 * Original code from auth_ldap module for Apache v1.3:
21 * Copyright 1998, 1999 Enbridge Pipelines Inc.
22 * Copyright 1999-2001 Dave Carrigan
23 */
24
25#include "httpd.h"
26#include "http_config.h"
27#include "http_core.h"
28#include "http_log.h"
29#include "http_protocol.h"
30#include "http_request.h"
31#include "util_mutex.h"
32#include "util_ldap.h"
33#include "util_ldap_cache.h"
34
35#include <apr_strings.h>
36
37#if APR_HAVE_UNISTD_H
38#include <unistd.h>
39#endif
40
41#if !APR_HAS_LDAP
42#error mod_ldap requires APR-util to have LDAP support built in
43#endif
44
45/* Default define for ldap functions that need a SIZELIMIT but
46 * do not have the define
47 * XXX This should be removed once a supporting #define is
48 * released through APR-Util.
49 */
50#ifndef APR_LDAP_SIZELIMIT
51#define APR_LDAP_SIZELIMIT -1
52#endif
53
54#ifdef LDAP_OPT_DEBUG_LEVEL
55#define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG_LEVEL
56#else
57#ifdef LDAP_OPT_DEBUG
58#define AP_LDAP_OPT_DEBUG LDAP_OPT_DEBUG
59#endif
60#endif
61
62#define AP_LDAP_HOPLIMIT_UNSET -1
63#define AP_LDAP_CHASEREFERRALS_SDKDEFAULT -1
64#define AP_LDAP_CHASEREFERRALS_OFF 0
65#define AP_LDAP_CHASEREFERRALS_ON 1
66
67#define AP_LDAP_CONNPOOL_DEFAULT -1
68#define AP_LDAP_CONNPOOL_INFINITE -2
69
70#if !defined(LDAP_OPT_NETWORK_TIMEOUT) && defined(LDAP_OPT_CONNECT_TIMEOUT)
71#define LDAP_OPT_NETWORK_TIMEOUT LDAP_OPT_CONNECT_TIMEOUT
72#endif
73
74module AP_MODULE_DECLARE_DATA ldap_module;
75static const char *ldap_cache_mutex_type = "ldap-cache";
77
78/* For OpenLDAP with the 3-arg version of ldap_set_rebind_proc(), use
79 * a simpler rebind callback than the implementation in APR-util.
80 * Testing for API version >= 3001 appears safe although OpenLDAP
81 * 2.1.x (API version = 2004) also has the 3-arg API. */
82#if APR_HAS_OPENLDAP_LDAPSDK && defined(LDAP_API_VERSION) && LDAP_API_VERSION >= 3001
83
84#define uldap_rebind_init(p) APR_SUCCESS /* noop */
85
86static int uldap_rebind_proc(LDAP *ld, const char *url, ber_tag_t request,
87 ber_int_t msgid, void *params)
88{
90
91 return ldap_bind_s(ld, ldc->binddn, ldc->bindpw, LDAP_AUTH_SIMPLE);
92}
93
95{
97 return APR_SUCCESS;
98}
99
100#else /* !APR_HAS_OPENLDAP_LDAPSDK */
101
102#define USE_APR_LDAP_REBIND
103#include <apr_ldap_rebind.h>
104
105#define uldap_rebind_init(p) apr_ldap_rebind_init(p)
106#define uldap_rebind_add(ldc) apr_ldap_rebind_add((ldc)->rebind_pool, \
107 (ldc)->ldap, (ldc)->binddn, \
108 (ldc)->bindpw)
109#endif
110
113 if (st->util_ldap_cache_lock) {
114 apr_status_t rv = apr_global_mutex_lock(st->util_ldap_cache_lock);
115 if (rv != APR_SUCCESS) {
116 ap_log_rerror(APLOG_MARK, APLOG_CRIT, rv, r, APLOGNO(10134) "LDAP cache lock failed");
117 ap_assert(0);
118 }
119 }
120 return rv;
121}
124 if (st->util_ldap_cache_lock) {
125 apr_status_t rv = apr_global_mutex_unlock(st->util_ldap_cache_lock);
126 if (rv != APR_SUCCESS) {
127 ap_log_rerror(APLOG_MARK, APLOG_CRIT, rv, r, APLOGNO(10135) "LDAP cache lock failed");
128 ap_assert(0);
129 }
130 }
131 return rv;
132}
133
134static void util_ldap_strdup (char **str, const char *newstr)
135{
136 if (*str) {
137 free(*str);
138 *str = NULL;
139 }
140
141 if (newstr) {
142 *str = strdup(newstr);
143 }
144}
145
146/*
147 * Status Handler
148 * --------------
149 *
150 * This handler generates a status page about the current performance of
151 * the LDAP cache. It is enabled as follows:
152 *
153 * <Location /ldap-status>
154 * SetHandler ldap-status
155 * </Location>
156 *
157 */
159{
161
162 r->allowed |= (1 << M_GET);
163 if (r->method_number != M_GET) {
164 return DECLINED;
165 }
166
167 if (strcmp(r->handler, "ldap-status")) {
168 return DECLINED;
169 }
170
172 &ldap_module);
173
174 ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
175
176 if (r->header_only)
177 return OK;
178
180 "<html><head><title>LDAP Cache Information</title></head>\n", r);
181 ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
182 "</h1>\n", r);
183
185
186 return OK;
187}
188
189
190
191/* ------------------------------------------------------------------ */
192/*
193 * Closes an LDAP connection by unlocking it. The next time
194 * uldap_connection_find() is called this connection will be
195 * available for reuse.
196 */
198{
199
200 /* We leave bound LDAP connections floating around in our pool,
201 * but always check/fix the binddn/bindpw when we take them out
202 * of the pool
203 */
204 if (!ldc->keep) {
206 ldc->r = NULL;
207 }
208 else {
209 /* mark our connection as available for reuse */
210 ldc->freed = apr_time_now();
211 ldc->r = NULL;
212 }
213
214#if APR_HAS_THREADS
216#endif
217}
218
219
220/*
221 * Destroys an LDAP connection by unbinding and closing the connection to
222 * the LDAP server. It is used to bring the connection back to a known
223 * state after an error.
224 */
226{
228
229 if (ldc) {
230#ifdef USE_APR_LDAP_REBIND
231 /* forget the rebind info for this conn */
232 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
233 apr_pool_clear(ldc->rebind_pool);
234 }
235#endif
236
237 if (ldc->ldap) {
238 if (ldc->r) {
239 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp unbind", ldc);
240 }
241 ldap_unbind_s(ldc->ldap);
242 ldc->ldap = NULL;
243 }
244 ldc->bound = 0;
245 }
246
247 return APR_SUCCESS;
248}
249
250/* not presently used, not part of the API */
251#if 0
252/*
253 * util_ldap_connection_remove frees all storage associated with the LDAP
254 * connection and removes it completely from the per-virtualhost list of
255 * connections
256 *
257 * The caller should hold the lock for this connection
258 */
260{
261 util_ldap_connection_t *ldc = param, *l = NULL, *prev = NULL;
263
264 if (!ldc) return APR_SUCCESS;
265
266 st = ldc->st;
267
269
270#if APR_HAS_THREADS
272#endif
273
274 /* Remove ldc from the list */
275 for (l=st->connections; l; l=l->next) {
276 if (l == ldc) {
277 if (prev) {
278 prev->next = l->next;
279 }
280 else {
281 st->connections = l->next;
282 }
283 break;
284 }
285 prev = l;
286 }
287
288 if (ldc->bindpw) {
289 free((void*)ldc->bindpw);
290 }
291 if (ldc->binddn) {
292 free((void*)ldc->binddn);
293 }
294
295#if APR_HAS_THREADS
298#endif
299
300 /* Destroy the pool associated with this connection */
301
302 apr_pool_destroy(ldc->pool);
303
304 return APR_SUCCESS;
305}
306#endif
307
310{
311 int rc = 0, ldap_option = 0;
312 int version = LDAP_VERSION3;
314#ifdef LDAP_OPT_NETWORK_TIMEOUT
315 struct timeval connectionTimeout = {0};
316#endif
319 &ldap_module);
320 int have_client_certs = !apr_is_empty_array(ldc->client_certs);
321#if !APR_HAS_SOLARIS_LDAPSDK
322 /*
323 * Normally we enable SSL/TLS with apr_ldap_set_option(), except
324 * with Solaris LDAP, where this is broken.
325 */
326 int secure = APR_LDAP_NONE;
327#else
328 /*
329 * With Solaris LDAP, we enable TSL via the secure argument
330 * to apr_ldap_init(). This requires a fix from apr-util >= 1.4.0.
331 *
332 * Just in case client certificates ever get supported, we
333 * handle those as with the other LDAP SDKs.
334 */
335 int secure = have_client_certs ? APR_LDAP_NONE : ldc->secure;
336#endif
337
338 /* Since the host will include a port if the default port is not used,
339 * always specify the default ports for the port parameter. This will
340 * allow a host string that contains multiple hosts the ability to mix
341 * some hosts with ports and some without. All hosts which do not
342 * specify a port will use the default port.
343 */
344 apr_ldap_init(r->pool, &(ldc->ldap),
345 ldc->host,
346 APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
347 secure, &(result));
348
349 if (NULL == result) {
350 /* something really bad happened */
351 ldc->bound = 0;
352 if (NULL == ldc->reason) {
353 ldc->reason = "LDAP: ldap initialization failed";
354 }
355 return(APR_EGENERAL);
356 }
357
358 if (result->rc) {
359 ldc->reason = result->reason;
360 ldc->bound = 0;
361 return result->rc;
362 }
363
364 if (NULL == ldc->ldap)
365 {
366 ldc->bound = 0;
367 if (NULL == ldc->reason) {
368 ldc->reason = "LDAP: ldap initialization failed";
369 }
370 else {
371 ldc->reason = result->reason;
372 }
373 return(result->rc);
374 }
375
376 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp init", ldc);
377
378 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
379 /* Now that we have an ldap struct, add it to the referral list for rebinds. */
381 if (rc != APR_SUCCESS) {
383 "LDAP: Unable to add rebind cross reference entry. Out of memory?");
385 ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
386 return(rc);
387 }
388 }
389
390 /* always default to LDAP V3 */
392
393 /* set client certificates */
394 if (have_client_certs) {
396 ldc->client_certs, &(result));
397 if (LDAP_SUCCESS != result->rc) {
399 ldc->reason = result->reason;
400 return(result->rc);
401 }
402 }
403
404 /* switch on SSL/TLS */
405 if (APR_LDAP_NONE != ldc->secure
407 /* See comments near apr_ldap_init() above */
409#endif
410 ) {
412 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
413 if (LDAP_SUCCESS != result->rc) {
415 ldc->reason = result->reason;
416 return(result->rc);
417 }
418 }
419
420 /* Set the alias dereferencing option */
421 ldap_option = ldc->deref;
423
424 if (ldc->ChaseReferrals != AP_LDAP_CHASEREFERRALS_SDKDEFAULT) {
425 /* Set options for rebind and referrals. */
427 "LDAP: Setting referrals to %s.",
428 ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"));
431 (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
433 &(result));
434 if (result->rc != LDAP_SUCCESS) {
436 "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
437 ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"),
438 result->rc);
439 result->reason = "Unable to set LDAP_OPT_REFERRALS.";
440 ldc->reason = result->reason;
442 return(result->rc);
443 }
444 }
445
446 if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
447 if ((ldc->ReferralHopLimit != AP_LDAP_HOPLIMIT_UNSET) && ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
448 /* Referral hop limit - only if referrals are enabled and a hop limit is explicitly requested */
450 "Setting referral hop limit to %d.",
451 ldc->ReferralHopLimit);
454 (void *)&ldc->ReferralHopLimit,
455 &(result));
456 if (result->rc != LDAP_SUCCESS) {
458 "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
459 ldc->ReferralHopLimit,
460 result->rc);
461 result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
462 ldc->reason = result->reason;
464 return(result->rc);
465 }
466 }
467 }
468
469/*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
470#ifdef APR_LDAP_OPT_VERIFY_CERT
472 &(st->verify_svr_cert), &(result));
473#else
474#if defined(LDAPSSL_VERIFY_SERVER)
475 if (st->verify_svr_cert) {
477 }
478 else {
480 }
481#elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
482 /* This is not a per-connection setting so just pass NULL for the
483 Ldap connection handle */
484 if (st->verify_svr_cert) {
487 }
488 else {
491 }
492#endif
493#endif
494
495#ifdef LDAP_OPT_NETWORK_TIMEOUT
496 if (st->connectionTimeout > 0) {
497 connectionTimeout.tv_sec = st->connectionTimeout;
498 }
499
500 if (connectionTimeout.tv_sec > 0) {
502 (void *)&connectionTimeout, &(result));
503 if (APR_SUCCESS != rc) {
505 "LDAP: Could not set the connection timeout");
506 }
507 }
508#endif
509
510#ifdef LDAP_OPT_TIMEOUT
511 /*
512 * LDAP_OPT_TIMEOUT is not portable, but it influences all synchronous ldap
513 * function calls and not just ldap_search_ext_s(), which accepts a timeout
514 * parameter.
515 * XXX: It would be possible to simulate LDAP_OPT_TIMEOUT by replacing all
516 * XXX: synchronous ldap function calls with asynchronous calls and using
517 * XXX: ldap_result() with a timeout.
518 */
519 if (st->opTimeout) {
521 st->opTimeout, &(result));
522 if (APR_SUCCESS != rc) {
524 "LDAP: Could not set LDAP_OPT_TIMEOUT");
525 }
526 }
527#endif
528
529 return(rc);
530}
531
533{
534 int ldaprc;
535#ifdef LDAP_OPT_ERROR_NUMBER
537#endif
538#ifdef LDAP_OPT_RESULT_CODE
540#endif
541 return LDAP_OTHER;
542}
543
544/*
545 * Replacement function for ldap_simple_bind_s() with a timeout.
546 * To do this in a portable way, we have to use ldap_simple_bind() and
547 * ldap_result().
548 *
549 * Returns LDAP_SUCCESS on success; and an error code on failure
550 */
552 char* bindpw, struct timeval *timeout)
553{
555 int rc;
556 int msgid = ldap_simple_bind(ldc->ldap, binddn, bindpw);
557 if (msgid == -1) {
558 ldc->reason = "LDAP: ldap_simple_bind() failed";
559 return uldap_ld_errno(ldc);
560 }
561 rc = ldap_result(ldc->ldap, msgid, 0, timeout, &result);
562 if (rc == -1) {
563 ldc->reason = "LDAP: ldap_simple_bind() result retrieval failed";
564 /* -1 is LDAP_SERVER_DOWN in openldap, use something else */
565 return uldap_ld_errno(ldc);
566 }
567 else if (rc == 0) {
568 ldc->reason = "LDAP: ldap_simple_bind() timed out";
570 } else if (ldap_parse_result(ldc->ldap, result, &rc, NULL, NULL, NULL,
571 NULL, 1) == -1) {
572 ldc->reason = "LDAP: ldap_simple_bind() parse result failed";
573 return uldap_ld_errno(ldc);
574 }
575 else {
576 ldc->last_backend_conn = ldc->r->request_time;
577 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, ldc->r, "LDC %pp bind", ldc);
578 }
579 return rc;
580}
581
582/*
583 * Connect to the LDAP server and binds. Does not connect if already
584 * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
585 *
586 * Returns LDAP_SUCCESS on success; and an error code on failure
587 */
590{
591 int rc = 0;
592 int failures = 0;
593 int new_connection = 0;
595
596 /* sanity check for NULL */
597 if (!ldc) {
598 return -1;
599 }
600
601 /* If the connection is already bound, return
602 */
603 if (ldc->bound && !ldc->must_rebind)
604 {
605 ldc->reason = "LDAP: connection open successful (already bound)";
606 return LDAP_SUCCESS;
607 }
608
609 /* create the ldap session handle
610 */
611 if (NULL == ldc->ldap)
612 {
613 new_connection = 1;
615 if (LDAP_SUCCESS != rc)
616 {
617 return rc;
618 }
619 }
620
621
623 &ldap_module);
624
625 /* loop trying to bind up to st->retries times if LDAP_SERVER_DOWN or LDAP_TIMEOUT
626 * are returned. Close the connection before the first retry, and then on every
627 * other retry.
628 *
629 * On Success or any other error, break out of the loop.
630 *
631 * NOTE: Looping is probably not a great idea. If the server isn't
632 * responding the chances it will respond after a few tries are poor.
633 * However, the original code looped and it only happens on
634 * the error condition.
635 */
636
637 while (failures <= st->retries) {
638 if (failures > 0 && st->retry_delay > 0) {
639 apr_sleep(st->retry_delay);
640 }
641 rc = uldap_simple_bind(ldc, (char *)ldc->binddn, (char *)ldc->bindpw,
642 st->opTimeout);
643
644 if (rc == LDAP_SUCCESS) break;
645
646 failures++;
647
650 "ldap_simple_bind() failed with server down "
651 "(try %d)", failures);
652 }
653 else if (rc == LDAP_TIMEOUT) {
655 "ldap_simple_bind() timed out on %s "
656 "connection, dropped by firewall?",
657 new_connection ? "new" : "reused");
658 }
659 else {
660 /* Other errors not retryable */
661 break;
662 }
663
664 if (!(failures % 2)) {
666 "attempt to re-init the connection");
669 /* leave rc as the initial bind return code */
670 break;
671 }
672 }
673 }
674
675 /* free the handle if there was an error
676 */
677 if (LDAP_SUCCESS != rc)
678 {
680 ldc->reason = "LDAP: ldap_simple_bind() failed";
681 }
682 else {
683 ldc->bound = 1;
684 ldc->must_rebind = 0;
685 ldc->reason = "LDAP: connection open successful";
686 }
687
688 return(rc);
689}
690
691
692/*
693 * Compare client certificate arrays.
694 *
695 * Returns 1 on compare failure, 0 otherwise.
696 */
699{
700 int i = 0;
702
703 /* arrays both NULL? if so, then equal */
704 if (srcs == NULL && dests == NULL) {
705 return 0;
706 }
707
708 /* arrays different length or either NULL? If so, then not equal */
709 if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
710 return 1;
711 }
712
713 /* run an actual comparison */
714 src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
715 dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
716 for (i = 0; i < srcs->nelts; i++) {
717 if ((strcmp(src[i].path, dest[i].path)) ||
718 (src[i].type != dest[i].type) ||
719 /* One is passwordless? If so, then not equal */
720 ((src[i].password == NULL) ^ (dest[i].password == NULL)) ||
721 (src[i].password != NULL && dest[i].password != NULL &&
723 return 1;
724 }
725 }
726
727 /* if we got here, the cert arrays were identical */
728 return 0;
729
730}
731
732
733/*
734 * Find an existing ldap connection struct that matches the
735 * provided ldap connection parameters.
736 *
737 * If not found in the cache, a new ldc structure will be allocated
738 * from st->pool and returned to the caller. If found in the cache,
739 * a pointer to the existing ldc structure will be returned.
740 */
743 const char *host, int port,
744 const char *binddn, const char *bindpw,
745 deref_options deref, int secure)
746{
747 struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
748 int secureflag = secure;
750
753 &ldap_module);
756
757#if APR_HAS_THREADS
758 /* mutex lock this function */
760#endif
761
762 if (secure < APR_LDAP_NONE) {
763 secureflag = st->secure;
764 }
765
766 /* Search for an exact connection match in the list that is not
767 * being used.
768 */
769 for (l=st->connections,p=NULL; l; l=l->next) {
770#if APR_HAS_THREADS
771 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
772#endif
773 if ( (l->port == port) && (strcmp(l->host, host) == 0)
774 && ((!l->binddn && !binddn) || (l->binddn && binddn
775 && !strcmp(l->binddn, binddn)))
776 && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
777 && !strcmp(l->bindpw, bindpw)))
778 && (l->deref == deref) && (l->secure == secureflag)
779 && !compare_client_certs(dc->client_certs, l->client_certs))
780 {
781 if (st->connection_pool_ttl > 0) {
782 if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
784 "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
785 (now - l->last_backend_conn) / APR_USEC_PER_SEC);
786 l->r = r;
788 /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
789 }
791 "Reuse %s LDC %pp",
792 l->bound ? "bound" : "unbound", l);
793 }
794 break;
795 }
796#if APR_HAS_THREADS
797 /* If this connection didn't match the criteria, then we
798 * need to unlock the mutex so it is available to be reused.
799 */
801 }
802#endif
803 p = l;
804 }
805
806 /* If nothing found, search again, but we don't care about the
807 * binddn and bindpw this time.
808 */
809 if (!l) {
810 for (l=st->connections,p=NULL; l; l=l->next) {
811#if APR_HAS_THREADS
812 if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
813
814#endif
815 if ((l->port == port) && (strcmp(l->host, host) == 0) &&
816 (l->deref == deref) && (l->secure == secureflag) &&
817 !compare_client_certs(dc->client_certs, l->client_certs))
818 {
819 if (st->connection_pool_ttl > 0) {
820 if (l->bound && (now - l->last_backend_conn) > st->connection_pool_ttl) {
822 "Removing LDAP connection last used %" APR_TIME_T_FMT " seconds ago",
823 (now - l->last_backend_conn) / APR_USEC_PER_SEC);
824 l->r = r;
826 /* Go ahead (by falling through) and use it, so we don't create more just to unbind some other old ones */
827 }
829 "Reuse %s LDC %pp (will rebind)",
830 l->bound ? "bound" : "unbound", l);
831 }
832
833 /* the bind credentials have changed */
834 l->must_rebind = 1;
835 util_ldap_strdup((char**)&(l->binddn), binddn);
836 util_ldap_strdup((char**)&(l->bindpw), bindpw);
837
838 break;
839 }
840#if APR_HAS_THREADS
841 /* If this connection didn't match the criteria, then we
842 * need to unlock the mutex so it is available to be reused.
843 */
845 }
846#endif
847 p = l;
848 }
849 }
850
851/* artificially disable cache */
852/* l = NULL; */
853
854 /* If no connection was found after the second search, we
855 * must create one.
856 */
857 if (!l) {
861 "util_ldap: Failed to create memory pool");
862#if APR_HAS_THREADS
864#endif
865 return NULL;
866 }
867 apr_pool_tag(newpool, "util_ldap_connection");
868
869 /*
870 * Add the new connection entry to the linked list. Note that we
871 * don't actually establish an LDAP connection yet; that happens
872 * the first time authentication is requested.
873 */
874
875 /* create the details of this connection in the new pool */
877 l->pool = newpool;
878 l->st = st;
879
880#if APR_HAS_THREADS
882 apr_thread_mutex_lock(l->lock);
883#endif
884 l->bound = 0;
885 l->host = apr_pstrdup(l->pool, host);
886 l->port = port;
887 l->deref = deref;
888 util_ldap_strdup((char**)&(l->binddn), binddn);
889 util_ldap_strdup((char**)&(l->bindpw), bindpw);
890 l->ChaseReferrals = dc->ChaseReferrals;
891 l->ReferralHopLimit = dc->ReferralHopLimit;
892
893 /* The security mode after parsing the URL will always be either
894 * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
895 * If the security setting is NONE, override it to the security
896 * setting optionally supplied by the admin using LDAPTrustedMode
897 */
898 l->secure = secureflag;
899
900 /* save away a copy of the client cert list that is presently valid */
901 l->client_certs = apr_array_copy_hdr(l->pool, dc->client_certs);
902
903 /* whether or not to keep this connection in the pool when it's returned */
904 l->keep = (st->connection_pool_ttl == 0) ? 0 : 1;
905
906#ifdef USE_APR_LDAP_REBIND
907 if (l->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
908 if (apr_pool_create(&(l->rebind_pool), l->pool) != APR_SUCCESS) {
910 "util_ldap: Failed to create memory pool");
911#if APR_HAS_THREADS
913#endif
914 return NULL;
915 }
916 apr_pool_tag(l->rebind_pool, "util_ldap_rebind");
917 }
918#endif
919
920 if (p) {
921 p->next = l;
922 }
923 else {
924 st->connections = l;
925 }
926 }
927
928#if APR_HAS_THREADS
930#endif
931 l->r = r;
932 return l;
933}
934
935/* ------------------------------------------------------------------ */
936
937/*
938 * Compares two DNs to see if they're equal. The only way to do this correctly
939 * is to search for the dn and then do ldap_get_dn() on the result. This should
940 * match the initial dn, since it would have been also retrieved with
941 * ldap_get_dn(). This is expensive, so if the configuration value
942 * compare_dn_on_server is false, just does an ordinary strcmp.
943 *
944 * The lock for the ldap cache should already be acquired.
945 */
947 const char *url, const char *dn,
948 const char *reqdn, int compare_dn_on_server)
949{
950 int result = 0;
951 util_url_node_t *curl;
955 int failures = 0;
956 LDAPMessage *res, *entry;
957 char *searchdn;
958
961 &ldap_module);
962
963 /* get cache entry (or create one) */
965
966 curnode.url = url;
967 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
968 if (curl == NULL) {
970 }
972
973 /* a simple compare? */
974 if (!compare_dn_on_server) {
975 /* unlock this read lock */
976 if (strcmp(dn, reqdn)) {
977 ldc->reason = "DN Comparison FALSE (direct strcmp())";
978 return LDAP_COMPARE_FALSE;
979 }
980 else {
981 ldc->reason = "DN Comparison TRUE (direct strcmp())";
982 return LDAP_COMPARE_TRUE;
983 }
984 }
985
986 if (curl) {
987 /* no - it's a server side compare */
989
990 /* is it in the compare cache? */
991 newnode.reqdn = (char *)reqdn;
992 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
993 if (node != NULL) {
994 /* If it's in the cache, it's good */
995 /* unlock this read lock */
997 ldc->reason = "DN Comparison TRUE (cached)";
998 return LDAP_COMPARE_TRUE;
999 }
1000
1001 /* unlock this read lock */
1003 }
1004
1006 if (failures > st->retries) {
1007 return result;
1008 }
1009
1010 if (failures > 0 && st->retry_delay > 0) {
1011 apr_sleep(st->retry_delay);
1012 }
1013
1014 /* make a server connection */
1016 /* connect to server failed */
1017 return result;
1018 }
1019
1020 /* search for reqdn */
1022 "(objectclass=*)", NULL, 1,
1023 NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
1025 {
1026 ldc->reason = "DN Comparison ldap_search_ext_s() "
1027 "failed with server down";
1029 failures++;
1030 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1031 goto start_over;
1032 }
1033 if (result == LDAP_TIMEOUT && failures == 0) {
1034 /*
1035 * we are reusing a connection that doesn't seem to be active anymore
1036 * (firewall state drop?), let's try a new connection.
1037 */
1038 ldc->reason = "DN Comparison ldap_search_ext_s() "
1039 "failed with timeout";
1041 failures++;
1042 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1043 goto start_over;
1044 }
1045 if (result != LDAP_SUCCESS) {
1046 /* search for reqdn failed - no match */
1047 ldc->reason = "DN Comparison ldap_search_ext_s() failed";
1048 return result;
1049 }
1050
1051 ldc->last_backend_conn = r->request_time;
1052 entry = ldap_first_entry(ldc->ldap, res);
1053 searchdn = ldap_get_dn(ldc->ldap, entry);
1054
1056 if (strcmp(dn, searchdn) != 0) {
1057 /* compare unsuccessful */
1058 ldc->reason = "DN Comparison FALSE (checked on server)";
1060 }
1061 else {
1062 if (curl) {
1063 /* compare successful - add to the compare cache */
1065 newnode.reqdn = (char *)reqdn;
1066 newnode.dn = (char *)dn;
1067
1068 node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
1069 if ( (node == NULL)
1070 || (strcmp(reqdn, node->reqdn) != 0)
1071 || (strcmp(dn, node->dn) != 0))
1072 {
1073 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
1074 }
1076 }
1077 ldc->reason = "DN Comparison TRUE (checked on server)";
1079 }
1081 return result;
1082
1083}
1084
1085/*
1086 * Does an generic ldap_compare operation. It accepts a cache that it will use
1087 * to lookup the compare in the cache. We cache two kinds of compares
1088 * (require group compares) and (require user compares). Each compare has a
1089 * different cache node: require group includes the DN; require user does not
1090 * because the require user cache is owned by the
1091 *
1092 */
1094 const char *url, const char *dn,
1095 const char *attrib, const char *value)
1096{
1097 int result = 0;
1098 util_url_node_t *curl;
1102 apr_time_t curtime = 0; /* silence gcc -Wall */
1103 int failures = 0;
1104
1107 &ldap_module);
1108
1109 /* get cache entry (or create one) */
1111 curnode.url = url;
1112 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1113 if (curl == NULL) {
1114 curl = util_ald_create_caches(st, url);
1115 }
1117
1118 if (curl) {
1119 /* make a comparison to the cache */
1122
1123 the_compare_node.dn = (char *)dn;
1124 the_compare_node.attrib = (char *)attrib;
1125 the_compare_node.value = (char *)value;
1126 the_compare_node.result = 0;
1127 the_compare_node.sgl_processed = 0;
1128 the_compare_node.subgroupList = NULL;
1129
1130 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1132
1133 if (compare_nodep != NULL) {
1134 /* found it... */
1135 if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
1136 /* ...but it is too old */
1137 util_ald_cache_remove(curl->compare_cache, compare_nodep);
1138 }
1139 else {
1140 /* ...and it is good */
1141 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
1142 ldc->reason = "Comparison true (cached)";
1143 }
1144 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
1145 ldc->reason = "Comparison false (cached)";
1146 }
1147 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
1148 ldc->reason = "Comparison no such attribute (cached)";
1149 }
1150 else {
1151 ldc->reason = apr_psprintf(r->pool,
1152 "Comparison undefined: (%d): %s (adding to cache)",
1154 }
1155
1156 /* record the result code to return with the reason... */
1157 result = compare_nodep->result;
1158 /* and unlock this read lock */
1160
1162 "ldap_compare_s(%pp, %s, %s, %s) = %s (cached)",
1163 ldc->ldap, dn, attrib, value, ldap_err2string(result));
1164 return result;
1165 }
1166 }
1167 /* unlock this read lock */
1169 }
1170
1172 if (failures > st->retries) {
1173 return result;
1174 }
1175
1176 if (failures > 0 && st->retry_delay > 0) {
1177 apr_sleep(st->retry_delay);
1178 }
1179
1181 /* connect failed */
1182 return result;
1183 }
1184
1185 result = ldap_compare_s(ldc->ldap,
1186 (char *)dn,
1187 (char *)attrib,
1188 (char *)value);
1190 /* connection failed - try again */
1191 ldc->reason = "ldap_compare_s() failed with server down";
1193 failures++;
1194 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1195 goto start_over;
1196 }
1197 if (result == LDAP_TIMEOUT && failures == 0) {
1198 /*
1199 * we are reusing a connection that doesn't seem to be active anymore
1200 * (firewall state drop?), let's try a new connection.
1201 */
1202 ldc->reason = "ldap_compare_s() failed with timeout";
1204 failures++;
1205 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1206 goto start_over;
1207 }
1208
1209 ldc->last_backend_conn = r->request_time;
1210 ldc->reason = "Comparison complete";
1211 if ((LDAP_COMPARE_TRUE == result) ||
1214 if (curl) {
1215 /* compare completed; caching result */
1217 the_compare_node.lastcompare = curtime;
1218 the_compare_node.result = result;
1219 the_compare_node.sgl_processed = 0;
1220 the_compare_node.subgroupList = NULL;
1221
1222 /* If the node doesn't exist then insert it, otherwise just update
1223 * it with the last results
1224 */
1225 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1227 if ( (compare_nodep == NULL)
1228 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
1229 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
1230 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
1231 {
1232 void *junk;
1233
1234 junk = util_ald_cache_insert(curl->compare_cache,
1236 if (junk == NULL) {
1238 "cache_compare: Cache insertion failure.");
1239 }
1240 }
1241 else {
1242 compare_nodep->lastcompare = curtime;
1243 compare_nodep->result = result;
1244 }
1246 }
1247
1248 if (LDAP_COMPARE_TRUE == result) {
1249 ldc->reason = "Comparison true (adding to cache)";
1250 }
1251 else if (LDAP_COMPARE_FALSE == result) {
1252 ldc->reason = "Comparison false (adding to cache)";
1253 }
1254 else if (LDAP_NO_SUCH_ATTRIBUTE == result) {
1255 ldc->reason = "Comparison no such attribute (adding to cache)";
1256 }
1257 else {
1258 ldc->reason = apr_psprintf(r->pool,
1259 "Comparison undefined: (%d): %s (adding to cache)",
1261 }
1262 }
1263
1265 "ldap_compare_s(%pp, %s, %s, %s) = %s",
1266 ldc->ldap, dn, attrib, value, ldap_err2string(result));
1267 return result;
1268}
1269
1270
1273 const char *url,
1274 const char *dn,
1275 char **subgroupAttrs,
1276 apr_array_header_t *subgroupclasses)
1277{
1278 int failures = 0;
1281 LDAPMessage *sga_res, *entry;
1283 apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
1286 &ldap_module);
1287
1288 sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1289
1290 if (!subgroupAttrs) {
1291 return res;
1292 }
1293
1295 /*
1296 * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
1297 */
1298 if (failures > st->retries) {
1299 return res;
1300 }
1301
1302 if (failures > 0 && st->retry_delay > 0) {
1303 apr_sleep(st->retry_delay);
1304 }
1305
1306
1308 /* connect failed */
1309 return res;
1310 }
1311
1312 /* try to do the search */
1313 result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
1314 NULL, subgroupAttrs, 0,
1317 ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
1318 " down";
1320 failures++;
1321 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1322 goto start_over;
1323 }
1324 if (result == LDAP_TIMEOUT && failures == 0) {
1325 /*
1326 * we are reusing a connection that doesn't seem to be active anymore
1327 * (firewall state drop?), let's try a new connection.
1328 */
1329 ldc->reason = "ldap_search_ext_s() for subgroups failed with timeout";
1331 failures++;
1332 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1333 goto start_over;
1334 }
1335
1336 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1337 if (result != LDAP_SUCCESS) {
1338 ldc->reason = "ldap_search_ext_s() for subgroups failed";
1339 return res;
1340 }
1341
1342 ldc->last_backend_conn = r->request_time;
1343 entry = ldap_first_entry(ldc->ldap, sga_res);
1344
1345 /*
1346 * Get values for the provided sub-group attributes.
1347 */
1348 if (subgroupAttrs) {
1349 int indx = 0, tmp_sgcIndex;
1350
1351 while (subgroupAttrs[indx]) {
1352 char **values;
1353 int val_index = 0;
1354
1355 /* Get *all* matching "member" values from this group. */
1356 values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1357
1358 if (values) {
1359 val_index = 0;
1360 /*
1361 * Now we are going to pare the subgroup members of this group
1362 * to *just* the subgroups, add them to the compare_nodep, and
1363 * then proceed to check the new level of subgroups.
1364 */
1365 while (values[val_index]) {
1366 /* Check if this entry really is a group. */
1367 tmp_sgcIndex = 0;
1370 && (result != LDAP_COMPARE_TRUE)) {
1373 "objectClass",
1375 );
1376
1377 if (result != LDAP_COMPARE_TRUE) {
1378 tmp_sgcIndex++;
1379 }
1380 }
1381 /* It's a group, so add it to the array. */
1382 if (result == LDAP_COMPARE_TRUE) {
1383 char **newgrp = (char **) apr_array_push(subgroups);
1385 }
1386 val_index++;
1387 }
1389 }
1390 indx++;
1391 }
1392 }
1393
1395
1396 if (subgroups->nelts > 0) {
1397 /* We need to fill in tmp_local_subgroups using the data from LDAP */
1398 int sgindex;
1399 char **group;
1401 res->subgroupDNs = apr_palloc(r->pool,
1402 sizeof(char *) * (subgroups->nelts));
1403 for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1404 res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1405 }
1406 res->len = sgindex;
1407 }
1408
1409 return res;
1410}
1411
1412
1413/*
1414 * Does a recursive lookup operation to try to find a user within (cached)
1415 * nested groups. It accepts a cache that it will use to lookup previous
1416 * compare attempts. We cache two kinds of compares (require group compares)
1417 * and (require user compares). Each compare has a different cache node:
1418 * require group includes the DN; require user does not because the require
1419 * user cache is owned by the
1420 *
1421 * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1422 *
1423 *
1424 * 1. Call uldap_cache_compare for each subgroupclass value to check the
1425 * generic, user-agnostic, cached group entry. This will create a new generic
1426 * cache entry if there
1427 * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1428 * have no groups.
1429 * 2. Lock The cache and get the generic cache entry.
1430 * 3. Check if there is already a subgrouplist in this generic group's cache
1431 * entry.
1432 * A. If there is, go to step 4.
1433 * B. If there isn't:
1434 * i) Use ldap_search to get the full list
1435 * of subgroup "members" (which may include non-group "members").
1436 * ii) Use uldap_cache_compare to strip the list down to just groups.
1437 * iii) Lock and add this stripped down list to the cache of the generic
1438 * group.
1439 * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1440 * for each
1441 * subgroup to see if the subgroup contains the user and to get the subgroups
1442 * added to the
1443 * cache (with user-afinity, if they aren't already there).
1444 * A. If the user is in the subgroup, then we'll be returning
1445 * LDAP_COMPARE_TRUE.
1446 * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1447 * uldap_cache_compare) then recursively call this function to get the
1448 * sub-subgroups added...
1449 * 5. Cleanup local allocations.
1450 * 6. Return the final result.
1451 */
1452
1455 const char *url, const char *dn,
1456 const char *attrib, const char *value,
1457 char **subgroupAttrs,
1458 apr_array_header_t *subgroupclasses,
1461{
1463 util_url_node_t *curl;
1468 int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
1470 (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1473 &ldap_module);
1474
1475 /*
1476 * Stop looking at deeper levels of nested groups if we have reached the
1477 * max. Since we already checked the top-level group in uldap_cache_compare,
1478 * we don't need to check it again here - so if max_subgroup_depth is set
1479 * to 0, we won't check it (i.e. that is why we check < rather than <=).
1480 * We'll be calling uldap_cache_compare from here to check if the user is
1481 * in the next level before we recurse into that next level looking for
1482 * more subgroups.
1483 */
1485 return LDAP_COMPARE_FALSE;
1486 }
1487
1488 /*
1489 * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1490 * TRUE return.
1491 */
1493 && (result != LDAP_COMPARE_TRUE)) {
1494 result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1496 if (result != LDAP_COMPARE_TRUE) {
1497 base_sgcIndex++;
1498 }
1499 }
1500
1501 if (result != LDAP_COMPARE_TRUE) {
1502 ldc->reason = "DN failed group verification.";
1503 return result;
1504 }
1505
1506 /*
1507 * 2. Find previously created cache entry and check if there is already a
1508 * subgrouplist.
1509 */
1511 curnode.url = url;
1512 curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1514
1515 if (curl && curl->compare_cache) {
1516 /* make a comparison to the cache */
1518
1519 the_compare_node.dn = (char *)dn;
1520 the_compare_node.attrib = (char *)"objectClass";
1522 the_compare_node.result = 0;
1523 the_compare_node.sgl_processed = 0;
1524 the_compare_node.subgroupList = NULL;
1525
1526 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1528
1529 if (compare_nodep != NULL) {
1530 /*
1531 * Found the generic group entry... but the user isn't in this
1532 * group or we wouldn't be here.
1533 */
1534 if (compare_nodep->sgl_processed) {
1535 if (compare_nodep->subgroupList) {
1536 /* Make a local copy of the subgroup list */
1537 int i;
1539 "Making local copy of SGL for "
1540 "group (%s)(objectClass=%s) ",
1541 dn, (char *)sgc_ents[base_sgcIndex].name);
1543 sizeof(util_compare_subgroup_t));
1544 tmp_local_sgl->len = compare_nodep->subgroupList->len;
1545 tmp_local_sgl->subgroupDNs =
1546 apr_palloc(r->pool,
1547 sizeof(char *) * compare_nodep->subgroupList->len);
1548 for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1549 tmp_local_sgl->subgroupDNs[i] =
1551 compare_nodep->subgroupList->subgroupDNs[i]);
1552 }
1553 }
1554 else {
1555 sgl_cached_empty = 1;
1556 }
1557 }
1558 }
1560 }
1561
1563 /* No Cached SGL, retrieve from LDAP */
1565 "no cached SGL for %s, retrieving from LDAP", dn);
1567 subgroupclasses);
1568 if (!tmp_local_sgl) {
1569 /* No SGL aailable via LDAP either */
1570 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01290) "no subgroups for %s",
1571 dn);
1572 }
1573
1574 if (curl && curl->compare_cache) {
1575 /*
1576 * Find the generic group cache entry and add the sgl we just retrieved.
1577 */
1579
1580 the_compare_node.dn = (char *)dn;
1581 the_compare_node.attrib = (char *)"objectClass";
1583 the_compare_node.result = 0;
1584 the_compare_node.sgl_processed = 0;
1585 the_compare_node.subgroupList = NULL;
1586
1587 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1589
1590 if (compare_nodep == NULL) {
1591 /*
1592 * The group entry we want to attach our SGL to doesn't exist.
1593 * We only got here if we verified this DN was actually a group
1594 * based on the objectClass, but we can't call the compare function
1595 * while we already hold the cache lock -- only the insert.
1596 */
1598 "Cache entry for %s doesn't exist", dn);
1600 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
1601 compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1603 if (compare_nodep == NULL) {
1605 "util_ldap: Couldn't retrieve group entry "
1606 "for %s from cache",
1607 dn);
1608 }
1609 }
1610
1611 /*
1612 * We have a valid cache entry and a locally generated SGL.
1613 * Attach the SGL to the cache entry
1614 */
1615 if (compare_nodep && !compare_nodep->sgl_processed) {
1616 if (!tmp_local_sgl) {
1617 /* We looked up an SGL for a group and found it to be empty */
1618 if (compare_nodep->subgroupList == NULL) {
1619 compare_nodep->sgl_processed = 1;
1620 }
1621 }
1622 else {
1624 util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1626 "Copying local SGL of len %d for group %s into cache",
1627 tmp_local_sgl->len, dn);
1628 if (sgl_copy) {
1629 if (compare_nodep->subgroupList) {
1630 util_ald_sgl_free(curl->compare_cache,
1631 &(compare_nodep->subgroupList));
1632 }
1633 compare_nodep->subgroupList = sgl_copy;
1634 compare_nodep->sgl_processed = 1;
1635 }
1636 else {
1638 "Copy of SGL failed to obtain shared memory, "
1639 "couldn't update cache");
1640 }
1641 }
1642 }
1644 }
1645 }
1646
1647 /*
1648 * tmp_local_sgl has either been created, or copied out of the cache
1649 * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1650 * return false
1651 */
1653 if (!tmp_local_sgl) {
1654 return result;
1655 }
1656
1658 const char *group = NULL;
1659 group = tmp_local_sgl->subgroupDNs[sgindex];
1660 /*
1661 * 4. Now loop through the subgroupList and call uldap_cache_compare
1662 * to check for the user.
1663 */
1665 if (result == LDAP_COMPARE_TRUE) {
1666 /*
1667 * 4.A. We found the user in the subgroup. Return
1668 * LDAP_COMPARE_TRUE.
1669 */
1671 "Found user %s in a subgroup (%s) at level %d of %d.",
1672 r->user, group, cur_subgroup_depth+1,
1674 }
1675 else {
1676 /*
1677 * 4.B. We didn't find the user in this subgroup, so recurse into
1678 * it and keep looking.
1679 */
1681 "User %s not found in subgroup (%s) at level %d of "
1682 "%d.", r->user, group, cur_subgroup_depth+1,
1686 subgroupclasses,
1689 }
1690 sgindex++;
1691 }
1692
1693 return result;
1694}
1695
1696
1698 const char *url, const char *basedn,
1699 int scope, char **attrs, const char *filter,
1700 const char *bindpw, const char **binddn,
1701 const char ***retvals)
1702{
1703 const char **vals = NULL;
1704 int numvals = 0;
1705 int result = 0;
1706 LDAPMessage *res, *entry;
1707 char *dn;
1708 int count;
1709 int failures = 0;
1710 util_url_node_t *curl; /* Cached URL node */
1712 util_search_node_t *search_nodep; /* Cached search node */
1715
1718 &ldap_module);
1719
1720 /* Get the cache node for this url */
1722 curnode.url = url;
1723 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1724 &curnode);
1725 if (curl == NULL) {
1726 curl = util_ald_create_caches(st, url);
1727 }
1729
1730 if (curl) {
1732 the_search_node.username = filter;
1733 search_nodep = util_ald_cache_fetch(curl->search_cache,
1735 if (search_nodep != NULL) {
1736
1737 /* found entry in search cache... */
1739
1740 /*
1741 * Remove this item from the cache if its expired. If the sent
1742 * password doesn't match the storepassword, the entry will
1743 * be removed and readded later if the credentials pass
1744 * authentication.
1745 */
1746 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1747 /* ...but entry is too old */
1748 util_ald_cache_remove(curl->search_cache, search_nodep);
1749 }
1750 else if ( (search_nodep->bindpw)
1751 && (search_nodep->bindpw[0] != '\0')
1752 && (strcmp(search_nodep->bindpw, bindpw) == 0))
1753 {
1754 /* ...and entry is valid */
1755 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1756 if (attrs) {
1757 int i;
1758 *retvals = apr_palloc(r->pool, sizeof(char *) * search_nodep->numvals);
1759 for (i = 0; i < search_nodep->numvals; i++) {
1760 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1761 }
1762 }
1764 ldc->reason = "Authentication successful (cached)";
1765 return LDAP_SUCCESS;
1766 }
1767 }
1768 /* unlock this read lock */
1770 }
1771
1772 /*
1773 * At this point, there is no valid cached search, so lets do the search.
1774 */
1775
1776 /*
1777 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1778 */
1780 if (failures > st->retries) {
1781 return result;
1782 }
1783
1784 if (failures > 0 && st->retry_delay > 0) {
1785 apr_sleep(st->retry_delay);
1786 }
1787
1789 return result;
1790 }
1791
1792 /* try do the search */
1794 (char *)basedn, scope,
1795 (char *)filter, attrs, 0,
1796 NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
1798 {
1799 ldc->reason = "ldap_search_ext_s() for user failed with server down";
1801 failures++;
1802 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1803 goto start_over;
1804 }
1805
1806 if (result == LDAP_TIMEOUT) {
1807 ldc->reason = "ldap_search_ext_s() for user failed with timeout";
1809 failures++;
1810 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1811 goto start_over;
1812 }
1813
1814
1815 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1816 if (result != LDAP_SUCCESS) {
1817 ldc->reason = "ldap_search_ext_s() for user failed";
1818 return result;
1819 }
1820
1821 /*
1822 * We should have found exactly one entry; to find a different
1823 * number is an error.
1824 */
1825 ldc->last_backend_conn = r->request_time;
1826 count = ldap_count_entries(ldc->ldap, res);
1827 if (count != 1)
1828 {
1829 if (count == 0 )
1830 ldc->reason = "User not found";
1831 else
1832 ldc->reason = "User is not unique (search found two "
1833 "or more matches)";
1835 return LDAP_NO_SUCH_OBJECT;
1836 }
1837
1838 entry = ldap_first_entry(ldc->ldap, res);
1839
1840 /* Grab the dn, copy it into the pool, and free it again */
1841 dn = ldap_get_dn(ldc->ldap, entry);
1842 *binddn = apr_pstrdup(r->pool, dn);
1843 ldap_memfree(dn);
1844
1845 /*
1846 * A bind to the server with an empty password always succeeds, so
1847 * we check to ensure that the password is not empty. This implies
1848 * that users who actually do have empty passwords will never be
1849 * able to authenticate with this module. I don't see this as a big
1850 * problem.
1851 */
1852 if (!bindpw || strlen(bindpw) <= 0) {
1854 ldc->reason = "Empty password not allowed";
1856 }
1857
1858 /*
1859 * Attempt to bind with the retrieved dn and the password. If the bind
1860 * fails, it means that the password is wrong (the dn obviously
1861 * exists, since we just retrieved it)
1862 */
1863 result = uldap_simple_bind(ldc, (char *)*binddn, (char *)bindpw,
1864 st->opTimeout);
1866 (result == LDAP_TIMEOUT && failures == 0)) {
1868 ldc->reason = "ldap_simple_bind() to check user credentials "
1869 "failed with server down";
1870 else
1871 ldc->reason = "ldap_simple_bind() to check user credentials "
1872 "timed out";
1875 failures++;
1876 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
1877 goto start_over;
1878 }
1879
1880 /* failure? if so - return */
1881 if (result != LDAP_SUCCESS) {
1882 ldc->reason = "ldap_simple_bind() to check user credentials failed";
1885 return result;
1886 }
1887 else {
1888 /*
1889 * We have just bound the connection to a different user and password
1890 * combination, which might be reused unintentionally next time this
1891 * connection is used from the connection pool.
1892 */
1893 ldc->must_rebind = 1;
1894 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "LDC %pp used for authn, must be rebound", ldc);
1895 }
1896
1897 /*
1898 * Get values for the provided attributes.
1899 */
1900 if (attrs) {
1901 int k = 0;
1902 int i = 0;
1903 while (attrs[k++]);
1904 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1905 numvals = k;
1906 while (attrs[i]) {
1907 char **values;
1908 int j = 0;
1909 char *str = NULL;
1910 /* get values */
1911 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1912 while (values && values[j]) {
1913 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1914 : apr_pstrdup(r->pool, values[j]);
1915 j++;
1916 }
1918 vals[i] = str;
1919 i++;
1920 }
1921 *retvals = vals;
1922 }
1923
1924 /*
1925 * Add the new username to the search cache.
1926 */
1927 if (curl) {
1929 the_search_node.username = filter;
1930 the_search_node.dn = *binddn;
1931 the_search_node.bindpw = bindpw;
1932 the_search_node.lastbind = apr_time_now();
1933 the_search_node.vals = vals;
1934 the_search_node.numvals = numvals;
1935
1936 /* Search again to make sure that another thread didn't ready insert
1937 * this node into the cache before we got here. If it does exist then
1938 * update the lastbind
1939 */
1940 search_nodep = util_ald_cache_fetch(curl->search_cache,
1942 if ((search_nodep == NULL) ||
1943 (strcmp(*binddn, search_nodep->dn) != 0)) {
1944
1945 /* Nothing in cache, insert new entry */
1946 util_ald_cache_insert(curl->search_cache, &the_search_node);
1947 }
1948 else if ((!search_nodep->bindpw) ||
1949 (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1950
1951 /* Entry in cache is invalid, remove it and insert new one */
1952 util_ald_cache_remove(curl->search_cache, search_nodep);
1953 util_ald_cache_insert(curl->search_cache, &the_search_node);
1954 }
1955 else {
1956 /* Cache entry is valid, update lastbind */
1957 search_nodep->lastbind = the_search_node.lastbind;
1958 }
1960 }
1962
1963 ldc->reason = "Authentication successful";
1964 return LDAP_SUCCESS;
1965}
1966
1967/*
1968 * This function will return the DN of the entry matching userid.
1969 * It is used to get the DN in case some other module than mod_auth_ldap
1970 * has authenticated the user.
1971 * The function is basically a copy of uldap_cache_checkuserid
1972 * with password checking removed.
1973 */
1975 const char *url, const char *basedn,
1976 int scope, char **attrs, const char *filter,
1977 const char **binddn, const char ***retvals)
1978{
1979 const char **vals = NULL;
1980 int numvals = 0;
1981 int result = 0;
1982 LDAPMessage *res, *entry;
1983 char *dn;
1984 int count;
1985 int failures = 0;
1986 util_url_node_t *curl; /* Cached URL node */
1988 util_search_node_t *search_nodep; /* Cached search node */
1991
1994 &ldap_module);
1995
1996 /* Get the cache node for this url */
1998 curnode.url = url;
1999 curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
2000 &curnode);
2001 if (curl == NULL) {
2002 curl = util_ald_create_caches(st, url);
2003 }
2005
2006 if (curl) {
2008 the_search_node.username = filter;
2009 search_nodep = util_ald_cache_fetch(curl->search_cache,
2011 if (search_nodep != NULL) {
2012
2013 /* found entry in search cache... */
2015
2016 /*
2017 * Remove this item from the cache if its expired.
2018 */
2019 if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
2020 /* ...but entry is too old */
2021 util_ald_cache_remove(curl->search_cache, search_nodep);
2022 }
2023 else {
2024 /* ...and entry is valid */
2025 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
2026 if (attrs) {
2027 int i;
2028 *retvals = apr_palloc(r->pool, sizeof(char *) * search_nodep->numvals);
2029 for (i = 0; i < search_nodep->numvals; i++) {
2030 (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
2031 }
2032 }
2034 ldc->reason = "Search successful (cached)";
2035 return LDAP_SUCCESS;
2036 }
2037 }
2038 /* unlock this read lock */
2040 }
2041
2042 /*
2043 * At this point, there is no valid cached search, so lets do the search.
2044 */
2045
2046 /*
2047 * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
2048 */
2050 if (failures > st->retries) {
2051 return result;
2052 }
2053
2054 if (failures > 0 && st->retry_delay > 0) {
2055 apr_sleep(st->retry_delay);
2056 }
2057
2059 return result;
2060 }
2061
2062 /* try do the search */
2064 (char *)basedn, scope,
2065 (char *)filter, attrs, 0,
2066 NULL, NULL, st->opTimeout, APR_LDAP_SIZELIMIT, &res);
2068 {
2069 ldc->reason = "ldap_search_ext_s() for user failed with server down";
2071 failures++;
2072 ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "%s (attempt %d)", ldc->reason, failures);
2073 goto start_over;
2074 }
2075
2076 /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
2077 if (result != LDAP_SUCCESS) {
2078 ldc->reason = "ldap_search_ext_s() for user failed";
2079 return result;
2080 }
2081
2082 /*
2083 * We should have found exactly one entry; to find a different
2084 * number is an error.
2085 */
2086 ldc->last_backend_conn = r->request_time;
2087 count = ldap_count_entries(ldc->ldap, res);
2088 if (count != 1)
2089 {
2090 if (count == 0 )
2091 ldc->reason = "User not found";
2092 else
2093 ldc->reason = "User is not unique (search found two "
2094 "or more matches)";
2096 return LDAP_NO_SUCH_OBJECT;
2097 }
2098
2099 entry = ldap_first_entry(ldc->ldap, res);
2100
2101 /* Grab the dn, copy it into the pool, and free it again */
2102 dn = ldap_get_dn(ldc->ldap, entry);
2103 *binddn = apr_pstrdup(r->pool, dn);
2104 ldap_memfree(dn);
2105
2106 /*
2107 * Get values for the provided attributes.
2108 */
2109 if (attrs) {
2110 int k = 0;
2111 int i = 0;
2112 while (attrs[k++]);
2113 vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
2114 numvals = k;
2115 while (attrs[i]) {
2116 char **values;
2117 int j = 0;
2118 char *str = NULL;
2119 /* get values */
2120 values = ldap_get_values(ldc->ldap, entry, attrs[i]);
2121 while (values && values[j]) {
2122 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
2123 : apr_pstrdup(r->pool, values[j]);
2124 j++;
2125 }
2127 vals[i] = str;
2128 i++;
2129 }
2130 *retvals = vals;
2131 }
2132
2133 /*
2134 * Add the new username to the search cache.
2135 */
2136 if (curl) {
2138 the_search_node.username = filter;
2139 the_search_node.dn = *binddn;
2140 the_search_node.bindpw = NULL;
2141 the_search_node.lastbind = apr_time_now();
2142 the_search_node.vals = vals;
2143 the_search_node.numvals = numvals;
2144
2145 /* Search again to make sure that another thread didn't ready insert
2146 * this node into the cache before we got here. If it does exist then
2147 * update the lastbind
2148 */
2149 search_nodep = util_ald_cache_fetch(curl->search_cache,
2151 if ((search_nodep == NULL) ||
2152 (strcmp(*binddn, search_nodep->dn) != 0)) {
2153
2154 /* Nothing in cache, insert new entry */
2155 util_ald_cache_insert(curl->search_cache, &the_search_node);
2156 }
2157 /*
2158 * Don't update lastbind on entries with bindpw because
2159 * we haven't verified that password. It's OK to update
2160 * the entry if there is no password in it.
2161 */
2162 else if (!search_nodep->bindpw) {
2163 /* Cache entry is valid, update lastbind */
2164 search_nodep->lastbind = the_search_node.lastbind;
2165 }
2167 }
2168
2170
2171 ldc->reason = "Search successful";
2172 return LDAP_SUCCESS;
2173}
2174
2175/*
2176 * Reports if ssl support is enabled
2177 *
2178 * 1 = enabled, 0 = not enabled
2179 */
2181{
2183 r->server->module_config, &ldap_module);
2184
2185 return(st->ssl_supported);
2186}
2187
2188
2189/* ---------------------------------------- */
2190/* config directives */
2191
2192
2194 const char *bytes)
2195{
2197 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2198 &ldap_module);
2199 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2200
2201 if (err != NULL) {
2202 return err;
2203 }
2204
2205 st->cache_bytes = atol(bytes);
2206
2207 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01297)
2208 "ldap cache: Setting shared memory cache size to "
2209 "%" APR_SIZE_T_FMT " bytes.",
2210 st->cache_bytes);
2211
2212 return NULL;
2213}
2214
2215static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
2216 const char *file)
2217{
2219 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2220 &ldap_module);
2221 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2222
2223 if (err != NULL) {
2224 return err;
2225 }
2226
2227 if (file) {
2228 st->cache_file = ap_server_root_relative(st->pool, file);
2229 }
2230 else {
2231 st->cache_file = NULL;
2232 }
2233
2234 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01298)
2235 "LDAP cache: Setting shared memory cache file to %s.",
2236 st->cache_file);
2237
2238 return NULL;
2239}
2240
2241static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
2242 const char *ttl)
2243{
2245 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2246 &ldap_module);
2247 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2248
2249 if (err != NULL) {
2250 return err;
2251 }
2252
2253 st->search_cache_ttl = atol(ttl) * 1000000;
2254
2255 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01299)
2256 "ldap cache: Setting cache TTL to %ld microseconds.",
2257 st->search_cache_ttl);
2258
2259 return NULL;
2260}
2261
2263 const char *size)
2264{
2266 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2267 &ldap_module);
2268 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2269
2270 if (err != NULL) {
2271 return err;
2272 }
2273
2274 st->search_cache_size = atol(size);
2275 if (st->search_cache_size < 0) {
2276 st->search_cache_size = 0;
2277 }
2278
2279 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01300)
2280 "ldap cache: Setting search cache size to %ld entries.",
2281 st->search_cache_size);
2282
2283 return NULL;
2284}
2285
2287 const char *ttl)
2288{
2290 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2291 &ldap_module);
2292 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2293
2294 if (err != NULL) {
2295 return err;
2296 }
2297
2298 st->compare_cache_ttl = atol(ttl) * APR_USEC_PER_SEC;
2299
2300 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01301)
2301 "ldap cache: Setting operation cache TTL to %ld microseconds.",
2302 st->compare_cache_ttl);
2303
2304 return NULL;
2305}
2306
2308 const char *size)
2309{
2311 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2312 &ldap_module);
2313 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2314
2315 if (err != NULL) {
2316 return err;
2317 }
2318
2319 st->compare_cache_size = atol(size);
2320 if (st->compare_cache_size < 0) {
2321 st->compare_cache_size = 0;
2322 }
2323
2324 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01302)
2325 "ldap cache: Setting operation cache size to %ld entries.",
2326 st->compare_cache_size);
2327
2328 return NULL;
2329}
2330
2331
2341static int util_ldap_parse_cert_type(const char *type)
2342{
2343 /* Authority file in binary DER format */
2344 if (0 == strcasecmp("CA_DER", type)) {
2345 return APR_LDAP_CA_TYPE_DER;
2346 }
2347
2348 /* Authority file in Base64 format */
2349 else if (0 == strcasecmp("CA_BASE64", type)) {
2351 }
2352
2353 /* Netscape certificate database file/directory */
2354 else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2356 }
2357
2358 /* Netscape secmod file/directory */
2359 else if (0 == strcasecmp("CA_SECMOD", type)) {
2361 }
2362
2363 /* Client cert file in DER format */
2364 else if (0 == strcasecmp("CERT_DER", type)) {
2366 }
2367
2368 /* Client cert file in Base64 format */
2369 else if (0 == strcasecmp("CERT_BASE64", type)) {
2371 }
2372
2373 /* Client cert file in PKCS#12 format */
2374 else if (0 == strcasecmp("CERT_PFX", type)) {
2376 }
2377
2378 /* Netscape client cert database file/directory */
2379 else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2381 }
2382
2383 /* Netscape client cert nickname */
2384 else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2386 }
2387
2388 /* Client cert key file in DER format */
2389 else if (0 == strcasecmp("KEY_DER", type)) {
2390 return APR_LDAP_KEY_TYPE_DER;
2391 }
2392
2393 /* Client cert key file in Base64 format */
2394 else if (0 == strcasecmp("KEY_BASE64", type)) {
2396 }
2397
2398 /* Client cert key file in PKCS#12 format */
2399 else if (0 == strcasecmp("KEY_PFX", type)) {
2400 return APR_LDAP_KEY_TYPE_PFX;
2401 }
2402
2403 else {
2405 }
2406
2407}
2408
2409
2421 void *dummy,
2422 const char *type,
2423 const char *file,
2424 const char *password)
2425{
2427 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2428 &ldap_module);
2429 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2430 apr_finfo_t finfo;
2431 apr_status_t rv;
2432 int cert_type = 0;
2434
2435 if (err != NULL) {
2436 return err;
2437 }
2438
2439 /* handle the certificate type */
2440 if (type) {
2443 return apr_psprintf(cmd->pool, "The certificate type %s is "
2444 "not recognised. It should be one "
2445 "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2446 "CA_SECMOD, CERT_DER, CERT_BASE64, "
2447 "CERT_KEY3_DB, CERT_NICKNAME, "
2448 "KEY_DER, KEY_BASE64", type);
2449 }
2450 }
2451 else {
2452 return "Certificate type was not specified.";
2453 }
2454
2455 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01303)
2456 "LDAP: SSL trusted global cert - %s (type %s)",
2457 file, type);
2458
2459 /* add the certificate to the global array */
2460 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2461 cert->type = cert_type;
2462 cert->path = file;
2463 cert->password = password;
2464
2465 /* if file is a file or path, fix the path */
2468
2469 cert->path = ap_server_root_relative(cmd->pool, file);
2470 if (cert->path &&
2471 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2472 != APR_SUCCESS))
2473 {
2474 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server, APLOGNO(01304)
2475 "LDAP: Could not open SSL trusted certificate "
2476 "authority file - %s",
2477 cert->path == NULL ? file : cert->path);
2478 return "Invalid global certificate file path";
2479 }
2480 }
2481
2482 return(NULL);
2483}
2484
2485
2495 void *config,
2496 const char *type,
2497 const char *file,
2498 const char *password)
2499{
2500 util_ldap_config_t *dc = config;
2501 apr_finfo_t finfo;
2502 apr_status_t rv;
2503 int cert_type = 0;
2505
2506 /* handle the certificate type */
2507 if (type) {
2510 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2511 "not recognised. It should be one "
2512 "of CA_DER, CA_BASE64, "
2513 "CERT_DER, CERT_BASE64, "
2514 "CERT_NICKNAME, CERT_PFX, "
2515 "KEY_DER, KEY_BASE64, KEY_PFX",
2516 type);
2517 }
2518 else if ( APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2522 return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2523 "only valid within a "
2524 "LDAPTrustedGlobalCert directive. "
2525 "Only CA_DER, CA_BASE64, "
2526 "CERT_DER, CERT_BASE64, "
2527 "CERT_NICKNAME, KEY_DER, and "
2528 "KEY_BASE64 may be used.", type);
2529 }
2530 }
2531 else {
2532 return "Certificate type was not specified.";
2533 }
2534
2535 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01305)
2536 "LDAP: SSL trusted client cert - %s (type %s)",
2537 file, type);
2538
2539 /* add the certificate to the client array */
2540 cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(dc->client_certs);
2541 cert->type = cert_type;
2542 cert->path = file;
2543 cert->password = password;
2544
2545 /* if file is a file or path, fix the path */
2548
2549 cert->path = ap_server_root_relative(cmd->pool, file);
2550 if (cert->path &&
2551 ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2552 != APR_SUCCESS))
2553 {
2554 ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server, APLOGNO(01306)
2555 "LDAP: Could not open SSL client certificate "
2556 "file - %s",
2557 cert->path == NULL ? file : cert->path);
2558 return "Invalid client certificate file path";
2559 }
2560
2561 }
2562
2563 return(NULL);
2564}
2565
2566
2576 const char *mode)
2577{
2579 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2580 &ldap_module);
2581
2582 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01307)
2583 "LDAP: SSL trusted mode - %s",
2584 mode);
2585
2586 if (0 == strcasecmp("NONE", mode)) {
2587 st->secure = APR_LDAP_NONE;
2588 }
2589 else if (0 == strcasecmp("SSL", mode)) {
2590 st->secure = APR_LDAP_SSL;
2591 }
2592 else if ( (0 == strcasecmp("TLS", mode))
2593 || (0 == strcasecmp("STARTTLS", mode))) {
2594 st->secure = APR_LDAP_STARTTLS;
2595 }
2596 else {
2597 return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2598 "SSL, or TLS/STARTTLS";
2599 }
2600
2601 st->secure_set = 1;
2602 return(NULL);
2603}
2604
2606 void *dummy,
2607 int mode)
2608{
2610 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2611 &ldap_module);
2612 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2613
2614 if (err != NULL) {
2615 return err;
2616 }
2617
2618 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01308)
2619 "LDAP: SSL verify server certificate - %s",
2620 mode?"TRUE":"FALSE");
2621
2622 st->verify_svr_cert = mode;
2623
2624 return(NULL);
2625}
2626
2627
2629 void *dummy,
2630 const char *ttl)
2631{
2632#ifdef LDAP_OPT_NETWORK_TIMEOUT
2634 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2635 &ldap_module);
2636#endif
2637 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2638
2639 if (err != NULL) {
2640 return err;
2641 }
2642
2643#ifdef LDAP_OPT_NETWORK_TIMEOUT
2644 st->connectionTimeout = atol(ttl);
2645
2646 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01309)
2647 "ldap connection: Setting connection timeout to %ld seconds.",
2648 st->connectionTimeout);
2649#else
2650 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, APLOGNO(01310)
2651 "LDAP: Connection timeout option not supported by the "
2652 "LDAP SDK in use." );
2653#endif
2654
2655 return NULL;
2656}
2657
2658
2660 void *config,
2661 const char *arg)
2662{
2663 util_ldap_config_t *dc = config;
2664
2665 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01311)
2666 "LDAP: Setting referral chasing %s", arg);
2667
2668 if (0 == strcasecmp(arg, "on")) {
2669 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2670 }
2671 else if (0 == strcasecmp(arg, "off")) {
2672 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_OFF;
2673 }
2674 else if (0 == strcasecmp(arg, "default")) {
2675 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_SDKDEFAULT;
2676 }
2677 else {
2678 return "LDAPReferrals must be 'on', 'off', or 'default'";
2679 }
2680
2681 return(NULL);
2682}
2683
2685 void *config,
2686 const char *arg) {
2687#ifdef AP_LDAP_OPT_DEBUG
2689 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2690 &ldap_module);
2691#endif
2692
2693 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2694 if (err != NULL) {
2695 return err;
2696 }
2697
2698#ifndef AP_LDAP_OPT_DEBUG
2699 return "This directive is not supported with the currently linked LDAP library";
2700#else
2701 st->debug_level = atoi(arg);
2702 return NULL;
2703#endif
2704}
2705
2707 void *config,
2708 const char *hop_limit)
2709{
2710 util_ldap_config_t *dc = config;
2711
2712 dc->ReferralHopLimit = atol(hop_limit);
2713
2714 if (dc->ReferralHopLimit <= 0) {
2715 return "LDAPReferralHopLimit must be greater than zero (Use 'LDAPReferrals Off' to disable referral chasing)";
2716 }
2717
2718 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01312)
2719 "LDAP: Limit chased referrals to maximum of %d hops.",
2720 dc->ReferralHopLimit);
2721
2722 return NULL;
2723}
2724
2726{
2727 util_ldap_config_t *dc =
2729
2730 /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
2731 dc->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2732 dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2733 dc->ReferralHopLimit = AP_LDAP_HOPLIMIT_UNSET;
2734
2735 return dc;
2736}
2737
2739 void *dummy,
2740 const char *val)
2741{
2742 long timeout;
2743 char *endptr;
2745 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2746 &ldap_module);
2747 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2748
2749 if (err != NULL) {
2750 return err;
2751 }
2752
2753 timeout = strtol(val, &endptr, 10);
2754 if ((val == endptr) || (*endptr != '\0')) {
2755 return "Timeout not numerical";
2756 }
2757 if (timeout < 0) {
2758 return "Timeout must be non-negative";
2759 }
2760
2761 if (timeout) {
2762 if (!st->opTimeout) {
2763 st->opTimeout = apr_pcalloc(cmd->pool, sizeof(struct timeval));
2764 }
2765 st->opTimeout->tv_sec = timeout;
2766 }
2767 else {
2768 st->opTimeout = NULL;
2769 }
2770
2771 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01313)
2772 "ldap connection: Setting op timeout to %ld seconds.",
2773 timeout);
2774
2775#ifndef LDAP_OPT_TIMEOUT
2776
2777 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01314)
2778 "LDAP: LDAP_OPT_TIMEOUT option not supported by the "
2779 "LDAP library in use. Using LDAPTimeout value as search "
2780 "timeout only." );
2781#endif
2782
2783 return NULL;
2784}
2785
2787 void *dummy,
2788 const char *val)
2789{
2792 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2793 &ldap_module);
2794
2795 /* Negative values mean AP_LDAP_CONNPOOL_INFINITE */
2796 if (val[0] != '-' &&
2798 return "LDAPConnectionPoolTTL has wrong format";
2799 }
2800
2801 if (timeout < 0) {
2802 /* reserve -1 for default value */
2804 }
2805 st->connection_pool_ttl = timeout;
2806 return NULL;
2807}
2808
2810 void *dummy,
2811 const char *val)
2812{
2815 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2816 &ldap_module);
2817 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2818
2819 if (err != NULL) {
2820 return err;
2821 }
2822
2824 return "LDAPRetryDelay has wrong format";
2825 }
2826
2827 if (timeout < 0) {
2828 return "LDAPRetryDelay must be >= 0";
2829 }
2830
2831 st->retry_delay = timeout;
2832 return NULL;
2833}
2834
2836 void *dummy,
2837 const char *val)
2838{
2840 (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2841 &ldap_module);
2842 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2843
2844 if (err != NULL) {
2845 return err;
2846 }
2847
2848 st->retries = atoi(val);
2849 if (st->retries < 0) {
2850 return "LDAPRetries must be >= 0";
2851 }
2852
2853 return NULL;
2854}
2855
2857{
2860
2861 /* Create a per vhost pool for mod_ldap to use, serialized with
2862 * st->mutex (also one per vhost). both are replicated by fork(),
2863 * no shared memory managed by either.
2864 */
2865 apr_pool_create(&st->pool, p);
2866 apr_pool_tag(st->pool, "util_ldap_state");
2867#if APR_HAS_THREADS
2869#endif
2870
2871 st->cache_bytes = 500000;
2872 st->search_cache_ttl = 600 * APR_USEC_PER_SEC; /* 10 minutes */
2873 st->search_cache_size = 1024;
2874 st->compare_cache_ttl = 600 * APR_USEC_PER_SEC; /* 10 minutes */
2875 st->compare_cache_size = 1024;
2876 st->connections = NULL;
2877 st->ssl_supported = 0;
2878 st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2879 st->secure = APR_LDAP_NONE;
2880 st->secure_set = 0;
2881 st->connectionTimeout = 10;
2882 st->opTimeout = apr_pcalloc(p, sizeof(struct timeval));
2883 st->opTimeout->tv_sec = 60;
2884 st->verify_svr_cert = 1;
2885 st->connection_pool_ttl = AP_LDAP_CONNPOOL_DEFAULT; /* no limit */
2886 st->retries = 3;
2887 st->retry_delay = 0; /* no delay */
2888
2889 return st;
2890}
2891
2892/* cache-related settings are not merged here, but in the post_config hook,
2893 * since the cache has not yet sprung to life
2894 */
2896 void *overridesv)
2897{
2901
2902 st->pool = overrides->pool;
2903#if APR_HAS_THREADS
2904 st->mutex = overrides->mutex;
2905#endif
2906
2907 /* The cache settings can not be modified in a
2908 virtual host since all server use the same
2909 shared memory cache. */
2910 st->cache_bytes = base->cache_bytes;
2911 st->search_cache_ttl = base->search_cache_ttl;
2912 st->search_cache_size = base->search_cache_size;
2913 st->compare_cache_ttl = base->compare_cache_ttl;
2914 st->compare_cache_size = base->compare_cache_size;
2915
2916 st->connections = NULL;
2917 st->ssl_supported = 0; /* not known until post-config and re-merged */
2918 st->global_certs = apr_array_append(p, base->global_certs,
2919 overrides->global_certs);
2920 st->secure = (overrides->secure_set == 0) ? base->secure
2921 : overrides->secure;
2922
2923 /* These LDAP connection settings can not be overwritten in
2924 a virtual host. Once set in the base server, they must
2925 remain the same. None of the LDAP SDKs seem to be able
2926 to handle setting the verify_svr_cert flag on a
2927 per-connection basis. The OpenLDAP client appears to be
2928 able to handle the connection timeout per-connection
2929 but the Novell SDK cannot. Allowing the timeout to
2930 be set by each vhost is of little value so rather than
2931 trying to make special exceptions for one LDAP SDK, GLOBAL_ONLY
2932 is being enforced on this setting as well. */
2933 st->connectionTimeout = base->connectionTimeout;
2934 st->opTimeout = base->opTimeout;
2935 st->verify_svr_cert = base->verify_svr_cert;
2936 st->debug_level = base->debug_level;
2937
2938 st->connection_pool_ttl = (overrides->connection_pool_ttl == AP_LDAP_CONNPOOL_DEFAULT) ?
2939 base->connection_pool_ttl : overrides->connection_pool_ttl;
2940
2941 st->retries = base->retries;
2942 st->retry_delay = base->retry_delay;
2943
2944 return st;
2945}
2946
2948{
2949 server_rec *s = data;
2951 s->module_config, &ldap_module);
2952
2953 if (st->ssl_supported) {
2955 }
2956
2957 return APR_SUCCESS;
2958}
2959
2961 apr_pool_t *ptemp)
2962{
2964
2966 APR_LOCK_DEFAULT, 0);
2967 if (result != APR_SUCCESS) {
2968 return result;
2969 }
2970
2971 return OK;
2972}
2973
2975 apr_pool_t *ptemp, server_rec *s)
2976{
2980
2982 ap_get_module_config(s->module_config,
2983 &ldap_module);
2984
2986 int rc;
2987
2988 /* util_ldap_post_config() will be called twice. Don't bother
2989 * going through all of the initialization on the first call
2990 * because it will just be thrown away.*/
2992
2993#if APR_HAS_SHARED_MEMORY
2994 /*
2995 * If we are using shared memory caching and the cache file already
2996 * exists then delete it. Otherwise we are going to run into problems
2997 * creating the shared memory.
2998 */
2999 if (st->cache_file && st->cache_bytes > 0) {
3000 char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
3001 NULL);
3002 apr_file_remove(lck_file, ptemp);
3003 }
3004#endif
3005 return OK;
3006 }
3007
3008#if APR_HAS_SHARED_MEMORY
3009 /*
3010 * initializing cache if we don't already have a shm address
3011 */
3012 if (!st->cache_shm) {
3013#endif
3015 if (result != APR_SUCCESS) {
3017 "LDAP cache: could not create shared memory segment");
3018 return DONE;
3019 }
3020
3021 result = ap_global_mutex_create(&st->util_ldap_cache_lock, NULL,
3023 if (result != APR_SUCCESS) {
3024 return result;
3025 }
3026
3027 /* merge config in all vhost */
3028 s_vhost = s->next;
3029 while (s_vhost) {
3031 ap_get_module_config(s_vhost->module_config,
3032 &ldap_module);
3033 st_vhost->util_ldap_cache = st->util_ldap_cache;
3034 st_vhost->util_ldap_cache_lock = st->util_ldap_cache_lock;
3035#if APR_HAS_SHARED_MEMORY
3036 st_vhost->cache_shm = st->cache_shm;
3037 st_vhost->cache_rmm = st->cache_rmm;
3038 st_vhost->cache_file = st->cache_file;
3040 "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
3041 "for VHOST: %s", st->cache_shm, st->cache_rmm,
3042 s_vhost->server_hostname);
3043#endif
3044 s_vhost = s_vhost->next;
3045 }
3046#if APR_HAS_SHARED_MEMORY
3047 }
3048 else {
3050 "LDAP cache: LDAPSharedCacheSize is zero, disabling "
3051 "shared memory cache");
3052 }
3053#endif
3054
3055 /* log the LDAP SDK used
3056 */
3057 {
3059 apr_ldap_info(p, &(result));
3060 if (result != NULL) {
3061 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(01318) "%s", result->reason);
3062 }
3063 }
3064
3067
3068 /*
3069 * Initialize SSL support, and log the result for the benefit of the admin.
3070 *
3071 * If SSL is not supported it is not necessarily an error, as the
3072 * application may not want to use it.
3073 */
3075 NULL,
3076 0,
3077 &(result_err));
3078 if (APR_SUCCESS == rc) {
3080 (void *)st->global_certs, &(result_err));
3081 }
3082
3083 if (APR_SUCCESS == rc) {
3084 st->ssl_supported = 1;
3086 "LDAP: SSL support available" );
3087 }
3088 else {
3089 st->ssl_supported = 0;
3091 "LDAP: SSL support unavailable%s%s",
3092 result_err ? ": " : "",
3093 result_err ? result_err->reason : "");
3094 }
3095
3096 /* ssl_supported is really a global setting */
3097 s_vhost = s->next;
3098 while (s_vhost) {
3100 ap_get_module_config(s_vhost->module_config,
3101 &ldap_module);
3102
3103 st_vhost->ssl_supported = st->ssl_supported;
3104 s_vhost = s_vhost->next;
3105 }
3106
3107 /* Initialize the rebind callback's cross reference list. */
3108 (void) uldap_rebind_init(p);
3109
3110#ifdef AP_LDAP_OPT_DEBUG
3111 if (st->debug_level > 0) {
3112 result = ldap_set_option(NULL, AP_LDAP_OPT_DEBUG, &st->debug_level);
3113 if (result != LDAP_SUCCESS) {
3115 "LDAP: Could not set the LDAP library debug level to %d:(%d) %s",
3116 st->debug_level, result, ldap_err2string(result));
3117 }
3118 }
3119#endif
3120
3121 return(OK);
3122}
3123
3125{
3127 util_ldap_state_t *st = ap_get_module_config(s->module_config,
3128 &ldap_module);
3129
3130 if (!st->util_ldap_cache_lock) return;
3131
3132 sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
3133 apr_global_mutex_lockfile(st->util_ldap_cache_lock), p);
3134 if (sts != APR_SUCCESS) {
3136 "Failed to initialise global mutex %s in child process",
3138 }
3139}
3140
3141static const command_rec util_ldap_cmds[] = {
3142 AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
3143 NULL, RSRC_CONF,
3144 "Set the size of the shared memory cache (in bytes). Use "
3145 "0 to disable the shared memory cache. (default: 500000)"),
3146
3147 AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
3148 NULL, RSRC_CONF,
3149 "Set the file name for the shared memory cache."),
3150
3151 AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
3152 NULL, RSRC_CONF,
3153 "Set the maximum number of entries that are possible in the "
3154 "LDAP search cache. Use 0 or -1 to disable the search cache "
3155 "(default: 1024)"),
3156
3157 AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
3158 NULL, RSRC_CONF,
3159 "Set the maximum time (in seconds) that an item can be "
3160 "cached in the LDAP search cache. Use 0 for no limit. "
3161 "(default 600)"),
3162
3163 AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
3164 NULL, RSRC_CONF,
3165 "Set the maximum number of entries that are possible "
3166 "in the LDAP compare cache. Use 0 or -1 to disable the compare cache "
3167 "(default: 1024)"),
3168
3169 AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
3170 NULL, RSRC_CONF,
3171 "Set the maximum time (in seconds) that an item is cached "
3172 "in the LDAP operation cache. Use 0 for no limit. "
3173 "(default: 600)"),
3174
3175 AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
3176 NULL, RSRC_CONF,
3177 "Takes three arguments; the first argument is the cert "
3178 "type of the second argument, one of CA_DER, CA_BASE64, "
3179 "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
3180 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
3181 "specifes the file and/or directory containing the trusted CA "
3182 "certificates (and global client certs for Netware) used to "
3183 "validate the LDAP server. The third argument is an optional "
3184 "passphrase if applicable."),
3185
3186 AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
3188 "Takes three arguments: the first argument is the certificate "
3189 "type of the second argument, one of CA_DER, CA_BASE64, "
3190 "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
3191 "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
3192 "specifies the file and/or directory containing the client "
3193 "certificate, or certificate ID used to validate this LDAP "
3194 "client. The third argument is an optional passphrase if "
3195 "applicable."),
3196
3197 AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
3198 NULL, RSRC_CONF,
3199 "Specify the type of security that should be applied to "
3200 "an LDAP connection. One of; NONE, SSL or STARTTLS."),
3201
3202 AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
3203 NULL, RSRC_CONF,
3204 "Set to 'ON' requires that the server certificate be verified"
3205 " before a secure LDAP connection can be establish. Default"
3206 " 'ON'"),
3207
3208 AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
3209 NULL, RSRC_CONF,
3210 "Specify the LDAP socket connection timeout in seconds "
3211 "(default: 10)"),
3212
3215 "Choose whether referrals are chased ['ON'|'OFF'|'DEFAULT']. Default 'ON'"),
3216
3217 AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
3219 "Limit the number of referral hops that LDAP can follow. "
3220 "(Integer value, Consult LDAP SDK documentation for applicability and defaults"),
3221
3222 AP_INIT_TAKE1("LDAPLibraryDebug", util_ldap_set_debug_level,
3223 NULL, RSRC_CONF,
3224 "Enable debugging in LDAP SDK (Default: off, values: SDK specific"),
3225
3227 NULL, RSRC_CONF,
3228 "Specify the LDAP bind/search timeout in seconds "
3229 "(0 = no limit). Default: 60"),
3230 AP_INIT_TAKE1("LDAPConnectionPoolTTL", util_ldap_set_conn_ttl,
3231 NULL, RSRC_CONF,
3232 "Specify the maximum amount of time a bound connection can sit "
3233 "idle and still be considered valid for reuse"
3234 "(0 = no pool, -1 = no limit, n = time in seconds). Default: -1"),
3235 AP_INIT_TAKE1("LDAPRetries", util_ldap_set_retries,
3236 NULL, RSRC_CONF,
3237 "Specify the number of times a failed LDAP operation should be retried "
3238 "(0 = no retries). Default: 3"),
3239 AP_INIT_TAKE1("LDAPRetryDelay", util_ldap_set_retry_delay,
3240 NULL, RSRC_CONF,
3241 "Specify the delay between retries of a failed LDAP operation "
3242 "(0 = no delay). Default: 0"),
3243
3244
3245 {NULL}
3246};
3247
3266
3269 util_ldap_create_dir_config, /* create dir config */
3270 NULL, /* merge dir config */
3271 util_ldap_create_config, /* create server config */
3272 util_ldap_merge_config, /* merge server config */
3273 util_ldap_cmds, /* command table */
3274 util_ldap_register_hooks, /* set up request processing hooks */
3275};
const char apr_size_t len
Definition ap_regex.h:187
char * strdup(const char *str)
Apache LDAP library.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
apr_array_append(apr_pool_t *p, const apr_array_header_t *first, const apr_array_header_t *second)
Definition apr_tables.c:213
apr_array_copy_hdr(apr_pool_t *p, const apr_array_header_t *arr)
Definition apr_tables.c:199
static apr_pool_t * pconf
Definition event.c:441
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
char * ap_server_root_relative(apr_pool_t *p, const char *fname)
Definition config.c:1594
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
void ap_hook_pre_config(ap_HOOK_pre_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:91
request_rec int int apr_table_t const char * path
request_rec * r
void ap_hook_child_init(ap_HOOK_child_init_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:167
#define AP_INIT_TAKE23(directive, func, mconfig, where, help)
#define DOCTYPE_HTML_3_2
Definition httpd.h:238
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DONE
Definition httpd.h:458
#define AP_SQ_MS_CREATE_PRE_CONFIG
Definition http_core.h:1047
int ap_state_query(int query_code)
Definition core.c:5378
#define AP_SQ_MAIN_STATE
Definition http_core.h:1030
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_NOTICE
Definition http_log.h:69
#define APLOG_TRACE4
Definition http_log.h:75
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_TRACE2
Definition http_log.h:73
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_TRACE5
Definition http_log.h:76
#define APLOG_DEBUG
Definition http_log.h:71
apr_status_t ap_global_mutex_create(apr_global_mutex_t **mutex, const char **name, const char *type, const char *instance_id, server_rec *server, apr_pool_t *pool, apr_int32_t options)
Definition util_mutex.c:407
apr_status_t ap_mutex_register(apr_pool_t *pconf, const char *type, const char *default_dir, apr_lockmech_e default_mech, apr_int32_t options)
Definition util_mutex.c:254
static APR_INLINE int ap_rputs(const char *str, request_rec *r)
void ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
const char apr_port_t port
Definition http_vhost.h:125
void * dummy
Definition http_vhost.h:62
const char * host
Definition http_vhost.h:124
void const char * arg
Definition http_vhost.h:63
#define APR_EGENERAL
Definition apr_errno.h:313
unsigned int count
Definition apr_md5.h:152
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
apr_pool_t const char * params
Definition apr_dbd.h:141
const char * src
Definition apr_encode.h:167
const char * url
Definition apr_escape.h:120
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
const char apr_hash_t ** values
#define APR_REGISTER_OPTIONAL_FN(name)
apr_redis_t * rc
Definition apr_redis.h:173
#define RSRC_CONF
#define OR_AUTHCFG
#define M_GET
Definition httpd.h:592
#define STANDARD20_MODULE_STUFF
#define ap_assert(exp)
Definition httpd.h:2271
apr_status_t ap_timeout_parameter_parse(const char *timeout_parameter, apr_interval_time_t *timeout, const char *default_time_unit)
Definition util.c:2622
#define GLOBAL_ONLY
const char * ap_check_cmd_context(cmd_parms *cmd, unsigned forbidden)
Definition core.c:1301
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char * value
Definition apr_env.h:51
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
const char apr_file_t * file
int type
#define APR_FINFO_MIN
apr_array_header_t ** result
int strcasecmp(const char *a, const char *b)
apr_byte_t ttl
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
@ APR_LOCK_DEFAULT
const void apr_size_t bytes
Definition apr_random.h:91
const char * s
Definition apr_strings.h:95
int nelts
Definition apr_tables.h:122
const char const char * password
apr_int32_t apr_int32_t apr_int32_t err
apr_cmdtype_e cmd
#define APR_TIME_T_FMT
Definition apr_time.h:52
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
#define APR_USEC_PER_SEC
Definition apr_time.h:60
apr_int64_t apr_time_t
Definition apr_time.h:45
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
struct param_s param
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
apr_pool_t * pool
struct param_s * next
Definition mod_mime.c:99
A structure that represents the current request.
Definition httpd.h:845
char * user
Definition httpd.h:1005
int header_only
Definition httpd.h:875
const char * handler
Definition httpd.h:994
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
apr_time_t request_time
Definition httpd.h:886
apr_int64_t allowed
Definition httpd.h:922
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
A structure to store information for each virtual server.
Definition httpd.h:1322
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
static apr_time_t now
Definition testtime.c:33
#define str
#define AP_LDAP_CHASEREFERRALS_OFF
Definition util_ldap.c:64
static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
Definition util_ldap.c:2974
static const char * util_ldap_set_retries(cmd_parms *cmd, void *dummy, const char *val)
Definition util_ldap.c:2835
static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
Definition util_ldap.c:3124
static void * util_ldap_merge_config(apr_pool_t *p, void *basev, void *overridesv)
Definition util_ldap.c:2895
static int uldap_ld_errno(util_ldap_connection_t *ldc)
Definition util_ldap.c:532
#define AP_LDAP_CHASEREFERRALS_SDKDEFAULT
Definition util_ldap.c:63
#define APR_LDAP_SIZELIMIT
Definition util_ldap.c:51
#define AP_LDAP_CONNPOOL_DEFAULT
Definition util_ldap.c:67
static const char * util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy, const char *mode)
Definition util_ldap.c:2575
#define AP_LDAP_CHASEREFERRALS_ON
Definition util_ldap.c:65
#define AP_LDAP_CONNPOOL_INFINITE
Definition util_ldap.c:68
static int uldap_cache_check_subgroups(request_rec *r, util_ldap_connection_t *ldc, const char *url, const char *dn, const char *attrib, const char *value, char **subgroupAttrs, apr_array_header_t *subgroupclasses, int cur_subgroup_depth, int max_subgroup_depth)
Definition util_ldap.c:1453
static const command_rec util_ldap_cmds[]
Definition util_ldap.c:3141
static apr_status_t util_ldap_cleanup_module(void *data)
Definition util_ldap.c:2947
static int compare_client_certs(apr_array_header_t *srcs, apr_array_header_t *dests)
Definition util_ldap.c:697
static const char * ldap_cache_mutex_type
Definition util_ldap.c:75
static apr_status_t uldap_connection_unbind(void *param)
Definition util_ldap.c:225
static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, const char *url, const char *dn, const char *reqdn, int compare_dn_on_server)
Definition util_ldap.c:946
static void * util_ldap_create_dir_config(apr_pool_t *p, char *d)
Definition util_ldap.c:2725
static const char * util_ldap_set_debug_level(cmd_parms *cmd, void *config, const char *arg)
Definition util_ldap.c:2684
static const char * util_ldap_set_verify_srv_cert(cmd_parms *cmd, void *dummy, int mode)
Definition util_ldap.c:2605
static const char * util_ldap_set_referral_hop_limit(cmd_parms *cmd, void *config, const char *hop_limit)
Definition util_ldap.c:2706
static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc, const char *url, const char *dn, const char *attrib, const char *value)
Definition util_ldap.c:1093
static APR_INLINE apr_status_t ldap_cache_lock(util_ldap_state_t *st, request_rec *r)
Definition util_ldap.c:111
static void util_ldap_register_hooks(apr_pool_t *p)
Definition util_ldap.c:3248
static const char * util_ldap_set_retry_delay(cmd_parms *cmd, void *dummy, const char *val)
Definition util_ldap.c:2809
static void * util_ldap_create_config(apr_pool_t *p, server_rec *s)
Definition util_ldap.c:2856
static const char * util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
Definition util_ldap.c:2241
static void uldap_connection_close(util_ldap_connection_t *ldc)
Definition util_ldap.c:197
static int util_ldap_handler(request_rec *r)
Definition util_ldap.c:158
#define AP_LDAP_HOPLIMIT_UNSET
Definition util_ldap.c:62
static int uldap_ssl_supported(request_rec *r)
Definition util_ldap.c:2180
static int util_ldap_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
Definition util_ldap.c:2960
static int uldap_connection_init(request_rec *r, util_ldap_connection_t *ldc)
Definition util_ldap.c:308
static const char * util_ldap_set_conn_ttl(cmd_parms *cmd, void *dummy, const char *val)
Definition util_ldap.c:2786
static void util_ldap_strdup(char **str, const char *newstr)
Definition util_ldap.c:134
static const char * util_ldap_set_trusted_client_cert(cmd_parms *cmd, void *config, const char *type, const char *file, const char *password)
Definition util_ldap.c:2494
static int uldap_simple_bind(util_ldap_connection_t *ldc, char *binddn, char *bindpw, struct timeval *timeout)
Definition util_ldap.c:551
#define uldap_rebind_add(ldc)
Definition util_ldap.c:106
static util_ldap_connection_t * uldap_connection_find(request_rec *r, const char *host, int port, const char *binddn, const char *bindpw, deref_options deref, int secure)
Definition util_ldap.c:742
static const char * util_ldap_set_chase_referrals(cmd_parms *cmd, void *config, const char *arg)
Definition util_ldap.c:2659
static int util_ldap_parse_cert_type(const char *type)
Definition util_ldap.c:2341
static const char * util_ldap_set_op_timeout(cmd_parms *cmd, void *dummy, const char *val)
Definition util_ldap.c:2738
static int uldap_connection_open(request_rec *r, util_ldap_connection_t *ldc)
Definition util_ldap.c:588
static util_compare_subgroup_t * uldap_get_subgroups(request_rec *r, util_ldap_connection_t *ldc, const char *url, const char *dn, char **subgroupAttrs, apr_array_header_t *subgroupclasses)
Definition util_ldap.c:1271
static const char * util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size)
Definition util_ldap.c:2307
static const char * util_ldap_set_cache_file(cmd_parms *cmd, void *dummy, const char *file)
Definition util_ldap.c:2215
static const char * util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes)
Definition util_ldap.c:2193
#define uldap_rebind_init(p)
Definition util_ldap.c:105
static const char * util_ldap_set_connection_timeout(cmd_parms *cmd, void *dummy, const char *ttl)
Definition util_ldap.c:2628
static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc, const char *url, const char *basedn, int scope, char **attrs, const char *filter, const char *bindpw, const char **binddn, const char ***retvals)
Definition util_ldap.c:1697
static const char * util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
Definition util_ldap.c:2286
static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc, const char *url, const char *basedn, int scope, char **attrs, const char *filter, const char **binddn, const char ***retvals)
Definition util_ldap.c:1974
static const char * util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size)
Definition util_ldap.c:2262
static APR_INLINE apr_status_t ldap_cache_unlock(util_ldap_state_t *st, request_rec *r)
Definition util_ldap.c:122
static const char * util_ldap_set_trusted_global_cert(cmd_parms *cmd, void *dummy, const char *type, const char *file, const char *password)
Definition util_ldap.c:2420
Apache LDAP library.
This switches LDAP support on or off.
Apache Mutex support library.
IN ULONG IN INT timeout