DSN: add References: header. Bug 2452
[exim.git] / src / src / lookups / ldap.c
index 63c0edf7979460c90a6738a962ad2572c78f5a9e..5b0cffaf8c88245960db21b0966361adb7946043 100644 (file)
@@ -143,8 +143,6 @@ LDAP_CONNECTION *lcp;
 struct timeval timeout;
 struct timeval *timeoutptr = NULL;
 
-uschar *attr;
-uschar **attrp;
 gstring * data = NULL;
 uschar *dn = NULL;
 uschar *host;
@@ -165,8 +163,8 @@ int    rescount = 0;
 BOOL   attribute_found = FALSE;
 BOOL   ldapi = FALSE;
 
-DEBUG(D_lookup)
-  debug_printf("perform_ldap_search: ldap%s URL = \"%s\" server=%s port=%d "
+DEBUG(D_lookup) debug_printf_indent("perform_ldap_search:"
+    " ldap%s URL = \"%s\" server=%s port=%d "
     "sizelimit=%d timelimit=%d tcplimit=%d\n",
     search_type == SEARCH_LDAP_MULTIPLE ? "m" :
     search_type == SEARCH_LDAP_DN       ? "dn" :
@@ -209,7 +207,7 @@ else
   port = ludp->lud_port;
   }
 
-DEBUG(D_lookup) debug_printf("after ldap_url_parse: host=%s port=%d\n",
+DEBUG(D_lookup) debug_printf_indent("after ldap_url_parse: host=%s port=%d\n",
   host, port);
 
 if (port == 0) port = LDAP_PORT;      /* Default if none given */
@@ -245,7 +243,7 @@ if (host)
 
 /* Count the attributes; we need this later to tell us how to format results */
 
-for (attrp = USS ludp->lud_attrs; attrp && *attrp; attrp++)
+for (uschar ** attrp = USS ludp->lud_attrs; attrp && *attrp; attrp++)
   attrs_requested++;
 
 /* See if we can find a cached connection to this host. The port is not
@@ -303,24 +301,18 @@ if (!lcp)
   than the host name + "ldaps:///" plus : and a port number, say 20 + the
   length of the host name. What we get should accommodate both, easily. */
 
-  uschar *shost = (host == NULL)? US"" : host;
-  uschar *init_url = store_get(20 + 3 * Ustrlen(shost));
-  uschar *init_ptr;
+  uschar * shost = host ? host : US"";
+  rmark reset_point = store_mark();
+  gstring * g;
 
   /* Handle connection via Unix socket ("ldapi"). We build a basic LDAP URI to
   contain the path name, with slashes escaped as %2F. */
 
   if (ldapi)
     {
-    int ch;
-    init_ptr = init_url + 8;
-    Ustrcpy(init_url, "ldapi://");
-    while ((ch = *shost++))
-      if (ch == '/')
-       { Ustrncpy(init_ptr, "%2F", 3); init_ptr += 3; }
-      else
-       *init_ptr++ = ch;
-    *init_ptr = 0;
+    g = string_catn(NULL, US"ldapi://", 8);
+    for (uschar ch; (ch = *shost); shost++)
+      g = ch == '/' ? string_catn(g, US"%2F", 3) : string_catn(g, shost, 1);
     }
 
   /* This is not an ldapi call. Just build a URI with the protocol type, host
@@ -328,22 +320,22 @@ if (!lcp)
 
   else
     {
-    init_ptr = Ustrchr(ldap_url, '/');
-    Ustrncpy(init_url, ldap_url, init_ptr - ldap_url);
-    init_ptr = init_url + (init_ptr - ldap_url);
-    sprintf(CS init_ptr, "//%s:%d/", shost, port);
+    uschar * init_ptr = Ustrchr(ldap_url, '/');
+    g = string_catn(NULL, ldap_url, init_ptr - ldap_url);
+    g = string_fmt_append(g, "//%s:%d/", shost, port);
     }
+  string_from_gstring(g);
 
   /* Call ldap_initialize() and check the result */
 
-  DEBUG(D_lookup) debug_printf("ldap_initialize with URL %s\n", init_url);
-  if ((rc = ldap_initialize(&ld, CS init_url)) != LDAP_SUCCESS)
+  DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", g->s);
+  if ((rc = ldap_initialize(&ld, CS g->s)) != LDAP_SUCCESS)
     {
     *errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n",
-      rc, init_url);
+      rc, g->s);
     goto RETURN_ERROR;
     }
-  store_reset(init_url);   /* Might as well save memory when we can */
+  store_reset(reset_point);   /* Might as well save memory when we can */
 
 
   /* ------------------------- Not OpenLDAP ---------------------- */
@@ -411,7 +403,7 @@ if (!lcp)
   ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void *)&eldap_version);
 #endif
 
-  DEBUG(D_lookup) debug_printf("initialized for LDAP (v%d) server %s%s\n",
+  DEBUG(D_lookup) debug_printf_indent("initialized for LDAP (v%d) server %s%s\n",
     eldap_version, host, porttext);
 
   /* If not using ldapi and TLS is available, set appropriate TLS options: hard
@@ -431,9 +423,9 @@ if (!lcp)
        : Ustrcmp(eldap_require_cert, "try")    == 0 ? LDAP_OPT_X_TLS_TRY
        : LDAP_OPT_X_TLS_NEVER;
 
-      DEBUG(D_lookup)
-        debug_printf("Require certificate overrides LDAP_OPT_X_TLS option (%d)\n",
-                     tls_option);
+      DEBUG(D_lookup) debug_printf_indent(
+       "Require certificate overrides LDAP_OPT_X_TLS option (%d)\n",
+       tls_option);
       }
     else
 # endif  /* LDAP_OPT_X_TLS_REQUIRE_CERT */
@@ -441,13 +433,13 @@ if (!lcp)
       {
       tls_option = LDAP_OPT_X_TLS_HARD;
       DEBUG(D_lookup)
-        debug_printf("LDAP_OPT_X_TLS_HARD set due to ldaps:// URI\n");
+        debug_printf_indent("LDAP_OPT_X_TLS_HARD set due to ldaps:// URI\n");
       }
     else
       {
       tls_option = LDAP_OPT_X_TLS_TRY;
       DEBUG(D_lookup)
-        debug_printf("LDAP_OPT_X_TLS_TRY set due to ldap:// URI\n");
+        debug_printf_indent("LDAP_OPT_X_TLS_TRY set due to ldap:// URI\n");
       }
     ldap_set_option(ld, LDAP_OPT_X_TLS, (void *)&tls_option);
     }
@@ -490,21 +482,21 @@ if (!lcp)
     rc = ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_REQUIRE_CERT, &cert_option);
     if (rc)
       DEBUG(D_lookup)
-        debug_printf("Unable to set TLS require cert_option(%d) globally: %s\n",
+        debug_printf_indent("Unable to set TLS require cert_option(%d) globally: %s\n",
           cert_option, ldap_err2string(rc));
     }
 #endif
 #ifdef LDAP_OPT_X_TLS_NEWCTX
   if ((rc = ldap_set_option(ldsetctx, LDAP_OPT_X_TLS_NEWCTX, &am_server)))
     DEBUG(D_lookup)
-      debug_printf("Unable to reload TLS context %d: %s\n",
+      debug_printf_indent("Unable to reload TLS context %d: %s\n",
                    rc, ldap_err2string(rc));
   #endif
 
   /* Now add this connection to the chain of cached connections */
 
-  lcp = store_get(sizeof(LDAP_CONNECTION));
-  lcp->host = (host == NULL)? NULL : string_copy(host);
+  lcp = store_get(sizeof(LDAP_CONNECTION), FALSE);
+  lcp->host = host ? string_copy(host) : NULL;
   lcp->bound = FALSE;
   lcp->user = NULL;
   lcp->password = NULL;
@@ -519,7 +511,7 @@ if (!lcp)
 
 else
   DEBUG(D_lookup)
-    debug_printf("re-using cached connection to LDAP server %s%s\n",
+    debug_printf_indent("re-using cached connection to LDAP server %s%s\n",
       host, porttext);
 
 /* Bind with the user/password supplied, or an anonymous bind if these values
@@ -534,7 +526,7 @@ if (  !lcp->bound
    || lcp->password && password && Ustrcmp(lcp->password, password) != 0
    )
   {
-  DEBUG(D_lookup) debug_printf("%sbinding with user=%s password=%s\n",
+  DEBUG(D_lookup) debug_printf_indent("%sbinding with user=%s password=%s\n",
     lcp->bound ? "re-" : "", user, password);
 
   if (eldap_start_tls && !lcp->is_start_tls_called && !ldapi)
@@ -553,7 +545,7 @@ if (  !lcp->bound
       }
     lcp->is_start_tls_called = TRUE;
 #else
-    DEBUG(D_lookup) debug_printf("TLS initiation not supported with this Exim"
+    DEBUG(D_lookup) debug_printf_indent("TLS initiation not supported with this Exim"
       " and your LDAP library.\n");
 #endif
     }
@@ -582,7 +574,7 @@ if (  !lcp->bound
   if (search_type == SEARCH_LDAP_AUTH && rc == LDAP_INVALID_CREDENTIALS)
     {
     DEBUG(D_lookup)
-      debug_printf("Invalid credentials: ldapauth returns FAIL\n");
+      debug_printf_indent("Invalid credentials: ldapauth returns FAIL\n");
     error_yield = FAIL;
     goto RETURN_ERROR_NOMSG;
     }
@@ -611,7 +603,7 @@ if (  !lcp->bound
 
 if (search_type == SEARCH_LDAP_AUTH)
   {
-  DEBUG(D_lookup) debug_printf("Bind succeeded: ldapauth returns OK\n");
+  DEBUG(D_lookup) debug_printf_indent("Bind succeeded: ldapauth returns OK\n");
   goto RETURN_OK;
   }
 
@@ -643,7 +635,7 @@ ldap_set_option(lcp->ld, LDAP_OPT_REFERRALS, referrals);
 
 /* Start the search on the server. */
 
-DEBUG(D_lookup) debug_printf("Start search\n");
+DEBUG(D_lookup) debug_printf_indent("Start search\n");
 
 msgid = ldap_search(lcp->ld, ludp->lud_dn, ludp->lud_scope, ludp->lud_filter,
   ludp->lud_attrs, 0);
@@ -675,7 +667,7 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
                     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_indent("LDAP result loop\n");
 
   for(e = ldap_first_entry(lcp->ld, result), valuecount = 0;
       e;
@@ -684,7 +676,7 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
     uschar *new_dn;
     BOOL insert_space = FALSE;
 
-    DEBUG(D_lookup) debug_printf("LDAP entry loop\n");
+    DEBUG(D_lookup) debug_printf_indent("LDAP entry loop\n");
 
     rescount++;   /* Count results */
 
@@ -730,10 +722,10 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
     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);
+    else for (uschar * attr = US ldap_first_attribute(lcp->ld, e, &ber);
               attr; attr = US ldap_next_attribute(lcp->ld, e, ber))
       {
-      DEBUG(D_lookup) debug_printf("LDAP attr loop\n");
+      DEBUG(D_lookup) debug_printf_indent("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 */
@@ -761,7 +753,7 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
             int len = Ustrlen(value);
             ++valuecount;
 
-            DEBUG(D_lookup) debug_printf("LDAP value loop %s:%s\n", attr, value);
+            DEBUG(D_lookup) debug_printf_indent("LDAP value 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.
@@ -776,9 +768,7 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
             internal quotes, backslashes, newlines, and must double commas. */
 
             if (attrs_requested != 1)
-              {
-              int j;
-              for (j = 0; j < len; j++)
+              for (int j = 0; j < len; j++)
                 {
                 if (value[j] == '\n')
                   data = string_catn(data, US"\\n", 2);
@@ -791,19 +781,15 @@ while ((rc = ldap_result(lcp->ld, msgid, 0, timeoutptr, &result)) ==
                   data = string_catn(data, value+j, 1);
                   }
                 }
-              }
 
             /* For single attributes, just double commas */
 
            else
-             {
-             int j;
-             for (j = 0; j < len; j++)
+             for (int j = 0; j < len; j++)
                if (value[j] == ',')
                  data = string_catn(data, US",,", 2);
                else
                  data = string_catn(data, value+j, 1);
-             }
 
 
             /* Move on to the next value */
@@ -846,7 +832,7 @@ an empty string. */
 
 if (!data) data = string_get(1);
 (void) string_from_gstring(data);
-gstring_reset_unused(data);
+gstring_release_unused(data);
 
 /* Copy the last dn into eldap_dn */
 
@@ -860,7 +846,7 @@ if (dn)
 #endif
   }
 
-DEBUG(D_lookup) debug_printf("search ended by ldap_result yielding %d\n",rc);
+DEBUG(D_lookup) debug_printf_indent("search ended by ldap_result yielding %d\n",rc);
 
 if (rc == 0)
   {
@@ -882,7 +868,7 @@ methods of handling error codes and generating error messages. */
 if (rc == -1 || !result)
   {
   int err;
-  DEBUG(D_lookup) debug_printf("ldap_result failed\n");
+  DEBUG(D_lookup) debug_printf_indent("ldap_result failed\n");
 
 #if defined LDAP_LIB_SOLARIS || defined LDAP_LIB_OPENLDAP2
     ldap_get_option(lcp->ld, LDAP_OPT_ERROR_NUMBER, &err);
@@ -925,7 +911,7 @@ We need to parse the message to find out exactly what's happened. */
   ldap_rc = rc;
   ldap_parse_rc = ldap_parse_result(lcp->ld, result, &rc, CSS &matched,
     CSS &error2, NULL, NULL, 0);
-  DEBUG(D_lookup) debug_printf("ldap_parse_result: %d\n", ldap_parse_rc);
+  DEBUG(D_lookup) debug_printf_indent("ldap_parse_result: %d\n", ldap_parse_rc);
   if (ldap_parse_rc < 0 &&
       (ldap_parse_rc != LDAP_NO_RESULTS_RETURNED
       #ifdef LDAP_RES_SEARCH_REFERENCE
@@ -967,7 +953,7 @@ We need to parse the message to find out exactly what's happened. */
       the lookup, so return DEFER (which is the default in error_yield).
 */
 
-DEBUG(D_lookup) debug_printf("ldap_parse_result yielded %d: %s\n",
+DEBUG(D_lookup) debug_printf_indent("ldap_parse_result yielded %d: %s\n",
   rc, ldap_err2string(rc));
 
 if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED
@@ -993,7 +979,7 @@ if (rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED
 #endif
 
     {
-    DEBUG(D_lookup) debug_printf("lookup failure forced\n");
+    DEBUG(D_lookup) debug_printf_indent("lookup failure forced\n");
     error_yield = FAIL;
     }
   goto RETURN_ERROR;
@@ -1012,7 +998,7 @@ if (search_type != SEARCH_LDAP_MULTIPLE && rescount > 1)
 
 if (rescount < 1)
   {
-  *errmsg = string_sprintf("LDAP search: no results");
+  *errmsg = US"LDAP search: no results";
   error_yield = FAIL;
   goto RETURN_ERROR_BREAK;
   }
@@ -1029,7 +1015,7 @@ if (!attribute_found)
 
 /* Otherwise, it's all worked */
 
-DEBUG(D_lookup) debug_printf("LDAP search: returning: %s\n", data->s);
+DEBUG(D_lookup) debug_printf_indent("LDAP search: returning: %s\n", data->s);
 *res = data->s;
 
 RETURN_OK:
@@ -1043,7 +1029,7 @@ RETURN_ERROR_BREAK:
 *defer_break = TRUE;
 
 RETURN_ERROR:
-DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
 
 RETURN_ERROR_NOMSG:
 if (result) ldap_msgfree(result);
@@ -1152,7 +1138,7 @@ while (strncmpic(url, US"ldap", 4) != 0)
         {
         *errmsg = string_sprintf("LDAP_OP_DEREF not defined in this LDAP "
           "library - cannot use \"dereference\"");
-        DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+        DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
         return DEFER;
         }
       #endif
@@ -1164,9 +1150,8 @@ while (strncmpic(url, US"ldap", 4) != 0)
         else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF;
         else
           {
-          *errmsg = string_sprintf("LDAP option REFERRALS is not \"follow\" "
-            "or \"nofollow\"");
-          DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+          *errmsg = US"LDAP option REFERRALS is not \"follow\" or \"nofollow\"";
+          DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
           return DEFER;
           }
         }
@@ -1175,7 +1160,7 @@ while (strncmpic(url, US"ldap", 4) != 0)
         {
         *errmsg = string_sprintf("LDAP_OP_REFERRALS not defined in this LDAP "
           "library - cannot use \"referrals\"");
-        DEBUG(D_lookup) debug_printf("%s\n", *errmsg);
+        DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
         return DEFER;
         }
       #endif
@@ -1185,7 +1170,7 @@ while (strncmpic(url, US"ldap", 4) != 0)
         *errmsg =
           string_sprintf("unknown parameter \"%.*s\" precedes LDAP URL",
             namelen, name);
-        DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
+        DEBUG(D_lookup) debug_printf_indent("LDAP query error: %s\n", *errmsg);
         return DEFER;
         }
       while (isspace(*url)) url++;
@@ -1193,7 +1178,7 @@ while (strncmpic(url, US"ldap", 4) != 0)
       }
     }
   *errmsg = US"malformed parameter setting precedes LDAP URL";
-  DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
+  DEBUG(D_lookup) debug_printf_indent("LDAP query error: %s\n", *errmsg);
   return DEFER;
   }
 
@@ -1205,9 +1190,8 @@ far too complicated. */
 
 if (user != NULL)
   {
-  uschar *s;
   uschar *t = user;
-  for (s = user; *s != 0; s++)
+  for (uschar * s = user; *s != 0; s++)
     {
     int c, d;
     if (*s == '%' && isxdigit(c=s[1]) && isxdigit(d=s[2]))
@@ -1225,7 +1209,7 @@ if (user != NULL)
   }
 
 DEBUG(D_lookup)
-  debug_printf("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d "
+  debug_printf_indent("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d "
     "dereference=%d referrals=%s\n", user, password, sizelimit, timelimit,
     tcplimit, dereference, (referrals == LDAP_OPT_ON)? "on" : "off");
 
@@ -1242,7 +1226,7 @@ if (search_type == SEARCH_LDAP_AUTH)
     }
   if (password[0] == 0)
     {
-    DEBUG(D_lookup) debug_printf("Empty password: ldapauth returns FAIL\n");
+    DEBUG(D_lookup) debug_printf_indent("Empty password: ldapauth returns FAIL\n");
     return FAIL;
     }
   }
@@ -1255,7 +1239,7 @@ if (Ustrncmp(p, "://", 3) != 0)
   {
   *errmsg = string_sprintf("LDAP URL does not start with \"ldap://\", "
     "\"ldaps://\", or \"ldapi://\" (it starts with \"%.16s...\")", url);
-  DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg);
+  DEBUG(D_lookup) debug_printf_indent("LDAP query error: %s\n", *errmsg);
   return DEFER;
   }
 
@@ -1367,7 +1351,7 @@ eldap_dn = NULL;
 
 while ((lcp = ldap_connections) != NULL)
   {
-  DEBUG(D_lookup) debug_printf("unbind LDAP connection to %s:%d\n", lcp->host,
+  DEBUG(D_lookup) debug_printf_indent("unbind LDAP connection to %s:%d\n", lcp->host,
     lcp->port);
   if(lcp->bound == TRUE)
     ldap_unbind(lcp->ld);
@@ -1492,7 +1476,7 @@ if (count == 0) return s;
 
 /* Get sufficient store to hold the quoted string */
 
-t = quoted = store_get(len + count + 1);
+t = quoted = store_get(len + count + 1, is_tainted(s));
 
 /* Handle plain quote_ldap */
 
@@ -1547,7 +1531,7 @@ else
       {
       if (Ustrchr(LDAP_DN_QUOTE, c) != NULL)
         {
-        Ustrncpy(t, "%5C", 3);               /* insert \ where needed */
+        Ustrncpy(t, US"%5C", 3);               /* insert \ where needed */
         t += 3;                              /* fall through to check URL */
         }
       if (Ustrchr(URL_NONQUOTE, c) == NULL)  /* e.g. ] => %5D */
@@ -1564,7 +1548,7 @@ else
 
   while (*ss++ != 0)
     {
-    Ustrncpy(t, "%5C%20", 6);
+    Ustrncpy(t, US"%5C%20", 6);
     t += 6;
     }
   }