X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/d7978c0f8af20ff4c3f770589b1bb81568aecff3..a85c067ba6c6940512cf57ec213277a370d87e70:/src/src/rewrite.c diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 74342e1d0..040525ec5 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -2,8 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with rewriting headers */ @@ -59,12 +61,12 @@ Arguments: Returns: fully-qualified address */ -uschar * -rewrite_address_qualify(uschar *s, BOOL is_recipient) +const uschar * +rewrite_address_qualify(const uschar *s, BOOL is_recipient) { -return (parse_find_at(s) != NULL)? s : - string_sprintf("%s@%s", s, - is_recipient? qualify_domain_recipient : qualify_domain_sender); +return parse_find_at(s) + ? s : string_sprintf("%s@%s", s, + is_recipient ? qualify_domain_recipient : qualify_domain_sender); } @@ -96,32 +98,31 @@ Returns: new address if rewritten; the input address if no change; rewritten address is returned, not just the active bit. */ -uschar * -rewrite_one(uschar *s, int flag, BOOL *whole, BOOL add_header, uschar *name, +const uschar * +rewrite_one(const uschar *s, int flag, BOOL *whole, BOOL add_header, uschar *name, rewrite_rule *rewrite_rules) { -uschar *yield = s; -uschar *subject = s; +const uschar *yield = s; +const uschar *subject = s; uschar *domain = NULL; BOOL done = FALSE; int rule_number = 1; int yield_start = 0, yield_end = 0; -if (whole != NULL) *whole = FALSE; +if (whole) *whole = FALSE; -/* Scan the rewriting rules */ +/* Scan the rewriting rules, ignoring any without matching flag */ -for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next) +for (rewrite_rule * rule = rewrite_rules; + rule && !done; + rule_number++, rule = rule->next) if (rule->flags & flag) { int start, end, pdomain; int count = 0; uschar *save_localpart; const uschar *save_domain; - uschar *error, *new, *newparsed; - - /* Ensure that the flag matches the flags in the rule. */ - - if ((rule->flags & flag) == 0) continue; + uschar *error, *new; + const uschar * newparsed; /* Come back here for a repeat after a successful rewrite. We do this only so many times. */ @@ -134,17 +135,19 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next with the other kinds of rewrite, where expansion happens inside match_address_list(). */ - if ((flag & rewrite_smtp) != 0) + if (flag & rewrite_smtp) { - uschar *key = expand_string(rule->key); - if (key == NULL) + BOOL textonly_re; + const uschar * key = expand_string_2(rule->key, &textonly_re); + if (!key) { if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand \"%s\" while " "checking for SMTP rewriting: %s", rule->key, expand_string_message); continue; } - if (match_check_string(subject, key, 0, TRUE, FALSE, FALSE, NULL) != OK) + if (match_check_string(subject, key, 0, + textonly_re ? MCS_CACHEABLE | MCS_PARTIAL : MCS_PARTIAL, NULL) != OK) continue; new = expand_string(rule->replacement); } @@ -155,7 +158,7 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next else { - if (domain == NULL) domain = Ustrrchr(subject, '@') + 1; + if (!domain) domain = Ustrrchr(subject, '@') + 1; /* Use the general function for matching an address against a list (here just one item, so use the "impossible value" separator UCHAR_MAX+1). */ @@ -183,7 +186,7 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next set up as an expansion variable */ domain[-1] = 0; - deliver_localpart = subject; + deliver_localpart = US subject; deliver_domain = domain; new = expand_string(rule->replacement); @@ -198,10 +201,10 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next give up altogether. For other expansion failures we have a configuration error. */ - if (new == NULL) + if (!new) { if (f.expand_string_forcedfail) - { if ((rule->flags & rewrite_quit) != 0) break; else continue; } + { if (rule->flags & rewrite_quit) break; else continue; } expand_string_message = expand_hide_passwords(expand_string_message); @@ -216,7 +219,7 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next newparsed = parse_extract_address(new, &error, &start, &end, &pdomain, flag == rewrite_envfrom || flag == (rewrite_smtp|rewrite_smtp_sender)); - if (newparsed == NULL) + if (!newparsed) { log_write(0, LOG_MAIN|LOG_PANIC, "Rewrite of %s yielded unparseable " "address: %s in address %s", subject, error, new); @@ -230,7 +233,7 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next if (pdomain == 0 && (*newparsed != 0 || (flag != rewrite_envfrom && flag != (rewrite_smtp|rewrite_smtp_sender)))) { - if ((rule->flags & rewrite_qualify) != 0) + if (rule->flags & rewrite_qualify) { newparsed = rewrite_address_qualify(newparsed, TRUE); new = string_sprintf("%.*s%s%.*s", start, new, newparsed, @@ -278,28 +281,23 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next flag set and so we must preserve the non-active portion of the current subject unless the current rule also has the w flag set. */ - if (whole != NULL && (flag & rewrite_all_headers) != 0) + if (whole && (flag & rewrite_all_headers)) { /* Current rule has the w flag set. We must ensure the phrase parts are syntactically valid if they are present. */ - if ((rule->flags & rewrite_whole) != 0) + if (rule->flags & rewrite_whole) { if (start > 0 && new[start-1] == '<') { uschar *p1 = new + start - 1; uschar *p2 = new + end + 1; const uschar *pf1, *pf2; - uschar buff1[256], buff2[256]; while (p1 > new && p1[-1] == ' ') p1--; - pf1 = parse_fix_phrase(new, p1 - new, buff1, sizeof(buff1)); + pf1 = parse_fix_phrase(new, p1 - new); while (*p2 == ' ') p2++; - pf2 = parse_fix_phrase(p2, Ustrlen(p2), buff2, sizeof(buff2)); - - /* Note that pf1 and pf2 are NOT necessarily buff1 and buff2. For - a non-RFC 2047 phrase that does not need to be RFC 2822 quoted, they - will be buff1+1 and buff2+1. */ + pf2 = parse_fix_phrase(p2, Ustrlen(p2)); start = Ustrlen(pf1) + start + new - p1; end = start + Ustrlen(newparsed); @@ -349,12 +347,12 @@ for (rewrite_rule * rule = rewrite_rules; rule; rule_number++, rule = rule->next /* If no further rewrites are to be done, set the done flag. This allows repeats of the current rule if configured before breaking the loop. */ - if ((rule->flags & rewrite_quit) != 0) done = TRUE; + if (rule->flags & rewrite_quit) done = TRUE; /* Allow the current rule to be applied up to 10 times if requested. */ - if ((rule->flags & rewrite_repeat) != 0) + if (rule->flags & rewrite_repeat) { if (count++ < 10) goto REPEAT_RULE; log_write(0, LOG_MAIN|LOG_PANIC, "rewrite rule repeat ignored after 10 " @@ -393,15 +391,16 @@ Arguments: Returns: possibly rewritten address */ -uschar * -rewrite_address(uschar *s, BOOL is_recipient, BOOL add_header, +const uschar * +rewrite_address(const uschar *s, BOOL is_recipient, BOOL add_header, rewrite_rule *rewrite_rules, int existflags) { -int flag = is_recipient? rewrite_envto : rewrite_envfrom; +int flag = is_recipient ? rewrite_envto : rewrite_envfrom; + s = rewrite_address_qualify(s, is_recipient); -if ((existflags & flag) != 0) +if (existflags & flag) { - uschar *new = rewrite_one(s, flag, NULL, add_header, is_recipient? + const uschar *new = rewrite_one(s, flag, NULL, add_header, is_recipient? US"original-recipient" : US"sender", rewrite_rules); if (new != s) s = new; } @@ -452,12 +451,13 @@ rewrite_one_header(header_line *h, int flag, { int lastnewline = 0; header_line *newh = NULL; -void *function_reset_point = store_get(0); +rmark function_reset_point = store_mark(); uschar *s = Ustrchr(h->text, ':') + 1; + while (isspace(*s)) s++; DEBUG(D_rewrite) - debug_printf("rewrite_one_header: type=%c:\n %s", h->type, h->text); + debug_printf_indent("rewrite_one_header: type=%c:\n %s", h->type, h->text); f.parse_allow_group = TRUE; /* Allow group syntax */ @@ -472,8 +472,9 @@ while (*s) { uschar *sprev; uschar *ss = parse_find_address_end(s, FALSE); - uschar *recipient, *new, *errmess; - void *loop_reset_point = store_get(0); + uschar *recipient, *new; + rmark loop_reset_point = store_mark(); + uschar *errmess = NULL; BOOL changed = FALSE; int terminator = *ss; int start, end, domain; @@ -483,18 +484,29 @@ while (*s) the next address, saving the start of the old one. */ *ss = 0; - recipient = parse_extract_address(s,&errmess,&start,&end,&domain,FALSE); + recipient = parse_extract_address(s, &errmess, &start, &end, &domain, FALSE); *ss = terminator; sprev = s; - s = ss + (terminator? 1:0); + s = ss + (terminator ? 1 : 0); while (isspace(*s)) s++; /* There isn't much we can do for syntactic disasters at this stage. - Pro tem (possibly for ever) ignore them. */ + Pro tem (possibly for ever) ignore them. + If we got nothing, then there was any sort of error: non-parsable address, + empty address, overlong addres. Sometimes the result matters, sometimes not. + It seems this function is called for *any* header we see. */ if (!recipient) { - store_reset(loop_reset_point); + /* Log unparesable addresses in the header. Slightly ugly because a + null output from the extract can also result from a header without an + address, "To: undisclosed recpients:;" being the classic case. Ignore + this one and carry on. */ + + if ((rewrite_rules || routed_old) && Ustrcmp(errmess, "empty address") != 0) + log_write(0, LOG_MAIN, "rewrite: %s", errmess); + + loop_reset_point = store_reset(loop_reset_point); continue; } @@ -508,7 +520,7 @@ while (*s) as abc@xyz, which the DNS lookup turns into abc@xyz.foo.com). However, if no change is made here, don't bother carrying on. */ - if (routed_old != NULL) + if (routed_old) { if (domain <= 0 || strcmpic(recipient+domain, routed_old) != 0) continue; recipient[domain-1] = 0; @@ -532,7 +544,8 @@ while (*s) { BOOL is_recipient = (flag & (rewrite_sender | rewrite_from | rewrite_replyto)) == 0; - new = rewrite_address_qualify(recipient, is_recipient); + /* deconst ok as recipient was notconst */ + new = US rewrite_address_qualify(recipient, is_recipient); changed = (new != recipient); recipient = new; @@ -541,7 +554,7 @@ while (*s) if (changed && ((is_recipient && !f.allow_unqualified_recipient) || (!is_recipient && !f.allow_unqualified_sender))) { - store_reset(loop_reset_point); + loop_reset_point = store_reset(loop_reset_point); continue; } } @@ -552,10 +565,11 @@ while (*s) "whole" flag set, adjust the pointers so that the whole address gets replaced, except possibly a final \n. */ - if ((existflags & flag) != 0) + if (existflags & flag) { BOOL whole; - new = rewrite_one(recipient, flag, &whole, FALSE, NULL, rewrite_rules); + /* deconst ok as recipient was notconst */ + new = US rewrite_one(recipient, flag, &whole, FALSE, NULL, rewrite_rules); if (new != recipient) { changed = TRUE; @@ -573,7 +587,7 @@ while (*s) point, because we may have a rewritten line from a previous time round the loop. */ - if (!changed) store_reset(loop_reset_point); + if (!changed) loop_reset_point = store_reset(loop_reset_point); /* If the address has changed, create a new header containing the rewritten address. We do not need to set the chain pointers at this @@ -590,9 +604,9 @@ while (*s) int newlen = Ustrlen(new); int oldlen = end - start; - header_line *prev = (newh == NULL)? h : newh; - uschar *newt = store_malloc(prev->slen - oldlen + newlen + 4); - uschar *newtstart = newt; + header_line * prev = newh ? newh : h; + uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, GET_TAINTED); + uschar * newtstart = newt; int type = prev->type; int slen = prev->slen - oldlen + newlen; @@ -631,7 +645,7 @@ while (*s) if (*p != '\n') { lastnewline = newt - newtstart; - Ustrcat(newt, "\n\t"); + Ustrcat(newt, US"\n\t"); slen += 2; } } @@ -654,16 +668,16 @@ while (*s) rewritten copy from a previous time round this loop. */ store_reset(function_reset_point); - newh = store_get(sizeof(header_line)); + function_reset_point = store_mark(); + newh = store_get(sizeof(header_line), GET_UNTAINTED); newh->type = type; newh->slen = slen; newh->text = string_copyn(newtstart, slen); - store_free(newtstart); /* Set up for scanning the rest of the header */ s = newh->text + remlen; - DEBUG(D_rewrite) debug_printf("remainder: %s", (*s == 0)? US"\n" : s); + DEBUG(D_rewrite) debug_printf("remainder: %s", *s ? s : US"\n"); } } @@ -673,10 +687,10 @@ f.parse_found_group = FALSE; /* If a rewrite happened and "replace" is true, put the new header into the chain following the old one, and mark the old one as replaced. */ -if (newh != NULL && replace) +if (newh && replace) { newh->next = h->next; - if (newh->next == NULL) header_last = newh; + if (!newh->next) header_last = newh; h->type = htype_old; h->next = newh; } @@ -719,34 +733,19 @@ rewrite_header(header_line *h, const uschar *routed_old, const uschar *routed_new, rewrite_rule *rewrite_rules, int existflags, BOOL replace) { +int flag; switch (h->type) { - case htype_sender: - return rewrite_one_header(h, rewrite_sender, routed_old, routed_new, - rewrite_rules, existflags, replace); - - case htype_from: - return rewrite_one_header(h, rewrite_from, routed_old, routed_new, - rewrite_rules, existflags, replace); - - case htype_to: - return rewrite_one_header(h, rewrite_to, routed_old, routed_new, - rewrite_rules, existflags, replace); - - case htype_cc: - return rewrite_one_header(h, rewrite_cc, routed_old, routed_new, - rewrite_rules, existflags, replace); - - case htype_bcc: - return rewrite_one_header(h, rewrite_bcc, routed_old, routed_new, - rewrite_rules, existflags, replace); - - case htype_reply_to: - return rewrite_one_header(h, rewrite_replyto, routed_old, routed_new, - rewrite_rules, existflags, replace); + case htype_sender: flag = rewrite_sender; break; + case htype_from: flag = rewrite_from; break; + case htype_to: flag = rewrite_to; break; + case htype_cc: flag = rewrite_cc; break; + case htype_bcc: flag = rewrite_bcc; break; + case htype_reply_to: flag = rewrite_replyto; break; + default: return NULL; } - -return NULL; +return rewrite_one_header(h, flag, routed_old, routed_new, + rewrite_rules, existflags, replace); } @@ -762,7 +761,8 @@ Argument: the address to test Returns: nothing */ -void rewrite_test(uschar *s) +void +rewrite_test(const uschar *s) { uschar *recipient, *error; int start, end, domain; @@ -779,8 +779,8 @@ pretending it is a sender. */ if ((rewrite_existflags & rewrite_smtp) != 0) { - uschar *new = rewrite_one(s, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE, - US"", global_rewrite_rules); + const uschar * new = rewrite_one(s, rewrite_smtp|rewrite_smtp_sender, NULL, + FALSE, US"", global_rewrite_rules); if (new != s) { if (*new == 0) @@ -813,7 +813,7 @@ for (int i = 0; i < 8; i++) { BOOL whole = FALSE; int flag = 1 << i; - uschar *new = rewrite_one(recipient, flag, &whole, FALSE, US"", + const uschar * new = rewrite_one(recipient, flag, &whole, FALSE, US"", global_rewrite_rules); printf("%s: ", rrname[i]); if (*new == 0)