From 5db00eeac41e74cb9749b6344258b38b1da8c700 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Tue, 14 Feb 2017 19:38:41 +0100 Subject: [PATCH] Fix missing line termination on the last received BDAT chunk (Bug 1974) (cherry picked from commit d953610fa77ec9a08fad9c1a183181079a79674e) Signed-off-by: Phil Pennock --- doc/doc-txt/ChangeLog | 4 ++++ src/src/receive.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 39e17b350..23f05ebe9 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -102,6 +102,10 @@ JH/17 Fix inbound CHUNKING when DKIM disabled at runtime. HS/01 Fix portability problems introduced by PP/08 for platforms where realloc(NULL) is not equivalent to malloc() [SunOS et al]. +HS/02 Bug 1974: Fix missing line terminator on the last received BDAT + chunk. This allows us to accept broken chunked messages. We need a more + general solution here. + Exim version 4.88 ----------------- diff --git a/src/src/receive.c b/src/src/receive.c index b152ceefb..5125a4f47 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -916,14 +916,41 @@ read_message_bdat_smtp(FILE *fout) { int linelength = 0, ch; enum CH_STATE ch_state = LF_SEEN; +BOOL fix_nl = FALSE; for(;;) { switch ((ch = (bdat_getc)(GETC_BUFFER_UNLIMITED))) { case EOF: return END_EOF; - case EOD: return END_DOT; /* normal exit */ case ERR: return END_PROTOCOL; + case EOD: + /* Nothing to get from the sender anymore. We check the last + character written to the spool. + + RFC 3030 states, that BDAT chunks are normal text, terminated by CRLF. + If we would be strict, we would refuse such broken messages. + But we are liberal, so we fix it. It would be easy just to append + the "\n" to the spool. + + But there are some more things (line counting, message size calculation and such), + that would need to be duplicated here. So we simply do some ungetc + trickery. + */ + fseek(fout, -1, SEEK_CUR); + if (fgetc(fout) == '\n') return END_DOT; + + if (linelength == -1) /* \r already seen (see below) */ + { + DEBUG(D_receive) debug_printf("Add missing LF\n"); + bdat_ungetc('\n'); + continue; + } + DEBUG(D_receive) debug_printf("Add missing CRLF\n"); + bdat_ungetc('\r'); /* not even \r was seen */ + fix_nl = TRUE; + + continue; case '\0': body_zerocount++; break; } switch (ch_state) @@ -944,6 +971,7 @@ for(;;) else if (ch == '\r') { ch_state = CR_SEEN; + if (fix_nl) bdat_ungetc('\n'); continue; /* don't write CR */ } break; -- 2.30.2