Version in base suite: 4.98.2-1+deb13u4 Base version: exim4_4.98.2-1+deb13u4 Target version: exim4_4.98.2-1+deb13u5 Base file: /srv/ftp-master.debian.org/ftp/pool/main/e/exim4/exim4_4.98.2-1+deb13u4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/e/exim4/exim4_4.98.2-1+deb13u5.dsc changelog | 20 + patches/85_0001-Proxy-protocol-Fix-OOB-read.patch | 95 +++++ patches/85_0002-Proxy-protocol-account-for-short-received-V2-header.patch | 68 ++++ patches/85_0004-Avoid-more-SMTP-smuggling-attack-types.patch | 169 ++++++++++ patches/series | 3 5 files changed, 355 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpfptie4jw/exim4_4.98.2-1+deb13u4.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpfptie4jw/exim4_4.98.2-1+deb13u5.dsc: no acceptable signature found diff -Nru exim4-4.98.2/debian/changelog exim4-4.98.2/debian/changelog --- exim4-4.98.2/debian/changelog 2026-07-23 17:04:03.000000000 +0000 +++ exim4-4.98.2/debian/changelog 2026-09-25 09:25:38.000000000 +0000 @@ -1,3 +1,23 @@ +exim4 (4.98.2-1+deb13u5) trixie-security; urgency=high + + * Cherry-pick relevant changes from security release 4.100.1. + CVE-2026-94054 EXIM-Security-2026-09-12.1 (GCVE-25-2026-09-50-1) + Exim before 4.100.1, when Proxy-Protocol is used with an attacker- + controlled proxy, has an out-of-bounds write. + CVE-2026-94056 EXIM-Security-2026-09-12.2 (GCVE-25-2026-09-55-1) + Exim before 4.100.1, when Proxy-Protocol is used with an attacker- + controlled proxy, allows attackers to read certain uninitialized + data from stack memory. + CVE-2026-94057 EXIM-Security-2026-09-12.4 (GCVE-25-2026-09-56-1) + Exim before 4.100.1 allows SMTP smuggling in which the received + message does not match any sent message, and instead depends on + crafted data sent after a rejection during DATA processing. + Closes: #1148506 + (CVE-2026-94055 is not relevant - TLS Early banner support was + introduced in 4.99.) + + -- Andreas Metzler Fri, 25 Sep 2026 11:25:38 +0200 + exim4 (4.98.2-1+deb13u4) trixie-security; urgency=high * Fix two local privilege escalation issues. diff -Nru exim4-4.98.2/debian/patches/85_0001-Proxy-protocol-Fix-OOB-read.patch exim4-4.98.2/debian/patches/85_0001-Proxy-protocol-Fix-OOB-read.patch --- exim4-4.98.2/debian/patches/85_0001-Proxy-protocol-Fix-OOB-read.patch 1970-01-01 00:00:00.000000000 +0000 +++ exim4-4.98.2/debian/patches/85_0001-Proxy-protocol-Fix-OOB-read.patch 2026-09-25 09:25:38.000000000 +0000 @@ -0,0 +1,95 @@ +From 8ead2b003af9225f712e85d246cd5544ef04ef91 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 27 Aug 2026 15:57:39 +0100 +Subject: [PATCH 1/4] Proxy-protocol: Fix OOB read + +--- + doc/ChangeLog | 9 +++++++++ + src/proxy.c | 17 ++++++----------- + 2 files changed, 15 insertions(+), 11 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -1,9 +1,12 @@ + 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/01 Proxy Protocol: fix an OOB read in V1 protocol header parsing ++ (GCVE-25-2026-09-50-1). ++ + JH/02 Do not expand a local_part gotten from a .forward file, under + force_command in a pipe transport. (GCVE-25-2026-07-45-3) + + JH/01 Restrict named-queue names. Previously, files could be corrupted by + poor choices of name. (GCVE-25-2026-07-45-1) +--- a/src/proxy.c ++++ b/src/proxy.c +@@ -378,24 +378,18 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, + goto proxyfail; + } + } + else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) + { +- uschar *p; +- uschar *end; +- uschar *sp; /* Utility variables follow */ +- int tmp_port; +- int r2; +- char *endc; ++ uschar * p, * end, * sp, * endc; /* Utility variables follow sp */ ++ int tmp_port, r2; + + /* get the rest of the line */ + r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); + if (r2 == -1) + goto proxyfail; +- ret += r2; +- +- p = string_copy(hdr.v1.line); ++ p = string_copyn(hdr.v1.line, ret = Ustrlen(hdr.v1.line)); + end = memchr(p, '\r', ret - 1); + + if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n') + { + DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n"); +@@ -425,12 +419,11 @@ else if (ret >= 8 && memcmp(hdr.v1.line, + else + { + DEBUG(D_receive) debug_printf("Invalid TCP type\n"); + goto proxyfail; + } +- +- p += Ustrlen(iptype); ++ p += Ustrlen(iptype); /* the field sizes happen to match our iptype strings */ + if (!isspace(*p++)) + { + DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n"); + goto proxyfail; + } +@@ -470,11 +463,11 @@ else if (ret >= 8 && memcmp(hdr.v1.line, + { + DEBUG(D_receive) debug_printf("Did not find proxied src port\n"); + goto proxyfail; + } + *sp = '\0'; +- tmp_port = strtol(CCS p, &endc, 10); ++ tmp_port = strtol(CCS p, CSS &endc, 10); + if (*endc || tmp_port == 0) + { + DEBUG(D_receive) + debug_printf("Proxied src port '%s' not an integer\n", p); + goto proxyfail; +@@ -485,11 +478,11 @@ else if (ret >= 8 && memcmp(hdr.v1.line, + if ((sp = Ustrchr(p, '\0')) == NULL) + { + DEBUG(D_receive) debug_printf("Did not find proxy dest port\n"); + goto proxyfail; + } +- tmp_port = strtol(CCS p, &endc, 10); ++ tmp_port = strtol(CCS p, CSS &endc, 10); + if (*endc || tmp_port == 0) + { + DEBUG(D_receive) + debug_printf("Proxy dest port '%s' not an integer\n", p); + goto proxyfail; diff -Nru exim4-4.98.2/debian/patches/85_0002-Proxy-protocol-account-for-short-received-V2-header.patch exim4-4.98.2/debian/patches/85_0002-Proxy-protocol-account-for-short-received-V2-header.patch --- exim4-4.98.2/debian/patches/85_0002-Proxy-protocol-account-for-short-received-V2-header.patch 1970-01-01 00:00:00.000000000 +0000 +++ exim4-4.98.2/debian/patches/85_0002-Proxy-protocol-account-for-short-received-V2-header.patch 2026-09-25 09:25:38.000000000 +0000 @@ -0,0 +1,68 @@ +From 2e3b687715f049bab7786c739a168b5842734d29 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Thu, 10 Sep 2026 14:52:02 +0100 +Subject: [PATCH 2/4] Proxy-protocol: account for short received V2 header + +--- + doc/ChangeLog | 5 +++++ + src/proxy.c | 17 ++++++++++------- + 2 files changed, 15 insertions(+), 7 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -3,10 +3,15 @@ affect Exim's operation, with an unchang + options, and new features, see the NewStuff file next to this ChangeLog. + + JH/01 Proxy Protocol: fix an OOB read in V1 protocol header parsing + (GCVE-25-2026-09-50-1). + ++JH/02 Proxy-protocol: account for incomplete V2 protocol header reception. ++ Previously uninitialised data was then used for the length field of the ++ header, giving possible data leakage to an attacker. This would require ++ a buggy or compromised proxy. (GCVE-25-2026-09-55-1). ++ + JH/02 Do not expand a local_part gotten from a .forward file, under + force_command in a pipe transport. (GCVE-25-2026-07-45-3) + + JH/01 Restrict named-queue names. Previously, files could be corrupted by + poor choices of name. (GCVE-25-2026-07-45-1) +--- a/src/proxy.c ++++ b/src/proxy.c +@@ -221,27 +221,30 @@ do + if (ret == -1) + goto proxyfail; + DEBUG(D_receive) proxy_debug(US &hdr, 0, ret); + + /* For v2, handle reading the length, and then the rest. */ +-if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) ++if (ret == PROXY_INITIAL_READ && memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0) + { + int retmore; + uint8_t ver; + + DEBUG(D_receive) debug_printf("v2\n"); + + /* First get the length fields. */ + do + { +- retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ); +- } while (retmore == -1 && errno == EINTR && !had_command_timeout); +- if (retmore == -1) +- goto proxyfail; +- DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); ++ do ++ { ++ retmore = read(fd, US &hdr + ret, PROXY_V2_HEADER_SIZE - ret); ++ } while (retmore == -1 && errno == EINTR && !had_command_timeout); ++ if (retmore <= 0) ++ goto proxyfail; ++ DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); + +- ret += retmore; ++ ret += retmore; ++ } while (ret < PROXY_V2_HEADER_SIZE); + + ver = (hdr.v2.ver_cmd & 0xf0) >> 4; + + /* May 2014: haproxy combined the version and command into one byte to + allow two full bytes for the length field in order to proxy SSL diff -Nru exim4-4.98.2/debian/patches/85_0004-Avoid-more-SMTP-smuggling-attack-types.patch exim4-4.98.2/debian/patches/85_0004-Avoid-more-SMTP-smuggling-attack-types.patch --- exim4-4.98.2/debian/patches/85_0004-Avoid-more-SMTP-smuggling-attack-types.patch 1970-01-01 00:00:00.000000000 +0000 +++ exim4-4.98.2/debian/patches/85_0004-Avoid-more-SMTP-smuggling-attack-types.patch 2026-09-25 09:25:38.000000000 +0000 @@ -0,0 +1,169 @@ +From fb7ccaa78db25a5816a3c4413d2e6caa44aaf822 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris +Date: Fri, 11 Sep 2026 12:53:24 +0100 +Subject: [PATCH 4/4] Avoid more "SMTP smuggling" attack types + +--- + doc/ChangeLog | 3 +++ + src/receive.c | 25 ++++++++++++------------- + 2 files changed, 15 insertions(+), 13 deletions(-) + +--- a/doc/ChangeLog ++++ b/doc/ChangeLog +@@ -8,10 +8,13 @@ JH/01 Proxy Protocol: fix an OOB read in + JH/02 Proxy-protocol: account for incomplete V2 protocol header reception. + Previously uninitialised data was then used for the length field of the + header, giving possible data leakage to an attacker. This would require + a buggy or compromised proxy. (GCVE-25-2026-09-55-1). + ++JH/04 Avoid more "SMTP smuggling" attack types. This extends the fixes for ++ CVE-2023-51766. (GCVE-25-2026-09-56-1). ++ + JH/02 Do not expand a local_part gotten from a .forward file, under + force_command in a pipe transport. (GCVE-25-2026-07-45-3) + + JH/01 Restrict named-queue names. Previously, files could be corrupted by + poor choices of name. (GCVE-25-2026-07-45-1) +--- a/src/receive.c ++++ b/src/receive.c +@@ -26,10 +26,11 @@ extern int dcc_ok; + + static int data_fd = -1; + static uschar *spool_name = US""; + + enum CH_STATE {LF_SEEN, MID_LINE, CR_SEEN}; ++static BOOL first_line_ended_crlf; + + #ifdef HAVE_LOCAL_SCAN + jmp_buf local_scan_env; /* error-handling context for local_scan */ + unsigned had_local_scan_crash; + unsigned had_local_scan_timeout; +@@ -833,17 +834,16 @@ followed by SMTP commands is a possible + the first (header) line for the message has a proper CRLF then enforce + that for the body: convert bare LF to a space. + + Arguments: + fout a FILE to which to write the message; NULL if skipping +- strict_crlf require full CRLF sequence as a line ending + + Returns: One of the END_xxx values indicating why it stopped reading + */ + + static int +-read_message_data_smtp(FILE * fout, BOOL strict_crlf) ++read_message_data_smtp(FILE * fout) + { + enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state = + s_linestart; + int linelength = 0, ch; + +@@ -867,11 +867,11 @@ while ((ch = (receive_getc)(GETC_BUFFER_ + { + ch_state = s_had_cr; + continue; /* Don't write the CR */ + } + if (ch == '\n') /* Bare LF at end of line */ +- if (strict_crlf) ++ if (first_line_ended_crlf) /* strict CRLF mode (normal case) */ + ch = ' '; /* replace LF with space */ + else + { /* treat as line ending */ + ch_state = s_linestart; + body_linecount++; +@@ -899,11 +899,11 @@ while ((ch = (receive_getc)(GETC_BUFFER_ + } + break; + + case s_had_nl_dot: /* After [CR] LF . */ + if (ch == '\n') /* [CR] LF . LF */ +- if (strict_crlf) ++ if (first_line_ended_crlf) /* strict CRLF mode (normal case) */ + ch = ' '; /* replace LF with space */ + else + return END_DOT; + else if (ch == '\r') /* [CR] LF . CR */ + { +@@ -1145,16 +1145,15 @@ tidily. + Argument: a FILE from which to read the message + Returns: nothing + */ + + void +-receive_swallow_smtp(void) ++receive_swallow_smtp() + { + if (message_ended >= END_NOTENDED) + message_ended = chunking_state <= CHUNKING_OFFERED +- ? read_message_data_smtp(NULL, FALSE) +- : read_message_bdat_smtp_wire(NULL); ++ ? read_message_data_smtp(NULL) : read_message_bdat_smtp_wire(NULL); + } + + + + /************************************************* +@@ -1717,11 +1716,10 @@ const int id_resolution = BASE_62 == 62 + + int ptr = 0; + + BOOL contains_resent_headers = FALSE; + BOOL extracted_ignored = FALSE; +-BOOL first_line_ended_crlf = TRUE_UNSET; + BOOL smtp_yield = TRUE; + BOOL yield = FALSE; + + BOOL resents_exist = FALSE; + uschar *resent_prefix = US""; +@@ -1909,11 +1907,11 @@ inside them, so that writing them out re + + Loop for each character of each header; the next structure for chaining the + header is set up already, with ptr the offset of the next character in + next->text. */ + +-for (;;) ++for (first_line_ended_crlf = TRUE_UNSET; ;) + { + int ch = (receive_getc)(GETC_BUFFER_UNLIMITED); + + /* If we hit EOF on a SMTP connection, it's an error, since incoming + SMTP must have a correct "." terminator. */ +@@ -3188,11 +3186,12 @@ Having created it, send the headers to t + if (cutthrough.cctx.sock >= 0 && cutthrough.delivery) + { + if (received_count > received_headers_max) + { + cancel_cutthrough_connection(TRUE, US"too many headers"); +- if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ ++ if (smtp_input) ++ receive_swallow_smtp(); + log_write(0, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " + "Too many \"Received\" headers", + sender_address, + sender_fullhost ? "H=" : "", sender_fullhost ? sender_fullhost : US"", + sender_ident ? "U=" : "", sender_ident ? sender_ident : US""); +@@ -3273,11 +3272,11 @@ message id or "next" line. */ + if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) + { + if (smtp_input) + { + message_ended = chunking_state <= CHUNKING_OFFERED +- ? read_message_data_smtp(spool_data_file, first_line_ended_crlf) ++ ? read_message_data_smtp(spool_data_file) + : spool_wireformat + ? read_message_bdat_smtp_wire(spool_data_file) + : read_message_bdat_smtp(spool_data_file); + receive_linecount++; /* The terminating "." line */ + } +@@ -3306,11 +3305,11 @@ if (!ferror(spool_data_file) && !(receiv + message; we want to see the ident value even for non-remote messages. */ + + case END_SIZE: + Uunlink(spool_name); /* Lose the data file when closed */ + cancel_cutthrough_connection(TRUE, US"mail too big"); +- if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ ++ if (smtp_input) receive_swallow_smtp(); + + log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " + "message too big: read=%d max=%d", + sender_address, + sender_fullhost ? " H=" : "", diff -Nru exim4-4.98.2/debian/patches/series exim4-4.98.2/debian/patches/series --- exim4-4.98.2/debian/patches/series 2026-07-22 17:04:38.000000000 +0000 +++ exim4-4.98.2/debian/patches/series 2026-09-25 09:25:38.000000000 +0000 @@ -22,4 +22,7 @@ 83-Security-fix-PROXYv2-uninitialised-stack-disclosure-.patch 84_01-Restrict-names-permitted-for-named-queues.patch 84_02-Do-not-expand-local_part-in-a-pipe-transport-under-f.patch +85_0001-Proxy-protocol-Fix-OOB-read.patch +85_0002-Proxy-protocol-account-for-short-received-V2-header.patch +85_0004-Avoid-more-SMTP-smuggling-attack-types.patch 90_localscan_dlopen.dpatch