X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/da47dd4d092ba35e4f8ff055d79693cc1266c816..1d28cc061677bd07d9bed48dd84bd5c590247043:/src/src/search.c diff --git a/src/src/search.c b/src/src/search.c index d1633a5e1..2b6e5d37f 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -2,9 +2,10 @@ * 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. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* A set of functions to search databases in various formats. An open database is represented by a void * value which is returned from a lookup- @@ -63,11 +64,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 +91,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 +216,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 +477,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 +579,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 +627,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 +645,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 +659,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 +819,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 +960,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; }