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