Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / dict_ldap.c
blob710147942153ad8922e9bae6e29071d32cd5d9e8
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dict_ldap 3
6 /* SUMMARY
7 /* dictionary manager interface to LDAP maps
8 /* SYNOPSIS
9 /* #include <dict_ldap.h>
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).
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.
25 /* In the first case, the configuration variables below are
26 /* specified in the file as \fBname\fR=\fBvalue\fR pairs.
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 start_tls
109 /* Whether or not to issue STARTTLS upon connection to the server.
110 /* At this time, STARTTLS and LDAP SSL are only available if the
111 /* LDAP client library used is OpenLDAP. Default is \fIno\fR.
112 /* .IP tls_ca_cert_file
113 /* File containing certificates for all of the X509 Certificate
114 /* Authorities the client will recognize. Takes precedence over
115 /* tls_ca_cert_dir.
116 /* .IP tls_ca_cert_dir
117 /* Directory containing X509 Certificate Authority certificates
118 /* in separate individual files.
119 /* .IP tls_cert
120 /* File containing client's X509 certificate.
121 /* .IP tls_key
122 /* File containing the private key corresponding to
123 /* tls_cert.
124 /* .IP tls_require_cert
125 /* Whether or not to request server's X509 certificate and check its
126 /* validity. The value "no" means don't check the cert trust chain
127 /* and (OpenLDAP 2.1+) don't check the peername. The value "yes" means
128 /* check both the trust chain and the peername (with OpenLDAP <= 2.0.11,
129 /* the peername checks use the reverse hostname from the LDAP servers's
130 /* IP address, not the user supplied servername).
131 /* .IP tls_random_file
132 /* Path of a file to obtain random bits from when /dev/[u]random is
133 /* not available. Generally set to the name of the EGD/PRNGD socket.
134 /* .IP tls_cipher_suite
135 /* Cipher suite to use in SSL/TLS negotiations.
136 /* .IP debuglevel
137 /* Debug level. See 'loglevel' option in slapd.conf(5) man page.
138 /* Currently only in openldap libraries (and derivatives).
139 /* SEE ALSO
140 /* dict(3) generic dictionary manager
141 /* AUTHOR(S)
142 /* Prabhat K Singh
143 /* VSNL, Bombay, India.
144 /* prabhat@giasbm01.vsnl.net.in
146 /* Wietse Venema
147 /* IBM T.J. Watson Research
148 /* P.O. Box 704
149 /* Yorktown Heights, NY 10532, USA
151 /* John Hensley
152 /* john@sunislelodge.com
154 /* Current maintainers:
156 /* LaMont Jones
157 /* lamont@debian.org
159 /* Victor Duchovni
160 /* Morgan Stanley
161 /* New York, USA
163 /* Liviu Daia
164 /* Institute of Mathematics of the Romanian Academy
165 /* P.O. BOX 1-764
166 /* RO-014700 Bucharest, ROMANIA
167 /*--*/
169 /* System library. */
171 #include "sys_defs.h"
173 #ifdef HAS_LDAP
175 #include <sys/time.h>
176 #include <stdio.h>
177 #include <signal.h>
178 #include <setjmp.h>
179 #include <stdlib.h>
180 #include <lber.h>
181 #include <ldap.h>
182 #include <string.h>
183 #include <ctype.h>
184 #include <unistd.h>
186 #ifdef STRCASECMP_IN_STRINGS_H
187 #include <strings.h>
188 #endif
191 * Older APIs have weird memory freeing behavior.
193 #if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000)
194 #error "Your LDAP version is too old"
195 #endif
197 /* Handle differences between LDAP SDK's constant definitions */
198 #ifndef LDAP_CONST
199 #define LDAP_CONST const
200 #endif
201 #ifndef LDAP_OPT_SUCCESS
202 #define LDAP_OPT_SUCCESS 0
203 #endif
205 /* Utility library. */
207 #include <msg.h>
208 #include <mymalloc.h>
209 #include <vstring.h>
210 #include <dict.h>
211 #include <stringops.h>
212 #include <binhash.h>
214 /* Global library. */
216 #include "cfg_parser.h"
217 #include "db_common.h"
219 /* Application-specific. */
221 #include "dict_ldap.h"
223 typedef struct {
224 LDAP *conn_ld;
225 int conn_refcount;
226 } LDAP_CONN;
229 * Structure containing all the configuration parameters for a given
230 * LDAP source, plus its connection handle.
232 typedef struct {
233 DICT dict; /* generic member */
234 CFG_PARSER *parser; /* common parameter parser */
235 char *query; /* db_common_expand() query */
236 char *result_format; /* db_common_expand() result_format */
237 void *ctx; /* db_common_parse() context */
238 int dynamic_base; /* Search base has substitutions? */
239 int expansion_limit;
240 char *server_host;
241 int server_port;
242 int scope;
243 char *search_base;
244 ARGV *result_attributes;
245 int num_terminal; /* Number of terminal attributes. */
246 int num_leaf; /* Number of leaf attributes */
247 int num_attributes; /* Combined # of non-special attrs */
248 int bind;
249 char *bind_dn;
250 char *bind_pw;
251 int timeout;
252 int dereference;
253 long recursion_limit;
254 long size_limit;
255 int chase_referrals;
256 int debuglevel;
257 int version;
258 #ifdef LDAP_API_FEATURE_X_OPENLDAP
259 int ldap_ssl;
260 int start_tls;
261 int tls_require_cert;
262 char *tls_ca_cert_file;
263 char *tls_ca_cert_dir;
264 char *tls_cert;
265 char *tls_key;
266 char *tls_random_file;
267 char *tls_cipher_suite;
268 #endif
269 BINHASH_INFO *ht; /* hash entry for LDAP connection */
270 LDAP *ld; /* duplicated from conn->conn_ld */
271 } DICT_LDAP;
273 #define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
275 #define DICT_LDAP_UNBIND_RETURN(__ld, __err, __ret) do { \
276 dict_ldap_unbind(__ld); \
277 (__ld) = 0; \
278 dict_errno = (__err); \
279 return ((__ret)); \
280 } while (0)
283 * Bitrot: LDAP_API 3000 and up (OpenLDAP 2.2.x) deprecated ldap_unbind()
285 #if LDAP_API_VERSION >= 3000
286 #define dict_ldap_unbind(ld) ldap_unbind_ext((ld), 0, 0)
287 #define dict_ldap_abandon(ld, msg) ldap_abandon_ext((ld), (msg), 0, 0)
288 #else
289 #define dict_ldap_unbind(ld) ldap_unbind(ld)
290 #define dict_ldap_abandon(ld, msg) ldap_abandon((ld), (msg))
291 #endif
293 static int dict_ldap_vendor_version(void)
295 const char *myname = "dict_ldap_api_info";
296 LDAPAPIInfo api;
299 * We tell the library our version, and it tells us its version and/or
300 * may return an error code if the versions are not the same.
302 api.ldapai_info_version = LDAP_API_INFO_VERSION;
303 if (ldap_get_option(0, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS
304 || api.ldapai_info_version != LDAP_API_INFO_VERSION) {
305 if (api.ldapai_info_version != LDAP_API_INFO_VERSION)
306 msg_fatal("%s: run-time API_INFO version: %d, compiled with: %d",
307 myname, api.ldapai_info_version, LDAP_API_INFO_VERSION);
308 else
309 msg_fatal("%s: ldap_get_option(API_INFO) failed", myname);
311 if (strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME) != 0)
312 msg_fatal("%s: run-time API vendor: %s, compiled with: %s",
313 myname, api.ldapai_vendor_name, LDAP_VENDOR_NAME);
315 return (api.ldapai_vendor_version);
319 * Quoting rules.
322 /* rfc2253_quote - Quote input key for safe inclusion in the search base */
324 static void rfc2253_quote(DICT *unused, const char *name, VSTRING *result)
326 const char *sub = name;
327 size_t len;
330 * The RFC only requires quoting of a leading or trailing space, but it
331 * is harmless to quote whitespace everywhere. Similarly, we quote all
332 * '#' characters, even though only the leading '#' character requires
333 * quoting per the RFC.
335 while (*sub)
336 if ((len = strcspn(sub, " \t\"#+,;<>\\")) > 0) {
337 vstring_strncat(result, sub, len);
338 sub += len;
339 } else
340 vstring_sprintf_append(result, "\\%02X",
341 *((const unsigned char *) sub++));
344 /* rfc2254_quote - Quote input key for safe inclusion in the query filter */
346 static void rfc2254_quote(DICT *unused, const char *name, VSTRING *result)
348 const char *sub = name;
349 size_t len;
352 * If any characters in the supplied address should be escaped per RFC
353 * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
354 * Samuel Tardieu for spotting that wildcard searches were being done in
355 * the first place, which prompted the ill-conceived lookup_wildcards
356 * parameter and then this more comprehensive mechanism.
358 while (*sub)
359 if ((len = strcspn(sub, "*()\\")) > 0) {
360 vstring_strncat(result, sub, len);
361 sub += len;
362 } else
363 vstring_sprintf_append(result, "\\%02X",
364 *((const unsigned char *) sub++));
367 static BINHASH *conn_hash = 0;
369 #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
371 * LDAP connection timeout support.
373 static jmp_buf env;
375 static void dict_ldap_timeout(int unused_sig)
377 longjmp(env, 1);
380 #endif
382 static void dict_ldap_logprint(LDAP_CONST char *data)
384 const char *myname = "dict_ldap_debug";
385 char *buf, *p;
387 buf = mystrdup(data);
388 if (*buf) {
389 p = buf + strlen(buf) - 1;
390 while (p - buf >= 0 && ISSPACE(*p))
391 *p-- = 0;
393 msg_info("%s: %s", myname, buf);
394 myfree(buf);
397 static int dict_ldap_get_errno(LDAP *ld)
399 int rc;
401 if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_OPT_SUCCESS)
402 rc = LDAP_OTHER;
403 return rc;
406 static int dict_ldap_set_errno(LDAP *ld, int rc)
408 (void) ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &rc);
409 return rc;
412 /* dict_ldap_result - Read and parse LDAP result */
414 static int dict_ldap_result(LDAP *ld, int msgid, int timeout, LDAPMessage **res)
416 struct timeval mytimeval;
418 mytimeval.tv_sec = timeout;
419 mytimeval.tv_usec = 0;
421 #define GET_ALL 1
422 if (ldap_result(ld, msgid, GET_ALL, &mytimeval, res) == -1)
423 return (dict_ldap_get_errno(ld));
425 if (dict_ldap_get_errno(ld) == LDAP_TIMEOUT) {
426 (void) dict_ldap_abandon(ld, msgid);
427 return (dict_ldap_set_errno(ld, LDAP_TIMEOUT));
429 return LDAP_SUCCESS;
432 /* dict_ldap_bind_st - Synchronous simple auth with timeout */
434 static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
436 int rc;
437 int msgid;
438 LDAPMessage *res;
439 struct berval cred;
441 cred.bv_val = dict_ldap->bind_pw;
442 cred.bv_len = strlen(cred.bv_val);
443 if ((rc = ldap_sasl_bind(dict_ldap->ld, dict_ldap->bind_dn,
444 LDAP_SASL_SIMPLE, &cred,
445 0, 0, &msgid)) != LDAP_SUCCESS)
446 return (rc);
447 if ((rc = dict_ldap_result(dict_ldap->ld, msgid, dict_ldap->timeout,
448 &res)) != LDAP_SUCCESS)
449 return (rc);
451 #define FREE_RESULT 1
452 return (ldap_parse_sasl_bind_result(dict_ldap->ld, res, 0, FREE_RESULT));
455 /* search_st - Synchronous search with timeout */
457 static int search_st(LDAP *ld, char *base, int scope, char *query,
458 char **attrs, int timeout, LDAPMessage **res)
460 struct timeval mytimeval;
461 int msgid;
462 int rc;
463 int err;
465 mytimeval.tv_sec = timeout;
466 mytimeval.tv_usec = 0;
468 #define WANTVALS 0
469 #define USE_SIZE_LIM_OPT -1 /* Any negative value will do */
471 if ((rc = ldap_search_ext(ld, base, scope, query, attrs, WANTVALS, 0, 0,
472 &mytimeval, USE_SIZE_LIM_OPT,
473 &msgid)) != LDAP_SUCCESS)
474 return rc;
476 if ((rc = dict_ldap_result(ld, msgid, timeout, res)) != LDAP_SUCCESS)
477 return (rc);
479 #define DONT_FREE_RESULT 0
480 rc = ldap_parse_result(ld, *res, &err, 0, 0, 0, 0, DONT_FREE_RESULT);
481 return (err != LDAP_SUCCESS ? err : rc);
484 #ifdef LDAP_API_FEATURE_X_OPENLDAP
485 static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
487 const char *myname = "dict_ldap_set_tls_options";
488 int rc;
490 #ifdef LDAP_OPT_X_TLS_NEWCTX
491 int am_server = 0;
492 LDAP *ld = dict_ldap->ld;
494 #else
495 LDAP *ld = 0;
497 #endif
499 if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
500 if (*dict_ldap->tls_random_file) {
501 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_RANDOM_FILE,
502 dict_ldap->tls_random_file)) != LDAP_SUCCESS) {
503 msg_warn("%s: Unable to set tls_random_file to %s: %d: %s",
504 myname, dict_ldap->tls_random_file,
505 rc, ldap_err2string(rc));
506 return (-1);
509 if (*dict_ldap->tls_ca_cert_file) {
510 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE,
511 dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS) {
512 msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s",
513 myname, dict_ldap->tls_ca_cert_file,
514 rc, ldap_err2string(rc));
515 return (-1);
518 if (*dict_ldap->tls_ca_cert_dir) {
519 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR,
520 dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS) {
521 msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s",
522 myname, dict_ldap->tls_ca_cert_dir,
523 rc, ldap_err2string(rc));
524 return (-1);
527 if (*dict_ldap->tls_cert) {
528 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE,
529 dict_ldap->tls_cert)) != LDAP_SUCCESS) {
530 msg_warn("%s: Unable to set tls_cert to %s: %d: %s",
531 myname, dict_ldap->tls_cert,
532 rc, ldap_err2string(rc));
533 return (-1);
536 if (*dict_ldap->tls_key) {
537 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE,
538 dict_ldap->tls_key)) != LDAP_SUCCESS) {
539 msg_warn("%s: Unable to set tls_key to %s: %d: %s",
540 myname, dict_ldap->tls_key,
541 rc, ldap_err2string(rc));
542 return (-1);
545 if (*dict_ldap->tls_cipher_suite) {
546 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CIPHER_SUITE,
547 dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS) {
548 msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s",
549 myname, dict_ldap->tls_cipher_suite,
550 rc, ldap_err2string(rc));
551 return (-1);
554 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_REQUIRE_CERT,
555 &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS) {
556 msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s",
557 myname, dict_ldap->tls_require_cert,
558 rc, ldap_err2string(rc));
559 return (-1);
561 #ifdef LDAP_OPT_X_TLS_NEWCTX
562 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &am_server))
563 != LDAP_SUCCESS) {
564 msg_warn("%s: Unable to allocate new TLS context %d: %s",
565 myname, rc, ldap_err2string(rc));
566 return (-1);
568 #endif
570 return (0);
573 #endif
575 /* Establish a connection to the LDAP server. */
576 static int dict_ldap_connect(DICT_LDAP *dict_ldap)
578 const char *myname = "dict_ldap_connect";
579 int rc = 0;
581 #ifdef LDAP_OPT_NETWORK_TIMEOUT
582 struct timeval mytimeval;
584 #endif
586 #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
587 void (*saved_alarm) (int);
589 #endif
591 #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
592 if (dict_ldap->debuglevel > 0 &&
593 ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
594 (LDAP_CONST void *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
595 msg_warn("%s: Unable to set ber logprint function.", myname);
596 #if defined(LBER_OPT_DEBUG_LEVEL)
597 if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
598 &(dict_ldap->debuglevel)) != LBER_OPT_SUCCESS)
599 msg_warn("%s: Unable to set BER debug level.", myname);
600 #endif
601 if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
602 &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
603 msg_warn("%s: Unable to set LDAP debug level.", myname);
604 #endif
606 dict_errno = 0;
608 if (msg_verbose)
609 msg_info("%s: Connecting to server %s", myname,
610 dict_ldap->server_host);
612 #ifdef LDAP_OPT_NETWORK_TIMEOUT
613 #ifdef LDAP_API_FEATURE_X_OPENLDAP
614 ldap_initialize(&(dict_ldap->ld), dict_ldap->server_host);
615 #else
616 dict_ldap->ld = ldap_init(dict_ldap->server_host,
617 (int) dict_ldap->server_port);
618 #endif
619 if (dict_ldap->ld == NULL) {
620 msg_warn("%s: Unable to init LDAP server %s",
621 myname, dict_ldap->server_host);
622 dict_errno = DICT_ERR_RETRY;
623 return (-1);
625 mytimeval.tv_sec = dict_ldap->timeout;
626 mytimeval.tv_usec = 0;
627 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) !=
628 LDAP_OPT_SUCCESS) {
629 msg_warn("%s: Unable to set network timeout.", myname);
630 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
632 #else
633 if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
634 msg_warn("%s: Error setting signal handler for open timeout: %m",
635 myname);
636 dict_errno = DICT_ERR_RETRY;
637 return (-1);
639 alarm(dict_ldap->timeout);
640 if (setjmp(env) == 0)
641 dict_ldap->ld = ldap_open(dict_ldap->server_host,
642 (int) dict_ldap->server_port);
643 else
644 dict_ldap->ld = 0;
645 alarm(0);
647 if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
648 msg_warn("%s: Error resetting signal handler after open: %m",
649 myname);
650 dict_errno = DICT_ERR_RETRY;
651 return (-1);
653 if (dict_ldap->ld == NULL) {
654 msg_warn("%s: Unable to connect to LDAP server %s",
655 myname, dict_ldap->server_host);
656 dict_errno = DICT_ERR_RETRY;
657 return (-1);
659 #endif
662 * v3 support is needed for referral chasing. Thanks to Sami Haahtinen
663 * for the patch.
665 #ifdef LDAP_OPT_PROTOCOL_VERSION
666 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_PROTOCOL_VERSION,
667 &dict_ldap->version) != LDAP_OPT_SUCCESS) {
668 msg_warn("%s: Unable to set LDAP protocol version", myname);
669 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
671 if (msg_verbose) {
672 if (ldap_get_option(dict_ldap->ld,
673 LDAP_OPT_PROTOCOL_VERSION,
674 &dict_ldap->version) != LDAP_OPT_SUCCESS)
675 msg_warn("%s: Unable to get LDAP protocol version", myname);
676 else
677 msg_info("%s: Actual Protocol version used is %d.",
678 myname, dict_ldap->version);
680 #endif
683 * Limit the number of entries returned by each query.
685 if (dict_ldap->size_limit) {
686 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT,
687 &dict_ldap->size_limit) != LDAP_OPT_SUCCESS) {
688 msg_warn("%s: %s: Unable to set query result size limit to %ld.",
689 myname, dict_ldap->parser->name, dict_ldap->size_limit);
690 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
695 * Configure alias dereferencing for this connection. Thanks to Mike
696 * Mattice for this, and to Hery Rakotoarisoa for the v3 update.
698 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF,
699 &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS)
700 msg_warn("%s: Unable to set dereference option.", myname);
702 /* Chase referrals. */
704 #ifdef LDAP_OPT_REFERRALS
705 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_REFERRALS,
706 dict_ldap->chase_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF)
707 != LDAP_OPT_SUCCESS) {
708 msg_warn("%s: Unable to set Referral chasing.", myname);
709 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
711 #else
712 if (dict_ldap->chase_referrals) {
713 msg_warn("%s: Unable to set Referral chasing.", myname);
715 #endif
717 #ifdef LDAP_API_FEATURE_X_OPENLDAP
718 if (dict_ldap_set_tls_options(dict_ldap) != 0)
719 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
720 if (dict_ldap->start_tls) {
721 if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
722 msg_warn("%s: Error setting signal handler for STARTTLS timeout: %m",
723 myname);
724 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
726 alarm(dict_ldap->timeout);
727 if (setjmp(env) == 0)
728 rc = ldap_start_tls_s(dict_ldap->ld, NULL, NULL);
729 else {
730 rc = LDAP_TIMEOUT;
731 dict_ldap->ld = 0; /* Unknown state after
732 * longjmp() */
734 alarm(0);
736 if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
737 msg_warn("%s: Error resetting signal handler after STARTTLS: %m",
738 myname);
739 dict_errno = DICT_ERR_RETRY;
740 return (-1);
742 if (rc != LDAP_SUCCESS) {
743 msg_error("%s: Unable to set STARTTLS: %d: %s", myname,
744 rc, ldap_err2string(rc));
745 dict_errno = DICT_ERR_RETRY;
746 return (-1);
749 #endif
752 * If this server requires a bind, do so. Thanks to Sam Tardieu for
753 * noticing that the original bind call was broken.
755 if (dict_ldap->bind) {
756 if (msg_verbose)
757 msg_info("%s: Binding to server %s as dn %s",
758 myname, dict_ldap->server_host, dict_ldap->bind_dn);
760 rc = dict_ldap_bind_st(dict_ldap);
762 if (rc != LDAP_SUCCESS) {
763 msg_warn("%s: Unable to bind to server %s as %s: %d (%s)",
764 myname, dict_ldap->server_host, dict_ldap->bind_dn,
765 rc, ldap_err2string(rc));
766 DICT_LDAP_UNBIND_RETURN(dict_ldap->ld, DICT_ERR_RETRY, -1);
768 if (msg_verbose)
769 msg_info("%s: Successful bind to server %s as %s ",
770 myname, dict_ldap->server_host, dict_ldap->bind_dn);
772 /* Save connection handle in shared container */
773 DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
775 if (msg_verbose)
776 msg_info("%s: Cached connection handle for LDAP source %s",
777 myname, dict_ldap->parser->name);
779 return (0);
783 * Locate or allocate connection cache entry.
785 static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
787 VSTRING *keybuf = vstring_alloc(10);
788 char *key;
789 int len;
791 #ifdef LDAP_API_FEATURE_X_OPENLDAP
792 int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl;
794 #endif
795 LDAP_CONN *conn;
797 #define ADDSTR(vp, s) vstring_memcat((vp), (s), strlen((s))+1)
798 #define ADDINT(vp, i) vstring_sprintf_append((vp), "%lu", (unsigned long)(i))
800 ADDSTR(keybuf, dict_ldap->server_host);
801 ADDINT(keybuf, dict_ldap->server_port);
802 ADDINT(keybuf, dict_ldap->bind);
803 ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_dn : "");
804 ADDSTR(keybuf, dict_ldap->bind ? dict_ldap->bind_pw : "");
805 ADDINT(keybuf, dict_ldap->dereference);
806 ADDINT(keybuf, dict_ldap->chase_referrals);
807 ADDINT(keybuf, dict_ldap->debuglevel);
808 ADDINT(keybuf, dict_ldap->version);
809 #ifdef LDAP_API_FEATURE_X_OPENLDAP
810 ADDINT(keybuf, dict_ldap->ldap_ssl);
811 ADDINT(keybuf, dict_ldap->start_tls);
812 ADDINT(keybuf, sslon ? dict_ldap->tls_require_cert : 0);
813 ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_file : "");
814 ADDSTR(keybuf, sslon ? dict_ldap->tls_ca_cert_dir : "");
815 ADDSTR(keybuf, sslon ? dict_ldap->tls_cert : "");
816 ADDSTR(keybuf, sslon ? dict_ldap->tls_key : "");
817 ADDSTR(keybuf, sslon ? dict_ldap->tls_random_file : "");
818 ADDSTR(keybuf, sslon ? dict_ldap->tls_cipher_suite : "");
819 #endif
821 key = vstring_str(keybuf);
822 len = VSTRING_LEN(keybuf);
824 if (conn_hash == 0)
825 conn_hash = binhash_create(0);
827 if ((dict_ldap->ht = binhash_locate(conn_hash, key, len)) == 0) {
828 conn = (LDAP_CONN *) mymalloc(sizeof(LDAP_CONN));
829 conn->conn_ld = 0;
830 conn->conn_refcount = 0;
831 dict_ldap->ht = binhash_enter(conn_hash, key, len, (char *) conn);
833 ++DICT_LDAP_CONN(dict_ldap)->conn_refcount;
835 vstring_free(keybuf);
839 * dict_ldap_get_values: for each entry returned by a search, get the values
840 * of all its attributes. Recurses to resolve any DN or URL values found.
842 * This and the rest of the handling of multiple attributes, DNs and URLs
843 * are thanks to LaMont Jones.
845 static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage *res,
846 VSTRING *result, const char *name)
848 static int recursion = 0;
849 static int expansion;
850 long entries = 0;
851 long i = 0;
852 int rc = 0;
853 LDAPMessage *resloop = 0;
854 LDAPMessage *entry = 0;
855 BerElement *ber;
856 char *attr;
857 struct berval **vals;
858 int valcount;
859 LDAPURLDesc *url;
860 const char *myname = "dict_ldap_get_values";
861 int is_leaf = 1; /* No recursion via this entry */
862 int is_terminal = 0; /* No expansion via this entry */
864 if (++recursion == 1)
865 expansion = 0;
867 if (msg_verbose)
868 msg_info("%s[%d]: Search found %d match(es)", myname, recursion,
869 ldap_count_entries(dict_ldap->ld, res));
871 for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
872 entry = ldap_next_entry(dict_ldap->ld, entry)) {
873 ber = NULL;
876 * LDAP should not, but may produce more than the requested maximum
877 * number of entries.
879 if (dict_errno == 0
880 && dict_ldap->size_limit
881 && ++entries > dict_ldap->size_limit) {
882 msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded",
883 myname, recursion, dict_ldap->parser->name,
884 dict_ldap->size_limit);
885 dict_errno = DICT_ERR_RETRY;
889 * Check for terminal attributes, these preclude expansion of all
890 * other attributes, and DN/URI recursion. Any terminal attributes
891 * are listed first in the attribute array.
893 if (dict_ldap->num_terminal > 0) {
894 for (i = 0; i < dict_ldap->num_terminal; ++i) {
895 attr = dict_ldap->result_attributes->argv[i];
896 if (!(vals = ldap_get_values_len(dict_ldap->ld, entry, attr)))
897 continue;
898 is_terminal = (ldap_count_values_len(vals) > 0);
899 ldap_value_free_len(vals);
900 if (is_terminal)
901 break;
906 * Check for special attributes, these preclude expansion of
907 * "leaf-only" attributes, and are at the end of the attribute array
908 * after the terminal, leaf and regular attributes.
910 if (is_terminal == 0 && dict_ldap->num_leaf > 0) {
911 for (i = dict_ldap->num_attributes;
912 dict_ldap->result_attributes->argv[i]; ++i) {
913 attr = dict_ldap->result_attributes->argv[i];
914 if (!(vals = ldap_get_values_len(dict_ldap->ld, entry, attr)))
915 continue;
916 is_leaf = (ldap_count_values_len(vals) == 0);
917 ldap_value_free_len(vals);
918 if (!is_leaf)
919 break;
922 for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber);
923 attr != NULL; ldap_memfree(attr),
924 attr = ldap_next_attribute(dict_ldap->ld, entry, ber)) {
926 vals = ldap_get_values_len(dict_ldap->ld, entry, attr);
927 if (vals == NULL) {
928 if (msg_verbose)
929 msg_info("%s[%d]: Entry doesn't have any values for %s",
930 myname, recursion, attr);
931 continue;
933 valcount = ldap_count_values_len(vals);
936 * If we previously encountered an error, we still continue
937 * through the loop, to avoid memory leaks, but we don't waste
938 * time accumulating any further results.
940 * XXX: There may be a more efficient way to exit the loop with no
941 * leaks, but it will likely be more fragile and not worth the
942 * extra code.
944 if (dict_errno != 0 || valcount == 0) {
945 ldap_value_free_len(vals);
946 continue;
950 * The "result_attributes" list enumerates all the requested
951 * attributes, first the ordinary result attribtutes and then the
952 * special result attributes that hold DN or LDAP URL values.
954 * The number of ordinary attributes is "num_attributes".
956 * We compute the attribute type (ordinary or special) from its
957 * index on the "result_attributes" list.
959 for (i = 0; dict_ldap->result_attributes->argv[i]; i++)
960 if (strcasecmp(dict_ldap->result_attributes->argv[i],
961 attr) == 0)
962 break;
965 * Append each returned address to the result list, possibly
966 * recursing (for dn or url attributes of non-terminal entries)
968 if (i < dict_ldap->num_attributes || is_terminal) {
969 if ((is_terminal && i >= dict_ldap->num_terminal)
970 || (!is_leaf &&
971 i < dict_ldap->num_terminal + dict_ldap->num_leaf)) {
972 if (msg_verbose)
973 msg_info("%s[%d]: skipping %ld value(s) of %s "
974 "attribute %s", myname, recursion, i,
975 is_terminal ? "non-terminal" : "leaf-only",
976 attr);
977 } else {
978 /* Ordinary result attribute */
979 for (i = 0; i < valcount; i++) {
980 if (db_common_expand(dict_ldap->ctx,
981 dict_ldap->result_format,
982 vals[i]->bv_val,
983 name, result, 0)
984 && dict_ldap->expansion_limit > 0
985 && ++expansion > dict_ldap->expansion_limit) {
986 msg_warn("%s[%d]: %s: Expansion limit exceeded "
987 "for key: '%s'", myname, recursion,
988 dict_ldap->parser->name, name);
989 dict_errno = DICT_ERR_RETRY;
990 break;
993 if (dict_errno != 0)
994 continue;
995 if (msg_verbose)
996 msg_info("%s[%d]: search returned %ld value(s) for"
997 " requested result attribute %s",
998 myname, recursion, i, attr);
1000 } else if (recursion < dict_ldap->recursion_limit
1001 && dict_ldap->result_attributes->argv[i]) {
1002 /* Special result attribute */
1003 for (i = 0; i < valcount; i++) {
1004 if (ldap_is_ldap_url(vals[i]->bv_val)) {
1005 if (msg_verbose)
1006 msg_info("%s[%d]: looking up URL %s", myname,
1007 recursion, vals[i]->bv_val);
1008 rc = ldap_url_parse(vals[i]->bv_val, &url);
1009 if (rc == 0) {
1010 rc = search_st(dict_ldap->ld, url->lud_dn,
1011 url->lud_scope, url->lud_filter,
1012 url->lud_attrs, dict_ldap->timeout,
1013 &resloop);
1014 ldap_free_urldesc(url);
1016 } else {
1017 if (msg_verbose)
1018 msg_info("%s[%d]: looking up DN %s",
1019 myname, recursion, vals[i]->bv_val);
1020 rc = search_st(dict_ldap->ld, vals[i]->bv_val,
1021 LDAP_SCOPE_BASE, "objectclass=*",
1022 dict_ldap->result_attributes->argv,
1023 dict_ldap->timeout, &resloop);
1025 switch (rc) {
1026 case LDAP_SUCCESS:
1027 dict_ldap_get_values(dict_ldap, resloop, result, name);
1028 break;
1029 case LDAP_NO_SUCH_OBJECT:
1032 * Go ahead and treat this as though the DN existed
1033 * and just didn't have any result attributes.
1035 msg_warn("%s[%d]: DN %s not found, skipping ", myname,
1036 recursion, vals[i]->bv_val);
1037 break;
1038 default:
1039 msg_warn("%s[%d]: search error %d: %s ", myname,
1040 recursion, rc, ldap_err2string(rc));
1041 dict_errno = DICT_ERR_RETRY;
1042 break;
1045 if (resloop != 0)
1046 ldap_msgfree(resloop);
1048 if (dict_errno != 0)
1049 break;
1051 if (dict_errno != 0)
1052 continue;
1053 if (msg_verbose)
1054 msg_info("%s[%d]: search returned %ld value(s) for"
1055 " special result attribute %s",
1056 myname, recursion, i, attr);
1057 } else if (recursion >= dict_ldap->recursion_limit
1058 && dict_ldap->result_attributes->argv[i]) {
1059 msg_warn("%s[%d]: %s: Recursion limit exceeded"
1060 " for special attribute %s=%s", myname, recursion,
1061 dict_ldap->parser->name, attr, vals[0]->bv_val);
1062 dict_errno = DICT_ERR_RETRY;
1064 ldap_value_free_len(vals);
1066 if (ber)
1067 ber_free(ber, 0);
1070 if (msg_verbose)
1071 msg_info("%s[%d]: Leaving %s", myname, recursion, myname);
1072 --recursion;
1075 /* dict_ldap_lookup - find database entry */
1077 static const char *dict_ldap_lookup(DICT *dict, const char *name)
1079 const char *myname = "dict_ldap_lookup";
1080 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
1081 LDAPMessage *res = 0;
1082 static VSTRING *base;
1083 static VSTRING *query;
1084 static VSTRING *result;
1085 int rc = 0;
1086 int sizelimit;
1088 dict_errno = 0;
1090 if (msg_verbose)
1091 msg_info("%s: In dict_ldap_lookup", myname);
1094 * Optionally fold the key.
1096 if (dict->flags & DICT_FLAG_FOLD_FIX) {
1097 if (dict->fold_buf == 0)
1098 dict->fold_buf = vstring_alloc(10);
1099 vstring_strcpy(dict->fold_buf, name);
1100 name = lowercase(vstring_str(dict->fold_buf));
1104 * If they specified a domain list for this map, then only search for
1105 * addresses in domains on the list. This can significantly reduce the
1106 * load on the LDAP server.
1108 if (db_common_check_domain(dict_ldap->ctx, name) == 0) {
1109 if (msg_verbose)
1110 msg_info("%s: Skipping lookup of '%s'", myname, name);
1111 return (0);
1113 #define INIT_VSTR(buf, len) do { \
1114 if (buf == 0) \
1115 buf = vstring_alloc(len); \
1116 VSTRING_RESET(buf); \
1117 VSTRING_TERMINATE(buf); \
1118 } while (0)
1120 INIT_VSTR(base, 10);
1121 INIT_VSTR(query, 10);
1122 INIT_VSTR(result, 10);
1125 * Because the connection may be shared and invalidated via queries for
1126 * another map, update private copy of "ld" from shared connection
1127 * container.
1129 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;
1132 * Connect to the LDAP server, if necessary.
1134 if (dict_ldap->ld == NULL) {
1135 if (msg_verbose)
1136 msg_info
1137 ("%s: No existing connection for LDAP source %s, reopening",
1138 myname, dict_ldap->parser->name);
1140 dict_ldap_connect(dict_ldap);
1143 * if dict_ldap_connect() set dict_errno, abort.
1145 if (dict_errno)
1146 return (0);
1147 } else if (msg_verbose)
1148 msg_info("%s: Using existing connection for LDAP source %s",
1149 myname, dict_ldap->parser->name);
1152 * Connection caching, means that the connection handle may have the
1153 * wrong size limit. Re-adjust before each query. This is cheap, just
1154 * sets a field in the ldap connection handle. We also do this in the
1155 * connect code, because we sometimes reconnect (below) in the middle of
1156 * a query.
1158 sizelimit = dict_ldap->size_limit ? dict_ldap->size_limit : LDAP_NO_LIMIT;
1159 if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
1160 != LDAP_OPT_SUCCESS) {
1161 msg_warn("%s: %s: Unable to set query result size limit to %ld.",
1162 myname, dict_ldap->parser->name, dict_ldap->size_limit);
1163 dict_errno = DICT_ERR_RETRY;
1164 return (0);
1168 * Expand the search base and query. Skip lookup when the input key lacks
1169 * sufficient domain components to satisfy all the requested
1170 * %-substitutions.
1172 * When the search base is not static, LDAP_NO_SUCH_OBJECT is expected and
1173 * is therefore treated as a non-error: the lookup returns no results
1174 * rather than a soft error.
1176 if (!db_common_expand(dict_ldap->ctx, dict_ldap->search_base,
1177 name, 0, base, rfc2253_quote)) {
1178 if (msg_verbose > 1)
1179 msg_info("%s: %s: Empty expansion for %s", myname,
1180 dict_ldap->parser->name, dict_ldap->search_base);
1181 return (0);
1183 if (!db_common_expand(dict_ldap->ctx, dict_ldap->query,
1184 name, 0, query, rfc2254_quote)) {
1185 if (msg_verbose > 1)
1186 msg_info("%s: %s: Empty expansion for %s", myname,
1187 dict_ldap->parser->name, dict_ldap->query);
1188 return (0);
1192 * On to the search.
1194 if (msg_verbose)
1195 msg_info("%s: %s: Searching with filter %s", myname,
1196 dict_ldap->parser->name, vstring_str(query));
1198 rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope,
1199 vstring_str(query), dict_ldap->result_attributes->argv,
1200 dict_ldap->timeout, &res);
1202 if (rc == LDAP_SERVER_DOWN) {
1203 if (msg_verbose)
1204 msg_info("%s: Lost connection for LDAP source %s, reopening",
1205 myname, dict_ldap->parser->name);
1207 dict_ldap_unbind(dict_ldap->ld);
1208 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
1209 dict_ldap_connect(dict_ldap);
1212 * if dict_ldap_connect() set dict_errno, abort.
1214 if (dict_errno)
1215 return (0);
1217 rc = search_st(dict_ldap->ld, vstring_str(base), dict_ldap->scope,
1218 vstring_str(query), dict_ldap->result_attributes->argv,
1219 dict_ldap->timeout, &res);
1222 switch (rc) {
1224 case LDAP_SUCCESS:
1227 * Search worked; extract the requested result_attribute.
1230 dict_ldap_get_values(dict_ldap, res, result, name);
1233 * OpenLDAP's ldap_next_attribute returns a bogus
1234 * LDAP_DECODING_ERROR; I'm ignoring that for now.
1237 rc = dict_ldap_get_errno(dict_ldap->ld);
1238 if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR)
1239 msg_warn
1240 ("%s: Had some trouble with entries returned by search: %s",
1241 myname, ldap_err2string(rc));
1243 if (msg_verbose)
1244 msg_info("%s: Search returned %s", myname,
1245 VSTRING_LEN(result) >
1246 0 ? vstring_str(result) : "nothing");
1247 break;
1249 case LDAP_NO_SUCH_OBJECT:
1252 * If the search base is input key dependent, then not finding it, is
1253 * equivalent to not finding the input key. Sadly, we cannot detect
1254 * misconfiguration in this case.
1256 if (dict_ldap->dynamic_base)
1257 break;
1259 msg_warn("%s: %s: Search base '%s' not found: %d: %s",
1260 myname, dict_ldap->parser->name,
1261 vstring_str(base), rc, ldap_err2string(rc));
1262 dict_errno = DICT_ERR_RETRY;
1263 break;
1265 default:
1268 * Rats. The search didn't work.
1270 msg_warn("%s: Search error %d: %s ", myname, rc,
1271 ldap_err2string(rc));
1274 * Tear down the connection so it gets set up from scratch on the
1275 * next lookup.
1277 dict_ldap_unbind(dict_ldap->ld);
1278 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
1281 * And tell the caller to try again later.
1283 dict_errno = DICT_ERR_RETRY;
1284 break;
1288 * Cleanup.
1290 if (res != 0)
1291 ldap_msgfree(res);
1294 * If we had an error, return nothing, Otherwise, return the result, if
1295 * any.
1297 return (VSTRING_LEN(result) > 0 && !dict_errno ? vstring_str(result) : 0);
1300 /* dict_ldap_close - disassociate from data base */
1302 static void dict_ldap_close(DICT *dict)
1304 const char *myname = "dict_ldap_close";
1305 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
1306 LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
1307 BINHASH_INFO *ht = dict_ldap->ht;
1309 if (--conn->conn_refcount == 0) {
1310 if (conn->conn_ld) {
1311 if (msg_verbose)
1312 msg_info("%s: Closed connection handle for LDAP source %s",
1313 myname, dict_ldap->parser->name);
1314 dict_ldap_unbind(conn->conn_ld);
1316 binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
1318 cfg_parser_free(dict_ldap->parser);
1319 myfree(dict_ldap->server_host);
1320 myfree(dict_ldap->search_base);
1321 myfree(dict_ldap->query);
1322 if (dict_ldap->result_format)
1323 myfree(dict_ldap->result_format);
1324 argv_free(dict_ldap->result_attributes);
1325 myfree(dict_ldap->bind_dn);
1326 myfree(dict_ldap->bind_pw);
1327 if (dict_ldap->ctx)
1328 db_common_free_ctx(dict_ldap->ctx);
1329 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1330 myfree(dict_ldap->tls_ca_cert_file);
1331 myfree(dict_ldap->tls_ca_cert_dir);
1332 myfree(dict_ldap->tls_cert);
1333 myfree(dict_ldap->tls_key);
1334 myfree(dict_ldap->tls_random_file);
1335 myfree(dict_ldap->tls_cipher_suite);
1336 #endif
1337 if (dict->fold_buf)
1338 vstring_free(dict->fold_buf);
1339 dict_free(dict);
1342 /* dict_ldap_open - create association with data base */
1344 DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
1346 const char *myname = "dict_ldap_open";
1347 DICT_LDAP *dict_ldap;
1348 VSTRING *url_list;
1349 char *s;
1350 char *h;
1351 char *server_host;
1352 char *scope;
1353 char *attr;
1354 int tmp;
1355 int vendor_version = dict_ldap_vendor_version();
1357 if (msg_verbose)
1358 msg_info("%s: Using LDAP source %s", myname, ldapsource);
1360 dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource,
1361 sizeof(*dict_ldap));
1362 dict_ldap->dict.lookup = dict_ldap_lookup;
1363 dict_ldap->dict.close = dict_ldap_close;
1364 dict_ldap->dict.flags = dict_flags;
1366 dict_ldap->ld = NULL;
1367 dict_ldap->parser = cfg_parser_alloc(ldapsource);
1369 server_host = cfg_get_str(dict_ldap->parser, "server_host",
1370 "localhost", 1, 0);
1373 * get configured value of "server_port"; default to LDAP_PORT (389)
1375 dict_ldap->server_port =
1376 cfg_get_int(dict_ldap->parser, "server_port", LDAP_PORT, 0, 0);
1379 * Define LDAP Protocol Version.
1381 dict_ldap->version = cfg_get_int(dict_ldap->parser, "version", 2, 2, 0);
1382 switch (dict_ldap->version) {
1383 case 2:
1384 dict_ldap->version = LDAP_VERSION2;
1385 break;
1386 case 3:
1387 dict_ldap->version = LDAP_VERSION3;
1388 break;
1389 default:
1390 msg_warn("%s: %s Unknown version %d, using 2.", myname, ldapsource,
1391 dict_ldap->version);
1392 dict_ldap->version = LDAP_VERSION2;
1395 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
1396 dict_ldap->ldap_ssl = 0;
1397 #endif
1399 url_list = vstring_alloc(32);
1400 s = server_host;
1401 while ((h = mystrtok(&s, " \t\n\r,")) != NULL) {
1402 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
1405 * Convert (host, port) pairs to LDAP URLs
1407 if (ldap_is_ldap_url(h)) {
1408 LDAPURLDesc *url_desc;
1409 int rc;
1411 if ((rc = ldap_url_parse(h, &url_desc)) != 0) {
1412 msg_error("%s: error parsing URL %s: %d: %s; skipping", myname,
1413 h, rc, ldap_err2string(rc));
1414 continue;
1416 if (strcasecmp(url_desc->lud_scheme, "ldap") != 0 &&
1417 dict_ldap->version != LDAP_VERSION3) {
1418 msg_warn("%s: URL scheme %s requires protocol version 3", myname,
1419 url_desc->lud_scheme);
1420 dict_ldap->version = LDAP_VERSION3;
1422 if (strcasecmp(url_desc->lud_scheme, "ldaps") == 0)
1423 dict_ldap->ldap_ssl = 1;
1424 ldap_free_urldesc(url_desc);
1425 if (VSTRING_LEN(url_list) > 0)
1426 VSTRING_ADDCH(url_list, ' ');
1427 vstring_strcat(url_list, h);
1428 } else {
1429 if (VSTRING_LEN(url_list) > 0)
1430 VSTRING_ADDCH(url_list, ' ');
1431 if (strrchr(h, ':'))
1432 vstring_sprintf_append(url_list, "ldap://%s", h);
1433 else
1434 vstring_sprintf_append(url_list, "ldap://%s:%d", h,
1435 dict_ldap->server_port);
1437 #else
1438 if (VSTRING_LEN(url_list) > 0)
1439 VSTRING_ADDCH(url_list, ' ');
1440 vstring_strcat(url_list, h);
1441 #endif
1443 VSTRING_TERMINATE(url_list);
1444 dict_ldap->server_host = vstring_export(url_list);
1446 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
1449 * With URL scheme, clear port to normalize connection cache key
1451 dict_ldap->server_port = LDAP_PORT;
1452 if (msg_verbose)
1453 msg_info("%s: %s server_host URL is %s", myname, ldapsource,
1454 dict_ldap->server_host);
1455 #endif
1456 myfree(server_host);
1459 * Scope handling thanks to Carsten Hoeger of SuSE.
1461 scope = cfg_get_str(dict_ldap->parser, "scope", "sub", 1, 0);
1463 if (strcasecmp(scope, "one") == 0) {
1464 dict_ldap->scope = LDAP_SCOPE_ONELEVEL;
1465 } else if (strcasecmp(scope, "base") == 0) {
1466 dict_ldap->scope = LDAP_SCOPE_BASE;
1467 } else if (strcasecmp(scope, "sub") == 0) {
1468 dict_ldap->scope = LDAP_SCOPE_SUBTREE;
1469 } else {
1470 msg_warn("%s: %s: Unrecognized value %s specified for scope; using sub",
1471 myname, ldapsource, scope);
1472 dict_ldap->scope = LDAP_SCOPE_SUBTREE;
1475 myfree(scope);
1477 dict_ldap->search_base = cfg_get_str(dict_ldap->parser, "search_base",
1478 "", 0, 0);
1481 * get configured value of "timeout"; default to 10 seconds
1483 * Thanks to Manuel Guesdon for spotting that this wasn't really getting
1484 * set.
1486 dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout", 10, 0, 0);
1488 #if 0 /* No benefit from changing
1489 * this to match the
1490 * MySQL/PGSQL syntax */
1491 if ((dict_ldap->query =
1492 cfg_get_str(dict_ldap->parser, "query", 0, 0, 0)) == 0)
1493 #endif
1494 dict_ldap->query =
1495 cfg_get_str(dict_ldap->parser, "query_filter",
1496 "(mailacceptinggeneralid=%s)", 0, 0);
1498 if ((dict_ldap->result_format =
1499 cfg_get_str(dict_ldap->parser, "result_format", 0, 0, 0)) == 0)
1500 dict_ldap->result_format =
1501 cfg_get_str(dict_ldap->parser, "result_filter", "%s", 1, 0);
1504 * Must parse all templates before we can use db_common_expand() If data
1505 * dependent substitutions are found in the search base, treat
1506 * NO_SUCH_OBJECT search errors as a non-matching key, rather than a
1507 * fatal run-time error.
1509 dict_ldap->ctx = 0;
1510 dict_ldap->dynamic_base =
1511 db_common_parse(&dict_ldap->dict, &dict_ldap->ctx,
1512 dict_ldap->search_base, 1);
1513 if (!db_common_parse(0, &dict_ldap->ctx, dict_ldap->query, 1)) {
1514 msg_warn("%s: %s: Fixed query_filter %s is probably useless",
1515 myname, ldapsource, dict_ldap->query);
1517 (void) db_common_parse(0, &dict_ldap->ctx, dict_ldap->result_format, 0);
1518 db_common_parse_domain(dict_ldap->parser, dict_ldap->ctx);
1521 * Maps that use substring keys should only be used with the full input
1522 * key.
1524 if (db_common_dict_partial(dict_ldap->ctx))
1525 dict_ldap->dict.flags |= DICT_FLAG_PATTERN;
1526 else
1527 dict_ldap->dict.flags |= DICT_FLAG_FIXED;
1528 if (dict_flags & DICT_FLAG_FOLD_FIX)
1529 dict_ldap->dict.fold_buf = vstring_alloc(10);
1531 /* Order matters, first the terminal attributes: */
1532 attr = cfg_get_str(dict_ldap->parser, "terminal_result_attribute", "", 0, 0);
1533 dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n");
1534 dict_ldap->num_terminal = dict_ldap->result_attributes->argc;
1535 myfree(attr);
1537 /* Order matters, next the leaf-only attributes: */
1538 attr = cfg_get_str(dict_ldap->parser, "leaf_result_attribute", "", 0, 0);
1539 if (*attr)
1540 argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
1541 dict_ldap->num_leaf =
1542 dict_ldap->result_attributes->argc - dict_ldap->num_terminal;
1543 myfree(attr);
1545 /* Order matters, next the regular attributes: */
1546 attr = cfg_get_str(dict_ldap->parser, "result_attribute", "maildrop", 0, 0);
1547 if (*attr)
1548 argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
1549 dict_ldap->num_attributes = dict_ldap->result_attributes->argc;
1550 myfree(attr);
1552 /* Order matters, finally the special attributes: */
1553 attr = cfg_get_str(dict_ldap->parser, "special_result_attribute", "", 0, 0);
1554 if (*attr)
1555 argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
1556 myfree(attr);
1559 * get configured value of "bind"; default to true
1561 dict_ldap->bind = cfg_get_bool(dict_ldap->parser, "bind", 1);
1564 * get configured value of "bind_dn"; default to ""
1566 dict_ldap->bind_dn = cfg_get_str(dict_ldap->parser, "bind_dn", "", 0, 0);
1569 * get configured value of "bind_pw"; default to ""
1571 dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw", "", 0, 0);
1574 * LDAP message caching never worked and is no longer supported.
1576 tmp = cfg_get_bool(dict_ldap->parser, "cache", 0);
1577 if (tmp)
1578 msg_warn("%s: %s ignoring cache", myname, ldapsource);
1580 tmp = cfg_get_int(dict_ldap->parser, "cache_expiry", -1, 0, 0);
1581 if (tmp >= 0)
1582 msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource);
1584 tmp = cfg_get_int(dict_ldap->parser, "cache_size", -1, 0, 0);
1585 if (tmp >= 0)
1586 msg_warn("%s: %s ignoring cache_size", myname, ldapsource);
1588 dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser,
1589 "recursion_limit", 1000, 1, 0);
1592 * XXX: The default should be non-zero for safety, but that is not
1593 * backwards compatible.
1595 dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser,
1596 "expansion_limit", 0, 0, 0);
1598 dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit",
1599 dict_ldap->expansion_limit, 0, 0);
1602 * Alias dereferencing suggested by Mike Mattice.
1604 dict_ldap->dereference = cfg_get_int(dict_ldap->parser, "dereference",
1605 0, 0, 0);
1606 if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) {
1607 msg_warn("%s: %s Unrecognized value %d specified for dereference; using 0",
1608 myname, ldapsource, dict_ldap->dereference);
1609 dict_ldap->dereference = 0;
1611 /* Referral chasing */
1612 dict_ldap->chase_referrals = cfg_get_bool(dict_ldap->parser,
1613 "chase_referrals", 0);
1615 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1618 * TLS options
1620 /* get configured value of "start_tls"; default to no */
1621 dict_ldap->start_tls = cfg_get_bool(dict_ldap->parser, "start_tls", 0);
1622 if (dict_ldap->start_tls) {
1623 if (dict_ldap->version < LDAP_VERSION3) {
1624 msg_warn("%s: %s start_tls requires protocol version 3",
1625 myname, ldapsource);
1626 dict_ldap->version = LDAP_VERSION3;
1628 /* Binary incompatibility in the OpenLDAP API from 2.0.11 to 2.0.12 */
1629 if (((LDAP_VENDOR_VERSION <= 20011) && !(vendor_version <= 20011))
1630 || (!(LDAP_VENDOR_VERSION <= 20011) && (vendor_version <= 20011)))
1631 msg_fatal("%s: incompatible TLS support: "
1632 "compile-time OpenLDAP version %d, "
1633 "run-time OpenLDAP version %d",
1634 myname, LDAP_VENDOR_VERSION, vendor_version);
1636 /* get configured value of "tls_require_cert"; default to no */
1637 dict_ldap->tls_require_cert =
1638 cfg_get_bool(dict_ldap->parser, "tls_require_cert", 0) ?
1639 LDAP_OPT_X_TLS_DEMAND : LDAP_OPT_X_TLS_NEVER;
1641 /* get configured value of "tls_ca_cert_file"; default "" */
1642 dict_ldap->tls_ca_cert_file = cfg_get_str(dict_ldap->parser,
1643 "tls_ca_cert_file", "", 0, 0);
1645 /* get configured value of "tls_ca_cert_dir"; default "" */
1646 dict_ldap->tls_ca_cert_dir = cfg_get_str(dict_ldap->parser,
1647 "tls_ca_cert_dir", "", 0, 0);
1649 /* get configured value of "tls_cert"; default "" */
1650 dict_ldap->tls_cert = cfg_get_str(dict_ldap->parser, "tls_cert",
1651 "", 0, 0);
1653 /* get configured value of "tls_key"; default "" */
1654 dict_ldap->tls_key = cfg_get_str(dict_ldap->parser, "tls_key",
1655 "", 0, 0);
1657 /* get configured value of "tls_random_file"; default "" */
1658 dict_ldap->tls_random_file = cfg_get_str(dict_ldap->parser,
1659 "tls_random_file", "", 0, 0);
1661 /* get configured value of "tls_cipher_suite"; default "" */
1662 dict_ldap->tls_cipher_suite = cfg_get_str(dict_ldap->parser,
1663 "tls_cipher_suite", "", 0, 0);
1664 #endif
1667 * Debug level.
1669 #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
1670 dict_ldap->debuglevel = cfg_get_int(dict_ldap->parser, "debuglevel",
1671 0, 0, 0);
1672 #endif
1675 * Find or allocate shared LDAP connection container.
1677 dict_ldap_conn_find(dict_ldap);
1680 * Return the new dict_ldap structure.
1682 return (DICT_DEBUG (&dict_ldap->dict));
1685 #endif