~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/ldap/util_ldap.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

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