186 #include <sys/time.h>
197 #ifdef STRCASECMP_IN_STRINGS_H
204 #if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000)
205 #error "Your LDAP version is too old"
210 #define LDAP_CONST const
212 #ifndef LDAP_OPT_SUCCESS
213 #define LDAP_OPT_SUCCESS 0
232 #if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
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)
250 static const NAME_CODE bindopt_table[] = {
252 "none", DICT_LDAP_BIND_NONE,
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,
284 ARGV *result_attributes;
293 long recursion_limit;
298 #ifdef LDAP_API_FEATURE_X_OPENLDAP
299 #if defined(USE_LDAP_SASL)
308 int tls_require_cert;
309 char *tls_ca_cert_file;
310 char *tls_ca_cert_dir;
313 char *tls_random_file;
314 char *tls_cipher_suite;
320 #define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
322 #define DICT_LDAP_UNBIND_RETURN(__ld, __err, __ret) do { \
323 dict_ldap_unbind(__ld); \
325 dict_ldap->dict.error = (__err); \
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)
336 #define dict_ldap_unbind(ld) ldap_unbind(ld)
337 #define dict_ldap_abandon(ld, msg) ldap_abandon((ld), (msg))
340 static int dict_ldap_vendor_version(
void)
342 const char *myname =
"dict_ldap_api_info";
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);
356 msg_fatal(
"%s: ldap_get_option(API_INFO) failed", myname);
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);
362 return (api.ldapai_vendor_version);
371 static void rfc2253_quote(
DICT *unused,
const char *name,
VSTRING *result)
373 const char *sub = name;
383 if ((len = strcspn(sub,
" \t\"#+,;<>\\")) > 0) {
388 *((
const unsigned char *) sub++));
393 static void rfc2254_quote(
DICT *unused,
const char *name,
VSTRING *result)
395 const char *sub = name;
406 if ((len = strcspn(sub,
"*()\\")) > 0) {
411 *((
const unsigned char *) sub++));
416 #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
422 static void dict_ldap_timeout(
int unused_sig)
429 static void dict_ldap_logprint(LDAP_CONST
char *data)
431 const char *myname =
"dict_ldap_debug";
436 p = buf + strlen(buf) - 1;
437 while (p - buf >= 0 &&
ISSPACE(*p))
444 static int dict_ldap_get_errno(LDAP *ld)
448 if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_OPT_SUCCESS)
453 static int dict_ldap_set_errno(LDAP *ld,
int rc)
455 (void) ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &rc);
459 #if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
464 typedef struct bind_props {
471 static int ldap_b2_interact(LDAP *ld,
unsigned flags,
void *props,
void *inter)
475 bind_props *ctx = (bind_props *) props;
477 for (in = inter; in->id != SASL_CB_LIST_END; in++) {
480 case SASL_CB_GETREALM:
481 in->result = ctx->realm;
483 case SASL_CB_AUTHNAME:
484 in->result = ctx->authcid;
487 in->result = ctx->authzid;
490 in->result = ctx->passwd;
494 in->len = strlen(in->result);
503 static int dict_ldap_result(LDAP *ld,
int msgid,
int timeout, LDAPMessage **res)
505 struct timeval mytimeval;
508 mytimeval.tv_sec = timeout;
509 mytimeval.tv_usec = 0;
512 if (ldap_result(ld, msgid, GET_ALL, &mytimeval, res) == -1)
513 return (dict_ldap_get_errno(ld));
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));
525 #if defined(USE_LDAP_SASL) && defined(LDAP_API_FEATURE_X_OPENLDAP)
529 static int dict_ldap_bind_sasl(DICT_LDAP *dict_ldap)
540 if ((rc = ldap_set_option(dict_ldap->ld, LDAP_OPT_X_SASL_SECPROPS,
541 (
char *) minssf)) != LDAP_OPT_SUCCESS)
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;
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)
555 return (LDAP_SUCCESS);
562 static int dict_ldap_bind_st(DICT_LDAP *dict_ldap)
565 int err = LDAP_SUCCESS;
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)
576 if ((rc = dict_ldap_result(dict_ldap->ld, msgid, dict_ldap->timeout,
577 &res)) != LDAP_SUCCESS)
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);
587 static int search_st(LDAP *ld,
char *base,
int scope,
char *query,
588 char **attrs,
int timeout, LDAPMessage **res)
590 struct timeval mytimeval;
595 mytimeval.tv_sec = timeout;
596 mytimeval.tv_usec = 0;
599 #define USE_SIZE_LIM_OPT -1
601 if ((rc = ldap_search_ext(ld, base, scope, query, attrs, WANTVALS, 0, 0,
602 &mytimeval, USE_SIZE_LIM_OPT,
603 &msgid)) != LDAP_SUCCESS)
606 if ((rc = dict_ldap_result(ld, msgid, timeout, res)) != LDAP_SUCCESS)
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);
614 #ifdef LDAP_API_FEATURE_X_OPENLDAP
615 static int dict_ldap_set_tls_options(DICT_LDAP *dict_ldap)
617 const char *myname =
"dict_ldap_set_tls_options";
620 #ifdef LDAP_OPT_X_TLS_NEWCTX
622 LDAP *ld = dict_ldap->ld;
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));
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));
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));
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));
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));
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));
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));
691 #ifdef LDAP_OPT_X_TLS_NEWCTX
692 if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_NEWCTX, &am_server))
694 msg_warn(
"%s: Unable to allocate new TLS context %d: %s",
695 myname, rc, ldap_err2string(rc));
706 static int dict_ldap_connect(DICT_LDAP *dict_ldap)
708 const char *myname =
"dict_ldap_connect";
711 #ifdef LDAP_OPT_NETWORK_TIMEOUT
712 struct timeval mytimeval;
716 #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
717 void (*saved_alarm) (
int);
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);
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);
736 dict_ldap->dict.error = 0;
739 msg_info(
"%s: Connecting to server %s", myname,
740 dict_ldap->server_host);
742 #ifdef LDAP_OPT_NETWORK_TIMEOUT
743 #ifdef LDAP_API_FEATURE_X_OPENLDAP
744 ldap_initialize(&(dict_ldap->ld), dict_ldap->server_host);
746 dict_ldap->ld = ldap_init(dict_ldap->server_host,
747 (
int) dict_ldap->server_port);
749 if (dict_ldap->ld == NULL) {
750 msg_warn(
"%s: Unable to init LDAP server %s",
751 myname, dict_ldap->server_host);
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) !=
759 msg_warn(
"%s: Unable to set network timeout.", myname);
763 if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
764 msg_warn(
"%s: Error setting signal handler for open timeout: %m",
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);
777 if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
778 msg_warn(
"%s: Error resetting signal handler after open: %m",
783 if (dict_ldap->ld == NULL) {
784 msg_warn(
"%s: Unable to connect to LDAP server %s",
785 myname, dict_ldap->server_host);
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);
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);
807 msg_info(
"%s: Actual Protocol version used is %d.",
808 myname, dict_ldap->version);
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);
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);
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);
842 if (dict_ldap->chase_referrals) {
843 msg_warn(
"%s: Unable to set Referral chasing.", myname);
847 #ifdef LDAP_API_FEATURE_X_OPENLDAP
848 if (dict_ldap_set_tls_options(dict_ldap) != 0)
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",
856 alarm(dict_ldap->timeout);
857 if (setjmp(env) == 0)
858 rc = ldap_start_tls_s(dict_ldap->ld, NULL, NULL);
866 if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
867 msg_warn(
"%s: Error resetting signal handler after STARTTLS: %m",
872 if (rc != LDAP_SUCCESS) {
873 msg_error(
"%s: Unable to set STARTTLS: %d: %s", myname,
874 rc, ldap_err2string(rc));
881 #define DN_LOG_VAL(dict_ldap) \
882 ((dict_ldap)->bind_dn[0] ? (dict_ldap)->bind_dn : "empty or implicit")
888 if (DICT_LDAP_DO_BIND(dict_ldap)) {
890 msg_info(
"%s: Binding to server %s with dn %s",
891 myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap));
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);
897 rc = dict_ldap_bind_st(dict_ldap);
900 rc = dict_ldap_bind_st(dict_ldap);
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));
910 msg_info(
"%s: Successful bind to server %s with dn %s",
911 myname, dict_ldap->server_host, DN_LOG_VAL(dict_ldap));
914 DICT_LDAP_CONN(dict_ldap)->conn_ld = dict_ldap->ld;
917 msg_info(
"%s: Cached connection handle for LDAP source %s",
918 myname, dict_ldap->parser->name);
926 static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
932 #ifdef LDAP_API_FEATURE_X_OPENLDAP
933 int sslon = dict_ldap->start_tls || dict_ldap->ldap_ssl;
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)
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);
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 :
"");
978 conn = (LDAP_CONN *)
mymalloc(
sizeof(LDAP_CONN));
980 conn->conn_refcount = 0;
981 dict_ldap->ht =
binhash_enter(conn_hash, key, len, (
void *) conn);
983 ++DICT_LDAP_CONN(dict_ldap)->conn_refcount;
990 static int attrdesc_subtype(
const char *a1,
const char *a2)
1002 if (*a1 == 0 && (*a2 == 0 || *a2 ==
';'))
1008 if (*a2 == 0 && *a1 ==
';')
1019 static char **url_attrs(DICT_LDAP *dict_ldap, LDAPURLDesc * url)
1038 if (url->lud_attrs == 0 || *url->lud_attrs == 0)
1039 return (dict_ldap->result_attributes->argv);
1060 for (a1 = url->lud_attrs; *a1; ++a1) {
1061 for (a2 = dict_ldap->result_attributes->argv; *a2; ++a2) {
1062 arel = attrdesc_subtype(*a1, *a2);
1070 return ((attrs->
argc > 0) ? attrs->
argv : 0);
1080 static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage *res,
1081 VSTRING *result,
const char *name)
1083 static int recursion = 0;
1084 static int expansion;
1088 LDAPMessage *resloop = 0;
1089 LDAPMessage *entry = 0;
1093 struct berval **vals;
1096 const char *myname =
"dict_ldap_get_values";
1098 int is_terminal = 0;
1100 if (++recursion == 1)
1104 msg_info(
"%s[%d]: Search found %d match(es)", myname, recursion,
1105 ldap_count_entries(dict_ldap->ld, res));
1107 for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
1108 entry = ldap_next_entry(dict_ldap->ld, entry)) {
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);
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)))
1134 is_terminal = (ldap_count_values_len(vals) > 0);
1135 ldap_value_free_len(vals);
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)))
1152 is_leaf = (ldap_count_values_len(vals) == 0);
1153 ldap_value_free_len(vals);
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)) {
1162 vals = ldap_get_values_len(dict_ldap->ld, entry, attr);
1165 msg_info(
"%s[%d]: Entry doesn't have any values for %s",
1166 myname, recursion, attr);
1169 valcount = ldap_count_values_len(vals);
1180 if (dict_ldap->dict.error != 0 || valcount == 0) {
1181 ldap_value_free_len(vals);
1195 for (i = 0; dict_ldap->result_attributes->argv[i]; i++)
1196 if (attrdesc_subtype(dict_ldap->result_attributes->argv[i],
1204 if (i < dict_ldap->num_attributes || is_terminal) {
1205 if ((is_terminal && i >= dict_ldap->num_terminal)
1207 i < dict_ldap->num_terminal + dict_ldap->num_leaf)) {
1209 msg_info(
"%s[%d]: skipping %d value(s) of %s "
1210 "attribute %s", myname, recursion, valcount,
1211 is_terminal ?
"non-terminal" :
"leaf-only",
1215 for (i = 0; i < valcount; i++) {
1217 dict_ldap->result_format,
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);
1229 if (dict_ldap->dict.error != 0)
1232 msg_info(
"%s[%d]: search returned %d value(s) for"
1233 " requested result attribute %s",
1234 myname, recursion, valcount, attr);
1236 }
else if (recursion < dict_ldap->recursion_limit
1237 && dict_ldap->result_attributes->argv[i]) {
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);
1243 if ((attrs = url_attrs(dict_ldap, url)) != 0) {
1245 msg_info(
"%s[%d]: looking up URL %s",
1248 rc = search_st(dict_ldap->ld, url->lud_dn,
1251 attrs, dict_ldap->timeout,
1254 ldap_free_urldesc(url);
1257 msg_info(
"%s[%d]: skipping URL %s: no "
1258 "pertinent attributes", myname,
1259 recursion, vals[i]->bv_val);
1263 msg_warn(
"%s[%d]: malformed URL %s: %s(%d)",
1264 myname, recursion, vals[i]->bv_val,
1265 ldap_err2string(rc), rc);
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);
1280 dict_ldap_get_values(dict_ldap, resloop, result, name);
1282 case LDAP_NO_SUCH_OBJECT:
1288 msg_warn(
"%s[%d]: DN %s not found, skipping ", myname,
1289 recursion, vals[i]->bv_val);
1292 msg_warn(
"%s[%d]: search error %d: %s ", myname,
1293 recursion, rc, ldap_err2string(rc));
1299 ldap_msgfree(resloop);
1301 if (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);
1315 ldap_value_free_len(vals);
1322 msg_info(
"%s[%d]: Leaving %s", myname, recursion, myname);
1328 static const char *dict_ldap_lookup(
DICT *dict,
const char *name)
1330 const char *myname =
"dict_ldap_lookup";
1331 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
1332 LDAPMessage *res = 0;
1340 dict_ldap->dict.error = 0;
1343 msg_info(
"%s: In dict_ldap_lookup", myname);
1351 msg_info(
"%s: %s: Skipping lookup of non-UTF-8 key '%s'",
1352 myname, dict_ldap->parser->name, name);
1373 msg_info(
"%s: %s: Skipping lookup of key '%s': domain mismatch",
1374 myname, dict_ldap->parser->name, name);
1380 #define INIT_VSTR(buf, len) do { \
1382 buf = vstring_alloc(len); \
1383 VSTRING_RESET(buf); \
1384 VSTRING_TERMINATE(buf); \
1387 INIT_VSTR(base, 10);
1388 INIT_VSTR(query, 10);
1389 INIT_VSTR(result, 10);
1396 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld;
1401 if (dict_ldap->ld == NULL) {
1404 (
"%s: No existing connection for LDAP source %s, reopening",
1405 myname, dict_ldap->parser->name);
1407 dict_ldap_connect(dict_ldap);
1412 if (dict_ldap->dict.error)
1415 msg_info(
"%s: Using existing connection for LDAP source %s",
1416 myname, dict_ldap->parser->name);
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);
1444 name, 0, base, rfc2253_quote)) {
1446 msg_info(
"%s: %s: Empty expansion for %s", myname,
1447 dict_ldap->parser->name, dict_ldap->search_base);
1451 name, 0, query, rfc2254_quote)) {
1453 msg_info(
"%s: %s: Empty expansion for %s", myname,
1454 dict_ldap->parser->name, dict_ldap->query);
1462 msg_info(
"%s: %s: Searching with filter %s", myname,
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);
1469 if (rc == LDAP_SERVER_DOWN) {
1471 msg_info(
"%s: Lost connection for LDAP source %s, reopening",
1472 myname, dict_ldap->parser->name);
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);
1481 if (dict_ldap->dict.error)
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);
1497 dict_ldap_get_values(dict_ldap, res, result, name);
1504 rc = dict_ldap_get_errno(dict_ldap->ld);
1505 if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR)
1507 (
"%s: Had some trouble with entries returned by search: %s",
1508 myname, ldap_err2string(rc));
1511 msg_info(
"%s: Search returned %s", myname,
1516 case LDAP_NO_SUCH_OBJECT:
1523 if (dict_ldap->dynamic_base)
1526 msg_warn(
"%s: %s: Search base '%s' not found: %d: %s",
1527 myname, dict_ldap->parser->name,
1537 msg_warn(
"%s: Search error %d: %s ", myname, rc,
1538 ldap_err2string(rc));
1544 dict_ldap_unbind(dict_ldap->ld);
1545 dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
1569 static void dict_ldap_close(
DICT *dict)
1571 const char *myname =
"dict_ldap_close";
1572 DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
1573 LDAP_CONN *conn = DICT_LDAP_CONN(dict_ldap);
1576 if (--conn->conn_refcount == 0) {
1577 if (conn->conn_ld) {
1579 msg_info(
"%s: Closed connection handle for LDAP source %s",
1580 myname, dict_ldap->parser->name);
1581 dict_ldap_unbind(conn->conn_ld);
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);
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);
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);
1620 const char *myname =
"dict_ldap_open";
1621 DICT_LDAP *dict_ldap;
1630 int vendor_version = dict_ldap_vendor_version();
1634 msg_info(
"%s: Using LDAP source %s", myname, ldapsource);
1639 if (open_flags != O_RDONLY)
1641 "%s:%s map requires O_RDONLY access mode",
1649 "open %s: %m", 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;
1657 dict_ldap->ld = NULL;
1658 dict_ldap->parser = parser;
1660 server_host =
cfg_get_str(dict_ldap->parser,
"server_host",
1666 dict_ldap->server_port =
1667 cfg_get_int(dict_ldap->parser,
"server_port", LDAP_PORT, 0, 0);
1672 dict_ldap->version =
cfg_get_int(dict_ldap->parser,
"version", 2, 2, 0);
1673 switch (dict_ldap->version) {
1675 dict_ldap->version = LDAP_VERSION2;
1678 dict_ldap->version = LDAP_VERSION3;
1681 msg_warn(
"%s: %s Unknown version %d, using 2.", myname, ldapsource,
1682 dict_ldap->version);
1683 dict_ldap->version = LDAP_VERSION2;
1686 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
1687 dict_ldap->ldap_ssl = 0;
1693 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
1698 if (ldap_is_ldap_url(h)) {
1699 LDAPURLDesc *url_desc;
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));
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;
1713 if (
strcasecmp(url_desc->lud_scheme,
"ldaps") == 0)
1714 dict_ldap->ldap_ssl = 1;
1715 ldap_free_urldesc(url_desc);
1722 if (strrchr(h,
':'))
1726 dict_ldap->server_port);
1737 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
1742 dict_ldap->server_port = LDAP_PORT;
1744 msg_info(
"%s: %s server_host URL is %s", myname, ldapsource,
1745 dict_ldap->server_host);
1752 scope =
cfg_get_str(dict_ldap->parser,
"scope",
"sub", 1, 0);
1755 dict_ldap->scope = LDAP_SCOPE_ONELEVEL;
1757 dict_ldap->scope = LDAP_SCOPE_BASE;
1759 dict_ldap->scope = LDAP_SCOPE_SUBTREE;
1761 msg_warn(
"%s: %s: Unrecognized value %s specified for scope; using sub",
1762 myname, ldapsource, scope);
1763 dict_ldap->scope = LDAP_SCOPE_SUBTREE;
1768 dict_ldap->search_base =
cfg_get_str(dict_ldap->parser,
"search_base",
1777 dict_ldap->timeout =
cfg_get_int(dict_ldap->parser,
"timeout", 10, 0, 0);
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);
1793 dict_ldap->dynamic_base =
1795 dict_ldap->search_base, 1);
1797 msg_warn(
"%s: %s: Fixed query_filter %s is probably useless",
1798 myname, ldapsource, dict_ldap->query);
1800 (void)
db_common_parse(0, &dict_ldap->ctx, dict_ldap->result_format, 0);
1815 attr =
cfg_get_str(dict_ldap->parser,
"terminal_result_attribute",
"", 0, 0);
1817 dict_ldap->num_terminal = dict_ldap->result_attributes->argc;
1821 attr =
cfg_get_str(dict_ldap->parser,
"leaf_result_attribute",
"", 0, 0);
1824 dict_ldap->num_leaf =
1825 dict_ldap->result_attributes->argc - dict_ldap->num_terminal;
1829 attr =
cfg_get_str(dict_ldap->parser,
"result_attribute",
"maildrop", 0, 0);
1832 dict_ldap->num_attributes = dict_ldap->result_attributes->argc;
1836 attr =
cfg_get_str(dict_ldap->parser,
"special_result_attribute",
"", 0, 0);
1846 if (dict_ldap->bind < 0)
1847 msg_fatal(
"%s: unsupported parameter value: %s = %s",
1848 dict_ldap->parser->name,
"bind", bindopt);
1854 dict_ldap->bind_dn =
cfg_get_str(dict_ldap->parser,
"bind_dn",
"", 0, 0);
1859 dict_ldap->bind_pw =
cfg_get_str(dict_ldap->parser,
"bind_pw",
"", 0, 0);
1866 msg_warn(
"%s: %s ignoring cache", myname, ldapsource);
1868 tmp =
cfg_get_int(dict_ldap->parser,
"cache_expiry", -1, 0, 0);
1870 msg_warn(
"%s: %s ignoring cache_expiry", myname, ldapsource);
1872 tmp =
cfg_get_int(dict_ldap->parser,
"cache_size", -1, 0, 0);
1874 msg_warn(
"%s: %s ignoring cache_size", myname, ldapsource);
1876 dict_ldap->recursion_limit =
cfg_get_int(dict_ldap->parser,
1877 "recursion_limit", 1000, 1, 0);
1883 dict_ldap->expansion_limit =
cfg_get_int(dict_ldap->parser,
1884 "expansion_limit", 0, 0, 0);
1886 dict_ldap->size_limit =
cfg_get_int(dict_ldap->parser,
"size_limit",
1887 dict_ldap->expansion_limit, 0, 0);
1892 dict_ldap->dereference =
cfg_get_int(dict_ldap->parser,
"dereference",
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;
1900 dict_ldap->chase_referrals =
cfg_get_bool(dict_ldap->parser,
1901 "chase_referrals", 0);
1903 #ifdef LDAP_API_FEATURE_X_OPENLDAP
1904 #if defined(USE_LDAP_SASL)
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);
1919 dict_ldap->sasl_mechs = 0;
1920 dict_ldap->sasl_realm = 0;
1921 dict_ldap->sasl_authz = 0;
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;
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);
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;
1950 dict_ldap->tls_ca_cert_file =
cfg_get_str(dict_ldap->parser,
1951 "tls_ca_cert_file",
"", 0, 0);
1954 dict_ldap->tls_ca_cert_dir =
cfg_get_str(dict_ldap->parser,
1955 "tls_ca_cert_dir",
"", 0, 0);
1958 dict_ldap->tls_cert =
cfg_get_str(dict_ldap->parser,
"tls_cert",
1962 dict_ldap->tls_key =
cfg_get_str(dict_ldap->parser,
"tls_key",
1966 dict_ldap->tls_random_file =
cfg_get_str(dict_ldap->parser,
1967 "tls_random_file",
"", 0, 0);
1970 dict_ldap->tls_cipher_suite =
cfg_get_str(dict_ldap->parser,
1971 "tls_cipher_suite",
"", 0, 0);
1977 #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
1978 dict_ldap->debuglevel =
cfg_get_int(dict_ldap->parser,
"debuglevel",
1985 dict_ldap_conn_find(dict_ldap);
void msg_error(const char *fmt,...)
char * mystrdup(const char *str)
ARGV * argv_free(ARGV *argvp)
DICT * dict_ldap_open(const char *, int, int)
int valid_utf8_string(const char *, ssize_t)
VSTRING * vstring_strncat(VSTRING *vp, const char *src, ssize_t len)
BINHASH * binhash_create(ssize_t size)
BINHASH_INFO * binhash_enter(BINHASH *table, const void *key, ssize_t key_len, void *value)
void argv_add(ARGV *argvp,...)
BINHASH_INFO * binhash_locate(BINHASH *table, const void *key, ssize_t key_len)
char * mystrtok(char **src, const char *sep)
int cfg_get_int(const CFG_PARSER *parser, const char *name, int defval, int min, int max)
ARGV * argv_alloc(ssize_t len)
#define DICT_FLAG_FOLD_FIX
void db_common_free_ctx(void *ctxPtr)
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
#define VSTRING_TERMINATE(vp)
int db_common_dict_partial(void *ctxPtr)
#define VSTRING_ADDCH(vp, ch)
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
void db_common_parse_domain(CFG_PARSER *parser, void *ctxPtr)
int db_common_expand(void *ctxArg, const char *format, const char *value, const char *key, VSTRING *result, db_quote_callback_t quote_func)
void msg_warn(const char *fmt,...)
VSTRING * vstring_alloc(ssize_t len)
#define NAME_CODE_FLAG_NONE
void argv_truncate(ARGV *argvp, ssize_t len)
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
int db_common_check_domain(void *ctxPtr, const char *addr)
char * lowercase(char *string)
const char *(* lookup)(struct DICT *, const char *)
int name_code(const NAME_CODE *table, int flags, const char *name)
NORETURN msg_fatal(const char *fmt,...)
#define DICT_ERR_VAL_RETURN(dict, err, val)
char * cfg_get_str(const CFG_PARSER *parser, const char *name, const char *defval, int min, int max)
#define DICT_FLAG_PATTERN
ARGV * argv_split(const char *, const char *)
CFG_PARSER * cfg_parser_free(CFG_PARSER *parser)
int strcasecmp(const char *s1, const char *s2)
CFG_PARSER * cfg_parser_alloc(const char *pname)
VSTRING * vstring_free(VSTRING *vp)
ARGV * argv_split_append(ARGV *, const char *, const char *)
int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
DICT * dict_alloc(const char *, const char *, ssize_t)
int cfg_get_bool(const CFG_PARSER *parser, const char *name, int defval)
#define cfg_get_owner(cfg)
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
char * vstring_export(VSTRING *vp)
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
void binhash_delete(BINHASH *table, const void *key, ssize_t key_len, void(*free_fn)(void *))
void * mymalloc(ssize_t len)
void msg_info(const char *fmt,...)