Your message dated Sat, 29 Jun 2024 10:47:48 +0000
with message-id <e1snvcs-002btz...@coccia.debian.org>
and subject line Released with 11.10
has caused the Debian Bug report #1073231,
regarding bullseye-pu: package sendmail/8.15.2-22+deb11u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact ow...@bugs.debian.org
immediately.)


-- 
1073231: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1073231
Debian Bug Tracking System
Contact ow...@bugs.debian.org with problems
--- 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

Attachment: signature.asc
Description: This is a digitally signed message part.


--- End Message ---
--- Begin Message ---
Version: 11.10

The upload requested in this bug has been released as part of 11.10.

--- End Message ---

Reply via email to