]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap.c
* modules/ldap/util_ldap.c (LDAP_CACHE_LOCK, LDAP_CACHE_UNLOCK):
[apache] / modules / ldap / util_ldap.c
1 /* Copyright 2001-2005 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * 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_ldap.h"
32 #include "util_ldap_cache.h"
33
34 #include <apr_strings.h>
35
36 #if APR_HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #if !APR_HAS_LDAP
41 #error mod_ldap requires APR-util to have LDAP support built in
42 #endif
43
44 #ifdef AP_NEED_SET_MUTEX_PERMS
45 #include "unixd.h"
46 #endif
47
48     /* defines for certificate file types
49     */
50 #define LDAP_CA_TYPE_UNKNOWN            0
51 #define LDAP_CA_TYPE_DER                1
52 #define LDAP_CA_TYPE_BASE64             2
53 #define LDAP_CA_TYPE_CERT7_DB           3
54
55
56 module AP_MODULE_DECLARE_DATA ldap_module;
57
58 static int util_ldap_handler(request_rec *r);
59 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s);
60
61
62 /*
63  * Some definitions to help between various versions of apache.
64  */
65
66 #ifndef DOCTYPE_HTML_2_0
67 #define DOCTYPE_HTML_2_0  "<!DOCTYPE HTML PUBLIC \"-//IETF//" \
68                           "DTD HTML 2.0//EN\">\n"
69 #endif
70
71 #ifndef DOCTYPE_HTML_3_2
72 #define DOCTYPE_HTML_3_2  "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
73                           "DTD HTML 3.2 Final//EN\">\n"
74 #endif
75
76 #ifndef DOCTYPE_HTML_4_0S
77 #define DOCTYPE_HTML_4_0S "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
78                           "DTD HTML 4.0//EN\"\n" \
79                           "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
80 #endif
81
82 #ifndef DOCTYPE_HTML_4_0T
83 #define DOCTYPE_HTML_4_0T "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
84                           "DTD HTML 4.0 Transitional//EN\"\n" \
85                           "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
86 #endif
87
88 #ifndef DOCTYPE_HTML_4_0F
89 #define DOCTYPE_HTML_4_0F "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
90                           "DTD HTML 4.0 Frameset//EN\"\n" \
91                           "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n"
92 #endif
93
94 #define LDAP_CACHE_LOCK() do {                                  \
95     if (st->util_ldap_cache_lock)                               \
96         apr_global_mutex_lock(st->util_ldap_cache_lock);        \
97 } while (0)
98
99 #define LDAP_CACHE_UNLOCK() do {                                \
100     if (st->util_ldap_cache_lock)                               \
101         apr_global_mutex_unlock(st->util_ldap_cache_lock);      \
102 } while (0)
103
104 static void util_ldap_strdup (char **str, const char *newstr)
105 {
106     if (*str) {
107         free(*str);
108         *str = NULL;
109     }
110
111     if (newstr) {
112         *str = calloc(1, strlen(newstr)+1);
113         strcpy (*str, newstr);
114     }
115 }
116
117 /*
118  * Status Handler
119  * --------------
120  *
121  * This handler generates a status page about the current performance of
122  * the LDAP cache. It is enabled as follows:
123  *
124  * <Location /ldap-status>
125  *   SetHandler ldap-status
126  * </Location>
127  *
128  */
129 static int util_ldap_handler(request_rec *r)
130 {
131     util_ldap_state_t *st = (util_ldap_state_t *)
132                             ap_get_module_config(r->server->module_config, 
133                                                  &ldap_module);
134
135     r->allowed |= (1 << M_GET);
136     if (r->method_number != M_GET)
137         return DECLINED;
138
139     if (strcmp(r->handler, "ldap-status")) {
140         return DECLINED;
141     }
142
143     r->content_type = "text/html";
144     if (r->header_only)
145         return OK;
146
147     ap_rputs(DOCTYPE_HTML_3_2
148              "<html><head><title>LDAP Cache Information</title></head>\n", r);
149     ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
150              "</h1>\n", r);
151
152     util_ald_cache_display(r, st);
153
154     return OK;
155 }
156
157 /* ------------------------------------------------------------------ */
158
159
160 /*
161  * Closes an LDAP connection by unlocking it. The next time
162  * uldap_connection_find() is called this connection will be
163  * available for reuse.
164  */
165 static void uldap_connection_close(util_ldap_connection_t *ldc)
166 {
167
168     /*
169      * QUESTION:
170      *
171      * Is it safe leaving bound connections floating around between the
172      * different modules? Keeping the user bound is a performance boost,
173      * but it is also a potential security problem - maybe.
174      *
175      * For now we unbind the user when we finish with a connection, but
176      * we don't have to...
177      */
178
179     /* mark our connection as available for reuse */
180
181 #if APR_HAS_THREADS
182     apr_thread_mutex_unlock(ldc->lock);
183 #endif
184 }
185
186
187 /*
188  * Destroys an LDAP connection by unbinding and closing the connection to
189  * the LDAP server. It is used to bring the connection back to a known
190  * state after an error, and during pool cleanup.
191  */
192 static apr_status_t uldap_connection_unbind(void *param)
193 {
194     util_ldap_connection_t *ldc = param;
195
196     if (ldc) {
197         if (ldc->ldap) {
198             ldap_unbind_s(ldc->ldap);
199             ldc->ldap = NULL;
200         }
201         ldc->bound = 0;
202     }
203
204     return APR_SUCCESS;
205 }
206
207
208 /*
209  * Clean up an LDAP connection by unbinding and unlocking the connection.
210  * This function is registered with the pool cleanup function - causing
211  * the LDAP connections to be shut down cleanly on graceful restart.
212  */
213 static apr_status_t uldap_connection_cleanup(void *param)
214 {
215     util_ldap_connection_t *ldc = param;
216
217     if (ldc) {
218
219         /* unbind and disconnect from the LDAP server */
220         uldap_connection_unbind(ldc);
221
222         /* free the username and password */
223         if (ldc->bindpw) {
224             free((void*)ldc->bindpw);
225         }
226         if (ldc->binddn) {
227             free((void*)ldc->binddn);
228         }
229
230         /* unlock this entry */
231         uldap_connection_close(ldc);
232     
233     }
234
235     return APR_SUCCESS;
236 }
237
238
239 /*
240  * Connect to the LDAP server and binds. Does not connect if already
241  * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
242  *
243  * Returns LDAP_SUCCESS on success; and an error code on failure
244  */
245 static int uldap_connection_open(request_rec *r, 
246                                  util_ldap_connection_t *ldc)
247 {
248     int rc = 0;
249     int failures = 0;
250     int version  = LDAP_VERSION3;
251     apr_ldap_err_t *result = NULL;
252     struct timeval timeOut = {10,0};    /* 10 second connection timeout */
253     util_ldap_state_t *st = 
254         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
255         &ldap_module);
256
257     /* sanity check for NULL */
258     if (!ldc) {
259         return -1;
260     }
261
262     /* If the connection is already bound, return
263     */
264     if (ldc->bound)
265     {
266         ldc->reason = "LDAP: connection open successful (already bound)";
267         return LDAP_SUCCESS;
268     }
269
270     /* create the ldap session handle
271     */
272     if (NULL == ldc->ldap)
273     {
274         /* Since the host will include a port if the default port is not used,
275          * always specify the default ports for the port parameter.  This will
276          * allow a host string that contains multiple hosts the ability to mix
277          * some hosts with ports and some without. All hosts which do not
278          * specify a port will use the default port.
279          */
280         apr_ldap_init(ldc->pool, &(ldc->ldap),
281                       ldc->host,
282                       APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
283                       APR_LDAP_NONE,
284                       &(result));
285
286
287         if (result != NULL && result->rc) {
288             ldc->reason = result->reason;
289         }
290
291         if (NULL == ldc->ldap)
292         {
293             ldc->bound = 0;
294             if (NULL == ldc->reason) {
295                 ldc->reason = "LDAP: ldap initialization failed";
296             }
297             else {
298                 ldc->reason = result->reason;
299             }
300             return(result->rc);
301         }
302
303         /* set client certificates */
304         if (!apr_is_empty_array(ldc->client_certs)) {
305             apr_ldap_set_option(ldc->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
306                                 ldc->client_certs, &(result));
307             if (LDAP_SUCCESS != result->rc) {
308                 ldap_unbind_s(ldc->ldap);
309                 ldc->ldap = NULL;
310                 ldc->bound = 0;
311                 ldc->reason = result->reason;
312                 return(result->rc);
313             }
314         }
315
316         /* switch on SSL/TLS */
317         if (APR_LDAP_NONE != ldc->secure) {
318             apr_ldap_set_option(ldc->pool, ldc->ldap, 
319                                 APR_LDAP_OPT_TLS, &ldc->secure, &(result));
320             if (LDAP_SUCCESS != result->rc) {
321                 ldap_unbind_s(ldc->ldap);
322                 ldc->ldap = NULL;
323                 ldc->bound = 0;
324                 ldc->reason = result->reason;
325                 return(result->rc);
326             }
327         }
328
329         /* Set the alias dereferencing option */
330         ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
331
332         /* always default to LDAP V3 */
333         ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
334
335 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
336 #ifdef APR_LDAP_OPT_VERIFY_CERT
337         apr_ldap_set_option(ldc->pool, ldc->ldap, 
338                             APR_LDAP_OPT_VERIFY_CERT, &(st->verify_svr_cert), &(result));
339 #else
340 #if defined(LDAPSSL_VERIFY_SERVER)
341         if (st->verify_svr_cert) {
342             result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
343         }
344         else {
345             result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
346         }
347 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
348                 /* This is not a per-connection setting so just pass NULL for the
349                    Ldap connection handle */
350         if (st->verify_svr_cert) {
351                         int i = LDAP_OPT_X_TLS_DEMAND;
352                         result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
353         }
354         else {
355                         int i = LDAP_OPT_X_TLS_NEVER;
356                         result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
357         }
358 #endif
359 #endif
360
361 #ifdef LDAP_OPT_NETWORK_TIMEOUT
362         if (st->connectionTimeout > 0) {
363             timeOut.tv_sec = st->connectionTimeout;
364         }
365     
366         if (st->connectionTimeout >= 0) {
367             rc = apr_ldap_set_option(ldc->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
368                                      (void *)&timeOut, &(result));
369             if (APR_SUCCESS != rc) {
370                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
371                                  "LDAP: Could not set the connection timeout");
372             }
373         }
374 #endif
375
376     
377     }
378
379
380     /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
381      * returned.  Break out of the loop on Success or any other error.
382      *
383      * NOTE: Looping is probably not a great idea. If the server isn't 
384      * responding the chances it will respond after a few tries are poor.
385      * However, the original code looped and it only happens on
386      * the error condition.
387       */
388     for (failures=0; failures<10; failures++)
389     {
390         rc = ldap_simple_bind_s(ldc->ldap,
391                                 (char *)ldc->binddn,
392                                 (char *)ldc->bindpw);
393         if (LDAP_SERVER_DOWN != rc) {
394             break;
395         }
396     }
397
398     /* free the handle if there was an error
399     */
400     if (LDAP_SUCCESS != rc)
401     {
402         ldap_unbind_s(ldc->ldap);
403         ldc->ldap = NULL;
404         ldc->bound = 0;
405         ldc->reason = "LDAP: ldap_simple_bind_s() failed";
406     }
407     else {
408         ldc->bound = 1;
409         ldc->reason = "LDAP: connection open successful";
410     }
411
412     return(rc);
413 }
414
415
416 /*
417  * Compare client certificate arrays.
418  *
419  * Returns 1 on compare failure, 0 otherwise.
420  */
421 static int compare_client_certs(apr_array_header_t *srcs, 
422                                 apr_array_header_t *dests)
423 {
424     int i = 0;
425     struct apr_ldap_opt_tls_cert_t *src, *dest;
426
427     /* arrays both NULL? if so, then equal */
428     if (srcs == NULL && dests == NULL) {
429         return 0;
430     }
431
432     /* arrays different length or either NULL? If so, then not equal */
433     if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
434         return 1;
435     }
436
437     /* run an actual comparison */
438     src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
439     dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
440     for (i = 0; i < srcs->nelts; i++) {
441         if (strcmp(src[i].path, dest[i].path) ||
442             strcmp(src[i].password, dest[i].password) ||
443             src[i].type != dest[i].type) {
444             return 1;
445         }
446     }
447
448     /* if we got here, the cert arrays were identical */
449     return 0;
450
451 }
452
453
454 /*
455  * Find an existing ldap connection struct that matches the
456  * provided ldap connection parameters.
457  *
458  * If not found in the cache, a new ldc structure will be allocated 
459  * from st->pool and returned to the caller.  If found in the cache, 
460  * a pointer to the existing ldc structure will be returned.
461  */
462 static util_ldap_connection_t * 
463             uldap_connection_find(request_rec *r,
464                                   const char *host, int port,
465                                   const char *binddn, const char *bindpw,
466                                   deref_options deref, int secure) 
467 {
468     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
469     int secureflag = secure;
470
471     util_ldap_state_t *st = 
472         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
473         &ldap_module);
474
475
476 #if APR_HAS_THREADS
477     /* mutex lock this function */
478     if (!st->mutex) {
479         apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, 
480                                 st->pool);
481     }
482     apr_thread_mutex_lock(st->mutex);
483 #endif
484
485     if (secure < APR_LDAP_NONE) {
486         secureflag = st->secure;
487     }
488
489     /* Search for an exact connection match in the list that is not
490      * being used.
491      */
492     for (l=st->connections,p=NULL; l; l=l->next) {
493 #if APR_HAS_THREADS
494         if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
495 #endif
496         if (   (l->port == port) && (strcmp(l->host, host) == 0) 
497             && ((!l->binddn && !binddn) || (l->binddn && binddn 
498                                              && !strcmp(l->binddn, binddn))) 
499             && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw 
500                                              && !strcmp(l->bindpw, bindpw))) 
501             && (l->deref == deref) && (l->secure == secureflag) 
502             && !compare_client_certs(st->client_certs, l->client_certs)) 
503         {
504             break;
505         }
506 #if APR_HAS_THREADS
507             /* If this connection didn't match the criteria, then we
508              * need to unlock the mutex so it is available to be reused.
509              */
510             apr_thread_mutex_unlock(l->lock);
511         }
512 #endif
513         p = l;
514     }
515
516     /* If nothing found, search again, but we don't care about the
517      * binddn and bindpw this time.
518      */
519     if (!l) {
520         for (l=st->connections,p=NULL; l; l=l->next) {
521 #if APR_HAS_THREADS
522             if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
523
524 #endif
525             if ((l->port == port) && (strcmp(l->host, host) == 0) && 
526                 (l->deref == deref) && (l->secure == secureflag) &&
527                 !compare_client_certs(st->client_certs, l->client_certs)) 
528             {
529                 /* the bind credentials have changed */
530                 l->bound = 0;
531                 util_ldap_strdup((char**)&(l->binddn), binddn);
532                 util_ldap_strdup((char**)&(l->bindpw), bindpw);
533                 break;
534             }
535 #if APR_HAS_THREADS
536                 /* If this connection didn't match the criteria, then we
537                  * need to unlock the mutex so it is available to be reused.
538                  */
539                 apr_thread_mutex_unlock(l->lock);
540             }
541 #endif
542             p = l;
543         }
544     }
545
546 /* artificially disable cache */
547 /* l = NULL; */
548
549     /* If no connection what found after the second search, we
550      * must create one.
551      */
552     if (!l) {
553
554         /* 
555          * Add the new connection entry to the linked list. Note that we
556          * don't actually establish an LDAP connection yet; that happens
557          * the first time authentication is requested.
558          */
559         /* create the details to the pool in st */
560         l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
561 #if APR_HAS_THREADS
562         apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
563         apr_thread_mutex_lock(l->lock);
564 #endif
565         l->pool = st->pool;
566         l->bound = 0;
567         l->host = apr_pstrdup(st->pool, host);
568         l->port = port;
569         l->deref = deref;
570         util_ldap_strdup((char**)&(l->binddn), binddn);
571         util_ldap_strdup((char**)&(l->bindpw), bindpw);
572
573         /* The security mode after parsing the URL will always be either
574          * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
575          * If the security setting is NONE, override it to the security
576          * setting optionally supplied by the admin using LDAPTrustedMode
577          */
578         l->secure = secureflag;
579
580         /* save away a copy of the client cert list that is presently valid */
581         l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
582
583         /* add the cleanup to the pool */
584         apr_pool_cleanup_register(l->pool, l,
585                                   uldap_connection_cleanup,
586                                   apr_pool_cleanup_null);
587
588         if (p) {
589             p->next = l;
590         }
591         else {
592             st->connections = l;
593         }
594     }
595
596 #if APR_HAS_THREADS
597     apr_thread_mutex_unlock(st->mutex);
598 #endif
599     return l;
600 }
601
602 /* ------------------------------------------------------------------ */
603
604 /*
605  * Compares two DNs to see if they're equal. The only way to do this correctly
606  * is to search for the dn and then do ldap_get_dn() on the result. This should
607  * match the initial dn, since it would have been also retrieved with 
608  * ldap_get_dn(). This is expensive, so if the configuration value 
609  * compare_dn_on_server is false, just does an ordinary strcmp.
610  *
611  * The lock for the ldap cache should already be acquired.
612  */
613 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, 
614                                  const char *url, const char *dn, 
615                                  const char *reqdn, int compare_dn_on_server)
616 {
617     int result = 0;
618     util_url_node_t *curl; 
619     util_url_node_t curnode;
620     util_dn_compare_node_t *node;
621     util_dn_compare_node_t newnode;
622     int failures = 0;
623     LDAPMessage *res, *entry;
624     char *searchdn;
625
626     util_ldap_state_t *st = (util_ldap_state_t *)
627                             ap_get_module_config(r->server->module_config, 
628                                                  &ldap_module);
629
630     /* get cache entry (or create one) */
631     LDAP_CACHE_LOCK();
632
633     curnode.url = url;
634     curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
635     if (curl == NULL) {
636         curl = util_ald_create_caches(st, url);
637     }
638     LDAP_CACHE_UNLOCK();
639
640     /* a simple compare? */
641     if (!compare_dn_on_server) {
642         /* unlock this read lock */
643         if (strcmp(dn, reqdn)) {
644             ldc->reason = "DN Comparison FALSE (direct strcmp())";
645             return LDAP_COMPARE_FALSE;
646         }
647         else {
648             ldc->reason = "DN Comparison TRUE (direct strcmp())";
649             return LDAP_COMPARE_TRUE;
650         }
651     }
652
653     if (curl) {
654         /* no - it's a server side compare */
655         LDAP_CACHE_LOCK();
656     
657         /* is it in the compare cache? */
658         newnode.reqdn = (char *)reqdn;
659         node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
660         if (node != NULL) {
661             /* If it's in the cache, it's good */
662             /* unlock this read lock */
663             LDAP_CACHE_UNLOCK();
664             ldc->reason = "DN Comparison TRUE (cached)";
665             return LDAP_COMPARE_TRUE;
666         }
667     
668         /* unlock this read lock */
669         LDAP_CACHE_UNLOCK();
670     }
671
672 start_over:
673     if (failures++ > 10) {
674         /* too many failures */
675         return result;
676     }
677
678     /* make a server connection */
679     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
680         /* connect to server failed */
681         return result;
682     }
683
684     /* search for reqdn */
685     if ((result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
686                                     "(objectclass=*)", NULL, 1, 
687                                     NULL, NULL, NULL, -1, &res)) 
688             == LDAP_SERVER_DOWN) 
689     {
690         ldc->reason = "DN Comparison ldap_search_ext_s() "
691                       "failed with server down";
692         uldap_connection_unbind(ldc);
693         goto start_over;
694     }
695     if (result != LDAP_SUCCESS) {
696         /* search for reqdn failed - no match */
697         ldc->reason = "DN Comparison ldap_search_ext_s() failed";
698         return result;
699     }
700
701     entry = ldap_first_entry(ldc->ldap, res);
702     searchdn = ldap_get_dn(ldc->ldap, entry);
703
704     ldap_msgfree(res);
705     if (strcmp(dn, searchdn) != 0) {
706         /* compare unsuccessful */
707         ldc->reason = "DN Comparison FALSE (checked on server)";
708         result = LDAP_COMPARE_FALSE;
709     }
710     else {
711         if (curl) {
712             /* compare successful - add to the compare cache */
713             LDAP_CACHE_LOCK();
714             newnode.reqdn = (char *)reqdn;
715             newnode.dn = (char *)dn;
716             
717             node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
718             if (   (node == NULL)
719                 || (strcmp(reqdn, node->reqdn) != 0) 
720                 || (strcmp(dn, node->dn) != 0)) 
721             {
722                 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
723             }
724             LDAP_CACHE_UNLOCK();
725         }
726         ldc->reason = "DN Comparison TRUE (checked on server)";
727         result = LDAP_COMPARE_TRUE;
728     }
729     ldap_memfree(searchdn);
730     return result;
731
732 }
733
734 /*
735  * Does an generic ldap_compare operation. It accepts a cache that it will use
736  * to lookup the compare in the cache. We cache two kinds of compares 
737  * (require group compares) and (require user compares). Each compare has a different
738  * cache node: require group includes the DN; require user does not because the
739  * require user cache is owned by the 
740  *
741  */
742 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
743                                const char *url, const char *dn,
744                                const char *attrib, const char *value)
745 {
746     int result = 0;
747     util_url_node_t *curl; 
748     util_url_node_t curnode;
749     util_compare_node_t *compare_nodep;
750     util_compare_node_t the_compare_node;
751     apr_time_t curtime = 0; /* silence gcc -Wall */
752     int failures = 0;
753
754     util_ldap_state_t *st = (util_ldap_state_t *)
755                             ap_get_module_config(r->server->module_config,
756                                                  &ldap_module);
757
758     /* get cache entry (or create one) */
759     LDAP_CACHE_LOCK();
760     curnode.url = url;
761     curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
762     if (curl == NULL) {
763         curl = util_ald_create_caches(st, url);
764     }
765     LDAP_CACHE_UNLOCK();
766
767     if (curl) {
768         /* make a comparison to the cache */
769         LDAP_CACHE_LOCK();
770         curtime = apr_time_now();
771     
772         the_compare_node.dn = (char *)dn;
773         the_compare_node.attrib = (char *)attrib;
774         the_compare_node.value = (char *)value;
775         the_compare_node.result = 0;
776     
777         compare_nodep = util_ald_cache_fetch(curl->compare_cache, 
778                                              &the_compare_node);
779     
780         if (compare_nodep != NULL) {
781             /* found it... */
782             if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
783                 /* ...but it is too old */
784                 util_ald_cache_remove(curl->compare_cache, compare_nodep);
785             }
786             else {
787                 /* ...and it is good */
788                 /* unlock this read lock */
789                 LDAP_CACHE_UNLOCK();
790                 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
791                     ldc->reason = "Comparison true (cached)";
792                     return compare_nodep->result;
793                 }
794                 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
795                     ldc->reason = "Comparison false (cached)";
796                     return compare_nodep->result;
797                 }
798                 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
799                     ldc->reason = "Comparison no such attribute (cached)";
800                     return compare_nodep->result;
801                 }
802                 else {
803                     ldc->reason = "Comparison undefined (cached)";
804                     return compare_nodep->result;
805                 }
806             }
807         }
808         /* unlock this read lock */
809         LDAP_CACHE_UNLOCK();
810     }
811
812 start_over:
813     if (failures++ > 10) {
814         /* too many failures */
815         return result;
816     }
817     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
818         /* connect failed */
819         return result;
820     }
821
822     if ((result = ldap_compare_s(ldc->ldap,
823                                  (char *)dn,
824                                  (char *)attrib,
825                                  (char *)value))
826                                                == LDAP_SERVER_DOWN) { 
827         /* connection failed - try again */
828         ldc->reason = "ldap_compare_s() failed with server down";
829         uldap_connection_unbind(ldc);
830         goto start_over;
831     }
832
833     ldc->reason = "Comparison complete";
834     if ((LDAP_COMPARE_TRUE == result) || 
835         (LDAP_COMPARE_FALSE == result) ||
836         (LDAP_NO_SUCH_ATTRIBUTE == result)) {
837         if (curl) {
838             /* compare completed; caching result */
839             LDAP_CACHE_LOCK();
840             the_compare_node.lastcompare = curtime;
841             the_compare_node.result = result;
842
843             /* If the node doesn't exist then insert it, otherwise just update
844              * it with the last results 
845              */
846             compare_nodep = util_ald_cache_fetch(curl->compare_cache, 
847                                                  &the_compare_node);
848             if (   (compare_nodep == NULL) 
849                 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0) 
850                 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0) 
851                 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
852             {
853                 util_ald_cache_insert(curl->compare_cache, &the_compare_node);
854             }
855             else {
856                 compare_nodep->lastcompare = curtime;
857                 compare_nodep->result = result;
858             }
859             LDAP_CACHE_UNLOCK();
860         }
861         if (LDAP_COMPARE_TRUE == result) {
862             ldc->reason = "Comparison true (adding to cache)";
863             return LDAP_COMPARE_TRUE;
864         }
865         else if (LDAP_COMPARE_FALSE == result) {
866             ldc->reason = "Comparison false (adding to cache)";
867             return LDAP_COMPARE_FALSE;
868         }
869         else {
870             ldc->reason = "Comparison no such attribute (adding to cache)";
871             return LDAP_NO_SUCH_ATTRIBUTE;
872         }
873     }
874     return result;
875 }
876
877 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
878                                    const char *url, const char *basedn, 
879                                    int scope, char **attrs, const char *filter,
880                                    const char *bindpw, const char **binddn, 
881                                    const char ***retvals)
882 {
883     const char **vals = NULL;
884     int numvals = 0;
885     int result = 0;
886     LDAPMessage *res, *entry;
887     char *dn;
888     int count;
889     int failures = 0;
890     util_url_node_t *curl;              /* Cached URL node */
891     util_url_node_t curnode;
892     util_search_node_t *search_nodep;   /* Cached search node */
893     util_search_node_t the_search_node;
894     apr_time_t curtime;
895
896     util_ldap_state_t *st = 
897         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
898         &ldap_module);
899
900     /* Get the cache node for this url */
901     LDAP_CACHE_LOCK();
902     curnode.url = url;
903     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, 
904                                                    &curnode);
905     if (curl == NULL) {
906         curl = util_ald_create_caches(st, url);
907     }
908     LDAP_CACHE_UNLOCK();
909
910     if (curl) {
911         LDAP_CACHE_LOCK();
912         the_search_node.username = filter;
913         search_nodep = util_ald_cache_fetch(curl->search_cache, 
914                                             &the_search_node);
915         if (search_nodep != NULL) {
916     
917             /* found entry in search cache... */
918             curtime = apr_time_now();
919     
920             /*
921              * Remove this item from the cache if its expired. If the sent 
922              * password doesn't match the storepassword, the entry will 
923              * be removed and readded later if the credentials pass 
924              * authentication.
925              */
926             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
927                 /* ...but entry is too old */
928                 util_ald_cache_remove(curl->search_cache, search_nodep);
929             }
930             else if (   (search_nodep->bindpw) 
931                      && (search_nodep->bindpw[0] != '\0') 
932                      && (strcmp(search_nodep->bindpw, bindpw) == 0)) 
933             {
934                 /* ...and entry is valid */
935                 *binddn = search_nodep->dn;
936                 *retvals = search_nodep->vals;
937                 LDAP_CACHE_UNLOCK();
938                 ldc->reason = "Authentication successful (cached)";
939                 return LDAP_SUCCESS;
940             }
941         }
942         /* unlock this read lock */
943         LDAP_CACHE_UNLOCK();
944     }
945
946     /*  
947      * At this point, there is no valid cached search, so lets do the search.
948      */
949
950     /*
951      * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
952      */
953 start_over:
954     if (failures++ > 10) {
955         return result;
956     }
957     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
958         return result;
959     }
960
961     /* try do the search */
962     if ((result = ldap_search_ext_s(ldc->ldap,
963                                     (char *)basedn, scope, 
964                                     (char *)filter, attrs, 0, 
965                                     NULL, NULL, NULL, -1, &res)) 
966             == LDAP_SERVER_DOWN) 
967     {
968         ldc->reason = "ldap_search_ext_s() for user failed with server down";
969         uldap_connection_unbind(ldc);
970         goto start_over;
971     }
972
973     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
974     if (result != LDAP_SUCCESS) {
975         ldc->reason = "ldap_search_ext_s() for user failed";
976         return result;
977     }
978
979     /* 
980      * We should have found exactly one entry; to find a different
981      * number is an error.
982      */
983     count = ldap_count_entries(ldc->ldap, res);
984     if (count != 1) 
985     {
986         if (count == 0 )
987             ldc->reason = "User not found";
988         else
989             ldc->reason = "User is not unique (search found two "
990                           "or more matches)";
991         ldap_msgfree(res);
992         return LDAP_NO_SUCH_OBJECT;
993     }
994
995     entry = ldap_first_entry(ldc->ldap, res);
996
997     /* Grab the dn, copy it into the pool, and free it again */
998     dn = ldap_get_dn(ldc->ldap, entry);
999     *binddn = apr_pstrdup(r->pool, dn);
1000     ldap_memfree(dn);
1001
1002     /* 
1003      * A bind to the server with an empty password always succeeds, so
1004      * we check to ensure that the password is not empty. This implies
1005      * that users who actually do have empty passwords will never be
1006      * able to authenticate with this module. I don't see this as a big
1007      * problem.
1008      */
1009     if (!bindpw || strlen(bindpw) <= 0) {
1010         ldap_msgfree(res);
1011         ldc->reason = "Empty password not allowed";
1012         return LDAP_INVALID_CREDENTIALS;
1013     }
1014
1015     /* 
1016      * Attempt to bind with the retrieved dn and the password. If the bind
1017      * fails, it means that the password is wrong (the dn obviously
1018      * exists, since we just retrieved it)
1019      */
1020     if ((result = ldap_simple_bind_s(ldc->ldap,
1021                                      (char *)*binddn,
1022                                      (char *)bindpw)) == LDAP_SERVER_DOWN) {
1023         ldc->reason = "ldap_simple_bind_s() to check user credentials "
1024                       "failed with server down";
1025         ldap_msgfree(res);
1026         uldap_connection_unbind(ldc);
1027         goto start_over;
1028     }
1029
1030     /* failure? if so - return */
1031     if (result != LDAP_SUCCESS) {
1032         ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1033         ldap_msgfree(res);
1034         uldap_connection_unbind(ldc);
1035         return result;
1036     }
1037     else {
1038         /*
1039          * We have just bound the connection to a different user and password
1040          * combination, which might be reused unintentionally next time this
1041          * connection is used from the connection pool. To ensure no confusion,
1042          * we mark the connection as unbound.
1043          */
1044         ldc->bound = 0;
1045     }
1046
1047     /*
1048      * Get values for the provided attributes.
1049      */
1050     if (attrs) {
1051         int k = 0;
1052         int i = 0;
1053         while (attrs[k++]);
1054         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1055         numvals = k;
1056         while (attrs[i]) {
1057             char **values;
1058             int j = 0;
1059             char *str = NULL;
1060             /* get values */
1061             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1062             while (values && values[j]) {
1063                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) 
1064                           : apr_pstrdup(r->pool, values[j]);
1065                 j++;
1066             }
1067             ldap_value_free(values);
1068             vals[i] = str;
1069             i++;
1070         }
1071         *retvals = vals;
1072     }
1073
1074     /*          
1075      * Add the new username to the search cache.
1076      */
1077     if (curl) {
1078         LDAP_CACHE_LOCK();
1079         the_search_node.username = filter;
1080         the_search_node.dn = *binddn;
1081         the_search_node.bindpw = bindpw;
1082         the_search_node.lastbind = apr_time_now();
1083         the_search_node.vals = vals;
1084         the_search_node.numvals = numvals;
1085
1086         /* Search again to make sure that another thread didn't ready insert 
1087          * this node into the cache before we got here. If it does exist then 
1088          * update the lastbind 
1089          */
1090         search_nodep = util_ald_cache_fetch(curl->search_cache, 
1091                                             &the_search_node);
1092         if ((search_nodep == NULL) || 
1093             (strcmp(*binddn, search_nodep->dn) != 0)) {
1094
1095             /* Nothing in cache, insert new entry */
1096             util_ald_cache_insert(curl->search_cache, &the_search_node);
1097         }
1098         else if ((!search_nodep->bindpw) ||
1099             (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1100
1101             /* Entry in cache is invalid, remove it and insert new one */
1102             util_ald_cache_remove(curl->search_cache, search_nodep);
1103             util_ald_cache_insert(curl->search_cache, &the_search_node);
1104         }
1105         else {
1106             /* Cache entry is valid, update lastbind */
1107             search_nodep->lastbind = the_search_node.lastbind;
1108         }
1109         LDAP_CACHE_UNLOCK();
1110     }
1111     ldap_msgfree(res);
1112
1113     ldc->reason = "Authentication successful";
1114     return LDAP_SUCCESS;
1115 }
1116
1117 /*
1118  * This function will return the DN of the entry matching userid.
1119  * It is used to get the DN in case some other module than mod_auth_ldap
1120  * has authenticated the user.
1121  * The function is basically a copy of uldap_cache_checkuserid
1122  * with password checking removed.
1123  */
1124 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1125                                  const char *url, const char *basedn, 
1126                                  int scope, char **attrs, const char *filter, 
1127                                  const char **binddn, const char ***retvals)
1128 {
1129     const char **vals = NULL;
1130     int numvals = 0;
1131     int result = 0;
1132     LDAPMessage *res, *entry;
1133     char *dn;
1134     int count;
1135     int failures = 0;
1136     util_url_node_t *curl;              /* Cached URL node */
1137     util_url_node_t curnode;
1138     util_search_node_t *search_nodep;   /* Cached search node */
1139     util_search_node_t the_search_node;
1140     apr_time_t curtime;
1141
1142     util_ldap_state_t *st = 
1143         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1144         &ldap_module);
1145
1146     /* Get the cache node for this url */
1147     LDAP_CACHE_LOCK();
1148     curnode.url = url;
1149     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, 
1150                                                    &curnode);
1151     if (curl == NULL) {
1152         curl = util_ald_create_caches(st, url);
1153     }
1154     LDAP_CACHE_UNLOCK();
1155
1156     if (curl) {
1157         LDAP_CACHE_LOCK();
1158         the_search_node.username = filter;
1159         search_nodep = util_ald_cache_fetch(curl->search_cache, 
1160                                             &the_search_node);
1161         if (search_nodep != NULL) {
1162     
1163             /* found entry in search cache... */
1164             curtime = apr_time_now();
1165     
1166             /*
1167              * Remove this item from the cache if its expired.
1168              */
1169             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1170                 /* ...but entry is too old */
1171                 util_ald_cache_remove(curl->search_cache, search_nodep);
1172             }
1173             else {
1174                 /* ...and entry is valid */
1175                 *binddn = search_nodep->dn;
1176                 *retvals = search_nodep->vals;
1177                 LDAP_CACHE_UNLOCK();
1178                 ldc->reason = "Search successful (cached)";
1179                 return LDAP_SUCCESS;
1180             }
1181         }
1182         /* unlock this read lock */
1183         LDAP_CACHE_UNLOCK();
1184     }
1185
1186     /*  
1187      * At this point, there is no valid cached search, so lets do the search.
1188      */
1189
1190     /*
1191      * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1192      */
1193 start_over:
1194     if (failures++ > 10) {
1195         return result;
1196     }
1197     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1198         return result;
1199     }
1200
1201     /* try do the search */
1202     if ((result = ldap_search_ext_s(ldc->ldap,
1203                                     (char *)basedn, scope,
1204                                     (char *)filter, attrs, 0, 
1205                                     NULL, NULL, NULL, -1, &res)) 
1206             == LDAP_SERVER_DOWN) 
1207     {
1208         ldc->reason = "ldap_search_ext_s() for user failed with server down";
1209         uldap_connection_unbind(ldc);
1210         goto start_over;
1211     }
1212
1213     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1214     if (result != LDAP_SUCCESS) {
1215         ldc->reason = "ldap_search_ext_s() for user failed";
1216         return result;
1217     }
1218
1219     /* 
1220      * We should have found exactly one entry; to find a different
1221      * number is an error.
1222      */
1223     count = ldap_count_entries(ldc->ldap, res);
1224     if (count != 1) 
1225     {
1226         if (count == 0 )
1227             ldc->reason = "User not found";
1228         else
1229             ldc->reason = "User is not unique (search found two "
1230                           "or more matches)";
1231         ldap_msgfree(res);
1232         return LDAP_NO_SUCH_OBJECT;
1233     }
1234
1235     entry = ldap_first_entry(ldc->ldap, res);
1236
1237     /* Grab the dn, copy it into the pool, and free it again */
1238     dn = ldap_get_dn(ldc->ldap, entry);
1239     *binddn = apr_pstrdup(st->pool, dn);
1240     ldap_memfree(dn);
1241
1242     /*
1243      * Get values for the provided attributes.
1244      */
1245     if (attrs) {
1246         int k = 0;
1247         int i = 0;
1248         while (attrs[k++]);
1249         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1250         numvals = k;
1251         while (attrs[i]) {
1252             char **values;
1253             int j = 0;
1254             char *str = NULL;
1255             /* get values */
1256             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1257             while (values && values[j]) {
1258                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) 
1259                           : apr_pstrdup(r->pool, values[j]);
1260                 j++;
1261             }
1262             ldap_value_free(values);
1263             vals[i] = str;
1264             i++;
1265         }
1266         *retvals = vals;
1267     }
1268
1269     /*          
1270      * Add the new username to the search cache.
1271      */
1272     if (curl) {
1273         LDAP_CACHE_LOCK();
1274         the_search_node.username = filter;
1275         the_search_node.dn = *binddn;
1276         the_search_node.bindpw = NULL;
1277         the_search_node.lastbind = apr_time_now();
1278         the_search_node.vals = vals;
1279         the_search_node.numvals = numvals;
1280
1281         /* Search again to make sure that another thread didn't ready insert 
1282          * this node into the cache before we got here. If it does exist then 
1283          * update the lastbind 
1284          */
1285         search_nodep = util_ald_cache_fetch(curl->search_cache,
1286                                             &the_search_node);
1287         if ((search_nodep == NULL) || 
1288             (strcmp(*binddn, search_nodep->dn) != 0)) {
1289
1290             /* Nothing in cache, insert new entry */
1291             util_ald_cache_insert(curl->search_cache, &the_search_node);
1292         }
1293         /*
1294          * Don't update lastbind on entries with bindpw because
1295          * we haven't verified that password. It's OK to update
1296          * the entry if there is no password in it.
1297          */
1298         else if (!search_nodep->bindpw) {
1299             /* Cache entry is valid, update lastbind */
1300             search_nodep->lastbind = the_search_node.lastbind;
1301         }
1302         LDAP_CACHE_UNLOCK();
1303     }
1304
1305     ldap_msgfree(res);
1306
1307     ldc->reason = "Search successful";
1308     return LDAP_SUCCESS;
1309 }
1310
1311 /*
1312  * Reports if ssl support is enabled 
1313  *
1314  * 1 = enabled, 0 = not enabled
1315  */
1316 static int uldap_ssl_supported(request_rec *r)
1317 {
1318    util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1319                                 r->server->module_config, &ldap_module);
1320
1321    return(st->ssl_supported);
1322 }
1323
1324
1325 /* ---------------------------------------- */
1326 /* config directives */
1327
1328
1329 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, 
1330                                              const char *bytes)
1331 {
1332     util_ldap_state_t *st = 
1333         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1334                                                   &ldap_module);
1335
1336     st->cache_bytes = atol(bytes);
1337
1338     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1339                  "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1340                  " cache size to %" APR_SIZE_T_FMT " bytes.", 
1341                  getpid(), st->cache_bytes);
1342
1343     return NULL;
1344 }
1345
1346 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy, 
1347                                             const char *file)
1348 {
1349     util_ldap_state_t *st = 
1350         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1351                                                   &ldap_module);
1352
1353     if (file) {
1354         st->cache_file = ap_server_root_relative(st->pool, file);
1355     }
1356     else {
1357         st->cache_file = NULL;
1358     }
1359
1360     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1361                  "LDAP cache: Setting shared memory cache file to %s bytes.", 
1362                  st->cache_file);
1363
1364     return NULL;
1365 }
1366
1367 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, 
1368                                            const char *ttl)
1369 {
1370     util_ldap_state_t *st = 
1371         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1372                                                   &ldap_module);
1373
1374     st->search_cache_ttl = atol(ttl) * 1000000;
1375
1376     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1377                  "[%d] ldap cache: Setting cache TTL to %ld microseconds.",
1378                  getpid(), st->search_cache_ttl);
1379
1380     return NULL;
1381 }
1382
1383 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, 
1384                                                const char *size)
1385 {
1386     util_ldap_state_t *st = 
1387         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1388                                                   &ldap_module);
1389
1390
1391     st->search_cache_size = atol(size);
1392     if (st->search_cache_size < 0) {
1393         st->search_cache_size = 0;
1394     }
1395
1396     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1397                  "[%d] ldap cache: Setting search cache size to %ld entries.",
1398                  getpid(), st->search_cache_size);
1399
1400     return NULL;
1401 }
1402
1403 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, 
1404                                              const char *ttl)
1405 {
1406     util_ldap_state_t *st = 
1407         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1408                                                   &ldap_module);
1409
1410     st->compare_cache_ttl = atol(ttl) * 1000000;
1411
1412     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1413                  "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.", 
1414                  getpid(), st->compare_cache_ttl);
1415
1416     return NULL;
1417 }
1418
1419 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, 
1420                                                  const char *size)
1421 {
1422     util_ldap_state_t *st = 
1423         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1424                                                   &ldap_module);
1425
1426     st->compare_cache_size = atol(size);
1427     if (st->compare_cache_size < 0) {
1428         st->compare_cache_size = 0;
1429     }
1430
1431     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1432                  "[%d] ldap cache: Setting operation cache size to %ld "
1433                  "entries.", getpid(), st->compare_cache_size);
1434
1435     return NULL;
1436 }
1437
1438
1439 /**
1440  * Parse the certificate type.
1441  *
1442  * The type can be one of the following:
1443  * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1444  * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1445  *
1446  * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1447  */
1448 static const int util_ldap_parse_cert_type(const char *type) {
1449
1450     /* Authority file in binary DER format */
1451     if (0 == strcasecmp("CA_DER", type)) {
1452         return APR_LDAP_CA_TYPE_DER;
1453     }
1454
1455     /* Authority file in Base64 format */
1456     else if (0 == strcasecmp("CA_BASE64", type)) {
1457         return APR_LDAP_CA_TYPE_BASE64;
1458     }
1459
1460     /* Netscape certificate database file/directory */
1461     else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1462         return APR_LDAP_CA_TYPE_CERT7_DB;
1463     }
1464
1465     /* Netscape secmod file/directory */
1466     else if (0 == strcasecmp("CA_SECMOD", type)) {
1467         return APR_LDAP_CA_TYPE_SECMOD;
1468     }
1469
1470     /* Client cert file in DER format */
1471     else if (0 == strcasecmp("CERT_DER", type)) {
1472         return APR_LDAP_CERT_TYPE_DER;
1473     }
1474
1475     /* Client cert file in Base64 format */
1476     else if (0 == strcasecmp("CERT_BASE64", type)) {
1477         return APR_LDAP_CERT_TYPE_BASE64;
1478     }
1479
1480     /* Client cert file in PKCS#12 format */
1481     else if (0 == strcasecmp("CERT_PFX", type)) {
1482         return APR_LDAP_CERT_TYPE_PFX;
1483     }
1484
1485     /* Netscape client cert database file/directory */
1486     else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1487         return APR_LDAP_CERT_TYPE_KEY3_DB;
1488     }
1489
1490     /* Netscape client cert nickname */
1491     else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1492         return APR_LDAP_CERT_TYPE_NICKNAME;
1493     }
1494
1495     /* Client cert key file in DER format */
1496     else if (0 == strcasecmp("KEY_DER", type)) {
1497         return APR_LDAP_KEY_TYPE_DER;
1498     }
1499
1500     /* Client cert key file in Base64 format */
1501     else if (0 == strcasecmp("KEY_BASE64", type)) {
1502         return APR_LDAP_KEY_TYPE_BASE64;
1503     }
1504
1505     /* Client cert key file in PKCS#12 format */
1506     else if (0 == strcasecmp("KEY_PFX", type)) {
1507         return APR_LDAP_KEY_TYPE_PFX;
1508     }
1509
1510     else {
1511         return APR_LDAP_CA_TYPE_UNKNOWN;
1512     }
1513
1514 }
1515
1516
1517 /**
1518  * Set LDAPTrustedGlobalCert.
1519  *
1520  * This directive takes either two or three arguments:
1521  * - certificate type
1522  * - certificate file / directory / nickname
1523  * - certificate password (optional)
1524  *
1525  * This directive may only be used globally.
1526  */
1527 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd, 
1528                                                      void *dummy, 
1529                                                      const char *type, 
1530                                                      const char *file, 
1531                                                      const char *password)
1532 {
1533     util_ldap_state_t *st =
1534         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1535                                                   &ldap_module);
1536     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1537     apr_finfo_t finfo;
1538     apr_status_t rv;
1539     int cert_type = 0;
1540     apr_ldap_opt_tls_cert_t *cert;
1541
1542     if (err != NULL) {
1543         return err;
1544     }
1545
1546     /* handle the certificate type */
1547     if (type) {
1548         cert_type = util_ldap_parse_cert_type(type);
1549         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1550            return apr_psprintf(cmd->pool, "The certificate type %s is "
1551                                           "not recognised. It should be one "
1552                                           "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1553                                           "CA_SECMOD, CERT_DER, CERT_BASE64, "
1554                                           "CERT_KEY3_DB, CERT_NICKNAME, "
1555                                           "KEY_DER, KEY_BASE64", type);
1556         }
1557     }
1558     else {
1559         return "Certificate type was not specified.";
1560     }
1561
1562     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1563                       "LDAP: SSL trusted global cert - %s (type %s)",
1564                        file, type);
1565
1566     /* add the certificate to the global array */
1567     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1568     cert->type = cert_type;
1569     cert->path = file;
1570     cert->password = password;
1571
1572     /* if file is a file or path, fix the path */
1573     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1574         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1575
1576         cert->path = ap_server_root_relative(cmd->pool, file);
1577         if (cert->path &&
1578             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) 
1579                 != APR_SUCCESS)) 
1580         {
1581             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1582                          "LDAP: Could not open SSL trusted certificate "
1583                          "authority file - %s",
1584                          cert->path == NULL ? file : cert->path);
1585             return "Invalid global certificate file path";
1586         }
1587     }
1588
1589     return(NULL);
1590 }
1591
1592
1593 /**
1594  * Set LDAPTrustedClientCert.
1595  *
1596  * This directive takes either two or three arguments:
1597  * - certificate type
1598  * - certificate file / directory / nickname
1599  * - certificate password (optional)
1600  */
1601 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd, 
1602                                                      void *config, 
1603                                                      const char *type, 
1604                                                      const char *file, 
1605                                                      const char *password)
1606 {
1607     util_ldap_state_t *st =
1608         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1609                                                   &ldap_module);
1610     apr_finfo_t finfo;
1611     apr_status_t rv;
1612     int cert_type = 0;
1613     apr_ldap_opt_tls_cert_t *cert;
1614
1615     /* handle the certificate type */
1616     if (type) {
1617         cert_type = util_ldap_parse_cert_type(type);
1618         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1619             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1620                                            "not recognised. It should be one "
1621                                            "of CERT_DER, CERT_BASE64, "
1622                                            "CERT_NICKNAME, CERT_PFX,"
1623                                            "KEY_DER, KEY_BASE64, KEY_PFX", 
1624                                            type);
1625         }
1626         else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1627                  APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1628                  APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1629                  APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1630                  APR_LDAP_CERT_TYPE_PFX == cert_type ||
1631                  APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1632             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1633                                            "only valid within a "
1634                                            "LDAPTrustedGlobalCert directive. "
1635                                            "Only CERT_DER, CERT_BASE64, "
1636                                            "CERT_NICKNAME, KEY_DER, and "
1637                                            "KEY_BASE64 may be used.", type);
1638         }
1639     }
1640     else {
1641         return "Certificate type was not specified.";
1642     }
1643
1644     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1645                       "LDAP: SSL trusted client cert - %s (type %s)",
1646                        file, type);
1647
1648     /* add the certificate to the global array */
1649     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1650     cert->type = cert_type; 
1651     cert->path = file;
1652     cert->password = password;
1653
1654     /* if file is a file or path, fix the path */
1655     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1656         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1657
1658         cert->path = ap_server_root_relative(cmd->pool, file);
1659         if (cert->path &&
1660             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) 
1661                 != APR_SUCCESS)) 
1662         {
1663             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1664                          "LDAP: Could not open SSL client certificate "
1665                          "file - %s",
1666                          cert->path == NULL ? file : cert->path);
1667             return "Invalid client certificate file path";
1668         }
1669
1670     }
1671
1672     return(NULL);
1673 }
1674
1675
1676 /**
1677  * Set LDAPTrustedMode.
1678  *                    
1679  * This directive sets what encryption mode to use on a connection:
1680  * - None (No encryption)
1681  * - SSL (SSL encryption)
1682  * - STARTTLS (TLS encryption)
1683  */ 
1684 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy, 
1685                                               const char *mode)
1686 {
1687     util_ldap_state_t *st =
1688     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1689                                               &ldap_module);
1690
1691     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1692                       "LDAP: SSL trusted mode - %s",
1693                        mode);
1694
1695     if (0 == strcasecmp("NONE", mode)) {
1696         st->secure = APR_LDAP_NONE;
1697     }
1698     else if (0 == strcasecmp("SSL", mode)) {
1699         st->secure = APR_LDAP_SSL;
1700     }
1701     else if (   (0 == strcasecmp("TLS", mode))
1702              || (0 == strcasecmp("STARTTLS", mode))) {
1703         st->secure = APR_LDAP_STARTTLS;
1704     }
1705     else {
1706         return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1707                "SSL, or TLS/STARTTLS";
1708     }
1709
1710     st->secure_set = 1;
1711     return(NULL);
1712 }
1713
1714 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd, 
1715                                                  void *dummy, 
1716                                                  int mode)
1717 {
1718     util_ldap_state_t *st =
1719     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1720                                               &ldap_module);
1721
1722     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1723                       "LDAP: SSL verify server certificate - %s", 
1724                       mode?"TRUE":"FALSE");
1725
1726     st->verify_svr_cert = mode;
1727
1728     return(NULL);
1729 }
1730
1731
1732 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd, 
1733                                                     void *dummy, 
1734                                                     const char *ttl)
1735 {
1736     util_ldap_state_t *st = 
1737         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1738                                                   &ldap_module);
1739     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1740
1741     if (err != NULL) {
1742         return err;
1743     }
1744
1745 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1746     st->connectionTimeout = atol(ttl);
1747
1748     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1749                  "[%d] ldap connection: Setting connection timeout to "
1750                  "%ld seconds.", getpid(), st->connectionTimeout);
1751 #else
1752     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
1753                  "LDAP: Connection timout option not supported by the "
1754                  "LDAP SDK in use." );
1755 #endif
1756
1757     return NULL;
1758 }
1759
1760
1761 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1762 {
1763     util_ldap_state_t *st = 
1764         (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1765
1766     st->pool = p;
1767
1768     st->cache_bytes = 100000;
1769     st->search_cache_ttl = 600000000;
1770     st->search_cache_size = 1024;
1771     st->compare_cache_ttl = 600000000;
1772     st->compare_cache_size = 1024;
1773     st->connections = NULL;
1774     st->ssl_supported = 0;
1775     st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1776     st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1777     st->secure = APR_LDAP_NONE;
1778     st->secure_set = 0;
1779     st->connectionTimeout = 10;
1780     st->verify_svr_cert = 1;
1781
1782     return st;
1783 }
1784
1785 static void *util_ldap_merge_config(apr_pool_t *p, void *basev, 
1786                                     void *overridesv)
1787 {
1788     util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
1789     util_ldap_state_t *base = (util_ldap_state_t *) basev;
1790     util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
1791
1792     st->pool = p;
1793
1794     st->cache_bytes = base->cache_bytes;
1795     st->search_cache_ttl = base->search_cache_ttl;
1796     st->search_cache_size = base->search_cache_size;
1797     st->compare_cache_ttl = base->compare_cache_ttl;
1798     st->compare_cache_size = base->compare_cache_size;
1799     st->connections = base->connections;
1800     st->ssl_supported = base->ssl_supported;
1801     st->global_certs = apr_array_append(p, base->global_certs, 
1802                                            overrides->global_certs);
1803     st->client_certs = apr_array_append(p, base->client_certs, 
1804                                            overrides->client_certs);
1805     st->secure = (overrides->secure_set == 0) ? base->secure 
1806                                               : overrides->secure;
1807
1808     return st;
1809 }
1810
1811 static apr_status_t util_ldap_cleanup_module(void *data)
1812 {
1813
1814     server_rec *s = data;
1815     util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1816         s->module_config, &ldap_module);
1817     
1818     if (st->ssl_supported) {
1819         apr_ldap_ssl_deinit();
1820     }
1821
1822     return APR_SUCCESS;
1823
1824 }
1825
1826 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog, 
1827                                  apr_pool_t *ptemp, server_rec *s)
1828 {
1829     apr_status_t result;
1830     char buf[MAX_STRING_LEN];
1831     server_rec *s_vhost;
1832     util_ldap_state_t *st_vhost;
1833
1834     util_ldap_state_t *st = (util_ldap_state_t *)
1835                             ap_get_module_config(s->module_config, 
1836                                                  &ldap_module);
1837
1838     void *data;
1839     const char *userdata_key = "util_ldap_init";
1840     apr_ldap_err_t *result_err = NULL;
1841     int rc;
1842
1843     /* util_ldap_post_config() will be called twice. Don't bother
1844      * going through all of the initialization on the first call
1845      * because it will just be thrown away.*/
1846     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1847     if (!data) {
1848         apr_pool_userdata_set((const void *)1, userdata_key,
1849                                apr_pool_cleanup_null, s->process->pool);
1850
1851 #if APR_HAS_SHARED_MEMORY
1852         /* If the cache file already exists then delete it.  Otherwise we are
1853          * going to run into problems creating the shared memory. */
1854         if (st->cache_file) {
1855             char *lck_file = apr_pstrcat(st->pool, st->cache_file, ".lck", 
1856                                          NULL);
1857             apr_file_remove(st->cache_file, ptemp);
1858             apr_file_remove(lck_file, ptemp);
1859         }
1860 #endif
1861         return OK;
1862     }
1863
1864 #if APR_HAS_SHARED_MEMORY
1865     /* initializing cache if shared memory size is not zero and we already 
1866      * don't have shm address 
1867      */
1868     if (!st->cache_shm && st->cache_bytes > 0) {
1869 #endif
1870         result = util_ldap_cache_init(p, st);
1871         if (result != APR_SUCCESS) {
1872             apr_strerror(result, buf, sizeof(buf));
1873             ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1874                          "LDAP cache: error while creating a shared memory "
1875                          "segment: %s", buf);
1876         }
1877
1878
1879 #if APR_HAS_SHARED_MEMORY
1880         if (st->cache_file) {
1881             st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck", 
1882                                         NULL);
1883         }
1884         else
1885 #endif
1886             st->lock_file = ap_server_root_relative(st->pool, tmpnam(NULL));
1887
1888         result = apr_global_mutex_create(&st->util_ldap_cache_lock, 
1889                                          st->lock_file, APR_LOCK_DEFAULT, 
1890                                          st->pool);
1891         if (result != APR_SUCCESS) {
1892             return result;
1893         }
1894
1895 #ifdef AP_NEED_SET_MUTEX_PERMS
1896         result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1897         if (result != APR_SUCCESS) {
1898             ap_log_error(APLOG_MARK, APLOG_CRIT, result, s, 
1899                          "LDAP cache: failed to set mutex permissions");
1900             return result;
1901         }
1902 #endif
1903
1904         /* merge config in all vhost */
1905         s_vhost = s->next;
1906         while (s_vhost) {
1907             st_vhost = (util_ldap_state_t *)
1908                        ap_get_module_config(s_vhost->module_config, 
1909                                             &ldap_module);
1910
1911 #if APR_HAS_SHARED_MEMORY
1912             st_vhost->cache_shm = st->cache_shm;
1913             st_vhost->cache_rmm = st->cache_rmm;
1914             st_vhost->cache_file = st->cache_file;
1915             ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s, 
1916                          "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
1917                          "for VHOST: %s", st->cache_shm, st->cache_rmm, 
1918                          s_vhost->server_hostname);
1919 #endif
1920             st_vhost->lock_file = st->lock_file;
1921             s_vhost = s_vhost->next;
1922         }
1923 #if APR_HAS_SHARED_MEMORY
1924     }
1925     else {
1926         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 
1927                      "LDAP cache: LDAPSharedCacheSize is zero, disabling "
1928                      "shared memory cache");
1929     }
1930 #endif
1931     
1932     /* log the LDAP SDK used 
1933      */
1934     {
1935         apr_ldap_err_t *result = NULL;
1936         apr_ldap_info(p, &(result));
1937         if (result != NULL) {
1938             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "%s", result->reason);
1939         }
1940     }
1941
1942     apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1943                               util_ldap_cleanup_module); 
1944
1945     /*
1946      * Initialize SSL support, and log the result for the benefit of the admin.
1947      *
1948      * If SSL is not supported it is not necessarily an error, as the
1949      * application may not want to use it.
1950      */
1951     rc = apr_ldap_ssl_init(p, 
1952                       NULL, 
1953                       0, 
1954                       &(result_err));
1955     if (APR_SUCCESS == rc) {
1956         rc = apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT,
1957                                  (void *)st->global_certs, &(result_err));
1958     }
1959
1960     if (APR_SUCCESS == rc) {
1961         st->ssl_supported = 1;
1962         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1963                          "LDAP: SSL support available" );
1964     }
1965     else {
1966         st->ssl_supported = 0;
1967         if (NULL != result_err) {
1968             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "%s", 
1969                          result_err->reason);
1970         }
1971         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, 
1972                          "LDAP: SSL support unavailable" );
1973     }
1974
1975     return(OK);
1976 }
1977
1978 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
1979 {
1980     apr_status_t sts;
1981     util_ldap_state_t *st = ap_get_module_config(s->module_config, 
1982                                                  &ldap_module);
1983
1984     if (!st->util_ldap_cache_lock) return;
1985
1986     sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock, 
1987                                       st->lock_file, p);
1988     if (sts != APR_SUCCESS) {
1989         ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
1990                      "Failed to initialise global mutex %s in child process %"
1991                      APR_PID_T_FMT ".",
1992                      st->lock_file, getpid());
1993         return;
1994     }
1995     else {
1996         ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, s, 
1997                      "Initialisation of global mutex %s in child process %"
1998                      APR_PID_T_FMT
1999                      " successful.",
2000                      st->lock_file, getpid());
2001     }
2002 }
2003
2004 command_rec util_ldap_cmds[] = {
2005     AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, 
2006                   NULL, RSRC_CONF,
2007                   "Set the size of the shared memory cache (in bytes). Use "
2008                   "0 to disable the shared memory cache. (default: 100000)"),
2009
2010     AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file, 
2011                   NULL, RSRC_CONF,
2012                   "Set the file name for the shared memory cache."),
2013
2014     AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, 
2015                   NULL, RSRC_CONF,
2016                   "Set the maximum number of entries that are possible in the "
2017                   "LDAP search cache. Use 0 for no limit. "
2018                   "-1 disables the cache. (default: 1024)"),
2019
2020     AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, 
2021                   NULL, RSRC_CONF,
2022                   "Set the maximum time (in seconds) that an item can be "
2023                   "cached in the LDAP search cache. Use 0 for no limit. "
2024                   "(default 600)"),
2025
2026     AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, 
2027                   NULL, RSRC_CONF,
2028                   "Set the maximum number of entries that are possible "
2029                   "in the LDAP compare cache. Use 0 for no limit. "
2030                   "Use -1 to disable the cache. (default: 1024)"),
2031
2032     AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, 
2033                   NULL, RSRC_CONF,
2034                   "Set the maximum time (in seconds) that an item is cached "
2035                   "in the LDAP operation cache. Use 0 for no limit. "
2036                   "(default: 600)"),
2037
2038     AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2039                    NULL, RSRC_CONF,
2040                    "Takes three args; the file and/or directory containing "
2041                    "the trusted CA certificates (and global client certs "
2042                    "for Netware) used to validate the LDAP server.  Second "
2043                    "arg is the cert type for the first arg, one of CA_DER, "
2044                    "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2045                    "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2046                    "Third arg is an optional passphrase if applicable."),
2047
2048     AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2049                    NULL, RSRC_CONF,
2050                    "Takes three args; the file and/or directory containing "
2051                    "the client certificate, or certificate ID used to "
2052                    "validate this LDAP client.  Second arg is the cert type "
2053                    "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2054                    "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2055                    "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2056                    "optional passphrase if applicable."),
2057
2058     AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode, 
2059                   NULL, RSRC_CONF,
2060                   "Specify the type of security that should be applied to "
2061                   "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2062
2063     AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert, 
2064                   NULL, RSRC_CONF,
2065                   "Set to 'ON' requires that the server certificate be verified "
2066                   "before a secure LDAP connection can be establish.  Default 'ON'"),
2067
2068     AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout, 
2069                   NULL, RSRC_CONF,
2070                   "Specify the LDAP socket connection timeout in seconds "
2071                   "(default: 10)"),
2072
2073     {NULL}
2074 };
2075
2076 static void util_ldap_register_hooks(apr_pool_t *p)
2077 {
2078     APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2079     APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2080     APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2081     APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2082     APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2083     APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2084     APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2085     APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2086     APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2087     APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2088
2089     ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2090     ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2091     ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2092 }
2093
2094 module AP_MODULE_DECLARE_DATA ldap_module = {
2095    STANDARD20_MODULE_STUFF,
2096    NULL,                                /* create dir config */
2097    NULL,                                /* merge dir config */
2098    util_ldap_create_config,             /* create server config */
2099    util_ldap_merge_config,              /* merge server config */
2100    util_ldap_cmds,                      /* command table */
2101    util_ldap_register_hooks,            /* set up request processing hooks */
2102 };