/*++ /* NAME /* dict_ldap 3 /* SUMMARY /* dictionary manager interface to LDAP maps /* SYNOPSIS /* #include /* /* DICT *dict_ldap_open(attribute, dummy, dict_flags) /* const char *attribute; /* int dummy; /* int dict_flags; /* DESCRIPTION /* dict_ldap_open() makes LDAP user information accessible via /* the generic dictionary operations described in dict_open(3). /* /* Arguments: /* .IP ldapsource /* The prefix which will be used to obtain configuration parameters /* for this search. If it's 'ldapone', the configuration variables below /* would look like 'ldapone_server_host', 'ldapone_search_base', and so /* on in main.cf. /* .IP dummy /* Not used; this argument exists only for compatibility with /* the dict_open(3) interface. /* .PP /* Configuration parameters: /* .IP \fIldapsource_\fRserver_host /* The host at which all LDAP queries are directed. /* .IP \fIldapsource_\fRserver_port /* The port the LDAP server listens on. /* .IP \fIldapsource_\fRsearch_base /* The LDAP search base, for example: \fIO=organization name, C=country\fR. /* .IP \fIldapsource_\fRdomain /* If specified, only lookups ending in this value will be queried. /* This can significantly reduce the query load on the LDAP server. /* .IP \fIldapsource_\fRtimeout /* Deadline for LDAP open() and LDAP search() . /* .IP \fIldapsource_\fRquery_filter /* The filter used to search for directory entries, for example /* \fI(mailacceptinggeneralid=%s)\fR. /* .IP \fIldapsource_\fRresult_filter /* The filter used to expand results from queries. Default is /* \fI%s\fR. /* .IP \fIldapsource_\fRresult_attribute /* The attribute(s) returned by the search, in which to find /* RFC822 addresses, for example \fImaildrop\fR. /* .IP \fIldapsource_\fRspecial_result_attribute /* The attribute(s) of directory entries that can contain DNs or URLs. /* If found, a recursive subsequent search is done using their values. /* .IP \fIldapsource_\fRscope /* LDAP search scope: sub, base, or one. /* .IP \fIldapsource_\fRbind /* Whether or not to bind to the server -- LDAP v3 implementations don't /* require it, which saves some overhead. /* .IP \fIldapsource_\fRbind_dn /* If you must bind to the server, do it with this distinguished name ... /* .IP \fIldapsource_\fRbind_pw /* \&... and this password. /* .IP \fIldapsource_\fRcache /* Whether or not to turn on client-side caching. /* .IP \fIldapsource_\fRcache_expiry /* If you do cache results, expire them after this many seconds. /* .IP \fIldapsource_\fRcache_size /* The cache size in bytes. Does nothing if the cache is off, of course. /* .IP \fIldapsource_\fRdereference /* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page. /* .IP \fIldapsource_\fRdebuglevel /* Debug level. See 'loglevel' option in slapd.conf(5) man page. /* Currently only in openldap libraries (and derivatives). /* SEE ALSO /* dict(3) generic dictionary manager /* AUTHOR(S) /* Prabhat K Singh /* VSNL, Bombay, India. /* prabhat@giasbm01.vsnl.net.in /* /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10532, USA /* /* John Hensley /* john@sunislelodge.com /* /* LaMont Jones /* lamont@hp.com /* /*--*/ /* System library. */ #include "sys_defs.h" #ifdef HAS_LDAP #include #include #include #include #include #include #include #include /* * Older APIs have weird memory freeing behavior. */ #if !defined(LDAP_API_VERSION) || (LDAP_API_VERSION < 2000) #error "Your LDAP version is too old" #endif /* Handle differences between LDAP SDK's constant definitions */ #ifndef LDAP_CONST #define LDAP_CONST const #endif #ifndef LDAP_OPT_SUCCESS #define LDAP_OPT_SUCCESS 0 #endif /* Utility library. */ #include "match_list.h" #include "match_ops.h" #include "msg.h" #include "mymalloc.h" #include "vstring.h" #include "dict.h" #include "dict_ldap.h" /* AAARGH!! */ #include "../global/mail_conf.h" /* * Structure containing all the configuration parameters for a given * LDAP source, plus its connection handle. */ typedef struct { DICT dict; /* generic member */ char *ldapsource; char *server_host; int server_port; int scope; char *search_base; MATCH_LIST *domain; char *query_filter; char *result_filter; ARGV *result_attributes; int num_attributes; /* rest of list is DN's. */ int bind; char *bind_dn; char *bind_pw; int timeout; int cache; long cache_expiry; long cache_size; int dereference; int chase_referrals; int debuglevel; int version; LDAP *ld; } DICT_LDAP; #ifndef LDAP_OPT_NETWORK_TIMEOUT /* * LDAP connection timeout support. */ static jmp_buf env; static void dict_ldap_timeout(int unused_sig) { longjmp(env, 1); } #endif static void dict_ldap_logprint(LDAP_CONST char *data) { char *myname = "dict_ldap_debug"; msg_info("%s: %s", myname, data); } static int dict_ldap_get_errno(LDAP * ld) { int rc; if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_OPT_SUCCESS) rc = LDAP_OTHER; return rc; } static int dict_ldap_set_errno(LDAP * ld, int rc) { (void) ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &rc); return rc; } /* * We need a version of ldap_bind that times out, otherwise all * of Postfix can get wedged during daemon initialization. */ static int dict_ldap_bind_st(DICT_LDAP *dict_ldap) { int msgid; LDAPMessage *res; struct timeval mytimeval; if ((msgid = ldap_bind(dict_ldap->ld, dict_ldap->bind_dn, dict_ldap->bind_pw, LDAP_AUTH_SIMPLE)) == -1) return (dict_ldap_get_errno(dict_ldap->ld)); mytimeval.tv_sec = dict_ldap->timeout; mytimeval.tv_usec = 0; if (ldap_result(dict_ldap->ld, msgid, 1, &mytimeval, &res) == -1) return (dict_ldap_get_errno(dict_ldap->ld)); if (dict_ldap_get_errno(dict_ldap->ld) == LDAP_TIMEOUT) { (void) ldap_abandon(dict_ldap->ld, msgid); return (dict_ldap_set_errno(dict_ldap->ld, LDAP_TIMEOUT)); } return (ldap_result2error(dict_ldap->ld, res, 1)); } /* Establish a connection to the LDAP server. */ static int dict_ldap_connect(DICT_LDAP *dict_ldap) { char *myname = "dict_ldap_connect"; int rc = 0; #ifdef LDAP_API_FEATURE_X_MEMCACHE LDAPMemCache *dircache; #endif #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval mytimeval; #else void (*saved_alarm) (int); #endif dict_errno = 0; if (msg_verbose) msg_info("%s: Connecting to server %s", myname, dict_ldap->server_host); #ifdef LDAP_OPT_NETWORK_TIMEOUT dict_ldap->ld = ldap_init(dict_ldap->server_host, (int) dict_ldap->server_port); if (dict_ldap->ld == NULL) { msg_warn("%s: Unable to init LDAP server %s", myname, dict_ldap->server_host); dict_errno = DICT_ERR_RETRY; return (-1); } mytimeval.tv_sec = dict_ldap->timeout; mytimeval.tv_usec = 0; if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to set network timeout.", myname); #else if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) { msg_warn("%s: Error setting signal handler for open timeout: %m", myname); dict_errno = DICT_ERR_RETRY; return (-1); } alarm(dict_ldap->timeout); if (setjmp(env) == 0) dict_ldap->ld = ldap_open(dict_ldap->server_host, (int) dict_ldap->server_port); else dict_ldap->ld = 0; alarm(0); if (signal(SIGALRM, saved_alarm) == SIG_ERR) { msg_warn("%s: Error resetting signal handler after open: %m", myname); dict_errno = DICT_ERR_RETRY; return (-1); } if (dict_ldap->ld == NULL) { msg_warn("%s: Unable to connect to LDAP server %s", myname, dict_ldap->server_host); dict_errno = DICT_ERR_RETRY; return (-1); } #endif /* * v3 support is needed for referral chasing. Thanks to Sami Haahtinen * for the patch. */ #ifdef LDAP_OPT_PROTOCOL_VERSION if (ldap_set_option(dict_ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &dict_ldap->version) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to set LDAP protocol version", myname); if (msg_verbose) { if (ldap_get_option(dict_ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &dict_ldap->version) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to get LDAP protocol version", myname); else msg_warn("%s: Actual Protocol version used is %d.", myname, dict_ldap->version); } #endif /* * Configure alias dereferencing for this connection. Thanks to Mike * Mattice for this, and to Hery Rakotoarisoa for the v3 update. */ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF, &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to set dereference option.", myname); #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) if (dict_ldap->debuglevel > 0 && ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS) msg_warn("%s: Unable to set ber logprint function.", myname); if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEBUG_LEVEL, &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to set LDAP debug level.", myname); #endif /* Chase referrals. */ /* * I have no clue where this was originally added so i'm skipping all * tests */ #ifdef LDAP_OPT_REFERRALS if (ldap_set_option(dict_ldap->ld, LDAP_OPT_REFERRALS, dict_ldap->chase_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF) != LDAP_OPT_SUCCESS) msg_warn("%s: Unable to set Referral chasing.", myname); #else if (dict_ldap->chase_referrals) { msg_warn("%s: Unable to set Referral chasing.", myname); } #endif /* * If this server requires a bind, do so. Thanks to Sam Tardieu for * noticing that the original bind call was broken. */ if (dict_ldap->bind) { if (msg_verbose) msg_info("%s: Binding to server %s as dn %s", myname, dict_ldap->server_host, dict_ldap->bind_dn); rc = dict_ldap_bind_st(dict_ldap); if (rc != LDAP_SUCCESS) { msg_warn("%s: Unable to bind to server %s as %s: %d (%s)", myname, dict_ldap->server_host, dict_ldap->bind_dn, rc, ldap_err2string(rc)); dict_errno = DICT_ERR_RETRY; return (-1); } if (msg_verbose) msg_info("%s: Successful bind to server %s as %s ", myname, dict_ldap->server_host, dict_ldap->bind_dn); } /* * Set up client-side caching if it's configured. */ if (dict_ldap->cache) { if (msg_verbose) msg_info ("%s: Enabling %ld-byte cache for %s with %ld-second expiry", myname, dict_ldap->cache_size, dict_ldap->ldapsource, dict_ldap->cache_expiry); #ifdef LDAP_API_FEATURE_X_MEMCACHE rc = ldap_memcache_init(dict_ldap->cache_expiry, dict_ldap->cache_size, NULL, NULL, &dircache); if (rc != LDAP_SUCCESS) { msg_warn ("%s: Unable to configure cache for %s: %d (%s) -- continuing", myname, dict_ldap->ldapsource, rc, ldap_err2string(rc)); } else { rc = ldap_memcache_set(dict_ldap->ld, dircache); if (rc != LDAP_SUCCESS) { msg_warn ("%s: Unable to configure cache for %s: %d (%s) -- continuing", myname, dict_ldap->ldapsource, rc, ldap_err2string(rc)); } else { if (msg_verbose) msg_info("%s: Caching enabled for %s", myname, dict_ldap->ldapsource); } } #else rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry, dict_ldap->cache_size); if (rc != LDAP_SUCCESS) { msg_warn ("%s: Unable to configure cache for %s: %d (%s) -- continuing", myname, dict_ldap->ldapsource, rc, ldap_err2string(rc)); } else { if (msg_verbose) msg_info("%s: Caching enabled for %s", myname, dict_ldap->ldapsource); } #endif } if (msg_verbose) msg_info("%s: Cached connection handle for LDAP source %s", myname, dict_ldap->ldapsource); return (0); } /* * expand a filter (lookup or result) */ static void dict_ldap_expand_filter(char *filter, char *value, VSTRING *out) { char *myname = "dict_ldap_expand_filter"; char *sub, *end; /* * Yes, replace all instances of %s with the address to look up. Replace * %u with the user portion, and %d with the domain portion. */ sub = filter; end = sub + strlen(filter); while (sub < end) { /* * Make sure it's %[sud] and not something else. For backward * compatibilty, treat anything other than %u or %d as %s, with a * warning. */ if (*(sub) == '%') { char *u = value; char *p = strrchr(u, '@'); switch (*(sub + 1)) { case 'd': if (p) vstring_strcat(out, p + 1); break; case 'u': if (p) vstring_strncat(out, u, p - u); else vstring_strcat(out, u); break; default: msg_warn ("%s: Invalid filter substitution format '%%%c'!", myname, *(sub + 1)); /* fall through */ case 's': vstring_strcat(out, u); break; } sub++; } else vstring_strncat(out, sub, 1); sub++; } } /* * dict_ldap_get_values: for each entry returned by a search, get the values * of all its attributes. Recurses to resolve any DN or URL values found. * * This and the rest of the handling of multiple attributes, DNs and URLs * are thanks to LaMont Jones. */ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, VSTRING *result) { long i = 0; int rc = 0; LDAPMessage *resloop = 0; LDAPMessage *entry = 0; BerElement *ber; char **vals; char *attr; char *myname = "dict_ldap_get_values"; struct timeval tv; LDAPURLDesc *url; tv.tv_sec = dict_ldap->timeout; tv.tv_usec = 0; if (msg_verbose) msg_info("%s: Search found %d match(es)", myname, ldap_count_entries(dict_ldap->ld, res)); for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL; entry = ldap_next_entry(dict_ldap->ld, entry)) { ber = NULL; for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber); attr != NULL; ldap_memfree(attr), attr = ldap_next_attribute(dict_ldap->ld, entry, ber)) { vals = ldap_get_values(dict_ldap->ld, entry, attr); if (vals == NULL) { if (msg_verbose) msg_info("%s: Entry doesn't have any values for %s", myname, attr); continue; } for (i = 0; dict_ldap->result_attributes->argv[i]; i++) { if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0) { if (msg_verbose) msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr); break; } } /* * Append each returned address to the result list, possibly * recursing (for dn or url attributes). */ if (i < dict_ldap->num_attributes) { for (i = 0; vals[i] != NULL; i++) { if (VSTRING_LEN(result) > 0) vstring_strcat(result, ","); if (dict_ldap->result_filter == NULL) vstring_strcat(result, vals[i]); else dict_ldap_expand_filter(dict_ldap->result_filter, vals[i], result); } } else if (dict_ldap->result_attributes->argv[i]) { for (i = 0; vals[i] != NULL; i++) { if (ldap_is_ldap_url(vals[i])) { if (msg_verbose) msg_info("%s: looking up URL %s", myname, vals[i]); rc = ldap_url_parse(vals[i], &url); if (rc == 0) { rc = ldap_search_st(dict_ldap->ld, url->lud_dn, url->lud_scope, url->lud_filter, url->lud_attrs, 0, &tv, &resloop); ldap_free_urldesc(url); } } else { if (msg_verbose) msg_info("%s: looking up DN %s", myname, vals[i]); rc = ldap_search_st(dict_ldap->ld, vals[i], LDAP_SCOPE_BASE, "objectclass=*", dict_ldap->result_attributes->argv, 0, &tv, &resloop); } switch (rc) { case LDAP_SUCCESS: dict_ldap_get_values(dict_ldap, resloop, result); break; case LDAP_NO_SUCH_OBJECT: /* * Go ahead and treat this as though the DN existed * and just didn't have any result attributes. */ msg_warn("%s: DN %s not found, skipping ", myname, vals[i]); break; default: msg_warn("%s: search error %d: %s ", myname, rc, ldap_err2string(rc)); dict_errno = DICT_ERR_RETRY; break; } if (resloop != 0) ldap_msgfree(resloop); } } ldap_value_free(vals); } if (ber) ber_free(ber, 0); } if (msg_verbose) msg_info("%s: Leaving %s", myname, myname); } /* dict_ldap_lookup - find database entry */ static const char *dict_ldap_lookup(DICT *dict, const char *name) { char *myname = "dict_ldap_lookup"; DICT_LDAP *dict_ldap = (DICT_LDAP *) dict; LDAPMessage *res = 0; static VSTRING *result; struct timeval tv; VSTRING *escaped_name = 0, *filter_buf = 0; int rc = 0; char *sub, *end; dict_errno = 0; if (msg_verbose) msg_info("%s: In dict_ldap_lookup", myname); /* * If they specified a domain list for this map, then only search for * addresses in domains on the list. This can significantly reduce the * load on the LDAP server. */ if (dict_ldap->domain) { const char *p = strrchr(name, '@'); if (p != 0) p = p + 1; else p = name; if (match_list_match(dict_ldap->domain, p) == 0) { if (msg_verbose) msg_info("%s: domain of %s not found in domain list", myname, name); return (0); } } /* * Initialize the result holder. */ if (result == 0) result = vstring_alloc(2); vstring_strcpy(result, ""); /* * Connect to the LDAP server, if necessary. */ if (dict_ldap->ld == NULL) { if (msg_verbose) msg_info ("%s: No existing connection for LDAP source %s, reopening", myname, dict_ldap->ldapsource); dict_ldap_connect(dict_ldap); /* * if dict_ldap_connect() set dict_errno, abort. */ if (dict_errno) return (0); } else if (msg_verbose) msg_info("%s: Using existing connection for LDAP source %s", myname, dict_ldap->ldapsource); /* * Prepare the query. */ tv.tv_sec = dict_ldap->timeout; tv.tv_usec = 0; escaped_name = vstring_alloc(20); filter_buf = vstring_alloc(30); /* * If any characters in the supplied address should be escaped per RFC * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to * Samuel Tardieu for spotting that wildcard searches were being done in * the first place, which prompted the ill-conceived lookup_wildcards * parameter and then this more comprehensive mechanism. */ end = (char *) name + strlen((char *) name); sub = (char *) strpbrk((char *) name, "*()\\\0"); if (sub && sub != end) { if (msg_verbose) msg_info("%s: Found character(s) in %s that must be escaped", myname, name); for (sub = (char *) name; sub != end; sub++) { switch (*sub) { case '*': vstring_strcat(escaped_name, "\\2a"); break; case '(': vstring_strcat(escaped_name, "\\28"); break; case ')': vstring_strcat(escaped_name, "\\29"); break; case '\\': vstring_strcat(escaped_name, "\\5c"); break; case '\0': vstring_strcat(escaped_name, "\\00"); break; default: vstring_strncat(escaped_name, sub, 1); } } if (msg_verbose) msg_info("%s: After escaping, it's %s", myname, vstring_str(escaped_name)); } else vstring_strcpy(escaped_name, (char *) name); /* * Does the supplied query_filter even include a substitution? */ if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) { /* * No, log the fact and continue. */ msg_warn("%s: Fixed query_filter %s is probably useless", myname, dict_ldap->query_filter); vstring_strcpy(filter_buf, dict_ldap->query_filter); } else { dict_ldap_expand_filter(dict_ldap->query_filter, vstring_str(escaped_name), filter_buf); } /* * On to the search. */ if (msg_verbose) msg_info("%s: Searching with filter %s", myname, vstring_str(filter_buf)); rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base, dict_ldap->scope, vstring_str(filter_buf), dict_ldap->result_attributes->argv, 0, &tv, &res); if (rc == LDAP_SERVER_DOWN) { if (msg_verbose) msg_info("%s: Lost connection for LDAP source %s, reopening", myname, dict_ldap->ldapsource); ldap_unbind(dict_ldap->ld); dict_ldap->ld = NULL; dict_ldap_connect(dict_ldap); /* * if dict_ldap_connect() set dict_errno, abort. */ if (dict_errno) return (0); rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base, dict_ldap->scope, vstring_str(filter_buf), dict_ldap->result_attributes->argv, 0, &tv, &res); } if (rc == LDAP_SUCCESS) { /* * Search worked; extract the requested result_attribute. */ dict_ldap_get_values(dict_ldap, res, result); /* * OpenLDAP's ldap_next_attribute returns a bogus * LDAP_DECODING_ERROR; I'm ignoring that for now. */ rc = dict_ldap_get_errno(dict_ldap->ld); if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR) msg_warn ("%s: Had some trouble with entries returned by search: %s", myname, ldap_err2string(rc)); if (msg_verbose) msg_info("%s: Search returned %s", myname, VSTRING_LEN(result) > 0 ? vstring_str(result) : "nothing"); } else { /* * Rats. The search didn't work. */ msg_warn("%s: Search error %d: %s ", myname, rc, ldap_err2string(rc)); /* * Tear down the connection so it gets set up from scratch on the * next lookup. */ ldap_unbind(dict_ldap->ld); dict_ldap->ld = NULL; /* * And tell the caller to try again later. */ dict_errno = DICT_ERR_RETRY; } /* * Cleanup. */ if (res != 0) ldap_msgfree(res); if (filter_buf != 0) vstring_free(filter_buf); if (escaped_name != 0) vstring_free(escaped_name); /* * If we had an error, return nothing, Otherwise, return the result, if * any. */ return (VSTRING_LEN(result) > 0 && !dict_errno ? vstring_str(result) : 0); } /* dict_ldap_close - disassociate from data base */ static void dict_ldap_close(DICT *dict) { char *myname = "dict_ldap_close"; DICT_LDAP *dict_ldap = (DICT_LDAP *) dict; if (dict_ldap->ld) ldap_unbind(dict_ldap->ld); myfree(dict_ldap->ldapsource); myfree(dict_ldap->server_host); myfree(dict_ldap->search_base); if (dict_ldap->domain) match_list_free(dict_ldap->domain); myfree(dict_ldap->query_filter); if (dict_ldap->result_filter) myfree(dict_ldap->result_filter); argv_free(dict_ldap->result_attributes); myfree(dict_ldap->bind_dn); myfree(dict_ldap->bind_pw); dict_free(dict); } /* dict_ldap_open - create association with data base */ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) { char *myname = "dict_ldap_open"; DICT_LDAP *dict_ldap; VSTRING *config_param; char *domainlist; char *scope; char *attr; if (msg_verbose) msg_info("%s: Using LDAP source %s", myname, ldapsource); dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource, sizeof(*dict_ldap)); dict_ldap->dict.lookup = dict_ldap_lookup; dict_ldap->dict.close = dict_ldap_close; dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED; dict_ldap->ldapsource = mystrdup(ldapsource); config_param = vstring_alloc(15); vstring_sprintf(config_param, "%s_server_host", ldapsource); dict_ldap->server_host = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "localhost", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->server_host); /* * get configured value of "ldapsource_server_port"; default to LDAP_PORT * (389) */ vstring_sprintf(config_param, "%s_server_port", ldapsource); dict_ldap->server_port = get_mail_conf_int(vstring_str(config_param), LDAP_PORT, 0, 0); if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->server_port); /* * Scope handling thanks to Carsten Hoeger of SuSE. */ vstring_sprintf(config_param, "%s_scope", ldapsource); scope = (char *) get_mail_conf_str(vstring_str(config_param), "sub", 0, 0); if (strcasecmp(scope, "one") == 0) { dict_ldap->scope = LDAP_SCOPE_ONELEVEL; if (msg_verbose) msg_info("%s: %s is LDAP_SCOPE_ONELEVEL", myname, vstring_str(config_param)); } else if (strcasecmp(scope, "base") == 0) { dict_ldap->scope = LDAP_SCOPE_BASE; if (msg_verbose) msg_info("%s: %s is LDAP_SCOPE_BASE", myname, vstring_str(config_param)); } else { dict_ldap->scope = LDAP_SCOPE_SUBTREE; if (msg_verbose) msg_info("%s: %s is LDAP_SCOPE_SUBTREE", myname, vstring_str(config_param)); } myfree(scope); vstring_sprintf(config_param, "%s_search_base", ldapsource); dict_ldap->search_base = mystrdup((char *) get_mail_conf_str(vstring_str (config_param), "", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->search_base); vstring_sprintf(config_param, "%s_domain", ldapsource); domainlist = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "", 0, 0)); if (*domainlist) { #ifdef MATCH_FLAG_NONE dict_ldap->domain = match_list_init(MATCH_FLAG_NONE, domainlist, 1, match_string); #else dict_ldap->domain = match_list_init(domainlist, 1, match_string); #endif if (dict_ldap->domain == NULL) msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it", myname, domainlist); if (msg_verbose) msg_info("%s: domain list created using \"%s\"", myname, domainlist); } else { dict_ldap->domain = NULL; } myfree(domainlist); /* * get configured value of "ldapsource_timeout"; default to 10 seconds * * Thanks to Manuel Guesdon for spotting that this wasn't really getting * set. */ vstring_sprintf(config_param, "%s_timeout", ldapsource); dict_ldap->timeout = get_mail_conf_int(vstring_str(config_param), 10, 0, 0); if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->timeout); vstring_sprintf(config_param, "%s_query_filter", ldapsource); dict_ldap->query_filter = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "(mailacceptinggeneralid=%s)", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->query_filter); vstring_sprintf(config_param, "%s_result_filter", ldapsource); dict_ldap->result_filter = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "%s", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->result_filter); if (strcmp(dict_ldap->result_filter, "%s") == 0) { myfree(dict_ldap->result_filter); dict_ldap->result_filter = NULL; } vstring_sprintf(config_param, "%s_result_attribute", ldapsource); attr = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "maildrop", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), attr);; dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n"); dict_ldap->num_attributes = dict_ldap->result_attributes->argc; vstring_sprintf(config_param, "%s_special_result_attribute", ldapsource); attr = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), attr); if (*attr) { argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n"); } /* * get configured value of "ldapsource_bind"; default to true */ vstring_sprintf(config_param, "%s_bind", ldapsource); dict_ldap->bind = get_mail_conf_bool(vstring_str(config_param), 1); if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->bind); /* * get configured value of "ldapsource_bind_dn"; default to "" */ vstring_sprintf(config_param, "%s_bind_dn", ldapsource); dict_ldap->bind_dn = mystrdup((char *) get_mail_conf_str(vstring_str (config_param), "", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->bind_dn); /* * get configured value of "ldapsource_bind_pw"; default to "" */ vstring_sprintf(config_param, "%s_bind_pw", ldapsource); dict_ldap->bind_pw = mystrdup((char *) get_mail_conf_str(vstring_str (config_param), "", 0, 0)); if (msg_verbose) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->bind_pw); /* * get configured value of "ldapsource_cache"; default to false */ vstring_sprintf(config_param, "%s_cache", ldapsource); dict_ldap->cache = get_mail_conf_bool(vstring_str(config_param), 0); if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->cache); /* * get configured value of "ldapsource_cache_expiry"; default to 30 * seconds */ vstring_sprintf(config_param, "%s_cache_expiry", ldapsource); dict_ldap->cache_expiry = get_mail_conf_int(vstring_str(config_param), 30, 0, 0); if (msg_verbose) msg_info("%s: %s is %ld", myname, vstring_str(config_param), dict_ldap->cache_expiry); /* * get configured value of "ldapsource_cache_size"; default to 32k */ vstring_sprintf(config_param, "%s_cache_size", ldapsource); dict_ldap->cache_size = get_mail_conf_int(vstring_str(config_param), 32768, 0, 0); if (msg_verbose) msg_info("%s: %s is %ld", myname, vstring_str(config_param), dict_ldap->cache_size); /* * Alias dereferencing suggested by Mike Mattice. */ vstring_sprintf(config_param, "%s_dereference", ldapsource); dict_ldap->dereference = get_mail_conf_int(vstring_str(config_param), 0, 0, 0); /* * Define LDAP Version. */ vstring_sprintf(config_param, "%s_version", ldapsource); dict_ldap->version = get_mail_conf_int(vstring_str(config_param), 2, 0, 0); switch (dict_ldap->version) { case 2: dict_ldap->version = LDAP_VERSION2; break; case 3: dict_ldap->version = LDAP_VERSION3; break; default: msg_warn("%s: Unknown version %d.", myname, dict_ldap->version); dict_ldap->version = LDAP_VERSION2; } /* * Make sure only valid options for alias dereferencing are used. */ if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) { msg_warn("%s: Unrecognized value %d specified for %s; using 0", myname, dict_ldap->dereference, vstring_str(config_param)); dict_ldap->dereference = 0; } if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->dereference); /* Referral chasing */ vstring_sprintf(config_param, "%s_chase_referrals", ldapsource); dict_ldap->chase_referrals = get_mail_conf_bool(vstring_str(config_param), 0); if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->chase_referrals); /* * Debug level. */ #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) vstring_sprintf(config_param, "%s_debuglevel", ldapsource); dict_ldap->debuglevel = get_mail_conf_int(vstring_str(config_param), 0, 0, 0); if (msg_verbose) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->debuglevel); #endif dict_ldap_connect(dict_ldap); /* * if dict_ldap_connect() set dict_errno, free dict_ldap and abort. */ if (dict_errno) { if (dict_ldap->ld) ldap_unbind(dict_ldap->ld); myfree((char *) dict_ldap); return (0); } /* * Otherwise, we're all set. Return the new dict_ldap structure. */ return (DICT_DEBUG (&dict_ldap->dict)); } #endif