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