diff options
Diffstat (limited to '')
-rw-r--r-- | debian/changelog | 11 | ||||
-rw-r--r-- | debian/patches/CVE-2023-51766.patch | 318 | ||||
-rw-r--r-- | debian/patches/series | 1 |
3 files changed, 330 insertions, 0 deletions
diff --git a/debian/changelog b/debian/changelog index 61d75f1..53ca9cb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +exim4 (4.92-8+deb10u9) buster-security; urgency=high + + * Non-maintainer upload by the LTS team. + * Fix CVE-2023-51766: + It was discovered that Exim, a mail transport agent, can be induced to + accept a second message embedded as part of the body of a first message in + certain configurations where PIPELINING or CHUNKING on incoming connections + is offered. + + -- Markus Koschany <apo@debian.org> Thu, 04 Jan 2024 21:07:16 +0100 + exim4 (4.92-8+deb10u8progress5u1) engywuck-security; urgency=high * Uploading to engywuck-security, remaining changes: diff --git a/debian/patches/CVE-2023-51766.patch b/debian/patches/CVE-2023-51766.patch new file mode 100644 index 0000000..4157fe5 --- /dev/null +++ b/debian/patches/CVE-2023-51766.patch @@ -0,0 +1,318 @@ +From: Markus Koschany <apo@debian.org> +Date: Wed, 27 Dec 2023 14:58:32 +0100 +Subject: CVE-2023-51766 + +Bug-Debian: https://bugs.debian.org/1059387 +Origin: https://git.exim.org/exim.git/commit/cf1376206284f2a4f11e32d931d4aade34c206c5 +Origin: https://git.exim.org/exim.git/commit/4596719398f6f2365bed563aafd757a6433ce7b4 +Origin: https://git.exim.org/exim.git/commit/5bb786d5ad568a88d50d15452aacc8404047e5ca +--- + doc/spec.txt | 7 ++- + src/receive.c | 184 ++++++++++++++++++++++++++++++++-------------------------- + src/smtp_in.c | 8 +-- + 3 files changed, 111 insertions(+), 88 deletions(-) + +diff --git a/doc/spec.txt b/doc/spec.txt +index 17e0452..330cd24 100644 +--- a/doc/spec.txt ++++ b/doc/spec.txt +@@ -32028,8 +32028,6 @@ that use CRLF in this circumstance. For this reason, and for compatibility with + other MTAs, the way Exim handles line endings for all messages is now as + follows: + +- * LF not preceded by CR is treated as a line ending. +- + * CR is treated as a line ending; if it is immediately followed by LF, the LF + is ignored. + +@@ -32044,7 +32042,10 @@ follows: + + * If the first header line received in a message ends with CRLF, a subsequent + bare LF in a header line is treated in the same way as a bare CR in a +- header line. ++ header line and a bare LF in a body line is replaced with a space. ++ ++ * If the first header line received in a message does not end with CRLF, a ++ subsequent LF not preceded by CR is treated as a line ending. + + + 47.3 Unqualified addresses +diff --git a/src/receive.c b/src/receive.c +index 227ace0..8870645 100644 +--- a/src/receive.c ++++ b/src/receive.c +@@ -777,100 +777,114 @@ July 2003: Bare CRs cause trouble. We now treat them as line terminators as + well, so that there are no CRs in spooled messages. However, the message + terminating dot is not recognized between two bare CRs. + ++Dec 2023: getting a site to send a body including an "LF . LF" sequence ++followed by SMTP commands is a possible "smtp smuggling" attack. If ++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 ++ 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) ++read_message_data_smtp(FILE * fout, BOOL strict_crlf) + { +-int ch_state = 0; +-int ch; +-int linelength = 0; ++enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state = ++ s_linestart; ++int linelength = 0, ch; + + while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) + { + if (ch == 0) body_zerocount++; + switch (ch_state) + { +- case 0: /* After LF or CRLF */ +- if (ch == '.') +- { +- ch_state = 3; +- continue; /* Don't ever write . after LF */ +- } +- ch_state = 1; ++ case s_linestart: /* After LF or CRLF */ ++ if (ch == '.') ++ { ++ ch_state = s_had_nl_dot; ++ continue; /* Don't ever write . after LF */ ++ } ++ ch_state = s_normal; + +- /* Else fall through to handle as normal uschar. */ ++ /* Else fall through to handle as normal uschar. */ + +- case 1: /* Normal state */ +- if (ch == '\n') +- { +- ch_state = 0; +- body_linecount++; ++ case s_normal: /* Normal state */ ++ if (ch == '\r') ++ { ++ ch_state = s_had_cr; ++ continue; /* Don't write the CR */ ++ } ++ if (ch == '\n') /* Bare LF at end of line */ ++ if (strict_crlf) ++ ch = ' '; /* replace LF with space */ ++ else ++ { /* treat as line ending */ ++ ch_state = s_linestart; ++ body_linecount++; ++ if (linelength > max_received_linelength) ++ max_received_linelength = linelength; ++ linelength = -1; ++ } ++ break; ++ ++ case s_had_cr: /* After (unwritten) CR */ ++ body_linecount++; /* Any char ends line */ + if (linelength > max_received_linelength) +- max_received_linelength = linelength; ++ max_received_linelength = linelength; + linelength = -1; +- } +- else if (ch == '\r') +- { +- ch_state = 2; +- continue; +- } +- break; ++ if (ch == '\n') /* proper CRLF */ ++ ch_state = s_linestart; ++ else ++ { ++ message_size++; /* convert the dropped CR to a stored NL */ ++ if (fout && fputc('\n', fout) == EOF) return END_WERROR; ++ cutthrough_data_put_nl(); ++ if (ch == '\r') /* CR; do not write */ ++ continue; ++ ch_state = s_normal; /* not LF or CR; process as standard */ ++ } ++ break; + +- case 2: /* After (unwritten) CR */ +- body_linecount++; +- if (linelength > max_received_linelength) +- max_received_linelength = linelength; +- linelength = -1; +- if (ch == '\n') +- { +- ch_state = 0; +- } +- else +- { +- message_size++; +- if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; +- cutthrough_data_put_nl(); +- if (ch != '\r') ch_state = 1; else continue; +- } +- break; ++ case s_had_nl_dot: /* After [CR] LF . */ ++ if (ch == '\n') /* [CR] LF . LF */ ++ if (strict_crlf) ++ ch = ' '; /* replace LF with space */ ++ else ++ return END_DOT; ++ else if (ch == '\r') /* [CR] LF . CR */ ++ { ++ ch_state = s_had_dot_cr; ++ continue; /* Don't write the CR */ ++ } ++ /* The dot was removed on reaching s_had_nl_dot. For a doubled dot, here, ++ reinstate it to cutthrough. The current ch, dot or not, is passed both to ++ cutthrough and to file below. */ ++ else if (ch == '.') ++ { ++ uschar c = ch; ++ cutthrough_data_puts(&c, 1); ++ } ++ ch_state = s_normal; ++ break; + +- case 3: /* After [CR] LF . */ +- if (ch == '\n') +- return END_DOT; +- if (ch == '\r') +- { +- ch_state = 4; +- continue; +- } +- /* The dot was removed at state 3. For a doubled dot, here, reinstate +- it to cutthrough. The current ch, dot or not, is passed both to cutthrough +- and to file below. */ +- if (ch == '.') +- { +- uschar c= ch; +- cutthrough_data_puts(&c, 1); +- } +- ch_state = 1; +- break; ++ case s_had_dot_cr: /* After [CR] LF . CR */ ++ if (ch == '\n') ++ return END_DOT; /* Preferred termination */ + +- case 4: /* After [CR] LF . CR */ +- if (ch == '\n') return END_DOT; +- message_size++; +- body_linecount++; +- if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; +- cutthrough_data_put_nl(); +- if (ch == '\r') +- { +- ch_state = 2; +- continue; +- } +- ch_state = 1; +- break; ++ message_size++; /* convert the dropped CR to a stored NL */ ++ body_linecount++; ++ if (fout && fputc('\n', fout) == EOF) return END_WERROR; ++ cutthrough_data_put_nl(); ++ if (ch == '\r') ++ { ++ ch_state = s_had_cr; ++ continue; /* CR; do not write */ ++ } ++ ch_state = s_normal; ++ break; + } + + /* Add the character to the spool file, unless skipping; then loop for the +@@ -1086,7 +1100,7 @@ receive_swallow_smtp(void) + { + if (message_ended >= END_NOTENDED) + message_ended = chunking_state <= CHUNKING_OFFERED +- ? read_message_data_smtp(NULL) ++ ? read_message_data_smtp(NULL, FALSE) + : read_message_bdat_smtp_wire(NULL); + } + +@@ -1865,8 +1879,10 @@ for (;;) + + if (ch == '\n') + { +- if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = FALSE; +- else if (first_line_ended_crlf) receive_ungetc(' '); ++ if (first_line_ended_crlf == TRUE_UNSET) ++ first_line_ended_crlf = FALSE; ++ else if (first_line_ended_crlf) ++ receive_ungetc(' '); + goto EOL; + } + +@@ -1881,8 +1897,13 @@ for (;;) + + if (ptr == 0 && ch == '.' && f.dot_ends) + { ++ /* leading dot while in headers-read mode */ + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); +- if (ch == '\r') ++ if (ch == '\n' && first_line_ended_crlf == TRUE /* and not TRUE_UNSET */ ) ++ /* dot, LF but we are in CRLF mode. Attack? */ ++ ch = ' '; /* replace the LF with a space */ ++ ++ else if (ch == '\r') + { + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); + if (ch != '\n') +@@ -1918,7 +1939,8 @@ for (;;) + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); + if (ch == '\n') + { +- if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE; ++ if (first_line_ended_crlf == TRUE_UNSET) ++ first_line_ended_crlf = TRUE; + goto EOL; + } + +@@ -3038,7 +3060,7 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.delivery) + + + /* Open a new spool file for the data portion of the message. We need +-to access it both via a file descriptor and a stream. Try to make the ++to access it both via a file descriptor and a stdio stream. Try to make the + directory if it isn't there. */ + + spool_name = spool_fname(US"input", message_subdir, message_id, US"-D"); +@@ -3107,7 +3129,7 @@ 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) ++ ? read_message_data_smtp(spool_data_file, first_line_ended_crlf) + : spool_wireformat + ? read_message_bdat_smtp_wire(spool_data_file) + : read_message_bdat_smtp(spool_data_file); +diff --git a/src/smtp_in.c b/src/smtp_in.c +index 76784c1..88a4f6b 100644 +--- a/src/smtp_in.c ++++ b/src/smtp_in.c +@@ -5392,12 +5392,12 @@ while (done <= 0) + } + + if (chunking_state > CHUNKING_OFFERED) +- rc = OK; /* No predata ACL or go-ahead output for BDAT */ ++ rc = OK; /* There is no predata ACL or go-ahead output for BDAT */ + else + { +- /* If there is an ACL, re-check the synchronization afterwards, since the +- ACL may have delayed. To handle cutthrough delivery enforce a dummy call +- to get the DATA command sent. */ ++ /* If there is a predata-ACL, re-check the synchronization afterwards, ++ since the ACL may have delayed. To handle cutthrough delivery enforce a ++ dummy call to get the DATA command sent. */ + + if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0) + rc = OK; diff --git a/debian/patches/series b/debian/patches/series index bab606e..9e714db 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -62,3 +62,4 @@ CVE-2022-37452.patch use-uschar-more-in-spa-authenticator.patch CVE-2023-42116.patch CVE-2023-42114.patch +CVE-2023-51766.patch |