7 /* dictionary manager interface to LDAP maps
9 /* #include <dict_ldap.h>
11 /* DICT *dict_ldap_open(attribute, dummy, dict_flags)
12 /* const char *ldapsource;
16 /* dict_ldap_open() makes LDAP user information accessible via
17 /* the generic dictionary operations described in dict_open(3).
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.
34 /* Not used; this argument exists only for compatibility with
35 /* the dict_open(3) interface.
37 /* Configuration parameters:
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
43 /* The port the LDAP server listens on.
45 /* The LDAP search base, for example: \fIO=organization name, C=country\fR.
47 /* If specified, only lookups ending in this value will be queried.
48 /* This can significantly reduce the query load on the LDAP server.
50 /* Deadline for LDAP open() and LDAP search() .
52 /* The search filter template used to search for directory entries,
53 /* for example \fI(mailacceptinggeneralid=%s)\fR. See ldap_table(5)
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
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.
74 /* LDAP search scope: sub, base, or one.
76 /* Whether or not to bind to the server -- LDAP v3 implementations don't
77 /* require it, which saves some overhead.
79 /* If you must bind to the server, do it with this distinguished name ...
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.
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.
104 /* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
106 /* Specifies the LDAP protocol version to use. Default is version
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
116 /* .IP tls_ca_cert_dir
117 /* Directory containing X509 Certificate Authority certificates
118 /* in separate individual files.
120 /* File containing client's X509 certificate.
122 /* File containing the private key corresponding to
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.
137 /* Debug level. See 'loglevel' option in slapd.conf(5) man page.
138 /* Currently only in openldap libraries (and derivatives).
140 /* dict(3) generic dictionary manager
143 /* VSNL, Bombay, India.
144 /* prabhat@giasbm01.vsnl.net.in
147 /* IBM T.J. Watson Research
149 /* Yorktown Heights, NY 10532, USA
152 /* john@sunislelodge.com
154 /* Current maintainers:
164 /* Institute of Mathematics of the Romanian Academy
166 /* RO-014700 Bucharest, ROMANIA
169 /* System library. */
171 #include "sys_defs.h"
175 #include <sys/time.h>
186 #ifdef STRCASECMP_IN_STRINGS_H
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"
197 /* Handle differences between LDAP SDK's constant definitions */
199 #define LDAP_CONST const
201 #ifndef LDAP_OPT_SUCCESS
202 #define LDAP_OPT_SUCCESS 0
205 /* Utility library. */
208 #include <mymalloc.h>
211 #include <stringops.h>
214 /* Global library. */
216 #include "cfg_parser.h"
217 #include "db_common.h"
219 /* Application-specific. */
221 #include "dict_ldap.h"
229 * Structure containing all the configuration parameters for a given
230 * LDAP source, plus its connection handle.
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? */
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 */
253 long recursion_limit
;
258 #ifdef LDAP_API_FEATURE_X_OPENLDAP
261 int tls_require_cert
;
262 char *tls_ca_cert_file
;
263 char *tls_ca_cert_dir
;
266 char *tls_random_file
;
267 char *tls_cipher_suite
;
269 BINHASH_INFO
*ht
; /* hash entry for LDAP connection */
270 LDAP
*ld
; /* duplicated from conn->conn_ld */
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); \
278 dict_errno = (__err); \
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)
289 #define dict_ldap_unbind(ld) ldap_unbind(ld)
290 #define dict_ldap_abandon(ld, msg) ldap_abandon((ld), (msg))
293 static int dict_ldap_vendor_version(void)
295 const char *myname
= "dict_ldap_api_info";
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
);
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
);
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
;
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.
336 if ((len
= strcspn(sub
, " \t\"#+,;<>\\")) > 0) {
337 vstring_strncat(result
, sub
, len
);
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
;
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.
359 if ((len
= strcspn(sub
, "*()\\")) > 0) {
360 vstring_strncat(result
, sub
, len
);
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.
375 static void dict_ldap_timeout(int unused_sig
)
382 static void dict_ldap_logprint(LDAP_CONST
char *data
)
384 const char *myname
= "dict_ldap_debug";
387 buf
= mystrdup(data
);
389 p
= buf
+ strlen(buf
) - 1;
390 while (p
- buf
>= 0 && ISSPACE(*p
))
393 msg_info("%s: %s", myname
, buf
);
397 static int dict_ldap_get_errno(LDAP
*ld
)
401 if (ldap_get_option(ld
, LDAP_OPT_ERROR_NUMBER
, &rc
) != LDAP_OPT_SUCCESS
)
406 static int dict_ldap_set_errno(LDAP
*ld
, int rc
)
408 (void) ldap_set_option(ld
, LDAP_OPT_ERROR_NUMBER
, &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;
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
));
432 /* dict_ldap_bind_st - Synchronous simple auth with timeout */
434 static int dict_ldap_bind_st(DICT_LDAP
*dict_ldap
)
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
)
447 if ((rc
= dict_ldap_result(dict_ldap
->ld
, msgid
, dict_ldap
->timeout
,
448 &res
)) != LDAP_SUCCESS
)
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
;
465 mytimeval
.tv_sec
= timeout
;
466 mytimeval
.tv_usec
= 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
)
476 if ((rc
= dict_ldap_result(ld
, msgid
, timeout
, res
)) != LDAP_SUCCESS
)
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";
490 #ifdef LDAP_OPT_X_TLS_NEWCTX
492 LDAP
*ld
= dict_ldap
->ld
;
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
));
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
));
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
));
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
));
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
));
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
));
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
));
561 #ifdef LDAP_OPT_X_TLS_NEWCTX
562 if ((rc
= ldap_set_option(ld
, LDAP_OPT_X_TLS_NEWCTX
, &am_server
))
564 msg_warn("%s: Unable to allocate new TLS context %d: %s",
565 myname
, rc
, ldap_err2string(rc
));
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";
581 #ifdef LDAP_OPT_NETWORK_TIMEOUT
582 struct timeval mytimeval
;
586 #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
587 void (*saved_alarm
) (int);
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
);
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
);
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
);
616 dict_ldap
->ld
= ldap_init(dict_ldap
->server_host
,
617 (int) dict_ldap
->server_port
);
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
;
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
) !=
629 msg_warn("%s: Unable to set network timeout.", myname
);
630 DICT_LDAP_UNBIND_RETURN(dict_ldap
->ld
, DICT_ERR_RETRY
, -1);
633 if ((saved_alarm
= signal(SIGALRM
, dict_ldap_timeout
)) == SIG_ERR
) {
634 msg_warn("%s: Error setting signal handler for open timeout: %m",
636 dict_errno
= DICT_ERR_RETRY
;
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
);
647 if (signal(SIGALRM
, saved_alarm
) == SIG_ERR
) {
648 msg_warn("%s: Error resetting signal handler after open: %m",
650 dict_errno
= DICT_ERR_RETRY
;
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
;
662 * v3 support is needed for referral chasing. Thanks to Sami Haahtinen
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);
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
);
677 msg_info("%s: Actual Protocol version used is %d.",
678 myname
, dict_ldap
->version
);
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);
712 if (dict_ldap
->chase_referrals
) {
713 msg_warn("%s: Unable to set Referral chasing.", myname
);
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",
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
);
731 dict_ldap
->ld
= 0; /* Unknown state after
736 if (signal(SIGALRM
, saved_alarm
) == SIG_ERR
) {
737 msg_warn("%s: Error resetting signal handler after STARTTLS: %m",
739 dict_errno
= DICT_ERR_RETRY
;
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
;
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
) {
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);
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
;
776 msg_info("%s: Cached connection handle for LDAP source %s",
777 myname
, dict_ldap
->parser
->name
);
783 * Locate or allocate connection cache entry.
785 static void dict_ldap_conn_find(DICT_LDAP
*dict_ldap
)
787 VSTRING
*keybuf
= vstring_alloc(10);
791 #ifdef LDAP_API_FEATURE_X_OPENLDAP
792 int sslon
= dict_ldap
->start_tls
|| dict_ldap
->ldap_ssl
;
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
: "");
821 key
= vstring_str(keybuf
);
822 len
= VSTRING_LEN(keybuf
);
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
));
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
;
853 LDAPMessage
*resloop
= 0;
854 LDAPMessage
*entry
= 0;
857 struct berval
**vals
;
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)
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
)) {
876 * LDAP should not, but may produce more than the requested maximum
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
)))
898 is_terminal
= (ldap_count_values_len(vals
) > 0);
899 ldap_value_free_len(vals
);
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
)))
916 is_leaf
= (ldap_count_values_len(vals
) == 0);
917 ldap_value_free_len(vals
);
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
);
929 msg_info("%s[%d]: Entry doesn't have any values for %s",
930 myname
, recursion
, attr
);
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
944 if (dict_errno
!= 0 || valcount
== 0) {
945 ldap_value_free_len(vals
);
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
],
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
)
971 i
< dict_ldap
->num_terminal
+ dict_ldap
->num_leaf
)) {
973 msg_info("%s[%d]: skipping %ld value(s) of %s "
974 "attribute %s", myname
, recursion
, i
,
975 is_terminal
? "non-terminal" : "leaf-only",
978 /* Ordinary result attribute */
979 for (i
= 0; i
< valcount
; i
++) {
980 if (db_common_expand(dict_ldap
->ctx
,
981 dict_ldap
->result_format
,
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
;
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
)) {
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
);
1010 rc
= search_st(dict_ldap
->ld
, url
->lud_dn
,
1011 url
->lud_scope
, url
->lud_filter
,
1012 url
->lud_attrs
, dict_ldap
->timeout
,
1014 ldap_free_urldesc(url
);
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
);
1027 dict_ldap_get_values(dict_ldap
, resloop
, result
, name
);
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
);
1039 msg_warn("%s[%d]: search error %d: %s ", myname
,
1040 recursion
, rc
, ldap_err2string(rc
));
1041 dict_errno
= DICT_ERR_RETRY
;
1046 ldap_msgfree(resloop
);
1048 if (dict_errno
!= 0)
1051 if (dict_errno
!= 0)
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
);
1071 msg_info("%s[%d]: Leaving %s", myname
, recursion
, myname
);
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
;
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) {
1110 msg_info("%s: Skipping lookup of '%s'", myname
, name
);
1113 #define INIT_VSTR(buf, len) do { \
1115 buf = vstring_alloc(len); \
1116 VSTRING_RESET(buf); \
1117 VSTRING_TERMINATE(buf); \
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
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
) {
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.
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
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
;
1168 * Expand the search base and query. Skip lookup when the input key lacks
1169 * sufficient domain components to satisfy all the requested
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
);
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
);
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
) {
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.
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
);
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
)
1240 ("%s: Had some trouble with entries returned by search: %s",
1241 myname
, ldap_err2string(rc
));
1244 msg_info("%s: Search returned %s", myname
,
1245 VSTRING_LEN(result
) >
1246 0 ? vstring_str(result
) : "nothing");
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
)
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
;
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
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
;
1294 * If we had an error, return nothing, Otherwise, return the result, if
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
) {
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
);
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
);
1338 vstring_free(dict
->fold_buf
);
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
;
1355 int vendor_version
= dict_ldap_vendor_version();
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",
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
) {
1384 dict_ldap
->version
= LDAP_VERSION2
;
1387 dict_ldap
->version
= LDAP_VERSION3
;
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;
1399 url_list
= vstring_alloc(32);
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
;
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
));
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
);
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
);
1434 vstring_sprintf_append(url_list
, "ldap://%s:%d", h
,
1435 dict_ldap
->server_port
);
1438 if (VSTRING_LEN(url_list
) > 0)
1439 VSTRING_ADDCH(url_list
, ' ');
1440 vstring_strcat(url_list
, h
);
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
;
1453 msg_info("%s: %s server_host URL is %s", myname
, ldapsource
,
1454 dict_ldap
->server_host
);
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
;
1470 msg_warn("%s: %s: Unrecognized value %s specified for scope; using sub",
1471 myname
, ldapsource
, scope
);
1472 dict_ldap
->scope
= LDAP_SCOPE_SUBTREE
;
1477 dict_ldap
->search_base
= cfg_get_str(dict_ldap
->parser
, "search_base",
1481 * get configured value of "timeout"; default to 10 seconds
1483 * Thanks to Manuel Guesdon for spotting that this wasn't really getting
1486 dict_ldap
->timeout
= cfg_get_int(dict_ldap
->parser
, "timeout", 10, 0, 0);
1488 #if 0 /* No benefit from changing
1490 * MySQL/PGSQL syntax */
1491 if ((dict_ldap
->query
=
1492 cfg_get_str(dict_ldap
->parser
, "query", 0, 0, 0)) == 0)
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.
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
1524 if (db_common_dict_partial(dict_ldap
->ctx
))
1525 dict_ldap
->dict
.flags
|= DICT_FLAG_PATTERN
;
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
;
1537 /* Order matters, next the leaf-only attributes: */
1538 attr
= cfg_get_str(dict_ldap
->parser
, "leaf_result_attribute", "", 0, 0);
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
;
1545 /* Order matters, next the regular attributes: */
1546 attr
= cfg_get_str(dict_ldap
->parser
, "result_attribute", "maildrop", 0, 0);
1548 argv_split_append(dict_ldap
->result_attributes
, attr
, " ,\t\r\n");
1549 dict_ldap
->num_attributes
= dict_ldap
->result_attributes
->argc
;
1552 /* Order matters, finally the special attributes: */
1553 attr
= cfg_get_str(dict_ldap
->parser
, "special_result_attribute", "", 0, 0);
1555 argv_split_append(dict_ldap
->result_attributes
, attr
, " ,\t\r\n");
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);
1578 msg_warn("%s: %s ignoring cache", myname
, ldapsource
);
1580 tmp
= cfg_get_int(dict_ldap
->parser
, "cache_expiry", -1, 0, 0);
1582 msg_warn("%s: %s ignoring cache_expiry", myname
, ldapsource
);
1584 tmp
= cfg_get_int(dict_ldap
->parser
, "cache_size", -1, 0, 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",
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
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",
1653 /* get configured value of "tls_key"; default "" */
1654 dict_ldap
->tls_key
= cfg_get_str(dict_ldap
->parser
, "tls_key",
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);
1669 #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
1670 dict_ldap
->debuglevel
= cfg_get_int(dict_ldap
->parser
, "debuglevel",
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
));