1 --- postfix-2.0.19.old/src/util/dict_ldap.c 2002-10-17 02:26:41.000000000 +0200
2 +++ postfix-2.0.19.new/src/util/dict_ldap.c 2004-04-10 21:08:37.000000000 +0200
5 /* Configuration parameters:
6 /* .IP \fIldapsource_\fRserver_host
7 -/* The host at which all LDAP queries are directed.
8 +/* List of hosts at which all LDAP queries are directed.
9 +/* The host names can also be LDAP URLs if the LDAP client library used
11 /* .IP \fIldapsource_\fRserver_port
12 /* The port the LDAP server listens on.
13 /* .IP \fIldapsource_\fRsearch_base
15 /* If you must bind to the server, do it with this distinguished name ...
16 /* .IP \fIldapsource_\fRbind_pw
17 /* \&... and this password.
18 -/* .IP \fIldapsource_\fRcache
19 +/* .IP \fIldapsource_\fRcache (no longer supported)
20 /* Whether or not to turn on client-side caching.
21 -/* .IP \fIldapsource_\fRcache_expiry
22 +/* .IP \fIldapsource_\fRcache_expiry (no longer supported)
23 /* If you do cache results, expire them after this many seconds.
24 -/* .IP \fIldapsource_\fRcache_size
25 +/* .IP \fIldapsource_\fRcache_size (no longer supported)
26 /* The cache size in bytes. Does nothing if the cache is off, of course.
27 +/* .IP \fIldapsource_\fRrecursion_limit
28 +/* Maximum recursion depth when expanding DN or URL references.
29 +/* Queries which exceed the recursion limit fail with
30 +/* dict_errno = DICT_ERR_RETRY.
31 +/* .IP \fIldapsource_\fRexpansion_limit
32 +/* Limit (if any) on the total number of lookup result values. Lookups which
33 +/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that
34 +/* each value of a multivalued result attribute counts as one result.
35 +/* .IP \fIldapsource_\fRsize_limit
36 +/* Limit on the number of entries returned by individual LDAP queries.
37 +/* Queries which exceed the limit fail with dict_errno=DICT_ERR_RETRY.
38 +/* This is an *entry* count, for any single query performed during the
39 +/* possibly recursive lookup.
40 +/* .IP \fIldapsource_\fRchase_referrals
41 +/* Controls whether LDAP referrals are obeyed.
42 /* .IP \fIldapsource_\fRdereference
43 /* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
44 +/* .IP \fIldapsource_\fRversion
45 +/* Specifies the LDAP protocol version to use. Default is version
47 +/* .IP \fIldapsource_\fRstart_tls
48 +/* Whether or not to issue STARTTLS upon connection to the server.
49 +/* At this time, STARTTLS and LDAP SSL are only available if the
50 +/* LDAP client library used is OpenLDAP. Default is \fIno\fR.
51 +/* .IP \fIldapsource_\fRtls_ca_cert_file
52 +/* File containing certificates for all of the X509 Certificate
53 +/* Authorities the client will recognize. Takes precedence over
55 +/* .IP \fIldapsource_\fRtls_ca_cert_dir
56 +/* Directory containing X509 Certificate Authority certificates
57 +/* in separate individual files.
58 +/* .IP \fIldapsource_\fRtls_cert
59 +/* File containing client's X509 certificate.
60 +/* .IP \fIldapsource_\fRtls_key
61 +/* File containing the private key corresponding to
63 +/* .IP \fIldapsource_\fRtls_require_cert
64 +/* Whether or not to request server's X509 certificate and check its
66 +/* .IP \fIldapsource_\fRtls_random_file
67 +/* Path of a file to obtain random bits from when /dev/[u]random is
68 +/* not available. Generally set to the name of the EGD/PRNGD socket.
69 +/* .IP \fIldapsource_\fRtls_cipher_suite
70 +/* Cipher suite to use in SSL/TLS negotiations.
71 /* .IP \fIldapsource_\fRdebuglevel
72 /* Debug level. See 'loglevel' option in slapd.conf(5) man page.
73 /* Currently only in openldap libraries (and derivatives).
82 * Older APIs have weird memory freeing behavior.
87 +#include "stringops.h"
90 #include "dict_ldap.h"
94 #include "../global/mail_conf.h"
96 +#include "dict_ldap.h"
104 * Structure containing all the configuration parameters for a given
105 * LDAP source, plus its connection handle.
106 @@ -152,17 +208,33 @@
114 + long recursion_limit;
115 + long expansion_limit;
120 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
123 + int tls_require_cert;
124 + char *tls_ca_cert_file;
125 + char *tls_ca_cert_dir;
128 + char *tls_random_file;
129 + char *tls_cipher_suite;
131 + BINHASH_INFO *ht; /* hash entry for LDAP connection */
135 -#ifndef LDAP_OPT_NETWORK_TIMEOUT
136 +#define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
138 +static BINHASH *conn_hash = 0;
140 +#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
142 * LDAP connection timeout support.
145 static void dict_ldap_logprint(LDAP_CONST char *data)
147 char *myname = "dict_ldap_debug";
151 - msg_info("%s: %s", myname, data);
152 + buf = mystrdup(data);
154 + p = buf + strlen(buf) - 1;
155 + while (p - buf >= 0 && ISSPACE(*p))
158 + msg_info("%s: %s", myname, buf);
163 @@ -225,24 +306,97 @@
164 return (ldap_result2error(dict_ldap->ld, res, 1));
167 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
168 +static void dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
170 + char *myname = "dict_ldap_set_tls_options";
173 + if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
174 + if (*dict_ldap->tls_random_file) {
175 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
176 + dict_ldap->tls_random_file)) != LDAP_SUCCESS)
177 + msg_warn("%s: Unable to set tls_random_file to %s: %d: %s",
178 + myname, dict_ldap->tls_random_file,
179 + rc, ldap_err2string(rc));
181 + if (*dict_ldap->tls_ca_cert_file) {
182 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
183 + dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS)
184 + msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s",
185 + myname, dict_ldap->tls_ca_cert_file,
186 + rc, ldap_err2string(rc));
188 + if (*dict_ldap->tls_ca_cert_dir) {
189 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR,
190 + dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS)
191 + msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s",
192 + myname, dict_ldap->tls_ca_cert_dir,
193 + rc, ldap_err2string(rc));
195 + if (*dict_ldap->tls_cert) {
196 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE,
197 + dict_ldap->tls_cert)) != LDAP_SUCCESS)
198 + msg_warn("%s: Unable to set tls_cert to %s: %d: %s",
199 + myname, dict_ldap->tls_cert,
200 + rc, ldap_err2string(rc));
202 + if (*dict_ldap->tls_key) {
203 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE,
204 + dict_ldap->tls_key)) != LDAP_SUCCESS)
205 + msg_warn("%s: Unable to set tls_key to %s: %d: %s",
206 + myname, dict_ldap->tls_key,
207 + rc, ldap_err2string(rc));
209 + if (*dict_ldap->tls_cipher_suite) {
210 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
211 + dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS)
212 + msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s",
213 + myname, dict_ldap->tls_cipher_suite,
214 + rc, ldap_err2string(rc));
216 + if (dict_ldap->tls_require_cert) {
217 + if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
218 + &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS)
219 + msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s",
220 + myname, dict_ldap->tls_require_cert,
221 + rc, ldap_err2string(rc));
229 /* Establish a connection to the LDAP server. */
230 static int dict_ldap_connect(DICT_LDAP *dict_ldap)
232 char *myname = "dict_ldap_connect";
235 -#ifdef LDAP_API_FEATURE_X_MEMCACHE
236 - LDAPMemCache *dircache;
240 #ifdef LDAP_OPT_NETWORK_TIMEOUT
241 struct timeval mytimeval;
246 +#if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
247 void (*saved_alarm) (int);
250 +#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
251 + if (dict_ldap->debuglevel > 0 &&
252 + ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
253 + (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
254 + msg_warn("%s: Unable to set ber logprint function.", myname);
255 +#if defined(LBER_OPT_DEBUG_LEVEL)
256 + if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
257 + &(dict_ldap->debuglevel)) != LBER_OPT_SUCCESS)
258 + msg_warn("%s: Unable to set BER debug level.", myname);
260 + if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
261 + &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
262 + msg_warn("%s: Unable to set LDAP debug level.", myname);
268 dict_ldap->server_host);
270 #ifdef LDAP_OPT_NETWORK_TIMEOUT
271 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
272 + dict_ldap_set_tls_options(dict_ldap);
273 + ldap_initialize(&(dict_ldap->ld), dict_ldap->server_host);
275 dict_ldap->ld = ldap_init(dict_ldap->server_host,
276 (int) dict_ldap->server_port);
278 if (dict_ldap->ld == NULL) {
279 msg_warn("%s: Unable to init LDAP server %s",
280 myname, dict_ldap->server_host);
281 @@ -308,12 +467,22 @@
282 &dict_ldap->version) != LDAP_OPT_SUCCESS)
283 msg_warn("%s: Unable to get LDAP protocol version", myname);
285 - msg_warn("%s: Actual Protocol version used is %d.",
286 + msg_info("%s: Actual Protocol version used is %d.",
287 myname, dict_ldap->version);
292 + * Limit the number of entries returned by each query.
294 + if (dict_ldap->size_limit) {
295 + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT,
296 + &dict_ldap->size_limit) != LDAP_OPT_SUCCESS)
297 + msg_warn("%s: %s: Unable to set query result size limit to %ld.",
298 + myname, dict_ldap->ldapsource, dict_ldap->size_limit);
302 * Configure alias dereferencing for this connection. Thanks to Mike
303 * Mattice for this, and to Hery Rakotoarisoa for the v3 update.
306 &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS)
307 msg_warn("%s: Unable to set dereference option.", myname);
309 -#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
310 - if (dict_ldap->debuglevel > 0 &&
311 - ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
312 - (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
313 - msg_warn("%s: Unable to set ber logprint function.", myname);
314 - if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEBUG_LEVEL,
315 - &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
316 - msg_warn("%s: Unable to set LDAP debug level.", myname);
319 /* Chase referrals. */
326 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
327 + if (dict_ldap->start_tls) {
328 + if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
329 + msg_warn("%s: Error setting signal handler for STARTTLS timeout: %m",
331 + dict_errno = DICT_ERR_RETRY;
334 + alarm(dict_ldap->timeout);
335 + if (setjmp(env) == 0)
336 + rc = ldap_start_tls_s(dict_ldap->ld, NULL, NULL);
341 + if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
342 + msg_warn("%s: Error resetting signal handler after STARTTLS: %m",
344 + dict_errno = DICT_ERR_RETRY;
347 + if (rc != LDAP_SUCCESS) {
348 + msg_error("%s: Unable to set STARTTLS: %d: %s", myname,
349 + rc, ldap_err2string(rc));
350 + dict_errno = DICT_ERR_RETRY;
357 * If this server requires a bind, do so. Thanks to Sam Tardieu for
358 * noticing that the original bind call was broken.
360 msg_info("%s: Successful bind to server %s as %s ",
361 myname, dict_ldap->server_host, dict_ldap->bind_dn);
363 + /* Save connection handle in shared container */
364 + DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
367 - * Set up client-side caching if it's configured.
369 - if (dict_ldap->cache) {
372 - ("%s: Enabling %ld-byte cache for %s with %ld-second expiry",
373 - myname, dict_ldap->cache_size, dict_ldap->ldapsource,
374 - dict_ldap->cache_expiry);
376 -#ifdef LDAP_API_FEATURE_X_MEMCACHE
377 - rc = ldap_memcache_init(dict_ldap->cache_expiry, dict_ldap->cache_size,
378 - NULL, NULL, &dircache);
379 - if (rc != LDAP_SUCCESS) {
381 - ("%s: Unable to configure cache for %s: %d (%s) -- continuing",
382 - myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
384 - rc = ldap_memcache_set(dict_ldap->ld, dircache);
385 - if (rc != LDAP_SUCCESS) {
387 - ("%s: Unable to configure cache for %s: %d (%s) -- continuing",
388 - myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
391 - msg_info("%s: Caching enabled for %s",
392 - myname, dict_ldap->ldapsource);
397 - rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry,
398 - dict_ldap->cache_size);
399 - if (rc != LDAP_SUCCESS) {
401 - ("%s: Unable to configure cache for %s: %d (%s) -- continuing",
402 - myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
405 - msg_info("%s: Caching enabled for %s",
406 - myname, dict_ldap->ldapsource);
412 msg_info("%s: Cached connection handle for LDAP source %s",
413 myname, dict_ldap->ldapsource);
418 + * Locate or allocate connection cache entry.
420 +static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
422 + VSTRING *keybuf = vstring_alloc(10);
426 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
427 + int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl;
432 +#define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1)
433 +#define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu", (unsigned long)(i))
435 + ADDSTR(keybuf, dict_ldap->server_host);
436 + ADDINT(keybuf, dict_ldap->server_port);
437 + ADDINT(keybuf, dict_ldap->bind);
438 + ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : "");
439 + ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : "");
440 + ADDINT(keybuf, dict_ldap->dereference);
441 + ADDINT(keybuf, dict_ldap->chase_referrals);
442 + ADDINT(keybuf, dict_ldap->debuglevel);
443 + ADDINT(keybuf, dict_ldap->version);
444 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
445 + ADDINT(keybuf, dict_ldap->ldap_ssl);
446 + ADDINT(keybuf, dict_ldap->start_tls);
447 + ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
448 + ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : "");
449 + ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : "");
450 + ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : "");
451 + ADDSTR(keybuf, sslon ? dict_ldap->tls_key : "");
452 + ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : "");
453 + ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : "");
456 + key = vstring_str(keybuf);
457 + len = VSTRING_LEN(keybuf);
459 + if (conn_hash == 0)
460 + conn_hash = binhash_create(0);
462 + if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) {
463 + conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN));
465 + conn->conn_refcount = 0;
466 + dict_ldap->ht = binhash_enter(conn_hash, key, len, (char *) conn);
468 + ++DICT_LDAP_CONN(dict_ldap)->conn_refcount;
470 + vstring_free(keybuf);
474 * expand a filter (lookup or result)
476 -static void dict_ldap_expand_filter(char *filter, char *value, VSTRING *out)
477 +static void dict_ldap_expand_filter(char *ldapsource, char *filter, char *value, VSTRING *out)
479 char *myname = "dict_ldap_expand_filter";
485 - ("%s: Invalid filter substitution format '%%%c'!",
486 - myname, *(sub + 1));
487 + ("%s: %s: Invalid filter substitution format '%%%c'!",
488 + myname, ldapsource, *(sub + 1));
491 vstring_strcat(out, u);
493 static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
496 + static int recursion = 0;
497 + static int expansion;
501 LDAPMessage *resloop = 0;
502 @@ -500,13 +705,27 @@
503 tv.tv_sec = dict_ldap->timeout;
506 + if (++recursion == 1)
510 - msg_info("%s: Search found %d match(es)", myname,
511 + msg_info("%s[%d]: Search found %d match(es)", myname, recursion,
512 ldap_count_entries(dict_ldap->ld, res));
514 for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
515 entry = ldap_next_entry(dict_ldap->ld, entry)) {
519 + * LDAP should not, but may produce more than the requested maximum
520 + * number of entries.
522 + if (dict_errno == 0 && ++entries > dict_ldap->size_limit
523 + && dict_ldap->size_limit) {
524 + msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded", myname,
525 + recursion, dict_ldap->ldapsource, dict_ldap->size_limit);
526 + dict_errno = DICT_ERR_RETRY;
528 for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber);
530 ldap_memfree(attr), attr = ldap_next_attribute(dict_ldap->ld,
531 @@ -514,17 +733,38 @@
532 vals = ldap_get_values(dict_ldap->ld, entry, attr);
535 - msg_info("%s: Entry doesn't have any values for %s",
537 + msg_info("%s[%d]: Entry doesn't have any values for %s",
538 + myname, recursion, attr);
543 + * If we previously encountered an error, we still continue
544 + * through the loop, to avoid memory leaks, but we don't waste
545 + * time accumulating any further results.
547 + * XXX: There may be a more efficient way to exit the loop with no
548 + * leaks, but it will likely be more fragile and not worth the
551 + if (dict_errno != 0 || vals[0] == 0) {
552 + ldap_value_free(vals);
557 + * The "result_attributes" list enumerates all the requested
558 + * attributes, first the ordinary result attribtutes and then the
559 + * special result attributes that hold DN or LDAP URL values.
561 + * The number of ordinary attributes is "num_attributes".
563 + * We compute the attribute type (ordinary or special) from its
564 + * index on the "result_attributes" list.
566 for (i = 0; dict_ldap->result_attributes->argv[i]; i++) {
567 - if (strcasecmp(dict_ldap->result_attributes->argv[i],
570 - msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr);
571 + if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0)
577 @@ -532,20 +772,38 @@
578 * recursing (for dn or url attributes).
580 if (i < dict_ldap->num_attributes) {
581 + /* Ordinary result attribute */
582 for (i = 0; vals[i] != NULL; i++) {
583 + if (++expansion > dict_ldap->expansion_limit &&
584 + dict_ldap->expansion_limit) {
585 + msg_warn("%s[%d]: %s: Expansion limit exceeded at"
586 + " result attribute %s=%s", myname, recursion,
587 + dict_ldap->ldapsource, attr, vals[i]);
588 + dict_errno = DICT_ERR_RETRY;
591 if (VSTRING_LEN(result) > 0)
592 vstring_strcat(result, ",");
593 if (dict_ldap->result_filter == NULL)
594 vstring_strcat(result, vals[i]);
596 - dict_ldap_expand_filter(dict_ldap->result_filter,
597 + dict_ldap_expand_filter(dict_ldap->ldapsource,
598 + dict_ldap->result_filter,
601 - } else if (dict_ldap->result_attributes->argv[i]) {
602 + if (dict_errno != 0)
605 + msg_info("%s[%d]: search returned %ld value(s) for"
606 + " requested result attribute %s",
607 + myname, recursion, i, attr);
608 + } else if (recursion < dict_ldap->recursion_limit
609 + && dict_ldap->result_attributes->argv[i]) {
610 + /* Special result attribute */
611 for (i = 0; vals[i] != NULL; i++) {
612 if (ldap_is_ldap_url(vals[i])) {
614 - msg_info("%s: looking up URL %s", myname,
615 + msg_info("%s[%d]: looking up URL %s", myname, recursion,
617 rc = ldap_url_parse(vals[i], &url);
623 - msg_info("%s: looking up DN %s", myname, vals[i]);
624 + msg_info("%s[%d]: looking up DN %s", myname, recursion, vals[i]);
625 rc = ldap_search_st(dict_ldap->ld, vals[i],
626 LDAP_SCOPE_BASE, "objectclass=*",
627 dict_ldap->result_attributes->argv,
628 @@ -573,11 +831,11 @@
629 * Go ahead and treat this as though the DN existed
630 * and just didn't have any result attributes.
632 - msg_warn("%s: DN %s not found, skipping ", myname,
634 + msg_warn("%s[%d]: DN %s not found, skipping ", myname,
635 + recursion, vals[i]);
638 - msg_warn("%s: search error %d: %s ", myname, rc,
639 + msg_warn("%s[%d]: search error %d: %s ", myname, recursion, rc,
640 ldap_err2string(rc));
641 dict_errno = DICT_ERR_RETRY;
646 ldap_msgfree(resloop);
647 + if (dict_errno != 0)
650 + if (dict_errno != 0)
653 + msg_info("%s[%d]: search returned %ld value(s) for"
654 + " special result attribute %s",
655 + myname, recursion, i, attr);
656 + } else if (recursion >= dict_ldap->recursion_limit
657 + && dict_ldap->result_attributes->argv[i]) {
658 + msg_warn("%s[%d]: %s: Recursion limit exceeded"
659 + " for special attribute %s=%s",
660 + myname, recursion, dict_ldap->ldapsource, attr, vals[0]);
661 + dict_errno = DICT_ERR_RETRY;
663 ldap_value_free(vals);
669 - msg_info("%s: Leaving %s", myname, myname);
670 + msg_info("%s[%d]: Leaving %s", myname, recursion, myname);
674 /* dict_ldap_lookup - find database entry */
676 VSTRING *escaped_name = 0,
684 if (dict_ldap->domain) {
685 const char *p = strrchr(name, '@');
691 - if (match_list_match(dict_ldap->domain, p) == 0) {
692 + if (p == 0 || p == name ||
693 + match_list_match(dict_ldap->domain, ++p) == 0) {
695 msg_info("%s: domain of %s not found in domain list", myname,
698 vstring_strcpy(result, "");
701 + * Because the connection may be shared and invalidated via queries for
702 + * another map, update private copy of "ld" from shared connection
705 + dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;
708 * Connect to the LDAP server, if necessary.
710 if (dict_ldap->ld == NULL) {
712 msg_info("%s: Using existing connection for LDAP source %s",
713 myname, dict_ldap->ldapsource);
716 + * Connection caching, means that the connection handle may have the
717 + * wrong size limit. Re-adjust before each query. This is cheap, just
718 + * sets a field in the ldap connection handle. We also do this in the
719 + * connect code, because we sometimes reconnect (below) in the middle of
722 + sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT;
723 + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
724 + != LDAP_OPT_SUCCESS)
725 + msg_warn("%s: %s: Unable to set query result size limit to %ld.",
726 + myname, dict_ldap->ldapsource, dict_ldap->size_limit);
730 @@ -720,11 +1010,11 @@
732 * No, log the fact and continue.
734 - msg_warn("%s: Fixed query_filter %s is probably useless", myname,
735 - dict_ldap->query_filter);
736 + msg_warn("%s: %s: Fixed query_filter %s is probably useless", myname,
737 + dict_ldap->ldapsource, dict_ldap->query_filter);
738 vstring_strcpy(filter_buf, dict_ldap->query_filter);
740 - dict_ldap_expand_filter(dict_ldap->query_filter,
741 + dict_ldap_expand_filter(dict_ldap->ldapsource, dict_ldap->query_filter,
742 vstring_str(escaped_name), filter_buf);
746 myname, dict_ldap->ldapsource);
748 ldap_unbind(dict_ldap->ld);
749 - dict_ldap->ld = NULL;
750 + dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
751 dict_ldap_connect(dict_ldap);
757 ldap_unbind(dict_ldap->ld);
758 - dict_ldap->ld = NULL;
759 + dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
762 * And tell the caller to try again later.
763 @@ -830,9 +1120,18 @@
765 char *myname = "dict_ldap_close";
766 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
767 + LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
768 + BINHASH_INFO *ht = dict_ldap->ht;
771 - ldap_unbind(dict_ldap->ld);
772 + if (--conn->conn_refcount == 0) {
773 + if (conn->conn_ld) {
775 + msg_info("%s: Closed connection handle for LDAP source %s",
776 + myname, dict_ldap->ldapsource);
777 + ldap_unbind(conn->conn_ld);
779 + binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
782 myfree(dict_ldap->ldapsource);
783 myfree(dict_ldap->server_host);
784 @@ -845,19 +1144,32 @@
785 argv_free(dict_ldap->result_attributes);
786 myfree(dict_ldap->bind_dn);
787 myfree(dict_ldap->bind_pw);
788 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
789 + myfree(dict_ldap->tls_ca_cert_file);
790 + myfree(dict_ldap->tls_ca_cert_dir);
791 + myfree(dict_ldap->tls_cert);
792 + myfree(dict_ldap->tls_key);
793 + myfree(dict_ldap->tls_random_file);
794 + myfree(dict_ldap->tls_cipher_suite);
799 /* dict_ldap_open - create association with data base */
801 -DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
802 +DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
804 char *myname = "dict_ldap_open";
805 DICT_LDAP *dict_ldap;
806 VSTRING *config_param;
817 msg_info("%s: Using LDAP source %s", myname, ldapsource);
818 @@ -870,15 +1182,14 @@
820 dict_ldap->ldapsource = mystrdup(ldapsource);
822 + dict_ldap->ld = NULL;
824 config_param = vstring_alloc(15);
825 vstring_sprintf(config_param, "%s_server_host", ldapsource);
827 - dict_ldap->server_host =
829 mystrdup((char *) get_mail_conf_str(vstring_str(config_param),
830 - "localhost", 0, 0));
832 - msg_info("%s: %s is %s", myname, vstring_str(config_param),
833 - dict_ldap->server_host);
834 + "localhost", 1, 0));
837 * get configured value of "ldapsource_server_port"; default to LDAP_PORT
838 @@ -892,11 +1203,87 @@
839 dict_ldap->server_port);
842 + * Define LDAP Version.
844 + vstring_sprintf(config_param, "%s_version", ldapsource);
845 + dict_ldap->version = get_mail_conf_int(vstring_str(config_param), 2, 2, 0);
846 + switch (dict_ldap->version) {
848 + dict_ldap->version = LDAP_VERSION2;
851 + dict_ldap->version = LDAP_VERSION3;
854 + msg_warn("%s: Unknown version %d.", myname, dict_ldap->version);
855 + dict_ldap->version = LDAP_VERSION2;
858 +#if defined(LDAP_API_FEATURE_X_OPENLDAP)
859 + dict_ldap->ldap_ssl = 0;
862 + url_list = vstring_alloc(32);
864 + while ((h = mystrtok(&s, " \t\n\r,")) != NULL) {
865 +#if defined(LDAP_API_FEATURE_X_OPENLDAP)
868 + * Convert (host, port) pairs to LDAP URLs
870 + if (ldap_is_ldap_url(h)) {
871 + LDAPURLDesc *url_desc;
874 + if ((rc = ldap_url_parse(h, &url_desc)) != 0) {
875 + msg_error("%s: error parsing URL %s: %d: %s; skipping", myname,
876 + h, rc, ldap_err2string(rc));
879 + if (strcasecmp(url_desc->lud_scheme, "ldap") != 0 &&
880 + dict_ldap->version != LDAP_VERSION3) {
881 + msg_warn("%s: URL scheme %s requires protocol version 3", myname,
882 + url_desc->lud_scheme);
883 + dict_ldap->version = LDAP_VERSION3;
885 + if (strcasecmp(url_desc->lud_scheme, "ldaps") == 0)
886 + dict_ldap->ldap_ssl = 1;
887 + ldap_free_urldesc(url_desc);
888 + vstring_sprintf_append(url_list, " %s", h);
890 + if (strrchr(h, ':'))
891 + vstring_sprintf_append(url_list, " ldap://%s", h);
893 + vstring_sprintf_append(url_list, " ldap://%s:%d", h,
894 + dict_ldap->server_port);
897 + vstring_sprintf_append(url_list, " %s", h);
901 + dict_ldap->server_host =
902 + mystrdup(VSTRING_LEN(url_list) > 0 ? vstring_str(url_list) + 1 : "");
904 +#if defined(LDAP_API_FEATURE_X_OPENLDAP)
906 + * With URL scheme, clear port to normalize connection cache key
909 + dict_ldap->server_port = LDAP_PORT;
911 + msg_info("%s: %s server_host URL is %s", myname, ldapsource,
912 + dict_ldap->server_host);
914 + myfree(server_host);
915 + vstring_free(url_list);
918 * Scope handling thanks to Carsten Hoeger of SuSE.
920 vstring_sprintf(config_param, "%s_scope", ldapsource);
922 - (char *) get_mail_conf_str(vstring_str(config_param), "sub", 0, 0);
923 + (char *) get_mail_conf_str(vstring_str(config_param), "sub", 1, 0);
925 if (strcasecmp(scope, "one") == 0) {
926 dict_ldap->scope = LDAP_SCOPE_ONELEVEL;
927 @@ -910,12 +1297,15 @@
928 msg_info("%s: %s is LDAP_SCOPE_BASE", myname,
929 vstring_str(config_param));
932 + } else if (strcasecmp(scope, "sub") == 0) {
933 dict_ldap->scope = LDAP_SCOPE_SUBTREE;
935 msg_info("%s: %s is LDAP_SCOPE_SUBTREE", myname,
936 vstring_str(config_param));
939 + dict_ldap->scope = LDAP_SCOPE_SUBTREE;
940 + msg_warn("%s: %s: Unrecognized value %s specified for scope; using sub",
941 + myname, vstring_str(config_param), scope);
946 msg_info("%s: %s is %s", myname, vstring_str(config_param), attr);;
947 dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n");
948 dict_ldap->num_attributes = dict_ldap->result_attributes->argc;
952 vstring_sprintf(config_param, "%s_special_result_attribute", ldapsource);
953 attr = mystrdup((char *) get_mail_conf_str(vstring_str(config_param),
954 @@ -1003,6 +1395,8 @@
955 argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
961 * get configured value of "ldapsource_bind"; default to true
963 @@ -1040,31 +1434,60 @@
964 * get configured value of "ldapsource_cache"; default to false
966 vstring_sprintf(config_param, "%s_cache", ldapsource);
967 - dict_ldap->cache = get_mail_conf_bool(vstring_str(config_param), 0);
969 - msg_info("%s: %s is %d", myname, vstring_str(config_param),
971 + tmp = get_mail_conf_bool(vstring_str(config_param), 0);
973 + msg_warn("%s: %s ignoring cache", myname, vstring_str(config_param));
976 * get configured value of "ldapsource_cache_expiry"; default to 30
979 vstring_sprintf(config_param, "%s_cache_expiry", ldapsource);
980 - dict_ldap->cache_expiry = get_mail_conf_int(vstring_str(config_param),
983 - msg_info("%s: %s is %ld", myname, vstring_str(config_param),
984 - dict_ldap->cache_expiry);
985 + tmp = get_mail_conf_int(vstring_str(config_param), -1, 0, 0);
988 + msg_warn("%s: %s ignoring cache_expiry", myname, vstring_str(config_param));
991 * get configured value of "ldapsource_cache_size"; default to 32k
993 vstring_sprintf(config_param, "%s_cache_size", ldapsource);
994 - dict_ldap->cache_size = get_mail_conf_int(vstring_str(config_param),
996 + tmp = get_mail_conf_int(vstring_str(config_param),
999 + msg_warn("%s: %s ignoring cache_size", myname, vstring_str(config_param));
1002 + * get configured value of "recursion_limit"; default to 1000
1004 + vstring_sprintf(config_param, "%s_recursion_limit", ldapsource);
1005 + dict_ldap->recursion_limit = get_mail_conf_int(vstring_str(config_param),
1009 + msg_info("%s: %s is %ld", myname, vstring_str(config_param),
1010 + dict_ldap->recursion_limit);
1013 + * Define LDAP Version.
1014 + * get configured value of "expansion_limit"; default to 0
1017 + vstring_sprintf(config_param, "%s_expansion_limit", ldapsource);
1018 + dict_ldap->expansion_limit = get_mail_conf_int(vstring_str(config_param),
1021 + msg_info("%s: %s is %ld", myname, vstring_str(config_param),
1022 + dict_ldap->expansion_limit);
1024 + * get configured value of "size_limit"; default to expansion_limit
1026 + vstring_sprintf(config_param, "%s_size_limit", ldapsource);
1027 + dict_ldap->size_limit = get_mail_conf_int(vstring_str(config_param),
1028 + dict_ldap->expansion_limit, 0, 0);
1030 - msg_info("%s: %s is %ld", myname, vstring_str(config_param),
1031 - dict_ldap->cache_size);
1032 + msg_info("%s: %s is %ld", myname, vstring_str(config_param),
1033 + dict_ldap->size_limit);
1036 * Alias dereferencing suggested by Mike Mattice.
1037 @@ -1074,29 +1497,11 @@
1041 - * Define LDAP Version.
1043 - vstring_sprintf(config_param, "%s_version", ldapsource);
1044 - dict_ldap->version = get_mail_conf_int(vstring_str(config_param), 2, 0,
1046 - switch (dict_ldap->version) {
1048 - dict_ldap->version = LDAP_VERSION2;
1051 - dict_ldap->version = LDAP_VERSION3;
1054 - msg_warn("%s: Unknown version %d.", myname, dict_ldap->version);
1055 - dict_ldap->version = LDAP_VERSION2;
1059 * Make sure only valid options for alias dereferencing are used.
1061 if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) {
1062 - msg_warn("%s: Unrecognized value %d specified for %s; using 0",
1063 - myname, dict_ldap->dereference, vstring_str(config_param));
1064 + msg_warn("%s: %s: Unrecognized value %d specified for %s; using 0",
1065 + myname, ldapsource, dict_ldap->dereference, vstring_str(config_param));
1066 dict_ldap->dereference = 0;
1069 @@ -1110,6 +1515,68 @@
1070 msg_info("%s: %s is %d", myname, vstring_str(config_param),
1071 dict_ldap->chase_referrals);
1073 +#ifdef LDAP_API_FEATURE_X_OPENLDAP
1078 + /* get configured value of "start_tls"; default to no */
1079 + vstring_sprintf(config_param, "%s_start_tls", ldapsource);
1080 + dict_ldap->start_tls = get_mail_conf_bool(vstring_str(config_param), 0);
1081 + if (dict_ldap->start_tls && dict_ldap->version < LDAP_VERSION3) {
1082 + msg_warn("%s: %s start_tls requires protocol version 3",
1083 + myname, ldapsource);
1084 + dict_ldap->version = LDAP_VERSION3;
1087 + /* get configured value of "tls_require_cert"; default to no */
1088 + vstring_sprintf(config_param, "%s_tls_require_cert", ldapsource);
1089 + dict_ldap->tls_require_cert = get_mail_conf_bool(vstring_str(config_param), 0);
1091 + /* get configured value of "tls_ca_cert_file"; default "" */
1092 + vstring_sprintf(config_param, "%s_tls_ca_cert_file", ldapsource);
1093 + dict_ldap->tls_ca_cert_file = mystrdup((char *)
1094 + get_mail_conf_str(vstring_str
1095 + (config_param), "", 0,
1098 + /* get configured value of "tls_ca_cert_dir"; default "" */
1099 + vstring_sprintf(config_param, "%s_tls_ca_cert_dir", ldapsource);
1100 + dict_ldap->tls_ca_cert_dir = mystrdup((char *)
1101 + get_mail_conf_str(vstring_str
1102 + (config_param), "", 0,
1105 + /* get configured value of "tls_cert"; default "" */
1106 + vstring_sprintf(config_param, "%s_tls_cert", ldapsource);
1107 + dict_ldap->tls_cert = mystrdup((char *)
1108 + get_mail_conf_str(vstring_str
1109 + (config_param), "", 0,
1112 + /* get configured value of "tls_key"; default "" */
1113 + vstring_sprintf(config_param, "%s_tls_key", ldapsource);
1114 + dict_ldap->tls_key = mystrdup((char *)
1115 + get_mail_conf_str(vstring_str
1116 + (config_param), "", 0,
1119 + /* get configured value of "tls_random_file"; default "" */
1120 + vstring_sprintf(config_param, "%s_tls_random_file", ldapsource);
1121 + dict_ldap->tls_random_file = mystrdup((char *)
1122 + get_mail_conf_str(vstring_str
1123 + (config_param), "", 0,
1126 + /* get configured value of "tls_cipher_suite"; default "" */
1127 + vstring_sprintf(config_param, "%s_tls_cipher_suite", ldapsource);
1128 + dict_ldap->tls_cipher_suite = mystrdup((char *)
1129 + get_mail_conf_str(vstring_str
1130 + (config_param), "", 0,
1138 @@ -1122,21 +1589,13 @@
1139 dict_ldap->debuglevel);
1142 - dict_ldap_connect(dict_ldap);
1145 - * if dict_ldap_connect() set dict_errno, free dict_ldap and abort.
1146 + * Find or allocate shared LDAP connection container.
1149 - if (dict_ldap->ld)
1150 - ldap_unbind(dict_ldap->ld);
1152 - myfree((char *) dict_ldap);
1155 + dict_ldap_conn_find(dict_ldap);
1158 - * Otherwise, we're all set. Return the new dict_ldap structure.
1159 + * Return the new dict_ldap structure.
1161 return (DICT_DEBUG (&dict_ldap->dict));