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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
18
* util_ldap.c: LDAP things
20
* Original code from auth_ldap module for Apache v1.3:
21
* Copyright 1998, 1999 Enbridge Pipelines Inc.
22
* Copyright 1999-2001 Dave Carrigan
26
#include "http_config.h"
27
#include "http_core.h"
29
#include "http_protocol.h"
30
#include "http_request.h"
31
#include "util_ldap.h"
32
#include "util_ldap_cache.h"
34
#include <apr_strings.h>
41
#error mod_ldap requires APR-util to have LDAP support built in
44
#ifdef AP_NEED_SET_MUTEX_PERMS
48
/* defines for certificate file types
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
56
module AP_MODULE_DECLARE_DATA ldap_module;
58
#define LDAP_CACHE_LOCK() do { \
59
if (st->util_ldap_cache_lock) \
60
apr_global_mutex_lock(st->util_ldap_cache_lock); \
63
#define LDAP_CACHE_UNLOCK() do { \
64
if (st->util_ldap_cache_lock) \
65
apr_global_mutex_unlock(st->util_ldap_cache_lock); \
68
static void util_ldap_strdup (char **str, const char *newstr)
76
*str = strdup(newstr);
84
* This handler generates a status page about the current performance of
85
* the LDAP cache. It is enabled as follows:
87
* <Location /ldap-status>
88
* SetHandler ldap-status
92
static int util_ldap_handler(request_rec *r)
94
util_ldap_state_t *st = (util_ldap_state_t *)
95
ap_get_module_config(r->server->module_config,
98
r->allowed |= (1 << M_GET);
99
if (r->method_number != M_GET)
102
if (strcmp(r->handler, "ldap-status")) {
106
r->content_type = "text/html";
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"
115
util_ald_cache_display(r, st);
120
/* ------------------------------------------------------------------ */
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.
128
static void uldap_connection_close(util_ldap_connection_t *ldc)
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.
138
* For now we unbind the user when we finish with a connection, but
139
* we don't have to...
142
/* mark our connection as available for reuse */
145
apr_thread_mutex_unlock(ldc->lock);
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.
155
static apr_status_t uldap_connection_unbind(void *param)
157
util_ldap_connection_t *ldc = param;
161
ldap_unbind_s(ldc->ldap);
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.
176
static apr_status_t uldap_connection_cleanup(void *param)
178
util_ldap_connection_t *ldc = param;
182
/* unbind and disconnect from the LDAP server */
183
uldap_connection_unbind(ldc);
185
/* free the username and password */
187
free((void*)ldc->bindpw);
190
free((void*)ldc->binddn);
193
/* unlock this entry */
194
uldap_connection_close(ldc);
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.
206
* Returns LDAP_SUCCESS on success; and an error code on failure
208
static int uldap_connection_open(request_rec *r,
209
util_ldap_connection_t *ldc)
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,
220
/* sanity check for NULL */
225
/* If the connection is already bound, return
229
ldc->reason = "LDAP: connection open successful (already bound)";
233
/* create the ldap session handle
235
if (NULL == ldc->ldap)
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.
243
apr_ldap_init(ldc->pool, &(ldc->ldap),
245
APR_LDAP_SSL == ldc->secure ? LDAPS_PORT : LDAP_PORT,
250
if (result != NULL && result->rc) {
251
ldc->reason = result->reason;
254
if (NULL == ldc->ldap)
257
if (NULL == ldc->reason) {
258
ldc->reason = "LDAP: ldap initialization failed";
261
ldc->reason = result->reason;
266
/* always default to LDAP V3 */
267
ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
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);
277
ldc->reason = result->reason;
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);
290
ldc->reason = result->reason;
295
/* Set the alias dereferencing option */
296
ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
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));
303
#if defined(LDAPSSL_VERIFY_SERVER)
304
if (st->verify_svr_cert) {
305
result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_SERVER);
308
result->rc = ldapssl_set_verify_mode(LDAPSSL_VERIFY_NONE);
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);
318
int i = LDAP_OPT_X_TLS_NEVER;
319
result->rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &i);
324
#ifdef LDAP_OPT_NETWORK_TIMEOUT
325
if (st->connectionTimeout > 0) {
326
timeOut.tv_sec = st->connectionTimeout;
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");
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.
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.
351
for (failures=0; failures<10; failures++)
353
rc = ldap_simple_bind_s(ldc->ldap,
355
(char *)ldc->bindpw);
356
if (LDAP_SERVER_DOWN != rc) {
361
/* free the handle if there was an error
363
if (LDAP_SUCCESS != rc)
365
ldap_unbind_s(ldc->ldap);
368
ldc->reason = "LDAP: ldap_simple_bind_s() failed";
372
ldc->reason = "LDAP: connection open successful";
380
* Compare client certificate arrays.
382
* Returns 1 on compare failure, 0 otherwise.
384
static int compare_client_certs(apr_array_header_t *srcs,
385
apr_array_header_t *dests)
388
struct apr_ldap_opt_tls_cert_t *src, *dest;
390
/* arrays both NULL? if so, then equal */
391
if (srcs == NULL && dests == NULL) {
395
/* arrays different length or either NULL? If so, then not equal */
396
if (srcs == NULL || dests == NULL || srcs->nelts != dests->nelts) {
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) {
411
/* if we got here, the cert arrays were identical */
418
* Find an existing ldap connection struct that matches the
419
* provided ldap connection parameters.
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.
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)
431
struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
432
int secureflag = secure;
434
util_ldap_state_t *st =
435
(util_ldap_state_t *)ap_get_module_config(r->server->module_config,
440
/* mutex lock this function */
441
apr_thread_mutex_lock(st->mutex);
444
if (secure < APR_LDAP_NONE) {
445
secureflag = st->secure;
448
/* Search for an exact connection match in the list that is not
451
for (l=st->connections,p=NULL; l; l=l->next) {
453
if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
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))
466
/* If this connection didn't match the criteria, then we
467
* need to unlock the mutex so it is available to be reused.
469
apr_thread_mutex_unlock(l->lock);
475
/* If nothing found, search again, but we don't care about the
476
* binddn and bindpw this time.
479
for (l=st->connections,p=NULL; l; l=l->next) {
481
if (APR_SUCCESS == apr_thread_mutex_trylock(l->lock)) {
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))
488
/* the bind credentials have changed */
490
util_ldap_strdup((char**)&(l->binddn), binddn);
491
util_ldap_strdup((char**)&(l->bindpw), bindpw);
495
/* If this connection didn't match the criteria, then we
496
* need to unlock the mutex so it is available to be reused.
498
apr_thread_mutex_unlock(l->lock);
505
/* artificially disable cache */
508
/* If no connection what found after the second search, we
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.
518
/* create the details to the pool in st */
519
l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
521
apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, st->pool);
522
apr_thread_mutex_lock(l->lock);
526
l->host = apr_pstrdup(st->pool, host);
529
util_ldap_strdup((char**)&(l->binddn), binddn);
530
util_ldap_strdup((char**)&(l->bindpw), bindpw);
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
537
l->secure = secureflag;
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);
542
/* add the cleanup to the pool */
543
apr_pool_cleanup_register(l->pool, l,
544
uldap_connection_cleanup,
545
apr_pool_cleanup_null);
556
apr_thread_mutex_unlock(st->mutex);
561
/* ------------------------------------------------------------------ */
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.
570
* The lock for the ldap cache should already be acquired.
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)
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;
582
LDAPMessage *res, *entry;
585
util_ldap_state_t *st = (util_ldap_state_t *)
586
ap_get_module_config(r->server->module_config,
589
/* get cache entry (or create one) */
593
curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
595
curl = util_ald_create_caches(st, url);
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;
607
ldc->reason = "DN Comparison TRUE (direct strcmp())";
608
return LDAP_COMPARE_TRUE;
613
/* no - it's a server side compare */
616
/* is it in the compare cache? */
617
newnode.reqdn = (char *)reqdn;
618
node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
620
/* If it's in the cache, it's good */
621
/* unlock this read lock */
623
ldc->reason = "DN Comparison TRUE (cached)";
624
return LDAP_COMPARE_TRUE;
627
/* unlock this read lock */
632
if (failures++ > 10) {
633
/* too many failures */
637
/* make a server connection */
638
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
639
/* connect to server failed */
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))
649
ldc->reason = "DN Comparison ldap_search_ext_s() "
650
"failed with server down";
651
uldap_connection_unbind(ldc);
654
if (result != LDAP_SUCCESS) {
655
/* search for reqdn failed - no match */
656
ldc->reason = "DN Comparison ldap_search_ext_s() failed";
660
entry = ldap_first_entry(ldc->ldap, res);
661
searchdn = ldap_get_dn(ldc->ldap, entry);
664
if (strcmp(dn, searchdn) != 0) {
665
/* compare unsuccessful */
666
ldc->reason = "DN Comparison FALSE (checked on server)";
667
result = LDAP_COMPARE_FALSE;
671
/* compare successful - add to the compare cache */
673
newnode.reqdn = (char *)reqdn;
674
newnode.dn = (char *)dn;
676
node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
678
|| (strcmp(reqdn, node->reqdn) != 0)
679
|| (strcmp(dn, node->dn) != 0))
681
util_ald_cache_insert(curl->dn_compare_cache, &newnode);
685
ldc->reason = "DN Comparison TRUE (checked on server)";
686
result = LDAP_COMPARE_TRUE;
688
ldap_memfree(searchdn);
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
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)
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 */
713
util_ldap_state_t *st = (util_ldap_state_t *)
714
ap_get_module_config(r->server->module_config,
717
/* get cache entry (or create one) */
720
curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode);
722
curl = util_ald_create_caches(st, url);
727
/* make a comparison to the cache */
729
curtime = apr_time_now();
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;
736
compare_nodep = util_ald_cache_fetch(curl->compare_cache,
739
if (compare_nodep != NULL) {
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);
746
/* ...and it is good */
747
/* unlock this read lock */
749
if (LDAP_COMPARE_TRUE == compare_nodep->result) {
750
ldc->reason = "Comparison true (cached)";
751
return compare_nodep->result;
753
else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
754
ldc->reason = "Comparison false (cached)";
755
return compare_nodep->result;
757
else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
758
ldc->reason = "Comparison no such attribute (cached)";
759
return compare_nodep->result;
762
ldc->reason = "Comparison undefined (cached)";
763
return compare_nodep->result;
767
/* unlock this read lock */
772
if (failures++ > 10) {
773
/* too many failures */
776
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
781
if ((result = ldap_compare_s(ldc->ldap,
785
== LDAP_SERVER_DOWN) {
786
/* connection failed - try again */
787
ldc->reason = "ldap_compare_s() failed with server down";
788
uldap_connection_unbind(ldc);
792
ldc->reason = "Comparison complete";
793
if ((LDAP_COMPARE_TRUE == result) ||
794
(LDAP_COMPARE_FALSE == result) ||
795
(LDAP_NO_SUCH_ATTRIBUTE == result)) {
797
/* compare completed; caching result */
799
the_compare_node.lastcompare = curtime;
800
the_compare_node.result = result;
802
/* If the node doesn't exist then insert it, otherwise just update
803
* it with the last results
805
compare_nodep = util_ald_cache_fetch(curl->compare_cache,
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))
812
util_ald_cache_insert(curl->compare_cache, &the_compare_node);
815
compare_nodep->lastcompare = curtime;
816
compare_nodep->result = result;
820
if (LDAP_COMPARE_TRUE == result) {
821
ldc->reason = "Comparison true (adding to cache)";
822
return LDAP_COMPARE_TRUE;
824
else if (LDAP_COMPARE_FALSE == result) {
825
ldc->reason = "Comparison false (adding to cache)";
826
return LDAP_COMPARE_FALSE;
829
ldc->reason = "Comparison no such attribute (adding to cache)";
830
return LDAP_NO_SUCH_ATTRIBUTE;
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)
842
const char **vals = NULL;
845
LDAPMessage *res, *entry;
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;
855
util_ldap_state_t *st =
856
(util_ldap_state_t *)ap_get_module_config(r->server->module_config,
859
/* Get the cache node for this url */
862
curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
865
curl = util_ald_create_caches(st, url);
871
the_search_node.username = filter;
872
search_nodep = util_ald_cache_fetch(curl->search_cache,
874
if (search_nodep != NULL) {
876
/* found entry in search cache... */
877
curtime = apr_time_now();
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
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);
889
else if ( (search_nodep->bindpw)
890
&& (search_nodep->bindpw[0] != '\0')
891
&& (strcmp(search_nodep->bindpw, bindpw) == 0))
893
/* ...and entry is valid */
894
*binddn = search_nodep->dn;
895
*retvals = search_nodep->vals;
897
ldc->reason = "Authentication successful (cached)";
901
/* unlock this read lock */
906
* At this point, there is no valid cached search, so lets do the search.
910
* If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
913
if (failures++ > 10) {
916
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
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))
927
ldc->reason = "ldap_search_ext_s() for user failed with server down";
928
uldap_connection_unbind(ldc);
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";
939
* We should have found exactly one entry; to find a different
940
* number is an error.
942
count = ldap_count_entries(ldc->ldap, res);
946
ldc->reason = "User not found";
948
ldc->reason = "User is not unique (search found two "
951
return LDAP_NO_SUCH_OBJECT;
954
entry = ldap_first_entry(ldc->ldap, res);
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);
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
968
if (!bindpw || strlen(bindpw) <= 0) {
970
ldc->reason = "Empty password not allowed";
971
return LDAP_INVALID_CREDENTIALS;
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)
979
if ((result = ldap_simple_bind_s(ldc->ldap,
981
(char *)bindpw)) == LDAP_SERVER_DOWN) {
982
ldc->reason = "ldap_simple_bind_s() to check user credentials "
983
"failed with server down";
985
uldap_connection_unbind(ldc);
989
/* failure? if so - return */
990
if (result != LDAP_SUCCESS) {
991
ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
993
uldap_connection_unbind(ldc);
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.
1007
* Get values for the provided attributes.
1013
vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
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]);
1026
ldap_value_free(values);
1034
* Add the new username to the search cache.
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;
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
1049
search_nodep = util_ald_cache_fetch(curl->search_cache,
1051
if ((search_nodep == NULL) ||
1052
(strcmp(*binddn, search_nodep->dn) != 0)) {
1054
/* Nothing in cache, insert new entry */
1055
util_ald_cache_insert(curl->search_cache, &the_search_node);
1057
else if ((!search_nodep->bindpw) ||
1058
(strcmp(bindpw, search_nodep->bindpw) != 0)) {
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);
1065
/* Cache entry is valid, update lastbind */
1066
search_nodep->lastbind = the_search_node.lastbind;
1068
LDAP_CACHE_UNLOCK();
1072
ldc->reason = "Authentication successful";
1073
return LDAP_SUCCESS;
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.
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)
1088
const char **vals = NULL;
1091
LDAPMessage *res, *entry;
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;
1101
util_ldap_state_t *st =
1102
(util_ldap_state_t *)ap_get_module_config(r->server->module_config,
1105
/* Get the cache node for this url */
1108
curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache,
1111
curl = util_ald_create_caches(st, url);
1113
LDAP_CACHE_UNLOCK();
1117
the_search_node.username = filter;
1118
search_nodep = util_ald_cache_fetch(curl->search_cache,
1120
if (search_nodep != NULL) {
1122
/* found entry in search cache... */
1123
curtime = apr_time_now();
1126
* Remove this item from the cache if its expired.
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);
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;
1141
/* unlock this read lock */
1142
LDAP_CACHE_UNLOCK();
1146
* At this point, there is no valid cached search, so lets do the search.
1150
* If LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
1153
if (failures++ > 10) {
1156
if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) {
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)
1167
ldc->reason = "ldap_search_ext_s() for user failed with server down";
1168
uldap_connection_unbind(ldc);
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";
1179
* We should have found exactly one entry; to find a different
1180
* number is an error.
1182
count = ldap_count_entries(ldc->ldap, res);
1186
ldc->reason = "User not found";
1188
ldc->reason = "User is not unique (search found two "
1191
return LDAP_NO_SUCH_OBJECT;
1194
entry = ldap_first_entry(ldc->ldap, res);
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);
1202
* Get values for the provided attributes.
1208
vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
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]);
1221
ldap_value_free(values);
1229
* Add the new username to the search cache.
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;
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
1244
search_nodep = util_ald_cache_fetch(curl->search_cache,
1246
if ((search_nodep == NULL) ||
1247
(strcmp(*binddn, search_nodep->dn) != 0)) {
1249
/* Nothing in cache, insert new entry */
1250
util_ald_cache_insert(curl->search_cache, &the_search_node);
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.
1257
else if (!search_nodep->bindpw) {
1258
/* Cache entry is valid, update lastbind */
1259
search_nodep->lastbind = the_search_node.lastbind;
1261
LDAP_CACHE_UNLOCK();
1266
ldc->reason = "Search successful";
1267
return LDAP_SUCCESS;
1271
* Reports if ssl support is enabled
1273
* 1 = enabled, 0 = not enabled
1275
static int uldap_ssl_supported(request_rec *r)
1277
util_ldap_state_t *st = (util_ldap_state_t *)ap_get_module_config(
1278
r->server->module_config, &ldap_module);
1280
return(st->ssl_supported);
1284
/* ---------------------------------------- */
1285
/* config directives */
1288
static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy,
1291
util_ldap_state_t *st =
1292
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1294
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1300
st->cache_bytes = atol(bytes);
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);
1310
static const char *util_ldap_set_cache_file(cmd_parms *cmd, void *dummy,
1313
util_ldap_state_t *st =
1314
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1316
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1323
st->cache_file = ap_server_root_relative(st->pool, file);
1326
st->cache_file = NULL;
1329
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1330
"LDAP cache: Setting shared memory cache file to %s bytes.",
1336
static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy,
1339
util_ldap_state_t *st =
1340
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1342
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1348
st->search_cache_ttl = atol(ttl) * 1000000;
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);
1357
static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy,
1360
util_ldap_state_t *st =
1361
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1363
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1369
st->search_cache_size = atol(size);
1370
if (st->search_cache_size < 0) {
1371
st->search_cache_size = 0;
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);
1381
static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy,
1384
util_ldap_state_t *st =
1385
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1387
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1393
st->compare_cache_ttl = atol(ttl) * 1000000;
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);
1402
static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy,
1405
util_ldap_state_t *st =
1406
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1408
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1414
st->compare_cache_size = atol(size);
1415
if (st->compare_cache_size < 0) {
1416
st->compare_cache_size = 0;
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);
1428
* Parse the certificate type.
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
1434
* If no matches are found, APR_LDAP_CA_TYPE_UNKNOWN is returned.
1436
static int util_ldap_parse_cert_type(const char *type)
1438
/* Authority file in binary DER format */
1439
if (0 == strcasecmp("CA_DER", type)) {
1440
return APR_LDAP_CA_TYPE_DER;
1443
/* Authority file in Base64 format */
1444
else if (0 == strcasecmp("CA_BASE64", type)) {
1445
return APR_LDAP_CA_TYPE_BASE64;
1448
/* Netscape certificate database file/directory */
1449
else if (0 == strcasecmp("CA_CERT7_DB", type)) {
1450
return APR_LDAP_CA_TYPE_CERT7_DB;
1453
/* Netscape secmod file/directory */
1454
else if (0 == strcasecmp("CA_SECMOD", type)) {
1455
return APR_LDAP_CA_TYPE_SECMOD;
1458
/* Client cert file in DER format */
1459
else if (0 == strcasecmp("CERT_DER", type)) {
1460
return APR_LDAP_CERT_TYPE_DER;
1463
/* Client cert file in Base64 format */
1464
else if (0 == strcasecmp("CERT_BASE64", type)) {
1465
return APR_LDAP_CERT_TYPE_BASE64;
1468
/* Client cert file in PKCS#12 format */
1469
else if (0 == strcasecmp("CERT_PFX", type)) {
1470
return APR_LDAP_CERT_TYPE_PFX;
1473
/* Netscape client cert database file/directory */
1474
else if (0 == strcasecmp("CERT_KEY3_DB", type)) {
1475
return APR_LDAP_CERT_TYPE_KEY3_DB;
1478
/* Netscape client cert nickname */
1479
else if (0 == strcasecmp("CERT_NICKNAME", type)) {
1480
return APR_LDAP_CERT_TYPE_NICKNAME;
1483
/* Client cert key file in DER format */
1484
else if (0 == strcasecmp("KEY_DER", type)) {
1485
return APR_LDAP_KEY_TYPE_DER;
1488
/* Client cert key file in Base64 format */
1489
else if (0 == strcasecmp("KEY_BASE64", type)) {
1490
return APR_LDAP_KEY_TYPE_BASE64;
1493
/* Client cert key file in PKCS#12 format */
1494
else if (0 == strcasecmp("KEY_PFX", type)) {
1495
return APR_LDAP_KEY_TYPE_PFX;
1499
return APR_LDAP_CA_TYPE_UNKNOWN;
1506
* Set LDAPTrustedGlobalCert.
1508
* This directive takes either two or three arguments:
1509
* - certificate type
1510
* - certificate file / directory / nickname
1511
* - certificate password (optional)
1513
* This directive may only be used globally.
1515
static const char *util_ldap_set_trusted_global_cert(cmd_parms *cmd,
1519
const char *password)
1521
util_ldap_state_t *st =
1522
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1524
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1528
apr_ldap_opt_tls_cert_t *cert;
1534
/* handle the certificate 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);
1547
return "Certificate type was not specified.";
1550
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1551
"LDAP: SSL trusted global cert - %s (type %s)",
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;
1558
cert->password = password;
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) {
1564
cert->path = ap_server_root_relative(cmd->pool, file);
1566
((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
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";
1582
* Set LDAPTrustedClientCert.
1584
* This directive takes either two or three arguments:
1585
* - certificate type
1586
* - certificate file / directory / nickname
1587
* - certificate password (optional)
1589
static const char *util_ldap_set_trusted_client_cert(cmd_parms *cmd,
1593
const char *password)
1595
util_ldap_state_t *st =
1596
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1601
apr_ldap_opt_tls_cert_t *cert;
1603
/* handle the certificate 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",
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);
1629
return "Certificate type was not specified.";
1632
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1633
"LDAP: SSL trusted client cert - %s (type %s)",
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;
1640
cert->password = password;
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) {
1646
cert->path = ap_server_root_relative(cmd->pool, file);
1648
((rv = apr_stat (&finfo, cert->path, APR_FINFO_MIN, cmd->pool))
1651
ap_log_error(APLOG_MARK, APLOG_ERR, rv, cmd->server,
1652
"LDAP: Could not open SSL client certificate "
1654
cert->path == NULL ? file : cert->path);
1655
return "Invalid client certificate file path";
1665
* Set LDAPTrustedMode.
1667
* This directive sets what encryption mode to use on a connection:
1668
* - None (No encryption)
1669
* - SSL (SSL encryption)
1670
* - STARTTLS (TLS encryption)
1672
static const char *util_ldap_set_trusted_mode(cmd_parms *cmd, void *dummy,
1675
util_ldap_state_t *st =
1676
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1679
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1680
"LDAP: SSL trusted mode - %s",
1683
if (0 == strcasecmp("NONE", mode)) {
1684
st->secure = APR_LDAP_NONE;
1686
else if (0 == strcasecmp("SSL", mode)) {
1687
st->secure = APR_LDAP_SSL;
1689
else if ( (0 == strcasecmp("TLS", mode))
1690
|| (0 == strcasecmp("STARTTLS", mode))) {
1691
st->secure = APR_LDAP_STARTTLS;
1694
return "Invalid LDAPTrustedMode setting: must be one of NONE, "
1695
"SSL, or TLS/STARTTLS";
1702
static const char *util_ldap_set_verify_srv_cert(cmd_parms *cmd,
1706
util_ldap_state_t *st =
1707
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1709
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1715
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
1716
"LDAP: SSL verify server certificate - %s",
1717
mode?"TRUE":"FALSE");
1719
st->verify_svr_cert = mode;
1725
static const char *util_ldap_set_connection_timeout(cmd_parms *cmd,
1729
util_ldap_state_t *st =
1730
(util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
1732
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1738
#ifdef LDAP_OPT_NETWORK_TIMEOUT
1739
st->connectionTimeout = atol(ttl);
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);
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." );
1754
static void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
1756
util_ldap_state_t *st =
1757
(util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
1759
/* Create a per vhost pool for mod_ldap to use, serialized with
1760
* st->mutex (also one per vhost)
1762
apr_pool_create(&st->pool, p);
1764
apr_thread_mutex_create(&st->mutex, APR_THREAD_MUTEX_DEFAULT, st->pool);
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;
1778
st->connectionTimeout = 10;
1779
st->verify_svr_cert = 1;
1784
static void *util_ldap_merge_config(apr_pool_t *p, void *basev,
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;
1791
st->pool = overrides->pool;
1793
st->mutex = overrides->mutex;
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;
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;
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;
1830
static apr_status_t util_ldap_cleanup_module(void *data)
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);
1837
if (st->ssl_supported) {
1838
apr_ldap_ssl_deinit();
1845
static int util_ldap_post_config(apr_pool_t *p, apr_pool_t *plog,
1846
apr_pool_t *ptemp, server_rec *s)
1848
apr_status_t result;
1849
server_rec *s_vhost;
1850
util_ldap_state_t *st_vhost;
1852
util_ldap_state_t *st = (util_ldap_state_t *)
1853
ap_get_module_config(s->module_config,
1857
const char *userdata_key = "util_ldap_init";
1858
apr_ldap_err_t *result_err = NULL;
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);
1866
apr_pool_userdata_set((const void *)1, userdata_key,
1867
apr_pool_cleanup_null, s->process->pool);
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",
1875
apr_file_remove(lck_file, ptemp);
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
1885
if (!st->cache_shm && st->cache_bytes > 0) {
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");
1895
#if APR_HAS_SHARED_MEMORY
1896
if (st->cache_file) {
1897
st->lock_file = apr_pstrcat(st->pool, st->cache_file, ".lck",
1902
result = apr_global_mutex_create(&st->util_ldap_cache_lock,
1903
st->lock_file, APR_LOCK_DEFAULT,
1905
if (result != APR_SUCCESS) {
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");
1918
/* merge config in all vhost */
1921
st_vhost = (util_ldap_state_t *)
1922
ap_get_module_config(s_vhost->module_config,
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);
1934
st_vhost->lock_file = st->lock_file;
1935
s_vhost = s_vhost->next;
1937
#if APR_HAS_SHARED_MEMORY
1940
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1941
"LDAP cache: LDAPSharedCacheSize is zero, disabling "
1942
"shared memory cache");
1946
/* log the LDAP SDK used
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);
1956
apr_pool_cleanup_register(p, s, util_ldap_cleanup_module,
1957
util_ldap_cleanup_module);
1960
* Initialize SSL support, and log the result for the benefit of the admin.
1962
* If SSL is not supported it is not necessarily an error, as the
1963
* application may not want to use it.
1965
rc = apr_ldap_ssl_init(p,
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));
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" );
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 : "");
1990
static void util_ldap_child_init(apr_pool_t *p, server_rec *s)
1993
util_ldap_state_t *st = ap_get_module_config(s->module_config,
1996
if (!st->util_ldap_cache_lock) return;
1998
sts = apr_global_mutex_child_init(&st->util_ldap_cache_lock,
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 %"
2004
st->lock_file, getpid());
2008
static const command_rec util_ldap_cmds[] = {
2009
AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes,
2011
"Set the size of the shared memory cache (in bytes). Use "
2012
"0 to disable the shared memory cache. (default: 100000)"),
2014
AP_INIT_TAKE1("LDAPSharedCacheFile", util_ldap_set_cache_file,
2016
"Set the file name for the shared memory cache."),
2018
AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries,
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)"),
2024
AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl,
2026
"Set the maximum time (in seconds) that an item can be "
2027
"cached in the LDAP search cache. Use 0 for no limit. "
2030
AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries,
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)"),
2036
AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl,
2038
"Set the maximum time (in seconds) that an item is cached "
2039
"in the LDAP operation cache. Use 0 for no limit. "
2042
AP_INIT_TAKE23("LDAPTrustedGlobalCert", util_ldap_set_trusted_global_cert,
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."),
2052
AP_INIT_TAKE23("LDAPTrustedClientCert", util_ldap_set_trusted_client_cert,
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."),
2062
AP_INIT_TAKE1("LDAPTrustedMode", util_ldap_set_trusted_mode,
2064
"Specify the type of security that should be applied to "
2065
"an LDAP connection. One of; NONE, SSL or STARTTLS."),
2067
AP_INIT_FLAG("LDAPVerifyServerCert", util_ldap_set_verify_srv_cert,
2069
"Set to 'ON' requires that the server certificate be verified "
2070
"before a secure LDAP connection can be establish. Default 'ON'"),
2072
AP_INIT_TAKE1("LDAPConnectionTimeout", util_ldap_set_connection_timeout,
2074
"Specify the LDAP socket connection timeout in seconds "
2080
static void util_ldap_register_hooks(apr_pool_t *p)
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);
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);
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 */