Your message dated Sat, 29 Jun 2024 10:47:48 +0000 with message-id <e1snvcs-002bts...@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.patchsignature.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 ---