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