summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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.patch265
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";