]> granicus.if.org Git - apache/blob - modules/ldap/util_ldap.c
* Another set of missed renames.
[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;
1466                     *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
1467                     for (i = 0; i < search_nodep->numvals; i++) {
1468                         (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1469                     }
1470                 }
1471                 LDAP_CACHE_UNLOCK();
1472                 ldc->reason = "Authentication successful (cached)";
1473                 return LDAP_SUCCESS;
1474             }
1475         }
1476         /* unlock this read lock */
1477         LDAP_CACHE_UNLOCK();
1478     }
1479
1480     /*
1481      * At this point, there is no valid cached search, so lets do the search.
1482      */
1483
1484     /*
1485      * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1486      */
1487 start_over:
1488     if (failures++ > 10) {
1489         return result;
1490     }
1491     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1492         return result;
1493     }
1494
1495     /* try do the search */
1496     result = ldap_search_ext_s(ldc->ldap,
1497                                (char *)basedn, scope,
1498                                (char *)filter, attrs, 0,
1499                                NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1500     if (AP_LDAP_IS_SERVER_DOWN(result))
1501     {
1502         ldc->reason = "ldap_search_ext_s() for user failed with server down";
1503         uldap_connection_unbind(ldc);
1504         goto start_over;
1505     }
1506
1507     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1508     if (result != LDAP_SUCCESS) {
1509         ldc->reason = "ldap_search_ext_s() for user failed";
1510         return result;
1511     }
1512
1513     /*
1514      * We should have found exactly one entry; to find a different
1515      * number is an error.
1516      */
1517     count = ldap_count_entries(ldc->ldap, res);
1518     if (count != 1)
1519     {
1520         if (count == 0 )
1521             ldc->reason = "User not found";
1522         else
1523             ldc->reason = "User is not unique (search found two "
1524                           "or more matches)";
1525         ldap_msgfree(res);
1526         return LDAP_NO_SUCH_OBJECT;
1527     }
1528
1529     entry = ldap_first_entry(ldc->ldap, res);
1530
1531     /* Grab the dn, copy it into the pool, and free it again */
1532     dn = ldap_get_dn(ldc->ldap, entry);
1533     *binddn = apr_pstrdup(r->pool, dn);
1534     ldap_memfree(dn);
1535
1536     /*
1537      * A bind to the server with an empty password always succeeds, so
1538      * we check to ensure that the password is not empty. This implies
1539      * that users who actually do have empty passwords will never be
1540      * able to authenticate with this module. I don't see this as a big
1541      * problem.
1542      */
1543     if (!bindpw || strlen(bindpw) <= 0) {
1544         ldap_msgfree(res);
1545         ldc->reason = "Empty password not allowed";
1546         return LDAP_INVALID_CREDENTIALS;
1547     }
1548
1549     /*
1550      * Attempt to bind with the retrieved dn and the password. If the bind
1551      * fails, it means that the password is wrong (the dn obviously
1552      * exists, since we just retrieved it)
1553      */
1554     result = ldap_simple_bind_s(ldc->ldap,
1555                                 (char *)*binddn,
1556                                 (char *)bindpw);
1557     if (AP_LDAP_IS_SERVER_DOWN(result)) {
1558         ldc->reason = "ldap_simple_bind_s() to check user credentials "
1559                       "failed with server down";
1560         ldap_msgfree(res);
1561         uldap_connection_unbind(ldc);
1562         goto start_over;
1563     }
1564
1565     /* failure? if so - return */
1566     if (result != LDAP_SUCCESS) {
1567         ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
1568         ldap_msgfree(res);
1569         uldap_connection_unbind(ldc);
1570         return result;
1571     }
1572     else {
1573         /*
1574          * We have just bound the connection to a different user and password
1575          * combination, which might be reused unintentionally next time this
1576          * connection is used from the connection pool. To ensure no confusion,
1577          * we mark the connection as unbound.
1578          */
1579         ldc->bound = 0;
1580     }
1581
1582     /*
1583      * Get values for the provided attributes.
1584      */
1585     if (attrs) {
1586         int k = 0;
1587         int i = 0;
1588         while (attrs[k++]);
1589         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1590         numvals = k;
1591         while (attrs[i]) {
1592             char **values;
1593             int j = 0;
1594             char *str = NULL;
1595             /* get values */
1596             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1597             while (values && values[j]) {
1598                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1599                           : apr_pstrdup(r->pool, values[j]);
1600                 j++;
1601             }
1602             ldap_value_free(values);
1603             vals[i] = str;
1604             i++;
1605         }
1606         *retvals = vals;
1607     }
1608
1609     /*
1610      * Add the new username to the search cache.
1611      */
1612     if (curl) {
1613         LDAP_CACHE_LOCK();
1614         the_search_node.username = filter;
1615         the_search_node.dn = *binddn;
1616         the_search_node.bindpw = bindpw;
1617         the_search_node.lastbind = apr_time_now();
1618         the_search_node.vals = vals;
1619         the_search_node.numvals = numvals;
1620
1621         /* Search again to make sure that another thread didn't ready insert
1622          * this node into the cache before we got here. If it does exist then
1623          * update the lastbind
1624          */
1625         search_nodep = util_ald_cache_fetch(curl->search_cache,
1626                                             &the_search_node);
1627         if ((search_nodep == NULL) ||
1628             (strcmp(*binddn, search_nodep->dn) != 0)) {
1629
1630             /* Nothing in cache, insert new entry */
1631             util_ald_cache_insert(curl->search_cache, &the_search_node);
1632         }
1633         else if ((!search_nodep->bindpw) ||
1634             (strcmp(bindpw, search_nodep->bindpw) != 0)) {
1635
1636             /* Entry in cache is invalid, remove it and insert new one */
1637             util_ald_cache_remove(curl->search_cache, search_nodep);
1638             util_ald_cache_insert(curl->search_cache, &the_search_node);
1639         }
1640         else {
1641             /* Cache entry is valid, update lastbind */
1642             search_nodep->lastbind = the_search_node.lastbind;
1643         }
1644         LDAP_CACHE_UNLOCK();
1645     }
1646     ldap_msgfree(res);
1647
1648     ldc->reason = "Authentication successful";
1649     return LDAP_SUCCESS;
1650 }
1651
1652 /*
1653  * This function will return the DN of the entry matching userid.
1654  * It is used to get the DN in case some other module than mod_auth_ldap
1655  * has authenticated the user.
1656  * The function is basically a copy of uldap_cache_checkuserid
1657  * with password checking removed.
1658  */
1659 static int uldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
1660                                  const char *url, const char *basedn,
1661                                  int scope, char **attrs, const char *filter,
1662                                  const char **binddn, const char ***retvals)
1663 {
1664     const char **vals = NULL;
1665     int numvals = 0;
1666     int result = 0;
1667     LDAPMessage *res, *entry;
1668     char *dn;
1669     int count;
1670     int failures = 0;
1671     util_url_node_t *curl;              /* Cached URL node */
1672     util_url_node_t curnode;
1673     util_search_node_t *search_nodep;   /* Cached search node */
1674     util_search_node_t the_search_node;
1675     apr_time_t curtime;
1676
1677     util_ldap_state_t *st =
1678         (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1679         &ldap_module);
1680
1681     /* Get the cache node for this url */
1682     LDAP_CACHE_LOCK();
1683     curnode.url = url;
1684     curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1685                                                    &curnode);
1686     if (curl == NULL) {
1687         curl = util_ald_create_caches(st, url);
1688     }
1689     LDAP_CACHE_UNLOCK();
1690
1691     if (curl) {
1692         LDAP_CACHE_LOCK();
1693         the_search_node.username = filter;
1694         search_nodep = util_ald_cache_fetch(curl->search_cache,
1695                                             &the_search_node);
1696         if (search_nodep != NULL) {
1697
1698             /* found entry in search cache... */
1699             curtime = apr_time_now();
1700
1701             /*
1702              * Remove this item from the cache if its expired.
1703              */
1704             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
1705                 /* ...but entry is too old */
1706                 util_ald_cache_remove(curl->search_cache, search_nodep);
1707             }
1708             else {
1709                 /* ...and entry is valid */
1710                 *binddn = apr_pstrdup(r->pool, search_nodep->dn);
1711                 if (attrs) {
1712                     int i;
1713                     *retvals = apr_pcalloc(r->pool, sizeof(char *) * search_nodep->numvals);
1714                     for (i = 0; i < search_nodep->numvals; i++) {
1715                         (*retvals)[i] = apr_pstrdup(r->pool, search_nodep->vals[i]);
1716                     }
1717                 }
1718                 LDAP_CACHE_UNLOCK();
1719                 ldc->reason = "Search successful (cached)";
1720                 return LDAP_SUCCESS;
1721             }
1722         }
1723         /* unlock this read lock */
1724         LDAP_CACHE_UNLOCK();
1725     }
1726
1727     /*
1728      * At this point, there is no valid cached search, so lets do the search.
1729      */
1730
1731     /*
1732      * If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1733      */
1734 start_over:
1735     if (failures++ > 10) {
1736         return result;
1737     }
1738     if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
1739         return result;
1740     }
1741
1742     /* try do the search */
1743     result = ldap_search_ext_s(ldc->ldap,
1744                                (char *)basedn, scope,
1745                                (char *)filter, attrs, 0,
1746                                NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &res);
1747     if (AP_LDAP_IS_SERVER_DOWN(result))
1748     {
1749         ldc->reason = "ldap_search_ext_s() for user failed with server down";
1750         uldap_connection_unbind(ldc);
1751         goto start_over;
1752     }
1753
1754     /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
1755     if (result != LDAP_SUCCESS) {
1756         ldc->reason = "ldap_search_ext_s() for user failed";
1757         return result;
1758     }
1759
1760     /*
1761      * We should have found exactly one entry; to find a different
1762      * number is an error.
1763      */
1764     count = ldap_count_entries(ldc->ldap, res);
1765     if (count != 1)
1766     {
1767         if (count == 0 )
1768             ldc->reason = "User not found";
1769         else
1770             ldc->reason = "User is not unique (search found two "
1771                           "or more matches)";
1772         ldap_msgfree(res);
1773         return LDAP_NO_SUCH_OBJECT;
1774     }
1775
1776     entry = ldap_first_entry(ldc->ldap, res);
1777
1778     /* Grab the dn, copy it into the pool, and free it again */
1779     dn = ldap_get_dn(ldc->ldap, entry);
1780     *binddn = apr_pstrdup(r->pool, dn);
1781     ldap_memfree(dn);
1782
1783     /*
1784      * Get values for the provided attributes.
1785      */
1786     if (attrs) {
1787         int k = 0;
1788         int i = 0;
1789         while (attrs[k++]);
1790         vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
1791         numvals = k;
1792         while (attrs[i]) {
1793             char **values;
1794             int j = 0;
1795             char *str = NULL;
1796             /* get values */
1797             values = ldap_get_values(ldc->ldap, entry, attrs[i]);
1798             while (values && values[j]) {
1799                 str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL)
1800                           : apr_pstrdup(r->pool, values[j]);
1801                 j++;
1802             }
1803             ldap_value_free(values);
1804             vals[i] = str;
1805             i++;
1806         }
1807         *retvals = vals;
1808     }
1809
1810     /*
1811      * Add the new username to the search cache.
1812      */
1813     if (curl) {
1814         LDAP_CACHE_LOCK();
1815         the_search_node.username = filter;
1816         the_search_node.dn = *binddn;
1817         the_search_node.bindpw = NULL;
1818         the_search_node.lastbind = apr_time_now();
1819         the_search_node.vals = vals;
1820         the_search_node.numvals = numvals;
1821
1822         /* Search again to make sure that another thread didn't ready insert
1823          * this node into the cache before we got here. If it does exist then
1824          * update the lastbind
1825          */
1826         search_nodep = util_ald_cache_fetch(curl->search_cache,
1827                                             &the_search_node);
1828         if ((search_nodep == NULL) ||
1829             (strcmp(*binddn, search_nodep->dn) != 0)) {
1830
1831             /* Nothing in cache, insert new entry */
1832             util_ald_cache_insert(curl->search_cache, &the_search_node);
1833         }
1834         /*
1835          * Don't update lastbind on entries with bindpw because
1836          * we haven't verified that password. It's OK to update
1837          * the entry if there is no password in it.
1838          */
1839         else if (!search_nodep->bindpw) {
1840             /* Cache entry is valid, update lastbind */
1841             search_nodep->lastbind = the_search_node.lastbind;
1842         }
1843         LDAP_CACHE_UNLOCK();
1844     }
1845
1846     ldap_msgfree(res);
1847
1848     ldc->reason = "Search successful";
1849     return LDAP_SUCCESS;
1850 }
1851
1852 /*
1853  * Reports if ssl support is enabled
1854  *
1855  * 1 = enabled, 0 = not enabled
1856  */
1857 static int uldap_ssl_supported(request_rec *r)
1858 {
1859    util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1860                                 r->server->module_config, &ldap_module);
1861
1862    return(st->ssl_supported);
1863 }
1864
1865
1866 /* ---------------------------------------- */
1867 /* config directives */
1868
1869
1870 static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1871                                              const char *bytes)
1872 {
1873     util_ldap_state_t *st =
1874         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1875                                                   &ldap_module);
1876     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1877
1878     if (err != NULL) {
1879         return err;
1880     }
1881
1882     st->cache_bytes = atol(bytes);
1883
1884     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1885                  "[%" APR_PID_T_FMT "] ldap cache: Setting shared memory "
1886                  " cache size to %" APR_SIZE_T_FMT " bytes.",
1887                  getpid(), st->cache_bytes);
1888
1889     return NULL;
1890 }
1891
1892 static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1893                                             const char *file)
1894 {
1895     util_ldap_state_t *st =
1896         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1897                                                   &ldap_module);
1898     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1899
1900     if (err != NULL) {
1901         return err;
1902     }
1903
1904     if (file) {
1905         st->cache_file = ap_server_root_relative(st->pool, file);
1906     }
1907     else {
1908         st->cache_file = NULL;
1909     }
1910
1911     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1912                  "LDAP cache: Setting shared memory cache file to %s bytes.",
1913                  st->cache_file);
1914
1915     return NULL;
1916 }
1917
1918 static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1919                                            const char *ttl)
1920 {
1921     util_ldap_state_t *st =
1922         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1923                                                   &ldap_module);
1924     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1925
1926     if (err != NULL) {
1927         return err;
1928     }
1929
1930     st->search_cache_ttl = atol(ttl) * 1000000;
1931
1932     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1933                  "[%" APR_PID_T_FMT "] ldap cache: Setting cache TTL to %ld"
1934                  " microseconds.", getpid(), st->search_cache_ttl);
1935
1936     return NULL;
1937 }
1938
1939 static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1940                                                const char *size)
1941 {
1942     util_ldap_state_t *st =
1943         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1944                                                   &ldap_module);
1945     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1946
1947     if (err != NULL) {
1948         return err;
1949     }
1950
1951     st->search_cache_size = atol(size);
1952     if (st->search_cache_size < 0) {
1953         st->search_cache_size = 0;
1954     }
1955
1956     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1957                  "[%" APR_PID_T_FMT "] ldap cache: Setting search cache size"
1958                  " to %ld entries.", getpid(), st->search_cache_size);
1959
1960     return NULL;
1961 }
1962
1963 static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1964                                              const char *ttl)
1965 {
1966     util_ldap_state_t *st =
1967         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1968                                                   &ldap_module);
1969     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1970
1971     if (err != NULL) {
1972         return err;
1973     }
1974
1975     st->compare_cache_ttl = atol(ttl) * 1000000;
1976
1977     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1978                  "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache TTL"
1979                  " to %ld microseconds.", getpid(), st->compare_cache_ttl);
1980
1981     return NULL;
1982 }
1983
1984 static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1985                                                  const char *size)
1986 {
1987     util_ldap_state_t *st =
1988         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1989                                                   &ldap_module);
1990     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1991
1992     if (err != NULL) {
1993         return err;
1994     }
1995
1996     st->compare_cache_size = atol(size);
1997     if (st->compare_cache_size < 0) {
1998         st->compare_cache_size = 0;
1999     }
2000
2001     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2002                  "[%" APR_PID_T_FMT "] ldap cache: Setting operation cache size"
2003                  " to %ld entries.", getpid(), st->compare_cache_size);
2004
2005     return NULL;
2006 }
2007
2008
2009 /**
2010  * Parse the certificate type.
2011  *
2012  * The type can be one of the following:
2013  * CA_DER, CA_BASE64, CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64,
2014  * CERT_KEY3_DB, CERT_NICKNAME, KEY_DER, KEY_BASE64
2015  *
2016  * If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
2017  */
2018 static int util_ldap_parse_cert_type(const char *type)
2019 {
2020     /* Authority file in binary DER format */
2021     if (0 == strcasecmp("CA_DER", type)) {
2022         return APR_LDAP_CA_TYPE_DER;
2023     }
2024
2025     /* Authority file in Base64 format */
2026     else if (0 == strcasecmp("CA_BASE64", type)) {
2027         return APR_LDAP_CA_TYPE_BASE64;
2028     }
2029
2030     /* Netscape certificate database file/directory */
2031     else if (0 == strcasecmp("CA_CERT7_DB", type)) {
2032         return APR_LDAP_CA_TYPE_CERT7_DB;
2033     }
2034
2035     /* Netscape secmod file/directory */
2036     else if (0 == strcasecmp("CA_SECMOD", type)) {
2037         return APR_LDAP_CA_TYPE_SECMOD;
2038     }
2039
2040     /* Client cert file in DER format */
2041     else if (0 == strcasecmp("CERT_DER", type)) {
2042         return APR_LDAP_CERT_TYPE_DER;
2043     }
2044
2045     /* Client cert file in Base64 format */
2046     else if (0 == strcasecmp("CERT_BASE64", type)) {
2047         return APR_LDAP_CERT_TYPE_BASE64;
2048     }
2049
2050     /* Client cert file in PKCS#12 format */
2051     else if (0 == strcasecmp("CERT_PFX", type)) {
2052         return APR_LDAP_CERT_TYPE_PFX;
2053     }
2054
2055     /* Netscape client cert database file/directory */
2056     else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
2057         return APR_LDAP_CERT_TYPE_KEY3_DB;
2058     }
2059
2060     /* Netscape client cert nickname */
2061     else if (0 == strcasecmp("CERT_NICKNAME", type)) {
2062         return APR_LDAP_CERT_TYPE_NICKNAME;
2063     }
2064
2065     /* Client cert key file in DER format */
2066     else if (0 == strcasecmp("KEY_DER", type)) {
2067         return APR_LDAP_KEY_TYPE_DER;
2068     }
2069
2070     /* Client cert key file in Base64 format */
2071     else if (0 == strcasecmp("KEY_BASE64", type)) {
2072         return APR_LDAP_KEY_TYPE_BASE64;
2073     }
2074
2075     /* Client cert key file in PKCS#12 format */
2076     else if (0 == strcasecmp("KEY_PFX", type)) {
2077         return APR_LDAP_KEY_TYPE_PFX;
2078     }
2079
2080     else {
2081         return APR_LDAP_CA_TYPE_UNKNOWN;
2082     }
2083
2084 }
2085
2086
2087 /**
2088  * Set LDAPTrustedGlobalCert.
2089  *
2090  * This directive takes either two or three arguments:
2091  * - certificate type
2092  * - certificate file / directory / nickname
2093  * - certificate password (optional)
2094  *
2095  * This directive may only be used globally.
2096  */
2097 static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
2098                                                      void *dummy,
2099                                                      const char *type,
2100                                                      const char *file,
2101                                                      const char *password)
2102 {
2103     util_ldap_state_t *st =
2104         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2105                                                   &ldap_module);
2106     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2107     apr_finfo_t finfo;
2108     apr_status_t rv;
2109     int cert_type = 0;
2110     apr_ldap_opt_tls_cert_t *cert;
2111
2112     if (err != NULL) {
2113         return err;
2114     }
2115
2116     /* handle the certificate type */
2117     if (type) {
2118         cert_type = util_ldap_parse_cert_type(type);
2119         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2120            return apr_psprintf(cmd->pool, "The certificate type %s is "
2121                                           "not recognised. It should be one "
2122                                           "of CA_DER, CA_BASE64, CA_CERT7_DB, "
2123                                           "CA_SECMOD, CERT_DER, CERT_BASE64, "
2124                                           "CERT_KEY3_DB, CERT_NICKNAME, "
2125                                           "KEY_DER, KEY_BASE64", type);
2126         }
2127     }
2128     else {
2129         return "Certificate type was not specified.";
2130     }
2131
2132     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2133                       "LDAP: SSL trusted global cert - %s (type %s)",
2134                        file, type);
2135
2136     /* add the certificate to the global array */
2137     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2138     cert->type = cert_type;
2139     cert->path = file;
2140     cert->password = password;
2141
2142     /* if file is a file or path, fix the path */
2143     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2144         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2145
2146         cert->path = ap_server_root_relative(cmd->pool, file);
2147         if (cert->path &&
2148             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2149                 != APR_SUCCESS))
2150         {
2151             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2152                          "LDAP: Could not open SSL trusted certificate "
2153                          "authority file - %s",
2154                          cert->path == NULL ? file : cert->path);
2155             return "Invalid global certificate file path";
2156         }
2157     }
2158
2159     return(NULL);
2160 }
2161
2162
2163 /**
2164  * Set LDAPTrustedClientCert.
2165  *
2166  * This directive takes either two or three arguments:
2167  * - certificate type
2168  * - certificate file / directory / nickname
2169  * - certificate password (optional)
2170  */
2171 static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
2172                                                      void *config,
2173                                                      const char *type,
2174                                                      const char *file,
2175                                                      const char *password)
2176 {
2177     util_ldap_state_t *st =
2178         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2179                                                   &ldap_module);
2180     apr_finfo_t finfo;
2181     apr_status_t rv;
2182     int cert_type = 0;
2183     apr_ldap_opt_tls_cert_t *cert;
2184
2185     /* handle the certificate type */
2186     if (type) {
2187         cert_type = util_ldap_parse_cert_type(type);
2188         if (APR_LDAP_CA_TYPE_UNKNOWN == cert_type) {
2189             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2190                                            "not recognised. It should be one "
2191                                            "of CERT_DER, CERT_BASE64, "
2192                                            "CERT_NICKNAME, CERT_PFX,"
2193                                            "KEY_DER, KEY_BASE64, KEY_PFX",
2194                                            type);
2195         }
2196         else if (APR_LDAP_CA_TYPE_DER == cert_type ||
2197                  APR_LDAP_CA_TYPE_BASE64 == cert_type ||
2198                  APR_LDAP_CA_TYPE_CERT7_DB == cert_type ||
2199                  APR_LDAP_CA_TYPE_SECMOD == cert_type ||
2200                  APR_LDAP_CERT_TYPE_PFX == cert_type ||
2201                  APR_LDAP_CERT_TYPE_KEY3_DB == cert_type) {
2202             return apr_psprintf(cmd->pool, "The certificate type \"%s\" is "
2203                                            "only valid within a "
2204                                            "LDAPTrustedGlobalCert directive. "
2205                                            "Only CERT_DER, CERT_BASE64, "
2206                                            "CERT_NICKNAME, KEY_DER, and "
2207                                            "KEY_BASE64 may be used.", type);
2208         }
2209     }
2210     else {
2211         return "Certificate type was not specified.";
2212     }
2213
2214     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2215                       "LDAP: SSL trusted client cert - %s (type %s)",
2216                        file, type);
2217
2218     /* add the certificate to the global array */
2219     cert = (apr_ldap_opt_tls_cert_t *)apr_array_push(st->global_certs);
2220     cert->type = cert_type;
2221     cert->path = file;
2222     cert->password = password;
2223
2224     /* if file is a file or path, fix the path */
2225     if (cert_type != APR_LDAP_CA_TYPE_UNKNOWN &&
2226         cert_type != APR_LDAP_CERT_TYPE_NICKNAME) {
2227
2228         cert->path = ap_server_root_relative(cmd->pool, file);
2229         if (cert->path &&
2230             ((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
2231                 != APR_SUCCESS))
2232         {
2233             ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
2234                          "LDAP: Could not open SSL client certificate "
2235                          "file - %s",
2236                          cert->path == NULL ? file : cert->path);
2237             return "Invalid client certificate file path";
2238         }
2239
2240     }
2241
2242     return(NULL);
2243 }
2244
2245
2246 /**
2247  * Set LDAPTrustedMode.
2248  *
2249  * This directive sets what encryption mode to use on a connection:
2250  * - None (No encryption)
2251  * - SSL (SSL encryption)
2252  * - STARTTLS (TLS encryption)
2253  */
2254 static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
2255                                               const char *mode)
2256 {
2257     util_ldap_state_t *st =
2258     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2259                                               &ldap_module);
2260
2261     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2262                       "LDAP: SSL trusted mode - %s",
2263                        mode);
2264
2265     if (0 == strcasecmp("NONE", mode)) {
2266         st->secure = APR_LDAP_NONE;
2267     }
2268     else if (0 == strcasecmp("SSL", mode)) {
2269         st->secure = APR_LDAP_SSL;
2270     }
2271     else if (   (0 == strcasecmp("TLS", mode))
2272              || (0 == strcasecmp("STARTTLS", mode))) {
2273         st->secure = APR_LDAP_STARTTLS;
2274     }
2275     else {
2276         return "Invalid LDAPTrustedMode setting: must be one of NONE, "
2277                "SSL, or TLS/STARTTLS";
2278     }
2279
2280     st->secure_set = 1;
2281     return(NULL);
2282 }
2283
2284 static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
2285                                                  void *dummy,
2286                                                  int mode)
2287 {
2288     util_ldap_state_t *st =
2289     (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2290                                               &ldap_module);
2291     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2292
2293     if (err != NULL) {
2294         return err;
2295     }
2296
2297     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2298                       "LDAP: SSL verify server certificate - %s",
2299                       mode?"TRUE":"FALSE");
2300
2301     st->verify_svr_cert = mode;
2302
2303     return(NULL);
2304 }
2305
2306
2307 static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
2308                                                     void *dummy,
2309                                                     const char *ttl)
2310 {
2311 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2312     util_ldap_state_t *st =
2313         (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
2314                                                   &ldap_module);
2315 #endif
2316     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
2317
2318     if (err != NULL) {
2319         return err;
2320     }
2321
2322 #ifdef LDAP_OPT_NETWORK_TIMEOUT
2323     st->connectionTimeout = atol(ttl);
2324
2325     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2326                  "[%" APR_PID_T_FMT "] ldap connection: Setting connection"
2327                  " timeout to %ld seconds.", getpid(), st->connectionTimeout);
2328 #else
2329     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
2330                  "LDAP: Connection timeout option not supported by the "
2331                  "LDAP SDK in use." );
2332 #endif
2333
2334     return NULL;
2335 }
2336
2337
2338 static const char *util_ldap_set_chase_referrals(cmd_parms *cmd,
2339                                                  void *config,
2340                                                  int mode)
2341 {
2342     util_ldap_config_t *dc =  config;
2343
2344     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2345                       "LDAP: Setting refferal chasing %s",
2346                       (mode == AP_LDAP_CHASEREFERRALS_ON) ? "ON" : "OFF");
2347
2348     dc->ChaseReferrals = mode;
2349
2350     return(NULL);
2351 }
2352
2353 static const char *util_ldap_set_referral_hop_limit(cmd_parms *cmd,
2354                                                     void *config,
2355                                                     const char *hop_limit)
2356 {
2357     util_ldap_config_t *dc =  config;
2358
2359     dc->ReferralHopLimit = atol(hop_limit);
2360
2361     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
2362                  "LDAP: Limit chased referrals to maximum of %d hops.",
2363                  dc->ReferralHopLimit);
2364
2365     return NULL;
2366 }
2367
2368 static void *util_ldap_create_dir_config(apr_pool_t *p, char *d) {
2369    util_ldap_config_t *dc =
2370        (util_ldap_config_t *) apr_pcalloc(p,sizeof(util_ldap_config_t));
2371
2372    /* defaults are AP_LDAP_CHASEREFERRALS_ON and AP_LDAP_DEFAULT_HOPLIMIT */
2373    dc->ChaseReferrals = AP_LDAP_CHASEREFERRALS_ON;
2374    dc->ReferralHopLimit = AP_LDAP_DEFAULT_HOPLIMIT;
2375
2376    return dc;
2377 }
2378
2379
2380 static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
2381 {
2382     util_ldap_state_t *st =
2383         (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
2384
2385     /* Create a per vhost pool for mod_ldap to use, serialized with 
2386      * st->mutex (also one per vhost).  both are replicated by fork(),
2387      * no shared memory managed by either.
2388      */
2389     apr_pool_create(&st->pool, p);
2390 #if APR_HAS_THREADS
2391     apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
2392 #endif
2393
2394     st->cache_bytes = 100000;
2395     st->search_cache_ttl = 600000000;
2396     st->search_cache_size = 1024;
2397     st->compare_cache_ttl = 600000000;
2398     st->compare_cache_size = 1024;
2399     st->connections = NULL;
2400     st->ssl_supported = 0;
2401     st->global_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2402     st->client_certs = apr_array_make(p, 10, sizeof(apr_ldap_opt_tls_cert_t));
2403     st->secure = APR_LDAP_NONE;
2404     st->secure_set = 0;
2405     st->connectionTimeout = 10;
2406     st->verify_svr_cert = 1;
2407
2408     return st;
2409 }
2410
2411 static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
2412                                     void *overridesv)
2413 {
2414     util_ldap_state_t *st = apr_pcalloc(p, sizeof(util_ldap_state_t));
2415     util_ldap_state_t *base = (util_ldap_state_t *) basev;
2416     util_ldap_state_t *overrides = (util_ldap_state_t *) overridesv;
2417
2418     st->pool = overrides->pool;
2419 #if APR_HAS_THREADS
2420     st->mutex = overrides->mutex;
2421 #endif
2422
2423     /* The cache settings can not be modified in a 
2424         virtual host since all server use the same
2425         shared memory cache. */
2426     st->cache_bytes = base->cache_bytes;
2427     st->search_cache_ttl = base->search_cache_ttl;
2428     st->search_cache_size = base->search_cache_size;
2429     st->compare_cache_ttl = base->compare_cache_ttl;
2430     st->compare_cache_size = base->compare_cache_size;
2431     st->util_ldap_cache_lock = base->util_ldap_cache_lock; 
2432
2433     st->connections = NULL;
2434     st->ssl_supported = 0;
2435     st->global_certs = apr_array_append(p, base->global_certs,
2436                                            overrides->global_certs);
2437     st->client_certs = apr_array_append(p, base->client_certs,
2438                                            overrides->client_certs);
2439     st->secure = (overrides->secure_set == 0) ? base->secure
2440                                               : overrides->secure;
2441
2442     /* These LDAP connection settings can not be overwritten in 
2443         a virtual host. Once set in the base server, they must 
2444         remain the same. None of the LDAP SDKs seem to be able
2445         to handle setting the verify_svr_cert flag on a 
2446         per-connection basis.  The OpenLDAP client appears to be
2447         able to handle the connection timeout per-connection
2448         but the Novell SDK cannot.  Allowing the timeout to
2449         be set by each vhost is of little value so rather than
2450         trying to make special expections for one LDAP SDK, GLOBAL_ONLY 
2451         is being enforced on this setting as well. */
2452     st->connectionTimeout = base->connectionTimeout;
2453     st->verify_svr_cert = base->verify_svr_cert;
2454
2455     return st;
2456 }
2457
2458 static apr_status_t util_ldap_cleanup_module(void *data)
2459 {
2460
2461     server_rec *s = data;
2462     util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
2463         s->module_config, &ldap_module);
2464
2465     if (st->ssl_supported) {
2466         apr_ldap_ssl_deinit();
2467     }
2468
2469     return APR_SUCCESS;
2470
2471 }
2472
2473 static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
2474                                  apr_pool_t *ptemp, server_rec *s)
2475 {
2476     apr_status_t result;
2477     server_rec *s_vhost;
2478     util_ldap_state_t *st_vhost;
2479
2480     util_ldap_state_t *st = (util_ldap_state_t *)
2481                             ap_get_module_config(s->module_config,
2482                                                  &ldap_module);
2483
2484     void *data;
2485     const char *userdata_key = "util_ldap_init";
2486     apr_ldap_err_t *result_err = NULL;
2487     int rc;
2488
2489     /* util_ldap_post_config() will be called twice. Don't bother
2490      * going through all of the initialization on the first call
2491      * because it will just be thrown away.*/
2492     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
2493     if (!data) {
2494         apr_pool_userdata_set((const void *)1, userdata_key,
2495                                apr_pool_cleanup_null, s->process->pool);
2496
2497 #if APR_HAS_SHARED_MEMORY
2498         /* If the cache file already exists then delete it.  Otherwise we are
2499          * going to run into problems creating the shared memory. */
2500         if (st->cache_file) {
2501             char *lck_file = apr_pstrcat(ptemp, st->cache_file, ".lck",
2502                                          NULL);
2503             apr_file_remove(lck_file, ptemp);
2504         }
2505 #endif
2506         return OK;
2507     }
2508
2509 #if APR_HAS_SHARED_MEMORY
2510     /* initializing cache if shared memory size is not zero and we already
2511      * don't have shm address
2512      */
2513     if (!st->cache_shm && st->cache_bytes > 0) {
2514 #endif
2515         result = util_ldap_cache_init(p, st);
2516         if (result != APR_SUCCESS) {
2517             ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
2518                          "LDAP cache: could not create shared memory segment");
2519             return DONE;
2520         }
2521
2522
2523 #if APR_HAS_SHARED_MEMORY
2524         if (st->cache_file) {
2525             st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
2526                                         NULL);
2527         }
2528 #endif
2529
2530         result = apr_global_mutex_create(&st->util_ldap_cache_lock,
2531                                          st->lock_file, APR_LOCK_DEFAULT,
2532                                          st->pool);
2533         if (result != APR_SUCCESS) {
2534             return result;
2535         }
2536
2537 #ifdef AP_NEED_SET_MUTEX_PERMS
2538         result = ap_unixd_set_global_mutex_perms(st->util_ldap_cache_lock);
2539         if (result != APR_SUCCESS) {
2540             ap_log_error(APLOG_MARK, APLOG_CRIT, result, s,
2541                          "LDAP cache: failed to set mutex permissions");
2542             return result;
2543         }
2544 #endif
2545
2546         /* merge config in all vhost */
2547         s_vhost = s->next;
2548         while (s_vhost) {
2549             st_vhost = (util_ldap_state_t *)
2550                        ap_get_module_config(s_vhost->module_config,
2551                                             &ldap_module);
2552
2553 #if APR_HAS_SHARED_MEMORY
2554             st_vhost->cache_shm = st->cache_shm;
2555             st_vhost->cache_rmm = st->cache_rmm;
2556             st_vhost->cache_file = st->cache_file;
2557             ap_log_error(APLOG_MARK, APLOG_DEBUG, result, s,
2558                          "LDAP merging Shared Cache conf: shm=0x%pp rmm=0x%pp "
2559                          "for VHOST: %s", st->cache_shm, st->cache_rmm,
2560                          s_vhost->server_hostname);
2561 #endif
2562             st_vhost->lock_file = st->lock_file;
2563             s_vhost = s_vhost->next;
2564         }
2565 #if APR_HAS_SHARED_MEMORY
2566     }
2567     else {
2568         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2569                      "LDAP cache: LDAPSharedCacheSize is zero, disabling "
2570                      "shared memory cache");
2571     }
2572 #endif
2573
2574     /* log the LDAP SDK used
2575      */
2576     {
2577         apr_ldap_err_t *result = NULL;
2578         apr_ldap_info(p, &(result));
2579         if (result != NULL) {
2580             ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "%s", result->reason);
2581         }
2582     }
2583
2584     apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
2585                               util_ldap_cleanup_module);
2586
2587     /*
2588      * Initialize SSL support, and log the result for the benefit of the admin.
2589      *
2590      * If SSL is not supported it is not necessarily an error, as the
2591      * application may not want to use it.
2592      */
2593     rc = apr_ldap_ssl_init(p,
2594                       NULL,
2595                       0,
2596                       &(result_err));
2597     if (APR_SUCCESS == rc) {
2598         rc = apr_ldap_set_option(ptemp, NULL, APR_LDAP_OPT_TLS_CERT,
2599                                  (void *)st->global_certs, &(result_err));
2600     }
2601
2602     if (APR_SUCCESS == rc) {
2603         st->ssl_supported = 1;
2604         ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2605                      "LDAP: SSL support available" );
2606     }
2607     else {
2608         st->ssl_supported = 0;
2609         ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
2610                      "LDAP: SSL support unavailable%s%s",
2611                      result_err ? ": " : "",
2612                      result_err ? result_err->reason : "");
2613     }
2614
2615     /* Initialize the rebind callback's cross reference list. */
2616     apr_ldap_rebind_init (p);
2617
2618     return(OK);
2619 }
2620
2621 static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
2622 {
2623     apr_status_t sts;
2624     util_ldap_state_t *st = ap_get_module_config(s->module_config,
2625                                                  &ldap_module);
2626
2627     if (!st->util_ldap_cache_lock) return;
2628
2629     sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
2630                                       st->lock_file, p);
2631     if (sts != APR_SUCCESS) {
2632         ap_log_error(APLOG_MARK, APLOG_CRIT, sts, s,
2633                      "Failed to initialise global mutex %s in child process %"
2634                      APR_PID_T_FMT ".",
2635                      st->lock_file, getpid());
2636     }
2637 }
2638
2639 static const command_rec util_ldap_cmds[] = {
2640     AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2641                   NULL, RSRC_CONF,
2642                   "Set the size of the shared memory cache (in bytes). Use "
2643                   "0 to disable the shared memory cache. (default: 100000)"),
2644
2645     AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2646                   NULL, RSRC_CONF,
2647                   "Set the file name for the shared memory cache."),
2648
2649     AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
2650                   NULL, RSRC_CONF,
2651                   "Set the maximum number of entries that are possible in the "
2652                   "LDAP search cache. Use 0 for no limit. "
2653                   "-1 disables the cache. (default: 1024)"),
2654
2655     AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2656                   NULL, RSRC_CONF,
2657                   "Set the maximum time (in seconds) that an item can be "
2658                   "cached in the LDAP search cache. Use 0 for no limit. "
2659                   "(default 600)"),
2660
2661     AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
2662                   NULL, RSRC_CONF,
2663                   "Set the maximum number of entries that are possible "
2664                   "in the LDAP compare cache. Use 0 for no limit. "
2665                   "Use -1 to disable the cache. (default: 1024)"),
2666
2667     AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2668                   NULL, RSRC_CONF,
2669                   "Set the maximum time (in seconds) that an item is cached "
2670                   "in the LDAP operation cache. Use 0 for no limit. "
2671                   "(default: 600)"),
2672
2673     AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
2674                    NULL, RSRC_CONF,
2675                    "Takes three arguments; the first argument is the cert "
2676                    "type of the second argument, one of CA_DER, CA_BASE64, "
2677                    "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2678                    "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
2679                    "specifes the file and/or directory containing the trusted CA "
2680                    "certificates (and global client certs for Netware) used to "
2681                    "validate the LDAP server. The third argument is an optional "
2682                    "passphrase if applicable."),
2683
2684     AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
2685                    NULL, RSRC_CONF,
2686                    "Takes three arguments: the first argument is the certificate "
2687                    "type of the second argument, one of CA_DER, CA_BASE64, "
2688                    "CA_CERT7_DB, CA_SECMOD, CERT_DER, CERT_BASE64, CERT_KEY3_DB, "
2689                    "CERT_NICKNAME, KEY_DER, or KEY_BASE64. The second argument "
2690                    "specifies the file and/or directory containing the client "
2691                    "certificate, or certificate ID used to validate this LDAP "
2692                    "client.  The third argument is an optional passphrase if "
2693                    "applicable."),
2694
2695     AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2696                   NULL, RSRC_CONF,
2697                   "Specify the type of security that should be applied to "
2698                   "an LDAP connection. One of; NONE, SSL or STARTTLS."),
2699
2700     AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2701                   NULL, RSRC_CONF,
2702                   "Set to 'ON' requires that the server certificate be verified"
2703                   " before a secure LDAP connection can be establish.  Default"
2704                   " 'ON'"),
2705
2706     AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2707                   NULL, RSRC_CONF,
2708                   "Specify the LDAP socket connection timeout in seconds "
2709                   "(default: 10)"),
2710
2711     AP_INIT_FLAG("LDAPReferrals", util_ldap_set_chase_referrals,
2712                   NULL, OR_AUTHCFG,
2713                   "Choose whether referrals are chased ['ON'|'OFF'].  Default 'ON'"),
2714
2715     AP_INIT_TAKE1("LDAPReferralHopLimit", util_ldap_set_referral_hop_limit,
2716                   NULL, OR_AUTHCFG,
2717                   "Limit the number of referral hops that LDAP can follow. "
2718                   "(Integer value, default=" AP_LDAP_DEFAULT_HOPLIMIT_STR ")"),
2719
2720     {NULL}
2721 };
2722
2723 static void util_ldap_register_hooks(apr_pool_t *p)
2724 {
2725     APR_REGISTER_OPTIONAL_FN(uldap_connection_open);
2726     APR_REGISTER_OPTIONAL_FN(uldap_connection_close);
2727     APR_REGISTER_OPTIONAL_FN(uldap_connection_unbind);
2728     APR_REGISTER_OPTIONAL_FN(uldap_connection_cleanup);
2729     APR_REGISTER_OPTIONAL_FN(uldap_connection_find);
2730     APR_REGISTER_OPTIONAL_FN(uldap_cache_comparedn);
2731     APR_REGISTER_OPTIONAL_FN(uldap_cache_compare);
2732     APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid);
2733     APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn);
2734     APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported);
2735     APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups);
2736
2737     ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
2738     ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
2739     ap_hook_child_init(util_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2740 }
2741
2742 module AP_MODULE_DECLARE_DATA ldap_module = {
2743    STANDARD20_MODULE_STUFF,
2744    util_ldap_create_dir_config, /* create dir config */
2745    NULL,                        /* merge dir config */
2746    util_ldap_create_config,     /* create server config */
2747    util_ldap_merge_config,      /* merge server config */
2748    util_ldap_cmds,              /* command table */
2749    util_ldap_register_hooks,    /* set up request processing hooks */
2750 };