diff --git a/mail/exim/files/debian/75_01-Fix-exit-on-attempt-to-rewrite-a-malformed-address.-.patch b/mail/exim/files/debian/75_01-Fix-exit-on-attempt-to-rewrite-a-malformed-address.-.patch new file mode 100644 index 000000000000..bf0f64942d7d --- /dev/null +++ b/mail/exim/files/debian/75_01-Fix-exit-on-attempt-to-rewrite-a-malformed-address.-.patch @@ -0,0 +1,57 @@ +From e7ec503729970a03d4509921342bc81313976126 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Tue, 12 Jul 2022 22:14:04 +0100 +Subject: [PATCH] Fix exit on attempt to rewrite a malformed address. Bug 2903 + +--- + doc/ChangeLog | 5 + + src/rewrite.c | 9 +- + test/confs/0471 | 7 + + test/log/0471 | 5 + + test/scripts/0000-Basic/0471 | 4 +- + test/stderr/0471 | 245 ++++++++++++++++++++++++++++++++++- + 6 files changed, 267 insertions(+), 8 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -1,9 +1,14 @@ + This document describes *changes* to previous versions, that might + affect Exim's operation, with an unchanged configuration file. For new + options, and new features, see the NewStuff file next to this ChangeLog. + ++JH/04 Bug 2903: avoid exit on an attempt to rewrite a malformed address. ++ Make the rewrite never match and keep the logging. Trust the ++ admin to be using verify=header-syntax (to actually reject the message). ++ ++ + Exim version 4.96 + ----------------- + + JH/01 Move the wait-for-next-tick (needed for unique messmage IDs) from + after reception to before a subsequent reception. This should +--- a/src/rewrite.c ++++ b/src/rewrite.c +@@ -493,19 +493,18 @@ + empty address, overlong addres. Sometimes the result matters, sometimes not. + It seems this function is called for *any* header we see. */ + + if (!recipient) + { +- /* Handle unparesable addresses in the header. Slightly ugly because a ++ /* 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. */ ++ 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); +- exim_exit(EXIT_FAILURE); +- } ++ + loop_reset_point = store_reset(loop_reset_point); + continue; + } + + /* If routed_old is not NULL, this is a rewrite caused by a router, diff --git a/mail/exim/files/debian/75_05-SPF-fix-memory-accounting-for-error-case.patch b/mail/exim/files/debian/75_05-SPF-fix-memory-accounting-for-error-case.patch new file mode 100644 index 000000000000..e474acf6f54d --- /dev/null +++ b/mail/exim/files/debian/75_05-SPF-fix-memory-accounting-for-error-case.patch @@ -0,0 +1,25 @@ +From 93c722ce0549360af68269f088f4e59ed8fc130e Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Sun, 7 Aug 2022 17:00:27 +0100 +Subject: [PATCH] SPF: fix memory accounting for error case + +--- + src/spf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/spf.c b/src/spf.c +index db6eea3a8..a8c0f75c4 100644 +--- a/src/spf.c ++++ b/src/spf.c +@@ -204,7 +204,7 @@ spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server, + "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND); + if (!spf_nxdomain) + { +- free(spf_dns_server); ++ store_free(spf_dns_server); + return NULL; + } + +-- +2.35.1 + diff --git a/mail/exim/files/debian/75_08-Fix-regex-n-use-after-free.-Bug-2915.patch b/mail/exim/files/debian/75_08-Fix-regex-n-use-after-free.-Bug-2915.patch new file mode 100644 index 000000000000..2429e9ff55b9 --- /dev/null +++ b/mail/exim/files/debian/75_08-Fix-regex-n-use-after-free.-Bug-2915.patch @@ -0,0 +1,193 @@ +From 4e9ed49f8f12eb331b29bd5b6dc3693c520fddc2 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Wed, 31 Aug 2022 15:37:40 +0100 +Subject: [PATCH] Fix $regex use-after-free. Bug 2915 + +--- + doc/ChangeLog | 8 +++++++- + src/exim.c | 4 +--- + src/expand.c | 2 +- + src/functions.h | 1 + + src/globals.c | 2 +- + src/regex.c | 29 ++++++++++++++++++----------- + src/smtp_in.c | 2 ++ + 7 files changed, 55 insertions(+), 17 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -4,15 +4,21 @@ + + JH/04 Bug 2903: avoid exit on an attempt to rewrite a malformed address. + Make the rewrite never match and keep the logging. Trust the + admin to be using verify=header-syntax (to actually reject the message). + ++JH/08 Bug 2915: Fix use-after-free for $regex variables. Previously when ++ more than one message arrived in a single connection a reference from ++ the earlier message could be re-used. Often a sigsegv resulted. ++ These variables were introduced in Exim 4.87. ++ Debug help from Graeme Fowler. ++ + + Exim version 4.96 + ----------------- + +-JH/01 Move the wait-for-next-tick (needed for unique messmage IDs) from ++JH/01 Move the wait-for-next-tick (needed for unique message IDs) from + after reception to before a subsequent reception. This should + mean slightly faster delivery, and also confirmation of reception + to senders. + + JH/02 Move from using the pcre library to pcre2. The former is no longer +--- a/src/exim.c ++++ b/src/exim.c +@@ -1999,12 +1999,10 @@ + + regex_whitelisted_macro = + regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); + #endif + +-for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +- + /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; + this seems to be a generally accepted convention, since one finds symbolic + links called "mailq" in standard OS configurations. */ + + if ((namelen == 5 && Ustrcmp(argv[0], "mailq") == 0) || +@@ -6082,11 +6080,11 @@ + 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; ++ regex_vars_clear(); + + store_reset(reset_point); + } + + exim_exit(EXIT_SUCCESS); /* Never returns */ +--- a/src/expand.c ++++ b/src/expand.c +@@ -1871,11 +1871,11 @@ + { + tree_node * node = tree_search(router_var, name + 2); + return node ? node->data.ptr : strict_acl_vars ? NULL : US""; + } + +-/* Handle $auth variables. */ ++/* Handle $auth, $regex variables. */ + + if (Ustrncmp(name, "auth", 4) == 0) + { + uschar *endptr; + int n = Ustrtoul(name + 4, &endptr, 10); +--- a/src/functions.h ++++ b/src/functions.h +@@ -436,10 +436,11 @@ + extern int regex(const uschar **); + #endif + extern BOOL regex_match(const pcre2_code *, const uschar *, int, uschar **); + extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, int); + extern const pcre2_code *regex_must_compile(const uschar *, BOOL, BOOL); ++extern void regex_vars_clear(void); + extern void retry_add_item(address_item *, uschar *, int); + extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, + uschar **, uschar **); + extern retry_config *retry_find_config(const uschar *, const uschar *, int, int); + extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *, +--- a/src/globals.c ++++ b/src/globals.c +@@ -1313,11 +1313,11 @@ + #ifndef DISABLE_PIPE_CONNECT + const pcre2_code *regex_EARLY_PIPE = NULL; + #endif + const pcre2_code *regex_ismsgid = NULL; + const pcre2_code *regex_smtp_code = NULL; +-const uschar *regex_vars[REGEX_VARS]; ++const uschar *regex_vars[REGEX_VARS] = { 0 };; + #ifdef WHITELIST_D_MACROS + const pcre2_code *regex_whitelisted_macro = NULL; + #endif + #ifdef WITH_CONTENT_SCAN + uschar *regex_match_string = NULL; +--- a/src/regex.c ++++ b/src/regex.c +@@ -94,22 +94,32 @@ + } + pcre2_match_data_free(md); + return FAIL; + } + ++ ++/* reset expansion variables */ ++void ++regex_vars_clear(void) ++{ ++regex_match_string = NULL; ++for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; ++} ++ ++ ++ + int +-regex(const uschar **listptr) ++regex(const uschar ** listptr) + { + unsigned long mbox_size; +-FILE *mbox_file; +-pcre_list *re_list_head; +-uschar *linebuffer; ++FILE * mbox_file; ++pcre_list * re_list_head; ++uschar * linebuffer; + long f_pos = 0; + int ret = FAIL; + +-/* reset expansion variable */ +-regex_match_string = NULL; ++regex_vars_clear(); + + if (!mime_stream) /* We are in the DATA ACL */ + { + if (!(mbox_file = spool_mbox(&mbox_size, NULL, NULL))) + { /* error while spooling */ +@@ -167,18 +177,17 @@ + + + int + mime_regex(const uschar **listptr) + { +-pcre_list *re_list_head = NULL; +-FILE *f; +-uschar *mime_subject = NULL; ++pcre_list * re_list_head = NULL; ++FILE * f; ++uschar * mime_subject = NULL; + int mime_subject_len = 0; + int ret; + +-/* reset expansion variable */ +-regex_match_string = NULL; ++regex_vars_clear(); + + /* precompile our regexes */ + if (!(re_list_head = compile(*listptr))) + return FAIL; /* no regexes -> nothing to do */ + +--- a/src/smtp_in.c ++++ b/src/smtp_in.c +@@ -2155,12 +2155,14 @@ + prdr_requested = FALSE; + #endif + #ifdef SUPPORT_I18N + message_smtputf8 = FALSE; + #endif ++regex_vars_clear(); + body_linecount = body_zerocount = 0; + ++lookup_value = NULL; /* Can be set by ACL */ + sender_rate = sender_rate_limit = sender_rate_period = NULL; + ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ + /* Note that ratelimiters_conn persists across resets. */ + + /* Reset message ACL variables */ diff --git a/mail/exim/files/debian/75_09-Fix-non-WITH_CONTENT_SCAN-build.patch b/mail/exim/files/debian/75_09-Fix-non-WITH_CONTENT_SCAN-build.patch new file mode 100644 index 000000000000..6071fa7c5bf4 --- /dev/null +++ b/mail/exim/files/debian/75_09-Fix-non-WITH_CONTENT_SCAN-build.patch @@ -0,0 +1,58 @@ +From d8ecc7bf97934a1e2244788c610c958cacd740bd Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Wed, 31 Aug 2022 17:03:37 +0100 +Subject: [PATCH 1/3] Fix non-WITH_CONTENT_SCAN build. + +Broken-by: 4e9ed49f8f +--- + src/exim.c | 11 +++++++++++ + src/regex.c | 10 ---------- + 2 files changed, 11 insertions(+), 10 deletions(-) + +--- a/src/exim.c ++++ b/src/exim.c +@@ -1677,10 +1677,21 @@ + if ((s = expand_string(big_buffer))) printf("%s\n", CS s); + else printf("Failed: %s\n", expand_string_message); + } + + ++/* reset regex expansion variables */ ++void ++regex_vars_clear(void) ++{ ++regex_match_string = NULL; ++for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; ++} ++ ++ ++ ++ + + /************************************************* + * Entry point and high-level code * + *************************************************/ + +--- a/src/regex.c ++++ b/src/regex.c +@@ -95,20 +95,10 @@ + pcre2_match_data_free(md); + return FAIL; + } + + +-/* reset expansion variables */ +-void +-regex_vars_clear(void) +-{ +-regex_match_string = NULL; +-for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +-} +- +- +- + int + regex(const uschar ** listptr) + { + unsigned long mbox_size; + FILE * mbox_file; diff --git a/mail/exim/files/debian/75_10-Fix-non-WITH_CONTENT_SCAN-build-2.patch b/mail/exim/files/debian/75_10-Fix-non-WITH_CONTENT_SCAN-build-2.patch new file mode 100644 index 000000000000..0a8ed514ffe8 --- /dev/null +++ b/mail/exim/files/debian/75_10-Fix-non-WITH_CONTENT_SCAN-build-2.patch @@ -0,0 +1,135 @@ +From 158dff9936e36a2d31d037d3988b9353458d6471 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Wed, 31 Aug 2022 17:17:59 +0100 +Subject: [PATCH 2/3] Fix non-WITH_CONTENT_SCAN build (2) + +Broken-by: d8ecc7bf97 +--- + src/exim.c | 13 +------------ + src/functions.h | 2 +- + src/globals.h | 2 +- + src/regex.c | 10 ++++++++++ + src/smtp_in.c | 2 ++ + 5 files changed, 15 insertions(+), 14 deletions(-) + +--- a/src/exim.c ++++ b/src/exim.c +@@ -1677,21 +1677,10 @@ + if ((s = expand_string(big_buffer))) printf("%s\n", CS s); + else printf("Failed: %s\n", expand_string_message); + } + + +-/* reset regex expansion variables */ +-void +-regex_vars_clear(void) +-{ +-regex_match_string = NULL; +-for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +-} +- +- +- +- + + /************************************************* + * Entry point and high-level code * + *************************************************/ + +@@ -6085,17 +6074,17 @@ + deliver_domain_orig = NULL; + deliver_host = deliver_host_address = NULL; + dnslist_domain = dnslist_matched = NULL; + #ifdef WITH_CONTENT_SCAN + malware_name = NULL; ++ regex_vars_clear(); + #endif + callout_address = NULL; + sending_ip_address = NULL; + deliver_localpart_data = deliver_domain_data = + recipient_data = sender_data = NULL; + acl_var_m = NULL; +- regex_vars_clear(); + + store_reset(reset_point); + } + + exim_exit(EXIT_SUCCESS); /* Never returns */ +--- a/src/functions.h ++++ b/src/functions.h +@@ -432,15 +432,15 @@ + extern BOOL receive_msg(BOOL); + extern int_eximarith_t receive_statvfs(BOOL, int *); + extern void receive_swallow_smtp(void); + #ifdef WITH_CONTENT_SCAN + extern int regex(const uschar **); ++extern void regex_vars_clear(void); + #endif + extern BOOL regex_match(const pcre2_code *, const uschar *, int, uschar **); + extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, int); + extern const pcre2_code *regex_must_compile(const uschar *, BOOL, BOOL); +-extern void regex_vars_clear(void); + extern void retry_add_item(address_item *, uschar *, int); + extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, + uschar **, uschar **); + extern retry_config *retry_find_config(const uschar *, const uschar *, int, int); + extern BOOL retry_ultimate_address_timeout(uschar *, const uschar *, +--- a/src/globals.h ++++ b/src/globals.h +@@ -895,16 +895,16 @@ + #ifndef DISABLE_PIPE_CONNECT + extern const pcre2_code *regex_EARLY_PIPE; /* For recognizing PIPE_CONNCT */ + #endif + extern const pcre2_code *regex_ismsgid; /* Compiled r.e. for message ID */ + extern const pcre2_code *regex_smtp_code; /* For recognizing SMTP codes */ +-extern const uschar *regex_vars[]; /* $regexN variables */ + #ifdef WHITELIST_D_MACROS + extern const pcre2_code *regex_whitelisted_macro; /* For -D macro values */ + #endif + #ifdef WITH_CONTENT_SCAN + extern uschar *regex_match_string; /* regex that matched a line (regex ACL condition) */ ++extern const uschar *regex_vars[]; + #endif + extern int remote_delivery_count; /* Number of remote addresses */ + extern int remote_max_parallel; /* Maximum parallel delivery */ + extern uschar *remote_sort_domains; /* Remote domain sorting order */ + extern retry_config *retries; /* Chain of retry config information */ +--- a/src/regex.c ++++ b/src/regex.c +@@ -95,10 +95,20 @@ + pcre2_match_data_free(md); + return FAIL; + } + + ++/* reset expansion variables */ ++void ++regex_vars_clear(void) ++{ ++regex_match_string = NULL; ++for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; ++} ++ ++ ++ + int + regex(const uschar ** listptr) + { + unsigned long mbox_size; + FILE * mbox_file; +--- a/src/smtp_in.c ++++ b/src/smtp_in.c +@@ -2155,11 +2155,13 @@ + prdr_requested = FALSE; + #endif + #ifdef SUPPORT_I18N + message_smtputf8 = FALSE; + #endif ++#ifdef WITH_CONTENT_SCAN + regex_vars_clear(); ++#endif + body_linecount = body_zerocount = 0; + + lookup_value = NULL; /* Can be set by ACL */ + sender_rate = sender_rate_limit = sender_rate_period = NULL; + ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ diff --git a/mail/exim/files/debian/75_11-Fix-non-WITH_CONTENT_SCAN-build-3.patch b/mail/exim/files/debian/75_11-Fix-non-WITH_CONTENT_SCAN-build-3.patch new file mode 100644 index 000000000000..b06d89679b7e --- /dev/null +++ b/mail/exim/files/debian/75_11-Fix-non-WITH_CONTENT_SCAN-build-3.patch @@ -0,0 +1,45 @@ +From 32da6327e434e986a18b75a84f2d8c687ba14619 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 1 Sep 2022 15:54:35 +0100 +Subject: [PATCH 3/3] Fix non-WITH_CONTENT_SCAN build (3) + +Broken-by: d8ecc7bf97 +--- + src/expand.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/expand.c b/src/expand.c +index 89de56255..831ca2b75 100644 +--- a/src/expand.c ++++ b/src/expand.c +@@ -1869,6 +1869,7 @@ if (Ustrncmp(name, "auth", 4) == 0) + if (!*endptr && n != 0 && n <= AUTH_VARS) + return auth_vars[n-1] ? auth_vars[n-1] : US""; + } ++#ifdef WITH_CONTENT_SCAN + else if (Ustrncmp(name, "regex", 5) == 0) + { + uschar *endptr; +@@ -1876,6 +1877,7 @@ else if (Ustrncmp(name, "regex", 5) == 0) + if (!*endptr && n != 0 && n <= REGEX_VARS) + return regex_vars[n-1] ? regex_vars[n-1] : US""; + } ++#endif + + /* For all other variables, search the table */ + +@@ -8715,9 +8717,11 @@ assert_variable_notin() treats as const, so deconst is safe. */ + for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i]) + assert_variable_notin(US"auth", US auth_vars[i], &e); + ++#ifdef WITH_CONTENT_SCAN + /* check regex variables. assert_variable_notin() treats as const. */ + for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i]) + assert_variable_notin(US"regex", US regex_vars[i], &e); ++#endif + + /* check known-name variables */ + for (var_entry * v = var_table; v < var_table + var_table_size; v++) +-- +2.35.1 + diff --git a/mail/exim/files/debian/75_16-GnuTLS-fix-for-clients-offering-no-TLS-extensions.patch b/mail/exim/files/debian/75_16-GnuTLS-fix-for-clients-offering-no-TLS-extensions.patch new file mode 100644 index 000000000000..ae2fa16f51ea --- /dev/null +++ b/mail/exim/files/debian/75_16-GnuTLS-fix-for-clients-offering-no-TLS-extensions.patch @@ -0,0 +1,114 @@ +From ece23f05d6a430a461a75639197271c23f6858ec Mon Sep 17 00:00:00 2001 +From: Jasen Betts +Date: Fri, 30 Sep 2022 13:49:41 +0100 +Subject: [PATCH] GnuTLS: fix for clients offering no TLS extensions + +--- + doc/ChangeLog | 3 +++ + src/tls-gnu.c | 3 ++- + src/tls-openssl.c | 39 +++++++++++++++--------------- + test/confs/2091 | 1 + + test/log/2091 | 3 +++ + test/scripts/2090-GnuTLS-ALPN/2091 | 19 +++++++++++++++ + test/stdout/2091 | 21 ++++++++++++++++ + 7 files changed, 68 insertions(+), 21 deletions(-) + create mode 120000 test/confs/2091 + create mode 100644 test/log/2091 + create mode 100644 test/scripts/2090-GnuTLS-ALPN/2091 + create mode 100644 test/stdout/2091 + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -10,10 +10,14 @@ + more than one message arrived in a single connection a reference from + the earlier message could be re-used. Often a sigsegv resulted. + These variables were introduced in Exim 4.87. + Debug help from Graeme Fowler. + ++JH/10 GnuTLS: fix for (IOT?) clients offering no TLS extensions at all. ++ Find and fix by Jasen Betts. ++ ++ + + Exim version 4.96 + ----------------- + + JH/01 Move the wait-for-next-tick (needed for unique message IDs) from +--- a/src/tls-gnu.c ++++ b/src/tls-gnu.c +@@ -1130,12 +1130,13 @@ + static int + tls_server_clienthello_cb(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, const gnutls_datum_t * msg) + { + /* Call fn for each extension seen. 3.6.3 onwards */ +-return gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg, ++int rc = gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg, + GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO); ++return rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE ? 0 : rc; + } + + + # ifdef notdef_crashes + /* Make a note that we saw a status-response */ +--- a/src/tls-openssl.c ++++ b/src/tls-openssl.c +@@ -940,40 +940,39 @@ + + Returns: nothing + */ + + static void +-info_callback(SSL *s, int where, int ret) ++info_callback(SSL * s, int where, int ret) + { + DEBUG(D_tls) + { +- const uschar * str; ++ gstring * g = NULL; + +- if (where & SSL_ST_CONNECT) +- str = US"SSL_connect"; +- else if (where & SSL_ST_ACCEPT) +- str = US"SSL_accept"; +- else +- str = US"SSL info (undefined)"; ++ if (where & SSL_ST_CONNECT) g = string_append_listele(g, ',', US"SSL_connect"); ++ if (where & SSL_ST_ACCEPT) g = string_append_listele(g, ',', US"SSL_accept"); ++ if (where & SSL_CB_LOOP) g = string_append_listele(g, ',', US"state_chg"); ++ if (where & SSL_CB_EXIT) g = string_append_listele(g, ',', US"hshake_exit"); ++ if (where & SSL_CB_READ) g = string_append_listele(g, ',', US"read"); ++ if (where & SSL_CB_WRITE) g = string_append_listele(g, ',', US"write"); ++ if (where & SSL_CB_ALERT) g = string_append_listele(g, ',', US"alert"); ++ if (where & SSL_CB_HANDSHAKE_START) g = string_append_listele(g, ',', US"hshake_start"); ++ if (where & SSL_CB_HANDSHAKE_DONE) g = string_append_listele(g, ',', US"hshake_done"); + + if (where & SSL_CB_LOOP) +- debug_printf("%s: %s\n", str, SSL_state_string_long(s)); ++ debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s)); + else if (where & SSL_CB_ALERT) +- debug_printf("SSL3 alert %s:%s:%s\n", +- str = where & SSL_CB_READ ? US"read" : US"write", ++ debug_printf("SSL %s %s:%s\n", g->s, + SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); + else if (where & SSL_CB_EXIT) + { +- if (ret == 0) +- debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s)); +- else if (ret < 0) +- debug_printf("%s: error in %s\n", str, SSL_state_string_long(s)); ++ if (ret <= 0) ++ debug_printf("SSL %s: %s in %s\n", g->s, ++ ret == 0 ? "failed" : "error", SSL_state_string_long(s)); + } +- else if (where & SSL_CB_HANDSHAKE_START) +- debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s)); +- else if (where & SSL_CB_HANDSHAKE_DONE) +- debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s)); ++ else if (where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)) ++ debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s)); + } + } + + #ifdef OPENSSL_HAVE_KEYLOG_CB + static void diff --git a/mail/exim/files/debian/75_18-Fix-Build-with-libopendmarc-1.4.x-fixes-2728.patch b/mail/exim/files/debian/75_18-Fix-Build-with-libopendmarc-1.4.x-fixes-2728.patch new file mode 100644 index 000000000000..f261d621d67a --- /dev/null +++ b/mail/exim/files/debian/75_18-Fix-Build-with-libopendmarc-1.4.x-fixes-2728.patch @@ -0,0 +1,88 @@ +From 1561c5d88b3a23a4348d8e3c1ce28554fcbcfe46 Mon Sep 17 00:00:00 2001 +From: "Heiko Schlittermann (HS12-RIPE)" +Date: Sat, 15 Oct 2022 19:30:58 +0200 +Subject: [PATCH 1/2] Fix: Build with libopendmarc 1.4.x (fixes 2728) + +--- + doc/ChangeLog | 3 +++ + src/EDITME | 7 +++++-- + src/config.h.defaults | 1 + + src/dmarc.c | 7 ++++++- + 4 files changed, 15 insertions(+), 3 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -13,10 +13,13 @@ + Debug help from Graeme Fowler. + + JH/10 GnuTLS: fix for (IOT?) clients offering no TLS extensions at all. + Find and fix by Jasen Betts. + ++HS/01 Bug 2728: Introduce EDITME option "DMARC_API" to work around incompatible ++ API changes in libopendmarc. ++ + + + Exim version 4.96 + ----------------- + +--- a/src/EDITME ++++ b/src/EDITME +@@ -600,18 +600,21 @@ + + # EXPERIMENTAL_DCC=yes + + # Uncomment the following line to add DMARC checking capability, implemented + # using libopendmarc libraries. You must have SPF and DKIM support enabled also. +-# Library version libopendmarc-1.4.1-1.fc33.x86_64 (on Fedora 33) is known broken; +-# 1.3.2-3 works. I seems that the OpenDMARC project broke their API. + # SUPPORT_DMARC=yes + # CFLAGS += -I/usr/local/include + # LDFLAGS += -lopendmarc + # Uncomment the following if you need to change the default. You can + # override it at runtime (main config option dmarc_tld_file) + # DMARC_TLD_FILE=/etc/exim/opendmarc.tlds ++# ++# Library version libopendmarc-1.4.1-1.fc33.x86_64 (on Fedora 33) is known broken; ++# 1.3.2-3 works. It seems that the OpenDMARC project broke their API. ++# Use this option if you need to build with an old library (1.3.x) ++# DMARC_API=100300 + + # Uncomment the following line to add ARC (Authenticated Received Chain) + # support. You must have SPF and DKIM support enabled also. + # EXPERIMENTAL_ARC=yes + +--- a/src/config.h.defaults ++++ b/src/config.h.defaults +@@ -148,10 +148,11 @@ + #define STRING_SPRINTF_BUFFER_SIZE (8192 * 4) + + #define SUPPORT_CRYPTEQ + #define SUPPORT_DANE + #define SUPPORT_DMARC ++#define DMARC_API 100400 + #define DMARC_TLD_FILE "/etc/exim/opendmarc.tlds" + #define SUPPORT_I18N + #define SUPPORT_I18N_2008 + #define SUPPORT_MAILDIR + #define SUPPORT_MAILSTORE +--- a/src/dmarc.c ++++ b/src/dmarc.c +@@ -457,11 +457,16 @@ + dkim_result = vs == PDKIM_VERIFY_PASS ? DMARC_POLICY_DKIM_OUTCOME_PASS : + vs == PDKIM_VERIFY_FAIL ? DMARC_POLICY_DKIM_OUTCOME_FAIL : + vs == PDKIM_VERIFY_INVALID ? DMARC_POLICY_DKIM_OUTCOME_TMPFAIL : + DMARC_POLICY_DKIM_OUTCOME_NONE; + libdm_status = opendmarc_policy_store_dkim(dmarc_pctx, US sig->domain, +- dkim_result, US""); ++/* The opendmarc project broke its API in a way we can't detect * easily. ++ * The EDITME provides a DMARC_API variable */ ++#if DMARC_API >= 100400 ++ sig->selector, ++#endif ++ dkim_result, US""); + DEBUG(D_receive) + debug_printf("DMARC adding DKIM sender domain = %s\n", sig->domain); + if (libdm_status != DMARC_PARSE_OKAY) + log_write(0, LOG_MAIN|LOG_PANIC, + "failure to store dkim (%s) for DMARC: %s", diff --git a/mail/exim/files/debian/75_19-DMARC-fix-use-after-free-in-dmarc_dns_lookup.patch b/mail/exim/files/debian/75_19-DMARC-fix-use-after-free-in-dmarc_dns_lookup.patch new file mode 100644 index 000000000000..e8bda9e07b35 --- /dev/null +++ b/mail/exim/files/debian/75_19-DMARC-fix-use-after-free-in-dmarc_dns_lookup.patch @@ -0,0 +1,39 @@ +From 12fb3842f81bcbd4a4519d5728f2d7e0e3ca1445 Mon Sep 17 00:00:00 2001 +From: Lorenz Brun +Date: Fri, 14 Oct 2022 21:02:51 +0200 +Subject: [PATCH 2/2] DMARC: fix use-after-free in dmarc_dns_lookup + +This fixes a use-after-free in dmarc_dns_lookup where the result +of dns_lookup in dnsa is freed before the required data is copied out. + +Fixes: 9258363 ("DNS: explicit alloc/free of workspace") +--- + src/dmarc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/dmarc.c b/src/dmarc.c +index ad0c26c91..53c2752ac 100644 +--- a/src/dmarc.c ++++ b/src/dmarc.c +@@ -226,16 +226,17 @@ dns_scan dnss; + int rc = dns_lookup(dnsa, string_sprintf("_dmarc.%s", dom), T_TXT, NULL); + + if (rc == DNS_SUCCEED) + for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + if (rr->type == T_TXT && rr->size > 3) + { ++ uschar *record = string_copyn_taint(US rr->data, rr->size, GET_TAINTED); + store_free_dns_answer(dnsa); +- return string_copyn_taint(US rr->data, rr->size, GET_TAINTED); ++ return record; + } + store_free_dns_answer(dnsa); + return NULL; + } + + + static int +-- +2.35.1 + diff --git a/mail/exim/files/debian/75_22-Fix-daemon-startup.-Bug-2930.patch b/mail/exim/files/debian/75_22-Fix-daemon-startup.-Bug-2930.patch new file mode 100644 index 000000000000..2a3434f8b0b1 --- /dev/null +++ b/mail/exim/files/debian/75_22-Fix-daemon-startup.-Bug-2930.patch @@ -0,0 +1,68 @@ +From 221321d2c51b83d1feced80ecd6c2fe33ec5456c Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 3 Nov 2022 20:08:25 +0000 +Subject: [PATCH 1/2] Fix daemon startup. Bug 2930 + +Broken-by: 7d5055276a +--- + doc/ChangeLog | 4 ++++ + src/daemon.c | 8 ++++++-- + 2 files changed, 10 insertions(+), 2 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -16,10 +16,14 @@ + Find and fix by Jasen Betts. + + HS/01 Bug 2728: Introduce EDITME option "DMARC_API" to work around incompatible + API changes in libopendmarc. + ++JH/12 Bug 2930: Fix daemon startup. When started from any process apart from ++ pid 1, in the normal "background daemon" mode, having to drop process- ++ group leadership also lost track of needing to create listener sockets. ++ + + + Exim version 4.96 + ----------------- + +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -1744,19 +1744,23 @@ + { + /* If the parent process of this one has pid == 1, we are re-initializing the + daemon as the result of a SIGHUP. In this case, there is no need to do + anything, because the controlling terminal has long gone. Otherwise, fork, in + case current process is a process group leader (see 'man setsid' for an +- explanation) before calling setsid(). */ ++ explanation) before calling setsid(). ++ All other forks want daemon_listen cleared. Rather than blow a register, jsut ++ restore it here. */ + + if (getppid() != 1) + { ++ BOOL daemon_listen = f.daemon_listen; + pid_t pid = exim_fork(US"daemon"); + if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "fork() failed when starting daemon: %s", strerror(errno)); + if (pid > 0) exit(EXIT_SUCCESS); /* in parent process, just exit */ + (void)setsid(); /* release controlling terminal */ ++ f.daemon_listen = daemon_listen; + } + } + + /* We are now in the disconnected, daemon process (unless debugging). Set up + the listening sockets if required. */ +@@ -2090,11 +2094,11 @@ + { /* found; append port to list */ + for (p = i2->log; *p; ) p++; /* end of existing string */ + if (*--p == '}') *p = '\0'; /* drop EOL */ + while (isdigit(*--p)) ; /* char before port */ + +- i2->log = *p == ':' /* no list yet? */ ++ i2->log = *p == ':' /* no list yet? { */ + ? string_sprintf("%.*s{%s,%d}", + (int)(p - i2->log + 1), i2->log, p+1, ipa->port) + : string_sprintf("%s,%d}", i2->log, ipa->port); + ipa->log = NULL; + break; diff --git a/mail/exim/files/debian/75_23-Fix-reccipients-after-run.-.-Bug-2929.patch b/mail/exim/files/debian/75_23-Fix-reccipients-after-run.-.-Bug-2929.patch new file mode 100644 index 000000000000..1c98ef26083b --- /dev/null +++ b/mail/exim/files/debian/75_23-Fix-reccipients-after-run.-.-Bug-2929.patch @@ -0,0 +1,45 @@ +From 6b331d5834d12bdda21857cd6fffac17038ce3c7 Mon Sep 17 00:00:00 2001 +From: Ruben Jenster +Date: Thu, 3 Nov 2022 21:38:15 +0000 +Subject: [PATCH 2/2] Fix $reccipients after ${run...}. Bug 2929 + +Broken-by: cfe6acff2d +--- + doc/ChangeLog | 3 +++ + src/transport.c | 3 ++- + 2 files changed, 5 insertions(+), 1 deletion(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -20,10 +20,13 @@ + + JH/12 Bug 2930: Fix daemon startup. When started from any process apart from + pid 1, in the normal "background daemon" mode, having to drop process- + group leadership also lost track of needing to create listener sockets. + ++JH/13 Bug 2929: Fix using $recipients after ${run...}. A change made for 4.96 ++ resulted in the variable appearing empty. Find and fix by Ruben Jenster. ++ + + + Exim version 4.96 + ----------------- + +--- a/src/transport.c ++++ b/src/transport.c +@@ -2342,13 +2342,14 @@ + /* Handle normal expansion string */ + + else + { + const uschar *expanded_arg; ++ BOOL enable_dollar_recipients_g = f.enable_dollar_recipients; + f.enable_dollar_recipients = allow_dollar_recipients; + expanded_arg = expand_cstring(argv[i]); +- f.enable_dollar_recipients = FALSE; ++ f.enable_dollar_recipients = enable_dollar_recipients_g; + + if (!expanded_arg) + { + uschar *msg = string_sprintf("Expansion of \"%s\" " + "from command \"%s\" in %s failed: %s", diff --git a/mail/exim/files/debian/75_31-Fix-regext-substring-capture-variables-for-null-matc.patch b/mail/exim/files/debian/75_31-Fix-regext-substring-capture-variables-for-null-matc.patch new file mode 100644 index 000000000000..bd250f2b7191 --- /dev/null +++ b/mail/exim/files/debian/75_31-Fix-regext-substring-capture-variables-for-null-matc.patch @@ -0,0 +1,79 @@ +From e63825824cc406c160ccbf2b154c5d81b168604a Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Fri, 11 Nov 2022 00:05:59 +0000 +Subject: [PATCH 1/2] Fix regext substring capture variables for null matches. + Bug 2933 + +broken-by: 59d66fdc13f0 +--- + doc/ChangeLog | 5 +++++ + src/exim.c | 2 ++ + src/malware.c | 3 +++ + src/regex.c | 2 +- + 4 files changed, 11 insertions(+), 1 deletion(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -22,10 +22,15 @@ + pid 1, in the normal "background daemon" mode, having to drop process- + group leadership also lost track of needing to create listener sockets. + + JH/13 Bug 2929: Fix using $recipients after ${run...}. A change made for 4.96 + resulted in the variable appearing empty. Find and fix by Ruben Jenster. ++ ++JH/14 Bug 2933: Fix regex substring match variables for null matches. Since 4.96 ++ a capture group which obtained no text (eg. "(abc)*" matching zero ++ occurrences) could cause a segfault if the corresponding $ was ++ expanded. + + + + Exim version 4.96 + ----------------- +--- a/src/exim.c ++++ b/src/exim.c +@@ -167,10 +167,12 @@ + for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) + { + PCRE2_SIZE len; + pcre2_substring_get_bynumber(md, matchnum, + (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len); ++ if (!expand_nstring[expand_nmax]) ++ { expand_nstring[expand_nmax] = US""; len = 0; } + expand_nlength[expand_nmax++] = (int)len; + } + expand_nmax--; + } + else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any) +--- a/src/malware.c ++++ b/src/malware.c +@@ -323,11 +323,14 @@ + int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx); + PCRE2_UCHAR * substr = NULL; + PCRE2_SIZE slen; + + if (i >= 2) /* Got it */ ++ { + pcre2_substring_get_bynumber(md, 1, &substr, &slen); ++ if (!substr) substr = US""; ++ } + return US substr; + } + + static const pcre2_code * + m_pcre_nextinlist(const uschar ** list, int * sep, +--- a/src/regex.c ++++ b/src/regex.c +@@ -84,11 +84,11 @@ + for (int nn = 1; nn < n; nn++) + { + PCRE2_UCHAR * cstr; + PCRE2_SIZE cslen; + pcre2_substring_get_bynumber(md, nn, &cstr, &cslen); +- regex_vars[nn-1] = CUS cstr; ++ regex_vars[nn-1] = cstr ? CUS cstr : CUS""; + } + + return OK; + } + } diff --git a/mail/exim/files/debian/75_32-Fix-regex-substring-capture-variables-for-null-match.patch b/mail/exim/files/debian/75_32-Fix-regex-substring-capture-variables-for-null-match.patch new file mode 100644 index 000000000000..7c2c14a385dc --- /dev/null +++ b/mail/exim/files/debian/75_32-Fix-regex-substring-capture-variables-for-null-match.patch @@ -0,0 +1,94 @@ +From 7ad1a2b2cc57b5f4bcb59186a9a8abcbed9f4f76 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Fri, 11 Nov 2022 18:22:00 +0000 +Subject: [PATCH 2/2] Fix regex substring capture variables for null matches + (again). Bug 2933 + +Broken-by: 59d66fdc13f0 +--- + src/exim.c | 11 +++++------ + src/malware.c | 10 +++++----- + src/regex.c | 8 ++++---- + test/aux-var-src/0383.F | 4 ++-- + test/log/0383 | 4 ++-- + test/mail/0383.CALLER | 8 ++++---- + test/scripts/0000-Basic/0002 | 2 ++ + test/stdout/0002 | 2 ++ + 8 files changed, 26 insertions(+), 23 deletions(-) + +--- a/src/exim.c ++++ b/src/exim.c +@@ -160,20 +160,19 @@ + PCRE_EOPT | options, md, pcre_mtc_ctx); + BOOL yield; + + if ((yield = (res >= 0))) + { ++ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); + res = pcre2_get_ovector_count(md); + expand_nmax = setup < 0 ? 0 : setup + 1; + for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) + { +- PCRE2_SIZE len; +- pcre2_substring_get_bynumber(md, matchnum, +- (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len); +- if (!expand_nstring[expand_nmax]) +- { expand_nstring[expand_nmax] = US""; len = 0; } +- expand_nlength[expand_nmax++] = (int)len; ++ int off = matchnum * 2; ++ int len = ovec[off + 1] - ovec[off]; ++ expand_nstring[expand_nmax] = string_copyn(subject + ovec[off], len); ++ expand_nlength[expand_nmax++] = len; + } + expand_nmax--; + } + else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any) + { +--- a/src/malware.c ++++ b/src/malware.c +@@ -319,19 +319,19 @@ + uschar * + m_pcre_exec(const pcre2_code * cre, uschar * text) + { + pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx); + int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx); +-PCRE2_UCHAR * substr = NULL; +-PCRE2_SIZE slen; ++uschar * substr = NULL; + + if (i >= 2) /* Got it */ + { +- pcre2_substring_get_bynumber(md, 1, &substr, &slen); +- if (!substr) substr = US""; ++ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); ++ int len = ovec[3] - ovec[2]; ++ substr = string_copyn(text + ovec[2], len); + } +-return US substr; ++return substr; + } + + static const pcre2_code * + m_pcre_nextinlist(const uschar ** list, int * sep, + char * listerr, uschar ** errstr) +--- a/src/regex.c ++++ b/src/regex.c +@@ -81,14 +81,14 @@ + sizeof(regex_match_string_buffer)-1); + regex_match_string = regex_match_string_buffer; + + for (int nn = 1; nn < n; nn++) + { +- PCRE2_UCHAR * cstr; +- PCRE2_SIZE cslen; +- pcre2_substring_get_bynumber(md, nn, &cstr, &cslen); +- regex_vars[nn-1] = cstr ? CUS cstr : CUS""; ++ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); ++ int off = nn * 2; ++ int len = ovec[off + 1] - ovec[off]; ++ regex_vars[nn-1] = string_copyn(linebuffer + ovec[off], len); + } + + return OK; + } + } diff --git a/mail/exim/files/debian/75_34-Fix-regex-substring-capture-commentary.-Bug-2933.patch b/mail/exim/files/debian/75_34-Fix-regex-substring-capture-commentary.-Bug-2933.patch new file mode 100644 index 000000000000..61d4483e5f04 --- /dev/null +++ b/mail/exim/files/debian/75_34-Fix-regex-substring-capture-commentary.-Bug-2933.patch @@ -0,0 +1,48 @@ +From 9ba47886c71d40edc99b026a99edee269d9c9c6f Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Sat, 12 Nov 2022 12:38:22 +0000 +Subject: [PATCH] Fix regex substring capture - commentary. Bug 2933 + +Broken-by (corrected): 22ed7a5295f1 +--- + src/exim.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/src/exim.c b/src/exim.c +index 16c0184e0..625494ce4 100644 +--- a/src/exim.c ++++ b/src/exim.c +@@ -102,11 +102,13 @@ pcre_gen_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); + * Execute regular expression and set strings * + *************************************************/ + + /* This function runs a regular expression match, and sets up the pointers to + the matched substrings. The matched strings are copied so the lifetime of +-the subject is not a problem. ++the subject is not a problem. Matched strings will have the same taint status ++as the subject string (this is not a de-taint method, and must not be made so ++given the support for wildcards in REs). + + Arguments: + re the compiled expression + subject the subject string + options additional PCRE options +@@ -130,10 +132,15 @@ if ((yield = (res >= 0))) + PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); + res = pcre2_get_ovector_count(md); + expand_nmax = setup < 0 ? 0 : setup + 1; + for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) + { ++ /* Although PCRE2 has a pcre2_substring_get_bynumber() conveneience, it ++ seems to return a bad pointer when a capture group had no data, eg. (.*) ++ matching zero letters. So use the underlying ovec and hope (!) that the ++ offsets are sane (including that case). Should we go further and range- ++ check each one vs. the subject string length? */ + int off = matchnum * 2; + int len = ovec[off + 1] - ovec[off]; + expand_nstring[expand_nmax] = string_copyn(subject + ovec[off], len); + expand_nlength[expand_nmax++] = len; + } +-- +2.35.1 + diff --git a/mail/exim/files/debian/75_37-OpenSSL-when-preloading-creds-do-the-server-certs-be.patch b/mail/exim/files/debian/75_37-OpenSSL-when-preloading-creds-do-the-server-certs-be.patch new file mode 100644 index 000000000000..2e21065fb1d6 --- /dev/null +++ b/mail/exim/files/debian/75_37-OpenSSL-when-preloading-creds-do-the-server-certs-be.patch @@ -0,0 +1,232 @@ +From 7f65a63b60c6ea86db683ac00e221939f3bb1d47 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Tue, 25 Oct 2022 21:26:30 +0100 +Subject: [PATCH 1/2] OpenSSL: when preloading creds do the server certs before + the OCSP proofs so that the latter can ve verified before loading + +--- + src/tls-openssl.c | 113 ++++++++++++++++++++++-------------------- + 1 file changed, 58 insertions(+), 55 deletions(-) + +diff --git a/src/tls-openssl.c b/src/tls-openssl.c +index 68ad6f15b..fdf0d92b2 100644 +--- a/src/tls-openssl.c ++++ b/src/tls-openssl.c +@@ -441,14 +441,16 @@ exim_openssl_state_st state_server = {.is_server = TRUE}; + static int + setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, + uschar ** errstr ); + + /* Callbacks */ + #ifndef DISABLE_OCSP + static int tls_server_stapling_cb(SSL *s, void *arg); ++static void x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk); ++static void x509_store_dump_cert_s_names(X509_STORE * store); + #endif + + + + /* Daemon-called, before every connection, key create/rotate */ + #ifndef DISABLE_TLS_RESUME + static void tk_init(void); +@@ -1307,15 +1309,14 @@ ocsp_load_response(exim_openssl_state_st * state, const uschar * filename, + { + BIO * bio; + OCSP_RESPONSE * resp; + OCSP_BASICRESP * basic_response; + OCSP_SINGLERESP * single_response; + ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd; + STACK_OF(X509) * sk; +-unsigned long verify_flags; + int status, reason, i; + + DEBUG(D_tls) + debug_printf("tls_ocsp_file (%s) '%s'\n", is_pem ? "PEM" : "DER", filename); + + if (!filename || !*filename) return; + +@@ -1372,28 +1373,28 @@ if ((status = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL) + if (!(basic_response = OCSP_response_get1_basic(resp))) + { + DEBUG(D_tls) + debug_printf("OCSP response parse error: unable to extract basic response.\n"); + goto bad; + } + +-sk = state->verify_stack; +-verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */ ++sk = state->verify_stack; /* set by setup_certs() / chain_from_pem_file() */ + + /* May need to expose ability to adjust those flags? + OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT + OCSP_TRUSTOTHER OCSP_NOINTERN */ + +-/* This does a full verify on the OCSP proof before we load it for serving +-up; possibly overkill - just date-checks might be nice enough. ++/* This does a partial verify (only the signer link, not the whole chain-to-CA) ++on the OCSP proof before we load it for serving up; possibly overkill - ++just date-checks might be nice enough. + + OCSP_basic_verify takes a "store" arg, but does not +-use it for the chain verification, which is all we do +-when OCSP_NOVERIFY is set. The content from the wire +-"basic_response" and a cert-stack "sk" are all that is used. ++use it for the chain verification, when OCSP_NOVERIFY is set. ++The content from the wire "basic_response" and a cert-stack "sk" are all ++that is used. + + We have a stack, loaded in setup_certs() if tls_verify_certificates + was a file (not a directory, or "system"). It is unfortunate we + cannot used the connection context store, as that would neatly + handle the "system" case too, but there seems to be no library + function for getting a stack from a store. + [ In OpenSSL 1.1 - ? X509_STORE_CTX_get0_chain(ctx) ? ] +@@ -1402,15 +1403,15 @@ SNI handling. + + Separately we might try to replace using OCSP_basic_verify() - which seems to not + be a public interface into the OpenSSL library (there's no manual entry) - + But what with? We also use OCSP_basic_verify in the client stapling callback. + And there we NEED it; we must verify that status... unless the + library does it for us anyway? */ + +-if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0) ++if ((i = OCSP_basic_verify(basic_response, sk, NULL, OCSP_NOVERIFY)) < 0) + { + DEBUG(D_tls) + { + ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring)); + debug_printf("OCSP response verify failure: %s\n", US ssl_errstring); + } + goto bad; +@@ -1747,61 +1748,18 @@ if (opt_unset_or_noexpand(tls_eccurve)) + if (init_ecdh(ctx, &dummy_errstr)) + state_server.lib_state.ecdh = TRUE; + } + else + DEBUG(D_tls) debug_printf("TLS: not preloading ECDH curve for server\n"); + + #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) +-/* If we can, preload the server-side cert, key and ocsp */ +- +-if ( opt_set_and_noexpand(tls_certificate) +-# ifndef DISABLE_OCSP +- && opt_unset_or_noexpand(tls_ocsp_file) +-#endif +- && opt_unset_or_noexpand(tls_privatekey)) +- { +- /* Set watches on the filenames. The implementation does de-duplication +- so we can just blindly do them all. */ +- +- if ( tls_set_watch(tls_certificate, TRUE) +-# ifndef DISABLE_OCSP +- && tls_set_watch(tls_ocsp_file, TRUE) +-#endif +- && tls_set_watch(tls_privatekey, TRUE)) +- { +- state_server.certificate = tls_certificate; +- state_server.privatekey = tls_privatekey; +-#ifndef DISABLE_OCSP +- state_server.u_ocsp.server.file = tls_ocsp_file; +-#endif +- +- DEBUG(D_tls) debug_printf("TLS: preloading server certs\n"); +- if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) +- state_server.lib_state.conn_certs = TRUE; +- } +- } +-else if ( !tls_certificate && !tls_privatekey +-# ifndef DISABLE_OCSP +- && !tls_ocsp_file +-#endif +- ) +- { /* Generate & preload a selfsigned cert. No files to watch. */ +- if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) +- { +- state_server.lib_state.conn_certs = TRUE; +- lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */ +- } +- } +-else +- DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n"); +- +- + /* If we can, preload the Authorities for checking client certs against. + Actual choice to do verify is made (tls_{,try_}verify_hosts) +-at TLS conn startup */ ++at TLS conn startup. ++Do this before the server ocsp so that its info can verify the ocsp. */ + + if ( opt_set_and_noexpand(tls_verify_certificates) + && opt_unset_or_noexpand(tls_crl)) + { + /* Watch the default dir also as they are always included */ + + if ( tls_set_watch(CUS X509_get_default_cert_file(), FALSE) +@@ -1809,18 +1767,63 @@ if ( opt_set_and_noexpand(tls_verify_certificates) + && tls_set_watch(tls_crl, FALSE)) + { + DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n"); + + if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr) + == OK) + state_server.lib_state.cabundle = TRUE; +- } ++ ++ /* If we can, preload the server-side cert, key and ocsp */ ++ ++ if ( opt_set_and_noexpand(tls_certificate) ++# ifndef DISABLE_OCSP ++ && opt_unset_or_noexpand(tls_ocsp_file) ++# endif ++ && opt_unset_or_noexpand(tls_privatekey)) ++ { ++ /* Set watches on the filenames. The implementation does de-duplication ++ so we can just blindly do them all. */ ++ ++ if ( tls_set_watch(tls_certificate, TRUE) ++# ifndef DISABLE_OCSP ++ && tls_set_watch(tls_ocsp_file, TRUE) ++# endif ++ && tls_set_watch(tls_privatekey, TRUE)) ++ { ++ state_server.certificate = tls_certificate; ++ state_server.privatekey = tls_privatekey; ++#ifndef DISABLE_OCSP ++ state_server.u_ocsp.server.file = tls_ocsp_file; ++# endif ++ ++ DEBUG(D_tls) debug_printf("TLS: preloading server certs\n"); ++ if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) ++ state_server.lib_state.conn_certs = TRUE; ++ } ++ } ++ else if ( !tls_certificate && !tls_privatekey ++# ifndef DISABLE_OCSP ++ && !tls_ocsp_file ++# endif ++ ) ++ { /* Generate & preload a selfsigned cert. No files to watch. */ ++ if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) ++ { ++ state_server.lib_state.conn_certs = TRUE; ++ lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */ ++ } ++ } ++ else ++ DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n"); ++ } + } + else + DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n"); ++ ++ + #endif /* EXIM_HAVE_INOTIFY */ + + + /* If we can, preload the ciphers control string */ + + if (opt_set_and_noexpand(tls_require_ciphers)) + { +-- +2.35.1 + diff --git a/mail/exim/files/debian/75_38-OpenSSL-fix-double-expansion-of-tls_verify_certifica.patch b/mail/exim/files/debian/75_38-OpenSSL-fix-double-expansion-of-tls_verify_certifica.patch new file mode 100644 index 000000000000..09e4f11ef20e --- /dev/null +++ b/mail/exim/files/debian/75_38-OpenSSL-fix-double-expansion-of-tls_verify_certifica.patch @@ -0,0 +1,217 @@ +From 62b97c2ecf148ee86053d82e5509e4c3a5a20054 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Sat, 29 Oct 2022 22:33:43 +0100 +Subject: [PATCH 2/2] OpenSSL: fix double-expansion of tls_verify_certificates + +--- + src/tls-openssl.c | 66 +++++++++++++++++++++---------------------- + 1 file changed, 33 insertions(+), 33 deletions(-) + +diff --git a/src/tls-openssl.c b/src/tls-openssl.c +index fdf0d92b2..2e09882d2 100644 +--- a/src/tls-openssl.c ++++ b/src/tls-openssl.c +@@ -435,15 +435,15 @@ typedef struct exim_openssl_state { + /* should figure out a cleanup of API to handle state preserved per + implementation, for various reasons, which can be void * in the APIs. + For now, we hack around it. */ + exim_openssl_state_st *client_static_state = NULL; /*XXX should not use static; multiple concurrent clients! */ + exim_openssl_state_st state_server = {.is_server = TRUE}; + + static int +-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, ++setup_certs(SSL_CTX * sctx, uschar ** certs, uschar * crl, host_item * host, + uschar ** errstr ); + + /* Callbacks */ + #ifndef DISABLE_OCSP + static int tls_server_stapling_cb(SSL *s, void *arg); + static void x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk); + static void x509_store_dump_cert_s_names(X509_STORE * store); +@@ -1762,18 +1762,18 @@ if ( opt_set_and_noexpand(tls_verify_certificates) + { + /* Watch the default dir also as they are always included */ + + if ( tls_set_watch(CUS X509_get_default_cert_file(), FALSE) + && tls_set_watch(tls_verify_certificates, FALSE) + && tls_set_watch(tls_crl, FALSE)) + { ++ uschar * v_certs = tls_verify_certificates; + DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n"); + +- if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr) +- == OK) ++ if (setup_certs(ctx, &v_certs, tls_crl, NULL, &dummy_errstr) == OK) + state_server.lib_state.cabundle = TRUE; + + /* If we can, preload the server-side cert, key and ocsp */ + + if ( opt_set_and_noexpand(tls_certificate) + # ifndef DISABLE_OCSP + && opt_unset_or_noexpand(tls_ocsp_file) +@@ -1897,18 +1897,19 @@ if ( opt_set_and_noexpand(ob->tls_verify_certificates) + { + if ( !watch + || tls_set_watch(CUS X509_get_default_cert_file(), FALSE) + && tls_set_watch(ob->tls_verify_certificates, FALSE) + && tls_set_watch(ob->tls_crl, FALSE) + ) + { ++ uschar * v_certs = ob->tls_verify_certificates; + DEBUG(D_tls) + debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name); + +- if (setup_certs(ctx, ob->tls_verify_certificates, ++ if (setup_certs(ctx, &v_certs, + ob->tls_crl, dummy_host, &dummy_errstr) == OK) + ob->tls_preload.cabundle = TRUE; + } + } + else + DEBUG(D_tls) + debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", t->name); +@@ -2238,22 +2239,20 @@ if (state->u_ocsp.server.file) + { + SSL_CTX_set_tlsext_status_cb(server_sni, tls_server_stapling_cb); + SSL_CTX_set_tlsext_status_arg(server_sni, state); + } + #endif + + { +- uschar * expcerts; +- if ( !expand_check(tls_verify_certificates, US"tls_verify_certificates", +- &expcerts, &dummy_errstr) +- || (rc = setup_certs(server_sni, expcerts, tls_crl, NULL, ++ uschar * v_certs = tls_verify_certificates; ++ if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL, + &dummy_errstr)) != OK) + goto bad; + +- if (expcerts && *expcerts) ++ if (v_certs && *v_certs) + setup_cert_verify(server_sni, FALSE, verify_callback_server); + } + + /* do this after setup_certs, because this can require the certs for verifying + OCSP information. */ + if ((rc = tls_expand_session_files(server_sni, state, &dummy_errstr)) != OK) + goto bad; +@@ -3017,32 +3016,33 @@ return TRUE; + + + /* Called by both client and server startup; on the server possibly + repeated after a Server Name Indication. + + Arguments: + sctx SSL_CTX* to initialise +- certs certs file, expanded ++ certs certs file, returned expanded + crl CRL file or NULL + host NULL in a server; the remote host in a client + errstr error string pointer + + Returns: OK/DEFER/FAIL + */ + + static int +-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, ++setup_certs(SSL_CTX * sctx, uschar ** certsp, uschar * crl, host_item * host, + uschar ** errstr) + { +-uschar *expcerts, *expcrl; ++uschar * expcerts, * expcrl; + +-if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr)) ++if (!expand_check(*certsp, US"tls_verify_certificates", &expcerts, errstr)) + return DEFER; + DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); + ++*certsp = expcerts; + if (expcerts && *expcerts) + { + /* Tell the library to use its compiled-in location for the system default + CA bundle. Then add the ones specified in the config, if any. */ + + if (!SSL_CTX_set_default_verify_paths(sctx)) + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL, errstr); +@@ -3330,28 +3330,28 @@ if (verify_check_host(&tls_verify_hosts) == OK) + server_verify_optional = FALSE; + else if (verify_check_host(&tls_try_verify_hosts) == OK) + server_verify_optional = TRUE; + else + goto skip_certs; + + { +- uschar * expcerts; +- if (!expand_check(tls_verify_certificates, US"tls_verify_certificates", +- &expcerts, errstr)) +- return DEFER; +- DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); ++ uschar * v_certs = tls_verify_certificates; + + if (state_server.lib_state.cabundle) +- { DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); } ++ { ++ DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); ++ setup_cert_verify(ctx, server_verify_optional, verify_callback_server); ++ } + else +- if ((rc = setup_certs(ctx, expcerts, tls_crl, NULL, errstr)) != OK) ++ { ++ if ((rc = setup_certs(ctx, &v_certs, tls_crl, NULL, errstr)) != OK) + return rc; +- +- if (expcerts && *expcerts) +- setup_cert_verify(ctx, server_verify_optional, verify_callback_server); ++ if (v_certs && *v_certs) ++ setup_cert_verify(ctx, server_verify_optional, verify_callback_server); ++ } + } + skip_certs: ; + + #ifndef DISABLE_TLS_RESUME + # if OPENSSL_VERSION_NUMBER < 0x30000000L + SSL_CTX_set_tlsext_ticket_key_cb(ctx, ticket_key_callback); + /* despite working, appears to always return failure, so ignoring */ +@@ -3606,28 +3606,28 @@ if ( ( ( !ob->tls_verify_hosts || !ob->tls_verify_hosts + client_verify_optional = FALSE; + else if (verify_check_given_host(CUSS &ob->tls_try_verify_hosts, host) == OK) + client_verify_optional = TRUE; + else + return OK; + + { +- uschar * expcerts; +- if (!expand_check(ob->tls_verify_certificates, US"tls_verify_certificates", +- &expcerts, errstr)) +- return DEFER; +- DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); ++ uschar * v_certs = ob->tls_verify_certificates; + + if (state->lib_state.cabundle) +- { DEBUG(D_tls) debug_printf("TLS: CA bundle was preloaded\n"); } ++ { ++ DEBUG(D_tls) debug_printf("TLS: CA bundle for tpt was preloaded\n"); ++ setup_cert_verify(ctx, client_verify_optional, verify_callback_client); ++ } + else +- if ((rc = setup_certs(ctx, expcerts, ob->tls_crl, host, errstr)) != OK) ++ { ++ if ((rc = setup_certs(ctx, &v_certs, ob->tls_crl, host, errstr)) != OK) + return rc; +- +- if (expcerts && *expcerts) +- setup_cert_verify(ctx, client_verify_optional, verify_callback_client); ++ if (v_certs && *v_certs) ++ setup_cert_verify(ctx, client_verify_optional, verify_callback_client); ++ } + } + + if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK) + { + state->verify_cert_hostnames = + #ifdef SUPPORT_I18N + string_domain_utf8_to_alabel(host->certname, NULL); +-- +2.35.1 + diff --git a/mail/exim/files/debian/75_50-Fix-logging-of-max-size-log-line.patch b/mail/exim/files/debian/75_50-Fix-logging-of-max-size-log-line.patch new file mode 100644 index 000000000000..5992faca6eac --- /dev/null +++ b/mail/exim/files/debian/75_50-Fix-logging-of-max-size-log-line.patch @@ -0,0 +1,82 @@ +From 1ed24e36e279c922d3366f6c3144570cc5f54d7a Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Mon, 19 Dec 2022 21:09:17 +0000 +Subject: [PATCH] Fix logging of max-size log line + +Broken-by: d12746bc15d8 +--- + doc/ChangeLog | 5 +++++ + src/log.c | 7 ++++--- + test/confs/0633 | 21 ++++++++++++++++++++ + test/scripts/0000-Basic/0633 | 9 +++++++++ + test/stderr/0633 | 38 ++++++++++++++++++++++++++++++++++++ + test/stdout/0633 | 15 ++++++++++++++ + 6 files changed, 92 insertions(+), 3 deletions(-) + create mode 100644 test/confs/0633 + create mode 100644 test/scripts/0000-Basic/0633 + create mode 100644 test/stderr/0633 + create mode 100644 test/stdout/0633 + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -28,10 +28,15 @@ JH/13 Bug 2929: Fix using $recipients af + JH/14 Bug 2933: Fix regex substring match variables for null matches. Since 4.96 + a capture group which obtained no text (eg. "(abc)*" matching zero + occurrences) could cause a segfault if the corresponding $ was + expanded. + ++JH/18 Fix a fencepost error in logging. Previously (since 4.92) when a log line ++ was exactly sized compared to the log buffer, a crash occurred with the ++ misleading message "bad memory reference; pool not found". ++ Found and traced by Jasen Betts. ++ + + + Exim version 4.96 + ----------------- + +--- a/src/log.c ++++ b/src/log.c +@@ -803,11 +803,11 @@ Returns: nothing + void + log_write(unsigned int selector, int flags, const char *format, ...) + { + int paniclogfd; + ssize_t written_len; +-gstring gs = { .size = LOG_BUFFER_SIZE-1, .ptr = 0, .s = log_buffer }; ++gstring gs = { .size = LOG_BUFFER_SIZE-2, .ptr = 0, .s = log_buffer }; + gstring * g; + va_list ap; + + /* If panic_recurseflag is set, we have failed to open the panic log. This is + the ultimate disaster. First try to write the message to a debug file and/or +@@ -949,15 +949,14 @@ DEBUG(D_any|D_v) + g->ptr = i; + g = string_cat(g, US"**** log string overflowed log buffer ****"); + } + va_end(ap); + +- g->size = LOG_BUFFER_SIZE; + g = string_catn(g, US"\n", 1); + debug_printf("%s", string_from_gstring(g)); + +- gs.size = LOG_BUFFER_SIZE-1; /* Having used the buffer for debug output, */ ++ gs.size = LOG_BUFFER_SIZE-2; /* Having used the buffer for debug output, */ + gs.ptr = 0; /* reset it for the real use. */ + gs.s = log_buffer; + } + /* If no log file is specified, we are in a mess. */ + +@@ -1035,10 +1034,12 @@ if ( flags & LOG_RECIPIENTS + if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break; + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s); + } + } + ++/* actual size, now we are placing the newline (and space for NUL) */ ++gs.size = LOG_BUFFER_SIZE; + g = string_catn(g, US"\n", 1); + string_from_gstring(g); + + /* Handle loggable errors when running a utility, or when address testing. + Write to log_stderr unless debugging (when it will already have been written), diff --git a/mail/exim/files/debian/75_55-Fix-recursion-on-dns_again_means_nonexist.-Bug-2911.patch b/mail/exim/files/debian/75_55-Fix-recursion-on-dns_again_means_nonexist.-Bug-2911.patch new file mode 100644 index 000000000000..bbbfbe9ef1e7 --- /dev/null +++ b/mail/exim/files/debian/75_55-Fix-recursion-on-dns_again_means_nonexist.-Bug-2911.patch @@ -0,0 +1,76 @@ +From 1d38781da934809e6ce0b8c3718c4b3bccdfe1d2 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Wed, 28 Dec 2022 19:39:06 +0000 +Subject: [PATCH] Fix recursion on dns_again_means_nonexist. Bug 2911 + +--- + doc/ChangeLog | 8 +++++ + src/dns.c | 12 ++++++++ + test/confs/2202 | 18 +++++++++-- + test/scripts/2200-dnsdb/2202 | 8 +++++ + test/stderr/2202 | 58 +++++++++++++++++++++++++++++++++++- + test/stdout/2202 | 8 +++++ + 6 files changed, 108 insertions(+), 4 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -33,10 +33,18 @@ JH/14 Bug 2933: Fix regex substring matc + JH/18 Fix a fencepost error in logging. Previously (since 4.92) when a log line + was exactly sized compared to the log buffer, a crash occurred with the + misleading message "bad memory reference; pool not found". + Found and traced by Jasen Betts. + ++JH/19 Bug 2911: Fix a recursion in DNS lookups. Previously, if the main option ++ dns_again_means_nonexist included an element causing a DNS lookup which ++ iteslf returned DNS_AGAIN, unbounded recursion occurred. Possible results ++ included (though probably not limited to) a process crash from stack ++ memory limit, or from excessive open files. Replace this with a paniclog ++ whine (as this is likely a configuration error), and returning ++ DNS_NOMATCH. ++ + + + Exim version 4.96 + ----------------- + +--- a/src/dns.c ++++ b/src/dns.c +@@ -799,10 +799,11 @@ int + dns_basic_lookup(dns_answer * dnsa, const uschar * name, int type) + { + int rc; + #ifndef STAND_ALONE + const uschar * save_domain; ++static BOOL try_again_recursion = FALSE; + #endif + + /* DNS lookup failures of any kind are cached in a tree. This is mainly so that + a timeout on one domain doesn't happen time and time again for messages that + have many addresses in the same domain. We rely on the resolver and name server +@@ -903,15 +904,26 @@ if (dnsa->answerlen < 0) switch (h_errno + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", + name, dns_text_type(type)); + + /* Cut this out for various test programs */ + #ifndef STAND_ALONE ++ if (try_again_recursion) ++ { ++ log_write(0, LOG_MAIN|LOG_PANIC, ++ "dns_again_means_nonexist recursion seen for %s (assuming nonexist)", ++ name); ++ return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); ++ } ++ ++ try_again_recursion = TRUE; + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); + deliver_domain = save_domain; ++ try_again_recursion = FALSE; ++ + if (rc != OK) + { + DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); + return dns_fail_return(name, type, 0, DNS_AGAIN); + } diff --git a/mail/exim/files/debian/75_58-Close-server-smtp-socket-explicitly-on-connect-ACL-d.patch b/mail/exim/files/debian/75_58-Close-server-smtp-socket-explicitly-on-connect-ACL-d.patch new file mode 100644 index 000000000000..ddf54fa02af2 --- /dev/null +++ b/mail/exim/files/debian/75_58-Close-server-smtp-socket-explicitly-on-connect-ACL-d.patch @@ -0,0 +1,50 @@ +From 57d70161718e02927a22d6a3481803b72035ac46 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Sat, 31 Dec 2022 13:37:17 +0000 +Subject: [PATCH] Close server smtp socket explicitly on connect ACL "drop" + +--- + src/smtp_in.c | 13 ++++++++ + test/confs/0022 | 2 ++ + test/log/0022 | 2 ++ + test/rejectlog/0022 | 3 ++ + test/scripts/0000-Basic/0022 | 13 ++++++++ + test/stderr/0022 | 60 ++++++++++++++++++------------------ + test/stdout/0022 | 6 ++++ + 7 files changed, 69 insertions(+), 30 deletions(-) + create mode 100644 test/rejectlog/0022 + +diff --git a/src/smtp_in.c b/src/smtp_in.c +index 1cfcc0404..6880e3c09 100644 +--- a/src/smtp_in.c ++++ b/src/smtp_in.c +@@ -3563,10 +3563,23 @@ log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL", + /* Run the not-quit ACL, but without any custom messages. This should not be a + problem, because we get here only if some other ACL has issued "drop", and + in that case, *its* custom messages will have been used above. */ + + smtp_notquit_exit(US"acl-drop", NULL, NULL); ++ ++/* An overenthusiastic fail2ban/iptables implimentation has been seen to result ++in the TCP conn staying open, and retrying, despite this process exiting. A ++malicious client could possibly do the same, tying up server netowrking ++resources. Close the socket explicitly to try to avoid that (there's a note in ++the Linux socket(7) manpage, SO_LINGER para, to the effect that exim() without ++close() results in the socket always lingering). */ ++ ++(void) poll_one_fd(fileno(smtp_in), POLLIN, 200); ++DEBUG(D_any) debug_printf_indent("SMTP(close)>>\n"); ++(void) fclose(smtp_in); ++(void) fclose(smtp_out); ++ + return 2; + } + + + +diff --git a/test/rejectlog/0022 b/test/rejectlog/0022 +new file mode 100644 +index 000000000..68e21fff3 +-- +2.39.0 + diff --git a/mail/exim/files/debian/75_60-OpenSSL-fix-tls_eccurve-setting-explicit-curve-group.patch b/mail/exim/files/debian/75_60-OpenSSL-fix-tls_eccurve-setting-explicit-curve-group.patch new file mode 100644 index 000000000000..da05ba9476a8 --- /dev/null +++ b/mail/exim/files/debian/75_60-OpenSSL-fix-tls_eccurve-setting-explicit-curve-group.patch @@ -0,0 +1,184 @@ +From ca4014de81e6aa367aa0a54c49b4c3d4b137814c Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Sun, 1 Jan 2023 12:18:38 +0000 +Subject: [PATCH] OpenSSL: fix tls_eccurve setting explicit curve/group. Bug + 2954 + +--- + doc/ChangeLog | 4 +++ + src/tls-openssl.c | 39 ++++++++++++++---------- + test/confs/2148 | 54 ++++++++++++++++++++++++++++++++++ + test/confs/2149 | 39 +++++++++++++----------- + test/log/2148 | 48 ++++++++++++++++++++++++++++++ + test/log/2149 | 39 ++++++++++++------------ + test/paniclog/{2149 => 2148} | 0 + test/scripts/2100-OpenSSL/2148 | 50 +++++++++++++++++++++++++++++++ + test/scripts/2100-OpenSSL/2149 | 50 ++++++++++++++++--------------- + test/stderr/2148 | 5 ++++ + test/stderr/2149 | 3 -- + 11 files changed, 250 insertions(+), 81 deletions(-) + create mode 100644 test/confs/2148 + create mode 100644 test/log/2148 + rename test/paniclog/{2149 => 2148} (100%) + create mode 100644 test/scripts/2100-OpenSSL/2148 + create mode 100644 test/stderr/2148 + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -41,10 +41,14 @@ JH/19 Bug 2911: Fix a recursion in DNS l + included (though probably not limited to) a process crash from stack + memory limit, or from excessive open files. Replace this with a paniclog + whine (as this is likely a configuration error), and returning + DNS_NOMATCH. + ++JH/20 Bug 2954: (OpenSSL) Fix setting of explicit EC curve/group. Previously ++ this always failed, probably leading to the usual downgrade to in-clear ++ connections. ++ + + + Exim version 4.96 + ----------------- + +--- a/src/tls-openssl.c ++++ b/src/tls-openssl.c +@@ -657,16 +657,16 @@ if (dh_bitsize <= tls_dh_max_bits) + /* EVP_PKEY_free(pkey); crashes */ + #endif + } + else + DEBUG(D_tls) +- debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n", ++ debug_printf(" Diffie-Hellman initialized from %s with %d-bit prime\n", + dhexpanded ? dhexpanded : US"default", dh_bitsize); + } + else + DEBUG(D_tls) +- debug_printf("dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n", ++ debug_printf(" dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n", + dhexpanded ? dhexpanded : US"default", dh_bitsize, tls_dh_max_bits); + + #if OPENSSL_VERSION_NUMBER < 0x30000000L + DH_free(dh); + #endif +@@ -712,23 +712,31 @@ init_ecdh(SSL_CTX * sctx, uschar ** errs + #ifdef OPENSSL_NO_ECDH + return TRUE; + #else + + uschar * exp_curve; +-int nid; +-BOOL rv; ++int nid, rc; + + # ifndef EXIM_HAVE_ECDH + DEBUG(D_tls) +- debug_printf("No OpenSSL API to define ECDH parameters, skipping\n"); ++ debug_printf(" No OpenSSL API to define ECDH parameters, skipping\n"); + return TRUE; + # else + + if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr)) + return FALSE; ++ ++/* Is the option deliberately empty? */ ++ + if (!exp_curve || !*exp_curve) ++ { ++#if OPENSSL_VERSION_NUMBER >= 0x10002000L ++ DEBUG(D_tls) debug_printf( " ECDH OpenSSL 1.0.2+: clearing curves list\n"); ++ (void) SSL_CTX_set1_curves(sctx, &nid, 0); ++#endif + return TRUE; ++ } + + /* "auto" needs to be handled carefully. + * OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1 + * OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto + * (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO) +@@ -737,27 +745,26 @@ if (!exp_curve || !*exp_curve) + */ + if (Ustrcmp(exp_curve, "auto") == 0) + { + #if OPENSSL_VERSION_NUMBER < 0x10002000L + DEBUG(D_tls) debug_printf( +- "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); ++ " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); + exp_curve = US"prime256v1"; + #else + # if defined SSL_CTRL_SET_ECDH_AUTO + DEBUG(D_tls) debug_printf( +- "ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); ++ " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); + SSL_CTX_set_ecdh_auto(sctx, 1); + return TRUE; + # else + DEBUG(D_tls) debug_printf( +- "ECDH OpenSSL 1.1.0+: temp key parameter settings: default selection\n"); ++ " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n"); + return TRUE; + # endif + #endif + } + +-DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve); + if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef + # ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID + && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef + # endif + ) +@@ -777,27 +784,27 @@ if ( (nid = OBJ_sn2nid (CCS exp_c + } + + /* The "tmp" in the name here refers to setting a temporary key + not to the stability of the interface. */ + +- if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) ++ if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) + tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), NULL, NULL, errstr); + else +- DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve); ++ DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve); + EC_KEY_free(ecdh); + } + + #else /* v 3.0.0 + */ + +-if ((rv = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0) ++if ((rc = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0) + tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr); + else +- DEBUG(D_tls) debug_printf("ECDH: enabled '%s' group\n", exp_curve); ++ DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group\n", exp_curve); + + #endif + +-return !rv; ++return !!rc; + + # endif /*EXIM_HAVE_ECDH*/ + #endif /*OPENSSL_NO_ECDH*/ + } + +@@ -1719,19 +1726,19 @@ state_server.lib_state.lib_ctx = ctx; + + /* Preload DH params and EC curve */ + + if (opt_unset_or_noexpand(tls_dhparam)) + { +- DEBUG(D_tls) debug_printf("TLS: preloading DH params for server\n"); ++ DEBUG(D_tls) debug_printf("TLS: preloading DH params '%s' for server\n", tls_dhparam); + if (init_dh(ctx, tls_dhparam, &dummy_errstr)) + state_server.lib_state.dh = TRUE; + } + else + DEBUG(D_tls) debug_printf("TLS: not preloading DH params for server\n"); + if (opt_unset_or_noexpand(tls_eccurve)) + { +- DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for server\n"); ++ DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve '%s' for server\n", tls_eccurve); + if (init_ecdh(ctx, &dummy_errstr)) + state_server.lib_state.ecdh = TRUE; + } + else + DEBUG(D_tls) debug_printf("TLS: not preloading ECDH curve for server\n"); diff --git a/mail/exim/files/debian/75_62-OpenSSL-Fix-tls_eccurve-on-earlier-versions-than-3.0.patch b/mail/exim/files/debian/75_62-OpenSSL-Fix-tls_eccurve-on-earlier-versions-than-3.0.patch new file mode 100644 index 000000000000..e346df17abdb --- /dev/null +++ b/mail/exim/files/debian/75_62-OpenSSL-Fix-tls_eccurve-on-earlier-versions-than-3.0.patch @@ -0,0 +1,42 @@ +From 7fa5764c203f2f4a900898a79ed02d674075313f Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Mon, 2 Jan 2023 15:04:14 +0000 +Subject: [PATCH 1/3] OpenSSL: Fix tls_eccurve on earlier versions than 3.0.0. + Bug 2954 + +Broken-by: ca4014de81e6 +--- + src/tls-openssl.c | 7 ++++--- + test/log/2149 | 28 ++++++++++++++-------------- + test/runtest | 3 +++ + test/scripts/2100-OpenSSL/2149 | 22 ++++++++++++---------- + 4 files changed, 33 insertions(+), 27 deletions(-) + +diff --git a/src/tls-openssl.c b/src/tls-openssl.c +index 4d0f99ea9..e063d29bd 100644 +--- a/src/tls-openssl.c ++++ b/src/tls-openssl.c +@@ -786,8 +786,9 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef + # endif + ) + { +- tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve), +- NULL, NULL, errstr); ++ uschar * s = string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve); ++ DEBUG(D_tls) debug_printf("TLS error '%s'\n", s); ++ if (errstr) *errstr = s; + return FALSE; + } + +@@ -803,7 +804,7 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef + /* The "tmp" in the name here refers to setting a temporary key + not to the stability of the interface. */ + +- if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) ++ if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh)) == 0) + tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), NULL, NULL, errstr); + else + DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve); +-- +2.39.0 + diff --git a/mail/exim/files/debian/75_63-OpenSSL-log-conns-rejected-for-bad-ALPN-with-the-off.patch b/mail/exim/files/debian/75_63-OpenSSL-log-conns-rejected-for-bad-ALPN-with-the-off.patch new file mode 100644 index 000000000000..15b1b8a8ff8d --- /dev/null +++ b/mail/exim/files/debian/75_63-OpenSSL-log-conns-rejected-for-bad-ALPN-with-the-off.patch @@ -0,0 +1,99 @@ +From e1aca33756f73c22b00a98d40ce2be8ed94464b1 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 5 Jan 2023 13:03:37 +0000 +Subject: [PATCH 2/3] OpenSSL: log conns rejected for bad ALPN, with the + offered value + +Unfortunately, no way to do this under GnuTLS +--- + src/match.c | 1 + + src/tls-gnu.c | 9 ++++++++- + src/tls-openssl.c | 13 +++++++++++-- + test/log/1190 | 2 ++ + test/runtest | 3 +++ + 5 files changed, 25 insertions(+), 3 deletions(-) + +diff --git a/src/match.c b/src/match.c +index 91a49c0f0..07070362d 100644 +--- a/src/match.c ++++ b/src/match.c +@@ -968,6 +968,7 @@ Arguments: + s string to search for + listptr ptr to ptr to colon separated list of patterns, or NULL + sep a separator value for the list (see string_nextinlist()) ++ or zero for auto + anchorptr ptr to tree for named items, or NULL if no named items + cache_bits ptr to cache_bits for ditto, or NULL if not caching + type MCL_DOMAIN when matching a domain list +diff --git a/src/tls-gnu.c b/src/tls-gnu.c +index 729fb5879..b47fabf1d 100644 +--- a/src/tls-gnu.c ++++ b/src/tls-gnu.c +@@ -1119,21 +1119,28 @@ switch (tls_id) + /* The format of "data" here doesn't seem to be documented, but appears + to be a 2-byte field with a (redundant, given the "size" arg) total length + then a sequence of one-byte size then string (not nul-term) names. The +- latter is as described in OpenSSL documentation. */ ++ latter is as described in OpenSSL documentation. ++ Note that we do not get called for a match_fail, making it hard to log ++ a single bad ALPN being offered (the common case). */ ++ { ++ gstring * g = NULL; + + DEBUG(D_tls) debug_printf("Seen ALPN extension from client (s=%u):", size); + for (const uschar * s = data+2; s-data < size-1; s += *s + 1) + { + server_seen_alpn++; ++ g = string_append_listele_n(g, ':', s+1, *s); + DEBUG(D_tls) debug_printf(" '%.*s'", (int)*s, s+1); + } + DEBUG(D_tls) debug_printf("\n"); + if (server_seen_alpn > 1) + { ++ log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); + DEBUG(D_tls) debug_printf("TLS: too many ALPNs presented in handshake\n"); + return GNUTLS_E_NO_APPLICATION_PROTOCOL; + } + break; ++ } + #endif + } + return 0; +diff --git a/src/tls-openssl.c b/src/tls-openssl.c +index e063d29bd..513ba0d3a 100644 +--- a/src/tls-openssl.c ++++ b/src/tls-openssl.c +@@ -2324,6 +2324,8 @@ static int + tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen, + const uschar * in, unsigned int inlen, void * arg) + { ++gstring * g = NULL; ++ + server_seen_alpn = TRUE; + DEBUG(D_tls) + { +@@ -2354,12 +2356,19 @@ if ( inlen > 1 /* at least one name */ + } + } + +-/* More than one name from clilent, or name did not match our list. */ ++/* More than one name from client, or name did not match our list. */ + + /* This will be fatal to the TLS conn; would be nice to kill TCP also. + Maybe as an option in future; for now leave control to the config (must-tls). */ + +-DEBUG(D_tls) debug_printf("TLS ALPN rejected\n"); ++for (int pos = 0, siz; pos < inlen; pos += siz+1) ++ { ++ siz = in[pos]; ++ if (pos + 1 + siz > inlen) siz = inlen - pos - 1; ++ g = string_append_listele_n(g, ':', in + pos + 1, siz); ++ } ++log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); ++gstring_release_unused(g); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + #endif /* EXIM_HAVE_ALPN */ +-- +2.39.0 + diff --git a/mail/exim/files/debian/75_64-DANE-do-not-check-dns_again_means_nonexist-for-TLSA-.patch b/mail/exim/files/debian/75_64-DANE-do-not-check-dns_again_means_nonexist-for-TLSA-.patch new file mode 100644 index 000000000000..417ffa4eed8c --- /dev/null +++ b/mail/exim/files/debian/75_64-DANE-do-not-check-dns_again_means_nonexist-for-TLSA-.patch @@ -0,0 +1,96 @@ +From 30520c8f87fcf660ed99a2344cae7f9787f7bc89 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 5 Jan 2023 18:39:51 +0000 +Subject: [PATCH 3/3] DANE: do not check dns_again_means_nonexist for TLSA + results of TRY_AGAIN + +--- + doc/doc-docbook/spec.xfpt | 7 ++++++- + doc/ChangeLog | 4 ++++ + src/dns.c | 35 ++++++++++++++++++++++------------- + 3 files changed, 32 insertions(+), 14 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -46,10 +46,14 @@ JH/19 Bug 2911: Fix a recursion in DNS l + JH/20 Bug 2954: (OpenSSL) Fix setting of explicit EC curve/group. Previously + this always failed, probably leading to the usual downgrade to in-clear + connections. + + ++JH/20 Fix TLSA lookups. Previously dns_again_means_nonexist would affect ++ SERVFAIL results, which breaks the downgrade resistance of DANE. Change ++ to not checking that list for these looks. ++ + + Exim version 4.96 + ----------------- + + JH/01 Move the wait-for-next-tick (needed for unique message IDs) from +--- a/src/dns.c ++++ b/src/dns.c +@@ -904,25 +904,34 @@ if (dnsa->answerlen < 0) switch (h_errno + DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) gave TRY_AGAIN\n", + name, dns_text_type(type)); + + /* Cut this out for various test programs */ + #ifndef STAND_ALONE +- if (try_again_recursion) ++ /* Permitting dns_again_means nonexist for TLSA lookups breaks the ++ doewngrade resistance of dane, so avoid for those. */ ++ ++ if (type == T_TLSA) ++ rc = FAIL; ++ else + { +- log_write(0, LOG_MAIN|LOG_PANIC, +- "dns_again_means_nonexist recursion seen for %s (assuming nonexist)", +- name); +- return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); +- } ++ if (try_again_recursion) ++ { ++ log_write(0, LOG_MAIN|LOG_PANIC, ++ "dns_again_means_nonexist recursion seen for %s" ++ " (assuming nonexist)", name); ++ return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), ++ DNS_NOMATCH); ++ } + +- try_again_recursion = TRUE; +- save_domain = deliver_domain; +- deliver_domain = string_copy(name); /* set $domain */ +- rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, +- &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); +- deliver_domain = save_domain; +- try_again_recursion = FALSE; ++ try_again_recursion = TRUE; ++ save_domain = deliver_domain; ++ deliver_domain = string_copy(name); /* set $domain */ ++ rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, ++ &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); ++ deliver_domain = save_domain; ++ try_again_recursion = FALSE; ++ } + + if (rc != OK) + { + DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); + return dns_fail_return(name, type, 0, DNS_AGAIN); +--- a/doc/spec.txt ++++ b/doc/spec.txt +@@ -14246,11 +14246,13 @@ dns_again_means_nonexist, it is treated + should be used with care. You can make it apply to reverse lookups by a setting + such as this: + + dns_again_means_nonexist = *.in-addr.arpa + +-This option applies to all DNS lookups that Exim does. It also applies when the ++This option applies to all DNS lookups that Exim does, except for TLSA lookups ++(where knowing about such failures +is security-relevant). It also applies ++when the + gethostbyname() or getipnodebyname() functions give temporary errors, since + these are most likely to be caused by DNS lookup problems. The dnslookup router + has some options of its own for controlling what happens when lookups for MX or + SRV records give temporary errors. These more specific options are applied + after this global option. diff --git a/mail/exim/files/debian/75_66-Fix-crash-in-expansions.patch b/mail/exim/files/debian/75_66-Fix-crash-in-expansions.patch new file mode 100644 index 000000000000..d776c8edf393 --- /dev/null +++ b/mail/exim/files/debian/75_66-Fix-crash-in-expansions.patch @@ -0,0 +1,84 @@ +From 70069b65a39a7ba73a36fbd95371ff03cde1eb23 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 2 Feb 2023 20:00:35 +0000 +Subject: [PATCH] Fix crash in expansions + +Broken-by: 1058096b8c53 +--- + doc/ChangeLog | 4 ++++ + src/expand.c | 9 +++++---- + test/stderr/0630 | 1 + + 3 files changed, 10 insertions(+), 4 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -50,10 +50,14 @@ JH/20 Bug 2954: (OpenSSL) Fix setting of + + JH/20 Fix TLSA lookups. Previously dns_again_means_nonexist would affect + SERVFAIL results, which breaks the downgrade resistance of DANE. Change + to not checking that list for these looks. + ++JH/23 Fix crash in string expansions. Previously, if an empty variable was ++ immediately followed by an expansion operator, a null-indirection read ++ was done, killing the process. ++ + + Exim version 4.96 + ----------------- + + JH/01 Move the wait-for-next-tick (needed for unique message IDs) from +--- a/src/expand.c ++++ b/src/expand.c +@@ -4652,11 +4652,11 @@ while (*s) + yield = string_catn(yield, value, len); + + continue; + } + +- if (isdigit(*s)) ++ if (isdigit(*s)) /* A $ variable */ + { + int n; + s = read_cnumber(&n, s); + if (n >= 0 && n <= expand_nmax) + yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); +@@ -7060,10 +7060,11 @@ NOT_ITEM: ; + if (arg) *arg++ = '_'; /* Put back for error messages */ + } + + /* Deal specially with operators that might take a certificate variable + as we do not want to do the usual expansion. For most, expand the string.*/ ++ + switch(c) + { + #ifndef DISABLE_TLS + case EOP_MD5: + case EOP_SHA1: +@@ -7107,11 +7108,11 @@ NOT_ITEM: ; + + /* Otherwise, switch on the operator type. After handling go back + to the main loop top. */ + + { +- int start = yield->ptr; ++ unsigned expansion_start = gstring_length(yield); + switch(c) + { + case EOP_BASE32: + { + uschar *t; +@@ -8168,12 +8169,12 @@ NOT_ITEM: ; + goto EXPAND_FAILED; + } /* EOP_* switch */ + + DEBUG(D_expand) + { +- const uschar * s = yield->s + start; +- int i = yield->ptr - start; ++ const uschar * s = yield->s + expansion_start; ++ int i = gstring_length(yield) - expansion_start; + BOOL tainted = is_tainted(s); + + DEBUG(D_noutf8) + { + debug_printf_indent("|-----op-res: %.*s\n", i, s);