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