Docs: more indexing
[exim.git] / src / src / search.c
index d1633a5e108ee310d7347a2c45ff7e8b6f42e29c..eec5437763716a48761447fe164968f16854bf8a 100644 (file)
@@ -2,8 +2,8 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2015 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* A set of functions to search databases in various formats. An open
@@ -63,11 +63,9 @@ Returns:     +ve => valid lookup name; value is offset in lookup_list
 */
 
 int
-search_findtype(const uschar *name, int len)
+search_findtype(const uschar * name, int len)
 {
-int bot = 0;
-int top = lookup_list_count;
-while (top > bot)
+for (int bot = 0, top = lookup_list_count; top > bot; )
   {
   int mid = (top + bot)/2;
   int c = Ustrncmp(name, lookup_list[mid]->name, len);
@@ -92,7 +90,7 @@ while (top > bot)
   if (c > 0) bot = mid + 1; else top = mid;
   }
 
-search_error_message = string_sprintf("unknown lookup type \"%.*s\"",len,name);
+search_error_message = string_sprintf("unknown lookup type \"%.*s\"", len, name);
 return -1;
 }
 
@@ -217,6 +215,55 @@ return stype;
 }
 
 
+/* Set the parameters for the three different kinds of lookup.
+Arguments:
+ search_type   the search-type code
+ search                the search-type string
+ query         argument for the search; filename or query
+ fnamep                pointer to return filename
+ opts          options
+
+Return:        keyquery        the search-type (for single-key) or query (for query-type)
+ */
+uschar *
+search_args(int search_type, uschar * search, uschar * query, uschar ** fnamep,
+  const uschar * opts)
+{
+Uskip_whitespace(&query);
+if (mac_islookup(search_type, lookup_absfilequery))
+  {                                    /* query-style but with file (sqlite) */
+  int sep = ',';
+
+  /* Check options first for new-style file spec */
+  if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); )
+    if (Ustrncmp(s, "file=", 5) == 0)
+      {
+      *fnamep = s+5;
+      return query;
+      }
+
+  /* If no filename from options, use old-tyle space-sep prefix on query */
+  if (*query == '/')
+    {
+    uschar * s = query;
+    while (*query && !isspace(*query)) query++;
+    *fnamep = string_copyn(s, query - s);
+    Uskip_whitespace(&query);
+    }
+  else
+    *fnamep = NULL;
+  return query;                /* remainder after file skipped */
+  }
+if (!mac_islookup(search_type, lookup_querystyle))
+  {                                    /* single-key */
+  *fnamep = query;
+  return search;       /* modifiers important so use "keyquery" for them */
+  }
+*fnamep = NULL;                                /* else query-style */
+return query;
+}
+
+
 
 /*************************************************
 *               Release cached resources         *
@@ -429,8 +476,8 @@ count alone. */
 
 if (!t)
   {
-  t = store_get(sizeof(tree_node) + Ustrlen(keybuffer), FALSE);
-  t->data.ptr = c = store_get(sizeof(search_cache), FALSE);
+  t = store_get(sizeof(tree_node) + Ustrlen(keybuffer), GET_UNTAINTED);
+  t->data.ptr = c = store_get(sizeof(search_cache), GET_UNTAINTED);
   c->item_cache = NULL;
   Ustrcpy(t->name, keybuffer);
   tree_insertnode(&search_tree, t);
@@ -531,6 +578,47 @@ else
       filename ? US"file" : US"database",
       keystring,
       filename ? US"\n  in " : US"", filename ? filename : US"");
+    if (!filename && is_tainted(keystring))
+      {
+      debug_printf_indent("                             ");
+      debug_print_taint(keystring);
+      }
+    }
+
+  /* Check that the query, for query-style lookups,
+  is either untainted or properly quoted for the lookup type.
+
+  XXX Should we this move into lf_sqlperform() ?  The server-taint check is there.
+  */
+
+  if (  !filename && lookup_list[search_type]->quote
+     && is_tainted(keystring) && !is_quoted_like(keystring, search_type))
+    {
+    uschar * s = acl_current_verb();
+    if (!s) s = authenticator_current_name();  /* must be before transport */
+    if (!s) s = transport_current_name();      /* must be before router */
+    if (!s) s = router_current_name(); /* GCC ?: would be good, but not in clang */
+    if (!s) s = US"";
+#ifdef enforce_quote_protection_notyet
+    search_error_message = string_sprintf(
+      "tainted search query is not properly quoted%s: %s%s",
+      s, keystring);
+    f.search_find_defer = TRUE;
+#else
+     {
+      int q = quoter_for_address(keystring);
+      /* If we're called from a transport, no privs to open the paniclog;
+      the logging punts to using stderr - and that seems to stop the debug
+      stream. */
+      log_write(0,
+       transport_name ? LOG_MAIN : LOG_MAIN|LOG_PANIC,
+       "tainted search query is not properly quoted%s: %s", s, keystring);
+
+      DEBUG(D_lookup) debug_printf_indent("search_type %d (%s) quoting %d (%s)\n",
+       search_type, lookup_list[search_type]->name,
+       q, is_real_quoter(q) ? lookup_list[q]->name : US"none");
+     }
+#endif
     }
 
   /* Call the code for the different kinds of search. DEFER is handled
@@ -538,7 +626,7 @@ else
   distinguish if necessary. */
 
   if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
-      &data, &search_error_message, &do_cache, opts) == DEFER)
+         &data, &search_error_message, &do_cache, opts) == DEFER)
     f.search_find_defer = TRUE;
 
   /* A record that has been found is now in data, which is either NULL
@@ -556,8 +644,8 @@ else
     if (!t)    /* No existing entry.  Create new one. */
       {
       int len = keylength + 1;
-      e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len,
-                   is_tainted(keystring));
+      /* The cache node value should never be expanded so use tainted mem */
+      e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, GET_TAINTED);
       t = (tree_node *)(e+1);
       memcpy(t->name, keystring, len);
       t->data.ptr = e;
@@ -570,8 +658,8 @@ else
     e->data.ptr = data;
     }
 
-  /* If caching was disabled, empty the cache tree. We just set the cache
-  pointer to NULL here, because we cannot release the store at this stage. */
+/* If caching was disabled, empty the cache tree. We just set the cache
+pointer to NULL here, because we cannot release the store at this stage. */
 
   else
     {
@@ -730,7 +818,7 @@ else if (partial >= 0)
   if (affixlen == 0) keystring2 = keystring; else
     {
     keystring2 = store_get(len + affixlen + 1,
-                       is_tainted(keystring) || is_tainted(affix));
+         is_tainted(keystring) || is_tainted(affix) ? GET_TAINTED : GET_UNTAINTED);
     Ustrncpy(keystring2, affix, affixlen);
     Ustrcpy(keystring2 + affixlen, keystring);
     DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2);
@@ -871,7 +959,7 @@ than the result.  Return a de-tainted version of the key on the grounds that
 it have been validated by the lookup. */
 
 if (yield && ret_key)
-  yield = string_copy_taint(keystring, FALSE);
+  yield = string_copy_taint(keystring, GET_UNTAINTED);
 
 return yield;
 }