[exim-cvs] Reject "dot, LF" as ending data phase (pt. 2). B…

Αρχική Σελίδα
Delete this message
Reply to this message
Συντάκτης: Exim Git Commits Mailing List
Ημερομηνία:  
Προς: exim-cvs
Αντικείμενο: [exim-cvs] Reject "dot, LF" as ending data phase (pt. 2). Bug 3063
Gitweb: https://git.exim.org/exim.git/commitdiff/5bb786d5ad568a88d50d15452aacc8404047e5ca
Commit:     5bb786d5ad568a88d50d15452aacc8404047e5ca
Parent:     4596719398f6f2365bed563aafd757a6433ce7b4
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Sat Dec 23 17:42:57 2023 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Sat Dec 23 17:42:57 2023 +0000


    Reject "dot, LF" as ending data phase (pt. 2).  Bug 3063
---
 doc/doc-docbook/spec.xfpt |  7 ++++---
 src/src/receive.c         | 42 +++++++++++++++++++++++++++---------------
 2 files changed, 31 insertions(+), 18 deletions(-)


diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 7ac7f5bda..ae3cc1748 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -36461,8 +36461,6 @@ other MTAs, the way Exim handles line endings for all messages is now as
follows:

.ilist
-LF not preceded by CR is treated as a line ending.
-.next
CR is treated as a line ending; if it is immediately followed by LF, the LF
is ignored.
.next
@@ -36477,7 +36475,10 @@ people trying to play silly games.
.next
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.
+line and a bare LF in a body line is replaced with a space.
+.next
+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.
.endlist


diff --git a/src/src/receive.c b/src/src/receive.c
index e8bd80596..ae7045068 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -829,14 +829,20 @@ 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)
 {
 enum { s_linestart, s_normal, s_had_cr, s_had_nl_dot, s_had_dot_cr } ch_state =
           s_linestart;
@@ -863,14 +869,17 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
     ch_state = s_had_cr;
     continue;            /* Don't write the CR */
     }
-      if (ch == '\n')            /* Bare NL ends line */
-    {
-    ch_state = s_linestart;
-    body_linecount++;
-    if (linelength > max_received_linelength)
-      max_received_linelength = linelength;
-    linelength = -1;
-    }
+      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 */
@@ -893,8 +902,11 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)


     case s_had_nl_dot:            /* After [CR] LF . */
       if (ch == '\n')            /* [CR] LF . LF */
-    return END_DOT;
-      if (ch == '\r')            /* [CR] LF . CR */
+    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 */
@@ -902,7 +914,7 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
       /* 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. */
-      if (ch == '.')
+      else if (ch == '.')
     {
     uschar c = ch;
     cutthrough_data_puts(&c, 1);
@@ -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);
 }


@@ -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);


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/