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