From c7d2171b0572a29901dd6eeaf39660bb84b88c4e397282566075b675ffc9cdd5 Mon Sep 17 00:00:00 2001 From: Peter Wullinger Date: Tue, 12 May 2020 08:36:31 +0000 Subject: [PATCH] Accepting request 802873 from home:pwcau:branches:server:mail - bring changes from current in +fixes branch (patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94) * fixes CVE-2020-12783 (bsc#1171490) * Regard command-line recipients as tainted. * Bug 2489: Fix crash in the "pam" expansion condition. * Use tainted buffers for the transport smtp context. * Bug 2493: Harden ARC verify against Outlook, which has been seen to mix the ordering of its ARC headers. This caused a crash. * Bug 2492: Use tainted memory for retry record when needed. Previously when a new record was being constructed with information from the peer, a trap was taken. * Bug 2494: Unset the default for dmarc_tld_file. * Fix an uninitialised flag in early-pipelining. Previously connections could, depending on the platform, hang at the STARTTLS response. * Bug 2498: Reset a counter used for ARC verify before handling another message on a connection. Previously if one message had ARC headers and the following one did not, a crash could result when adding an Authentication-Results: header. * Bug 2500: Rewind some of the common-coding in string handling between the Exim main code and Exim-related utities. * Fix the variables set by the gsasl authenticator. * Bug 2507: Modules: on handling a dynamic-module (lookups) open failure, only retrieve the errormessage once. * Bug 2501: Fix init call in the heimdal authenticator. Previously it adjusted the size of a major service buffer; this failed because the buffer was in use at the time. Change to a compile-time increase in the buffer size, when this authenticator is compiled into exim. OBS-URL: https://build.opensuse.org/request/show/802873 OBS-URL: https://build.opensuse.org/package/show/server:mail/exim?expand=0&rev=234 --- exim.changes | 30 + exim.spec | 4 +- ...s-ee83de04d3087efaf808d1f2235a988275c2ee94 | 1184 +++++++++++++++++ 3 files changed, 1217 insertions(+), 1 deletion(-) create mode 100644 patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94 diff --git a/exim.changes b/exim.changes index 4f21ad4..6cdf701 100644 --- a/exim.changes +++ b/exim.changes @@ -1,3 +1,33 @@ +Tue May 12 08:19:17 UTC 2020 - wullinger@rz.uni-kiel.de +- bring changes from current in +fixes branch + (patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94) + * fixes CVE-2020-12783 (bsc#1171490) + * Regard command-line recipients as tainted. + * Bug 2489: Fix crash in the "pam" expansion condition. + * Use tainted buffers for the transport smtp context. + * Bug 2493: Harden ARC verify against Outlook, which has been seen to mix + the ordering of its ARC headers. This caused a crash. + * Bug 2492: Use tainted memory for retry record when needed. Previously when + a new record was being constructed with information from the peer, a trap + was taken. + * Bug 2494: Unset the default for dmarc_tld_file. + * Fix an uninitialised flag in early-pipelining. Previously connections + could, depending on the platform, hang at the STARTTLS response. + * Bug 2498: Reset a counter used for ARC verify before handling another + message on a connection. Previously if one message had ARC headers and + the following one did not, a crash could result when adding an + Authentication-Results: header. + * Bug 2500: Rewind some of the common-coding in string handling between the + Exim main code and Exim-related utities. + * Fix the variables set by the gsasl authenticator. + * Bug 2507: Modules: on handling a dynamic-module (lookups) open failure, + only retrieve the errormessage once. + * Bug 2501: Fix init call in the heimdal authenticator. Previously it + adjusted the size of a major service buffer; this failed because the + buffer was in use at the time. Change to a compile-time increase in the + buffer size, when this authenticator is compiled into exim. + +------------------------------------------------------------------- Wed Apr 1 12:52:10 UTC 2020 - wullinger@rz.uni-kiel.de - don't create logfiles during install * fixes CVE-2020-8015 (bsc#1154183) diff --git a/exim.spec b/exim.spec index 765119e..b225aa7 100644 --- a/exim.spec +++ b/exim.spec @@ -73,7 +73,7 @@ Requires(pre): group(mail) Requires(pre): fileutils textutils %endif Version: 4.93.0.4 -Release: 2 +Release: 3 %if %{with_mysql} BuildRequires: mysql-devel %endif @@ -102,6 +102,7 @@ Source32: eximstats.conf-2.2 Source40: exim.service Patch0: exim-tail.patch Patch1: gnu_printf.patch +Patch2: patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94 %package -n eximon Summary: Eximon, an graphical frontend to administer Exim's mail queue @@ -145,6 +146,7 @@ once, if at all. The rest is done by logrotate / cron.) %setup -q -n exim-%{version} %patch0 %patch1 -p1 +%patch2 -p1 # build with fPIE/pie on SUSE 10.0 or newer, or on any other platform %if %{?suse_version:%suse_version}%{?!suse_version:99999} > 930 fPIE="-fPIE" diff --git a/patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94 b/patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94 new file mode 100644 index 0000000..efc01d9 --- /dev/null +++ b/patch-exim-fixes-ee83de04d3087efaf808d1f2235a988275c2ee94 @@ -0,0 +1,1184 @@ +Only in a: Local +diff -ru a/OS/Makefile-Base b/OS/Makefile-Base +--- a/OS/Makefile-Base 2020-01-03 13:08:52.000000000 +0100 ++++ b/OS/Makefile-Base 2020-05-12 10:13:22.020198000 +0200 +@@ -659,10 +659,13 @@ + .c.o:; @echo "$(CC) $*.c" + $(FE)$(CC) -c $(CFLAGS) -I. $(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE) $*.c + +-# Update Exim's version information and build the version object. ++# Update Exim's version information and build the version object. The dependency ++# chain here avoids problems under parallel-make. + +-version.h version.sh:: ++version.sh: + @../scripts/reversion ++ ++version.h: version.sh + + cnumber.h: version.h + +Only in a: doc +diff -ru a/exim_monitor/em_version.c b/exim_monitor/em_version.c +--- a/exim_monitor/em_version.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/exim_monitor/em_version.c 2020-05-12 10:13:22.022512000 +0200 +@@ -5,6 +5,8 @@ + /* Copyright (c) University of Cambridge 1995 - 2018 */ + /* See the file NOTICE for conditions of use and distribution. */ + ++#define EM_VERSION_C ++ + #include "mytypes.h" + #include "store.h" + #include "macros.h" +diff -ru a/src/acl.c b/src/acl.c +--- a/src/acl.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/acl.c 2020-05-12 10:13:22.024864000 +0200 +@@ -2259,7 +2259,7 @@ + + /* Parse the other options. */ + +-while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))) ++while ((ss = string_nextinlist(&arg, &sep, NULL, 0))) + { + if (strcmpic(ss, US"leaky") == 0) leaky = TRUE; + else if (strcmpic(ss, US"strict") == 0) strict = TRUE; +diff -ru a/src/arc.c b/src/arc.c +--- a/src/arc.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/arc.c 2020-05-12 10:13:22.025287000 +0200 +@@ -735,7 +735,7 @@ + + /* Setup the interface to the signing library */ + +-if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx))) ++if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL))) + { + DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr); + as->ams_verify_done = arc_state_reason = US"internal sigverify init error"; +@@ -964,7 +964,7 @@ + /* We know the b-tag blob is of a nul-term string, so safe as a string */ + pdkim_decode_base64(hdr_as->b.data, &sighash); + +-if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx))) ++if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL))) + { + DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr); + return US"fail"; +diff -ru a/src/auths/README b/src/auths/README +--- a/src/auths/README 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/auths/README 2020-04-01 15:04:02.374233000 +0200 +@@ -34,7 +34,7 @@ + the server and/or client functions are available for this authenticator. + Typically this depends on whether server or client configuration options have + been set, but it is also possible to have an authenticator that has only one of +-the server or client functions. ++the server or client functions. The function may not touch big_buffer. + + SERVER AUTHENTICATION + +diff -ru a/src/auths/call_radius.c b/src/auths/call_radius.c +--- a/src/auths/call_radius.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/auths/call_radius.c 2020-05-12 10:13:22.026117000 +0200 +@@ -2,6 +2,7 @@ + * Exim - an Internet mail transport agent * + *************************************************/ + ++/* Copyright (c) The Exim Maintainers 2020 */ + /* Copyright (c) University of Cambridge 1995 - 2016 */ + /* See the file NOTICE for conditions of use and distribution. */ + +@@ -115,34 +116,34 @@ + *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE); + + else if (rc_read_dictionary(rc_conf_str("dictionary")) != 0) +- *errptr = string_sprintf("RADIUS: can't read dictionary"); ++ *errptr = US"RADIUS: can't read dictionary"; + +-else if (rc_avpair_add(&send, PW_USER_NAME, user, 0) == NULL) +- *errptr = string_sprintf("RADIUS: add user name failed\n"); ++else if (!rc_avpair_add(&send, PW_USER_NAME, user, 0)) ++ *errptr = US"RADIUS: add user name failed"; + +-else if (rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0) == NULL) +- *errptr = string_sprintf("RADIUS: add password failed\n"); ++else if (!rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0)) ++ *errptr = US"RADIUS: add password failed"); + +-else if (rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0) == NULL) +- *errptr = string_sprintf("RADIUS: add service type failed\n"); ++else if (!rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0)) ++ *errptr = US"RADIUS: add service type failed"; + + #else /* RADIUS_LIB_RADIUSCLIENT unset => RADIUS_LIB_RADIUSCLIENT2 */ + +-if ((h = rc_read_config(RADIUS_CONFIG_FILE)) == NULL) ++if (!(h = rc_read_config(RADIUS_CONFIG_FILE))) + *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE); + + else if (rc_read_dictionary(h, rc_conf_str(h, "dictionary")) != 0) +- *errptr = string_sprintf("RADIUS: can't read dictionary"); ++ *errptr = US"RADIUS: can't read dictionary"; + +-else if (rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0) == NULL) +- *errptr = string_sprintf("RADIUS: add user name failed\n"); ++else if (!rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0)) ++ *errptr = US"RADIUS: add user name failed"; + +-else if (rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args, +- Ustrlen(radius_args), 0) == NULL) +- *errptr = string_sprintf("RADIUS: add password failed\n"); ++else if (!rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args, ++ Ustrlen(radius_args), 0)) ++ *errptr = US"RADIUS: add password failed"; + +-else if (rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0) == NULL) +- *errptr = string_sprintf("RADIUS: add service type failed\n"); ++else if (!rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0)) ++ *errptr = US"RADIUS: add service type failed"; + + #endif /* RADIUS_LIB_RADIUSCLIENT */ + +diff -ru a/src/auths/cyrus_sasl.c b/src/auths/cyrus_sasl.c +--- a/src/auths/cyrus_sasl.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/auths/cyrus_sasl.c 2020-05-12 10:13:22.026558000 +0200 +@@ -347,10 +347,10 @@ + } + else + { +- /* make sure that we have a null-terminated string */ +- out2 = string_copyn(output, outlen); ++ /* auth_get_data() takes a length-specfied block of binary ++ which can include zeroes; no terminating NUL is needed */ + +- if ((rc = auth_get_data(&input, out2, outlen)) != OK) ++ if ((rc = auth_get_data(&input, output, outlen)) != OK) + { + /* we couldn't get the data, so free up the library before + * returning whatever error we get */ +diff -ru a/src/auths/heimdal_gssapi.c b/src/auths/heimdal_gssapi.c +--- a/src/auths/heimdal_gssapi.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/auths/heimdal_gssapi.c 2020-05-12 10:13:22.028703000 +0200 +@@ -200,16 +200,6 @@ + + krb5_free_context(context); + +-/* RFC 4121 section 5.2, SHOULD support 64K input buffers */ +-if (big_buffer_size < (64 * 1024)) +- { +- uschar *newbuf; +- big_buffer_size = 64 * 1024; +- newbuf = store_malloc(big_buffer_size); +- store_free(big_buffer); +- big_buffer = newbuf; +- } +- + ablock->server = TRUE; + } + +diff -ru a/src/auths/spa.c b/src/auths/spa.c +--- a/src/auths/spa.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/auths/spa.c 2020-05-12 10:13:22.029070000 +0200 +@@ -139,7 +139,8 @@ + SPAAuthResponse response; + SPAAuthResponse *responseptr = &response; + uschar msgbuf[2048]; +-uschar *clearpass; ++uschar *clearpass, *s; ++unsigned off; + + /* send a 334, MS Exchange style, and grab the client's request, + unless we already have it via an initial response. */ +@@ -194,9 +195,19 @@ + + { + int i; +- char *p = ((char*)responseptr) + IVAL(&responseptr->uUser.offset,0); ++ char * p; + int len = SVAL(&responseptr->uUser.len,0)/2; + ++ if ( (off = IVAL(&responseptr->uUser.offset,0)) >= sizeof(SPAAuthResponse) ++ || len >= sizeof(responseptr->buffer)/2 ++ || (p = (CS responseptr) + off) + len*2 >= CS (responseptr+1) ++ ) ++ { ++ DEBUG(D_auth) ++ debug_printf("auth_spa_server(): bad uUser spec in response\n"); ++ return FAIL; ++ } ++ + if (len + 1 >= sizeof(msgbuf)) return FAIL; + for (i = 0; i < len; ++i) + { +@@ -245,13 +256,17 @@ + + /* compare NT hash (LM may not be available) */ + +-if (memcmp(ntRespData, +- ((unsigned char*)responseptr)+IVAL(&responseptr->ntResponse.offset,0), +- 24) == 0) +- /* success. we have a winner. */ ++off = IVAL(&responseptr->ntResponse.offset,0); ++if (off >= sizeof(SPAAuthResponse) - 24) + { +- return auth_check_serv_cond(ablock); ++ DEBUG(D_auth) ++ debug_printf("auth_spa_server(): bad ntRespData spec in response\n"); ++ return FAIL; + } ++s = (US responseptr) + off; ++ ++if (memcmp(ntRespData, s, 24) == 0) ++ return auth_check_serv_cond(ablock); /* success. we have a winner. */ + + /* Expand server_condition as an authorization check (PH) */ + +Only in a/src: cnumber.h +diff -ru a/src/dkim.c b/src/dkim.c +--- a/src/dkim.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/dkim.c 2020-05-12 10:13:22.056772000 +0200 +@@ -401,7 +401,7 @@ + dkim_cur_sig = sig; + dkim_signing_domain = US sig->domain; + dkim_signing_selector = US sig->selector; +- dkim_key_length = sig->sighash.len * 8; ++ dkim_key_length = sig->keybits; + + /* These two return static strings, so we can compare the addr + later to see if the ACL overwrote them. Check that when logging */ +diff -ru a/src/drtables.c b/src/drtables.c +--- a/src/drtables.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/drtables.c 2020-05-12 10:13:22.057565000 +0200 +@@ -740,10 +740,11 @@ + + dl = dlopen(CS big_buffer, RTLD_NOW);// TJ was LAZY + if (dl == NULL) { +- fprintf(stderr, "Error loading %s: %s\n", name, dlerror()); +- moduleerrors++; +- log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, dlerror()); +- continue; ++ errormessage = dlerror(); ++ fprintf(stderr, "Error loading %s: %s\n", name, errormessage); ++ log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, errormessage); ++ moduleerrors++; ++ continue; + } + + /* FreeBSD nsdispatch() can trigger dlerror() errors about +@@ -756,16 +757,16 @@ + info = (struct lookup_module_info*) dlsym(dl, "_lookup_module_info"); + if ((errormsg = dlerror()) != NULL) { + fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg); ++ log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg); + dlclose(dl); + moduleerrors++; +- log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg); + continue; + } + if (info->magic != LOOKUP_MODULE_INFO_MAGIC) { + fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name); ++ log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name); + dlclose(dl); + moduleerrors++; +- log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name); + continue; + } + +diff -ru a/src/exim.c b/src/exim.c +--- a/src/exim.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/exim.c 2020-05-12 10:13:22.059213000 +0200 +@@ -2554,8 +2554,10 @@ + #ifdef SUPPORT_I18N + allow_utf8_domains = TRUE; + #endif +- sender_address = parse_extract_address(argrest, &errmess, +- &dummy_start, &dummy_end, &sender_address_domain, TRUE); ++ if (!(sender_address = parse_extract_address(argrest, &errmess, ++ &dummy_start, &dummy_end, &sender_address_domain, TRUE))) ++ exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); ++ + sender_address = string_copy_taint(sender_address, TRUE); + #ifdef SUPPORT_I18N + message_smtputf8 = string_is_utf8(sender_address); +@@ -2563,8 +2565,6 @@ + #endif + allow_domain_literals = FALSE; + strip_trailing_dot = FALSE; +- if (!sender_address) +- exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); + } + f.sender_address_forced = TRUE; + } +@@ -5021,6 +5021,8 @@ + deliver_localpart_orig = NULL; + deliver_domain_orig = NULL; + callout_address = sending_ip_address = NULL; ++ deliver_localpart_data = deliver_domain_data = ++ recipient_data = sender_data = NULL; + sender_rate = sender_rate_limit = sender_rate_period = NULL; + } + smtp_log_no_mail(); +@@ -5681,6 +5683,8 @@ + #endif + callout_address = NULL; + sending_ip_address = NULL; ++ deliver_localpart_data = deliver_domain_data = ++ recipient_data = sender_data = NULL; + acl_var_m = NULL; + for(int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; + +diff -ru a/src/expand.c b/src/expand.c +--- a/src/expand.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/expand.c 2020-05-12 10:13:22.095677000 +0200 +@@ -5029,7 +5029,7 @@ + { + client_conn_ctx cctx; + int timeout = 5; +- int save_ptr = yield->ptr; ++ int save_ptr = yield ? yield->ptr : 0; + FILE * fp = NULL; + uschar * arg; + uschar * sub_arg[4]; +@@ -5070,8 +5070,9 @@ + uschar * item; + int sep = 0; + +- item = string_nextinlist(&list, &sep, NULL, 0); +- if ((timeout = readconf_readtime(item, 0, FALSE)) < 0) ++ if ( !(item = string_nextinlist(&list, &sep, NULL, 0)) ++ || !*item ++ || (timeout = readconf_readtime(item, 0, FALSE)) < 0) + { + expand_string_message = string_sprintf("bad time value %s", item); + goto EXPAND_FAILED; +@@ -5262,7 +5263,7 @@ + + if (sigalrm_seen) + { +- yield->ptr = save_ptr; ++ if (yield) yield->ptr = save_ptr; + expand_string_message = US "socket read timed out"; + goto SOCK_FAIL; + } +@@ -5429,7 +5430,7 @@ + + case EITEM_TR: + { +- int oldptr = yield->ptr; ++ int oldptr = yield ? yield->ptr : 0; + int o2m; + uschar *sub[3]; + +@@ -6170,7 +6171,7 @@ + case EITEM_REDUCE: + { + int sep = 0; +- int save_ptr = yield->ptr; ++ int save_ptr = yield ? yield->ptr : 0; + uschar outsep[2] = { '\0', '\0' }; + const uschar *list, *expr, *temp; + uschar *save_iterate_item = iterate_item; +@@ -6317,7 +6318,8 @@ + item of the output list, add in a space if the new item begins with the + separator character, or is an empty string. */ + +- if (yield->ptr != save_ptr && (temp[0] == *outsep || temp[0] == 0)) ++ if ( yield && yield->ptr != save_ptr ++ && (temp[0] == *outsep || temp[0] == 0)) + yield = string_catn(yield, US" ", 1); + + /* Add the string in "temp" to the output list that we are building, +@@ -6357,7 +6359,7 @@ + the redundant final separator. Even though an empty item at the end of a + list does not count, this is tidier. */ + +- else if (yield->ptr != save_ptr) yield->ptr--; ++ else if (yield && yield->ptr != save_ptr) yield->ptr--; + + /* Restore preserved $item */ + +@@ -7222,7 +7224,7 @@ + { + uschar outsep[2] = { ':', '\0' }; + uschar *address, *error; +- int save_ptr = yield->ptr; ++ int save_ptr = yield ? yield->ptr : 0; + int start, end, domain; /* Not really used */ + + while (isspace(*sub)) sub++; +@@ -7253,7 +7255,7 @@ + + if (address) + { +- if (yield->ptr != save_ptr && address[0] == *outsep) ++ if (yield && yield->ptr != save_ptr && address[0] == *outsep) + yield = string_catn(yield, US" ", 1); + + for (;;) +@@ -7282,7 +7284,7 @@ + /* If we have generated anything, remove the redundant final + separator. */ + +- if (yield->ptr != save_ptr) yield->ptr--; ++ if (yield && yield->ptr != save_ptr) yield->ptr--; + f.parse_allow_group = FALSE; + continue; + } +@@ -7421,10 +7423,10 @@ + + case EOP_FROM_UTF8: + { +- while (*sub != 0) ++ uschar * buff = store_get(4, is_tainted(sub)); ++ while (*sub) + { + int c; +- uschar buff[4]; + GETUTF8INC(c, sub); + if (c > 255) c = '_'; + buff[0] = c; +@@ -7445,7 +7447,17 @@ + int complete; + uschar seq_buff[4]; /* accumulate utf-8 here */ + +- while (*sub != 0) ++ /* Manually track tainting, as we deal in individual chars below */ ++ ++ if (is_tainted(sub)) ++ if (yield->s && yield->ptr) ++ gstring_rebuffer(yield); ++ else ++ yield->s = store_get(yield->size = Ustrlen(sub), TRUE); ++ ++ /* Check the UTF-8, byte-by-byte */ ++ ++ while (*sub) + { + complete = 0; + uschar c = *sub++; +diff -ru a/src/functions.h b/src/functions.h +--- a/src/functions.h 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/functions.h 2020-05-12 10:13:22.097024000 +0200 +@@ -187,6 +187,7 @@ + extern uschar *deliver_get_sender_address (uschar *id); + extern void delivery_re_exec(int); + ++extern void die_tainted(const uschar *, const uschar *, int); + extern BOOL directory_make(const uschar *, const uschar *, int, BOOL); + #ifndef DISABLE_DKIM + extern uschar *dkim_exim_query_dns_txt(const uschar *); +@@ -602,6 +603,58 @@ + extern ssize_t write_to_fd_buf(int, const uschar *, size_t); + + ++/******************************************************************************/ ++/* Predicate: if an address is in a tainted pool. ++By extension, a variable pointing to this address is tainted. ++*/ ++ ++static inline BOOL ++is_tainted(const void * p) ++{ ++#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) || defined(EM_VERSION_C) ++return FALSE; ++ ++#else ++extern BOOL is_tainted_fn(const void *); ++return is_tainted_fn(p); ++#endif ++} ++ ++/******************************************************************************/ ++/* String functions */ ++static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) ++{ ++#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) ++if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); ++#endif ++return US strcat(CS dst, CCS src); ++} ++static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) ++{ ++#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) ++if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); ++#endif ++return US strcpy(CS dst, CCS src); ++} ++static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) ++{ ++#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) ++if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); ++#endif ++return US strncat(CS dst, CCS src, n); ++} ++static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) ++{ ++#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) ++if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); ++#endif ++return US strncpy(CS dst, CCS src, n); ++} ++/*XXX will likely need unchecked copy also */ ++ ++ ++/******************************************************************************/ ++ + #if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) + /* exim_chown - in some NFSv4 setups *seemes* to be an issue with + chown(, ). +@@ -634,8 +687,8 @@ + return chown(CCS name, owner, group) + ? exim_chown_failure(-1, name, owner, group) : 0; + } +- + #endif /* !MACRO_PREDEF && !COMPILE_UTILITY */ ++ + /******************************************************************************/ + /* String functions */ + +@@ -848,6 +901,18 @@ + va_end(ap); + return g; + } ++ ++ ++/* Copy the content of a string to tainted memory */ ++ ++static inline void ++gstring_rebuffer(gstring * g) ++{ ++uschar * s = store_get(g->size, TRUE); ++memcpy(s, g->s, g->ptr); ++g->s = s; ++} ++ + + /******************************************************************************/ + +diff -ru a/src/globals.c b/src/globals.c +--- a/src/globals.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/globals.c 2020-05-12 10:13:22.097761000 +0200 +@@ -311,6 +311,7 @@ + .synchronous_delivery = FALSE, + .system_filtering = FALSE, + ++ .taint_check_slow = FALSE, + .tcp_fastopen_ok = FALSE, + .tcp_in_fastopen = FALSE, + .tcp_in_fastopen_data = FALSE, +diff -ru a/src/globals.h b/src/globals.h +--- a/src/globals.h 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/globals.h 2020-05-12 10:13:22.098294000 +0200 +@@ -272,6 +272,7 @@ + BOOL synchronous_delivery :1; /* TRUE if -odi is set */ + BOOL system_filtering :1; /* TRUE when running system filter */ + ++ BOOL taint_check_slow :1; /* malloc/mmap are not returning distinct ranges */ + BOOL tcp_fastopen_ok :1; /* appears to be supported by kernel */ + BOOL tcp_in_fastopen :1; /* conn usefully used fastopen */ + BOOL tcp_in_fastopen_data :1; /* fastopen carried data */ +@@ -339,7 +340,7 @@ + extern BOOL allow_domain_literals; /* As it says */ + extern BOOL allow_mx_to_ip; /* Allow MX records to -> ip address */ + #ifdef EXPERIMENTAL_ARC +-struct arc_set *arc_received; /* highest ARC instance evaluation struct */ ++extern struct arc_set *arc_received; /* highest ARC instance evaluation struct */ + extern int arc_received_instance; /* highest ARC instance number in headers */ + extern int arc_oldest_pass; /* lowest passing instance number in headers */ + extern const uschar *arc_state; /* verification state */ +diff -ru a/src/ip.c b/src/ip.c +--- a/src/ip.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/ip.c 2020-05-12 10:13:22.099852000 +0200 +@@ -269,28 +269,34 @@ + /*XXX also seen on successful TFO, sigh */ + tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA; + } +- else if (errno == EINPROGRESS) /* expected if we had no cookie for peer */ ++ else switch (errno) ++ { ++ case EINPROGRESS: /* expected if we had no cookie for peer */ + /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */ + /* apparently no visibility of the diffference at this point */ + /* seen for with-data, proper TFO opt, cookie-req */ + /* with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */ + /* ? older Experimental TFO option behaviour ? */ +- { /* queue unsent data */ +- DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", +- fastopen_blob->len > 0 ? "with" : "no"); +- if (!fastopen_blob->data) +- { +- tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ +- rc = 0; +- } +- else +- rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); +- } +- else if(errno == EOPNOTSUPP) +- { +- DEBUG(D_transport) +- debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); +- goto legacy_connect; ++ DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", ++ fastopen_blob->len > 0 ? "with" : "no"); ++ if (!fastopen_blob->data) ++ { ++ tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ ++ rc = 0; ++ } ++ else /* queue unsent data */ ++ rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); ++ break; ++ ++ case EOPNOTSUPP: ++ DEBUG(D_transport) ++ debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); ++ goto legacy_connect; ++ ++ case EPIPE: ++ DEBUG(D_transport) ++ debug_printf("Tried TCP Fast Open but kernel too old to support it\n"); ++ goto legacy_connect; + } + # endif + # ifdef EXIM_TFO_CONNECTX +diff -ru a/src/lookups/dsearch.c b/src/lookups/dsearch.c +--- a/src/lookups/dsearch.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/lookups/dsearch.c 2020-05-12 10:13:22.117328000 +0200 +@@ -28,7 +28,7 @@ + dsearch_open(uschar *dirname, uschar **errmsg) + { + DIR *dp = opendir(CS dirname); +-if (dp == NULL) ++if (!dp) + { + int save_errno = errno; + *errmsg = string_open_failed(errno, "%s for directory search", dirname); +@@ -47,8 +47,8 @@ + /* The handle will always be (void *)(-1), but don't try casting it to an + integer as this gives warnings on 64-bit systems. */ + +-BOOL +-static dsearch_check(void *handle, uschar *filename, int modemask, uid_t *owners, ++static BOOL ++dsearch_check(void *handle, uschar *filename, int modemask, uid_t *owners, + gid_t *owngroups, uschar **errmsg) + { + handle = handle; +@@ -87,7 +87,9 @@ + filename = string_sprintf("%s/%s", dirname, keystring); + if (Ulstat(filename, &statbuf) >= 0) + { +- *result = string_copy(keystring); ++ /* Since the filename exists in the filesystem, we can return a ++ non-tainted result. */ ++ *result = string_copy_taint(keystring, FALSE); + return OK; + } + +diff -ru a/src/macros.h b/src/macros.h +--- a/src/macros.h 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/macros.h 2020-05-12 10:13:22.122799000 +0200 +@@ -152,12 +152,19 @@ + into big_buffer_size and in some circumstances increased. It should be at least + as long as the maximum path length. */ + +-#if defined PATH_MAX && PATH_MAX > 16384 ++#ifdef AUTH_HEIMDAL_GSSAPI ++ /* RFC 4121 section 5.2, SHOULD support 64K input buffers */ ++# define __BIG_BUFFER_SIZE 65536 ++#else ++# define __BIG_BUFFER_SIZE 16384 ++#endif ++ ++#if defined PATH_MAX && PATH_MAX > __BIG_BUFFER_SIZE + # define BIG_BUFFER_SIZE PATH_MAX +-#elif defined MAXPATHLEN && MAXPATHLEN > 16384 ++#elif defined MAXPATHLEN && MAXPATHLEN > __BIG_BUFFER_SIZE + # define BIG_BUFFER_SIZE MAXPATHLEN + #else +-# define BIG_BUFFER_SIZE 16384 ++# define BIG_BUFFER_SIZE __BIG_BUFFER_SIZE + #endif + + /* header size of pipe content +diff -ru a/src/mytypes.h b/src/mytypes.h +--- a/src/mytypes.h 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/mytypes.h 2020-05-12 10:13:22.125222000 +0200 +@@ -100,19 +100,15 @@ + #define Uread(f,b,l) read(f,CS(b),l) + #define Urename(s,t) rename(CCS(s),CCS(t)) + #define Ustat(s,t) stat(CCS(s),t) +-#define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) + #define Ustrchr(s,n) US strchr(CCS(s),n) + #define CUstrchr(s,n) CUS strchr(CCS(s),n) + #define CUstrerror(n) CUS strerror(n) + #define Ustrcmp(s,t) strcmp(CCS(s),CCS(t)) +-#define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) + #define Ustrcpy_nt(s,t) strcpy(CS s, CCS t) /* no taint check */ + #define Ustrcspn(s,t) strcspn(CCS(s),CCS(t)) + #define Ustrftime(s,m,f,t) strftime(CS(s),m,f,t) + #define Ustrlen(s) (int)strlen(CCS(s)) +-#define Ustrncat(s,t,n) __Ustrncat(s, CUS(t),n, __FUNCTION__, __LINE__) + #define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n) +-#define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t),n, __FUNCTION__, __LINE__) + #define Ustrncpy_nt(s,t,n) strncpy(CS s, CCS t, n) /* no taint check */ + #define Ustrpbrk(s,t) strpbrk(CCS(s),CCS(t)) + #define Ustrrchr(s,n) US strrchr(CCS(s),n) +@@ -125,57 +121,17 @@ + #define Ustrtoul(s,t,b) strtoul(CCS(s),CSS(t),b) + #define Uunlink(s) unlink(CCS(s)) + +-extern void die_tainted(const uschar *, const uschar *, int); +- +-/* Predicate: if an address is in a tainted pool. +-By extension, a variable pointing to this address is tainted. +-*/ +- +-static inline BOOL +-is_tainted(const void * p) +-{ +-#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) +-return FALSE; +- +-#elif defined(TAINT_CHECK_SLOW) +-extern BOOL is_tainted_fn(const void *); +-return is_tainted_fn(p); +- ++#ifdef EM_VERSION_C ++# define Ustrcat(s,t) strcat(CS(s), CCS(t)) ++# define Ustrcpy(s,t) strcpy(CS(s), CCS(t)) ++# define Ustrncat(s,t,n) strncat(CS(s), CCS(t), n) ++# define Ustrncpy(s,t,n) strncpy(CS(s), CCS(t), n) + #else +-extern void * tainted_base, * tainted_top; +-return p >= tainted_base && p < tainted_top; ++# define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) ++# define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) ++# define Ustrncat(s,t,n) __Ustrncat(s, CUS(t), n, __FUNCTION__, __LINE__) ++# define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t), n, __FUNCTION__, __LINE__) + #endif +-} +- +-static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) +-{ +-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); +-#endif +-return US strcat(CS dst, CCS src); +-} +-static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) +-{ +-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); +-#endif +-return US strcpy(CS dst, CCS src); +-} +-static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) +-{ +-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); +-#endif +-return US strncat(CS dst, CCS src, n); +-} +-static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) +-{ +-#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +-if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); +-#endif +-return US strncpy(CS dst, CCS src, n); +-} +-/*XXX will likely need unchecked copy also */ + + #endif + /* End of mytypes.h */ +diff -ru a/src/pdkim/pdkim.c b/src/pdkim/pdkim.c +--- a/src/pdkim/pdkim.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/pdkim/pdkim.c 2020-05-12 10:13:22.126558000 +0200 +@@ -1430,7 +1430,7 @@ + + if ((*errstr = exim_dkim_verify_init(&p->key, + sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER, +- vctx))) ++ vctx, &sig->keybits))) + { + DEBUG(D_acl) debug_printf("verify_init: %s\n", *errstr); + sig->verify_status = PDKIM_VERIFY_INVALID; +diff -ru a/src/pdkim/pdkim.h b/src/pdkim/pdkim.h +--- a/src/pdkim/pdkim.h 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/pdkim/pdkim.h 2020-05-12 10:13:22.126756000 +0200 +@@ -140,8 +140,9 @@ + int version; + + /* (a=) The signature algorithm. */ +- int keytype; /* pdkim_keytypes index */ +- int hashtype; /* pdkim_hashes index */ ++ int keytype; /* pdkim_keytypes index */ ++ unsigned keybits; /* size of the key */ ++ int hashtype; /* pdkim_hashes index */ + + /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE + or PDKIM_CANON_RELAXED */ +diff -ru a/src/pdkim/signing.c b/src/pdkim/signing.c +--- a/src/pdkim/signing.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/pdkim/signing.c 2020-05-12 10:13:22.127061000 +0200 +@@ -155,7 +155,8 @@ + Return: NULL for success, or an error string */ + + const uschar * +-exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx) ++exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx, ++ unsigned * bits) + { + gnutls_datum_t k; + int rc; +@@ -182,6 +183,7 @@ + ret = US"pubkey format not handled"; + break; + } ++if (!ret && bits) gnutls_pubkey_get_pk_algorithm(verify_ctx->key, bits); + return ret; + } + +@@ -552,7 +554,8 @@ + Return: NULL for success, or an error string */ + + const uschar * +-exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx) ++exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx, ++ unsigned * bits) + { + /* + in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi() +@@ -560,6 +563,7 @@ + uschar tag_class; + int taglen; + long alen; ++unsigned nbits; + int rc; + uschar * errstr; + gcry_error_t gerr; +@@ -608,10 +612,10 @@ + + /* read two integers */ + DEBUG(D_acl) stage = US"MPI"; +-if ( (errstr = as_mpi(pubkey, &verify_ctx->n)) +- || (errstr = as_mpi(pubkey, &verify_ctx->e)) +- ) +- return errstr; ++nbits = pubkey->len; ++if ((errstr = as_mpi(pubkey, &verify_ctx->n))) return errstr; ++nbits = (nbits - pubkey->len) * 8; ++if ((errstr = as_mpi(pubkey, &verify_ctx->e))) return errstr; + + #ifdef extreme_debug + DEBUG(D_acl) debug_printf_indent("rsa_verify_init:\n"); +@@ -624,6 +628,7 @@ + } + + #endif ++if (bits) *bits = nbits; + return NULL; + + asn_err: +@@ -794,7 +799,8 @@ + Return: NULL for success, or an error string */ + + const uschar * +-exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx) ++exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx, ++ unsigned * bits) + { + const uschar * s = pubkey->data; + uschar * ret = NULL; +@@ -818,6 +824,7 @@ + break; + } + ++if (!ret && bits) *bits = EVP_PKEY_bits(verify_ctx->key); + return ret; + } + +diff -ru a/src/pdkim/signing.h b/src/pdkim/signing.h +--- a/src/pdkim/signing.h 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/pdkim/signing.h 2020-05-12 10:13:22.127197000 +0200 +@@ -90,7 +90,7 @@ + + extern const uschar * exim_dkim_signing_init(const uschar *, es_ctx *); + extern const uschar * exim_dkim_sign(es_ctx *, hashmethod, blob *, blob *); +-extern const uschar * exim_dkim_verify_init(blob *, keyformat, ev_ctx *); ++extern const uschar * exim_dkim_verify_init(blob *, keyformat, ev_ctx *, unsigned *); + extern const uschar * exim_dkim_verify(ev_ctx *, hashmethod, blob *, blob *); + + #endif /*DISABLE_DKIM*/ +diff -ru a/src/readconf.c b/src/readconf.c +--- a/src/readconf.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/readconf.c 2020-05-12 10:13:22.129420000 +0200 +@@ -3788,6 +3788,7 @@ + if (!d->driver_name) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "no driver defined for %s \"%s\"", class, d->name); ++ /* s is using big_buffer, so this call had better not */ + (d->info->init)(d); + d = NULL; + } +diff -ru a/src/receive.c b/src/receive.c +--- a/src/receive.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/receive.c 2020-05-12 10:13:22.130574000 +0200 +@@ -270,8 +270,8 @@ + "check_space = " PR_EXIM_ARITH "K inodes = %d msg_size = %d\n", + space, inodes, check_spool_space, check_spool_inodes, msg_size); + +- if ((space >= 0 && space < check_spool_space) || +- (inodes >= 0 && inodes < check_spool_inodes)) ++ if ( space >= 0 && space + msg_size / 1024 < check_spool_space ++ || inodes >= 0 && inodes < check_spool_inodes) + { + log_write(0, LOG_MAIN, "spool directory space check failed: space=" + PR_EXIM_ARITH " inodes=%d", space, inodes); +@@ -1755,6 +1755,13 @@ + + message_linecount = body_linecount = body_zerocount = + max_received_linelength = 0; ++ ++#ifdef WITH_CONTENT_SCAN ++/* reset non-per-part mime variables */ ++mime_is_coverletter = 0; ++mime_is_rfc822 = 0; ++mime_part_count = -1; ++#endif + + #ifndef DISABLE_DKIM + /* Call into DKIM to set up the context. In CHUNKING mode +diff -ru a/src/smtp_in.c b/src/smtp_in.c +--- a/src/smtp_in.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/smtp_in.c 2020-05-12 10:13:22.137165000 +0200 +@@ -2050,7 +2050,8 @@ + f.active_local_sender_retain = local_sender_retain; /* Can be set by ACL */ + sending_ip_address = NULL; + return_path = sender_address = NULL; +-sender_data = NULL; /* Can be set by ACL */ ++deliver_localpart_data = deliver_domain_data = ++recipient_data = sender_data = NULL; /* Can be set by ACL */ + deliver_localpart_parent = deliver_localpart_orig = NULL; + deliver_domain_parent = deliver_domain_orig = NULL; + callout_address = NULL; +diff -ru a/src/spf.c b/src/spf.c +--- a/src/spf.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/spf.c 2020-05-12 10:13:22.138197000 +0200 +@@ -139,7 +139,12 @@ + srr.rr[found++] = (void *) s; + } + +-srr.num_rr = found; ++/* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with ++empty ANSWER section. */ ++ ++if (!(srr.num_rr = found)) ++ srr.herrno = NO_DATA; ++ + /* spfrr->rr must have been malloc()d for this */ + SPF_dns_rr_dup(&spfrr, &srr); + return spfrr; +diff -ru a/src/store.c b/src/store.c +--- a/src/store.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/store.c 2020-05-12 10:13:22.139605000 +0200 +@@ -102,13 +102,6 @@ + static void *next_yield[NPOOLS]; + static int yield_length[NPOOLS] = { -1, -1, -1, -1, -1, -1 }; + +-/* The limits of the tainted pools. Tracking these on new allocations enables +-a fast is_tainted implementation. We assume the kernel only allocates mmaps using +-one side or the other of data+heap, not both. */ +- +-void * tainted_base = (void *)-1; +-void * tainted_top = (void *)0; +- + /* pool_malloc holds the amount of memory used by the store pools; this goes up + and down as store is reset or released. nonpool_malloc is the total got by + malloc from other calls; this doesn't go down because it is just freed by +@@ -162,32 +155,34 @@ + + /******************************************************************************/ + +-/* Slower version check, for use when platform intermixes malloc and mmap area +-addresses. */ ++/* Test if a pointer refers to tainted memory. + ++Slower version check, for use when platform intermixes malloc and mmap area ++addresses. Test against the current-block of all tainted pools first, then all ++blocks of all tainted pools. ++ ++Return: TRUE iff tainted ++*/ ++ + BOOL + is_tainted_fn(const void * p) + { + storeblock * b; +-int pool; + +-for (pool = 0; pool < nelem(chainbase); pool++) ++for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) + if ((b = current_block[pool])) + { +- char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; +- if (CS p >= bc && CS p <= bc + b->length) goto hit; ++ uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; ++ if (US p >= bc && US p <= bc + b->length) return TRUE; + } + +-for (pool = 0; pool < nelem(chainbase); pool++) ++for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) + for (b = chainbase[pool]; b; b = b->next) + { +- char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; +- if (CS p >= bc && CS p <= bc + b->length) goto hit; ++ uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; ++ if (US p >= bc && US p <= bc + b->length) return TRUE; + } + return FALSE; +- +-hit: +-return pool >= POOL_TAINT_BASE; + } + + +@@ -199,6 +194,7 @@ + } + + ++ + /************************************************* + * Get a block from the current pool * + *************************************************/ +@@ -730,7 +726,7 @@ + BOOL release_ok = !tainted && store_last_get[pool] == block; + uschar * newtext; + +-#ifndef MACRO_PREDEF ++#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) + if (is_tainted(block) != tainted) + die_tainted(US"store_newblock", CUS func, linenumber); + #endif +@@ -786,9 +782,6 @@ + PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to mmap %d bytes of memory: " + "called from line %d of %s", size, line, func); +- +-if (yield < tainted_base) tainted_base = yield; +-if ((top = US yield + size) > tainted_top) tainted_top = top; + + return store_alloc_tail(yield, size, func, line, US"Mmap"); + } +diff -ru a/src/string.c b/src/string.c +--- a/src/string.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/string.c 2020-05-12 10:13:22.146645000 +0200 +@@ -12,7 +12,6 @@ + #include "exim.h" + #include + +-static void gstring_rebuffer(gstring * g); + + #ifndef COMPILE_UTILITY + /************************************************* +@@ -1241,16 +1240,6 @@ + return !!gp; + } + +- +- +-/* Copy the content of a string to tainted memory */ +-static void +-gstring_rebuffer(gstring * g) +-{ +-uschar * s = store_get(g->size, TRUE); +-memcpy(s, g->s, g->ptr); +-g->s = s; +-} + + + +diff -ru a/src/tls-gnu.c b/src/tls-gnu.c +--- a/src/tls-gnu.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/tls-gnu.c 2020-05-12 10:13:22.148137000 +0200 +@@ -181,6 +181,10 @@ + BOOL peer_dane_verified; + BOOL trigger_sni_changes; + BOOL have_set_peerdn; ++#ifdef SUPPORT_CORK ++ BOOL corked:1; ++#endif ++ + const struct host_item *host; /* NULL if server */ + gnutls_x509_crt_t peercert; + uschar *peerdn; +@@ -3309,9 +3313,8 @@ + size_t left = len; + exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server; + #ifdef SUPPORT_CORK +-static BOOL corked = FALSE; + +-if (more && !corked) gnutls_record_cork(state->session); ++if (more && !state->corked) gnutls_record_cork(state->session); + #endif + + DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__, +@@ -3352,10 +3355,10 @@ + } + + #ifdef SUPPORT_CORK +-if (more != corked) ++if (more != state->corked) + { + if (!more) (void) gnutls_record_uncork(state->session, 0); +- corked = more; ++ state->corked = more; + } + #endif + +diff -ru a/src/tls-openssl.c b/src/tls-openssl.c +--- a/src/tls-openssl.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/tls-openssl.c 2020-05-12 10:13:22.149180000 +0200 +@@ -2480,7 +2480,7 @@ + #endif + } + +- /* If a certificate file is empty, the next function fails with an ++ /* If a certificate file is empty, the load function fails with an + unhelpful error message. If we skip it, we get the correct behaviour (no + certificates are recognized, but the error message is still misleading (it + says no certificate was supplied). But this is better. */ +@@ -2489,9 +2489,9 @@ + && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) + return tls_error(US"SSL_CTX_load_verify_locations", host, NULL, errstr); + +- /* Load the list of CAs for which we will accept certs, for sending +- to the client. This is only for the one-file tls_verify_certificates +- variant. ++ /* On the server load the list of CAs for which we will accept certs, for ++ sending to the client. This is only for the one-file ++ tls_verify_certificates variant. + If a list isn't loaded into the server, but some verify locations are set, + the server end appears to make a wildcard request for client certs. + Meanwhile, the client library as default behaviour *ignores* the list +@@ -2503,7 +2503,7 @@ + { + STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); + +- SSL_CTX_set_client_CA_list(sctx, names); ++ if (!host) SSL_CTX_set_client_CA_list(sctx, names); + DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", + sk_X509_NAME_num(names)); + } +diff -ru a/src/transport.c b/src/transport.c +--- a/src/transport.c 2020-01-03 13:08:52.000000000 +0100 ++++ b/src/transport.c 2020-05-12 10:13:22.150276000 +0200 +@@ -253,6 +253,7 @@ + + else + { ++ sigalrm_seen = FALSE; + ALARM(local_timeout); + + rc = +Only in a/src: version.h +Only in a/src: version.sh