X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/24623111ecf127cb91a9b91942af29e5b5dc40ca..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/filter.c diff --git a/src/src/filter.c b/src/src/filter.c index bd9a3f15e..82a9122c6 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -2,9 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for mail filtering functions. */ @@ -498,7 +499,7 @@ for (;;) /* Build a condition block from the specific word. */ - c = store_get(sizeof(condition_block), FALSE); + c = store_get(sizeof(condition_block), GET_UNTAINTED); c->left.u = c->right.u = NULL; c->testfor = testfor; testfor = TRUE; @@ -528,7 +529,7 @@ for (;;) } ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE); if (*error_pointer) break; - aa = store_get(sizeof(string_item), FALSE); + aa = store_get(sizeof(string_item), GET_UNTAINTED); aa->text = string_copy(buffer); aa->next = c->left.a; c->left.a = aa; @@ -655,7 +656,7 @@ for (;;) else { - const uschar *saveptr = ptr; +// const uschar *saveptr = ptr; ptr = nextword(ptr, buffer, sizeof(buffer), FALSE); if (*error_pointer) break; @@ -684,7 +685,7 @@ for (;;) else if (Ustrcmp(buffer, "and") == 0) { - condition_block *andc = store_get(sizeof(condition_block), FALSE); + condition_block * andc = store_get(sizeof(condition_block), GET_UNTAINTED); andc->parent = current_parent; andc->type = cond_and; andc->testfor = TRUE; @@ -702,8 +703,8 @@ for (;;) else if (Ustrcmp(buffer, "or") == 0) { - condition_block *orc = store_get(sizeof(condition_block), FALSE); - condition_block *or_parent = NULL; + condition_block * orc = store_get(sizeof(condition_block), GET_UNTAINTED); + condition_block * or_parent = NULL; if (current_parent) { @@ -860,6 +861,8 @@ terminated by white space, but there are two exceptions, which are the "if" and as brackets are allowed in conditions and users will expect not to require white space here. */ +*buffer = '\0'; /* compiler quietening */ + if (Ustrncmp(ptr, "if(", 3) == 0) { Ustrcpy(buffer, US"if"); @@ -1019,9 +1022,10 @@ switch (command) FALSE for logging commands, and it doesn't matter for testprint, as that doesn't change the "delivered" status. */ - if (*error_pointer) yield = FALSE; else + if (*error_pointer) yield = FALSE; + else { - new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), FALSE); + new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), GET_UNTAINTED); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); @@ -1105,12 +1109,12 @@ switch (command) /* Finish has no arguments; fmsg defaults to NULL */ case finish_command: - new = store_get(sizeof(filter_cmd), FALSE); + new = store_get(sizeof(filter_cmd), GET_UNTAINTED); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); new->command = command; - new->seen = seen_force? seen_value : FALSE; + new->seen = seen_force ? seen_value : FALSE; new->args[0].u = fmsg; break; @@ -1129,7 +1133,7 @@ switch (command) /* Set up the command block for if */ - new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); + new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &new->next; @@ -1157,7 +1161,7 @@ switch (command) while (had_else_endif == had_elif) { filter_cmd *newnew = - store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); + store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); new->args[2].f = newnew; new = newnew; new->next = NULL; @@ -1210,10 +1214,10 @@ switch (command) case mail_command: case vacation_command: - new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), FALSE); + new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), GET_UNTAINTED); new->next = NULL; new->command = command; - new->seen = seen_force? seen_value : FALSE; + new->seen = seen_force ? seen_value : FALSE; new->noerror = noerror_force; for (i = 0; i < mailargs_total; i++) new->args[i].u = NULL; @@ -1287,18 +1291,18 @@ switch (command) if (command == vacation_command) { - if (new->args[mailarg_index_file].u == NULL) + if (!new->args[mailarg_index_file].u) { new->args[mailarg_index_file].u = string_copy(US".vacation.msg"); new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */ } - if (new->args[mailarg_index_log].u == NULL) + if (!new->args[mailarg_index_log].u) new->args[mailarg_index_log].u = string_copy(US".vacation.log"); - if (new->args[mailarg_index_once].u == NULL) + if (!new->args[mailarg_index_once].u) new->args[mailarg_index_once].u = string_copy(US".vacation"); - if (new->args[mailarg_index_once_repeat].u == NULL) + if (!new->args[mailarg_index_once_repeat].u) new->args[mailarg_index_once_repeat].u = string_copy(US"7d"); - if (new->args[mailarg_index_subject].u == NULL) + if (!new->args[mailarg_index_subject].u) new->args[mailarg_index_subject].u = string_copy(US"On vacation"); } @@ -1421,213 +1425,203 @@ Returns: TRUE if the condition is met */ static BOOL -test_condition(condition_block *c, BOOL toplevel) +test_condition(condition_block * c, BOOL toplevel) { -BOOL yield = FALSE; -const uschar *exp[2], * p, * pp; +BOOL yield = FALSE, textonly_re; +const uschar * exp[2], * p, * pp; int val[2]; -int i; -if (c == NULL) return TRUE; /* does this ever occur? */ +if (!c) return TRUE; /* does this ever occur? */ switch (c->type) { case cond_and: - yield = test_condition(c->left.c, FALSE) && - *error_pointer == NULL && - test_condition(c->right.c, FALSE); - break; + yield = test_condition(c->left.c, FALSE) && + *error_pointer == NULL && + test_condition(c->right.c, FALSE); + break; case cond_or: - yield = test_condition(c->left.c, FALSE) || - (*error_pointer == NULL && - test_condition(c->right.c, FALSE)); - break; + yield = test_condition(c->left.c, FALSE) || + (*error_pointer == NULL && + test_condition(c->right.c, FALSE)); + break; - /* The personal test is meaningless in a system filter. The tests are now in - a separate function (so Sieve can use them). However, an Exim filter does not - scan Cc: (hence the FALSE argument). */ + /* The personal test is meaningless in a system filter. The tests are now in + a separate function (so Sieve can use them). However, an Exim filter does not + scan Cc: (hence the FALSE argument). */ case cond_personal: - yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE); - break; + yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE); + break; case cond_delivered: - yield = filter_delivered; - break; + yield = filter_delivered; + break; - /* Only TRUE if a message is actually being processed; FALSE for address - testing and verification. */ + /* Only TRUE if a message is actually being processed; FALSE for address + testing and verification. */ case cond_errormsg: - yield = message_id[0] != 0 && - (sender_address == NULL || sender_address[0] == 0); - break; + yield = message_id[0] != 0 && + (sender_address == NULL || sender_address[0] == 0); + break; - /* Only FALSE if a message is actually being processed; TRUE for address - and filter testing and verification. */ + /* Only FALSE if a message is actually being processed; TRUE for address + and filter testing and verification. */ case cond_firsttime: - yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime; - break; + yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime; + break; - /* Only TRUE if a message is actually being processed; FALSE for address - testing and verification. */ + /* Only TRUE if a message is actually being processed; FALSE for address + testing and verification. */ case cond_manualthaw: - yield = message_id[0] != 0 && f.deliver_manual_thaw; - break; + yield = message_id[0] != 0 && f.deliver_manual_thaw; + break; - /* The foranyaddress condition loops through a list of addresses */ + /* The foranyaddress condition loops through a list of addresses */ case cond_foranyaddress: - p = c->left.u; - if (!(pp = expand_cstring(p))) - { - *error_pointer = string_sprintf("failed to expand \"%s\" in " - "filter file: %s", p, expand_string_message); - return FALSE; - } + p = c->left.u; + if (!(pp = expand_cstring(p))) + { + *error_pointer = string_sprintf("failed to expand \"%s\" in " + "filter file: %s", p, expand_string_message); + return FALSE; + } - yield = FALSE; - f.parse_allow_group = TRUE; /* Allow group syntax */ + yield = FALSE; + f.parse_allow_group = TRUE; /* Allow group syntax */ - while (*pp) - { - uschar *error; - int start, end, domain; - uschar * s; + while (*pp) + { + uschar *error; + int start, end, domain; + uschar * s; - p = parse_find_address_end(pp, FALSE); - s = string_copyn(pp, p - pp); + p = parse_find_address_end(pp, FALSE); + s = string_copyn(pp, p - pp); - filter_thisaddress = - parse_extract_address(s, &error, &start, &end, &domain, FALSE); + filter_thisaddress = + parse_extract_address(s, &error, &start, &end, &domain, FALSE); - if (filter_thisaddress) - { - if ((filter_test != FTEST_NONE && debug_selector != 0) || - (debug_selector & D_filter) != 0) - { - indent(); - debug_printf_indent("Extracted address %s\n", filter_thisaddress); - } - yield = test_condition(c->right.c, FALSE); - } + if (filter_thisaddress) + { + if ((filter_test != FTEST_NONE && debug_selector != 0) || + (debug_selector & D_filter) != 0) + { + indent(); + debug_printf_indent("Extracted address %s\n", filter_thisaddress); + } + yield = test_condition(c->right.c, FALSE); + } - if (yield) break; - if (!*p) break; - pp = p + 1; - } + if (yield) break; + if (!*p) break; + pp = p + 1; + } - f.parse_allow_group = FALSE; /* Reset group syntax flags */ - f.parse_found_group = FALSE; - break; + f.parse_allow_group = FALSE; /* Reset group syntax flags */ + f.parse_found_group = FALSE; + break; - /* All other conditions have left and right values that need expanding; - on error, it doesn't matter what value is returned. */ + /* All other conditions have left and right values that need expanding; + on error, it doesn't matter what value is returned. */ - default: - p = c->left.u; - for (i = 0; i < 2; i++) - { - if (!(exp[i] = expand_cstring(p))) + default: + p = c->left.u; + for (int i = 0; i < 2; i++) { - *error_pointer = string_sprintf("failed to expand \"%s\" in " - "filter file: %s", p, expand_string_message); - return FALSE; + if (!(exp[i] = expand_string_2(p, &textonly_re))) + { + *error_pointer = string_sprintf("failed to expand \"%s\" in " + "filter file: %s", p, expand_string_message); + return FALSE; + } + p = c->right.u; } - p = c->right.u; - } - - /* Inner switch for the different cases */ - - switch(c->type) - { - case cond_is: - yield = strcmpic(exp[0], exp[1]) == 0; - break; - case cond_IS: - yield = Ustrcmp(exp[0], exp[1]) == 0; - break; + /* Inner switch for the different cases */ - case cond_contains: - yield = strstric(exp[0], exp[1], FALSE) != NULL; - break; + switch(c->type) + { + case cond_is: + yield = strcmpic(exp[0], exp[1]) == 0; + break; - case cond_CONTAINS: - yield = Ustrstr(exp[0], exp[1]) != NULL; - break; + case cond_IS: + yield = Ustrcmp(exp[0], exp[1]) == 0; + break; - case cond_begins: - yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0; - break; + case cond_contains: + yield = strstric_c(exp[0], exp[1], FALSE) != NULL; + break; - case cond_BEGINS: - yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0; - break; + case cond_CONTAINS: + yield = Ustrstr(exp[0], exp[1]) != NULL; + break; - case cond_ends: - case cond_ENDS: - { - int len = Ustrlen(exp[1]); - const uschar *s = exp[0] + Ustrlen(exp[0]) - len; - yield = s < exp[0] - ? FALSE - : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0; - } - break; + case cond_begins: + yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0; + break; - case cond_matches: - case cond_MATCHES: - { - const pcre2_code *re; - int err; - PCRE2_SIZE offset; + case cond_BEGINS: + yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0; + break; - if ((filter_test != FTEST_NONE && debug_selector != 0) || - (debug_selector & D_filter) != 0) + case cond_ends: + case cond_ENDS: { - debug_printf_indent("Match expanded arguments:\n"); - debug_printf_indent(" Subject = %s\n", exp[0]); - debug_printf_indent(" Pattern = %s\n", exp[1]); + int len = Ustrlen(exp[1]); + const uschar *s = exp[0] + Ustrlen(exp[0]) - len; + yield = s < exp[0] + ? FALSE + : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0; + break; } - if (!(re = pcre2_compile((PCRE2_SPTR)exp[1], PCRE2_ZERO_TERMINATED, - PCRE_COPT | (c->type == cond_matches ? PCRE2_CASELESS : 0), - &err, &offset, pcre_cmp_ctx))) + case cond_matches: + case cond_MATCHES: { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - *error_pointer = string_sprintf("error while compiling " - "regular expression \"%s\": %s at offset %ld", - exp[1], errbuf, (long)offset); - return FALSE; - } + const pcre2_code * re; + mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS; - yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1); - break; - } + if ((filter_test != FTEST_NONE && debug_selector != 0) || + (debug_selector & D_filter) != 0) + { + debug_printf_indent("Match expanded arguments:\n"); + debug_printf_indent(" Subject = %s\n", exp[0]); + debug_printf_indent(" Pattern = %s\n", exp[1]); + } - /* For above and below, convert the strings to numbers */ + if (c->type == cond_matches) flags |= MCS_CASELESS; + if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx))) + return FALSE; - case cond_above: - case cond_below: - for (i = 0; i < 2; i++) - { - val[i] = get_number(exp[i], &yield); - if (!yield) - { - *error_pointer = string_sprintf("malformed numerical string \"%s\"", - exp[i]); - return FALSE; - } + yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1); + break; + } + + /* For above and below, convert the strings to numbers */ + + case cond_above: + case cond_below: + for (int i = 0; i < 2; i++) + { + val[i] = get_number(exp[i], &yield); + if (!yield) + { + *error_pointer = string_sprintf("malformed numerical string \"%s\"", + exp[i]); + return FALSE; + } + } + yield = c->type == cond_above ? (val[0] > val[1]) : (val[0] < val[1]); + break; } - yield = (c->type == cond_above)? (val[0] > val[1]) : (val[0] < val[1]); break; - } - break; } if ((filter_test != FTEST_NONE && debug_selector != 0) || @@ -1892,7 +1886,7 @@ while (commands) if (expand_nmax >= 0 || filter_thisaddress != NULL) { int ecount = expand_nmax >= 0 ? expand_nmax : -1; - uschar **ss = store_get(sizeof(uschar *) * (ecount + 3), FALSE); + uschar ** ss = store_get(sizeof(uschar *) * (ecount + 3), GET_UNTAINTED); addr->pipe_expandn = ss; if (!filter_thisaddress) filter_thisaddress = US""; @@ -2304,7 +2298,7 @@ while (commands) addr->next = *generated; *generated = addr; - addr->reply = store_get(sizeof(reply_item), FALSE); + addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED); addr->reply->from = NULL; addr->reply->to = string_copy(to); addr->reply->file_expand = @@ -2353,7 +2347,7 @@ while (commands) commands = commands->next; } -return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED; +return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED; }