* 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
*/
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);
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;
}
}
+/* 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 *
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);
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
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
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;
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
{
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);
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;
}