On Wed, Jul 12, 2006 at 07:46:25PM +0100, Alain Williams wrote:
> Hi,
>
> I am trying to use LDAP from exim to determine which (MS Exchange) machine to deliver mail to.
> This is in a largish (1500) company with multiple sites. I am querying the local MS Active Directory servers.
> My Linux box is relegated to the DMZ and is restricted to what it can see.
> ....
I have now found and fixed this problem. The LDAP servers (MS Active Directory) all had the
full database (through replication) but insisted on sending referrals back to the client;
the client (exim) was unable to reach these other servers due to firewall/... restrictions.
There is a standard LDAP library option to not follow referrals, exim does not allow this to
be set (it allows DEREFERENCEing) which is different. Please find attached a patch that adds this
to exim; the default is the standard LDAP default of following referrals - so existing configurations
should not break.
I have tested this on my client's box (RedHat enterprise 4 (openldap-2.2.13), with MS Active
Directory LDAP servers) and it works for me. It *should* work elsewhere. If it does not
compile, try surrounding my additions with:
#ifdef LDAP_OPT_REFERRALS
--
Alain Williams
Parliament Hill Computers Ltd.
Linux Consultant - Mail systems, Web sites, Networking, Programmer, IT Lecturer.
+44 (0) 787 668 0256
http://www.phcomp.co.uk/
#include <std_disclaimer.h>
--- src/lookups/ldap.c.old 2006-04-28 11:32:22.000000000 +0100
+++ src/lookups/ldap.c 2006-07-13 08:46:03.233143808 +0100
@@ -132,6 +132,8 @@
tcplimit max time for network activity, e.g. connect, or 0 for OS default
deference the dereference option, which is one of
LDAP_DEREF_{NEVER,SEARCHING,FINDING,ALWAYS}
+ referrals Specifies whether referrals returned by LDAP servers will be followed,
+ one of: LDAP_OPT_ON or LDAP_OPT_OFF
Returns: OK or FAIL or DEFER
FAIL is given only if a lookup was performed successfully, but
@@ -141,7 +143,7 @@
static int
perform_ldap_search(uschar *ldap_url, uschar *server, int s_port, int search_type,
uschar **res, uschar **errmsg, BOOL *defer_break, uschar *user, uschar *password,
- int sizelimit, int timelimit, int tcplimit, int dereference)
+ int sizelimit, int timelimit, int tcplimit, int dereference, void* referrals)
{
LDAPURLDesc *ludp = NULL;
LDAPMessage *result = NULL;
@@ -551,6 +553,9 @@
ldap_set_option(lcp->ld, LDAP_OPT_DEREF, (void *)&dereference);
#endif
+/* Should the library follow referrals that the LDAP server may return: */
+ldap_set_option( lcp->ld, LDAP_OPT_REFERRALS, referrals);
+
/* Start the search on the server. */
DEBUG(D_lookup) debug_printf("Start search\n");
@@ -973,6 +978,7 @@
int sizelimit = LDAP_NO_LIMIT;
int tcplimit = 0;
int dereference = LDAP_DEREF_NEVER;
+void* referrals = LDAP_OPT_ON;
int sep = 0;
uschar *url = ldap_url;
uschar *p;
@@ -1030,6 +1036,19 @@
#endif
+ else if (strncmpic(name, US"REFERRALS=", namelen) == 0)
+ {
+ if (strcmpic(value, US"follow") == 0) referrals = LDAP_OPT_ON;
+ else if (strcmpic(value, US"nofollow") == 0)
+ referrals = LDAP_OPT_OFF;
+ else
+ {
+ *errmsg = string_sprintf("LDAP option REFERRALS not given value follow or nofollow");
+ DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+ return DEFER;
+ }
+ }
+
else
{
*errmsg =
@@ -1076,8 +1095,8 @@
DEBUG(D_lookup)
debug_printf("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d "
- "dereference=%d\n", user, password, sizelimit, timelimit, tcplimit,
- dereference);
+ "dereference=%d referrals=%s\n", user, password, sizelimit, timelimit, tcplimit,
+ dereference, (referrals == LDAP_OPT_ON ? "on" : "off"));
/* If the request is just to check authentication, some credentials must
be given. The password must not be empty because LDAP binds with an empty
@@ -1114,7 +1133,7 @@
if (eldap_default_servers == NULL || p[3] != '/')
{
return perform_ldap_search(url, NULL, 0, search_type, res, errmsg,
- &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference);
+ &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, referrals);
}
/* Loop through the default servers until OK or FAIL */
@@ -1131,7 +1150,7 @@
port = Uatoi(colon+1);
}
rc = perform_ldap_search(url, server, port, search_type, res, errmsg,
- &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference);
+ &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, referrals);
if (rc != DEFER || defer_break) return rc;
}
--- doc/spec.txt.old 2006-04-28 11:32:23.000000000 +0100
+++ doc/spec.txt 2006-07-13 10:05:36.521493680 +0100
@@ -6291,10 +6291,27 @@
PASS set the password, likewise
SIZE set the limit for the number of entries returned
TIME set the maximum waiting time for a query
+REFERRALS set if referrals to other LDAP servers are to be followed
The value of the DEREFERENCE parameter must be one of the words "never",
"searching", "finding", or "always".
+The value of the REFERRALS parameter must be one of the words "follow" or
+"nofollow", the default is "follow". If there are several LDAP servers the
+first one queried may return the addresses of other LDAP servers of which the
+client (ie exim) may ask the same question. This referral to other LDAP
+servers makes sense in a distributed environment where no one server may have
+all of the answers. This is likely to be returned where the baseDN only covers
+part of the domain - so the referrals suggest where to find out about other
+parts of the domain - this is termed following a referral and is the default
+action of the LDAP library. In a redundent system each LDAP server may be master
+of it's part of the domain but also keeps the information for the entire domain;
+so in spite of it knowing (and returning) all the answers it may still refer
+to other servers (Microsoft Active Directory does this). A problem may arise
+if the other servers are not available, in which case exim may loop in the
+LDAP library trying to access the other servers. NETTIME does not help since
+it is the timeout for accessing an individual server.
+
The name CONNECT is an obsolete name for NETTIME, retained for backwards
compatibility. This timeout (specified as a number of seconds) is enforced from
the client end for operations that can be carried out over a network.