--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
X-Debbugs-Cc: sendm...@packages.debian.org
Control: affects -1 + src:sendmail
User: release.debian....@packages.debian.org
Usertags: pu
[ Reason ]
Fix CVE-2023-51765 (smtp smugling)
[ Impact ]
SMTP smugling
[ Tests ]
Manual test using virtual machine
[ Risks ]
Low
[ 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 ]
* 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.
diff -Nru sendmail-8.15.2/debian/changelog sendmail-8.15.2/debian/changelog
--- sendmail-8.15.2/debian/changelog 2021-03-16 15:04:16.000000000 +0000
+++ sendmail-8.15.2/debian/changelog 2024-05-13 18:44:56.000000000 +0000
@@ -1,3 +1,24 @@
+sendmail (8.15.2-22+deb11u1) bullseye-security; urgency=medium
+
+ * 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 <ro...@debian.org> Mon, 13 May 2024 18:44:56 +0000
+
sendmail (8.15.2-22) unstable; urgency=medium
* QA upload.
diff -Nru sendmail-8.15.2/debian/configure.ac sendmail-8.15.2/debian/configure.ac
--- sendmail-8.15.2/debian/configure.ac 2021-03-16 15:04:16.000000000 +0000
+++ sendmail-8.15.2/debian/configure.ac 2024-05-13 18:44:56.000000000 +0000
@@ -468,6 +468,7 @@
sm_envdef="$sm_envdef -DHASFLOCK=0";
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.16"; then
diff -Nru sendmail-8.15.2/debian/NEWS.Debian sendmail-8.15.2/debian/NEWS.Debian
--- sendmail-8.15.2/debian/NEWS.Debian 1970-01-01 00:00:00.000000000 +0000
+++ sendmail-8.15.2/debian/NEWS.Debian 2024-05-13 18:44:56.000000000 +0000
@@ -0,0 +1,19 @@
+sendmail (8.18.1-3) unstable; 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 <ro...@debian.org> Sun, 12 May 2024 19:38:09 +0000
+
diff -Nru sendmail-8.15.2/debian/patches/0024-CVE-2023-51765.patch sendmail-8.15.2/debian/patches/0024-CVE-2023-51765.patch
--- sendmail-8.15.2/debian/patches/0024-CVE-2023-51765.patch 1970-01-01 00:00:00.000000000 +0000
+++ sendmail-8.15.2/debian/patches/0024-CVE-2023-51765.patch 2024-05-13 18:44:56.000000000 +0000
@@ -0,0 +1,1242 @@
+From: =?utf-8?q?Bastien_Roucari=C3=A8s?= <ro...@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 | 24 ++++-
+ libsm/lowercase.c | 168 +++++++++++++++++++++++++++++++++
+ sendmail/collect.c | 204 ++++++++++++++++++++++++++++++----------
+ sendmail/main.c | 5 +-
+ sendmail/mime.c | 8 +-
+ sendmail/sendmail.h | 19 +++-
+ sendmail/srvrsmtp.c | 265 ++++++++++++++++++++++++++++++++++++----------------
+ sendmail/usersmtp.c | 11 ++-
+ sendmail/util.c | 2 +-
+ 9 files changed, 563 insertions(+), 143 deletions(-)
+ create mode 100644 libsm/lowercase.c
+
+diff --git a/RELEASE_NOTES b/RELEASE_NOTES
+index 18a7cae..e1aeca9 100644
+--- a/RELEASE_NOTES
++++ b/RELEASE_NOTES
+@@ -5,6 +5,28 @@ 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.15.2/8.15.2 2015/07/03
+ If FEATURE(`nopercenthack') is used then some bogus input triggered
+ a recursion which was caught and logged as
+@@ -8173,7 +8195,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
+new file mode 100644
+index 0000000..f980d2f
+--- /dev/null
++++ b/libsm/lowercase.c
+@@ -0,0 +1,168 @@
++/*
++ * Copyright (c) 2020 Proofpoint, Inc. and its suppliers.
++ * All rights reserved.
++ *
++ * By using this file, you agree to the terms and conditions set
++ * forth in the LICENSE file which can be found at the top level of
++ * the sendmail distribution.
++ *
++ */
++
++#include <sm/gen.h>
++#include <sm/sendmail.h>
++
++#include <ctype.h>
++#include <sm/string.h>
++#include <sm/heap.h>
++#if USE_EAI
++# include <sm/ixlen.h>
++# include <unicode/ucasemap.h>
++# include <unicode/ustring.h>
++# include <unicode/uchar.h>
++
++/*
++** ASCIISTR -- check whether a string is printable ASCII
++**
++** Parameters:
++** str -- string
++**
++** Returns:
++** TRUE iff printable ASCII
++*/
++
++bool
++asciistr(str)
++ const char *str;
++{
++ unsigned char ch;
++
++ if (str == NULL)
++ return true;
++
++ SM_REQUIRE(len < INT_MAX);
++ n = 0;
++ while (n < len && (ch = (unsigned char)*str) != '\0'
++ && ch >= 32 && ch < 127)
++ {
++ n++;
++ str++;
++ return ch == '\0';
++}
++#endif /* USE_EAI */
++
++/*
++** MAKELOWER -- Translate a line into lower case
++**
++** Parameters:
++** p -- string to translate (modified in place if possible). [A]
++**
++** Returns:
++** lower cased string
++**
++** Side Effects:
++** String p is translated to lower case if possible.
++*/
++
++char *
++makelower(p)
++ char *p;
++{
++ char c;
++ char *orig;
++
++ if (p == NULL)
++ return p;
++ orig = p;
++#if USE_EAI
++ if (!asciistr(p))
++ return (char *)sm_lowercase(p);
++#endif
++ for (; (c = *p) != '\0'; p++)
++ if (isascii(c) && isupper(c))
++ *p = tolower(c);
++ return orig;
++}
++
++#if USE_EAI
++/*
++** SM_LOWERCASE -- lower case a UTF-8 string
++** Note: this should ONLY be applied to a UTF-8 string,
++** i.e., the caller should check first if it isn't an ASCII string.
++**
++** Parameters:
++** str -- original string
++**
++** Returns:
++** lower case version of string [S]
++**
++** How to return an error description due to failed unicode calls?
++** However, is that even relevant?
++*/
++
++char *
++sm_lowercase(str)
++ const char *str;
++{
++ int olen, ilen;
++ UErrorCode error;
++ ssize_t req;
++ int n;
++ static UCaseMap *csm = NULL;
++ static char *out = NULL;
++ static int outlen = 0;
++
++# if SM_CHECK_REQUIRE
++ if (sm_debug_active(&SmExpensiveRequire, 3))
++ SM_REQUIRE(!asciistr(str));
++# endif
++ /* an empty string is always ASCII */
++ SM_REQUIRE(NULL != str && '\0' != *str);
++
++ if (NULL == csm)
++ {
++ error = U_ZERO_ERROR;
++ csm = ucasemap_open("en_US", U_FOLD_CASE_DEFAULT, &error);
++ if (U_SUCCESS(error) == 0)
++ {
++ /* syserr("ucasemap_open error: %s", u_errorName(error)); */
++ return NULL;
++ }
++ }
++
++ ilen = strlen(str);
++ olen = ilen + 1;
++ if (olen > outlen)
++ {
++ outlen = olen;
++ out = sm_realloc_x(out, outlen);
++ }
++
++ for (n = 0; n < 3; n++)
++ {
++ error = U_ZERO_ERROR;
++ req = ucasemap_utf8FoldCase(csm, out, olen, str, ilen, &error);
++ if (U_SUCCESS(error))
++ {
++ if (req >= olen)
++ {
++ outlen = req + 1;
++ out = sm_realloc_x(out, outlen);
++ out[req] = '\0';
++ }
++ break;
++ }
++ else if (error == U_BUFFER_OVERFLOW_ERROR)
++ {
++ outlen = req + 1;
++ out = sm_realloc_x(out, outlen);
++ olen = outlen;
++ }
++ else
++ {
++ /* syserr("conversion error for \"%s\": %s", str, u_errorName(error)); */
++ return NULL;
++ }
++ }
++ return out;
++}
++#endif /* USE_EAI */
+diff --git a/sendmail/collect.c b/sendmail/collect.c
+index 5f090b2..ef3be9c 100644
+--- a/sendmail/collect.c
++++ b/sendmail/collect.c
+@@ -230,6 +230,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.
+ **
+@@ -265,20 +295,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;
+@@ -304,12 +340,26 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ #if _FFR_REJECT_NUL_BYTE
+ bool hasNUL; /* has at least one NUL input byte */
+ #endif /* _FFR_REJECT_NUL_BYTE */
++ 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);
+@@ -332,15 +382,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.
+@@ -356,7 +406,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 (;;)
+ {
+@@ -377,7 +427,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+
+ /* timeout? */
+ if (c == SM_IO_EOF && errno == EAGAIN
+- && smtpmode)
++ && SMTPMODE)
+ {
+ /*
+ ** Override e_message in
+@@ -415,15 +465,32 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ hasNUL = true;
+ #endif /* _FFR_REJECT_NUL_BYTE */
+ 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:
+@@ -435,11 +502,9 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ break;
+
+ case IS_DOT:
+- if (c == '\n' && !ignrdot &&
+- !bitset(EF_NL_NOT_EOL, e->e_flags))
+- goto readerr;
+- else if (c == '\r' &&
+- !bitset(EF_CRLF_NOT_EOL, e->e_flags))
++ if (c == '\n' && !ignrdot && !SMTPMODE_STRICT)
++ goto readdone;
++ else if (c == '\r')
+ {
+ istate = IS_DOTCR;
+ continue;
+@@ -460,7 +525,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 +548,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,8 +582,7 @@ collect(fp, smtpmode, hdrp, e, rsetsize)
+ istate = IS_CR;
+ continue;
+ }
+- else if (c == '\n' && !bitset(EF_NL_NOT_EOL,
+- e->e_flags))
++ else if (c == '\n' && !SMTPMODE_STRICT)
+ istate = IS_BOL;
+ else
+ istate = IS_NORM;
+@@ -525,7 +607,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:
+@@ -593,8 +676,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:
+@@ -625,7 +708,7 @@ nextstate:
+
+ /* timeout? */
+ if (c == SM_IO_EOF && errno == EAGAIN
+- && smtpmode)
++ && SMTPMODE)
+ {
+ /*
+ ** Override e_message in
+@@ -654,7 +737,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';
+@@ -674,7 +757,7 @@ nextstate:
+ sm_dprintf("EOH\n");
+
+ if (headeronly)
+- goto readerr;
++ goto readdone;
+
+ df = collect_eoh(e, numhdrs, hdrslen);
+ if (df == NULL)
+@@ -703,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;
+
+@@ -744,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 */
+@@ -809,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 */
+@@ -867,6 +960,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 df74288..c9bc158 100644
+--- a/sendmail/main.c
++++ b/sendmail/main.c
+@@ -2801,7 +2801,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 */
+ }
+@@ -2861,7 +2862,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 ecfc761..69171e0 100644
+--- a/sendmail/mime.c
++++ b/sendmail/mime.c
+@@ -346,7 +346,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))
+@@ -408,7 +408,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))
+@@ -482,7 +482,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))
+@@ -836,7 +836,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 b2d0211..28e76d4 100644
+--- a/sendmail/sendmail.h
++++ b/sendmail/sendmail.h
+@@ -983,7 +983,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 */
+@@ -1028,6 +1028,7 @@ struct envelope
+ #define EF_SPLIT 0x04000000L /* envelope has been split */
+ #define EF_UNSAFE 0x08000000L /* unsafe: read from untrusted source */
+ #define EF_TOODEEP 0x10000000L /* message is nested too deep */
++#define EF_7BITBODY 0x40000000L /* strip body to 7bit on input */
+
+ #define DLVR_NOTIFY 0x01
+ #define DLVR_RETURN 0x02
+@@ -2216,6 +2217,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])
+
+@@ -2672,7 +2678,7 @@ extern void cleanup_shm __P((bool));
+ #endif /* SM_CONF_SHM */
+ 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 bool connection_rate_check __P((SOCKADDR *, ENVELOPE *));
+ extern time_t convtime __P((char *, int));
+ extern char **copyplist __P((char **, bool, SM_RPOOL_T *));
+@@ -2855,6 +2861,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 */
++
+ #if _FFR_RCPTFLAGS
+ extern bool newmodmailer __P((ADDRESS *, int));
+ #endif
+diff --git a/sendmail/srvrsmtp.c b/sendmail/srvrsmtp.c
+index db056ae..b5f6d43 100644
+--- a/sendmail/srvrsmtp.c
++++ b/sendmail/srvrsmtp.c
+@@ -47,26 +47,32 @@ static bool NotFirstDelivery = false;
+ #endif /* _FFR_DM_ONE */
+
+ /* 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 /* _FFR_NO_PIPE */
+ #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 */
+-
+-static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
++#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 */
++#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 long srvfeatures __P((ENVELOPE *, char *, unsigned long));
+
+ #define STOP_ATTACK ((time_t) -1)
+ static time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
+@@ -74,6 +80,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
+ # if SASL >= 20000
+@@ -392,6 +399,39 @@ rcptmods(rcpt, e)
+ # define rcptmods(a, e)
+ #endif /* _FFR_RCPTFLAGS */
+
++/*
++** 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.
+ **
+@@ -552,7 +592,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"
+
+@@ -832,12 +872,10 @@ smtp(nullserver, d_flags, e)
+ bool saveSuprErrs;
+ time_t tlsstart;
+ #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 /* _FFR_NO_PIPE */
+-#endif /* PIPELINING */
++# endif
+ volatile time_t log_delay = (time_t) 0;
+ #if MILTER
+ volatile bool milter_cmd_done, milter_cmd_safe;
+@@ -894,8 +932,12 @@ smtp(nullserver, d_flags, e)
+ #endif /* PIPELINING */
+
+ 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)
+@@ -913,6 +955,7 @@ smtp(nullserver, d_flags, e)
+ #if PIPELINING
+ | SRV_OFFER_PIPE
+ #endif /* PIPELINING */
++ | 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
+@@ -932,15 +975,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;
+@@ -1307,46 +1348,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;
+-
+- /* 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;
+- }
++ struct timeval *tp; /* total pause */
++
++ /* 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)
++ if ((tp = channel_readable(InChannel, msecs)) != NULL)
+ {
+- sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
+- eoftest);
+- gettimeofday(&ep, NULL);
+- timersub(&ep, &bp, &tp);
+ 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)
+ );
+ }
+ }
+@@ -2343,6 +2361,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 */
+@@ -3181,7 +3223,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;
+
+@@ -3612,6 +3655,7 @@ doquit:
+ ** Parameters:
+ ** smtp -- status of SMTP connection.
+ ** e -- envelope.
++** check_stuffing -- check for transaction stuffing.
+ **
+ ** Returns:
+ ** true iff SMTP session can continue.
+@@ -3621,9 +3665,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;
+@@ -3635,7 +3680,7 @@ smtp_data(smtp, e)
+ ENVELOPE *ee;
+ char *id;
+ char *oldid;
+- unsigned int features;
++ unsigned long features;
+ char buf[32];
+
+ SmtpPhase = "server DATA";
+@@ -3649,6 +3694,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,
+@@ -3767,7 +3824,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));
+@@ -5186,35 +5249,40 @@ 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 },
+- { 'L', SRV_REQ_AUTH },
++ { '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 },
++ { 'L', SRV_REQ_AUTH, 0 },
+ #if PIPELINING
+ # if _FFR_NO_PIPE
+- { 'N', SRV_NO_PIPE },
++ { 'N', SRV_NO_PIPE, 0 },
+ # endif /* _FFR_NO_PIPE */
+- { 'P', SRV_OFFER_PIPE },
++ { 'P', SRV_OFFER_PIPE, 0 },
+ #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 }
++ { 'O', SRV_REQ_CRLF , 0 }, /* eOl */
++ { '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 },
++ { 'X', SRV_OFFER_EXPN,0 },
++/* { 'Y', SRV_OFFER_VRFY,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;
+@@ -5248,7 +5316,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;
+ }
+@@ -5257,9 +5325,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 24d38ee..25516a5 100644
+--- a/sendmail/usersmtp.c
++++ b/sendmail/usersmtp.c
+@@ -2275,6 +2275,9 @@ smtprcpt(to, m, mci, e, ctladdr, xstart)
+ {
+ char *bufp;
+ char optbuf[MAXLINE];
++#if PIPELINING
++ char *oldto;
++#endif
+
+ #if PIPELINING
+ /*
+@@ -2282,20 +2285,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);
++ giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
++ 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 9775915..5f5000a 100644
+--- a/sendmail/util.c
++++ b/sendmail/util.c
+@@ -944,7 +944,7 @@ makelower(p)
+ }
+
+ /*
+-** FIXCRLF -- fix <CR><LF> in line.
++** FIXCRLF -- fix CRLF in line.
+ **
+ ** Looks for the <CR><LF> combination and turns it into the
+ ** UNIX canonical <NL> character. It only takes one line,
diff -Nru sendmail-8.15.2/debian/patches/series sendmail-8.15.2/debian/patches/series
--- sendmail-8.15.2/debian/patches/series 2021-03-16 15:04:16.000000000 +0000
+++ sendmail-8.15.2/debian/patches/series 2024-05-13 18:44:56.000000000 +0000
@@ -25,3 +25,4 @@
connect-from-null.patch
log-stop-at-debug-level.patch
glibc-2.30.patch
+0024-CVE-2023-51765.patch
signature.asc
Description: This is a digitally signed message part.
--- End Message ---