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