Lookups: avoid leaking user/passwd from server spec to log. Bug 3066
[exim.git] / src / src / lookups / lf_sqlperform.c
index 2d7f326840972cf124b4136e6fbe50f55b8501f1..0f3984ba765cb2d3cb4363521ed88b5826a6af6d 100644 (file)
@@ -2,8 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "../exim.h"
 
 
 #include "../exim.h"
 
 
 
 
 
 
+static int
+server_len_for_logging(const uschar * server)
+{
+const uschar * s = Ustrchr(server, '/');
+if (!s) return 64;
+if (!(s = Ustrchr(s+1, '/'))) return 64;
+return (int) (s - server);
+}
+
 /*************************************************
 *    Call SQL server(s) to run an actual query   *
 *************************************************/
 /*************************************************
 *    Call SQL server(s) to run an actual query   *
 *************************************************/
@@ -27,7 +38,8 @@ Arguments:
   query          the query
   result         where to pass back the result
   errmsg         where to pass back an error message
   query          the query
   result         where to pass back the result
   errmsg         where to pass back an error message
-  do_cache       to be set FALSE if data is changed
+  do_cache       to be set zero if data is changed
+  opts          options (which suffixed the lookup name, minus cache-control ones) or NULL
   func           the lookup function to call
 
 Returns:         the return from the lookup function, or DEFER
   func           the lookup function to call
 
 Returns:         the return from the lookup function, or DEFER
@@ -36,55 +48,35 @@ Returns:         the return from the lookup function, or DEFER
 int
 lf_sqlperform(const uschar *name, const uschar *optionname,
   const uschar *optserverlist, const uschar *query,
 int
 lf_sqlperform(const uschar *name, const uschar *optionname,
   const uschar *optserverlist, const uschar *query,
-  uschar **result, uschar **errmsg, BOOL *do_cache,
-  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *))
+  uschar **result, uschar **errmsg, uint *do_cache, const uschar * opts,
+  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *, const uschar *))
 {
 {
-int sep, rc;
-uschar *server;
-const uschar *serverlist;
-uschar buffer[512];
+int rc;
+uschar * server;
 BOOL defer_break = FALSE;
 
 BOOL defer_break = FALSE;
 
-DEBUG(D_lookup) debug_printf("%s query: %s\n", name, query);
+DEBUG(D_lookup) debug_printf_indent("%s query: \"%s\" opts '%s'\n", name, query, opts);
 
 
-/* Handle queries that do not have server information at the start. */
+/* Handle queries that do have server information at the start (old style). */
 
 
-if (Ustrncmp(query, "servers", 7) != 0)
+if (Ustrncmp(query, "servers", 7) == 0)
   {
   {
-  sep = 0;
-  serverlist = optserverlist;
-  while ((server = string_nextinlist(&serverlist, &sep, buffer,
-          sizeof(buffer))) != NULL)
-    {
-    rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache);
-    if (rc != DEFER || defer_break) return rc;
-    }
-  if (optserverlist == NULL)
-    *errmsg = string_sprintf("no %s servers defined (%s option)", name,
-      optionname);
-  }
+  int qsep = 0;
+  const uschar * s, * ss, * qserverlist;
 
 
-/* Handle queries that do have server information at the start. */
-
-else
-  {
-  int qsep;
-  const uschar *s, *ss;
-  const uschar *qserverlist;
-  uschar *qserver;
-  uschar qbuffer[512];
+  log_write(0, LOG_MAIN|LOG_CONFIG_IN, "WARNING: obsolete syntax used for lookup");
 
   s = query + 7;
 
   s = query + 7;
-  while (isspace(*s)) s++;
+  skip_whitespace(&s);
   if (*s++ != '=')
     {
     *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name);
     return DEFER;
     }
   if (*s++ != '=')
     {
     *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name);
     return DEFER;
     }
-  while (isspace(*s)) s++;
+  skip_whitespace(&s);
 
   ss = Ustrchr(s, ';');
 
   ss = Ustrchr(s, ';');
-  if (ss == NULL)
+  if (!ss)
     {
     *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup",
       name);
     {
     *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup",
       name);
@@ -98,41 +90,102 @@ else
     return DEFER;
     }
 
     return DEFER;
     }
 
-  qserverlist = string_sprintf("%.*s", ss - s, s);
-  qsep = 0;
+  qserverlist = string_sprintf("%.*s", (int)(ss - s), s);
+  query = ss + 1;
 
 
-  while ((qserver = string_nextinlist(&qserverlist, &qsep, qbuffer,
-           sizeof(qbuffer))) != NULL)
+  for (uschar * qsrv; qsrv = string_nextinlist(&qserverlist, &qsep, NULL, 0); )
     {
     {
-    if (Ustrchr(qserver, '/') != NULL)
-      server = qserver;
+    if (Ustrchr(qsrv, '/'))
+      server = qsrv;                   /* full server spec */
     else
     else
-      {
-      int len = Ustrlen(qserver);
+      {                                        /* only name; search in option list */
+      int len = Ustrlen(qsrv);
+      const uschar * serverlist = optserverlist;
 
 
-      sep = 0;
-      serverlist = optserverlist;
-      while ((server = string_nextinlist(&serverlist, &sep, buffer,
-              sizeof(buffer))) != NULL)
-        {
-        if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/')
+      for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);)
+        if (Ustrncmp(server, qsrv, len) == 0 && server[len] == '/')
           break;
           break;
-        }
 
 
-      if (server == NULL)
+      if (!server)
         {
         {
-        *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
-          qserver, optionname);
+        *errmsg = string_sprintf("%s server \"%.*s\" not found in %s",
+         name, server_len_for_logging(qsrv), qsrv, optionname);
         return DEFER;
         }
       }
 
         return DEFER;
         }
       }
 
-    rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache);
+    if (is_tainted(server))
+      {
+      *errmsg = string_sprintf("%s server \"%.*s\" is tainted",
+       name, server_len_for_logging(server), server);
+      return DEFER;
+      }
+
+    rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
     if (rc != DEFER || defer_break) return rc;
     }
   }
 
     if (rc != DEFER || defer_break) return rc;
     }
   }
 
+/* Handle queries that do not have server information at the start. */
+
+else
+  {
+  const uschar * serverlist = NULL;
+
+  /* If options are present, scan for a server definition.  Default to
+  the "optserverlist" srgument. */
+
+  if (opts)
+    {
+    uschar * ele;
+    for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); )
+      if (Ustrncmp(ele, "servers=", 8) == 0)
+       { serverlist = ele + 8; break; }
+    }
+
+  if (!serverlist)
+    serverlist = optserverlist;
+  if (!serverlist)
+    *errmsg = string_sprintf("no %s servers defined (%s option)", name,
+      optionname);
+  else
+    for (int d = 0; server = string_nextinlist(&serverlist, &d, NULL, 0); )
+      {
+      /* If not a full spec assume from options; scan main list for matching
+      hostname */
+
+      if (!Ustrchr(server, '/'))
+       {
+       int len = Ustrlen(server);
+       const uschar * slist = optserverlist;
+       uschar * ele;
+       for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); )
+         if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/')
+           break;
+       if (!ele)
+         {
+         *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
+           server, optionname);
+         return DEFER;
+         }
+       server = ele;
+       }
+
+      if (is_tainted(server))
+        {
+        *errmsg = string_sprintf("%s server \"%.*s\" is tainted",
+         name, server_len_for_logging(server), server);
+        return DEFER;
+        }
+
+      rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
+      if (rc != DEFER || defer_break) return rc;
+      }
+  }
+
 return DEFER;
 }
 
 /* End of lf_sqlperform.c */
 return DEFER;
 }
 
 /* End of lf_sqlperform.c */
+/* vi: aw ai sw=2
+*/