Copyright year bumps for substantive changes 2017
[exim.git] / src / src / lookups / ldap.c
index a56eff326fac792c2433ebfd3f6bd4710c384565..235af0f937a42a43cfbae145ef9c6c55bda00a9e 100644 (file)
@@ -2,11 +2,11 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Many thanks to Stuart Lynne for contributing the original code for this
-driver. Further contibutions from Michael Haardt, Brian Candler, Barry
+driver. Further contributions from Michael Haardt, Brian Candler, Barry
 Pederson, Peter Savitch and Christian Kellner. Particular thanks to Brian for
 researching how to handle the different kinds of error. */
 
@@ -145,7 +145,7 @@ struct timeval *timeoutptr = NULL;
 
 uschar *attr;
 uschar **attrp;
-uschar *data = NULL;
+gstring * data = NULL;
 uschar *dn = NULL;
 uschar *host;
 uschar **values;
@@ -156,14 +156,12 @@ uschar *error1 = NULL;   /* string representation of errcode (static) */
 uschar *error2 = NULL;   /* error message from the server */
 uschar *matched = NULL;  /* partially matched DN */
 
-int    attr_count = 0;
+int    attrs_requested = 0;
 int    error_yield = DEFER;
 int    msgid;
 int    rc, ldap_rc, ldap_parse_rc;
 int    port;
-int    ptr = 0;
 int    rescount = 0;
-int    size = 0;
 BOOL   attribute_found = FALSE;
 BOOL   ldapi = FALSE;
 
@@ -248,7 +246,7 @@ if (host != NULL)
 /* Count the attributes; we need this later to tell us how to format results */
 
 for (attrp = USS ludp->lud_attrs; attrp != NULL && *attrp != NULL; attrp++)
-  attr_count++;
+  attrs_requested++;
 
 /* See if we can find a cached connection to this host. The port is not
 relevant for ldapi. The host name pointer is set to NULL if no host was given
@@ -580,7 +578,7 @@ if (!lcp->bound ||
   {
   DEBUG(D_lookup) debug_printf("%sbinding with user=%s password=%s\n",
     (lcp->bound)? "re-" : "", user, password);
-  if (eldap_start_tls && !lcp->is_start_tls_called)
+  if (eldap_start_tls && !lcp->is_start_tls_called && !ldapi)
     {
 #if defined(LDAP_OPT_X_TLS) && !defined(LDAP_LIB_SOLARIS)
     /* The Oracle LDAP libraries (LDAP_LIB_TYPE=SOLARIS) don't support this.
@@ -713,11 +711,16 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
         LDAP_RES_SEARCH_ENTRY)
   {
   LDAPMessage  *e;
+  int valuecount;   /* We can see an attr spread across several
+                    entries. If B is derived from A and we request
+                    A and the directory contains both, A and B,
+                    then we get two entries, one for A and one for B.
+                    Here we just count the values per entry */
 
-  DEBUG(D_lookup) debug_printf("ldap_result loop\n");
+  DEBUG(D_lookup) debug_printf("LDAP result loop\n");
 
-  for(e = ldap_first_entry(lcp->ld, result);
-      e != NULL;
+  for(e = ldap_first_entry(lcp->ld, result), valuecount = 0;
+      e;
       e = ldap_next_entry(lcp->ld, e))
     {
     uschar *new_dn;
@@ -729,7 +732,7 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
 
     /* Results for multiple entries values are separated by newlines. */
 
-    if (data != NULL) data = string_cat(data, &size, &ptr, US"\n", 1);
+    if (data) data = string_catn(data, US"\n", 1);
 
     /* Get the DN from the last result. */
 
@@ -757,73 +760,79 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
       {                                  /* condition, because of the else */
       if (new_dn != NULL)                /* below, that's for the first only */
         {
-        data = string_cat(data, &size, &ptr, new_dn, Ustrlen(new_dn));
-        data[ptr] = 0;
+        data = string_cat(data, new_dn);
+       (void) string_from_gstring(data);
         attribute_found = TRUE;
         }
       }
 
     /* Otherwise, loop through the entry, grabbing attribute values. If there's
     only one attribute being retrieved, no attribute name is given, and the
-    result is not quoted. Multiple values are separated by (comma, space).
+    result is not quoted. Multiple values are separated by (comma).
     If more than one attribute is being retrieved, the data is given as a
-    sequence of name=value pairs, with the value always in quotes. If there are
-    multiple values, they are given within the quotes, comma separated. */
+    sequence of name=value pairs, separated by (space), with the value always in quotes.
+    If there are multiple values, they are given within the quotes, comma separated. */
 
     else for (attr = US ldap_first_attribute(lcp->ld, e, &ber);
-              attr != NULL;
-              attr = US ldap_next_attribute(lcp->ld, e, ber))
+              attr; attr = US ldap_next_attribute(lcp->ld, e, ber))
       {
+      DEBUG(D_lookup) debug_printf("LDAP attr loop\n");
+
+      /* In case of attrs_requested == 1 we just count the values, in all other cases
+      (0, >1) we count the values per attribute */
+      if (attrs_requested != 1) valuecount = 0;
+
       if (attr[0] != 0)
         {
         /* Get array of values for this attribute. */
 
-        if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr))
-             != NULL)
+        if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr)))
           {
-          if (attr_count != 1)
+
+          if (attrs_requested != 1)
             {
             if (insert_space)
-              data = string_cat(data, &size, &ptr, US" ", 1);
+              data = string_catn(data, US" ", 1);
             else
               insert_space = TRUE;
-            data = string_cat(data, &size, &ptr, attr, Ustrlen(attr));
-            data = string_cat(data, &size, &ptr, US"=\"", 2);
+            data = string_cat(data, attr);
+            data = string_catn(data, US"=\"", 2);
             }
 
-          while (*values != NULL)
+          while (*values)
             {
             uschar *value = *values;
             int len = Ustrlen(value);
+            ++valuecount;
+
+            DEBUG(D_lookup) debug_printf("LDAP value loop %s:%s\n", attr, value);
 
-            DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value);
+            /* In case we requested one attribute only but got several times
+            into that attr loop, we need to append the additional values.
+            (This may happen if you derive attributeTypes B and C from A and
+            then query for A.) In all other cases we detect the different
+            attribute and append only every non first value. */
 
-           /* In case we requested one attribute only but got
-            * several times into that attr loop, we need to append
-            * the additional values. (This may happen if you derive 
-            * attributeTypes B and C from A and then query for A.)
-            * In all other cases we detect the different attribute
-            * and append only every non first value. */
-           if ((attr_count == 1 && data) || (values != firstval))
-              data = string_cat(data, &size, &ptr, US",", 1);
+            if (data && valuecount > 1)
+              data = string_catn(data, US",", 1);
 
             /* For multiple attributes, the data is in quotes. We must escape
             internal quotes, backslashes, newlines, and must double commas. */
 
-            if (attr_count != 1)
+            if (attrs_requested != 1)
               {
               int j;
               for (j = 0; j < len; j++)
                 {
                 if (value[j] == '\n')
-                  data = string_cat(data, &size, &ptr, US"\\n", 2);
+                  data = string_catn(data, US"\\n", 2);
                 else if (value[j] == ',')
-                  data = string_cat(data, &size, &ptr, US",,", 2);
+                  data = string_catn(data, US",,", 2);
                 else
                   {
                   if (value[j] == '\"' || value[j] == '\\')
-                    data = string_cat(data, &size, &ptr, US"\\", 1);
-                  data = string_cat(data, &size, &ptr, value+j, 1);
+                    data = string_catn(data, US"\\", 1);
+                  data = string_catn(data, value+j, 1);
                   }
                 }
               }
@@ -834,12 +843,10 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
              {
              int j;
              for (j = 0; j < len; j++)
-               {
                if (value[j] == ',')
-                 data = string_cat(data, &size, &ptr, US",,", 2);
+                 data = string_catn(data, US",,", 2);
                else
-                 data = string_cat(data, &size, &ptr, value+j, 1);
-               }
+                 data = string_catn(data, value+j, 1);
              }
 
 
@@ -851,8 +858,8 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
 
           /* Closing quote at the end of the data for a named attribute. */
 
-          if (attr_count != 1)
-            data = string_cat(data, &size, &ptr, US"\"", 1);
+          if (attrs_requested != 1)
+            data = string_catn(data, US"\"", 1);
 
           /* Free the values */
 
@@ -879,15 +886,15 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
 
 /* Terminate the dynamic string that we have built and reclaim unused store */
 
-if (data != NULL)
+if (data)
   {
-  data[ptr] = 0;
-  store_reset(data + ptr + 1);
+  (void) string_from_gstring(data);
+  store_reset(data->s + data->ptr + 1);
   }
 
 /* Copy the last dn into eldap_dn */
 
-if (dn != NULL)
+if (dn)
   {
   eldap_dn = string_copy(dn);
   #if defined LDAP_LIB_NETSCAPE || defined LDAP_LIB_OPENLDAP2
@@ -1066,8 +1073,8 @@ if (!attribute_found)
 
 /* Otherwise, it's all worked */
 
-DEBUG(D_lookup) debug_printf("LDAP search: returning: %s\n", data);
-*res = data;
+DEBUG(D_lookup) debug_printf("LDAP search: returning: %s\n", data->s);
+*res = data->s;
 
 RETURN_OK:
 if (result != NULL) ldap_msgfree(result);
@@ -1339,7 +1346,7 @@ The handle and filename arguments are not used. */
 
 static int
 eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1348,7 +1355,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_SINGLE, result, errmsg));
 
 static int
 eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1357,7 +1364,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_MULTIPLE, result, errmsg));
 
 static int
 eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;
@@ -1366,7 +1373,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_DN, result, errmsg));
 
 int
 eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
-  uschar **result, uschar **errmsg, BOOL *do_cache)
+  uschar **result, uschar **errmsg, uint *do_cache)
 {
 /* Keep picky compilers happy */
 do_cache = do_cache;