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