]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap.c
Update copyright year to 2005 and standardize on current copyright owner line.
[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  * util_ldap_connection_find() is called this connection will be
158  * available for reuse.
159  */
160 LDAP_DECLARE(void) util_ldap_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) util_ldap_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) util_ldap_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         util_ldap_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         util_ldap_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) util_ldap_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 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              util_ldap_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                                   util_ldap_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) util_ldap_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 = util_ldap_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         util_ldap_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) util_ldap_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 = util_ldap_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         util_ldap_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) util_ldap_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 result = 0;
819     LDAPMessage *res, *entry;
820     char *dn;
821     int count;
822     int failures = 0;
823     util_url_node_t *curl;              /* Cached URL node */
824     util_url_node_t curnode;
825     util_search_node_t *search_nodep;   /* Cached search node */
826     util_search_node_t the_search_node;
827     apr_time_t curtime;
828
829     util_ldap_state_t *st = 
830         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
831         &ldap_module);
832
833     /* Get the cache node for this url */
834     LDAP_CACHE_LOCK();
835     curnode.url = url;
836     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
837     if (curl == NULL) {
838         curl = util_ald_create_caches(st, url);
839     }
840     LDAP_CACHE_UNLOCK();
841
842     if (curl) {
843         LDAP_CACHE_LOCK();
844         the_search_node.username = filter;
845         search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
846         if (search_nodep != NULL) {
847     
848             /* found entry in search cache... */
849             curtime = apr_time_now();
850     
851             /*
852              * Remove this item from the cache if its expired. If the sent password
853              * doesn't match the storepassword, the entry will be removed and readded
854              * later if the credentials pass authentication.
855              */
856             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
857                 /* ...but entry is too old */
858                 util_ald_cache_remove(curl->search_cache, search_nodep);
859             }
860             else if ((search_nodep->bindpw) && (search_nodep->bindpw[0] != '\0') && 
861                      (strcmp(search_nodep->bindpw, bindpw) == 0)) {
862                 /* ...and entry is valid */
863                 *binddn = search_nodep->dn;
864                 *retvals = search_nodep->vals;
865                 LDAP_CACHE_UNLOCK();
866                 ldc->reason = "Authentication successful (cached)";
867                 return LDAP_SUCCESS;
868             }
869         }
870         /* unlock this read lock */
871         LDAP_CACHE_UNLOCK();
872     }
873
874     /*  
875      * At this point, there is no valid cached search, so lets do the search.
876      */
877
878     /*
879      * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
880      */
881 start_over:
882     if (failures++ > 10) {
883         return result;
884     }
885     if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
886         return result;
887     }
888
889     /* try do the search */
890     if ((result = ldap_search_ext_s(ldc->ldap,
891                                     (char *)basedn, scope, 
892                                     (char *)filter, attrs, 0, 
893                                     NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
894         ldc->reason = "ldap_search_ext_s() for user failed with server down";
895         util_ldap_connection_unbind(ldc);
896         goto start_over;
897     }
898
899     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
900     if (result != LDAP_SUCCESS) {
901         ldc->reason = "ldap_search_ext_s() for user failed";
902         return result;
903     }
904
905     /* 
906      * We should have found exactly one entry; to find a different
907      * number is an error.
908      */
909     count = ldap_count_entries(ldc->ldap, res);
910     if (count != 1) 
911     {
912         if (count == 0 )
913             ldc->reason = "User not found";
914         else
915             ldc->reason = "User is not unique (search found two or more matches)";
916         ldap_msgfree(res);
917         return LDAP_NO_SUCH_OBJECT;
918     }
919
920     entry = ldap_first_entry(ldc->ldap, res);
921
922     /* Grab the dn, copy it into the pool, and free it again */
923     dn = ldap_get_dn(ldc->ldap, entry);
924     *binddn = apr_pstrdup(r->pool, dn);
925     ldap_memfree(dn);
926
927     /* 
928      * A bind to the server with an empty password always succeeds, so
929      * we check to ensure that the password is not empty. This implies
930      * that users who actually do have empty passwords will never be
931      * able to authenticate with this module. I don't see this as a big
932      * problem.
933      */
934     if (!bindpw || strlen(bindpw) <= 0) {
935         ldap_msgfree(res);
936         ldc->reason = "Empty password not allowed";
937         return LDAP_INVALID_CREDENTIALS;
938     }
939
940     /* 
941      * Attempt to bind with the retrieved dn and the password. If the bind
942      * fails, it means that the password is wrong (the dn obviously
943      * exists, since we just retrieved it)
944      */
945     if ((result = ldap_simple_bind_s(ldc->ldap,
946                                      (char *)*binddn,
947                                      (char *)bindpw)) == LDAP_SERVER_DOWN) {
948         ldc->reason = "ldap_simple_bind_s() to check user credentials "
949                       "failed with server down";
950         ldap_msgfree(res);
951         util_ldap_connection_unbind(ldc);
952         goto start_over;
953     }
954
955     /* failure? if so - return */
956     if (result != LDAP_SUCCESS) {
957         ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
958         ldap_msgfree(res);
959         util_ldap_connection_unbind(ldc);
960         return result;
961     }
962     else {
963         /*
964          * We have just bound the connection to a different user and password
965          * combination, which might be reused unintentionally next time this
966          * connection is used from the connection pool. To ensure no confusion,
967          * we mark the connection as unbound.
968          */
969         ldc->bound = 0;
970     }
971
972     /*
973      * Get values for the provided attributes.
974      */
975     if (attrs) {
976         int k = 0;
977         int i = 0;
978         while (attrs[k++]);
979         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
980         while (attrs[i]) {
981             char **values;
982             int j = 0;
983             char *str = NULL;
984             /* get values */
985             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
986             while (values && values[j]) {
987                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
988                 j++;
989             }
990             ldap_value_free(values);
991             vals[i] = str;
992             i++;
993         }
994         *retvals = vals;
995     }
996
997     /*          
998      * Add the new username to the search cache.
999      */
1000     if (curl) {
1001         LDAP_CACHE_LOCK();
1002         the_search_node.username = filter;
1003         the_search_node.dn = *binddn;
1004         the_search_node.bindpw = bindpw;
1005         the_search_node.lastbind = apr_time_now();
1006         the_search_node.vals = vals;
1007
1008         /* Search again to make sure that another thread didn't ready insert this node
1009            into the cache before we got here. If it does exist then update the lastbind */
1010         search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1011         if ((search_nodep == NULL) || 
1012             (strcmp(*binddn, search_nodep->dn) != 0)) {
1013
1014             /* Nothing in cache, insert new entry */
1015             util_ald_cache_insert(curl->search_cache, &the_search_node);
1016         }
1017         else if ((!search_nodep->bindpw) ||
1018             (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1019
1020             /* Entry in cache is invalid, remove it and insert new one */
1021             util_ald_cache_remove(curl->search_cache, search_nodep);
1022             util_ald_cache_insert(curl->search_cache, &the_search_node);
1023         }
1024         else {
1025             /* Cache entry is valid, update lastbind */
1026             search_nodep->lastbind = the_search_node.lastbind;
1027         }
1028         LDAP_CACHE_UNLOCK();
1029     }
1030     ldap_msgfree(res);
1031
1032     ldc->reason = "Authentication successful";
1033     return LDAP_SUCCESS;
1034 }
1035
1036   /*
1037  * This function will return the DN of the entry matching userid.
1038  * It is used to get the DN in case some other module than mod_auth_ldap
1039  * has authenticated the user.
1040  * The function is basically a copy of util_ldap_cache_checkuserid
1041  * with password checking removed.
1042  */
1043 LDAP_DECLARE(int) util_ldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1044                               const char *url, const char *basedn, int scope, char **attrs,
1045                               const char *filter, const char **binddn,
1046                               const char ***retvals)
1047 {
1048     const char **vals = NULL;
1049     int result = 0;
1050     LDAPMessage *res, *entry;
1051     char *dn;
1052     int count;
1053     int failures = 0;
1054     util_url_node_t *curl;              /* Cached URL node */
1055     util_url_node_t curnode;
1056     util_search_node_t *search_nodep;   /* Cached search node */
1057     util_search_node_t the_search_node;
1058     apr_time_t curtime;
1059
1060     util_ldap_state_t *st = 
1061         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1062         &ldap_module);
1063
1064     /* Get the cache node for this url */
1065     LDAP_CACHE_LOCK();
1066     curnode.url = url;
1067     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1068     if (curl == NULL) {
1069         curl = util_ald_create_caches(st, url);
1070     }
1071     LDAP_CACHE_UNLOCK();
1072
1073     if (curl) {
1074         LDAP_CACHE_LOCK();
1075         the_search_node.username = filter;
1076         search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1077         if (search_nodep != NULL) {
1078     
1079             /* found entry in search cache... */
1080             curtime = apr_time_now();
1081     
1082             /*
1083              * Remove this item from the cache if its expired.
1084              */
1085             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1086                 /* ...but entry is too old */
1087                 util_ald_cache_remove(curl->search_cache, search_nodep);
1088             }
1089             else {
1090                 /* ...and entry is valid */
1091                 *binddn = search_nodep->dn;
1092                 *retvals = search_nodep->vals;
1093                 LDAP_CACHE_UNLOCK();
1094                 ldc->reason = "Search successful (cached)";
1095                 return LDAP_SUCCESS;
1096             }
1097         }
1098         /* unlock this read lock */
1099         LDAP_CACHE_UNLOCK();
1100     }
1101
1102     /*  
1103      * At this point, there is no valid cached search, so lets do the search.
1104      */
1105
1106     /*
1107      * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1108      */
1109 start_over:
1110     if (failures++ > 10) {
1111         return result;
1112     }
1113     if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
1114         return result;
1115     }
1116
1117     /* try do the search */
1118     if ((result = ldap_search_ext_s(ldc->ldap,
1119                                     (char *)basedn, scope,
1120                                     (char *)filter, attrs, 0, 
1121                                     NULL, NULL,
1122                                     NULL, -1, &res)) == LDAP_SERVER_DOWN) {
1123         ldc->reason = "ldap_search_ext_s() for user failed with server down";
1124         goto start_over;
1125     }
1126
1127     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1128     if (result != LDAP_SUCCESS) {
1129         ldc->reason = "ldap_search_ext_s() for user failed";
1130         return result;
1131     }
1132
1133     /* 
1134      * We should have found exactly one entry; to find a different
1135      * number is an error.
1136      */
1137     count = ldap_count_entries(ldc->ldap, res);
1138     if (count != 1) 
1139     {
1140         if (count == 0 )
1141             ldc->reason = "User not found";
1142         else
1143             ldc->reason = "User is not unique (search found two or more matches)";
1144         ldap_msgfree(res);
1145         return LDAP_NO_SUCH_OBJECT;
1146     }
1147
1148     entry = ldap_first_entry(ldc->ldap, res);
1149
1150     /* Grab the dn, copy it into the pool, and free it again */
1151     dn = ldap_get_dn(ldc->ldap, entry);
1152     *binddn = apr_pstrdup(st->pool, dn);
1153     ldap_memfree(dn);
1154
1155     /*
1156      * Get values for the provided attributes.
1157      */
1158     if (attrs) {
1159         int k = 0;
1160         int i = 0;
1161         while (attrs[k++]);
1162         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1163         while (attrs[i]) {
1164             char **values;
1165             int j = 0;
1166             char *str = NULL;
1167             /* get values */
1168             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1169             while (values && values[j]) {
1170                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
1171                 j++;
1172             }
1173             ldap_value_free(values);
1174             vals[i] = str;
1175             i++;
1176         }
1177         *retvals = vals;
1178     }
1179
1180     /*          
1181      * Add the new username to the search cache.
1182      */
1183     if (curl) {
1184         LDAP_CACHE_LOCK();
1185         the_search_node.username = filter;
1186         the_search_node.dn = *binddn;
1187         the_search_node.bindpw = NULL;
1188         the_search_node.lastbind = apr_time_now();
1189         the_search_node.vals = vals;
1190
1191         /* Search again to make sure that another thread didn't ready insert this node
1192            into the cache before we got here. If it does exist then update the lastbind */
1193         search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
1194         if ((search_nodep == NULL) || 
1195             (strcmp(*binddn, search_nodep->dn) != 0)) {
1196
1197             /* Nothing in cache, insert new entry */
1198             util_ald_cache_insert(curl->search_cache, &the_search_node);
1199         }
1200         /*
1201          * Don't update lastbind on entries with bindpw because
1202          * we haven't verified that password. It's OK to update
1203          * the entry if there is no password in it.
1204          */
1205         else if (!search_nodep->bindpw) {
1206             /* Cache entry is valid, update lastbind */
1207             search_nodep->lastbind = the_search_node.lastbind;
1208         }
1209         LDAP_CACHE_UNLOCK();
1210     }
1211
1212     ldap_msgfree(res);
1213
1214     ldc->reason = "Search successful";
1215     return LDAP_SUCCESS;
1216 }
1217
1218 /*
1219  * Reports if ssl support is enabled 
1220  *
1221  * 1 = enabled, 0 = not enabled
1222  */
1223 LDAP_DECLARE(int) util_ldap_ssl_supported(request_rec *r)
1224 {
1225    util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1226                                 r->server->module_config, &ldap_module);
1227
1228    return(st->ssl_supported);
1229 }
1230
1231
1232 /* ---------------------------------------- */
1233 /* config directives */
1234
1235
1236 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes)
1237 {
1238     util_ldap_state_t *st = 
1239         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1240                                                   &ldap_module);
1241
1242     st->cache_bytes = atol(bytes);
1243
1244     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1245                  "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1246                  " cache size to %" APR_SIZE_T_FMT " bytes.", 
1247                  getpid(), st->cache_bytes);
1248
1249     return NULL;
1250 }
1251
1252 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy, const char *file)
1253 {
1254     util_ldap_state_t *st = 
1255         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1256                                                   &ldap_module);
1257
1258     if (file) {
1259         st->cache_file = ap_server_root_relative(st->pool, file);
1260     }
1261     else {
1262         st->cache_file = NULL;
1263     }
1264
1265     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1266                  "LDAP cache: Setting shared memory cache file to %s bytes.", 
1267                  st->cache_file);
1268
1269     return NULL;
1270 }
1271
1272 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
1273 {
1274     util_ldap_state_t *st = 
1275         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1276                                                   &ldap_module);
1277
1278     st->search_cache_ttl = atol(ttl) * 1000000;
1279
1280     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1281                       "[%d] ldap cache: Setting cache TTL to %ld microseconds.", 
1282                       getpid(), st->search_cache_ttl);
1283
1284     return NULL;
1285 }
1286
1287 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size)
1288 {
1289     util_ldap_state_t *st = 
1290         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1291                                                   &ldap_module);
1292
1293
1294     st->search_cache_size = atol(size);
1295     if (st->search_cache_size < 0) {
1296         st->search_cache_size = 0;
1297     }
1298
1299     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1300                       "[%d] ldap cache: Setting search cache size to %ld entries.", 
1301                       getpid(), st->search_cache_size);
1302
1303     return NULL;
1304 }
1305
1306 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
1307 {
1308     util_ldap_state_t *st = 
1309         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1310                                                   &ldap_module);
1311
1312     st->compare_cache_ttl = atol(ttl) * 1000000;
1313
1314     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1315                       "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.", 
1316                       getpid(), st->compare_cache_ttl);
1317
1318     return NULL;
1319 }
1320
1321 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size)
1322 {
1323     util_ldap_state_t *st = 
1324         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1325                                                   &ldap_module);
1326
1327     st->compare_cache_size = atol(size);
1328     if (st->compare_cache_size < 0) {
1329         st->compare_cache_size = 0;
1330     }
1331
1332     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1333                       "[%d] ldap cache: Setting operation cache size to %ld entries.", 
1334                       getpid(), st->compare_cache_size);
1335
1336     return NULL;
1337 }
1338
1339
1340 /**
1341  * Parse the certificate type.
1342  *
1343  * The type can be one of the following:
1344  * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
1345  * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
1346  *
1347  * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1348  */
1349 static const int util_ldap_parse_cert_type(const char *type) {
1350
1351     /* Authority file in binary DER format */
1352     if (0 == strcasecmp("CA_DER", type)) {
1353         return APR_LDAP_CA_TYPE_DER;
1354     }
1355
1356     /* Authority file in Base64 format */
1357     else if (0 == strcasecmp("CA_BASE64", type)) {
1358         return APR_LDAP_CA_TYPE_BASE64;
1359     }
1360
1361     /* Netscape certificate database file/directory */
1362     else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1363         return APR_LDAP_CA_TYPE_CERT7_DB;
1364     }
1365
1366     /* Netscape secmod file/directory */
1367     else if (0 == strcasecmp("CA_SECMOD", type)) {
1368         return APR_LDAP_CA_TYPE_SECMOD;
1369     }
1370
1371     /* Client cert file in DER format */
1372     else if (0 == strcasecmp("CERT_DER", type)) {
1373         return APR_LDAP_CERT_TYPE_DER;
1374     }
1375
1376     /* Client cert file in Base64 format */
1377     else if (0 == strcasecmp("CERT_BASE64", type)) {
1378         return APR_LDAP_CERT_TYPE_BASE64;
1379     }
1380
1381     /* Client cert file in PKCS#12 format */
1382     else if (0 == strcasecmp("CERT_PFX", type)) {
1383         return APR_LDAP_CERT_TYPE_PFX;
1384     }
1385
1386     /* Netscape client cert database file/directory */
1387     else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1388         return APR_LDAP_CERT_TYPE_KEY3_DB;
1389     }
1390
1391     /* Netscape client cert nickname */
1392     else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1393         return APR_LDAP_CERT_TYPE_NICKNAME;
1394     }
1395
1396     /* Client cert key file in DER format */
1397     else if (0 == strcasecmp("KEY_DER", type)) {
1398         return APR_LDAP_KEY_TYPE_DER;
1399     }
1400
1401     /* Client cert key file in Base64 format */
1402     else if (0 == strcasecmp("KEY_BASE64", type)) {
1403         return APR_LDAP_KEY_TYPE_BASE64;
1404     }
1405
1406     /* Client cert key file in PKCS#12 format */
1407     else if (0 == strcasecmp("KEY_PFX", type)) {
1408         return APR_LDAP_KEY_TYPE_PFX;
1409     }
1410
1411     else {
1412         return APR_LDAP_CA_TYPE_UNKNOWN;
1413     }
1414
1415 }
1416
1417
1418 /**
1419  * Set LDAPTrustedGlobalCert.
1420  *
1421  * This directive takes either two or three arguments:
1422  * - certificate type
1423  * - certificate file / directory / nickname
1424  * - certificate password (optional)
1425  *
1426  * This directive may only be used globally.
1427  */
1428 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd, void *dummy, const char *type, const char *file, const char *password)
1429 {
1430     util_ldap_state_t *st =
1431         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1432                                                   &ldap_module);
1433     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1434     apr_finfo_t finfo;
1435     apr_status_t rv;
1436     int cert_type = 0;
1437     apr_ldap_opt_tls_cert_t *cert;
1438
1439     if (err != NULL) {
1440         return err;
1441     }
1442
1443     /* handle the certificate type */
1444     if (type) {
1445         cert_type = util_ldap_parse_cert_type(type);
1446         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1447            return apr_psprintf(cmd->pool, "The certificate type %s is "
1448                                           "not recognised. It should be one "
1449                                           "of CA_DER, CA_BASE64, CA_CERT7_DB, "
1450                                           "CA_SECMOD, CERT_DER, CERT_BASE64, "
1451                                           "CERT_KEY3_DB, CERT_NICKNAME, "
1452                                           "KEY_DER, KEY_BASE64", type);
1453         }
1454     }
1455     else {
1456         return "Certificate type was not specified.";
1457     }
1458
1459     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1460                       "LDAP: SSL trusted global cert - %s (type %s)",
1461                        file, type);
1462
1463     /* add the certificate to the global array */
1464     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1465     cert->type = cert_type;
1466     cert->path = file;
1467     cert->password = password;
1468
1469     /* if file is a file or path, fix the path */
1470     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1471         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1472
1473         cert->path = ap_server_root_relative(cmd->pool, file);
1474         if (cert->path &&
1475             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) != APR_SUCCESS)) {
1476             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1477                          "LDAP: Could not open SSL trusted certificate "
1478                          "authority file - %s",
1479                          cert->path == NULL ? file : cert->path);
1480             return "Invalid global certificate file path";
1481         }
1482
1483     }
1484
1485     return(NULL);
1486 }
1487
1488
1489 /**
1490  * Set LDAPTrustedClientCert.
1491  *
1492  * This directive takes either two or three arguments:
1493  * - certificate type
1494  * - certificate file / directory / nickname
1495  * - certificate password (optional)
1496  */
1497 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd, void *config, const char *type, const char *file, const char *password)
1498 {
1499     util_ldap_state_t *st =
1500         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1501                                                   &ldap_module);
1502     apr_finfo_t finfo;
1503     apr_status_t rv;
1504     int cert_type = 0;
1505     apr_ldap_opt_tls_cert_t *cert;
1506
1507     /* handle the certificate type */
1508     if (type) {
1509         cert_type = util_ldap_parse_cert_type(type);
1510         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
1511             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1512                                            "not recognised. It should be one "
1513                                            "of CERT_DER, CERT_BASE64, "
1514                                            "CERT_NICKNAME, CERT_PFX,"
1515                                            "KEY_DER, KEY_BASE64, KEY_PFX", type);
1516         }
1517         else if (APR_LDAP_CA_TYPE_DER == cert_type ||
1518                  APR_LDAP_CA_TYPE_BASE64 == cert_type ||
1519                  APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
1520                  APR_LDAP_CA_TYPE_SECMOD == cert_type ||
1521                  APR_LDAP_CERT_TYPE_PFX == cert_type ||
1522                  APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
1523             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
1524                                            "only valid within a "
1525                                            "LDAPTrustedGlobalCert directive. "
1526                                            "Only CERT_DER, CERT_BASE64, "
1527                                            "CERT_NICKNAME, KEY_DER, and "
1528                                            "KEY_BASE64 may be used.", type);
1529         }
1530     }
1531     else {
1532         return "Certificate type was not specified.";
1533     }
1534
1535     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1536                       "LDAP: SSL trusted client cert - %s (type %s)",
1537                        file, type);
1538
1539     /* add the certificate to the global array */
1540     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
1541     cert->type = cert_type; 
1542     cert->path = file;
1543     cert->password = password;
1544
1545     /* if file is a file or path, fix the path */
1546     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
1547         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
1548
1549         cert->path = ap_server_root_relative(cmd->pool, file);
1550         if (cert->path &&
1551             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool)) != APR_SUCCESS)) {
1552             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1553                          "LDAP: Could not open SSL client certificate "
1554                          "file - %s",
1555                          cert->path == NULL ? file : cert->path);
1556             return "Invalid client certificate file path";
1557         }
1558
1559     }
1560
1561     return(NULL);
1562 }
1563
1564
1565 /**
1566  * Set LDAPTrustedMode.
1567  *                    
1568  * This directive sets what encryption mode to use on a connection:
1569  * - None (No encryption)
1570  * - SSL (SSL encryption)
1571  * - STARTTLS (TLS encryption)
1572  */ 
1573 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy, const char *mode)
1574 {
1575     util_ldap_state_t *st =
1576     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1577                                               &ldap_module);
1578
1579     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
1580                       "LDAP: SSL trusted mode - %s",
1581                        mode);
1582
1583     if (0 == strcasecmp("NONE", mode)) {
1584         st->secure = APR_LDAP_NONE;
1585     }
1586     else if (0 == strcasecmp("SSL", mode)) {
1587         st->secure = APR_LDAP_SSL;
1588     }
1589     else if (0 == strcasecmp("TLS", mode) || 0 == strcasecmp("STARTTLS", mode)) {
1590         st->secure = APR_LDAP_STARTTLS;
1591     }
1592     else {
1593         return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1594                "SSL, or TLS/STARTTLS";
1595     }
1596
1597     st->secure_set = 1;
1598     return(NULL);
1599 }
1600
1601 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd, void *dummy, const char *ttl)
1602 {
1603     util_ldap_state_t *st = 
1604         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, 
1605                                                   &ldap_module);
1606     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1607
1608     if (err != NULL) {
1609         return err;
1610     }
1611
1612 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1613     st->connectionTimeout = atol(ttl);
1614
1615     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, 
1616                       "[%d] ldap connection: Setting connection timeout to %ld seconds.", 
1617                       getpid(), st->connectionTimeout);
1618 #else
1619     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
1620                      "LDAP: Connection timout option not supported by the LDAP SDK in use." );
1621 #endif
1622
1623     return NULL;
1624 }
1625
1626
1627 void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1628 {
1629     util_ldap_state_t *st = 
1630         (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1631
1632     st->pool = p;
1633
1634     st->cache_bytes = 100000;
1635     st->search_cache_ttl = 600000000;
1636     st->search_cache_size = 1024;
1637     st->compare_cache_ttl = 600000000;
1638     st->compare_cache_size = 1024;
1639     st->connections = NULL;
1640     st->ssl_supported = 0;
1641     st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1642     st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
1643     st->secure = APR_LDAP_NONE;
1644     st->secure_set = 0;
1645     st->connectionTimeout = 10;
1646
1647     return st;
1648 }
1649
1650 static void *util_ldap_merge_config(apr_pool_t *p, void *basev, void *overridesv)
1651 {
1652     util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
1653     util_ldap_state_t *base = (util_ldap_state_t *) basev;
1654     util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
1655
1656     st->pool = p;
1657
1658     st->cache_bytes = base->cache_bytes;
1659     st->search_cache_ttl = base->search_cache_ttl;
1660     st->search_cache_size = base->search_cache_size;
1661     st->compare_cache_ttl = base->compare_cache_ttl;
1662     st->compare_cache_size = base->compare_cache_size;
1663     st->connections = base->connections;
1664     st->ssl_supported = base->ssl_supported;
1665     st->global_certs = apr_array_append(p, base->global_certs, overrides->global_certs);
1666     st->client_certs = apr_array_append(p, base->client_certs, overrides->client_certs);
1667     st->secure = (overrides->secure_set == 0) ? base->secure : overrides->secure;
1668
1669     return st;
1670 }
1671
1672 static apr_status_t util_ldap_cleanup_module(void *data)
1673 {
1674
1675     server_rec *s = data;
1676     util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1677         s->module_config, &ldap_module);
1678     
1679     if (st->ssl_supported) {
1680         apr_ldap_ssl_deinit();
1681     }
1682
1683     return APR_SUCCESS;
1684
1685 }
1686
1687 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog, 
1688                                  apr_pool_t *ptemp, server_rec *s)
1689 {
1690     apr_status_t result;
1691     char buf[MAX_STRING_LEN];
1692     server_rec *s_vhost;
1693     util_ldap_state_t *st_vhost;
1694
1695     util_ldap_state_t *st =
1696         (util_ldap_state_t *)ap_get_module_config(s->module_config, &ldap_module);
1697
1698     void *data;
1699     const char *userdata_key = "util_ldap_init";
1700     apr_ldap_err_t *result_err = NULL;
1701     int rc;
1702     struct timeval timeOut = {10,0};    /* 10 second connection timeout */
1703
1704     /* util_ldap_post_config() will be called twice. Don't bother
1705      * going through all of the initialization on the first call
1706      * because it will just be thrown away.*/
1707     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
1708     if (!data) {
1709         apr_pool_userdata_set((const void *)1, userdata_key,
1710                                apr_pool_cleanup_null, s->process->pool);
1711
1712 #if APR_HAS_SHARED_MEMORY
1713         /* If the cache file already exists then delete it.  Otherwise we are
1714          * going to run into problems creating the shared memory. */
1715         if (st->cache_file) {
1716             char *lck_file = apr_pstrcat (st->pool, st->cache_file, ".lck", NULL);
1717             apr_file_remove(st->cache_file, ptemp);
1718             apr_file_remove(lck_file, ptemp);
1719         }
1720 #endif
1721         return OK;
1722     }
1723
1724 #if APR_HAS_SHARED_MEMORY
1725     /* initializing cache if shared memory size is not zero and we already don't have shm address */
1726     if (!st->cache_shm && st->cache_bytes > 0) {
1727 #endif
1728         result = util_ldap_cache_init(p, st);
1729         if (result != APR_SUCCESS) {
1730             apr_strerror(result, buf, sizeof(buf));
1731             ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
1732                          "LDAP cache: error while creating a shared memory segment: %s", buf);
1733         }
1734
1735
1736 #if APR_HAS_SHARED_MEMORY
1737         if (st->cache_file) {
1738             st->lock_file = apr_pstrcat (st->pool, st->cache_file, ".lck", NULL);
1739         }
1740         else
1741 #endif
1742             st->lock_file = ap_server_root_relative(st->pool, tmpnam(NULL));
1743
1744         result = apr_global_mutex_create(&st->util_ldap_cache_lock, st->lock_file, APR_LOCK_DEFAULT, st->pool);
1745         if (result != APR_SUCCESS) {
1746             return result;
1747         }
1748
1749 #ifdef AP_NEED_SET_MUTEX_PERMS
1750         result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
1751         if (result != APR_SUCCESS) {
1752             ap_log_error(APLOG_MARK, APLOG_CRIT, result, s, 
1753                          "LDAP cache: failed to set mutex permissions");
1754             return result;
1755         }
1756 #endif
1757
1758         /* merge config in all vhost */
1759         s_vhost = s->next;
1760         while (s_vhost) {
1761             st_vhost = (util_ldap_state_t *)ap_get_module_config(s_vhost->module_config, &ldap_module);
1762
1763 #if APR_HAS_SHARED_MEMORY
1764             st_vhost->cache_shm = st->cache_shm;
1765             st_vhost->cache_rmm = st->cache_rmm;
1766             st_vhost->cache_file = st->cache_file;
1767             ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s, 
1768                          "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp for VHOST: %s",
1769                          st->cache_shm, st->cache_rmm, s_vhost->server_hostname);
1770 #endif
1771             st_vhost->lock_file = st->lock_file;
1772             s_vhost = s_vhost->next;
1773         }
1774 #if APR_HAS_SHARED_MEMORY
1775     }
1776     else {
1777         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "LDAP cache: LDAPSharedCacheSize is zero, disabling shared memory cache");
1778     }
1779 #endif
1780     
1781     /* log the LDAP SDK used 
1782      */
1783     {
1784         apr_ldap_err_t *result = NULL;
1785         apr_ldap_info(p, &(result));
1786         if (result != NULL) {
1787             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "%s", result->reason);
1788         }
1789     }
1790
1791     apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1792                               util_ldap_cleanup_module); 
1793
1794     /*
1795      * Initialize SSL support, and log the result for the benefit of the admin.
1796      *
1797      * If SSL is not supported it is not necessarily an error, as the
1798      * application may not want to use it.
1799      */
1800     rc = apr_ldap_ssl_init(p, 
1801                       NULL, 
1802                       0, 
1803                       &(result_err));
1804     if (APR_SUCCESS == rc) {
1805         rc = apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT,
1806                                  (void *)st->global_certs, &(result_err));
1807     }
1808
1809     if (APR_SUCCESS == rc) {
1810         st->ssl_supported = 1;
1811         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1812                          "LDAP: SSL support available" );
1813     }
1814     else {
1815         st->ssl_supported = 0;
1816         if (NULL != result_err) {
1817             ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "%s", result_err->reason);
1818         }
1819         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, 
1820                          "LDAP: SSL support unavailable" );
1821     }
1822
1823 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1824     if (st->connectionTimeout > 0) {
1825         timeOut.tv_sec = st->connectionTimeout;
1826     }
1827
1828     if (st->connectionTimeout >= 0) {
1829         rc = apr_ldap_set_option(p, NULL, LDAP_OPT_NETWORK_TIMEOUT,
1830                                  (void *)&timeOut, &(result_err));
1831         if (APR_SUCCESS != rc) {
1832             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1833                              "LDAP: Could not set the connection timeout" );
1834         }
1835     }
1836 #endif
1837
1838     
1839     return(OK);
1840 }
1841
1842 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
1843 {
1844     apr_status_t sts;
1845     util_ldap_state_t *st = ap_get_module_config(s->module_config, &ldap_module);
1846
1847     if (!st->util_ldap_cache_lock) return;
1848
1849     sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock, st->lock_file, p);
1850     if (sts != APR_SUCCESS) {
1851         ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
1852                      "Failed to initialise global mutex %s in child process %"
1853                      APR_PID_T_FMT
1854                      ".",
1855                      st->lock_file, getpid());
1856         return;
1857     }
1858     else {
1859         ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, s, 
1860                      "Initialisation of global mutex %s in child process %"
1861                      APR_PID_T_FMT
1862                      " successful.",
1863                      st->lock_file, getpid());
1864     }
1865 }
1866
1867 command_rec util_ldap_cmds[] = {
1868     AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, NULL, RSRC_CONF,
1869                   "Sets the size of the shared memory cache in bytes. "
1870                   "Zero means disable the shared memory cache. Defaults to 100KB."),
1871
1872     AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file, NULL, RSRC_CONF,
1873                   "Sets the file of the shared memory cache."
1874                   "Nothing means disable the shared memory cache."),
1875
1876     AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, NULL, RSRC_CONF,
1877                   "Sets the maximum number of entries that are possible in the LDAP "
1878                   "search cache. "
1879                   "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
1880
1881     AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, NULL, RSRC_CONF,
1882                   "Sets the maximum time (in seconds) that an item can be cached in the LDAP "
1883                   "search cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
1884
1885     AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, NULL, RSRC_CONF,
1886                   "Sets the maximum number of entries that are possible in the LDAP "
1887                   "compare cache. "
1888                   "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
1889
1890     AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, NULL, RSRC_CONF,
1891                   "Sets the maximum time (in seconds) that an item is cached in the LDAP "
1892                   "operation cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
1893
1894     AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert, NULL, RSRC_CONF,
1895                   "Sets the file and/or directory containing the trusted "
1896                   "certificate authority certificates, and global client "
1897                   "certificates (Netware). Used to validate the LDAP server "
1898                   "certificate for SSL/TLS connections. "
1899                   "The following types are supported:  "
1900                   "  CA_DER        - Authority file in binary DER format "
1901                   "  CA_BASE64     - Authority file in Base64 format "
1902                   "  CA_CERT7_DB   - Netscape certificate database file/directory "
1903                   "  CA_SECMOD     - Netscape secmod file/directory "
1904                   "  CERT_DER      - Client cert file in DER format "
1905                   "  CERT_BASE64   - Client cert file in Base64 format "
1906                   "  CERT_KEY3_DB  - Netscape client cert database file/directory "
1907                   "  CERT_NICKNAME - Netscape client cert nickname "
1908                   "  KEY_DER       - Client cert key file in DER format "
1909                   "  KEY_BASE64    - Client cert key file in Base64 format "),
1910
1911     AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert, NULL, OR_ALL,
1912                   "Specifies a file containing a client certificate or private "
1913                   "key, or the ID of the certificate to usethe type of the Certificate Authority file.  "
1914                  "The following types are supported:  "
1915                  "  CA_DER        - Authority file in binary DER format "
1916                  "  CA_BASE64     - Authority file in Base64 format "
1917                  "  CA_CERT7_DB   - Netscape certificate database file/directory "
1918                  "  CA_SECMOD     - Netscape secmod file/directory "
1919                  "  CERT_DER      - Client cert file in DER format "
1920                  "  CERT_BASE64   - Client cert file in Base64 format "
1921                  "  CERT_KEY3_DB  - Netscape client cert database file/directory "
1922                  "  CERT_NICKNAME - Netscape client cert nickname "
1923                  "  KEY_DER       - Client cert key file in DER format "
1924                  "  KEY_BASE64    - Client cert key file in Base64 format "),
1925
1926     AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode, NULL, OR_ALL,
1927                   "Specifies the type of security that should be applied to "
1928                   "an LDAP connection. The types supported are: "
1929                   "   NONE - no encryption enabled "
1930                   "   SSL - SSL encryption enabled (forced by ldaps://) "
1931                   "   STARTTLS - STARTTLS MUST be enabled "),
1932
1933     AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout, NULL, RSRC_CONF,
1934                   "Specifies the LDAP socket connection timeout in seconds. "
1935                   "Default is 10 seconds. "),
1936
1937     {NULL}
1938 };
1939
1940 static void util_ldap_register_hooks(apr_pool_t *p)
1941 {
1942     ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
1943     ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
1944     ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1945 }
1946
1947 module ldap_module = {
1948    STANDARD20_MODULE_STUFF,
1949    NULL,                                /* dir config creater */
1950    NULL,                                /* dir merger --- default is to override */
1951    util_ldap_create_config,             /* server config */
1952    util_ldap_merge_config,              /* merge server config */
1953    util_ldap_cmds,                      /* command table */
1954    util_ldap_register_hooks,            /* set up request processing hooks */
1955 };