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