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