]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap.c
* Fix a typo (timout -> timeout)
[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 apr_status_t util_ldap_connection_remove (void *param);
70
71 static void util_ldap_strdup (char **str, const char *newstr)
72 {
73     if (*str) {
74         free(*str);
75         *str = NULL;
76     }
77
78     if (newstr) {
79         *str = strdup(newstr);
80     }
81 }
82
83 /*
84  * Status Handler
85  * --------------
86  *
87  * This handler generates a status page about the current performance of
88  * the LDAP cache. It is enabled as follows:
89  *
90  * <Location /ldap-status>
91  *   SetHandler ldap-status
92  * </Location>
93  *
94  */
95 static int util_ldap_handler(request_rec *r)
96 {
97     util_ldap_state_t *st = (util_ldap_state_t *)
98                             ap_get_module_config(r->server->module_config,
99                                                  &ldap_module);
100
101     r->allowed |= (1 << M_GET);
102     if (r->method_number != M_GET)
103         return DECLINED;
104
105     if (strcmp(r->handler, "ldap-status")) {
106         return DECLINED;
107     }
108
109     ap_set_content_type(r, "text/html; charset=ISO-8859-1");
110
111     if (r->header_only)
112         return OK;
113
114     ap_rputs(DOCTYPE_HTML_3_2
115              "<html><head><title>LDAP Cache Information</title></head>\n", r);
116     ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information"
117              "</h1>\n", r);
118
119     util_ald_cache_display(r, st);
120
121     return OK;
122 }
123
124
125
126 /* ------------------------------------------------------------------ */
127 /*
128  * Closes an LDAP connection by unlocking it. The next time
129  * uldap_connection_find() is called this connection will be
130  * available for reuse.
131  */
132 static void uldap_connection_close(util_ldap_connection_t *ldc)
133 {
134
135     /*
136      * QUESTION:
137      *
138      * Is it safe leaving bound connections floating around between the
139      * different modules? Keeping the user bound is a performance boost,
140      * but it is also a potential security problem - maybe.
141      *
142      * For now we unbind the user when we finish with a connection, but
143      * we don't have to...
144      */
145
146      if (!ldc->keep) { 
147          util_ldap_connection_remove(ldc);
148      }
149      else { 
150          /* mark our connection as available for reuse */
151 #if APR_HAS_THREADS
152          apr_thread_mutex_unlock(ldc->lock);
153 #endif
154      }
155 }
156
157
158 /*
159  * Destroys an LDAP connection by unbinding and closing the connection to
160  * the LDAP server. It is used to bring the connection back to a known
161  * state after an error.
162  */
163 static apr_status_t uldap_connection_unbind(void *param)
164 {
165     util_ldap_connection_t *ldc = param;
166
167     if (ldc) {
168         if (ldc->ldap) {
169             ldap_unbind_s(ldc->ldap);
170             ldc->ldap = NULL;
171         }
172         ldc->bound = 0;
173     }
174
175     return APR_SUCCESS;
176 }
177
178
179 /*
180  * Clean up an LDAP connection by unbinding and unlocking the connection.
181  * This cleanup does not remove the util_ldap_connection_t from the 
182  * per-virtualhost list of connections, does not remove the storage
183  * for the util_ldap_connection_t or it's data, and is NOT run automatically.
184  */
185 static apr_status_t uldap_connection_cleanup(void *param)
186 {
187     util_ldap_connection_t *ldc = param;
188
189     if (ldc) {
190         /* Release the rebind info for this connection. No more referral rebinds required. */
191         apr_ldap_rebind_remove(ldc->ldap);
192
193         /* unbind and disconnect from the LDAP server */
194         uldap_connection_unbind(ldc);
195
196         /* free the username and password */
197         if (ldc->bindpw) {
198             free((void*)ldc->bindpw);
199         }
200         if (ldc->binddn) {
201             free((void*)ldc->binddn);
202         }
203         /* ldc->reason is allocated from r->pool */
204         if (ldc->reason) {
205             ldc->reason = NULL;
206         }
207         /* unlock this entry */
208         uldap_connection_close(ldc);
209
210      }
211
212     return APR_SUCCESS;
213 }
214
215 /*
216  * util_ldap_connection_remove frees all storage associated with the LDAP
217  * connection and removes it completely from the per-virtualhost list of
218  * connections
219  *
220  * The caller should hold the lock for this connection
221  */
222 static apr_status_t util_ldap_connection_remove (void *param) { 
223     util_ldap_connection_t *ldc = param, *l  = NULL, *prev = NULL;
224     util_ldap_state_t *st = ldc->st;
225
226     if (!ldc) return APR_SUCCESS;
227
228     uldap_connection_unbind(ldc);
229
230 #if APR_HAS_THREADS
231     apr_thread_mutex_lock(st->mutex);
232 #endif
233
234     /* Remove ldc from the list */
235     for (l=st->connections; l; l=l->next) {
236         if (l == ldc) {
237             if (prev) {
238                 prev->next = l->next; 
239             }
240             else { 
241                 st->connections = l->next;
242             }
243             break;
244         }
245         prev = l;
246     }
247
248     /* Some unfortunate duplication between this method
249      * and uldap_connection_cleanup()
250     */
251     if (ldc->bindpw) {
252         free((void*)ldc->bindpw);
253     }
254     if (ldc->binddn) {
255         free((void*)ldc->binddn);
256     }
257
258 #if APR_HAS_THREADS
259     apr_thread_mutex_unlock(ldc->lock);
260     apr_thread_mutex_unlock(st->mutex);
261 #endif
262
263     /* Destory the pool associated with this connection */
264
265     apr_pool_destroy(ldc->pool);   
266    
267     return APR_SUCCESS;
268 }
269
270 static int uldap_connection_init(request_rec *r,
271                                  util_ldap_connection_t *ldc)
272 {
273     int rc = 0, ldap_option = 0;
274     int version  = LDAP_VERSION3;
275     apr_ldap_err_t *result = NULL;
276 #ifdef LDAP_OPT_NETWORK_TIMEOUT
277     struct timeval timeOut = {10,0};    /* 10 second connection timeout */
278 #endif
279     util_ldap_state_t *st =
280         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
281         &ldap_module);
282
283     /* Since the host will include a port if the default port is not used,
284      * always specify the default ports for the port parameter.  This will
285      * allow a host string that contains multiple hosts the ability to mix
286      * some hosts with ports and some without. All hosts which do not
287      * specify a port will use the default port.
288      */
289     apr_ldap_init(r->pool, &(ldc->ldap),
290                   ldc->host,
291                   APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
292                   APR_LDAP_NONE,
293                   &(result));
294
295     if (result != NULL && result->rc) {
296         ldc->reason = result->reason;
297     }
298
299     if (NULL == ldc->ldap)
300     {
301         ldc->bound = 0;
302         if (NULL == ldc->reason) {
303             ldc->reason = "LDAP: ldap initialization failed";
304         }
305         else {
306             ldc->reason = result->reason;
307         }
308         return(result->rc);
309     }
310
311     /* Now that we have an ldap struct, add it to the referral list for rebinds. */
312     rc = apr_ldap_rebind_add(ldc->pool, ldc->ldap, ldc->binddn, ldc->bindpw);
313     if (rc != APR_SUCCESS) {
314         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
315                      "LDAP: Unable to add rebind cross reference entry. Out of memory?");
316         uldap_connection_unbind(ldc);
317         ldc->reason = "LDAP: Unable to add rebind cross reference entry.";
318         return(rc);
319     }
320
321     /* always default to LDAP V3 */
322     ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
323
324     /* set client certificates */
325     if (!apr_is_empty_array(ldc->client_certs)) {
326         apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_TLS_CERT,
327                             ldc->client_certs, &(result));
328         if (LDAP_SUCCESS != result->rc) {
329             uldap_connection_unbind( ldc );
330             ldc->reason = result->reason;
331             return(result->rc);
332         }
333     }
334
335     /* switch on SSL/TLS */
336     if (APR_LDAP_NONE != ldc->secure) {
337         apr_ldap_set_option(r->pool, ldc->ldap,
338                             APR_LDAP_OPT_TLS, &ldc->secure, &(result));
339         if (LDAP_SUCCESS != result->rc) {
340             uldap_connection_unbind( ldc );
341             ldc->reason = result->reason;
342             return(result->rc);
343         }
344     }
345
346     /* Set the alias dereferencing option */
347     ldap_option = ldc->deref;
348     ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &ldap_option);
349
350     /* Set options for rebind and referrals. */
351     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
352                  "LDAP: Setting referrals to %s.",
353                  ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"));
354     apr_ldap_set_option(r->pool, ldc->ldap,
355                         APR_LDAP_OPT_REFERRALS,
356                         (void *)((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ?
357                                  LDAP_OPT_ON : LDAP_OPT_OFF),
358                         &(result));
359     if (result->rc != LDAP_SUCCESS) {
360         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
361                      "Unable to set LDAP_OPT_REFERRALS option to %s: %d.",
362                      ((ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) ? "On" : "Off"),
363                      result->rc);
364         result->reason = "Unable to set LDAP_OPT_REFERRALS.";
365         uldap_connection_unbind(ldc);
366         return(result->rc);
367     }
368
369     if (ldc->ChaseReferrals == AP_LDAP_CHASEREFERRALS_ON) {
370         /* Referral hop limit - only if referrals are enabled */
371         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
372                      "Setting referral hop limit to %d.",
373                      ldc->ReferralHopLimit);
374         apr_ldap_set_option(r->pool, ldc->ldap,
375                             APR_LDAP_OPT_REFHOPLIMIT,
376                             (void *)&ldc->ReferralHopLimit,
377                             &(result));
378         if (result->rc != LDAP_SUCCESS) {
379           ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
380                        "Unable to set LDAP_OPT_REFHOPLIMIT option to %d: %d.",
381                        ldc->ReferralHopLimit,
382                        result->rc);
383           result->reason = "Unable to set LDAP_OPT_REFHOPLIMIT.";
384           uldap_connection_unbind(ldc);
385           return(result->rc);
386         }
387     }
388
389 /*XXX All of the #ifdef's need to be removed once apr-util 1.2 is released */
390 #ifdef APR_LDAP_OPT_VERIFY_CERT
391     apr_ldap_set_option(r->pool, ldc->ldap, APR_LDAP_OPT_VERIFY_CERT,
392                         &(st->verify_svr_cert), &(result));
393 #else
394 #if defined(LDAPSSL_VERIFY_SERVER)
395     if (st->verify_svr_cert) {
396         result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
397     }
398     else {
399         result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
400     }
401 #elif defined(LDAP_OPT_X_TLS_REQUIRE_CERT)
402     /* This is not a per-connection setting so just pass NULL for the
403        Ldap connection handle */
404     if (st->verify_svr_cert) {
405         int i = LDAP_OPT_X_TLS_DEMAND;
406         result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
407     }
408     else {
409         int i = LDAP_OPT_X_TLS_NEVER;
410         result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
411     }
412 #endif
413 #endif
414
415 #ifdef LDAP_OPT_NETWORK_TIMEOUT
416     if (st->connectionTimeout > 0) {
417         timeOut.tv_sec = st->connectionTimeout;
418     }
419
420     if (st->connectionTimeout >= 0) {
421         rc = apr_ldap_set_option(r->pool, ldc->ldap, LDAP_OPT_NETWORK_TIMEOUT,
422                                  (void *)&timeOut, &(result));
423         if (APR_SUCCESS != rc) {
424             ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
425                              "LDAP: Could not set the connection timeout");
426         }
427     }
428 #endif
429
430     return(rc);
431 }
432
433 /*
434  * Connect to the LDAP server and binds. Does not connect if already
435  * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
436  *
437  * Returns LDAP_SUCCESS on success; and an error code on failure
438  */
439 static int uldap_connection_open(request_rec *r,
440                                  util_ldap_connection_t *ldc)
441 {
442     int rc = 0;
443     int failures = 0;
444
445     /* sanity check for NULL */
446     if (!ldc) {
447         return -1;
448     }
449
450     /* If the connection is already bound, return
451     */
452     if (ldc->bound)
453     {
454         ldc->reason = "LDAP: connection open successful (already bound)";
455         return LDAP_SUCCESS;
456     }
457
458     /* create the ldap session handle
459     */
460     if (NULL == ldc->ldap)
461     {
462        rc = uldap_connection_init( r, ldc );
463        if (LDAP_SUCCESS != rc)
464        {
465            return rc;
466        }
467     }
468
469
470     /* loop trying to bind up to 10 times if LDAP_SERVER_DOWN error is
471      * returned.  Break out of the loop on Success or any other error.
472      *
473      * NOTE: Looping is probably not a great idea. If the server isn't
474      * responding the chances it will respond after a few tries are poor.
475      * However, the original code looped and it only happens on
476      * the error condition.
477       */
478     for (failures=0; failures<10; failures++)
479     {
480         rc = ldap_simple_bind_s(ldc->ldap,
481                                 (char *)ldc->binddn,
482                                 (char *)ldc->bindpw);
483         if (!AP_LDAP_IS_SERVER_DOWN(rc)) {
484             break;
485         } else if (failures == 5) {
486            /* attempt to init the connection once again */
487            uldap_connection_unbind( ldc );
488            rc = uldap_connection_init( r, ldc );
489            if (LDAP_SUCCESS != rc)
490            {
491                break;
492            }
493        }
494     }
495
496     /* free the handle if there was an error
497     */
498     if (LDAP_SUCCESS != rc)
499     {
500        uldap_connection_unbind(ldc);
501         ldc->reason = "LDAP: ldap_simple_bind_s() failed";
502     }
503     else {
504         ldc->bound = 1;
505         ldc->reason = "LDAP: connection open successful";
506     }
507
508     return(rc);
509 }
510
511
512 /*
513  * Compare client certificate arrays.
514  *
515  * Returns 1 on compare failure, 0 otherwise.
516  */
517 static int compare_client_certs(apr_array_header_t *srcs,
518                                 apr_array_header_t *dests)
519 {
520     int i = 0;
521     struct apr_ldap_opt_tls_cert_t *src, *dest;
522
523     /* arrays both NULL? if so, then equal */
524     if (srcs == NULL && dests == NULL) {
525         return 0;
526     }
527
528     /* arrays different length or either NULL? If so, then not equal */
529     if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
530         return 1;
531     }
532
533     /* run an actual comparison */
534     src = (struct apr_ldap_opt_tls_cert_t *)srcs->elts;
535     dest = (struct apr_ldap_opt_tls_cert_t *)dests->elts;
536     for (i = 0; i < srcs->nelts; i++) {
537         if (strcmp(src[i].path, dest[i].path) ||
538             strcmp(src[i].password, dest[i].password) ||
539             src[i].type != dest[i].type) {
540             return 1;
541         }
542     }
543
544     /* if we got here, the cert arrays were identical */
545     return 0;
546
547 }
548
549
550 /*
551  * Find an existing ldap connection struct that matches the
552  * provided ldap connection parameters.
553  *
554  * If not found in the cache, a new ldc structure will be allocated
555  * from st->pool and returned to the caller.  If found in the cache,
556  * a pointer to the existing ldc structure will be returned.
557  */
558 static util_ldap_connection_t *
559             uldap_connection_find(request_rec *r,
560                                   const char *host, int port,
561                                   const char *binddn, const char *bindpw,
562                                   deref_options deref, int secure)
563 {
564     struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
565     int secureflag = secure;
566
567     util_ldap_state_t *st =
568         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
569         &ldap_module);
570     util_ldap_config_t *dc =
571         (util_ldap_config_t *) ap_get_module_config(r->per_dir_config, &ldap_module);
572
573 #if APR_HAS_THREADS
574     /* mutex lock this function */
575     apr_thread_mutex_lock(st->mutex);
576 #endif
577
578     if (secure < APR_LDAP_NONE) {
579         secureflag = st->secure;
580     }
581
582     /* Search for an exact connection match in the list that is not
583      * being used.
584      */
585     for (l=st->connections,p=NULL; l; l=l->next) {
586 #if APR_HAS_THREADS
587         if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
588 #endif
589         if (   (l->port == port) && (strcmp(l->host, host) == 0)
590             && ((!l->binddn && !binddn) || (l->binddn && binddn
591                                              && !strcmp(l->binddn, binddn)))
592             && ((!l->bindpw && !bindpw) || (l->bindpw && bindpw
593                                              && !strcmp(l->bindpw, bindpw)))
594             && (l->deref == deref) && (l->secure == secureflag)
595             && !compare_client_certs(st->client_certs, l->client_certs))
596         {
597             break;
598         }
599 #if APR_HAS_THREADS
600             /* If this connection didn't match the criteria, then we
601              * need to unlock the mutex so it is available to be reused.
602              */
603             apr_thread_mutex_unlock(l->lock);
604         }
605 #endif
606         p = l;
607     }
608
609     /* If nothing found, search again, but we don't care about the
610      * binddn and bindpw this time.
611      */
612     if (!l) {
613         for (l=st->connections,p=NULL; l; l=l->next) {
614 #if APR_HAS_THREADS
615             if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
616
617 #endif
618             if ((l->port == port) && (strcmp(l->host, host) == 0) &&
619                 (l->deref == deref) && (l->secure == secureflag) &&
620                 !compare_client_certs(st->client_certs, l->client_certs))
621             {
622                 /* the bind credentials have changed */
623                 l->bound = 0;
624                 util_ldap_strdup((char**)&(l->binddn), binddn);
625                 util_ldap_strdup((char**)&(l->bindpw), bindpw);
626                 break;
627             }
628 #if APR_HAS_THREADS
629                 /* If this connection didn't match the criteria, then we
630                  * need to unlock the mutex so it is available to be reused.
631                  */
632                 apr_thread_mutex_unlock(l->lock);
633             }
634 #endif
635             p = l;
636         }
637     }
638
639 /* artificially disable cache */
640 /* l = NULL; */
641
642     /* If no connection was found after the second search, we
643      * must create one.
644      */
645     if (!l) {
646         apr_pool_t *newpool;
647         if (apr_pool_create(&newpool, NULL) != APR_SUCCESS) {
648             ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
649                           "util_ldap: Failed to create memory pool");
650 #if APR_HAS_THREADS
651             apr_thread_mutex_unlock(st->mutex);
652 #endif
653             return NULL;
654         }
655  
656         /*
657          * Add the new connection entry to the linked list. Note that we
658          * don't actually establish an LDAP connection yet; that happens
659          * the first time authentication is requested.
660          */
661
662         /* create the details of this connection in the new pool */
663         l = apr_pcalloc(newpool, sizeof(util_ldap_connection_t));
664         l->pool = newpool;
665         l->st = st;
666
667 #if APR_HAS_THREADS
668         apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, l->pool);
669         apr_thread_mutex_lock(l->lock);
670 #endif
671         l->bound = 0;
672         l->host = apr_pstrdup(l->pool, host);
673         l->port = port;
674         l->deref = deref;
675         util_ldap_strdup((char**)&(l->binddn), binddn);
676         util_ldap_strdup((char**)&(l->bindpw), bindpw);
677         l->ChaseReferrals = dc->ChaseReferrals;
678         l->ReferralHopLimit = dc->ReferralHopLimit;
679
680         /* The security mode after parsing the URL will always be either
681          * APR_LDAP_NONE (ldap://) or APR_LDAP_SSL (ldaps://).
682          * If the security setting is NONE, override it to the security
683          * setting optionally supplied by the admin using LDAPTrustedMode
684          */
685         l->secure = secureflag;
686
687         /* save away a copy of the client cert list that is presently valid */
688         l->client_certs = apr_array_copy_hdr(l->pool, st->client_certs);
689
690         l->keep = 1;
691
692         if (p) {
693             p->next = l;
694         }
695         else {
696             st->connections = l;
697         }
698     }
699
700 #if APR_HAS_THREADS
701     apr_thread_mutex_unlock(st->mutex);
702 #endif
703     return l;
704 }
705
706 /* ------------------------------------------------------------------ */
707
708 /*
709  * Compares two DNs to see if they're equal. The only way to do this correctly
710  * is to search for the dn and then do ldap_get_dn() on the result. This should
711  * match the initial dn, since it would have been also retrieved with
712  * ldap_get_dn(). This is expensive, so if the configuration value
713  * compare_dn_on_server is false, just does an ordinary strcmp.
714  *
715  * The lock for the ldap cache should already be acquired.
716  */
717 static int uldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
718                                  const char *url, const char *dn,
719                                  const char *reqdn, int compare_dn_on_server)
720 {
721     int result = 0;
722     util_url_node_t *curl;
723     util_url_node_t curnode;
724     util_dn_compare_node_t *node;
725     util_dn_compare_node_t newnode;
726     int failures = 0;
727     LDAPMessage *res, *entry;
728     char *searchdn;
729
730     util_ldap_state_t *st = (util_ldap_state_t *)
731                             ap_get_module_config(r->server->module_config,
732                                                  &ldap_module);
733
734     /* get cache entry (or create one) */
735     LDAP_CACHE_LOCK();
736
737     curnode.url = url;
738     curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
739     if (curl == NULL) {
740         curl = util_ald_create_caches(st, url);
741     }
742     LDAP_CACHE_UNLOCK();
743
744     /* a simple compare? */
745     if (!compare_dn_on_server) {
746         /* unlock this read lock */
747         if (strcmp(dn, reqdn)) {
748             ldc->reason = "DN Comparison FALSE (direct strcmp())";
749             return LDAP_COMPARE_FALSE;
750         }
751         else {
752             ldc->reason = "DN Comparison TRUE (direct strcmp())";
753             return LDAP_COMPARE_TRUE;
754         }
755     }
756
757     if (curl) {
758         /* no - it's a server side compare */
759         LDAP_CACHE_LOCK();
760
761         /* is it in the compare cache? */
762         newnode.reqdn = (char *)reqdn;
763         node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
764         if (node != NULL) {
765             /* If it's in the cache, it's good */
766             /* unlock this read lock */
767             LDAP_CACHE_UNLOCK();
768             ldc->reason = "DN Comparison TRUE (cached)";
769             return LDAP_COMPARE_TRUE;
770         }
771
772         /* unlock this read lock */
773         LDAP_CACHE_UNLOCK();
774     }
775
776 start_over:
777     if (failures++ > 10) {
778         /* too many failures */
779         return result;
780     }
781
782     /* make a server connection */
783     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
784         /* connect to server failed */
785         return result;
786     }
787
788     /* search for reqdn */
789     result = ldap_search_ext_s(ldc->ldap, (char *)reqdn, LDAP_SCOPE_BASE,
790                                "(objectclass=*)", NULL, 1,
791                                NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
792     if (AP_LDAP_IS_SERVER_DOWN(result))
793     {
794         ldc->reason = "DN Comparison ldap_search_ext_s() "
795                       "failed with server down";
796         uldap_connection_unbind(ldc);
797         goto start_over;
798     }
799     if (result != LDAP_SUCCESS) {
800         /* search for reqdn failed - no match */
801         ldc->reason = "DN Comparison ldap_search_ext_s() failed";
802         return result;
803     }
804
805     entry = ldap_first_entry(ldc->ldap, res);
806     searchdn = ldap_get_dn(ldc->ldap, entry);
807
808     ldap_msgfree(res);
809     if (strcmp(dn, searchdn) != 0) {
810         /* compare unsuccessful */
811         ldc->reason = "DN Comparison FALSE (checked on server)";
812         result = LDAP_COMPARE_FALSE;
813     }
814     else {
815         if (curl) {
816             /* compare successful - add to the compare cache */
817             LDAP_CACHE_LOCK();
818             newnode.reqdn = (char *)reqdn;
819             newnode.dn = (char *)dn;
820
821             node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
822             if (   (node == NULL)
823                 || (strcmp(reqdn, node->reqdn) != 0)
824                 || (strcmp(dn, node->dn) != 0))
825             {
826                 util_ald_cache_insert(curl->dn_compare_cache, &newnode);
827             }
828             LDAP_CACHE_UNLOCK();
829         }
830         ldc->reason = "DN Comparison TRUE (checked on server)";
831         result = LDAP_COMPARE_TRUE;
832     }
833     ldap_memfree(searchdn);
834     return result;
835
836 }
837
838 /*
839  * Does an generic ldap_compare operation. It accepts a cache that it will use
840  * to lookup the compare in the cache. We cache two kinds of compares
841  * (require group compares) and (require user compares). Each compare has a
842  * different cache node: require group includes the DN; require user does not
843  * because the require user cache is owned by the
844  *
845  */
846 static int uldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
847                                const char *url, const char *dn,
848                                const char *attrib, const char *value)
849 {
850     int result = 0;
851     util_url_node_t *curl;
852     util_url_node_t curnode;
853     util_compare_node_t *compare_nodep;
854     util_compare_node_t the_compare_node;
855     apr_time_t curtime = 0; /* silence gcc -Wall */
856     int failures = 0;
857
858     util_ldap_state_t *st = (util_ldap_state_t *)
859                             ap_get_module_config(r->server->module_config,
860                                                  &ldap_module);
861
862     /* get cache entry (or create one) */
863     LDAP_CACHE_LOCK();
864     curnode.url = url;
865     curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
866     if (curl == NULL) {
867         curl = util_ald_create_caches(st, url);
868     }
869     LDAP_CACHE_UNLOCK();
870
871     if (curl) {
872         /* make a comparison to the cache */
873         LDAP_CACHE_LOCK();
874         curtime = apr_time_now();
875
876         the_compare_node.dn = (char *)dn;
877         the_compare_node.attrib = (char *)attrib;
878         the_compare_node.value = (char *)value;
879         the_compare_node.result = 0;
880         the_compare_node.sgl_processed = 0;
881         the_compare_node.subgroupList = NULL;
882
883         compare_nodep = util_ald_cache_fetch(curl->compare_cache,
884                                              &the_compare_node);
885
886         if (compare_nodep != NULL) {
887             /* found it... */
888             if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
889                 /* ...but it is too old */
890                 util_ald_cache_remove(curl->compare_cache, compare_nodep);
891             }
892             else {
893                 /* ...and it is good */
894                 if (LDAP_COMPARE_TRUE == compare_nodep->result) {
895                     ldc->reason = "Comparison true (cached)";
896                 }
897                 else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
898                     ldc->reason = "Comparison false (cached)";
899                 }
900                 else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
901                     ldc->reason = "Comparison no such attribute (cached)";
902                 }
903                 else {
904                     ldc->reason = "Comparison undefined (cached)";
905                 }
906
907                 /* record the result code to return with the reason... */
908                 result = compare_nodep->result;
909                 /* and unlock this read lock */
910                 LDAP_CACHE_UNLOCK();
911                 return result;
912             }
913         }
914         /* unlock this read lock */
915         LDAP_CACHE_UNLOCK();
916     }
917
918 start_over:
919     if (failures++ > 10) {
920         /* too many failures */
921         return result;
922     }
923
924     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
925         /* connect failed */
926         return result;
927     }
928
929     result = ldap_compare_s(ldc->ldap,
930                             (char *)dn,
931                             (char *)attrib,
932                             (char *)value);
933     if (AP_LDAP_IS_SERVER_DOWN(result)) { 
934         /* connection failed - try again */
935         ldc->reason = "ldap_compare_s() failed with server down";
936         uldap_connection_unbind(ldc);
937         goto start_over;
938     }
939
940     ldc->reason = "Comparison complete";
941     if ((LDAP_COMPARE_TRUE == result) ||
942         (LDAP_COMPARE_FALSE == result) ||
943         (LDAP_NO_SUCH_ATTRIBUTE == result)) {
944         if (curl) {
945             /* compare completed; caching result */
946             LDAP_CACHE_LOCK();
947             the_compare_node.lastcompare = curtime;
948             the_compare_node.result = result;
949             the_compare_node.sgl_processed = 0;
950             the_compare_node.subgroupList = NULL;
951
952             /* If the node doesn't exist then insert it, otherwise just update
953              * it with the last results
954              */
955             compare_nodep = util_ald_cache_fetch(curl->compare_cache,
956                                                  &the_compare_node);
957             if (   (compare_nodep == NULL)
958                 || (strcmp(the_compare_node.dn, compare_nodep->dn) != 0)
959                 || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0)
960                 || (strcmp(the_compare_node.value, compare_nodep->value) != 0))
961             {
962                 void *junk;
963
964                 junk = util_ald_cache_insert(curl->compare_cache,
965                                              &the_compare_node);
966                 if(junk == NULL) {
967                     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
968                                   "[%" APR_PID_T_FMT "] cache_compare: Cache"
969                                   " insertion failure.", getpid());
970                 }
971             }
972             else {
973                 compare_nodep->lastcompare = curtime;
974                 compare_nodep->result = result;
975             }
976             LDAP_CACHE_UNLOCK();
977         }
978         if (LDAP_COMPARE_TRUE == result) {
979             ldc->reason = "Comparison true (adding to cache)";
980             return LDAP_COMPARE_TRUE;
981         }
982         else if (LDAP_COMPARE_FALSE == result) {
983             ldc->reason = "Comparison false (adding to cache)";
984             return LDAP_COMPARE_FALSE;
985         }
986         else {
987             ldc->reason = "Comparison no such attribute (adding to cache)";
988             return LDAP_NO_SUCH_ATTRIBUTE;
989         }
990     }
991     return result;
992 }
993
994
995 static util_compare_subgroup_t* uldap_get_subgroups(request_rec *r,
996                                                     util_ldap_connection_t *ldc,
997                                                     const char *url,
998                                                     const char *dn,
999                                                     char **subgroupAttrs,
1000                                                     apr_array_header_t *subgroupclasses)
1001 {
1002     int failures = 0;
1003     int result = LDAP_COMPARE_FALSE;
1004     util_compare_subgroup_t *res = NULL;
1005     LDAPMessage *sga_res, *entry;
1006     struct mod_auth_ldap_groupattr_entry_t *sgc_ents;
1007     apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *));
1008
1009     sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1010
1011     if (!subgroupAttrs) {
1012         return res;
1013     }
1014
1015 start_over:
1016     /*
1017      * 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups.
1018      */
1019     if (failures++ > 10) {
1020         /* too many failures */
1021         return res;
1022     }
1023
1024     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1025         /* connect failed */
1026         return res;
1027     }
1028
1029     /* try to do the search */
1030     result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE,
1031                                (char *)"cn=*", subgroupAttrs, 0,
1032                                NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res);
1033     if (AP_LDAP_IS_SERVER_DOWN(result)) {
1034         ldc->reason = "ldap_search_ext_s() for subgroups failed with server"
1035                       " down";
1036         uldap_connection_unbind(ldc);
1037         goto start_over;
1038     }
1039
1040     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1041     if (result != LDAP_SUCCESS) {
1042         ldc->reason = "ldap_search_ext_s() for subgroups failed";
1043         return res;
1044     }
1045
1046     entry = ldap_first_entry(ldc->ldap, sga_res);
1047
1048     /*
1049      * Get values for the provided sub-group attributes.
1050      */
1051     if (subgroupAttrs) {
1052         int indx = 0, tmp_sgcIndex;
1053
1054         while (subgroupAttrs[indx]) {
1055             char **values;
1056             int val_index = 0;
1057
1058             /* Get *all* matching "member" values from this group. */
1059             values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]);
1060
1061             if (values) {
1062                 val_index = 0;
1063                 /*
1064                  * Now we are going to pare the subgroup members of this group
1065                  * to *just* the subgroups, add them to the compare_nodep, and
1066                  * then proceed to check the new level of subgroups.
1067                  */
1068                 while (values[val_index]) {
1069                     /* Check if this entry really is a group. */
1070                     tmp_sgcIndex = 0;
1071                     result = LDAP_COMPARE_FALSE;
1072                     while ((tmp_sgcIndex < subgroupclasses->nelts)
1073                            && (result != LDAP_COMPARE_TRUE)) {
1074                         result = uldap_cache_compare(r, ldc, url,
1075                                                      values[val_index],
1076                                                      "objectClass",
1077                                                      sgc_ents[tmp_sgcIndex].name
1078                                                      );
1079
1080                         if (result != LDAP_COMPARE_TRUE) {
1081                             tmp_sgcIndex++;
1082                         }
1083                     }
1084                     /* It's a group, so add it to the array.  */
1085                     if (result == LDAP_COMPARE_TRUE) {
1086                         char **newgrp = (char **) apr_array_push(subgroups);
1087                         *newgrp = apr_pstrdup(r->pool, values[val_index]);
1088                     }
1089                     val_index++;
1090                 }
1091                 ldap_value_free(values);
1092             }
1093             indx++;
1094         }
1095     }
1096
1097     ldap_msgfree(sga_res);
1098
1099     if (subgroups->nelts > 0) {
1100         /* We need to fill in tmp_local_subgroups using the data from LDAP */
1101         int sgindex;
1102         char **group;
1103         res = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t));
1104         res->subgroupDNs  = apr_pcalloc(r->pool,
1105                                         sizeof(char *) * (subgroups->nelts));
1106         for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) {
1107             res->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group);
1108         }
1109         res->len = sgindex;
1110     }
1111
1112     return res;
1113 }
1114
1115
1116 /*
1117  * Does a recursive lookup operation to try to find a user within (cached)
1118  * nested groups. It accepts a cache that it will use to lookup previous
1119  * compare attempts. We cache two kinds of compares (require group compares)
1120  * and (require user compares). Each compare has a different cache node:
1121  * require group includes the DN; require user does not because the require
1122  * user cache is owned by the
1123  *
1124  * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!!
1125  *
1126  *
1127  * 1. Call uldap_cache_compare for each subgroupclass value to check the
1128  *    generic, user-agnostic, cached group entry. This will create a new generic
1129  *    cache entry if there
1130  *    wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we
1131  *    have no groups.
1132  * 2. Lock The cache and get the generic cache entry.
1133  * 3. Check if there is already a subgrouplist in this generic group's cache
1134  *    entry.
1135  *    A. If there is, go to step 4.
1136  *    B. If there isn't:
1137  *       i)   Use ldap_search to get the full list
1138  *            of subgroup "members" (which may include non-group "members").
1139  *       ii)  Use uldap_cache_compare to strip the list down to just groups.
1140  *       iii) Lock and add this stripped down list to the cache of the generic
1141  *            group.
1142  * 4. Loop through the sgl and call uldap_cache_compare (using the user info)
1143  *    for each
1144  *    subgroup to see if the subgroup contains the user and to get the subgroups
1145  *    added to the
1146  *    cache (with user-afinity, if they aren't already there).
1147  *    A. If the user is in the subgroup, then we'll be returning
1148  *       LDAP_COMPARE_TRUE.
1149  *    B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via
1150  *       uldap_cache_compare) then recursively call this function to get the
1151  *       sub-subgroups added...
1152  * 5. Cleanup local allocations.
1153  * 6. Return the final result.
1154  */
1155
1156 static int uldap_cache_check_subgroups(request_rec *r,
1157                                        util_ldap_connection_t *ldc,
1158                                        const char *url, const char *dn,
1159                                        const char *attrib, const char *value,
1160                                        char **subgroupAttrs,
1161                                        apr_array_header_t *subgroupclasses,
1162                                        int cur_subgroup_depth,
1163                                        int max_subgroup_depth)
1164 {
1165     int result = LDAP_COMPARE_FALSE;
1166     util_url_node_t *curl;
1167     util_url_node_t curnode;
1168     util_compare_node_t *compare_nodep;
1169     util_compare_node_t the_compare_node;
1170     util_compare_subgroup_t *tmp_local_sgl = NULL;
1171     int sgl_cached_empty = 0, sgindex = 0, base_sgcIndex = 0;
1172     struct mod_auth_ldap_groupattr_entry_t *sgc_ents =
1173             (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts;
1174     util_ldap_state_t *st = (util_ldap_state_t *)
1175                             ap_get_module_config(r->server->module_config,
1176                                                  &ldap_module);
1177
1178     /*
1179      * Stop looking at deeper levels of nested groups if we have reached the
1180      * max. Since we already checked the top-level group in uldap_cache_compare,
1181      * we don't need to check it again here - so if max_subgroup_depth is set
1182      * to 0, we won't check it (i.e. that is why we check < rather than <=).
1183      * We'll be calling uldap_cache_compare from here to check if the user is
1184      * in the next level before we recurse into that next level looking for
1185      * more subgroups.
1186      */
1187     if (cur_subgroup_depth >= max_subgroup_depth) {
1188         return LDAP_COMPARE_FALSE;
1189     }
1190
1191     /*
1192      * 1. Check the "groupiness" of the specified basedn. Stopping at the first
1193      *    TRUE return.
1194      */
1195     while ((base_sgcIndex < subgroupclasses->nelts)
1196            && (result != LDAP_COMPARE_TRUE)) {
1197         result = uldap_cache_compare(r, ldc, url, dn, "objectClass",
1198                                      sgc_ents[base_sgcIndex].name);
1199         if (result != LDAP_COMPARE_TRUE) {
1200             base_sgcIndex++;
1201         }
1202     }
1203
1204     if (result != LDAP_COMPARE_TRUE) {
1205         ldc->reason = "DN failed group verification.";
1206         return result;
1207     }
1208
1209     /*
1210      * 2. Find previously created cache entry and check if there is already a
1211      *    subgrouplist.
1212      */
1213     LDAP_CACHE_LOCK();
1214     curnode.url = url;
1215     curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
1216     LDAP_CACHE_UNLOCK();
1217
1218     if (curl && curl->compare_cache) {
1219         /* make a comparison to the cache */
1220         LDAP_CACHE_LOCK();
1221
1222         the_compare_node.dn = (char *)dn;
1223         the_compare_node.attrib = (char *)"objectClass";
1224         the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1225         the_compare_node.result = 0;
1226         the_compare_node.sgl_processed = 0;
1227         the_compare_node.subgroupList = NULL;
1228
1229         compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1230                                              &the_compare_node);
1231
1232         if (compare_nodep != NULL) {
1233             /*
1234              * Found the generic group entry... but the user isn't in this
1235              * group or we wouldn't be here.
1236              */
1237             if (compare_nodep->sgl_processed) {
1238                 if (compare_nodep->subgroupList) {
1239                     /* Make a local copy of the subgroup list */
1240                     int i;
1241                     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1242                                   "[%" APR_PID_T_FMT "] util_ldap:"
1243                                   " Making local copy of SGL for "
1244                                   "group (%s)(objectClass=%s) ",
1245                                   getpid(), dn,
1246                                   (char *)sgc_ents[base_sgcIndex].name);
1247                     tmp_local_sgl = apr_pcalloc(r->pool,
1248                                                 sizeof(util_compare_subgroup_t));
1249                     tmp_local_sgl->len = compare_nodep->subgroupList->len;
1250                     tmp_local_sgl->subgroupDNs =
1251                         apr_pcalloc(r->pool,
1252                                     sizeof(char *) * compare_nodep->subgroupList->len);
1253                     for (i = 0; i < compare_nodep->subgroupList->len; i++) {
1254                         tmp_local_sgl->subgroupDNs[i] =
1255                             apr_pstrdup(r->pool,
1256                                         compare_nodep->subgroupList->subgroupDNs[i]);
1257                     }
1258                 }
1259                 else {
1260                     sgl_cached_empty = 1;
1261                 }
1262             }
1263         }
1264         LDAP_CACHE_UNLOCK();
1265     }
1266
1267     if (!tmp_local_sgl && !sgl_cached_empty) {
1268         /* No Cached SGL, retrieve from LDAP */
1269         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1270                       "[%" APR_PID_T_FMT "] util_ldap: no cached SGL for %s,"
1271                       " retrieving from LDAP" , getpid(), dn);
1272         tmp_local_sgl = uldap_get_subgroups(r, ldc, url, dn, subgroupAttrs,
1273                                             subgroupclasses);
1274         if (!tmp_local_sgl) {
1275             /* No SGL aailable via LDAP either */
1276             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1277                           " util_ldap: no subgroups for %s" , getpid(), dn);
1278         }
1279
1280       if (curl && curl->compare_cache) {
1281         /*
1282          * Find the generic group cache entry and add the sgl we just retrieved.
1283          */
1284         LDAP_CACHE_LOCK();
1285
1286         the_compare_node.dn = (char *)dn;
1287         the_compare_node.attrib = (char *)"objectClass";
1288         the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name;
1289         the_compare_node.result = 0;
1290         the_compare_node.sgl_processed = 0;
1291         the_compare_node.subgroupList = NULL;
1292
1293         compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1294                                              &the_compare_node);
1295
1296         if (compare_nodep == NULL) {
1297             /*
1298              * The group entry we want to attach our SGL to doesn't exist.
1299              * We only got here if we verified this DN was actually a group
1300              * based on the objectClass, but we can't call the compare function
1301              * while we already hold the cache lock -- only the insert.
1302              */
1303             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1304                           "[%" APR_PID_T_FMT "] util_ldap: Cache entry "
1305                           "for %s doesn't exist",
1306                            getpid(), dn);
1307             the_compare_node.result = LDAP_COMPARE_TRUE;
1308             util_ald_cache_insert(curl->compare_cache, &the_compare_node);
1309             compare_nodep = util_ald_cache_fetch(curl->compare_cache,
1310                                                  &the_compare_node);
1311             if (compare_nodep == NULL) {
1312                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1313                               "[%" APR_PID_T_FMT "] util_ldap: Couldn't "
1314                               "retrieve group entry for %s from cache",
1315                                getpid(), dn);
1316             }
1317         }
1318
1319         /*
1320          * We have a valid cache entry and a locally generated SGL.
1321          * Attach the SGL to the cache entry
1322          */
1323         if (compare_nodep && !compare_nodep->sgl_processed) {
1324             if (!tmp_local_sgl) {
1325                 /* We looked up an SGL for a group and found it to be empty */
1326                 if (compare_nodep->subgroupList == NULL) {
1327                     compare_nodep->sgl_processed = 1;
1328                 }
1329             }
1330             else {
1331                 util_compare_subgroup_t *sgl_copy =
1332                     util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl);
1333                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1334                              "Copying local SGL of len %d for group %s into cache",
1335                              tmp_local_sgl->len, dn);
1336                 if (sgl_copy) {
1337                     if (compare_nodep->subgroupList) {
1338                         util_ald_sgl_free(curl->compare_cache,
1339                                           &(compare_nodep->subgroupList));
1340                     }
1341                     compare_nodep->subgroupList = sgl_copy;
1342                     compare_nodep->sgl_processed = 1;
1343                 }
1344                 else {
1345                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
1346                                  "Copy of SGL failed to obtain shared memory, "
1347                                  "couldn't update cache");
1348                 }
1349             }
1350         }
1351         LDAP_CACHE_UNLOCK();
1352       }
1353     }
1354
1355     /*
1356      * tmp_local_sgl has either been created, or copied out of the cache
1357      * If tmp_local_sgl is NULL, there are no subgroups to process and we'll
1358      * return false
1359      */
1360     result = LDAP_COMPARE_FALSE;
1361     if (!tmp_local_sgl) {
1362         return result;
1363     }
1364
1365     while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) {
1366         const char *group = NULL;
1367         group = tmp_local_sgl->subgroupDNs[sgindex];
1368         /*
1369          * 4. Now loop through the subgroupList and call uldap_cache_compare
1370          * to check for the user.
1371          */
1372         result = uldap_cache_compare(r, ldc, url, group, attrib, value);
1373         if (result == LDAP_COMPARE_TRUE) {
1374             /*
1375              * 4.A. We found the user in the subgroup. Return
1376              * LDAP_COMPARE_TRUE.
1377              */
1378             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1379                           " util_ldap: Found user %s in a subgroup (%s) at"
1380                           " level %d of %d.", getpid(), r->user, group,
1381                           cur_subgroup_depth+1, max_subgroup_depth);
1382         }
1383         else {
1384             /*
1385              * 4.B. We didn't find the user in this subgroup, so recurse into
1386              * it and keep looking.
1387              */
1388             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "]"
1389                           " util_ldap: user %s not found in subgroup (%s) at"
1390                           " level %d of %d.", getpid(), r->user, group,
1391                           cur_subgroup_depth+1, max_subgroup_depth);
1392             result = uldap_cache_check_subgroups(r, ldc, url, group, attrib,
1393                                                  value, subgroupAttrs,
1394                                                  subgroupclasses,
1395                                                  cur_subgroup_depth+1,
1396                                                  max_subgroup_depth);
1397         }
1398         sgindex++;
1399     }
1400
1401     return result;
1402 }
1403
1404
1405 static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
1406                                    const char *url, const char *basedn,
1407                                    int scope, char **attrs, const char *filter,
1408                                    const char *bindpw, const char **binddn,
1409                                    const char ***retvals)
1410 {
1411     const char **vals = NULL;
1412     int numvals = 0;
1413     int result = 0;
1414     LDAPMessage *res, *entry;
1415     char *dn;
1416     int count;
1417     int failures = 0;
1418     util_url_node_t *curl;              /* Cached URL node */
1419     util_url_node_t curnode;
1420     util_search_node_t *search_nodep;   /* Cached search node */
1421     util_search_node_t the_search_node;
1422     apr_time_t curtime;
1423
1424     util_ldap_state_t *st =
1425         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1426         &ldap_module);
1427
1428     /* Get the cache node for this url */
1429     LDAP_CACHE_LOCK();
1430     curnode.url = url;
1431     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1432                                                    &curnode);
1433     if (curl == NULL) {
1434         curl = util_ald_create_caches(st, url);
1435     }
1436     LDAP_CACHE_UNLOCK();
1437
1438     if (curl) {
1439         LDAP_CACHE_LOCK();
1440         the_search_node.username = filter;
1441         search_nodep = util_ald_cache_fetch(curl->search_cache,
1442                                             &the_search_node);
1443         if (search_nodep != NULL) {
1444
1445             /* found entry in search cache... */
1446             curtime = apr_time_now();
1447
1448             /*
1449              * Remove this item from the cache if its expired. If the sent
1450              * password doesn't match the storepassword, the entry will
1451              * be removed and readded later if the credentials pass
1452              * authentication.
1453              */
1454             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1455                 /* ...but entry is too old */
1456                 util_ald_cache_remove(curl->search_cache, search_nodep);
1457             }
1458             else if (   (search_nodep->bindpw)
1459                      && (search_nodep->bindpw[0] != '\0')
1460                      && (strcmp(search_nodep->bindpw, bindpw) == 0))
1461             {
1462                 /* ...and entry is valid */
1463                 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1464                 if (attrs) {
1465                     int i = 0, k = 0;
1466                     while (attrs[k++]);
1467                     *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1468                     while (search_nodep->vals[i]) {
1469                         (*retvals)[i] = apr_pstrdup(r->pool,
1470                                                     search_nodep->vals[i]);
1471                         i++;
1472                     }
1473                 }
1474                 LDAP_CACHE_UNLOCK();
1475                 ldc->reason = "Authentication successful (cached)";
1476                 return LDAP_SUCCESS;
1477             }
1478         }
1479         /* unlock this read lock */
1480         LDAP_CACHE_UNLOCK();
1481     }
1482
1483     /*
1484      * At this point, there is no valid cached search, so lets do the search.
1485      */
1486
1487     /*
1488      * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1489      */
1490 start_over:
1491     if (failures++ > 10) {
1492         return result;
1493     }
1494     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1495         return result;
1496     }
1497
1498     /* try do the search */
1499     result = ldap_search_ext_s(ldc->ldap,
1500                                (char *)basedn, scope,
1501                                (char *)filter, attrs, 0,
1502                                NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1503     if (AP_LDAP_IS_SERVER_DOWN(result))
1504     {
1505         ldc->reason = "ldap_search_ext_s() for user failed with server down";
1506         uldap_connection_unbind(ldc);
1507         goto start_over;
1508     }
1509
1510     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1511     if (result != LDAP_SUCCESS) {
1512         ldc->reason = "ldap_search_ext_s() for user failed";
1513         return result;
1514     }
1515
1516     /*
1517      * We should have found exactly one entry; to find a different
1518      * number is an error.
1519      */
1520     count = ldap_count_entries(ldc->ldap, res);
1521     if (count != 1)
1522     {
1523         if (count == 0 )
1524             ldc->reason = "User not found";
1525         else
1526             ldc->reason = "User is not unique (search found two "
1527                           "or more matches)";
1528         ldap_msgfree(res);
1529         return LDAP_NO_SUCH_OBJECT;
1530     }
1531
1532     entry = ldap_first_entry(ldc->ldap, res);
1533
1534     /* Grab the dn, copy it into the pool, and free it again */
1535     dn = ldap_get_dn(ldc->ldap, entry);
1536     *binddn = apr_pstrdup(r->pool, dn);
1537     ldap_memfree(dn);
1538
1539     /*
1540      * A bind to the server with an empty password always succeeds, so
1541      * we check to ensure that the password is not empty. This implies
1542      * that users who actually do have empty passwords will never be
1543      * able to authenticate with this module. I don't see this as a big
1544      * problem.
1545      */
1546     if (!bindpw || strlen(bindpw) <= 0) {
1547         ldap_msgfree(res);
1548         ldc->reason = "Empty password not allowed";
1549         return LDAP_INVALID_CREDENTIALS;
1550     }
1551
1552     /*
1553      * Attempt to bind with the retrieved dn and the password. If the bind
1554      * fails, it means that the password is wrong (the dn obviously
1555      * exists, since we just retrieved it)
1556      */
1557     result = ldap_simple_bind_s(ldc->ldap,
1558                                 (char *)*binddn,
1559                                 (char *)bindpw);
1560     if (AP_LDAP_IS_SERVER_DOWN(result)) {
1561         ldc->reason = "ldap_simple_bind_s() to check user credentials "
1562                       "failed with server down";
1563         ldap_msgfree(res);
1564         uldap_connection_unbind(ldc);
1565         goto start_over;
1566     }
1567
1568     /* failure? if so - return */
1569     if (result != LDAP_SUCCESS) {
1570         ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1571         ldap_msgfree(res);
1572         uldap_connection_unbind(ldc);
1573         return result;
1574     }
1575     else {
1576         /*
1577          * We have just bound the connection to a different user and password
1578          * combination, which might be reused unintentionally next time this
1579          * connection is used from the connection pool. To ensure no confusion,
1580          * we mark the connection as unbound.
1581          */
1582         ldc->bound = 0;
1583     }
1584
1585     /*
1586      * Get values for the provided attributes.
1587      */
1588     if (attrs) {
1589         int k = 0;
1590         int i = 0;
1591         while (attrs[k++]);
1592         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1593         numvals = k;
1594         while (attrs[i]) {
1595             char **values;
1596             int j = 0;
1597             char *str = NULL;
1598             /* get values */
1599             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1600             while (values && values[j]) {
1601                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1602                           : apr_pstrdup(r->pool, values[j]);
1603                 j++;
1604             }
1605             ldap_value_free(values);
1606             vals[i] = str;
1607             i++;
1608         }
1609         *retvals = vals;
1610     }
1611
1612     /*
1613      * Add the new username to the search cache.
1614      */
1615     if (curl) {
1616         LDAP_CACHE_LOCK();
1617         the_search_node.username = filter;
1618         the_search_node.dn = *binddn;
1619         the_search_node.bindpw = bindpw;
1620         the_search_node.lastbind = apr_time_now();
1621         the_search_node.vals = vals;
1622         the_search_node.numvals = numvals;
1623
1624         /* Search again to make sure that another thread didn't ready insert
1625          * this node into the cache before we got here. If it does exist then
1626          * update the lastbind
1627          */
1628         search_nodep = util_ald_cache_fetch(curl->search_cache,
1629                                             &the_search_node);
1630         if ((search_nodep == NULL) ||
1631             (strcmp(*binddn, search_nodep->dn) != 0)) {
1632
1633             /* Nothing in cache, insert new entry */
1634             util_ald_cache_insert(curl->search_cache, &the_search_node);
1635         }
1636         else if ((!search_nodep->bindpw) ||
1637             (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1638
1639             /* Entry in cache is invalid, remove it and insert new one */
1640             util_ald_cache_remove(curl->search_cache, search_nodep);
1641             util_ald_cache_insert(curl->search_cache, &the_search_node);
1642         }
1643         else {
1644             /* Cache entry is valid, update lastbind */
1645             search_nodep->lastbind = the_search_node.lastbind;
1646         }
1647         LDAP_CACHE_UNLOCK();
1648     }
1649     ldap_msgfree(res);
1650
1651     ldc->reason = "Authentication successful";
1652     return LDAP_SUCCESS;
1653 }
1654
1655 /*
1656  * This function will return the DN of the entry matching userid.
1657  * It is used to get the DN in case some other module than mod_auth_ldap
1658  * has authenticated the user.
1659  * The function is basically a copy of uldap_cache_checkuserid
1660  * with password checking removed.
1661  */
1662 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1663                                  const char *url, const char *basedn,
1664                                  int scope, char **attrs, const char *filter,
1665                                  const char **binddn, const char ***retvals)
1666 {
1667     const char **vals = NULL;
1668     int numvals = 0;
1669     int result = 0;
1670     LDAPMessage *res, *entry;
1671     char *dn;
1672     int count;
1673     int failures = 0;
1674     util_url_node_t *curl;              /* Cached URL node */
1675     util_url_node_t curnode;
1676     util_search_node_t *search_nodep;   /* Cached search node */
1677     util_search_node_t the_search_node;
1678     apr_time_t curtime;
1679
1680     util_ldap_state_t *st =
1681         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1682         &ldap_module);
1683
1684     /* Get the cache node for this url */
1685     LDAP_CACHE_LOCK();
1686     curnode.url = url;
1687     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1688                                                    &curnode);
1689     if (curl == NULL) {
1690         curl = util_ald_create_caches(st, url);
1691     }
1692     LDAP_CACHE_UNLOCK();
1693
1694     if (curl) {
1695         LDAP_CACHE_LOCK();
1696         the_search_node.username = filter;
1697         search_nodep = util_ald_cache_fetch(curl->search_cache,
1698                                             &the_search_node);
1699         if (search_nodep != NULL) {
1700
1701             /* found entry in search cache... */
1702             curtime = apr_time_now();
1703
1704             /*
1705              * Remove this item from the cache if its expired.
1706              */
1707             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1708                 /* ...but entry is too old */
1709                 util_ald_cache_remove(curl->search_cache, search_nodep);
1710             }
1711             else {
1712                 /* ...and entry is valid */
1713                 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1714                 if (attrs) {
1715                     int i = 0, k = 0;
1716                     while (attrs[k++]);
1717                     *retvals = apr_pcalloc(r->pool, sizeof(char *) * k);
1718                     while (search_nodep->vals[i]) {
1719                         (*retvals)[i] = apr_pstrdup(r->pool,
1720                                                     search_nodep->vals[i]);
1721                         i++;
1722                     }
1723                 }
1724                 LDAP_CACHE_UNLOCK();
1725                 ldc->reason = "Search successful (cached)";
1726                 return LDAP_SUCCESS;
1727             }
1728         }
1729         /* unlock this read lock */
1730         LDAP_CACHE_UNLOCK();
1731     }
1732
1733     /*
1734      * At this point, there is no valid cached search, so lets do the search.
1735      */
1736
1737     /*
1738      * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1739      */
1740 start_over:
1741     if (failures++ > 10) {
1742         return result;
1743     }
1744     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1745         return result;
1746     }
1747
1748     /* try do the search */
1749     result = ldap_search_ext_s(ldc->ldap,
1750                                (char *)basedn, scope,
1751                                (char *)filter, attrs, 0,
1752                                NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1753     if (AP_LDAP_IS_SERVER_DOWN(result))
1754     {
1755         ldc->reason = "ldap_search_ext_s() for user failed with server down";
1756         uldap_connection_unbind(ldc);
1757         goto start_over;
1758     }
1759
1760     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1761     if (result != LDAP_SUCCESS) {
1762         ldc->reason = "ldap_search_ext_s() for user failed";
1763         return result;
1764     }
1765
1766     /*
1767      * We should have found exactly one entry; to find a different
1768      * number is an error.
1769      */
1770     count = ldap_count_entries(ldc->ldap, res);
1771     if (count != 1)
1772     {
1773         if (count == 0 )
1774             ldc->reason = "User not found";
1775         else
1776             ldc->reason = "User is not unique (search found two "
1777                           "or more matches)";
1778         ldap_msgfree(res);
1779         return LDAP_NO_SUCH_OBJECT;
1780     }
1781
1782     entry = ldap_first_entry(ldc->ldap, res);
1783
1784     /* Grab the dn, copy it into the pool, and free it again */
1785     dn = ldap_get_dn(ldc->ldap, entry);
1786     *binddn = apr_pstrdup(r->pool, dn);
1787     ldap_memfree(dn);
1788
1789     /*
1790      * Get values for the provided attributes.
1791      */
1792     if (attrs) {
1793         int k = 0;
1794         int i = 0;
1795         while (attrs[k++]);
1796         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1797         numvals = k;
1798         while (attrs[i]) {
1799             char **values;
1800             int j = 0;
1801             char *str = NULL;
1802             /* get values */
1803             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1804             while (values && values[j]) {
1805                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1806                           : apr_pstrdup(r->pool, values[j]);
1807                 j++;
1808             }
1809             ldap_value_free(values);
1810             vals[i] = str;
1811             i++;
1812         }
1813         *retvals = vals;
1814     }
1815
1816     /*
1817      * Add the new username to the search cache.
1818      */
1819     if (curl) {
1820         LDAP_CACHE_LOCK();
1821         the_search_node.username = filter;
1822         the_search_node.dn = *binddn;
1823         the_search_node.bindpw = NULL;
1824         the_search_node.lastbind = apr_time_now();
1825         the_search_node.vals = vals;
1826         the_search_node.numvals = numvals;
1827
1828         /* Search again to make sure that another thread didn't ready insert
1829          * this node into the cache before we got here. If it does exist then
1830          * update the lastbind
1831          */
1832         search_nodep = util_ald_cache_fetch(curl->search_cache,
1833                                             &the_search_node);
1834         if ((search_nodep == NULL) ||
1835             (strcmp(*binddn, search_nodep->dn) != 0)) {
1836
1837             /* Nothing in cache, insert new entry */
1838             util_ald_cache_insert(curl->search_cache, &the_search_node);
1839         }
1840         /*
1841          * Don't update lastbind on entries with bindpw because
1842          * we haven't verified that password. It's OK to update
1843          * the entry if there is no password in it.
1844          */
1845         else if (!search_nodep->bindpw) {
1846             /* Cache entry is valid, update lastbind */
1847             search_nodep->lastbind = the_search_node.lastbind;
1848         }
1849         LDAP_CACHE_UNLOCK();
1850     }
1851
1852     ldap_msgfree(res);
1853
1854     ldc->reason = "Search successful";
1855     return LDAP_SUCCESS;
1856 }
1857
1858 /*
1859  * Reports if ssl support is enabled
1860  *
1861  * 1 = enabled, 0 = not enabled
1862  */
1863 static int uldap_ssl_supported(request_rec *r)
1864 {
1865    util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1866                                 r->server->module_config, &ldap_module);
1867
1868    return(st->ssl_supported);
1869 }
1870
1871
1872 /* ---------------------------------------- */
1873 /* config directives */
1874
1875
1876 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1877                                              const char *bytes)
1878 {
1879     util_ldap_state_t *st =
1880         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1881                                                   &ldap_module);
1882     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1883
1884     if (err != NULL) {
1885         return err;
1886     }
1887
1888     st->cache_bytes = atol(bytes);
1889
1890     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1891                  "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1892                  " cache size to %" APR_SIZE_T_FMT " bytes.",
1893                  getpid(), st->cache_bytes);
1894
1895     return NULL;
1896 }
1897
1898 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1899                                             const char *file)
1900 {
1901     util_ldap_state_t *st =
1902         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1903                                                   &ldap_module);
1904     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1905
1906     if (err != NULL) {
1907         return err;
1908     }
1909
1910     if (file) {
1911         st->cache_file = ap_server_root_relative(st->pool, file);
1912     }
1913     else {
1914         st->cache_file = NULL;
1915     }
1916
1917     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1918                  "LDAP cache: Setting shared memory cache file to %s bytes.",
1919                  st->cache_file);
1920
1921     return NULL;
1922 }
1923
1924 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1925                                            const char *ttl)
1926 {
1927     util_ldap_state_t *st =
1928         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1929                                                   &ldap_module);
1930     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1931
1932     if (err != NULL) {
1933         return err;
1934     }
1935
1936     st->search_cache_ttl = atol(ttl) * 1000000;
1937
1938     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1939                  "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1940                  " microseconds.", getpid(), st->search_cache_ttl);
1941
1942     return NULL;
1943 }
1944
1945 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1946                                                const char *size)
1947 {
1948     util_ldap_state_t *st =
1949         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1950                                                   &ldap_module);
1951     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1952
1953     if (err != NULL) {
1954         return err;
1955     }
1956
1957     st->search_cache_size = atol(size);
1958     if (st->search_cache_size < 0) {
1959         st->search_cache_size = 0;
1960     }
1961
1962     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1963                  "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1964                  " to %ld entries.", getpid(), st->search_cache_size);
1965
1966     return NULL;
1967 }
1968
1969 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1970                                              const char *ttl)
1971 {
1972     util_ldap_state_t *st =
1973         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1974                                                   &ldap_module);
1975     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1976
1977     if (err != NULL) {
1978         return err;
1979     }
1980
1981     st->compare_cache_ttl = atol(ttl) * 1000000;
1982
1983     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1984                  "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1985                  " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1986
1987     return NULL;
1988 }
1989
1990 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1991                                                  const char *size)
1992 {
1993     util_ldap_state_t *st =
1994         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1995                                                   &ldap_module);
1996     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1997
1998     if (err != NULL) {
1999         return err;
2000     }
2001
2002     st->compare_cache_size = atol(size);
2003     if (st->compare_cache_size < 0) {
2004         st->compare_cache_size = 0;
2005     }
2006
2007     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2008                  "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
2009                  " to %ld entries.", getpid(), st->compare_cache_size);
2010
2011     return NULL;
2012 }
2013
2014
2015 /**
2016  * Parse the certificate type.
2017  *
2018  * The type can be one of the following:
2019  * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
2020  * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
2021  *
2022  * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
2023  */
2024 static int util_ldap_parse_cert_type(const char *type)
2025 {
2026     /* Authority file in binary DER format */
2027     if (0 == strcasecmp("CA_DER", type)) {
2028         return APR_LDAP_CA_TYPE_DER;
2029     }
2030
2031     /* Authority file in Base64 format */
2032     else if (0 == strcasecmp("CA_BASE64", type)) {
2033         return APR_LDAP_CA_TYPE_BASE64;
2034     }
2035
2036     /* Netscape certificate database file/directory */
2037     else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2038         return APR_LDAP_CA_TYPE_CERT7_DB;
2039     }
2040
2041     /* Netscape secmod file/directory */
2042     else if (0 == strcasecmp("CA_SECMOD", type)) {
2043         return APR_LDAP_CA_TYPE_SECMOD;
2044     }
2045
2046     /* Client cert file in DER format */
2047     else if (0 == strcasecmp("CERT_DER", type)) {
2048         return APR_LDAP_CERT_TYPE_DER;
2049     }
2050
2051     /* Client cert file in Base64 format */
2052     else if (0 == strcasecmp("CERT_BASE64", type)) {
2053         return APR_LDAP_CERT_TYPE_BASE64;
2054     }
2055
2056     /* Client cert file in PKCS#12 format */
2057     else if (0 == strcasecmp("CERT_PFX", type)) {
2058         return APR_LDAP_CERT_TYPE_PFX;
2059     }
2060
2061     /* Netscape client cert database file/directory */
2062     else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2063         return APR_LDAP_CERT_TYPE_KEY3_DB;
2064     }
2065
2066     /* Netscape client cert nickname */
2067     else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2068         return APR_LDAP_CERT_TYPE_NICKNAME;
2069     }
2070
2071     /* Client cert key file in DER format */
2072     else if (0 == strcasecmp("KEY_DER", type)) {
2073         return APR_LDAP_KEY_TYPE_DER;
2074     }
2075
2076     /* Client cert key file in Base64 format */
2077     else if (0 == strcasecmp("KEY_BASE64", type)) {
2078         return APR_LDAP_KEY_TYPE_BASE64;
2079     }
2080
2081     /* Client cert key file in PKCS#12 format */
2082     else if (0 == strcasecmp("KEY_PFX", type)) {
2083         return APR_LDAP_KEY_TYPE_PFX;
2084     }
2085
2086     else {
2087         return APR_LDAP_CA_TYPE_UNKNOWN;
2088     }
2089
2090 }
2091
2092
2093 /**
2094  * Set LDAPTrustedGlobalCert.
2095  *
2096  * This directive takes either two or three arguments:
2097  * - certificate type
2098  * - certificate file / directory / nickname
2099  * - certificate password (optional)
2100  *
2101  * This directive may only be used globally.
2102  */
2103 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2104                                                      void *dummy,
2105                                                      const char *type,
2106                                                      const char *file,
2107                                                      const char *password)
2108 {
2109     util_ldap_state_t *st =
2110         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2111                                                   &ldap_module);
2112     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2113     apr_finfo_t finfo;
2114     apr_status_t rv;
2115     int cert_type = 0;
2116     apr_ldap_opt_tls_cert_t *cert;
2117
2118     if (err != NULL) {
2119         return err;
2120     }
2121
2122     /* handle the certificate type */
2123     if (type) {
2124         cert_type = util_ldap_parse_cert_type(type);
2125         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2126            return apr_psprintf(cmd->pool, "The certificate type %s is "
2127                                           "not recognised. It should be one "
2128                                           "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2129                                           "CA_SECMOD, CERT_DER, CERT_BASE64, "
2130                                           "CERT_KEY3_DB, CERT_NICKNAME, "
2131                                           "KEY_DER, KEY_BASE64", type);
2132         }
2133     }
2134     else {
2135         return "Certificate type was not specified.";
2136     }
2137
2138     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2139                       "LDAP: SSL trusted global cert - %s (type %s)",
2140                        file, type);
2141
2142     /* add the certificate to the global array */
2143     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2144     cert->type = cert_type;
2145     cert->path = file;
2146     cert->password = password;
2147
2148     /* if file is a file or path, fix the path */
2149     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2150         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2151
2152         cert->path = ap_server_root_relative(cmd->pool, file);
2153         if (cert->path &&
2154             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2155                 != APR_SUCCESS))
2156         {
2157             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2158                          "LDAP: Could not open SSL trusted certificate "
2159                          "authority file - %s",
2160                          cert->path == NULL ? file : cert->path);
2161             return "Invalid global certificate file path";
2162         }
2163     }
2164
2165     return(NULL);
2166 }
2167
2168
2169 /**
2170  * Set LDAPTrustedClientCert.
2171  *
2172  * This directive takes either two or three arguments:
2173  * - certificate type
2174  * - certificate file / directory / nickname
2175  * - certificate password (optional)
2176  */
2177 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2178                                                      void *config,
2179                                                      const char *type,
2180                                                      const char *file,
2181                                                      const char *password)
2182 {
2183     util_ldap_state_t *st =
2184         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2185                                                   &ldap_module);
2186     apr_finfo_t finfo;
2187     apr_status_t rv;
2188     int cert_type = 0;
2189     apr_ldap_opt_tls_cert_t *cert;
2190
2191     /* handle the certificate type */
2192     if (type) {
2193         cert_type = util_ldap_parse_cert_type(type);
2194         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2195             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2196                                            "not recognised. It should be one "
2197                                            "of CERT_DER, CERT_BASE64, "
2198                                            "CERT_NICKNAME, CERT_PFX,"
2199                                            "KEY_DER, KEY_BASE64, KEY_PFX",
2200                                            type);
2201         }
2202         else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2203                  APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2204                  APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2205                  APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2206                  APR_LDAP_CERT_TYPE_PFX == cert_type ||
2207                  APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2208             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2209                                            "only valid within a "
2210                                            "LDAPTrustedGlobalCert directive. "
2211                                            "Only CERT_DER, CERT_BASE64, "
2212                                            "CERT_NICKNAME, KEY_DER, and "
2213                                            "KEY_BASE64 may be used.", type);
2214         }
2215     }
2216     else {
2217         return "Certificate type was not specified.";
2218     }
2219
2220     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2221                       "LDAP: SSL trusted client cert - %s (type %s)",
2222                        file, type);
2223
2224     /* add the certificate to the global array */
2225     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2226     cert->type = cert_type;
2227     cert->path = file;
2228     cert->password = password;
2229
2230     /* if file is a file or path, fix the path */
2231     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2232         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2233
2234         cert->path = ap_server_root_relative(cmd->pool, file);
2235         if (cert->path &&
2236             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2237                 != APR_SUCCESS))
2238         {
2239             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2240                          "LDAP: Could not open SSL client certificate "
2241                          "file - %s",
2242                          cert->path == NULL ? file : cert->path);
2243             return "Invalid client certificate file path";
2244         }
2245
2246     }
2247
2248     return(NULL);
2249 }
2250
2251
2252 /**
2253  * Set LDAPTrustedMode.
2254  *
2255  * This directive sets what encryption mode to use on a connection:
2256  * - None (No encryption)
2257  * - SSL (SSL encryption)
2258  * - STARTTLS (TLS encryption)
2259  */
2260 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2261                                               const char *mode)
2262 {
2263     util_ldap_state_t *st =
2264     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2265                                               &ldap_module);
2266
2267     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2268                       "LDAP: SSL trusted mode - %s",
2269                        mode);
2270
2271     if (0 == strcasecmp("NONE", mode)) {
2272         st->secure = APR_LDAP_NONE;
2273     }
2274     else if (0 == strcasecmp("SSL", mode)) {
2275         st->secure = APR_LDAP_SSL;
2276     }
2277     else if (   (0 == strcasecmp("TLS", mode))
2278              || (0 == strcasecmp("STARTTLS", mode))) {
2279         st->secure = APR_LDAP_STARTTLS;
2280     }
2281     else {
2282         return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2283                "SSL, or TLS/STARTTLS";
2284     }
2285
2286     st->secure_set = 1;
2287     return(NULL);
2288 }
2289
2290 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2291                                                  void *dummy,
2292                                                  int mode)
2293 {
2294     util_ldap_state_t *st =
2295     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2296                                               &ldap_module);
2297     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2298
2299     if (err != NULL) {
2300         return err;
2301     }
2302
2303     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2304                       "LDAP: SSL verify server certificate - %s",
2305                       mode?"TRUE":"FALSE");
2306
2307     st->verify_svr_cert = mode;
2308
2309     return(NULL);
2310 }
2311
2312
2313 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2314                                                     void *dummy,
2315                                                     const char *ttl)
2316 {
2317 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2318     util_ldap_state_t *st =
2319         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2320                                                   &ldap_module);
2321 #endif
2322     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2323
2324     if (err != NULL) {
2325         return err;
2326     }
2327
2328 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2329     st->connectionTimeout = atol(ttl);
2330
2331     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2332                  "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2333                  " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2334 #else
2335     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2336                  "LDAP: Connection timeout option not supported by the "
2337                  "LDAP SDK in use." );
2338 #endif
2339
2340     return NULL;
2341 }
2342
2343
2344 static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
2345                                                  void *config,
2346                                                  int mode)
2347 {
2348     util_ldap_config_t *dc =  config;
2349
2350     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2351                       "LDAP: Setting refferal chasing %s",
2352                       (mode == AP_LDAP_CHASEREFERRALS_ON) ? "ON" : "OFF");
2353
2354     dc->ChaseReferrals = mode;
2355
2356     return(NULL);
2357 }
2358
2359 static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
2360                                                     void *config,
2361                                                     const char *hop_limit)
2362 {
2363     util_ldap_config_t *dc =  config;
2364
2365     dc->ReferralHopLimit = atol(hop_limit);
2366
2367     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2368                  "LDAP: Limit chased referrals to maximum of %d hops.",
2369                  dc->ReferralHopLimit);
2370
2371     return NULL;
2372 }
2373
2374 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
2375    util_ldap_config_t *dc =
2376        (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
2377
2378    /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
2379    dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2380    dc->ReferralHopLimit = AP_LDAP_DEFAULT_HOPLIMIT;
2381
2382    return dc;
2383 }
2384
2385
2386 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2387 {
2388     util_ldap_state_t *st =
2389         (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2390
2391     /* Create a per vhost pool for mod_ldap to use, serialized with 
2392      * st->mutex (also one per vhost).  both are replicated by fork(),
2393      * no shared memory managed by either.
2394      */
2395     apr_pool_create(&st->pool, p);
2396 #if APR_HAS_THREADS
2397     apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2398 #endif
2399
2400     st->cache_bytes = 100000;
2401     st->search_cache_ttl = 600000000;
2402     st->search_cache_size = 1024;
2403     st->compare_cache_ttl = 600000000;
2404     st->compare_cache_size = 1024;
2405     st->connections = NULL;
2406     st->ssl_supported = 0;
2407     st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2408     st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2409     st->secure = APR_LDAP_NONE;
2410     st->secure_set = 0;
2411     st->connectionTimeout = 10;
2412     st->verify_svr_cert = 1;
2413
2414     /* Initialize the rebind callback's cross reference list. */
2415     apr_ldap_rebind_init (p);
2416
2417     return st;
2418 }
2419
2420 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2421                                     void *overridesv)
2422 {
2423     util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2424     util_ldap_state_t *base = (util_ldap_state_t *) basev;
2425     util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2426
2427     st->pool = overrides->pool;
2428 #if APR_HAS_THREADS
2429     st->mutex = overrides->mutex;
2430 #endif
2431
2432     /* The cache settings can not be modified in a 
2433         virtual host since all server use the same
2434         shared memory cache. */
2435     st->cache_bytes = base->cache_bytes;
2436     st->search_cache_ttl = base->search_cache_ttl;
2437     st->search_cache_size = base->search_cache_size;
2438     st->compare_cache_ttl = base->compare_cache_ttl;
2439     st->compare_cache_size = base->compare_cache_size;
2440     st->util_ldap_cache_lock = base->util_ldap_cache_lock; 
2441
2442     st->connections = NULL;
2443     st->ssl_supported = 0;
2444     st->global_certs = apr_array_append(p, base->global_certs,
2445                                            overrides->global_certs);
2446     st->client_certs = apr_array_append(p, base->client_certs,
2447                                            overrides->client_certs);
2448     st->secure = (overrides->secure_set == 0) ? base->secure
2449                                               : overrides->secure;
2450
2451     /* These LDAP connection settings can not be overwritten in 
2452         a virtual host. Once set in the base server, they must 
2453         remain the same. None of the LDAP SDKs seem to be able
2454         to handle setting the verify_svr_cert flag on a 
2455         per-connection basis.  The OpenLDAP client appears to be
2456         able to handle the connection timeout per-connection
2457         but the Novell SDK cannot.  Allowing the timeout to
2458         be set by each vhost is of little value so rather than
2459         trying to make special expections for one LDAP SDK, GLOBAL_ONLY 
2460         is being enforced on this setting as well. */
2461     st->connectionTimeout = base->connectionTimeout;
2462     st->verify_svr_cert = base->verify_svr_cert;
2463
2464     return st;
2465 }
2466
2467 static apr_status_t util_ldap_cleanup_module(void *data)
2468 {
2469
2470     server_rec *s = data;
2471     util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2472         s->module_config, &ldap_module);
2473
2474     if (st->ssl_supported) {
2475         apr_ldap_ssl_deinit();
2476     }
2477
2478     return APR_SUCCESS;
2479
2480 }
2481
2482 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2483                                  apr_pool_t *ptemp, server_rec *s)
2484 {
2485     apr_status_t result;
2486     server_rec *s_vhost;
2487     util_ldap_state_t *st_vhost;
2488
2489     util_ldap_state_t *st = (util_ldap_state_t *)
2490                             ap_get_module_config(s->module_config,
2491                                                  &ldap_module);
2492
2493     void *data;
2494     const char *userdata_key = "util_ldap_init";
2495     apr_ldap_err_t *result_err = NULL;
2496     int rc;
2497
2498     /* util_ldap_post_config() will be called twice. Don't bother
2499      * going through all of the initialization on the first call
2500      * because it will just be thrown away.*/
2501     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2502     if (!data) {
2503         apr_pool_userdata_set((const void *)1, userdata_key,
2504                                apr_pool_cleanup_null, s->process->pool);
2505
2506 #if APR_HAS_SHARED_MEMORY
2507         /* If the cache file already exists then delete it.  Otherwise we are
2508          * going to run into problems creating the shared memory. */
2509         if (st->cache_file) {
2510             char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2511                                          NULL);
2512             apr_file_remove(lck_file, ptemp);
2513         }
2514 #endif
2515         return OK;
2516     }
2517
2518 #if APR_HAS_SHARED_MEMORY
2519     /* initializing cache if shared memory size is not zero and we already
2520      * don't have shm address
2521      */
2522     if (!st->cache_shm && st->cache_bytes > 0) {
2523 #endif
2524         result = util_ldap_cache_init(p, st);
2525         if (result != APR_SUCCESS) {
2526             ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2527                          "LDAP cache: could not create shared memory segment");
2528             return DONE;
2529         }
2530
2531
2532 #if APR_HAS_SHARED_MEMORY
2533         if (st->cache_file) {
2534             st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2535                                         NULL);
2536         }
2537 #endif
2538
2539         result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2540                                          st->lock_file, APR_LOCK_DEFAULT,
2541                                          st->pool);
2542         if (result != APR_SUCCESS) {
2543             return result;
2544         }
2545
2546 #ifdef AP_NEED_SET_MUTEX_PERMS
2547         result = unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2548         if (result != APR_SUCCESS) {
2549             ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2550                          "LDAP cache: failed to set mutex permissions");
2551             return result;
2552         }
2553 #endif
2554
2555         /* merge config in all vhost */
2556         s_vhost = s->next;
2557         while (s_vhost) {
2558             st_vhost = (util_ldap_state_t *)
2559                        ap_get_module_config(s_vhost->module_config,
2560                                             &ldap_module);
2561
2562 #if APR_HAS_SHARED_MEMORY
2563             st_vhost->cache_shm = st->cache_shm;
2564             st_vhost->cache_rmm = st->cache_rmm;
2565             st_vhost->cache_file = st->cache_file;
2566             ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2567                          "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2568                          "for VHOST: %s", st->cache_shm, st->cache_rmm,
2569                          s_vhost->server_hostname);
2570 #endif
2571             st_vhost->lock_file = st->lock_file;
2572             s_vhost = s_vhost->next;
2573         }
2574 #if APR_HAS_SHARED_MEMORY
2575     }
2576     else {
2577         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2578                      "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2579                      "shared memory cache");
2580     }
2581 #endif
2582
2583     /* log the LDAP SDK used
2584      */
2585     {
2586         apr_ldap_err_t *result = NULL;
2587         apr_ldap_info(p, &(result));
2588         if (result != NULL) {
2589             ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2590         }
2591     }
2592
2593     apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2594                               util_ldap_cleanup_module);
2595
2596     /*
2597      * Initialize SSL support, and log the result for the benefit of the admin.
2598      *
2599      * If SSL is not supported it is not necessarily an error, as the
2600      * application may not want to use it.
2601      */
2602     rc = apr_ldap_ssl_init(p,
2603                       NULL,
2604                       0,
2605                       &(result_err));
2606     if (APR_SUCCESS == rc) {
2607         rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2608                                  (void *)st->global_certs, &(result_err));
2609     }
2610
2611     if (APR_SUCCESS == rc) {
2612         st->ssl_supported = 1;
2613         ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2614                      "LDAP: SSL support available" );
2615     }
2616     else {
2617         st->ssl_supported = 0;
2618         ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2619                      "LDAP: SSL support unavailable%s%s",
2620                      result_err ? ": " : "",
2621                      result_err ? result_err->reason : "");
2622     }
2623
2624     return(OK);
2625 }
2626
2627 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2628 {
2629     apr_status_t sts;
2630     util_ldap_state_t *st = ap_get_module_config(s->module_config,
2631                                                  &ldap_module);
2632
2633     if (!st->util_ldap_cache_lock) return;
2634
2635     sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2636                                       st->lock_file, p);
2637     if (sts != APR_SUCCESS) {
2638         ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2639                      "Failed to initialise global mutex %s in child process %"
2640                      APR_PID_T_FMT ".",
2641                      st->lock_file, getpid());
2642     }
2643 }
2644
2645 static const command_rec util_ldap_cmds[] = {
2646     AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2647                   NULL, RSRC_CONF,
2648                   "Set the size of the shared memory cache (in bytes). Use "
2649                   "0 to disable the shared memory cache. (default: 100000)"),
2650
2651     AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2652                   NULL, RSRC_CONF,
2653                   "Set the file name for the shared memory cache."),
2654
2655     AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2656                   NULL, RSRC_CONF,
2657                   "Set the maximum number of entries that are possible in the "
2658                   "LDAP search cache. Use 0 for no limit. "
2659                   "-1 disables the cache. (default: 1024)"),
2660
2661     AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2662                   NULL, RSRC_CONF,
2663                   "Set the maximum time (in seconds) that an item can be "
2664                   "cached in the LDAP search cache. Use 0 for no limit. "
2665                   "(default 600)"),
2666
2667     AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2668                   NULL, RSRC_CONF,
2669                   "Set the maximum number of entries that are possible "
2670                   "in the LDAP compare cache. Use 0 for no limit. "
2671                   "Use -1 to disable the cache. (default: 1024)"),
2672
2673     AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2674                   NULL, RSRC_CONF,
2675                   "Set the maximum time (in seconds) that an item is cached "
2676                   "in the LDAP operation cache. Use 0 for no limit. "
2677                   "(default: 600)"),
2678
2679     AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2680                    NULL, RSRC_CONF,
2681                    "Takes three args; the file and/or directory containing "
2682                    "the trusted CA certificates (and global client certs "
2683                    "for Netware) used to validate the LDAP server.  Second "
2684                    "arg is the cert type for the first arg, one of CA_DER, "
2685                    "CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, "
2686                    "CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, or KEY_BASE64. "
2687                    "Third arg is an optional passphrase if applicable."),
2688
2689     AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2690                    NULL, RSRC_CONF,
2691                    "Takes three args; the file and/or directory containing "
2692                    "the client certificate, or certificate ID used to "
2693                    "validate this LDAP client.  Second arg is the cert type "
2694                    "for the first arg, one of CA_DER, CA_BASE64, CA_CERT7_DB, "
2695                    "CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2696                    "CERT_NICKNAME, KEY_DER, or KEY_BASE64. Third arg is an "
2697                    "optional passphrase if applicable."),
2698
2699     AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2700                   NULL, RSRC_CONF,
2701                   "Specify the type of security that should be applied to "
2702                   "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2703
2704     AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2705                   NULL, RSRC_CONF,
2706                   "Set to 'ON' requires that the server certificate be verified"
2707                   " before a secure LDAP connection can be establish.  Default"
2708                   " 'ON'"),
2709
2710     AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2711                   NULL, RSRC_CONF,
2712                   "Specify the LDAP socket connection timeout in seconds "
2713                   "(default: 10)"),
2714
2715     AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
2716                   NULL, OR_AUTHCFG,
2717                   "Choose whether referrals are chased ['ON'|'OFF'].  Default 'ON'"),
2718
2719     AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
2720                   NULL, OR_AUTHCFG,
2721                   "Limit the number of referral hops that LDAP can follow. "
2722                   "(Integer value, default=" AP_LDAP_DEFAULT_HOPLIMIT_STR ")"),
2723
2724     {NULL}
2725 };
2726
2727 static void util_ldap_register_hooks(apr_pool_t *p)
2728 {
2729     APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2730     APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2731     APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2732     APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2733     APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2734     APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2735     APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2736     APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2737     APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2738     APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2739     APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2740
2741     ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2742     ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2743     ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2744 }
2745
2746 module AP_MODULE_DECLARE_DATA ldap_module = {
2747    STANDARD20_MODULE_STUFF,
2748    util_ldap_create_dir_config, /* create dir config */
2749    NULL,                        /* merge dir config */
2750    util_ldap_create_config,     /* create server config */
2751    util_ldap_merge_config,      /* merge server config */
2752    util_ldap_cmds,              /* command table */
2753    util_ldap_register_hooks,    /* set up request processing hooks */
2754 };