Re: [exim] LDAP problem - Fix & patch to exim

Top Page
Delete this message
Reply to this message
Author: Alain Williams
Date:  
To: exim-users
Old-Topics: [exim] LDAP problem
Subject: Re: [exim] LDAP problem - Fix & patch to exim
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.