diff options
Diffstat (limited to 'mail-mta/exim/files/exim-4.97-CVE-2023-51766.patch')
-rw-r--r-- | mail-mta/exim/files/exim-4.97-CVE-2023-51766.patch | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/mail-mta/exim/files/exim-4.97-CVE-2023-51766.patch b/mail-mta/exim/files/exim-4.97-CVE-2023-51766.patch new file mode 100644 index 000000000000..7eed4eb1855f --- /dev/null +++ b/mail-mta/exim/files/exim-4.97-CVE-2023-51766.patch @@ -0,0 +1,265 @@ +https://nvd.nist.gov/vuln/detail/CVE-2023-51766 + + +From cf1376206284f2a4f11e32d931d4aade34c206c5 Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Fri, 22 Dec 2023 23:57:05 +0000 +Subject: [PATCH] Reject "dot, LF" as ending data phase. Bug 3063 + +From 5bb786d5ad568a88d50d15452aacc8404047e5ca Mon Sep 17 00:00:00 2001 +From: Jeremy Harris <jgh146exb@wizmail.org> +Date: Sat, 23 Dec 2023 17:42:57 +0000 +Subject: [PATCH] Reject "dot, LF" as ending data phase (pt. 2). Bug 3063 + +reduced to source changes only for Gentoo + + + +diff --git a/src/src/receive.c b/src/src/receive.c +index e35400aec..c6f612832 100644 +--- a/src/src/receive.c ++++ b/src/src/receive.c +@@ -836,93 +842,101 @@ + */ + + 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 +@@ -1140,7 +1152,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); + } + +@@ -1960,8 +1960,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; + } + +@@ -1977,7 +1980,11 @@ for (;;) + if (f.dot_ends && ptr == 0 && ch == '.') + { + 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') +@@ -2013,7 +2020,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; + } + +@@ -3241,7 +3253,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/src/smtp_in.c b/src/src/smtp_in.c +index e19c86ff8..aeaffeb37 100644 +--- a/src/src/smtp_in.c ++++ b/src/src/smtp_in.c +@@ -5112,7 +5112,10 @@ while (done <= 0) + to get the DATA command sent. */ + + if (!acl_smtp_predata && cutthrough.cctx.sock < 0) ++ { ++ if (!check_sync()) goto SYNC_FAILURE; + rc = OK; ++ } + else + { + uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept"; |