1/* $NetBSD: dict_ldap.c,v 1.5 2023/12/23 20:30:43 christos Exp $ */ 2 3/*++ 4/* NAME 5/* dict_ldap 3 6/* SUMMARY 7/* dictionary manager interface to LDAP maps 8/* SYNOPSIS 9/* #include <dict_ldap.h> 10/* 11/* DICT *dict_ldap_open(attribute, dummy, dict_flags) 12/* const char *ldapsource; 13/* int dummy; 14/* int dict_flags; 15/* DESCRIPTION 16/* dict_ldap_open() makes LDAP user information accessible via 17/* the generic dictionary operations described in dict_open(3). 18/* 19/* Arguments: 20/* .IP ldapsource 21/* Either the path to the LDAP configuration file (if it starts 22/* with '/' or '.'), or the prefix which will be used to obtain 23/* configuration parameters for this search. 24/* 25/* In the first case, the configuration variables below are 26/* specified in the file as \fBname\fR=\fBvalue\fR pairs. 27/* 28/* In the second case, the configuration variables are prefixed 29/* with the value of \fIldapsource\fR and an underscore, 30/* and they are specified in main.cf. For example, if this 31/* value is \fBldapone\fR, the variables would look like 32/* \fBldapone_server_host\fR, \fBldapone_search_base\fR, and so on. 33/* .IP dummy 34/* Not used; this argument exists only for compatibility with 35/* the dict_open(3) interface. 36/* SEE ALSO 37/* dict(3) generic dictionary manager 38/* ldap_table(5) LDAP client configuration 39/* AUTHOR(S) 40/* Prabhat K Singh 41/* VSNL, Bombay, India. 42/* prabhat@giasbm01.vsnl.net.in 43/* 44/* Wietse Venema 45/* IBM T.J. Watson Research 46/* P.O. Box 704 47/* Yorktown Heights, NY 10598, USA 48/* 49/* Wietse Venema 50/* Google, Inc. 51/* 111 8th Avenue 52/* New York, NY 10011, USA 53/* 54/* John Hensley 55/* john@sunislelodge.com 56/* 57/* LaMont Jones 58/* lamont@debian.org 59/* 60/* Victor Duchovni 61/* Morgan Stanley 62/* New York, USA 63/* 64/* Liviu Daia 65/* Institute of Mathematics of the Romanian Academy 66/* P.O. BOX 1-764 67/* RO-014700 Bucharest, ROMANIA 68/*--*/ 69 70/* System library. */ 71 72#include "sys_defs.h" 73 74#ifdef HAS_LDAP 75 76#include <sys/time.h> 77#include <stdio.h> 78#include <signal.h> 79#include <setjmp.h> 80#include <stdlib.h> 81#include <lber.h> 82#include <ldap.h> 83#include <string.h> 84#include <ctype.h> 85#include <unistd.h> 86 87#ifdef STRCASECMP_IN_STRINGS_H 88#include <strings.h> 89#endif 90 91 /* 92 * Older APIs have weird memory freeing behavior. 93 */ 94#if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000) 95#error "Your LDAP version is too old" 96#endif 97 98/* Handle differences between LDAP SDK's constant definitions */ 99#ifndef LDAP_CONST 100#define LDAP_CONST const 101#endif 102#ifndef LDAP_OPT_SUCCESS 103#define LDAP_OPT_SUCCESS 0 104#endif 105 106/* Utility library. */ 107 108#include <msg.h> 109#include <mymalloc.h> 110#include <vstring.h> 111#include <dict.h> 112#include <stringops.h> 113#include <binhash.h> 114#include <name_code.h> 115 116/* Global library. */ 117 118#include "cfg_parser.h" 119#include "db_common.h" 120#include "mail_conf.h" 121 122#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP) 123 124 /* 125 * SASL headers, for sasl_interact_t. Either SASL v1 or v2 should be fine. 126 */ 127#include <sasl.h> 128#endif 129 130/* Application-specific. */ 131 132#include "dict_ldap.h" 133 134#define DICT_LDAP_BIND_NONE 0 135#define DICT_LDAP_BIND_SIMPLE 1 136#define DICT_LDAP_BIND_SASL 2 137#define DICT_LDAP_DO_BIND(d) ((d)->bind != DICT_LDAP_BIND_NONE) 138#define DICT_LDAP_DO_SASL(d) ((d)->bind == DICT_LDAP_BIND_SASL) 139 140static const NAME_CODE bindopt_table[] = { 141 CONFIG_BOOL_NO, DICT_LDAP_BIND_NONE, 142 "none", DICT_LDAP_BIND_NONE, 143 CONFIG_BOOL_YES, DICT_LDAP_BIND_SIMPLE, 144 "simple", DICT_LDAP_BIND_SIMPLE, 145#ifdef LDAP_API_FEATURE_X_OPENLDAP 146#if defined(USE_LDAP_SASL) 147 "sasl", DICT_LDAP_BIND_SASL, 148#endif 149#endif 150 0, -1, 151}; 152 153typedef struct { 154 LDAP *conn_ld; 155 int conn_refcount; 156} LDAP_CONN; 157 158/* 159 * Structure containing all the configuration parameters for a given 160 * LDAP source, plus its connection handle. 161 */ 162typedef struct { 163 DICT dict; /* generic member */ 164 CFG_PARSER *parser; /* common parameter parser */ 165 char *query; /* db_common_expand() query */ 166 char *result_format; /* db_common_expand() result_format */ 167 void *ctx; /* db_common_parse() context */ 168 int dynamic_base; /* Search base has substitutions? */ 169 int expansion_limit; 170 char *server_host; 171 int server_port; 172 int scope; 173 char *search_base; 174 ARGV *result_attributes; 175 int num_terminal; /* Number of terminal attributes. */ 176 int num_leaf; /* Number of leaf attributes */ 177 int num_attributes; /* Combined # of non-special attrs */ 178 int bind; 179 char *bind_dn; 180 char *bind_pw; 181 int timeout; 182 int dereference; 183 long recursion_limit; 184 long size_limit; 185 int chase_referrals; 186 int debuglevel; 187 int version; 188#ifdef LDAP_API_FEATURE_X_OPENLDAP 189#if defined(USE_LDAP_SASL) 190 int sasl; 191 char *sasl_mechs; 192 char *sasl_realm; 193 char *sasl_authz; 194 int sasl_minssf; 195#endif 196 int ldap_ssl; 197 int start_tls; 198 int tls_require_cert; 199 char *tls_ca_cert_file; 200 char *tls_ca_cert_dir; 201 char *tls_cert; 202 char *tls_key; 203 char *tls_random_file; 204 char *tls_cipher_suite; 205#endif 206 BINHASH_INFO *ht; /* hash entry for LDAP connection */ 207 LDAP *ld; /* duplicated from conn->conn_ld */ 208} DICT_LDAP; 209 210#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value)) 211 212#define DICT_LDAP_UNBIND_RETURN(__ld, __err, __ret) do { \ 213 dict_ldap_unbind(__ld); \ 214 (__ld) = 0; \ 215 dict_ldap->dict.error = (__err); \ 216 return ((__ret)); \ 217 } while (0) 218 219 /* 220 * Bitrot: LDAP_API 3000 and up (OpenLDAP 2.2.x) deprecated ldap_unbind() 221 */ 222#if LDAP_API_VERSION >= 3000 223#define dict_ldap_unbind(ld) ldap_unbind_ext((ld), 0, 0) 224#define dict_ldap_abandon(ld, msg) ldap_abandon_ext((ld), (msg), 0, 0) 225#else 226#define dict_ldap_unbind(ld) ldap_unbind(ld) 227#define dict_ldap_abandon(ld, msg) ldap_abandon((ld), (msg)) 228#endif 229 230static int dict_ldap_vendor_version(void) 231{ 232 const char *myname = "dict_ldap_api_info"; 233 LDAPAPIInfo api; 234 235 /* 236 * We tell the library our version, and it tells us its version and/or 237 * may return an error code if the versions are not the same. 238 */ 239 api.ldapai_info_version = LDAP_API_INFO_VERSION; 240 if (ldap_get_option(0, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS 241 || api.ldapai_info_version != LDAP_API_INFO_VERSION) { 242 if (api.ldapai_info_version != LDAP_API_INFO_VERSION) 243 msg_fatal("%s: run-time API_INFO version: %d, compiled with: %d", 244 myname, api.ldapai_info_version, LDAP_API_INFO_VERSION); 245 else 246 msg_fatal("%s: ldap_get_option(API_INFO) failed", myname); 247 } 248 if (strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME) != 0) 249 msg_fatal("%s: run-time API vendor: %s, compiled with: %s", 250 myname, api.ldapai_vendor_name, LDAP_VENDOR_NAME); 251 252 return (api.ldapai_vendor_version); 253} 254 255/* 256 * Quoting rules. 257 */ 258 259/* rfc2253_quote - Quote input key for safe inclusion in the search base */ 260 261static void rfc2253_quote(DICT *unused, const char *name, VSTRING *result) 262{ 263 const char *sub = name; 264 size_t len; 265 266 /* 267 * The RFC only requires quoting of a leading or trailing space, but it 268 * is harmless to quote whitespace everywhere. Similarly, we quote all 269 * '#' characters, even though only the leading '#' character requires 270 * quoting per the RFC. 271 */ 272 while (*sub) 273 if ((len = strcspn(sub, " \t\"#+,;<>\\")) > 0) { 274 vstring_strncat(result, sub, len); 275 sub += len; 276 } else 277 vstring_sprintf_append(result, "\\%02X", 278 *((const unsigned char *) sub++)); 279} 280 281/* rfc2254_quote - Quote input key for safe inclusion in the query filter */ 282 283static void rfc2254_quote(DICT *unused, const char *name, VSTRING *result) 284{ 285 const char *sub = name; 286 size_t len; 287 288 /* 289 * If any characters in the supplied address should be escaped per RFC 290 * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to 291 * Samuel Tardieu for spotting that wildcard searches were being done in 292 * the first place, which prompted the ill-conceived lookup_wildcards 293 * parameter and then this more comprehensive mechanism. 294 */ 295 while (*sub) 296 if ((len = strcspn(sub, "*()\\")) > 0) { 297 vstring_strncat(result, sub, len); 298 sub += len; 299 } else 300 vstring_sprintf_append(result, "\\%02X", 301 *((const unsigned char *) sub++)); 302} 303 304static BINHASH *conn_hash = 0; 305 306#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT) 307/* 308 * LDAP connection timeout support. 309 */ 310static jmp_buf env; 311 312static void dict_ldap_timeout(int unused_sig) 313{ 314 longjmp(env, 1); 315} 316 317#endif 318 319static void dict_ldap_logprint(LDAP_CONST char *data) 320{ 321 const char *myname = "dict_ldap_debug"; 322 char *buf, *p; 323 324 buf = mystrdup(data); 325 if (*buf) { 326 p = buf + strlen(buf) - 1; 327 while (p - buf >= 0 && ISSPACE(*p)) 328 *p-- = 0; 329 } 330 msg_info("%s: %s", myname, buf); 331 myfree(buf); 332} 333 334static int dict_ldap_get_errno(LDAP *ld) 335{ 336 int rc; 337 338 if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_OPT_SUCCESS) 339 rc = LDAP_OTHER; 340 return rc; 341} 342 343static int dict_ldap_set_errno(LDAP *ld, int rc) 344{ 345 (void) ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &rc); 346 return rc; 347} 348 349#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP) 350 351 /* 352 * Context structure for SASL property callback. 353 */ 354typedef struct bind_props { 355 char *authcid; 356 char *passwd; 357 char *realm; 358 char *authzid; 359} bind_props; 360 361static int ldap_b2_interact(LDAP *ld, unsigned flags, void *props, void *inter) 362{ 363 364 sasl_interact_t *in; 365 bind_props *ctx = (bind_props *) props; 366 367 for (in = inter; in->id != SASL_CB_LIST_END; in++) { 368 in->result = NULL; 369 switch (in->id) { 370 case SASL_CB_GETREALM: 371 in->result = ctx->realm; 372 break; 373 case SASL_CB_AUTHNAME: 374 in->result = ctx->authcid; 375 break; 376 case SASL_CB_USER: 377 in->result = ctx->authzid; 378 break; 379 case SASL_CB_PASS: 380 in->result = ctx->passwd; 381 break; 382 } 383 if (in->result) 384 in->len = strlen(in->result); 385 } 386 return LDAP_SUCCESS; 387} 388 389#endif 390 391/* dict_ldap_result - Read and parse LDAP result */ 392 393static int dict_ldap_result(LDAP *ld, int msgid, int timeout, LDAPMessage **res) 394{ 395 struct timeval mytimeval; 396 int err; 397 398 mytimeval.tv_sec = timeout; 399 mytimeval.tv_usec = 0; 400 401#define GET_ALL 1 402 if (ldap_result(ld, msgid, GET_ALL, &mytimeval, res) == -1) 403 return (dict_ldap_get_errno(ld)); 404 405 if ((err = dict_ldap_get_errno(ld)) != LDAP_SUCCESS) { 406 if (err == LDAP_TIMEOUT) { 407 (void) dict_ldap_abandon(ld, msgid); 408 return (dict_ldap_set_errno(ld, LDAP_TIMEOUT)); 409 } 410 return err; 411 } 412 return LDAP_SUCCESS; 413} 414 415#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP) 416 417/* Asynchronous SASL auth if SASL is enabled */ 418 419static int dict_ldap_bind_sasl(DICT_LDAP *dict_ldap) 420{ 421 int rc; 422 bind_props props; 423 static VSTRING *minssf = 0; 424 425 if (minssf == 0) 426 minssf = vstring_alloc(12); 427 428 vstring_sprintf(minssf, "minssf=%d", dict_ldap->sasl_minssf); 429 430 if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_X_SASL_SECPROPS, 431 (char *) minssf)) != LDAP_OPT_SUCCESS) 432 return (rc); 433 434 props.authcid = dict_ldap->bind_dn; 435 props.passwd = dict_ldap->bind_pw; 436 props.realm = dict_ldap->sasl_realm; 437 props.authzid = dict_ldap->sasl_authz; 438 439 if ((rc = ldap_sasl_interactive_bind_s(dict_ldap->ld, NULL, 440 dict_ldap->sasl_mechs, NULL, NULL, 441 LDAP_SASL_QUIET, ldap_b2_interact, 442 &props)) != LDAP_SUCCESS) 443 return (rc); 444 445 return (LDAP_SUCCESS); 446} 447 448#endif 449 450/* dict_ldap_bind_st - Synchronous simple auth with timeout */ 451 452static int dict_ldap_bind_st(DICT_LDAP *dict_ldap) 453{ 454 int rc; 455 int err = LDAP_SUCCESS; 456 int msgid; 457 LDAPMessage *res; 458 struct berval cred; 459 460 cred.bv_val = dict_ldap->bind_pw; 461 cred.bv_len = strlen(cred.bv_val); 462 if ((rc = ldap_sasl_bind(dict_ldap->ld, dict_ldap->bind_dn, 463 LDAP_SASL_SIMPLE, &cred, 464 0, 0, &msgid)) != LDAP_SUCCESS) 465 return (rc); 466 if ((rc = dict_ldap_result(dict_ldap->ld, msgid, dict_ldap->timeout, 467 &res)) != LDAP_SUCCESS) 468 return (rc); 469 470#define FREE_RESULT 1 471 rc = ldap_parse_result(dict_ldap->ld, res, &err, 0, 0, 0, 0, FREE_RESULT); 472 return (rc == LDAP_SUCCESS ? err : rc); 473} 474 475/* search_st - Synchronous search with timeout */ 476 477static int search_st(LDAP *ld, char *base, int scope, char *query, 478 char **attrs, int timeout, LDAPMessage **res) 479{ 480 struct timeval mytimeval; 481 int msgid; 482 int rc; 483 int err; 484 485 mytimeval.tv_sec = timeout; 486 mytimeval.tv_usec = 0; 487 488#define WANTVALS 0 489#define USE_SIZE_LIM_OPT -1 /* Any negative value will do */ 490 491 if ((rc = ldap_search_ext(ld, base, scope, query, attrs, WANTVALS, 0, 0, 492 &mytimeval, USE_SIZE_LIM_OPT, 493 &msgid)) != LDAP_SUCCESS) 494 return rc; 495 496 if ((rc = dict_ldap_result(ld, msgid, timeout, res)) != LDAP_SUCCESS) 497 return (rc); 498 499#define DONT_FREE_RESULT 0 500 rc = ldap_parse_result(ld, *res, &err, 0, 0, 0, 0, DONT_FREE_RESULT); 501 return (err != LDAP_SUCCESS ? err : rc); 502} 503 504#ifdef LDAP_API_FEATURE_X_OPENLDAP 505static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap) 506{ 507 const char *myname = "dict_ldap_set_tls_options"; 508 int rc; 509 510#ifdef LDAP_OPT_X_TLS_NEWCTX 511 int am_server = 0; 512 LDAP *ld = dict_ldap->ld; 513 514#else 515 LDAP *ld = 0; 516 517#endif 518 519 if (dict_ldap->start_tls || dict_ldap->ldap_ssl) { 520 if (*dict_ldap->tls_random_file) { 521 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_RANDOM_FILE, 522 dict_ldap->tls_random_file)) != LDAP_SUCCESS) { 523 msg_warn("%s: Unable to set tls_random_file to %s: %d: %s", 524 myname, dict_ldap->tls_random_file, 525 rc, ldap_err2string(rc)); 526 return (-1); 527 } 528 } 529 if (*dict_ldap->tls_ca_cert_file) { 530 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, 531 dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS) { 532 msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s", 533 myname, dict_ldap->tls_ca_cert_file, 534 rc, ldap_err2string(rc)); 535 return (-1); 536 } 537 } 538 if (*dict_ldap->tls_ca_cert_dir) { 539 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, 540 dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS) { 541 msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s", 542 myname, dict_ldap->tls_ca_cert_dir, 543 rc, ldap_err2string(rc)); 544 return (-1); 545 } 546 } 547 if (*dict_ldap->tls_cert) { 548 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, 549 dict_ldap->tls_cert)) != LDAP_SUCCESS) { 550 msg_warn("%s: Unable to set tls_cert to %s: %d: %s", 551 myname, dict_ldap->tls_cert, 552 rc, ldap_err2string(rc)); 553 return (-1); 554 } 555 } 556 if (*dict_ldap->tls_key) { 557 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, 558 dict_ldap->tls_key)) != LDAP_SUCCESS) { 559 msg_warn("%s: Unable to set tls_key to %s: %d: %s", 560 myname, dict_ldap->tls_key, 561 rc, ldap_err2string(rc)); 562 return (-1); 563 } 564 } 565 if (*dict_ldap->tls_cipher_suite) { 566 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CIPHER_SUITE, 567 dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS) { 568 msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s", 569 myname, dict_ldap->tls_cipher_suite, 570 rc, ldap_err2string(rc)); 571 return (-1); 572 } 573 } 574 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT, 575 &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS) { 576 msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s", 577 myname, dict_ldap->tls_require_cert, 578 rc, ldap_err2string(rc)); 579 return (-1); 580 } 581#ifdef LDAP_OPT_X_TLS_NEWCTX 582 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &am_server)) 583 != LDAP_SUCCESS) { 584 msg_warn("%s: Unable to allocate new TLS context %d: %s", 585 myname, rc, ldap_err2string(rc)); 586 return (-1); 587 } 588#endif 589 } 590 return (0); 591} 592 593#endif 594 595/* Establish a connection to the LDAP server. */ 596static int dict_ldap_connect(DICT_LDAP *dict_ldap) 597{ 598 const char *myname = "dict_ldap_connect"; 599 int rc = 0; 600 601#ifdef LDAP_OPT_NETWORK_TIMEOUT 602 struct timeval mytimeval; 603 604#endif 605 606#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT) 607 void (*saved_alarm) (int); 608 609#endif 610 611#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) 612 if (dict_ldap->debuglevel > 0 && 613 ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, 614 (LDAP_CONST void *) dict_ldap_logprint) != LBER_OPT_SUCCESS) 615 msg_warn("%s: Unable to set ber logprint function.", myname); 616#if defined(LBER_OPT_DEBUG_LEVEL) 617 if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, 618 &(dict_ldap->debuglevel)) != LBER_OPT_SUCCESS) 619 msg_warn("%s: Unable to set BER debug level.", myname); 620#endif 621 if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 622 &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS) 623 msg_warn("%s: Unable to set LDAP debug level.", myname); 624#endif 625 626 dict_ldap->dict.error = 0; 627 628 if (msg_verbose) 629 msg_info("%s: Connecting to server %s", myname, 630 dict_ldap->server_host); 631 632#ifdef LDAP_OPT_NETWORK_TIMEOUT 633#ifdef LDAP_API_FEATURE_X_OPENLDAP 634 ldap_initialize(&(dict_ldap->ld), dict_ldap->server_host); 635#else 636 dict_ldap->ld = ldap_init(dict_ldap->server_host, 637 (int) dict_ldap->server_port); 638#endif 639 if (dict_ldap->ld == NULL) { 640 msg_warn("%s: Unable to init LDAP server %s", 641 myname, dict_ldap->server_host); 642 dict_ldap->dict.error = DICT_ERR_RETRY; 643 return (-1); 644 } 645 mytimeval.tv_sec = dict_ldap->timeout; 646 mytimeval.tv_usec = 0; 647 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) != 648 LDAP_OPT_SUCCESS) { 649 msg_warn("%s: Unable to set network timeout.", myname); 650 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 651 } 652#else 653 if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) { 654 msg_warn("%s: Error setting signal handler for open timeout: %m", 655 myname); 656 dict_ldap->dict.error = DICT_ERR_RETRY; 657 return (-1); 658 } 659 alarm(dict_ldap->timeout); 660 if (setjmp(env) == 0) 661 dict_ldap->ld = ldap_open(dict_ldap->server_host, 662 (int) dict_ldap->server_port); 663 else 664 dict_ldap->ld = 0; 665 alarm(0); 666 667 if (signal(SIGALRM, saved_alarm) == SIG_ERR) { 668 msg_warn("%s: Error resetting signal handler after open: %m", 669 myname); 670 dict_ldap->dict.error = DICT_ERR_RETRY; 671 return (-1); 672 } 673 if (dict_ldap->ld == NULL) { 674 msg_warn("%s: Unable to connect to LDAP server %s", 675 myname, dict_ldap->server_host); 676 dict_ldap->dict.error = DICT_ERR_RETRY; 677 return (-1); 678 } 679#endif 680 681 /* 682 * v3 support is needed for referral chasing. Thanks to Sami Haahtinen 683 * for the patch. 684 */ 685#ifdef LDAP_OPT_PROTOCOL_VERSION 686 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_PROTOCOL_VERSION, 687 &dict_ldap->version) != LDAP_OPT_SUCCESS) { 688 msg_warn("%s: Unable to set LDAP protocol version", myname); 689 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 690 } 691 if (msg_verbose) { 692 if (ldap_get_option(dict_ldap->ld, 693 LDAP_OPT_PROTOCOL_VERSION, 694 &dict_ldap->version) != LDAP_OPT_SUCCESS) 695 msg_warn("%s: Unable to get LDAP protocol version", myname); 696 else 697 msg_info("%s: Actual Protocol version used is %d.", 698 myname, dict_ldap->version); 699 } 700#endif 701 702 /* 703 * Limit the number of entries returned by each query. 704 */ 705 if (dict_ldap->size_limit) { 706 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, 707 &dict_ldap->size_limit) != LDAP_OPT_SUCCESS) { 708 msg_warn("%s: %s: Unable to set query result size limit to %ld.", 709 myname, dict_ldap->parser->name, dict_ldap->size_limit); 710 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 711 } 712 } 713 714 /* 715 * Configure alias dereferencing for this connection. Thanks to Mike 716 * Mattice for this, and to Hery Rakotoarisoa for the v3 update. 717 */ 718 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF, 719 &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS) 720 msg_warn("%s: Unable to set dereference option.", myname); 721 722 /* Chase referrals. */ 723 724#ifdef LDAP_OPT_REFERRALS 725 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_REFERRALS, 726 dict_ldap->chase_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF) 727 != LDAP_OPT_SUCCESS) { 728 msg_warn("%s: Unable to set Referral chasing.", myname); 729 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 730 } 731#else 732 if (dict_ldap->chase_referrals) { 733 msg_warn("%s: Unable to set Referral chasing.", myname); 734 } 735#endif 736 737#ifdef LDAP_API_FEATURE_X_OPENLDAP 738 if (dict_ldap_set_tls_options(dict_ldap) != 0) 739 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 740 if (dict_ldap->start_tls) { 741 if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) { 742 msg_warn("%s: Error setting signal handler for STARTTLS timeout: %m", 743 myname); 744 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 745 } 746 alarm(dict_ldap->timeout); 747 if (setjmp(env) == 0) 748 rc = ldap_start_tls_s(dict_ldap->ld, NULL, NULL); 749 else { 750 rc = LDAP_TIMEOUT; 751 dict_ldap->ld = 0; /* Unknown state after 752 * longjmp() */ 753 } 754 alarm(0); 755 756 if (signal(SIGALRM, saved_alarm) == SIG_ERR) { 757 msg_warn("%s: Error resetting signal handler after STARTTLS: %m", 758 myname); 759 dict_ldap->dict.error = DICT_ERR_RETRY; 760 return (-1); 761 } 762 if (rc != LDAP_SUCCESS) { 763 msg_error("%s: Unable to set STARTTLS: %d: %s", myname, 764 rc, ldap_err2string(rc)); 765 dict_ldap->dict.error = DICT_ERR_RETRY; 766 return (-1); 767 } 768 } 769#endif 770 771#define DN_LOG_VAL(dict_ldap) \ 772 ((dict_ldap)->bind_dn[0] ? (dict_ldap)->bind_dn : "empty or implicit") 773 774 /* 775 * If this server requires a bind, do so. Thanks to Sam Tardieu for 776 * noticing that the original bind call was broken. 777 */ 778 if (DICT_LDAP_DO_BIND(dict_ldap)) { 779 if (msg_verbose) 780 msg_info("%s: Binding to server %s with dn %s", 781 myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap)); 782 783#if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP) 784 if (DICT_LDAP_DO_SASL(dict_ldap)) { 785 rc = dict_ldap_bind_sasl(dict_ldap); 786 } else { 787 rc = dict_ldap_bind_st(dict_ldap); 788 } 789#else 790 rc = dict_ldap_bind_st(dict_ldap); 791#endif 792 793 if (rc != LDAP_SUCCESS) { 794 msg_warn("%s: Unable to bind to server %s with dn %s: %d (%s)", 795 myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap), 796 rc, ldap_err2string(rc)); 797 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1); 798 } 799 if (msg_verbose) 800 msg_info("%s: Successful bind to server %s with dn %s", 801 myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap)); 802 } 803 /* Save connection handle in shared container */ 804 DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld; 805 806 if (msg_verbose) 807 msg_info("%s: Cached connection handle for LDAP source %s", 808 myname, dict_ldap->parser->name); 809 810 return (0); 811} 812 813/* 814 * Locate or allocate connection cache entry. 815 */ 816static void dict_ldap_conn_find(DICT_LDAP *dict_ldap) 817{ 818 VSTRING *keybuf = vstring_alloc(10); 819 char *key; 820 int len; 821 822#ifdef LDAP_API_FEATURE_X_OPENLDAP 823 int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl; 824 825#endif 826 LDAP_CONN *conn; 827 828 /* 829 * Join key fields with null characters. 830 */ 831#define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1) 832#define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu%c", (unsigned long)(i), 0) 833 834 ADDSTR(keybuf, dict_ldap->server_host); 835 ADDINT(keybuf, dict_ldap->server_port); 836 ADDINT(keybuf, dict_ldap->bind); 837 ADDSTR(keybuf, DICT_LDAP_DO_BIND(dict_ldap) ? dict_ldap->bind_dn : ""); 838 ADDSTR(keybuf, DICT_LDAP_DO_BIND(dict_ldap) ? dict_ldap->bind_pw : ""); 839 ADDINT(keybuf, dict_ldap->dereference); 840 ADDINT(keybuf, dict_ldap->chase_referrals); 841 ADDINT(keybuf, dict_ldap->debuglevel); 842 ADDINT(keybuf, dict_ldap->version); 843#ifdef LDAP_API_FEATURE_X_OPENLDAP 844#if defined(USE_LDAP_SASL) 845 ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_mechs : ""); 846 ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_realm : ""); 847 ADDSTR(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_authz : ""); 848 ADDINT(keybuf, DICT_LDAP_DO_SASL(dict_ldap) ? dict_ldap->sasl_minssf : 0); 849#endif 850 ADDINT(keybuf, dict_ldap->ldap_ssl); 851 ADDINT(keybuf, dict_ldap->start_tls); 852 ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0); 853 ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : ""); 854 ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : ""); 855 ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : ""); 856 ADDSTR(keybuf, sslon ? dict_ldap->tls_key : ""); 857 ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : ""); 858 ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : ""); 859#endif 860 861 key = vstring_str(keybuf); 862 len = VSTRING_LEN(keybuf); 863 864 if (conn_hash == 0) 865 conn_hash = binhash_create(0); 866 867 if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) { 868 conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN)); 869 conn->conn_ld = 0; 870 conn->conn_refcount = 0; 871 dict_ldap->ht = binhash_enter(conn_hash, key, len, (void *) conn); 872 } 873 ++DICT_LDAP_CONN(dict_ldap)->conn_refcount; 874 875 vstring_free(keybuf); 876} 877 878/* attr_sub_type - Is one of two attributes a sub-type of another */ 879 880static int attrdesc_subtype(const char *a1, const char *a2) 881{ 882 883 /* 884 * RFC 2251 section 4.1.4: LDAP attribute names are case insensitive 885 */ 886 while (*a1 && TOLOWER(*a1) == TOLOWER(*a2)) 887 ++a1, ++a2; 888 889 /* 890 * Names equal to end of a1, is a2 equal or a subtype? 891 */ 892 if (*a1 == 0 && (*a2 == 0 || *a2 == ';')) 893 return (1); 894 895 /* 896 * Names equal to end of a2, is a1 a subtype? 897 */ 898 if (*a2 == 0 && *a1 == ';') 899 return (-1); 900 901 /* 902 * Distinct attributes 903 */ 904 return (0); 905} 906 907/* url_attrs - attributes we want from LDAP URL */ 908 909static char **url_attrs(DICT_LDAP *dict_ldap, LDAPURLDesc * url) 910{ 911 static ARGV *attrs; 912 char **a1; 913 char **a2; 914 int arel; 915 916 /* 917 * If the LDAP URI specified no attributes, all entry attributes are 918 * returned, leading to unnecessarily large LDAP results, particularly 919 * since dynamic groups are most useful for large groups. 920 * 921 * Since we only make use of the various mumble_results attributes, we ask 922 * only for these, thus making large queries much faster. 923 * 924 * In one test case, a query returning 75K users took 16 minutes when all 925 * attributes are returned, and just under 3 minutes with only the 926 * desired result attribute. 927 */ 928 if (url->lud_attrs == 0 || *url->lud_attrs == 0) 929 return (dict_ldap->result_attributes->argv); 930 931 /* 932 * When the LDAP URI explicitly specifies a set of attributes, we use the 933 * interaction of the URI attributes and our result attributes. This way 934 * LDAP URIs can hide certain attributes that should not be part of the 935 * query. There is no point in retrieving attributes not listed in our 936 * result set, we won't make any use of those. 937 */ 938 if (attrs) 939 argv_truncate(attrs, 0); 940 else 941 attrs = argv_alloc(2); 942 943 /* 944 * Retrieve only those attributes that are of interest to us. 945 * 946 * If the URL attribute and the attribute we want differ only in the 947 * "options" part of the attribute descriptor, select the more specific 948 * attribute descriptor. 949 */ 950 for (a1 = url->lud_attrs; *a1; ++a1) { 951 for (a2 = dict_ldap->result_attributes->argv; *a2; ++a2) { 952 arel = attrdesc_subtype(*a1, *a2); 953 if (arel > 0) 954 argv_add(attrs, *a2, ARGV_END); 955 else if (arel < 0) 956 argv_add(attrs, *a1, ARGV_END); 957 } 958 } 959 960 return ((attrs->argc > 0) ? attrs->argv : 0); 961} 962 963/* 964 * dict_ldap_get_values: for each entry returned by a search, get the values 965 * of all its attributes. Recurses to resolve any DN or URL values found. 966 * 967 * This and the rest of the handling of multiple attributes, DNs and URLs 968 * are thanks to LaMont Jones. 969 */ 970static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage *res, 971 VSTRING *result, const char *name) 972{ 973 static int recursion = 0; 974 static int expansion; 975 long entries = 0; 976 long i = 0; 977 int rc = 0; 978 LDAPMessage *resloop = 0; 979 LDAPMessage *entry = 0; 980 BerElement *ber; 981 char *attr; 982 char **attrs; 983 struct berval **vals; 984 int valcount; 985 LDAPURLDesc *url; 986 const char *myname = "dict_ldap_get_values"; 987 int is_leaf = 1; /* No recursion via this entry */ 988 int is_terminal = 0; /* No expansion via this entry */ 989 990 if (++recursion == 1) 991 expansion = 0; 992 993 if (msg_verbose) 994 msg_info("%s[%d]: Search found %d match(es)", myname, recursion, 995 ldap_count_entries(dict_ldap->ld, res)); 996 997 for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL; 998 entry = ldap_next_entry(dict_ldap->ld, entry)) { 999 ber = NULL; 1000 1001 /* 1002 * LDAP should not, but may produce more than the requested maximum 1003 * number of entries. 1004 */ 1005 if (dict_ldap->dict.error == 0 1006 && dict_ldap->size_limit 1007 && ++entries > dict_ldap->size_limit) { 1008 msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded", 1009 myname, recursion, dict_ldap->parser->name, 1010 dict_ldap->size_limit); 1011 dict_ldap->dict.error = DICT_ERR_RETRY; 1012 } 1013 1014 /* 1015 * Check for terminal attributes, these preclude expansion of all 1016 * other attributes, and DN/URI recursion. Any terminal attributes 1017 * are listed first in the attribute array. 1018 */ 1019 if (dict_ldap->num_terminal > 0) { 1020 for (i = 0; i < dict_ldap->num_terminal; ++i) { 1021 attr = dict_ldap->result_attributes->argv[i]; 1022 if (!(vals = ldap_get_values_len(dict_ldap->ld, entry, attr))) 1023 continue; 1024 is_terminal = (ldap_count_values_len(vals) > 0); 1025 ldap_value_free_len(vals); 1026 if (is_terminal) 1027 break; 1028 } 1029 } 1030 1031 /* 1032 * Check for special attributes, these preclude expansion of 1033 * "leaf-only" attributes, and are at the end of the attribute array 1034 * after the terminal, leaf and regular attributes. 1035 */ 1036 if (is_terminal == 0 && dict_ldap->num_leaf > 0) { 1037 for (i = dict_ldap->num_attributes; 1038 dict_ldap->result_attributes->argv[i]; ++i) { 1039 attr = dict_ldap->result_attributes->argv[i]; 1040 if (!(vals = ldap_get_values_len(dict_ldap->ld, entry, attr))) 1041 continue; 1042 is_leaf = (ldap_count_values_len(vals) == 0); 1043 ldap_value_free_len(vals); 1044 if (!is_leaf) 1045 break; 1046 } 1047 } 1048 for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber); 1049 attr != NULL; ldap_memfree(attr), 1050 attr = ldap_next_attribute(dict_ldap->ld, entry, ber)) { 1051 1052 vals = ldap_get_values_len(dict_ldap->ld, entry, attr); 1053 if (vals == NULL) { 1054 if (msg_verbose) 1055 msg_info("%s[%d]: Entry doesn't have any values for %s", 1056 myname, recursion, attr); 1057 continue; 1058 } 1059 valcount = ldap_count_values_len(vals); 1060 1061 /* 1062 * If we previously encountered an error, we still continue 1063 * through the loop, to avoid memory leaks, but we don't waste 1064 * time accumulating any further results. 1065 * 1066 * XXX: There may be a more efficient way to exit the loop with no 1067 * leaks, but it will likely be more fragile and not worth the 1068 * extra code. 1069 */ 1070 if (dict_ldap->dict.error != 0 || valcount == 0) { 1071 ldap_value_free_len(vals); 1072 continue; 1073 } 1074 1075 /* 1076 * The "result_attributes" list enumerates all the requested 1077 * attributes, first the ordinary result attributes and then the 1078 * special result attributes that hold DN or LDAP URL values. 1079 * 1080 * The number of ordinary attributes is "num_attributes". 1081 * 1082 * We compute the attribute type (ordinary or special) from its 1083 * index on the "result_attributes" list. 1084 */ 1085 for (i = 0; dict_ldap->result_attributes->argv[i]; i++) 1086 if (attrdesc_subtype(dict_ldap->result_attributes->argv[i], 1087 attr) > 0) 1088 break; 1089 1090 /* 1091 * Append each returned address to the result list, possibly 1092 * recursing (for dn or url attributes of non-terminal entries) 1093 */ 1094 if (i < dict_ldap->num_attributes || is_terminal) { 1095 if ((is_terminal && i >= dict_ldap->num_terminal) 1096 || (!is_leaf && 1097 i < dict_ldap->num_terminal + dict_ldap->num_leaf)) { 1098 if (msg_verbose) 1099 msg_info("%s[%d]: skipping %d value(s) of %s " 1100 "attribute %s", myname, recursion, valcount, 1101 is_terminal ? "non-terminal" : "leaf-only", 1102 attr); 1103 } else { 1104 /* Ordinary result attribute */ 1105 for (i = 0; i < valcount; i++) { 1106 if (db_common_expand(dict_ldap->ctx, 1107 dict_ldap->result_format, 1108 vals[i]->bv_val, 1109 name, result, 0) 1110 && dict_ldap->expansion_limit > 0 1111 && ++expansion > dict_ldap->expansion_limit) { 1112 msg_warn("%s[%d]: %s: Expansion limit exceeded " 1113 "for key: '%s'", myname, recursion, 1114 dict_ldap->parser->name, name); 1115 dict_ldap->dict.error = DICT_ERR_RETRY; 1116 break; 1117 } 1118 } 1119 if (dict_ldap->dict.error != 0) 1120 continue; 1121 if (msg_verbose) 1122 msg_info("%s[%d]: search returned %d value(s) for" 1123 " requested result attribute %s", 1124 myname, recursion, valcount, attr); 1125 } 1126 } else if (recursion < dict_ldap->recursion_limit 1127 && dict_ldap->result_attributes->argv[i]) { 1128 /* Special result attribute */ 1129 for (i = 0; i < valcount; i++) { 1130 if (ldap_is_ldap_url(vals[i]->bv_val)) { 1131 rc = ldap_url_parse(vals[i]->bv_val, &url); 1132 if (rc == 0) { 1133 if ((attrs = url_attrs(dict_ldap, url)) != 0) { 1134 if (msg_verbose) 1135 msg_info("%s[%d]: looking up URL %s", 1136 myname, recursion, 1137 vals[i]->bv_val); 1138 rc = search_st(dict_ldap->ld, url->lud_dn, 1139 url->lud_scope, 1140 url->lud_filter, 1141 attrs, dict_ldap->timeout, 1142 &resloop); 1143 } 1144 ldap_free_urldesc(url); 1145 if (attrs == 0) { 1146 if (msg_verbose) 1147 msg_info("%s[%d]: skipping URL %s: no " 1148 "pertinent attributes", myname, 1149 recursion, vals[i]->bv_val); 1150 continue; 1151 } 1152 } else { 1153 msg_warn("%s[%d]: malformed URL %s: %s(%d)", 1154 myname, recursion, vals[i]->bv_val, 1155 ldap_err2string(rc), rc); 1156 dict_ldap->dict.error = DICT_ERR_RETRY; 1157 break; 1158 } 1159 } else { 1160 if (msg_verbose) 1161 msg_info("%s[%d]: looking up DN %s", 1162 myname, recursion, vals[i]->bv_val); 1163 rc = search_st(dict_ldap->ld, vals[i]->bv_val, 1164 LDAP_SCOPE_BASE, "objectclass=*", 1165 dict_ldap->result_attributes->argv, 1166 dict_ldap->timeout, &resloop); 1167 } 1168 switch (rc) { 1169 case LDAP_SUCCESS: 1170 dict_ldap_get_values(dict_ldap, resloop, result, name); 1171 break; 1172 case LDAP_NO_SUCH_OBJECT: 1173 1174 /* 1175 * Go ahead and treat this as though the DN existed 1176 * and just didn't have any result attributes. 1177 */ 1178 msg_warn("%s[%d]: DN %s not found, skipping ", myname, 1179 recursion, vals[i]->bv_val); 1180 break; 1181 default: 1182 msg_warn("%s[%d]: search error %d: %s ", myname, 1183 recursion, rc, ldap_err2string(rc)); 1184 dict_ldap->dict.error = DICT_ERR_RETRY; 1185 break; 1186 } 1187 1188 if (resloop != 0) 1189 ldap_msgfree(resloop); 1190 1191 if (dict_ldap->dict.error != 0) 1192 break; 1193 } 1194 if (msg_verbose && dict_ldap->dict.error == 0) 1195 msg_info("%s[%d]: search returned %d value(s) for" 1196 " special result attribute %s", 1197 myname, recursion, valcount, attr); 1198 } else if (recursion >= dict_ldap->recursion_limit 1199 && dict_ldap->result_attributes->argv[i]) { 1200 msg_warn("%s[%d]: %s: Recursion limit exceeded" 1201 " for special attribute %s=%s", myname, recursion, 1202 dict_ldap->parser->name, attr, vals[0]->bv_val); 1203 dict_ldap->dict.error = DICT_ERR_RETRY; 1204 } 1205 ldap_value_free_len(vals); 1206 } 1207 if (ber) 1208 ber_free(ber, 0); 1209 } 1210 1211 if (msg_verbose) 1212 msg_info("%s[%d]: Leaving %s", myname, recursion, myname); 1213 --recursion; 1214} 1215 1216/* dict_ldap_lookup - find database entry */ 1217 1218static const char *dict_ldap_lookup(DICT *dict, const char *name) 1219{ 1220 const char *myname = "dict_ldap_lookup"; 1221 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict; 1222 LDAPMessage *res = 0; 1223 static VSTRING *base; 1224 static VSTRING *query; 1225 static VSTRING *result; 1226 int rc = 0; 1227 int sizelimit; 1228 int domain_rc; 1229 1230 dict_ldap->dict.error = 0; 1231 1232 if (msg_verbose) 1233 msg_info("%s: In dict_ldap_lookup", myname); 1234 1235 /* 1236 * Don't frustrate future attempts to make Postfix UTF-8 transparent. 1237 */ 1238 if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 1239 && !valid_utf8_string(name, strlen(name))) { 1240 if (msg_verbose) 1241 msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'", 1242 myname, dict_ldap->parser->name, name); 1243 return (0); 1244 } 1245 1246 /* 1247 * Optionally fold the key. 1248 */ 1249 if (dict->flags & DICT_FLAG_FOLD_FIX) { 1250 if (dict->fold_buf == 0) 1251 dict->fold_buf = vstring_alloc(10); 1252 vstring_strcpy(dict->fold_buf, name); 1253 name = lowercase(vstring_str(dict->fold_buf)); 1254 } 1255 1256 /* 1257 * If they specified a domain list for this map, then only search for 1258 * addresses in domains on the list. This can significantly reduce the 1259 * load on the LDAP server. 1260 */ 1261 if ((domain_rc = db_common_check_domain(dict_ldap->ctx, name)) == 0) { 1262 if (msg_verbose) 1263 msg_info("%s: %s: Skipping lookup of key '%s': domain mismatch", 1264 myname, dict_ldap->parser->name, name); 1265 return (0); 1266 } 1267 if (domain_rc < 0) 1268 DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0); 1269 1270#define INIT_VSTR(buf, len) do { \ 1271 if (buf == 0) \ 1272 buf = vstring_alloc(len); \ 1273 VSTRING_RESET(buf); \ 1274 VSTRING_TERMINATE(buf); \ 1275 } while (0) 1276 1277 INIT_VSTR(base, 10); 1278 INIT_VSTR(query, 10); 1279 INIT_VSTR(result, 10); 1280 1281 /* 1282 * Because the connection may be shared and invalidated via queries for 1283 * another map, update private copy of "ld" from shared connection 1284 * container. 1285 */ 1286 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld; 1287 1288 /* 1289 * Connect to the LDAP server, if necessary. 1290 */ 1291 if (dict_ldap->ld == NULL) { 1292 if (msg_verbose) 1293 msg_info 1294 ("%s: No existing connection for LDAP source %s, reopening", 1295 myname, dict_ldap->parser->name); 1296 1297 dict_ldap_connect(dict_ldap); 1298 1299 /* 1300 * if dict_ldap_connect() set dict_ldap->dict.error, abort. 1301 */ 1302 if (dict_ldap->dict.error) 1303 return (0); 1304 } else if (msg_verbose) 1305 msg_info("%s: Using existing connection for LDAP source %s", 1306 myname, dict_ldap->parser->name); 1307 1308 /* 1309 * Connection caching, means that the connection handle may have the 1310 * wrong size limit. Re-adjust before each query. This is cheap, just 1311 * sets a field in the ldap connection handle. We also do this in the 1312 * connect code, because we sometimes reconnect (below) in the middle of 1313 * a query. 1314 */ 1315 sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT; 1316 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit) 1317 != LDAP_OPT_SUCCESS) { 1318 msg_warn("%s: %s: Unable to set query result size limit to %ld.", 1319 myname, dict_ldap->parser->name, dict_ldap->size_limit); 1320 dict_ldap->dict.error = DICT_ERR_RETRY; 1321 return (0); 1322 } 1323 1324 /* 1325 * Expand the search base and query. Skip lookup when the input key lacks 1326 * sufficient domain components to satisfy all the requested 1327 * %-substitutions. 1328 * 1329 * When the search base is not static, LDAP_NO_SUCH_OBJECT is expected and 1330 * is therefore treated as a non-error: the lookup returns no results 1331 * rather than a soft error. 1332 */ 1333 if (!db_common_expand(dict_ldap->ctx, dict_ldap->search_base, 1334 name, 0, base, rfc2253_quote)) { 1335 if (msg_verbose > 1) 1336 msg_info("%s: %s: Empty expansion for %s", myname, 1337 dict_ldap->parser->name, dict_ldap->search_base); 1338 return (0); 1339 } 1340 if (!db_common_expand(dict_ldap->ctx, dict_ldap->query, 1341 name, 0, query, rfc2254_quote)) { 1342 if (msg_verbose > 1) 1343 msg_info("%s: %s: Empty expansion for %s", myname, 1344 dict_ldap->parser->name, dict_ldap->query); 1345 return (0); 1346 } 1347 1348 /* 1349 * On to the search. 1350 */ 1351 if (msg_verbose) 1352 msg_info("%s: %s: Searching with filter %s", myname, 1353 dict_ldap->parser->name, vstring_str(query)); 1354 1355 rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope, 1356 vstring_str(query), dict_ldap->result_attributes->argv, 1357 dict_ldap->timeout, &res); 1358 1359 if (rc == LDAP_SERVER_DOWN) { 1360 if (msg_verbose) 1361 msg_info("%s: Lost connection for LDAP source %s, reopening", 1362 myname, dict_ldap->parser->name); 1363 1364 dict_ldap_unbind(dict_ldap->ld); 1365 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0; 1366 dict_ldap_connect(dict_ldap); 1367 1368 /* 1369 * if dict_ldap_connect() set dict_ldap->dict.error, abort. 1370 */ 1371 if (dict_ldap->dict.error) 1372 return (0); 1373 1374 rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope, 1375 vstring_str(query), dict_ldap->result_attributes->argv, 1376 dict_ldap->timeout, &res); 1377 1378 } 1379 switch (rc) { 1380 1381 case LDAP_SUCCESS: 1382 1383 /* 1384 * Search worked; extract the requested result_attribute. 1385 */ 1386 1387 dict_ldap_get_values(dict_ldap, res, result, name); 1388 1389 /* 1390 * OpenLDAP's ldap_next_attribute returns a bogus 1391 * LDAP_DECODING_ERROR; I'm ignoring that for now. 1392 */ 1393 1394 rc = dict_ldap_get_errno(dict_ldap->ld); 1395 if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR) 1396 msg_warn 1397 ("%s: Had some trouble with entries returned by search: %s", 1398 myname, ldap_err2string(rc)); 1399 1400 if (msg_verbose) 1401 msg_info("%s: Search returned %s", myname, 1402 VSTRING_LEN(result) > 1403 0 ? vstring_str(result) : "nothing"); 1404 break; 1405 1406 case LDAP_NO_SUCH_OBJECT: 1407 1408 /* 1409 * If the search base is input key dependent, then not finding it, is 1410 * equivalent to not finding the input key. Sadly, we cannot detect 1411 * misconfiguration in this case. 1412 */ 1413 if (dict_ldap->dynamic_base) 1414 break; 1415 1416 msg_warn("%s: %s: Search base '%s' not found: %d: %s", 1417 myname, dict_ldap->parser->name, 1418 vstring_str(base), rc, ldap_err2string(rc)); 1419 dict_ldap->dict.error = DICT_ERR_RETRY; 1420 break; 1421 1422 default: 1423 1424 /* 1425 * Rats. The search didn't work. 1426 */ 1427 msg_warn("%s: Search error %d: %s ", myname, rc, 1428 ldap_err2string(rc)); 1429 1430 /* 1431 * Tear down the connection so it gets set up from scratch on the 1432 * next lookup. 1433 */ 1434 dict_ldap_unbind(dict_ldap->ld); 1435 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0; 1436 1437 /* 1438 * And tell the caller to try again later. 1439 */ 1440 dict_ldap->dict.error = DICT_ERR_RETRY; 1441 break; 1442 } 1443 1444 /* 1445 * Cleanup. 1446 */ 1447 if (res != 0) 1448 ldap_msgfree(res); 1449 1450 /* 1451 * If we had an error, return nothing, Otherwise, return the result, if 1452 * any. 1453 */ 1454 return (VSTRING_LEN(result) > 0 && !dict_ldap->dict.error ? vstring_str(result) : 0); 1455} 1456 1457/* dict_ldap_close - disassociate from data base */ 1458 1459static void dict_ldap_close(DICT *dict) 1460{ 1461 const char *myname = "dict_ldap_close"; 1462 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict; 1463 LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap); 1464 BINHASH_INFO *ht = dict_ldap->ht; 1465 1466 if (--conn->conn_refcount == 0) { 1467 if (conn->conn_ld) { 1468 if (msg_verbose) 1469 msg_info("%s: Closed connection handle for LDAP source %s", 1470 myname, dict_ldap->parser->name); 1471 dict_ldap_unbind(conn->conn_ld); 1472 } 1473 binhash_delete(conn_hash, ht->key, ht->key_len, myfree); 1474 } 1475 cfg_parser_free(dict_ldap->parser); 1476 myfree(dict_ldap->server_host); 1477 myfree(dict_ldap->search_base); 1478 myfree(dict_ldap->query); 1479 if (dict_ldap->result_format) 1480 myfree(dict_ldap->result_format); 1481 argv_free(dict_ldap->result_attributes); 1482 myfree(dict_ldap->bind_dn); 1483 myfree(dict_ldap->bind_pw); 1484 if (dict_ldap->ctx) 1485 db_common_free_ctx(dict_ldap->ctx); 1486#ifdef LDAP_API_FEATURE_X_OPENLDAP 1487#if defined(USE_LDAP_SASL) 1488 if (DICT_LDAP_DO_SASL(dict_ldap)) { 1489 myfree(dict_ldap->sasl_mechs); 1490 myfree(dict_ldap->sasl_realm); 1491 myfree(dict_ldap->sasl_authz); 1492 } 1493#endif 1494 myfree(dict_ldap->tls_ca_cert_file); 1495 myfree(dict_ldap->tls_ca_cert_dir); 1496 myfree(dict_ldap->tls_cert); 1497 myfree(dict_ldap->tls_key); 1498 myfree(dict_ldap->tls_random_file); 1499 myfree(dict_ldap->tls_cipher_suite); 1500#endif 1501 if (dict->fold_buf) 1502 vstring_free(dict->fold_buf); 1503 dict_free(dict); 1504} 1505 1506/* dict_ldap_open - create association with data base */ 1507 1508DICT *dict_ldap_open(const char *ldapsource, int open_flags, int dict_flags) 1509{ 1510 const char *myname = "dict_ldap_open"; 1511 DICT_LDAP *dict_ldap; 1512 VSTRING *url_list; 1513 char *s; 1514 char *h; 1515 char *server_host; 1516 char *scope; 1517 char *attr; 1518 char *bindopt; 1519 int tmp; 1520 int vendor_version = dict_ldap_vendor_version(); 1521 CFG_PARSER *parser; 1522 1523 if (msg_verbose) 1524 msg_info("%s: Using LDAP source %s", myname, ldapsource); 1525 1526 /* 1527 * Sanity check. 1528 */ 1529 if (open_flags != O_RDONLY) 1530 return (dict_surrogate(DICT_TYPE_LDAP, ldapsource, open_flags, dict_flags, 1531 "%s:%s map requires O_RDONLY access mode", 1532 DICT_TYPE_LDAP, ldapsource)); 1533 1534 /* 1535 * Open the configuration file. 1536 */ 1537 if ((parser = cfg_parser_alloc(ldapsource)) == 0) 1538 return (dict_surrogate(DICT_TYPE_LDAP, ldapsource, open_flags, dict_flags, 1539 "open %s: %m", ldapsource)); 1540 1541 dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource, 1542 sizeof(*dict_ldap)); 1543 dict_ldap->dict.lookup = dict_ldap_lookup; 1544 dict_ldap->dict.close = dict_ldap_close; 1545 dict_ldap->dict.flags = dict_flags; 1546 1547 dict_ldap->ld = NULL; 1548 dict_ldap->parser = parser; 1549 1550 server_host = cfg_get_str(dict_ldap->parser, "server_host", 1551 "localhost", 1, 0); 1552 1553 /* 1554 * get configured value of "server_port"; default to LDAP_PORT (389) 1555 */ 1556 dict_ldap->server_port = 1557 cfg_get_int(dict_ldap->parser, "server_port", LDAP_PORT, 0, 0); 1558 1559 /* 1560 * Define LDAP Protocol Version. 1561 */ 1562 dict_ldap->version = cfg_get_int(dict_ldap->parser, "version", 2, 2, 0); 1563 switch (dict_ldap->version) { 1564 case 2: 1565 dict_ldap->version = LDAP_VERSION2; 1566 break; 1567 case 3: 1568 dict_ldap->version = LDAP_VERSION3; 1569 break; 1570 default: 1571 msg_warn("%s: %s Unknown version %d, using 2.", myname, ldapsource, 1572 dict_ldap->version); 1573 dict_ldap->version = LDAP_VERSION2; 1574 } 1575 1576#if defined(LDAP_API_FEATURE_X_OPENLDAP) 1577 dict_ldap->ldap_ssl = 0; 1578#endif 1579 1580 url_list = vstring_alloc(32); 1581 s = server_host; 1582 while ((h = mystrtok(&s, CHARS_COMMA_SP)) != NULL) { 1583#if defined(LDAP_API_FEATURE_X_OPENLDAP) 1584 1585 /* 1586 * Convert (host, port) pairs to LDAP URLs 1587 */ 1588 if (ldap_is_ldap_url(h)) { 1589 LDAPURLDesc *url_desc; 1590 int rc; 1591 1592 if ((rc = ldap_url_parse(h, &url_desc)) != 0) { 1593 msg_error("%s: error parsing URL %s: %d: %s; skipping", myname, 1594 h, rc, ldap_err2string(rc)); 1595 continue; 1596 } 1597 if (strcasecmp(url_desc->lud_scheme, "ldap") != 0 && 1598 dict_ldap->version != LDAP_VERSION3) { 1599 msg_warn("%s: URL scheme %s requires protocol version 3", myname, 1600 url_desc->lud_scheme); 1601 dict_ldap->version = LDAP_VERSION3; 1602 } 1603 if (strcasecmp(url_desc->lud_scheme, "ldaps") == 0) 1604 dict_ldap->ldap_ssl = 1; 1605 ldap_free_urldesc(url_desc); 1606 if (VSTRING_LEN(url_list) > 0) 1607 VSTRING_ADDCH(url_list, ' '); 1608 vstring_strcat(url_list, h); 1609 } else { 1610 if (VSTRING_LEN(url_list) > 0) 1611 VSTRING_ADDCH(url_list, ' '); 1612 if (strrchr(h, ':')) 1613 vstring_sprintf_append(url_list, "ldap://%s", h); 1614 else 1615 vstring_sprintf_append(url_list, "ldap://%s:%d", h, 1616 dict_ldap->server_port); 1617 } 1618#else 1619 if (VSTRING_LEN(url_list) > 0) 1620 VSTRING_ADDCH(url_list, ' '); 1621 vstring_strcat(url_list, h); 1622#endif 1623 } 1624 VSTRING_TERMINATE(url_list); 1625 dict_ldap->server_host = vstring_export(url_list); 1626 1627#if defined(LDAP_API_FEATURE_X_OPENLDAP) 1628 1629 /* 1630 * With URL scheme, clear port to normalize connection cache key 1631 */ 1632 dict_ldap->server_port = LDAP_PORT; 1633 if (msg_verbose) 1634 msg_info("%s: %s server_host URL is %s", myname, ldapsource, 1635 dict_ldap->server_host); 1636#endif 1637 myfree(server_host); 1638 1639 /* 1640 * Scope handling thanks to Carsten Hoeger of SuSE. 1641 */ 1642 scope = cfg_get_str(dict_ldap->parser, "scope", "sub", 1, 0); 1643 1644 if (strcasecmp(scope, "one") == 0) { 1645 dict_ldap->scope = LDAP_SCOPE_ONELEVEL; 1646 } else if (strcasecmp(scope, "base") == 0) { 1647 dict_ldap->scope = LDAP_SCOPE_BASE; 1648 } else if (strcasecmp(scope, "sub") == 0) { 1649 dict_ldap->scope = LDAP_SCOPE_SUBTREE; 1650 } else { 1651 msg_warn("%s: %s: Unrecognized value %s specified for scope; using sub", 1652 myname, ldapsource, scope); 1653 dict_ldap->scope = LDAP_SCOPE_SUBTREE; 1654 } 1655 1656 myfree(scope); 1657 1658 dict_ldap->search_base = cfg_get_str(dict_ldap->parser, "search_base", 1659 "", 0, 0); 1660 1661 /* 1662 * get configured value of "timeout"; default to 10 seconds 1663 * 1664 * Thanks to Manuel Guesdon for spotting that this wasn't really getting 1665 * set. 1666 */ 1667 dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout", 10, 0, 0); 1668 dict_ldap->query = 1669 cfg_get_str(dict_ldap->parser, "query_filter", 1670 "(mailacceptinggeneralid=%s)", 0, 0); 1671 if ((dict_ldap->result_format = 1672 cfg_get_str(dict_ldap->parser, "result_format", 0, 0, 0)) == 0) 1673 dict_ldap->result_format = 1674 cfg_get_str(dict_ldap->parser, "result_filter", "%s", 1, 0); 1675 1676 /* 1677 * Must parse all templates before we can use db_common_expand() If data 1678 * dependent substitutions are found in the search base, treat 1679 * NO_SUCH_OBJECT search errors as a non-matching key, rather than a 1680 * fatal run-time error. 1681 */ 1682 dict_ldap->ctx = 0; 1683 dict_ldap->dynamic_base = 1684 db_common_parse(&dict_ldap->dict, &dict_ldap->ctx, 1685 dict_ldap->search_base, 1); 1686 if (!db_common_parse(0, &dict_ldap->ctx, dict_ldap->query, 1)) { 1687 msg_warn("%s: %s: Fixed query_filter %s is probably useless", 1688 myname, ldapsource, dict_ldap->query); 1689 } 1690 (void) db_common_parse(0, &dict_ldap->ctx, dict_ldap->result_format, 0); 1691 db_common_parse_domain(dict_ldap->parser, dict_ldap->ctx); 1692 1693 /* 1694 * Maps that use substring keys should only be used with the full input 1695 * key. 1696 */ 1697 if (db_common_dict_partial(dict_ldap->ctx)) 1698 dict_ldap->dict.flags |= DICT_FLAG_PATTERN; 1699 else 1700 dict_ldap->dict.flags |= DICT_FLAG_FIXED; 1701 if (dict_flags & DICT_FLAG_FOLD_FIX) 1702 dict_ldap->dict.fold_buf = vstring_alloc(10); 1703 1704 /* Order matters, first the terminal attributes: */ 1705 attr = cfg_get_str(dict_ldap->parser, "terminal_result_attribute", "", 0, 0); 1706 dict_ldap->result_attributes = argv_split(attr, CHARS_COMMA_SP); 1707 dict_ldap->num_terminal = dict_ldap->result_attributes->argc; 1708 myfree(attr); 1709 1710 /* Order matters, next the leaf-only attributes: */ 1711 attr = cfg_get_str(dict_ldap->parser, "leaf_result_attribute", "", 0, 0); 1712 if (*attr) 1713 argv_split_append(dict_ldap->result_attributes, attr, CHARS_COMMA_SP); 1714 dict_ldap->num_leaf = 1715 dict_ldap->result_attributes->argc - dict_ldap->num_terminal; 1716 myfree(attr); 1717 1718 /* Order matters, next the regular attributes: */ 1719 attr = cfg_get_str(dict_ldap->parser, "result_attribute", "maildrop", 0, 0); 1720 if (*attr) 1721 argv_split_append(dict_ldap->result_attributes, attr, CHARS_COMMA_SP); 1722 dict_ldap->num_attributes = dict_ldap->result_attributes->argc; 1723 myfree(attr); 1724 1725 /* Order matters, finally the special attributes: */ 1726 attr = cfg_get_str(dict_ldap->parser, "special_result_attribute", "", 0, 0); 1727 if (*attr) 1728 argv_split_append(dict_ldap->result_attributes, attr, CHARS_COMMA_SP); 1729 myfree(attr); 1730 1731 /* 1732 * get configured value of "bind"; default to simple bind 1733 */ 1734 bindopt = cfg_get_str(dict_ldap->parser, "bind", CONFIG_BOOL_YES, 1, 0); 1735 dict_ldap->bind = name_code(bindopt_table, NAME_CODE_FLAG_NONE, bindopt); 1736 if (dict_ldap->bind < 0) 1737 msg_fatal("%s: unsupported parameter value: %s = %s", 1738 dict_ldap->parser->name, "bind", bindopt); 1739 myfree(bindopt); 1740 1741 /* 1742 * get configured value of "bind_dn"; default to "" 1743 */ 1744 dict_ldap->bind_dn = cfg_get_str(dict_ldap->parser, "bind_dn", "", 0, 0); 1745 1746 /* 1747 * get configured value of "bind_pw"; default to "" 1748 */ 1749 dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw", "", 0, 0); 1750 1751 /* 1752 * LDAP message caching never worked and is no longer supported. 1753 */ 1754 tmp = cfg_get_bool(dict_ldap->parser, "cache", 0); 1755 if (tmp) 1756 msg_warn("%s: %s ignoring cache", myname, ldapsource); 1757 1758 tmp = cfg_get_int(dict_ldap->parser, "cache_expiry", -1, 0, 0); 1759 if (tmp >= 0) 1760 msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource); 1761 1762 tmp = cfg_get_int(dict_ldap->parser, "cache_size", -1, 0, 0); 1763 if (tmp >= 0) 1764 msg_warn("%s: %s ignoring cache_size", myname, ldapsource); 1765 1766 dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser, 1767 "recursion_limit", 1000, 1, 0); 1768 1769 /* 1770 * XXX: The default should be non-zero for safety, but that is not 1771 * backwards compatible. 1772 */ 1773 dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser, 1774 "expansion_limit", 0, 0, 0); 1775 1776 dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit", 1777 dict_ldap->expansion_limit, 0, 0); 1778 1779 /* 1780 * Alias dereferencing suggested by Mike Mattice. 1781 */ 1782 dict_ldap->dereference = cfg_get_int(dict_ldap->parser, "dereference", 1783 0, 0, 0); 1784 if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) { 1785 msg_warn("%s: %s Unrecognized value %d specified for dereference; using 0", 1786 myname, ldapsource, dict_ldap->dereference); 1787 dict_ldap->dereference = 0; 1788 } 1789 /* Referral chasing */ 1790 dict_ldap->chase_referrals = cfg_get_bool(dict_ldap->parser, 1791 "chase_referrals", 0); 1792 1793#ifdef LDAP_API_FEATURE_X_OPENLDAP 1794#if defined(USE_LDAP_SASL) 1795 1796 /* 1797 * SASL options 1798 */ 1799 if (DICT_LDAP_DO_SASL(dict_ldap)) { 1800 dict_ldap->sasl_mechs = 1801 cfg_get_str(dict_ldap->parser, "sasl_mechs", "", 0, 0); 1802 dict_ldap->sasl_realm = 1803 cfg_get_str(dict_ldap->parser, "sasl_realm", "", 0, 0); 1804 dict_ldap->sasl_authz = 1805 cfg_get_str(dict_ldap->parser, "sasl_authz_id", "", 0, 0); 1806 dict_ldap->sasl_minssf = 1807 cfg_get_int(dict_ldap->parser, "sasl_minssf", 0, 0, 4096); 1808 } else { 1809 dict_ldap->sasl_mechs = 0; 1810 dict_ldap->sasl_realm = 0; 1811 dict_ldap->sasl_authz = 0; 1812 } 1813#endif 1814 1815 /* 1816 * TLS options 1817 */ 1818 /* get configured value of "start_tls"; default to no */ 1819 dict_ldap->start_tls = cfg_get_bool(dict_ldap->parser, "start_tls", 0); 1820 if (dict_ldap->start_tls) { 1821 if (dict_ldap->version < LDAP_VERSION3) { 1822 msg_warn("%s: %s start_tls requires protocol version 3", 1823 myname, ldapsource); 1824 dict_ldap->version = LDAP_VERSION3; 1825 } 1826 /* Binary incompatibility in the OpenLDAP API from 2.0.11 to 2.0.12 */ 1827 if (((LDAP_VENDOR_VERSION <= 20011) && !(vendor_version <= 20011)) 1828 || (!(LDAP_VENDOR_VERSION <= 20011) && (vendor_version <= 20011))) 1829 msg_fatal("%s: incompatible TLS support: " 1830 "compile-time OpenLDAP version %d, " 1831 "run-time OpenLDAP version %d", 1832 myname, LDAP_VENDOR_VERSION, vendor_version); 1833 } 1834 /* get configured value of "tls_require_cert"; default to no */ 1835 dict_ldap->tls_require_cert = 1836 cfg_get_bool(dict_ldap->parser, "tls_require_cert", 0) ? 1837 LDAP_OPT_X_TLS_DEMAND : LDAP_OPT_X_TLS_NEVER; 1838 1839 /* get configured value of "tls_ca_cert_file"; default "" */ 1840 dict_ldap->tls_ca_cert_file = cfg_get_str(dict_ldap->parser, 1841 "tls_ca_cert_file", "", 0, 0); 1842 1843 /* get configured value of "tls_ca_cert_dir"; default "" */ 1844 dict_ldap->tls_ca_cert_dir = cfg_get_str(dict_ldap->parser, 1845 "tls_ca_cert_dir", "", 0, 0); 1846 1847 /* get configured value of "tls_cert"; default "" */ 1848 dict_ldap->tls_cert = cfg_get_str(dict_ldap->parser, "tls_cert", 1849 "", 0, 0); 1850 1851 /* get configured value of "tls_key"; default "" */ 1852 dict_ldap->tls_key = cfg_get_str(dict_ldap->parser, "tls_key", 1853 "", 0, 0); 1854 1855 /* get configured value of "tls_random_file"; default "" */ 1856 dict_ldap->tls_random_file = cfg_get_str(dict_ldap->parser, 1857 "tls_random_file", "", 0, 0); 1858 1859 /* get configured value of "tls_cipher_suite"; default "" */ 1860 dict_ldap->tls_cipher_suite = cfg_get_str(dict_ldap->parser, 1861 "tls_cipher_suite", "", 0, 0); 1862#endif 1863 1864 /* 1865 * Debug level. 1866 */ 1867#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) 1868 dict_ldap->debuglevel = cfg_get_int(dict_ldap->parser, "debuglevel", 1869 0, 0, 0); 1870#endif 1871 1872 /* 1873 * Find or allocate shared LDAP connection container. 1874 */ 1875 dict_ldap_conn_find(dict_ldap); 1876 1877 /* 1878 * Return the new dict_ldap structure. 1879 */ 1880 dict_ldap->dict.owner = cfg_get_owner(dict_ldap->parser); 1881 return (DICT_DEBUG (&dict_ldap->dict)); 1882} 1883 1884#endif 1885