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