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