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