Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: sendmail@packages.debian.org
Control: affects -1 + src:sendmail
User: release.debian.org@packages.debian.org
Usertags: pu
[ Reason ]
sendmail was affected by CVE-2023-51765
[ Impact ]
close CVE-2023-51765 and reject NUL mail
[ Tests ]
CVE-2023-51765 fix was tested manually and cross checked
[ Risks ]
Code is complex and rejecting NUL is slighly RFC non conformant
[ Checklist ]
[X] *all* changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in (old)stable
[X] the issue is verified as fixed in unstable
[ Changes ]
Fix CVE-2023-51765 (Closes: #1059386):
sendmail allowed SMTP smuggling in certain configurations.
Remote attackers can use a published exploitation
technique to inject e-mail messages with a spoofed
MAIL FROM address, allowing bypass of an SPF protection
mechanism. This occurs because sendmail supports
<LF>.<CR><LF> but some other popular e-mail servers
do not. This is resolved with 'o' in srv_features.
* Enable _FFR_REJECT_NUL_BYTE for rejecting mail that
include NUL byte
* By default enable rejecting mail that include NUL byte.
set confREJECT_NUL to 'true' by default .
User could disable by setting confREJECT_NUL to false.
(Closes: #1070190). Close a variant of CVE-2023-51765
aka SMTP smuggling.
[ Other info ]
No regression bugs in sid/trixie since at least two week
diff -Nru sendmail-8.17.1.9/debian/cf/ostype/debian.m4.in sendmail-8.17.1.9/debian/cf/ostype/debian.m4.in
--- sendmail-8.17.1.9/debian/cf/ostype/debian.m4.in 2023-01-11 22:26:28.000000000 +0000
+++ sendmail-8.17.1.9/debian/cf/ostype/debian.m4.in 2024-05-13 18:44:56.000000000 +0000
@@ -65,6 +65,9 @@
dnl #
define(`confDEF_USER_ID', `mail:mail')dnl
dnl #
+ifelse(eval(index(sm_ffr, `-D_FFR_REJECT_NUL_BYTE') >= 0), `1',dnl
+`define(`confREJECT_NUL',`true')')dnl
+dnl #
dnl #---------------------------------------------------------------------
dnl # mailer paths and options
dnl #---------------------------------------------------------------------
diff -Nru sendmail-8.17.1.9/debian/changelog sendmail-8.17.1.9/debian/changelog
--- sendmail-8.17.1.9/debian/changelog 2023-01-11 22:26:28.000000000 +0000
+++ sendmail-8.17.1.9/debian/changelog 2024-05-13 18:44:56.000000000 +0000
@@ -1,3 +1,24 @@
+sendmail (8.17.1.9-2+deb12u1) bookworm-security; urgency=high
+
+ * QA upload
+ * Fix CVE-2023-51765 (Closes: #1059386):
+ sendmail allowed SMTP smuggling in certain configurations.
+ Remote attackers can use a published exploitation
+ technique to inject e-mail messages with a spoofed
+ MAIL FROM address, allowing bypass of an SPF protection
+ mechanism. This occurs because sendmail supports
+ <LF>.<CR><LF> but some other popular e-mail servers
+ do not. This is resolved with 'o' in srv_features.
+ * Enable _FFR_REJECT_NUL_BYTE for rejecting mail that
+ include NUL byte
+ * By default enable rejecting mail that include NUL byte.
+ set confREJECT_NUL to 'true' by default .
+ User could disable by setting confREJECT_NUL to false.
+ (Closes: #1070190). Close a variant of CVE-2023-51765
+ aka SMTP smuggling.
+
+ -- Bastien Roucariès <rouca@debian.org> Mon, 13 May 2024 18:44:56 +0000
+
sendmail (8.17.1.9-2) unstable; urgency=medium
* QA upload.
diff -Nru sendmail-8.17.1.9/debian/configure.ac sendmail-8.17.1.9/debian/configure.ac
--- sendmail-8.17.1.9/debian/configure.ac 2023-01-11 22:26:28.000000000 +0000
+++ sendmail-8.17.1.9/debian/configure.ac 2024-05-13 18:44:56.000000000 +0000
@@ -466,6 +466,7 @@
sm_envdef="$sm_envdef -DHASFLOCK=1";
sm_libsm_envdef="$sm_libsm_envdef -DHAVE_NANOSLEEP=1";
sm_ffr="$sm_ffr -D_FFR_QUEUE_SCHED_DBG"; # %%%%%% TESTING %%%%%%%%
+sm_ffr="$sm_ffr -D_FFR_REJECT_NUL_BYTE";
#
# version specific setup
if test "$sm_version_major" = "8.17"; then
diff -Nru sendmail-8.17.1.9/debian/NEWS.Debian sendmail-8.17.1.9/debian/NEWS.Debian
--- sendmail-8.17.1.9/debian/NEWS.Debian 1970-01-01 00:00:00.000000000 +0000
+++ sendmail-8.17.1.9/debian/NEWS.Debian 2024-05-13 18:44:56.000000000 +0000
@@ -0,0 +1,19 @@
+sendmail (8.17.1.9-2+deb12u1) bookworm-security; urgency=medium
+
+ Sendmail was affected by SMTP smurgling (CVE-2023-51765).
+ Remote attackers can use a published exploitation technique
+ to inject e-mail messages with a spoofed MAIL FROM address,
+ allowing bypass of an SPF protection mechanism.
+ This occurs because sendmail supports some combinaison of
+ <CR><LF><NUL>.
+ .
+ This particular injection vulnerability has been closed,
+ unfortunatly full closure need to reject mail that
+ contain NUL.
+ .
+ This is slighly non conformant with RFC and could
+ be opt-out by setting confREJECT_NUL to 'false'
+ in sendmail.mc file.
+
+ -- Bastien Roucariès <rouca@debian.org> Sun, 12 May 2024 19:38:09 +0000
+
diff -Nru sendmail-8.17.1.9/debian/patches/0024-CVE-2023-51765.patch sendmail-8.17.1.9/debian/patches/0024-CVE-2023-51765.patch
--- sendmail-8.17.1.9/debian/patches/0024-CVE-2023-51765.patch 1970-01-01 00:00:00.000000000 +0000
+++ sendmail-8.17.1.9/debian/patches/0024-CVE-2023-51765.patch 2024-05-13 18:44:56.000000000 +0000
@@ -0,0 +1,1156 @@
+From: =?utf-8?q?Bastien_Roucari=C3=A8s?= <rouca@debian.org>
+Date: Thu, 15 Feb 2024 07:59:27 +0000
+Subject: CVE-2023-51765
+
+sendmail allowed SMTP smuggling in certain configurations.
+
+Remote attackers can use a published exploitation technique
+to inject e-mail messages with a spoofed MAIL FROM address,
+allowing bypass of an SPF protection mechanism.
+
+This occurs because sendmail supports <LF>.<CR><LF> but some other popular
+e-mail servers do not. This is resolved in 8.18 and later versions with 'o' in srv_features.
+---
+ RELEASE_NOTES | 25 ++++-
+ libsm/lowercase.c | 10 +-
+ sendmail/collect.c | 199 ++++++++++++++++++++++++++++--------
+ sendmail/main.c | 5 +-
+ sendmail/mime.c | 8 +-
+ sendmail/sendmail.h | 24 ++++-
+ sendmail/srvrsmtp.c | 285 +++++++++++++++++++++++++++++++++++-----------------
+ sendmail/usersmtp.c | 9 +-
+ sendmail/util.c | 10 +-
+ 9 files changed, 420 insertions(+), 155 deletions(-)
+
+diff --git a/RELEASE_NOTES b/RELEASE_NOTES
+index 1a747f3..56ec3d1 100644
+--- a/RELEASE_NOTES
++++ b/RELEASE_NOTES
+@@ -5,6 +5,27 @@ This listing shows the version of the sendmail binary, the version
+ of the sendmail configuration files, the date of release, and a
+ summary of the changes in that release.
+
++Backport 8.18.1/8.18.1 2024/01/31
++ sendmail is now stricter in following the RFCs and rejects
++ some invalid input with respect to line endings
++ and pipelining:
++ - Prevent transaction stuffing by ensuring SMTP clients
++ wait for the HELO/EHLO and DATA response before sending
++ further SMTP commands. This can be disabled using
++ the new srv_features option 'F'. Issue reported by
++ Yepeng Pan and Christian Rossow from CISPA Helmholtz
++ Center for Information Security.
++ - Accept only CRLF . CRLF as end of an SMTP message
++ as required by the RFCs, which can disabled by the
++ new srv_features option 'O'.
++ - Do not accept a CR or LF except in the combination
++ CRLF (as required by the RFCs). These checks can
++ be disabled by the new srv_features options
++ 'U' and 'G', respectively. In this case it is
++ suggested to use 'u2' and 'g2' instead so the server
++ replaces offending bare CR or bare LF with a space.
++ It is recommended to only turn these protections off
++ for trusted networks due to the potential for abuse.
+
+ 8.17.2/8.17.2 202X/XX/XX
+ Fix a regression introduced in 8.17.1: aliases file which
+@@ -5425,7 +5446,7 @@ summary of the changes in that release.
+ characters (in LMTP mode), mail.local split the incoming
+ line up into 2046-character output lines (excluding the
+ newline). If an input line was 2047 characters long
+- (excluding CR-LF) and the last character was a '.',
++ (excluding CRLF) and the last character was a '.',
+ mail.local saw it as the end of input, transferred it to the
+ user mailbox and tried to write an `ok' back to sendmail.
+ If the message was much longer, both sendmail and
+@@ -8447,7 +8468,7 @@ summary of the changes in that release.
+ should show the pathname rather than hex bytes.
+ Restore ``-ba'' mode -- this reads a file from stdin and parses
+ the header for envelope sender information and uses
+- CR-LF as message terminators. It was thought to be
++ CRLF as message terminators. It was thought to be
+ obsolete (used only for Arpanet NCP protocols), but it
+ turns out that the UK ``Grey Book'' protocols require
+ that functionality.
+diff --git a/libsm/lowercase.c b/libsm/lowercase.c
+index 8448eee..f980d2f 100644
+--- a/libsm/lowercase.c
++++ b/libsm/lowercase.c
+@@ -36,9 +36,15 @@ asciistr(str)
+ {
+ unsigned char ch;
+
+- if (str == NULL)
++ if (str == NULL)
+ return true;
+- while ((ch = (unsigned char)*str) != '\0' && ch >= 32 && ch < 127)
++
++ SM_REQUIRE(len < INT_MAX);
++ n = 0;
++ while (n < len && (ch = (unsigned char)*str) != '\0'
++ && ch >= 32 && ch < 127)
++ {
++ n++;
+ str++;
+ return ch == '\0';
+ }
+diff --git a/sendmail/collect.c b/sendmail/collect.c
+index 762c601..454d441 100644
+--- a/sendmail/collect.c
++++ b/sendmail/collect.c
+@@ -232,6 +232,36 @@ collect_dfopen(e)
+ return df;
+ }
+
++#if _FFR_TESTS
++/* just for testing/debug output */
++static const char *
++makeprint(c)
++ char c;
++{
++ static char prt[6];
++
++ prt[1] = '\0';
++ prt[2] = '\0';
++ if (isprint((unsigned char)c))
++ prt[0] = c;
++ else if ('\n' == c)
++ {
++ prt[0] = 'L';
++ prt[1] = 'F';
++ }
++ else if ('\r' == c)
++ {
++ prt[0] = 'C';
++ prt[1] = 'R';
++ }
++ else
++ snprintf(prt, sizeof(prt), "%o", c);
++ return prt;
++}
++#else /* _FFR_TESTS */
++# define makeprint(c) "X"
++#endif /* _FFR_TESTS */
++
+ /*
+ ** COLLECT -- read & parse message header & make temp file.
+ **
+@@ -267,20 +297,26 @@ collect_dfopen(e)
+ /* values for input state machine */
+ #define IS_NORM 0 /* middle of line */
+ #define IS_BOL 1 /* beginning of line */
+-#define IS_DOT 2 /* read a dot at beginning of line */
++#define IS_DOT 2 /* read "." at beginning of line */
+ #define IS_DOTCR 3 /* read ".\r" at beginning of line */
+-#define IS_CR 4 /* read a carriage return */
++#define IS_CR 4 /* read "\r" */
++
++/* hack to enhance readability of debug output */
++static const char *istates[] = { "NORM", "BOL", "DOT", "DOTCR", "CR" };
++#define ISTATE istates[istate]
+
+ /* values for message state machine */
+ #define MS_UFROM 0 /* reading Unix from line */
+ #define MS_HEADER 1 /* reading message header */
+ #define MS_BODY 2 /* reading message body */
+ #define MS_DISCARD 3 /* discarding rest of message */
++#define BARE_LF_MSG "Bare linefeed (LF) not allowed"
++#define BARE_CR_MSG "Bare carriage return (CR) not allowed"
+
+ void
+ collect(fp, smtpmode, hdrp, e, rsetsize)
+ SM_FILE_T *fp;
+- bool smtpmode;
++ int smtpmode;
+ HDR **hdrp;
+ register ENVELOPE *e;
+ bool rsetsize;
+@@ -306,12 +342,26 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ #if _FFR_REJECT_NUL_BYTE
+ bool hasNUL; /* has at least one NUL input byte */
+ #endif
++ int bare_lf, bare_cr;
++
++#define SMTPMODE (smtpmode >= SMTPMODE_LAX)
++#define SMTPMODE_STRICT ((smtpmode & SMTPMODE_CRLF) != 0)
++#define BARE_LF_421 ((smtpmode & SMTPMODE_LF_421) != 0)
++#define BARE_CR_421 ((smtpmode & SMTPMODE_CR_421) != 0)
++#define BARE_LF_SP ((smtpmode & SMTPMODE_LF_SP) != 0)
++#define BARE_CR_SP ((smtpmode & SMTPMODE_CR_SP) != 0)
++
++/* for bare_{lf,cr} */
++#define BARE_IN_HDR 0x01
++#define BARE_IN_BDY 0x02
++#define BARE_WHERE ((MS_BODY == mstate) ? BARE_IN_BDY : BARE_IN_HDR)
+
+ df = NULL;
+- ignrdot = smtpmode ? false : IgnrDot;
++ ignrdot = SMTPMODE ? false : IgnrDot;
++ bare_lf = bare_cr = 0;
+
+ /* timeout for I/O functions is in milliseconds */
+- dbto = smtpmode ? ((int) TimeOuts.to_datablock * 1000)
++ dbto = SMTPMODE ? ((int) TimeOuts.to_datablock * 1000)
+ : SM_TIME_FOREVER;
+ sm_io_setinfo(fp, SM_IO_WHAT_TIMEOUT, &dbto);
+ old_rd_tmo = set_tls_rd_tmo(TimeOuts.to_datablock);
+@@ -334,15 +384,15 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ ** Tell ARPANET to go ahead.
+ */
+
+- if (smtpmode)
+- message("354 Enter mail, end with \".\" on a line by itself");
++ if (SMTPMODE)
++ message("354 End data with <CR><LF>.<CR><LF>");
+
+ /* simulate an I/O timeout when used as sink */
+ if (tTd(83, 101))
+ sleep(319);
+
+ if (tTd(30, 2))
+- sm_dprintf("collect\n");
++ sm_dprintf("collect, smtpmode=%#x\n", smtpmode);
+
+ /*
+ ** Read the message.
+@@ -358,7 +408,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ for (;;)
+ {
+ if (tTd(30, 35))
+- sm_dprintf("top, istate=%d, mstate=%d\n", istate,
++ sm_dprintf("top, istate=%s, mstate=%d\n", ISTATE,
+ mstate);
+ for (;;)
+ {
+@@ -379,7 +429,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+
+ /* timeout? */
+ if (c == SM_IO_EOF && errno == EAGAIN
+- && smtpmode)
++ && SMTPMODE)
+ {
+ /*
+ ** Override e_message in
+@@ -417,15 +467,32 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ hasNUL = true;
+ #endif
+ if (c == SM_IO_EOF)
+- goto readerr;
+- if (SevenBitInput)
++ goto readdone;
++ if (SevenBitInput ||
++ bitset(EF_7BITBODY, e->e_flags))
+ c &= 0x7f;
+ else
+ HasEightBits |= bitset(0x80, c);
+ }
+ if (tTd(30, 94))
+- sm_dprintf("istate=%d, c=%c (0x%x)\n",
+- istate, (char) c, c);
++ sm_dprintf("istate=%s, c=%s (0x%x)\n",
++ ISTATE, makeprint((char) c), c);
++ if ('\n' == c && SMTPMODE &&
++ !(IS_CR == istate || IS_DOTCR == istate))
++ {
++ bare_lf |= BARE_WHERE;
++ if (BARE_LF_421)
++ {
++ inputerr = true;
++ goto readabort;
++ }
++ if (BARE_LF_SP)
++ {
++ if (TTD(30, 64))
++ sm_dprintf("LF: c=%s %#x\n", makeprint((char) c), c);
++ c = ' ';
++ }
++ }
+ switch (istate)
+ {
+ case IS_BOL:
+@@ -437,8 +504,8 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ break;
+
+ case IS_DOT:
+- if (c == '\n' && !ignrdot)
+- goto readerr;
++ if (c == '\n' && !ignrdot && !SMTPMODE_STRICT)
++ goto readdone;
+ else if (c == '\r')
+ {
+ istate = IS_DOTCR;
+@@ -460,7 +527,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+
+ case IS_DOTCR:
+ if (c == '\n' && !ignrdot)
+- goto readerr;
++ goto readdone;
+ else
+ {
+ /* push back the ".\rx" */
+@@ -483,12 +550,30 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+
+ case IS_CR:
+ if (c == '\n')
++ {
++ if (TTD(30, 64))
++ sm_dprintf("state=CR, c=%s %#x -> BOL\n", makeprint((char) c), c);
+ istate = IS_BOL;
++ }
+ else
+ {
++ if (TTD(30, 64))
++ sm_dprintf("state=CR, c=%s %#x -> NORM\n", makeprint((char) c), c);
++ if (SMTPMODE)
++ {
++ bare_cr |= BARE_WHERE;
++ if (BARE_CR_421)
++ {
++ inputerr = true;
++ goto readabort;
++ }
++ }
+ (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
+ c);
+- c = '\r';
++ if (BARE_CR_SP)
++ c = ' ';
++ else
++ c = '\r';
+ istate = IS_NORM;
+ }
+ goto bufferchar;
+@@ -499,7 +584,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ istate = IS_CR;
+ continue;
+ }
+- else if (c == '\n')
++ else if (c == '\n' && !SMTPMODE_STRICT)
+ istate = IS_BOL;
+ else
+ istate = IS_NORM;
+@@ -524,7 +609,8 @@ bufferchar:
+ if (!bitset(EF_TOOBIG, e->e_flags))
+ (void) sm_io_putc(df, SM_TIME_DEFAULT,
+ c);
+-
++ if (TTD(30, 64))
++ sm_dprintf("state=%s, put=%s %#x\n", ISTATE, makeprint((char) c), c);
+ /* FALLTHROUGH */
+
+ case MS_DISCARD:
+@@ -592,8 +678,8 @@ bufferchar:
+
+ nextstate:
+ if (tTd(30, 35))
+- sm_dprintf("nextstate, istate=%d, mstate=%d, line=\"%s\"\n",
+- istate, mstate, buf);
++ sm_dprintf("nextstate, istate=%s, mstate=%d, line=\"%s\"\n",
++ ISTATE, mstate, buf);
+ switch (mstate)
+ {
+ case MS_UFROM:
+@@ -624,7 +710,7 @@ nextstate:
+
+ /* timeout? */
+ if (c == SM_IO_EOF && errno == EAGAIN
+- && smtpmode)
++ && SMTPMODE)
+ {
+ /*
+ ** Override e_message in
+@@ -653,7 +739,7 @@ nextstate:
+ /* guaranteed by isheader(buf) */
+ SM_ASSERT(*(bp - 1) != '\n' || bp > buf + 1);
+
+- /* trim off trailing CRLF or NL */
++ /* trim off trailing CRLF or LF */
+ if (*--bp != '\n' || *--bp != '\r')
+ bp++;
+ *bp = '\0';
+@@ -673,7 +759,7 @@ nextstate:
+ sm_dprintf("EOH\n");
+
+ if (headeronly)
+- goto readerr;
++ goto readdone;
+
+ df = collect_eoh(e, numhdrs, hdrslen);
+ if (df == NULL)
+@@ -700,8 +786,8 @@ nextstate:
+ bp = buf;
+ }
+
+-readerr:
+- if ((sm_io_eof(fp) && smtpmode) || sm_io_error(fp))
++readdone:
++ if ((sm_io_eof(fp) && SMTPMODE) || sm_io_error(fp))
+ {
+ const char *errmsg;
+
+@@ -741,7 +827,7 @@ readerr:
+ }
+ else if (SuperSafe == SAFE_NO ||
+ SuperSafe == SAFE_INTERACTIVE ||
+- (SuperSafe == SAFE_REALLY_POSTMILTER && smtpmode))
++ (SuperSafe == SAFE_REALLY_POSTMILTER && SMTPMODE))
+ {
+ /* skip next few clauses */
+ /* EMPTY */
+@@ -806,33 +892,43 @@ readerr:
+ readabort:
+ if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
+ {
+- char *host;
+ char *problem;
+ ADDRESS *q;
+
+- host = RealHostName;
+- if (host == NULL)
+- host = "localhost";
+-
+ if (sm_io_eof(fp))
+ problem = "unexpected close";
+ else if (sm_io_error(fp))
+ problem = "I/O error";
++ else if (0 != bare_lf)
++ problem = BARE_LF_MSG;
++ else if (0 != bare_cr)
++ problem = BARE_CR_MSG;
+ else
+ problem = "read timeout";
+- if (LogLevel > 0 && sm_io_eof(fp))
++
++#define LOG_CLT ((NULL != RealHostName) ? RealHostName: "localhost")
++#define CONN_ERR_TXT "collect: relay=%s, from=%s, info=%s%s%s%s"
++#define CONN_ERR_CODE "421 4.4.1 "
++#define CONN_LOG_FROM shortenstring(e->e_from.q_paddr, MAXSHORTSTR)
++#define CONN_ERR_BARE (0 != bare_lf) ? BARE_LF_MSG : ((0 != bare_cr) ? BARE_CR_MSG : "")
++#define CONN_ERR_WHERE(bare_xy) (BARE_IN_HDR==(bare_xy) ? "header" : \
++ (BARE_IN_BDY==(bare_xy) ? "body" : "header+body"))
++
++#define HAS_BARE_XY (0 != (bare_lf | bare_cr))
++#define CONN_ERR_ARGS LOG_CLT, CONN_LOG_FROM, problem, \
++ HAS_BARE_XY ? ", where=" : "", \
++ HAS_BARE_XY ? CONN_ERR_WHERE(bare_lf|bare_cr) : "", \
++ HAS_BARE_XY ? ", status=tempfail" : ""
++
++ if (LogLevel > 0 && (sm_io_eof(fp) || (0 != (bare_lf | bare_cr))))
+ sm_syslog(LOG_NOTICE, e->e_id,
+- "collect: %s on connection from %.100s, sender=%s",
+- problem, host,
+- shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
+- if (sm_io_eof(fp))
+- usrerr("421 4.4.1 collect: %s on connection from %s, from=%s",
+- problem, host,
+- shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
++ CONN_ERR_TXT, CONN_ERR_ARGS);
++ if (0 != (bare_lf | bare_cr))
++ usrerr("421 4.5.0 %s", CONN_ERR_BARE);
++ else if (sm_io_eof(fp))
++ usrerr(CONN_ERR_CODE CONN_ERR_TXT, CONN_ERR_ARGS);
+ else
+- syserr("421 4.4.1 collect: %s on connection from %s, from=%s",
+- problem, host,
+- shortenstring(e->e_from.q_paddr, MAXSHORTSTR));
++ syserr(CONN_ERR_CODE CONN_ERR_TXT, CONN_ERR_ARGS);
+ flush_errors(true);
+
+ /* don't return an error indication */
+@@ -863,6 +959,21 @@ readerr:
+ e->e_flags &= ~EF_LOGSENDER;
+ }
+
++#define LOG_BARE_XY(bare_xy, bare_xy_sp, bare_xy_msg) \
++ do \
++ { \
++ if ((0 != bare_xy) && LogLevel > 8) \
++ sm_syslog(LOG_NOTICE, e->e_id, \
++ "collect: relay=%s, from=%s, info=%s, where=%s%s" \
++ , LOG_CLT, CONN_LOG_FROM, bare_xy_msg \
++ , CONN_ERR_WHERE(bare_xy) \
++ , bare_xy_sp ? ", status=replaced" : "" \
++ ); \
++ } while (0)
++
++ LOG_BARE_XY(bare_lf, BARE_LF_SP, BARE_LF_MSG);
++ LOG_BARE_XY(bare_cr, BARE_CR_SP, BARE_CR_MSG);
++
+ /* check for message too large */
+ if (bitset(EF_TOOBIG, e->e_flags))
+ {
+diff --git a/sendmail/main.c b/sendmail/main.c
+index d9851a5..a76a922 100644
+--- a/sendmail/main.c
++++ b/sendmail/main.c
+@@ -2878,7 +2878,8 @@ main(argc, argv, envp)
+
+ /* collect body for UUCP return */
+ if (OpMode != MD_VERIFY)
+- collect(InChannel, false, NULL, &MainEnvelope, true);
++ collect(InChannel, SMTPMODE_NO, NULL, &MainEnvelope,
++ true);
+ finis(true, true, EX_USAGE);
+ /* NOTREACHED */
+ }
+@@ -2938,7 +2939,7 @@ main(argc, argv, envp)
+ MainEnvelope.e_flags &= ~EF_FATALERRS;
+ Errors = 0;
+ buffer_errors();
+- collect(InChannel, false, NULL, &MainEnvelope, true);
++ collect(InChannel, SMTPMODE_NO, NULL, &MainEnvelope, true);
+
+ /* header checks failed */
+ if (Errors > 0)
+diff --git a/sendmail/mime.c b/sendmail/mime.c
+index 126304c..75cb112 100644
+--- a/sendmail/mime.c
++++ b/sendmail/mime.c
+@@ -347,7 +347,7 @@ mime8to7(mci, header, e, boundaries, flags, level)
+ goto writeerr;
+ if (tTd(43, 35))
+ sm_dprintf(" ...%s\n", buf);
+- collect(e->e_dfp, false, &hdr, e, false);
++ collect(e->e_dfp, SMTPMODE_NO, &hdr, e, false);
+ if (tTd(43, 101))
+ putline("+++after collect", mci);
+ if (!putheader(mci, hdr, e, flags))
+@@ -409,7 +409,7 @@ mime8to7(mci, header, e, boundaries, flags, level)
+ goto writeerr;
+
+ mci->mci_flags |= MCIF_INMIME;
+- collect(e->e_dfp, false, &hdr, e, false);
++ collect(e->e_dfp, SMTPMODE_NO, &hdr, e, false);
+ if (tTd(43, 101))
+ putline("+++after collect", mci);
+ if (!putheader(mci, hdr, e, flags))
+@@ -483,7 +483,7 @@ mime8to7(mci, header, e, boundaries, flags, level)
+ ** If more than 1/8 of the total characters have the
+ ** eighth bit set, use base64; else use quoted-printable.
+ ** However, only encode binary encoded data as base64,
+- ** since otherwise the NL=>CRLF mapping will be a problem.
++ ** since otherwise the LF=>CRLF mapping will be a problem.
+ */
+
+ if (tTd(43, 8))
+@@ -837,7 +837,7 @@ mime_getchar(fp, boundaries, btp)
+ return *bp++;
+ }
+ /*
+-** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF
++** MIME_GETCHAR_CRLF -- do mime_getchar, but translate LF => CRLF
+ **
+ ** Parameters:
+ ** fp -- the input file.
+diff --git a/sendmail/sendmail.h b/sendmail/sendmail.h
+index 9579f1e..1504100 100644
+--- a/sendmail/sendmail.h
++++ b/sendmail/sendmail.h
+@@ -1132,7 +1132,7 @@ struct envelope
+ long e_deliver_by; /* deliver by */
+ int e_dlvr_flag; /* deliver by flag */
+ SM_RPOOL_T *e_rpool; /* resource pool for this envelope */
+- unsigned int e_features; /* server features */
++ unsigned long e_features; /* server features */
+ #define ENHSC_LEN 11
+ #if _FFR_MILTER_ENHSC
+ char e_enhsc[ENHSC_LEN]; /* enhanced status code */
+@@ -1167,8 +1167,8 @@ struct envelope
+ #define EF_LOGSENDER 0x00008000L /* need to log the sender */
+ #define EF_NORECEIPT 0x00010000L /* suppress all return-receipts */
+ #define EF_HAS8BIT 0x00020000L /* at least one 8-bit char in body */
+-/* was: EF_NL_NOT_EOL 0x00040000L * don't accept raw NL as EOLine */
+-/* was: EF_CRLF_NOT_EOL 0x00080000L * don't accept CR-LF as EOLine */
++/* was: EF_NL_NOT_EOL 0x00040000L * don't accept raw LF as EOLine */
++/* was: EF_CRLF_NOT_EOL 0x00080000L * don't accept CRLF as EOLine */
+ #define EF_RET_PARAM 0x00100000L /* RCPT command had RET argument */
+ #define EF_HAS_DF 0x00200000L /* set when data file is instantiated */
+ #define EF_IS_MIME 0x00400000L /* really is a MIME message */
+@@ -1179,6 +1179,7 @@ struct envelope
+ #define EF_UNSAFE 0x08000000L /* unsafe: read from untrusted source */
+ #define EF_TOODEEP 0x10000000L /* message is nested too deep */
+ #define EF_SECURE 0x20000000L /* DNSSEC for currently parsed addr */
++#define EF_7BITBODY 0x40000000L /* strip body to 7bit on input */
+
+ #define DLVR_NOTIFY 0x01
+ #define DLVR_RETURN 0x02
+@@ -2336,6 +2337,11 @@ extern void inittimeouts __P((char *, bool));
+ # define tTd(flag, level) (tTdvect[flag] >= (unsigned char)level)
+ #else
+ # define tTd(flag, level) (tTdvect[flag] >= (unsigned char)level && !IntSig)
++# if _FFR_TESTS
++# define TTD(flag, level) (tTdvect[flag] >= (unsigned char)level && !IntSig)
++# else
++# define TTD(flag, level) false
++# endif
+ #endif
+ #define tTdlevel(flag) (tTdvect[flag])
+
+@@ -2818,7 +2824,7 @@ extern void cleanup_shm __P((bool));
+ #endif
+ extern void close_sendmail_pid __P((void));
+ extern void clrdaemon __P((void));
+-extern void collect __P((SM_FILE_T *, bool, HDR **, ENVELOPE *, bool));
++extern void collect __P((SM_FILE_T *, int, HDR **, ENVELOPE *, bool));
+ extern time_t convtime __P((char *, int));
+ extern char **copyplist __P((char **, bool, SM_RPOOL_T *));
+ extern void copy_class __P((int, int));
+@@ -3001,6 +3007,15 @@ extern bool xtextok __P((char *));
+ extern int xunlink __P((char *));
+ extern char *xuntextify __P((char *));
+
++/* flags for collect() */
++#define SMTPMODE_NO 0
++#define SMTPMODE_LAX 0x01
++#define SMTPMODE_CRLF 0x02 /* CRLF.CRLF required for EOM */
++#define SMTPMODE_LF_421 0x04 /* bare LF: drop connection */
++#define SMTPMODE_CR_421 0x08 /* bare CR: drop connection */
++#define SMTPMODE_LF_SP 0x10 /* bare LF: replace with space */
++#define SMTPMODE_CR_SP 0x20 /* bare CR: replace with space */
++
+ #define ASSIGN_IFDIFF(old, new) \
+ do \
+ { \
+@@ -3014,6 +3029,7 @@ extern char *xuntextify __P((char *));
+
+ #if USE_EAI
+ extern bool addr_is_ascii __P((const char *));
++extern bool str_is_print __P((const char *));
+ extern const char *hn2alabel __P((const char *));
+ #endif
+
+diff --git a/sendmail/srvrsmtp.c b/sendmail/srvrsmtp.c
+index e0062b7..57f26e0 100644
+--- a/sendmail/srvrsmtp.c
++++ b/sendmail/srvrsmtp.c
+@@ -52,30 +52,36 @@ static bool NotFirstDelivery = false;
+ #endif
+
+ /* server features */
+-#define SRV_NONE 0x0000 /* none... */
+-#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
+-#define SRV_VRFY_CLT 0x0002 /* request a cert */
+-#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
+-#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
+-#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
+-#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
+-#define SRV_OFFER_VERB 0x0040 /* offer VERB */
+-#define SRV_OFFER_DSN 0x0080 /* offer DSN */
++#define SRV_NONE 0x00000000 /* none... */
++#define SRV_OFFER_TLS 0x00000001 /* offer STARTTLS */
++#define SRV_VRFY_CLT 0x00000002 /* request a cert */
++#define SRV_OFFER_AUTH 0x00000004 /* offer AUTH */
++#define SRV_OFFER_ETRN 0x00000008 /* offer ETRN */
++#define SRV_OFFER_VRFY 0x00000010 /* offer VRFY (not yet used) */
++#define SRV_OFFER_EXPN 0x00000020 /* offer EXPN */
++#define SRV_OFFER_VERB 0x00000040 /* offer VERB */
++#define SRV_OFFER_DSN 0x00000080 /* offer DSN */
+ #if PIPELINING
+-# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
++# define SRV_OFFER_PIPE 0x00000100 /* offer PIPELINING */
+ # if _FFR_NO_PIPE
+-# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
++# define SRV_NO_PIPE 0x00000200 /* disable PIPELINING, sleep if used */
+ # endif
+ #endif /* PIPELINING */
+-#define SRV_REQ_AUTH 0x0400 /* require AUTH */
+-#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */
+-#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
++#define SRV_REQ_AUTH 0x00000400 /* require AUTH */
++#define SRV_REQ_SEC 0x00000800 /* require security - equiv to AuthOptions=p */
++#define SRV_TMP_FAIL 0x00001000 /* ruleset caused a temporary failure */
+ #if USE_EAI
+-# define SRV_OFFER_EAI 0x2000 /* offer SMTPUTF8 */
++# define SRV_OFFER_EAI 0x00002000 /* offer SMTPUTF8 */
+ #endif
+-#define SRV_NO_HTTP_CMD 0x4000 /* always reject HTTP commands */
++#define SRV_NO_HTTP_CMD 0x00004000 /* always reject HTTP commands */
++#define SRV_BAD_PIPELINE 0x00008000 /* reject bad pipelining (see comment below) */
++#define SRV_REQ_CRLF 0x00010000 /* require CRLF as EOL */
++#define SRV_BARE_LF_421 0x00020000 /* bare LF - drop connection */
++#define SRV_BARE_CR_421 0x00040000 /* bare CR - drop connection */
++#define SRV_BARE_LF_SP 0x00080000
++#define SRV_BARE_CR_SP 0x00100000
+
+-static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
++static unsigned long srvfeatures __P((ENVELOPE *, char *, unsigned long));
+
+ #define STOP_ATTACK ((time_t) -1)
+ static time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
+@@ -83,6 +89,7 @@ static time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
+ static void printvrfyaddr __P((ADDRESS *, bool, bool));
+ static char *skipword __P((char *volatile, char *));
+ static void setup_smtpd_io __P((void));
++static struct timeval *channel_readable __P((SM_FILE_T *, int));
+
+ #if SASL
+ # ifndef MAX_AUTH_USER_LEN
+@@ -507,6 +514,39 @@ rmargsorigp(delimptr, origp, id, p)
+ }
+ #endif /* _FFR_8BITENVADDR */
+
++/*
++** CHANNEL_READBLE -- determine if data is readable from the SMTP channel
++**
++** Parameters:
++** channel -- connect channel for reading
++** timeout -- how long to pause for data in milliseconds
++**
++** Returns:
++** timeval contained how long we waited if data detected,
++** NULL otherwise
++*/
++
++static struct timeval *
++channel_readable(channel, timeout)
++ SM_FILE_T *channel;
++ int timeout;
++{
++ struct timeval bp, ep; /* {begin,end} pause */
++ static struct timeval tp; /* total pause */
++ int eoftest;
++
++ /* check if data is on the channel during the pause */
++ gettimeofday(&bp, NULL);
++ if ((eoftest = sm_io_getc(channel, timeout)) != SM_IO_EOF)
++ {
++ gettimeofday(&ep, NULL);
++ sm_io_ungetc(channel, SM_TIME_DEFAULT, eoftest);
++ timersub(&ep, &bp, &tp);
++ return &tp;
++ }
++ return NULL;
++}
++
+ /*
+ ** SMTP -- run the SMTP protocol.
+ **
+@@ -667,7 +707,7 @@ typedef struct
+ char *sm_quarmsg; /* carry quarantining across messages */
+ } SMTP_T;
+
+-static bool smtp_data __P((SMTP_T *, ENVELOPE *));
++static bool smtp_data __P((SMTP_T *, ENVELOPE *, bool));
+
+ #define MSG_TEMPFAIL "451 4.3.2 Please try again later"
+
+@@ -972,11 +1010,9 @@ smtp(nullserver, d_flags, e)
+ int save_errno;
+ extern int TLSsslidx;
+ #endif /* STARTTLS */
+- volatile unsigned int features;
+-#if PIPELINING
+-# if _FFR_NO_PIPE
++ volatile unsigned long features;
++#if PIPELINING && _FFR_NO_PIPE
+ int np_log = 0;
+-# endif
+ #endif
+ volatile time_t log_delay = (time_t) 0;
+ #if MILTER
+@@ -1035,8 +1070,12 @@ smtp(nullserver, d_flags, e)
+ #endif
+
+ sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
+-
+- /* Set default features for server. */
++ /*
++ ** Set default features for server.
++ **
++ ** Changing SRV_BARE_LF_421 | SRV_BARE_CR_421 below also
++ ** requires changing srvfeatures() variant code.
++ */
+ features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
+ bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
+ | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
+@@ -1054,6 +1093,7 @@ smtp(nullserver, d_flags, e)
+ #if PIPELINING
+ | SRV_OFFER_PIPE
+ #endif
++ | SRV_BAD_PIPELINE
+ #if STARTTLS
+ | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
+ | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
+@@ -1062,6 +1102,7 @@ smtp(nullserver, d_flags, e)
+ #if USE_EAI
+ | (SMTPUTF8 ? SRV_OFFER_EAI : 0)
+ #endif
++ | SRV_REQ_CRLF | SRV_BARE_LF_421 | SRV_BARE_CR_421
+ ;
+ if (nullserver == NULL)
+ {
+@@ -1076,15 +1117,13 @@ smtp(nullserver, d_flags, e)
+ }
+ else
+ {
+-#if PIPELINING
+-# if _FFR_NO_PIPE
++#if PIPELINING && _FFR_NO_PIPE
+ if (bitset(SRV_NO_PIPE, features))
+ {
+ /* for consistency */
+ features &= ~SRV_OFFER_PIPE;
+ }
+-# endif /* _FFR_NO_PIPE */
+-#endif /* PIPELINING */
++#endif /* PIPELINING && _FFR_NO_PIPE */
+ #if SASL
+ if (bitset(SRV_REQ_SEC, features))
+ SASLOpts |= SASL_SEC_NOPLAINTEXT;
+@@ -1452,46 +1491,23 @@ smtp(nullserver, d_flags, e)
+
+ if (msecs > 0)
+ {
+- int fd;
+- fd_set readfds;
+- struct timeval timeout;
+- struct timeval bp, ep, tp; /* {begin,end,total}pause */
+- int eoftest;
+-
+- /* pause for a moment */
+- timeout.tv_sec = msecs / 1000;
+- timeout.tv_usec = (msecs % 1000) * 1000;
++ struct timeval *tp; /* total pause */
+
+- /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
+- if (timeout.tv_sec >= 300)
+- {
+- timeout.tv_sec = 300;
+- timeout.tv_usec = 0;
+- }
++ /* Obey RFC 2821: 4.5.3.2: 220 timeout of 5 minutes (300 seconds) */
++ if (msecs >= 300000)
++ msecs = 300000;
+
+ /* check if data is on the socket during the pause */
+- fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
+- FD_ZERO(&readfds);
+- SM_FD_SET(fd, &readfds);
+- gettimeofday(&bp, NULL);
+- if (select(fd + 1, FDSET_CAST &readfds,
+- NULL, NULL, &timeout) > 0 &&
+- FD_ISSET(fd, &readfds) &&
+- (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT))
+- != SM_IO_EOF)
+- {
+- sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
+- eoftest);
+- gettimeofday(&ep, NULL);
+- timersub(&ep, &bp, &tp);
++ if ((tp = channel_readable(InChannel, msecs)) != NULL)
++ {
+ greetcode = "554";
+ nullserver = "Command rejected";
+ sm_syslog(LOG_INFO, e->e_id,
+ "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
+ peerhostname,
+ anynet_ntoa(&RealHostAddr),
+- (int) tp.tv_sec +
+- (tp.tv_usec >= 500000 ? 1 : 0)
++ (int) tp->tv_sec +
++ (tp->tv_usec >= 500000 ? 1 : 0)
+ );
+ }
+ }
+@@ -2520,6 +2536,30 @@ smtp(nullserver, d_flags, e)
+ STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
+ true, "HELO/EHLO", e));
+
++ /*
++ ** Despite the fact that the name indicates this
++ ** a PIPELINE related feature, do not enclose
++ ** it in #if PIPELINING so we can protect SMTP
++ ** servers not compiled with PIPELINE support
++ ** from transaction stuffing.
++ */
++
++ /* check if data is on the socket before the EHLO reply */
++ if (bitset(SRV_BAD_PIPELINE, features) &&
++ sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
++ {
++ sm_syslog(LOG_INFO, e->e_id,
++ "rejecting %s from %s [%s] due to traffic before response",
++ SmtpPhase, CurHostName,
++ anynet_ntoa(&RealHostAddr));
++ usrerr("554 5.5.0 SMTP protocol error");
++ nullserver = "Command rejected";
++#if MILTER
++ smtp.sm_milterize = false;
++#endif
++ break;
++ }
++
+ #if 0
+ /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
+ /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
+@@ -3434,7 +3474,8 @@ smtp(nullserver, d_flags, e)
+
+ case CMDDATA: /* data -- text of mail */
+ DELAY_CONN("DATA");
+- if (!smtp_data(&smtp, e))
++ if (!smtp_data(&smtp, e,
++ bitset(SRV_BAD_PIPELINE, features)))
+ goto doquit;
+ break;
+
+@@ -3914,6 +3955,7 @@ doquit:
+ ** Parameters:
+ ** smtp -- status of SMTP connection.
+ ** e -- envelope.
++** check_stuffing -- check for transaction stuffing.
+ **
+ ** Returns:
+ ** true iff SMTP session can continue.
+@@ -3923,9 +3965,10 @@ doquit:
+ */
+
+ static bool
+-smtp_data(smtp, e)
++smtp_data(smtp, e, check_stuffing)
+ SMTP_T *smtp;
+ ENVELOPE *e;
++ bool check_stuffing;
+ {
+ #if MILTER
+ bool milteraccept;
+@@ -3937,7 +3980,7 @@ smtp_data(smtp, e)
+ ENVELOPE *ee;
+ char *id;
+ char *oldid;
+- unsigned int features;
++ unsigned long features;
+ char buf[32];
+
+ SmtpPhase = "server DATA";
+@@ -3951,6 +3994,18 @@ smtp_data(smtp, e)
+ usrerr("503 5.0.0 Need RCPT (recipient)");
+ return true;
+ }
++
++ /* check if data is on the socket before the DATA reply */
++ if (check_stuffing &&
++ sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
++ {
++ sm_syslog(LOG_INFO, e->e_id,
++ "rejecting %s from %s [%s] due to traffic before response",
++ SmtpPhase, CurHostName, anynet_ntoa(&RealHostAddr));
++ usrerr("554 5.5.0 SMTP protocol error");
++ return false;
++ }
++
+ (void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
+ if (rscheck("check_data", buf, NULL, e,
+ RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
+@@ -4071,7 +4126,13 @@ smtp_data(smtp, e)
+ SmtpPhase = "collect";
+ buffer_errors();
+
+- collect(InChannel, true, NULL, e, true);
++ collect(InChannel, SMTPMODE_LAX
++ | (bitset(SRV_BARE_LF_421, e->e_features) ? SMTPMODE_LF_421 : 0)
++ | (bitset(SRV_BARE_CR_421, e->e_features) ? SMTPMODE_CR_421 : 0)
++ | (bitset(SRV_BARE_LF_SP, e->e_features) ? SMTPMODE_LF_SP : 0)
++ | (bitset(SRV_BARE_CR_SP, e->e_features) ? SMTPMODE_CR_SP : 0)
++ | (bitset(SRV_REQ_CRLF, e->e_features) ? SMTPMODE_CRLF : 0),
++ NULL, e, true);
+
+ /* redefine message size */
+ (void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
+@@ -5557,39 +5618,50 @@ initsrvtls(tls_ok)
+ static struct
+ {
+ char srvf_opt;
+- unsigned int srvf_flag;
++ unsigned long srvf_flag;
++ unsigned long srvf_flag2;
+ } srv_feat_table[] =
+ {
+- { 'A', SRV_OFFER_AUTH },
+- { 'B', SRV_OFFER_VERB },
+- { 'C', SRV_REQ_SEC },
+- { 'D', SRV_OFFER_DSN },
+- { 'E', SRV_OFFER_ETRN },
+- { 'H', SRV_NO_HTTP_CMD },
++ { 'A', SRV_OFFER_AUTH , 0 },
++ { 'B', SRV_OFFER_VERB , 0 },
++ { 'C', SRV_REQ_SEC , 0 },
++ { 'D', SRV_OFFER_DSN , 0 },
++ { 'E', SRV_OFFER_ETRN , 0 },
++ { 'F', SRV_BAD_PIPELINE , 0 },
++ { 'G', SRV_BARE_LF_421 , SRV_BARE_LF_SP },
++ { 'H', SRV_NO_HTTP_CMD , 0 },
+ #if USE_EAI
+- { 'I', SRV_OFFER_EAI },
++ { 'I', SRV_OFFER_EAI , 0 },
++#endif
++/* { 'J', 0 , 0 }, */
++/* { 'K', 0 , 0 }, */
++ { 'L', SRV_REQ_AUTH , 0 },
++/* { 'M', 0 , 0 }, */
++#if PIPELINING && _FFR_NO_PIPE
++ { 'N', SRV_NO_PIPE , 0 },
+ #endif
+- { 'L', SRV_REQ_AUTH },
++ { 'O', SRV_REQ_CRLF , 0 }, /* eOl */
+ #if PIPELINING
+-# if _FFR_NO_PIPE
+- { 'N', SRV_NO_PIPE },
+-# endif
+- { 'P', SRV_OFFER_PIPE },
+-#endif /* PIPELINING */
+- { 'R', SRV_VRFY_CLT }, /* same as V; not documented */
+- { 'S', SRV_OFFER_TLS },
+-/* { 'T', SRV_TMP_FAIL }, */
+- { 'V', SRV_VRFY_CLT },
+- { 'X', SRV_OFFER_EXPN },
+-/* { 'Y', SRV_OFFER_VRFY }, */
+- { '\0', SRV_NONE }
++ { 'P', SRV_OFFER_PIPE , 0 },
++#endif
++/* { 'Q', 0 , 0 }, */
++ { 'R', SRV_VRFY_CLT , 0 }, /* same as V; not documented */
++ { 'S', SRV_OFFER_TLS , 0 },
++/* { 'T', SRV_TMP_FAIL , 0 }, */
++ { 'U', SRV_BARE_CR_421 , SRV_BARE_CR_SP },
++ { 'V', SRV_VRFY_CLT , 0 },
++/* { 'W', 0 , 0 }, */
++ { 'X', SRV_OFFER_EXPN , 0 },
++/* { 'Y', SRV_OFFER_VRFY , 0 }, */
++/* { 'Z', 0 , 0 }, */
++ { '\0', SRV_NONE , 0 }
+ };
+
+-static unsigned int
++static unsigned long
+ srvfeatures(e, clientname, features)
+ ENVELOPE *e;
+ char *clientname;
+- unsigned int features;
++ unsigned long features;
+ {
+ int r, i, j;
+ char **pvp, c, opt;
+@@ -5623,7 +5695,7 @@ srvfeatures(e, clientname, features)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+- "srvfeatures: unknown feature %s",
++ "srv_features: unknown feature %s",
+ pvp[i]);
+ break;
+ }
+@@ -5632,9 +5704,40 @@ srvfeatures(e, clientname, features)
+ features &= ~(srv_feat_table[j].srvf_flag);
+ break;
+ }
++
++ /*
++ ** Note: the "noflag" code below works ONLY for
++ ** the current situation:
++ ** - _flag itself is set by default
++ ** (drop session if bare CR or LF is found)
++ ** - _flag2 is only "effective" if _flag is not set,
++ ** hence using it turns off _flag.
++ ** If that situation changes, the code must be changed!
++ */
++
+ if (c == tolower(opt))
+ {
+- features |= srv_feat_table[j].srvf_flag;
++ unsigned long flag, noflag;
++
++ c = pvp[i][1];
++ flag = noflag = 0;
++ if ('2' == c)
++ {
++ flag = srv_feat_table[j].srvf_flag2;
++ noflag = srv_feat_table[j].srvf_flag;
++ }
++ else if ('\0' == c)
++ flag = srv_feat_table[j].srvf_flag;
++ if (0 != flag)
++ {
++ features |= flag;
++ if (0 != noflag)
++ features &= ~noflag;
++ }
++ else if (LogLevel > 9)
++ sm_syslog(LOG_WARNING, e->e_id,
++ "srv_features: unknown variant %s",
++ pvp[i]);
+ break;
+ }
+ ++j;
+diff --git a/sendmail/usersmtp.c b/sendmail/usersmtp.c
+index b246421..85979ad 100644
+--- a/sendmail/usersmtp.c
++++ b/sendmail/usersmtp.c
+@@ -2393,6 +2393,9 @@ smtprcpt(to, m, mci, e, ctladdr, xstart)
+ char buf[MAXNAME + 1]; /* EAI:ok */
+ int len, nlen;
+ #endif
++#if PIPELINING
++ char *oldto;
++#endif
+
+ #if PIPELINING
+ /*
+@@ -2400,20 +2403,24 @@ smtprcpt(to, m, mci, e, ctladdr, xstart)
+ ** This should normally happen because of SMTP pipelining.
+ */
+
++ oldto = e->e_to;
+ while (mci->mci_nextaddr != NULL &&
+ sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
+ {
+ int r;
+
++ e->e_to = mci->mci_nextaddr->q_paddr;
+ r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
+ if (r != EX_OK)
+ {
+ markfailure(e, mci->mci_nextaddr, mci, r, false);
+ giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
+- ctladdr, xstart, e, to);
++ ctladdr, xstart, e, mci->mci_nextaddr);
+ }
+ mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
++ e->e_to = oldto;
+ }
++ e->e_to = oldto;
+ #endif /* PIPELINING */
+
+ /*
+diff --git a/sendmail/util.c b/sendmail/util.c
+index 03671de..00030a7 100644
+--- a/sendmail/util.c
++++ b/sendmail/util.c
+@@ -1084,15 +1084,15 @@ makelower_buf(str, buf, buflen)
+ }
+
+ /*
+-** FIXCRLF -- fix <CR><LF> in line.
++** FIXCRLF -- fix CRLF in line.
+ **
+ ** XXX: Could this be a problem for EAI? That is, can there
+ ** be a string with \n and the previous octet is \n
+ ** but is part of a UTF8 "char"?
+ **
+-** Looks for the <CR><LF> combination and turns it into the
+-** UNIX canonical <NL> character. It only takes one line,
+-** i.e., it is assumed that the first <NL> found is the end
++** Looks for the CRLF combination and turns it into the
++** UNIX canonical LF character. It only takes one line,
++** i.e., it is assumed that the first LF found is the end
+ ** of the line.
+ **
+ ** Parameters:
+@@ -1529,7 +1529,7 @@ sfgets(buf, siz, fp, timeout, during)
+ ** Side Effects:
+ ** buf gets lines from f, with continuation lines (lines
+ ** with leading white space) appended. CRLF's are mapped
+-** into single newlines. Any trailing NL is stripped.
++** into single newlines. Any trailing LF is stripped.
+ ** Increases LineNumber for each line.
+ */
+
diff -Nru sendmail-8.17.1.9/debian/patches/reject_nul.patch sendmail-8.17.1.9/debian/patches/reject_nul.patch
--- sendmail-8.17.1.9/debian/patches/reject_nul.patch 1970-01-01 00:00:00.000000000 +0000
+++ sendmail-8.17.1.9/debian/patches/reject_nul.patch 2024-05-13 18:44:56.000000000 +0000
@@ -0,0 +1,15 @@
+Author: Andreas Beckmann <anbe@debian.org>
+Description: add configurable 'O RejectNUL' to *.cf
+
+--- a/cf/m4/proto.m4
++++ b/cf/m4/proto.m4
+@@ -720,6 +720,9 @@ _OPTION(MaxNOOPCommands, `confMAX_NOOP_C
+ # Name to use for EHLO (defaults to $j)
+ _OPTION(HeloName, `confHELO_NAME')
+
++# Reject NUL bytes in message body, requires _FFR_REJECT_NUL_BYTE
++_OPTION(RejectNUL, `confREJECT_NUL', `false')
++
+ ifdef(`_NEED_SMTPOPMODES_', `dnl
+ # SMTP operation modes
+ C{SMTPOpModes} s d D')
diff -Nru sendmail-8.17.1.9/debian/patches/series sendmail-8.17.1.9/debian/patches/series
--- sendmail-8.17.1.9/debian/patches/series 2023-01-11 22:26:28.000000000 +0000
+++ sendmail-8.17.1.9/debian/patches/series 2024-05-13 18:44:56.000000000 +0000
@@ -21,3 +21,6 @@
fhs.patch
typos.patch
log-stop-at-debug-level.patch
+0024-CVE-2023-51765.patch
+reject_nul.patch
+
Attachment:
signature.asc
Description: This is a digitally signed message part.