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