From a6596ec37a4892e1d9c2498ecbfc4b8e6be5156a Mon Sep 17 00:00:00 2001
From: Wietse Venema
Date: Fri, 22 Dec 2023 00:00:00 -0500
Subject: [PATCH] postfix-3.6.13
---
Upstream-Status: Backport from [https://launchpad.net/ubuntu/+source/postfix/3.6.4-1ubuntu1.3]
CVE: CVE-2023-51764
Signed-off-by: Ashish Sharma
man/man5/postconf.5 | 55 +++++++++++++++++++++++++++++++++++++++++++++++
man/man8/smtpd.8 | 9 +++++++
mantools/postlink | 2 +
proto/postconf.proto | 52 ++++++++++++++++++++++++++++++++++++++++++++
src/global/mail_params.h | 11 ++++++++-
src/global/smtp_stream.c | 14 +++++++++++
src/global/smtp_stream.h | 2 +
src/smtpd/smtpd.c | 42 +++++++++++++++++++++++++++++++++++
8 files changed, 185 insertions(+), 2 deletions(-)
--- a/man/man5/postconf.5
+++ b/man/man5/postconf.5
@@ -10412,6 +10412,61 @@
parameter $name expansion.
.PP
This feature is available in Postfix 2.0 and later.
+.SH smtpd_forbid_bare_newline (default: Postfix < 3.9: no)
+Reply with "Error: bare received" and disconnect
+when a remote SMTP client sends a line ending in , violating
+the RFC 5321 requirement that lines must end in .
+This feature is disbled by default with Postfix < 3.9. Use
+smtpd_forbid_bare_newline_exclusions to exclude non\-standard clients
+such as netcat. Specify "smtpd_forbid_bare_newline = no" to disable
+(not recommended for an Internet\-connected MTA).
+.PP
+See
+https://www.postfix.org/smtp\-smuggling.html for details.
+.PP
+Example:
+.sp
+.in +4
+.nf
+.na
+.ft C
+# Disconnect remote SMTP clients that send bare newlines, but allow
+# local clients with non\-standard SMTP implementations such as netcat,
+# fax machines, or load balancer health checks.
+#
+smtpd_forbid_bare_newline = yes
+smtpd_forbid_bare_newline_exclusions = $mynetworks
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix >= 3.9, 3.8.4, 3.7.9,
+3.6.13, and 3.5.23.
+.SH smtpd_forbid_bare_newline_exclusions (default: $mynetworks)
+Exclude the specified clients from smtpd_forbid_bare_newline
+enforcement. It uses the same syntax and parent\-domain matching
+behavior as mynetworks.
+.PP
+Example:
+.sp
+.in +4
+.nf
+.na
+.ft C
+# Disconnect remote SMTP clients that send bare newlines, but allow
+# local clients with non\-standard SMTP implementations such as netcat,
+# fax machines, or load balancer health checks.
+#
+smtpd_forbid_bare_newline = yes
+smtpd_forbid_bare_newline_exclusions = $mynetworks
+.fi
+.ad
+.ft R
+.in -4
+.PP
+This feature is available in Postfix >= 3.9, 3.8.4, 3.7.9,
+3.6.13, and 3.5.23.
.SH smtpd_forbidden_commands (default: CONNECT, GET, POST)
List of commands that cause the Postfix SMTP server to immediately
terminate the session with a 221 code. This can be used to disconnect
--- a/man/man8/smtpd.8
+++ b/man/man8/smtpd.8
@@ -808,6 +808,15 @@
The maximal number of AUTH commands that any client is allowed to
send to this service per time unit, regardless of whether or not
Postfix actually accepts those commands.
+.PP
+Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later:
+.IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR"
+Reply with "Error: bare received" and disconnect
+when a remote SMTP client sends a line ending in , violating
+the RFC 5321 requirement that lines must end in .
+.IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR"
+Exclude the specified clients from smtpd_forbid_bare_newline
+enforcement.
.SH "TARPIT CONTROLS"
.na
.nf
--- a/mantools/postlink
+++ b/mantools/postlink
@@ -547,6 +547,8 @@
s;\bsmtpd_error_sleep_time\b;$&;g;
s;\bsmtpd_etrn_restrictions\b;$&;g;
s;\bsmtpd_expansion_filter\b;$&;g;
+ s;\bsmtpd_for[-]*\n*[ ]*bid_bare_newline\b;$&;g;
+ s;\bsmtpd_for[-]*\n*[ ]*bid_bare_newline_exclusions\b;$&;g;
s;\bsmtpd_for[-]*\n*[ ]*bidden_commands\b;$&;g;
s;\bsmtpd_hard_error_limit\b;$&;g;
s;\bsmtpd_helo_required\b;$&;g;
--- a/proto/postconf.proto
+++ b/proto/postconf.proto
@@ -18058,3 +18058,55 @@
name or port number.
This feature is available in Postfix 3.6 and later.
+
+%PARAM smtpd_forbid_bare_newline Postfix < 3.9: no
+
+ Reply with "Error: bare <LF> received" and disconnect
+when a remote SMTP client sends a line ending in <LF>, violating
+the RFC 5321 requirement that lines must end in <CR><LF>.
+This feature is disbled by default with Postfix < 3.9. Use
+smtpd_forbid_bare_newline_exclusions to exclude non-standard clients
+such as netcat. Specify "smtpd_forbid_bare_newline = no" to disable
+(not recommended for an Internet-connected MTA).
+
+ See
+https://www.postfix.org/smtp-smuggling.html for details.
+
+
Example:
+
+
+
+# Disconnect remote SMTP clients that send bare newlines, but allow
+# local clients with non-standard SMTP implementations such as netcat,
+# fax machines, or load balancer health checks.
+#
+smtpd_forbid_bare_newline = yes
+smtpd_forbid_bare_newline_exclusions = $mynetworks
+
+
+
+ This feature is available in Postfix ≥ 3.9, 3.8.4, 3.7.9,
+3.6.13, and 3.5.23.
+
+%PARAM smtpd_forbid_bare_newline_exclusions $mynetworks
+
+ Exclude the specified clients from smtpd_forbid_bare_newline
+enforcement. It uses the same syntax and parent-domain matching
+behavior as mynetworks.
+
+ Example:
+
+
+
+# Disconnect remote SMTP clients that send bare newlines, but allow
+# local clients with non-standard SMTP implementations such as netcat,
+# fax machines, or load balancer health checks.
+#
+smtpd_forbid_bare_newline = yes
+smtpd_forbid_bare_newline_exclusions = $mynetworks
+
+
+
+ This feature is available in Postfix ≥ 3.9, 3.8.4, 3.7.9,
+3.6.13, and 3.5.23.
+
--- a/src/global/mail_params.h
+++ b/src/global/mail_params.h
@@ -4170,7 +4170,16 @@
extern char *var_smtpd_dns_re_filter;
/*
- * Share TLS sessions through tlproxy(8).
+ * Backwards compatibility.
+ */
+#define VAR_SMTPD_FORBID_BARE_LF "smtpd_forbid_bare_newline"
+#define DEF_SMTPD_FORBID_BARE_LF 0
+
+#define VAR_SMTPD_FORBID_BARE_LF_EXCL "smtpd_forbid_bare_newline_exclusions"
+#define DEF_SMTPD_FORBID_BARE_LF_EXCL "$" VAR_MYNETWORKS
+
+ /*
+ * Share TLS sessions through tlsproxy(8).
*/
#define VAR_SMTP_TLS_CONN_REUSE "smtp_tls_connection_reuse"
#define DEF_SMTP_TLS_CONN_REUSE 0
--- a/src/global/smtp_stream.c
+++ b/src/global/smtp_stream.c
@@ -50,6 +50,8 @@
/* VSTREAM *stream;
/* char *format;
/* va_list ap;
+/*
+/* int smtp_forbid_bare_lf;
/* AUXILIARY API
/* int smtp_get_noexcept(vp, stream, maxlen, flags)
/* VSTRING *vp;
@@ -124,11 +126,16 @@
/* smtp_vprintf() is the machine underneath smtp_printf().
/*
/* smtp_get_noexcept() implements the subset of smtp_get()
-/* without timeouts and without making long jumps. Instead,
+/* without long jumps for timeout or EOF errors. Instead,
/* query the stream status with vstream_feof() etc.
+/* This function will make a VSTREAM long jump (error code
+/* SMTP_ERR_LF) when rejecting input with a bare newline byte.
/*
/* smtp_timeout_setup() is a backwards-compatibility interface
/* for programs that don't require per-record deadline support.
+/*
+/* smtp_forbid_bare_lf controls whether smtp_get_noexcept()
+/* will reject input with a bare newline byte.
/* DIAGNOSTICS
/* .fi
/* .ad
@@ -201,6 +208,8 @@
#include "smtp_stream.h"
+int smtp_forbid_bare_lf;
+
/* smtp_timeout_reset - reset per-stream error flags, restart deadline timer */
static void smtp_timeout_reset(VSTREAM *stream)
@@ -404,6 +413,9 @@
*/
case '\n':
vstring_truncate(vp, VSTRING_LEN(vp) - 1);
+ if (smtp_forbid_bare_lf
+ && (VSTRING_LEN(vp) == 0 || vstring_end(vp)[-1] != '\r'))
+ vstream_longjmp(stream, SMTP_ERR_LF);
while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
vstring_truncate(vp, VSTRING_LEN(vp) - 1);
VSTRING_TERMINATE(vp);
--- a/src/global/smtp_stream.h
+++ b/src/global/smtp_stream.h
@@ -32,6 +32,7 @@
#define SMTP_ERR_QUIET 3 /* silent cleanup (application) */
#define SMTP_ERR_NONE 4 /* non-error case */
#define SMTP_ERR_DATA 5 /* application data error */
+#define SMTP_ERR_LF 6 /* bare protocol error */
extern void smtp_stream_setup(VSTREAM *, int, int);
extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
@@ -43,6 +44,7 @@
extern void smtp_fwrite(const char *, ssize_t len, VSTREAM *);
extern void smtp_fread_buf(VSTRING *, ssize_t len, VSTREAM *);
extern void smtp_fputc(int, VSTREAM *);
+extern int smtp_forbid_bare_lf;
extern void smtp_vprintf(VSTREAM *, const char *, va_list);
--- a/src/smtpd/smtpd.c
+++ b/src/smtpd/smtpd.c
@@ -762,6 +762,15 @@
/* The maximal number of AUTH commands that any client is allowed to
/* send to this service per time unit, regardless of whether or not
/* Postfix actually accepts those commands.
+/* .PP
+/* Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later:
+/* .IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR"
+/* Reply with "Error: bare received" and disconnect
+/* when a remote SMTP client sends a line ending in , violating
+/* the RFC 5321 requirement that lines must end in .
+/* .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR"
+/* Exclude the specified clients from smtpd_forbid_bare_newline
+/* enforcement.
/* TARPIT CONTROLS
/* .ad
/* .fi
@@ -1467,6 +1476,10 @@
int var_smtpd_uproxy_tmout;
bool var_relay_before_rcpt_checks;
+bool var_smtpd_forbid_bare_lf;
+char *var_smtpd_forbid_bare_lf_excl;
+static NAMADR_LIST *bare_lf_excl;
+
/*
* Silly little macros.
*/
@@ -1541,6 +1554,7 @@
#define REASON_TIMEOUT "timeout"
#define REASON_LOST_CONNECTION "lost connection"
#define REASON_ERROR_LIMIT "too many errors"
+#define REASON_BARE_LF "bare received"
#ifdef USE_TLS
@@ -3967,6 +3981,7 @@
*/
done = 0;
do {
+ int payload_err;
/*
* Do not skip the smtp_fread_buf() call if read_len == 0. We still
@@ -3980,6 +3995,10 @@
smtp_fread_buf(state->buffer, read_len, state->client);
state->bdat_get_stream = vstream_memreopen(
state->bdat_get_stream, state->buffer, O_RDONLY);
+ vstream_control(state->bdat_get_stream, CA_VSTREAM_CTL_EXCEPT,
+ CA_VSTREAM_CTL_END);
+ if ((payload_err = vstream_setjmp(state->bdat_get_stream)) != 0)
+ vstream_longjmp(state->client, payload_err);
/*
* Read lines from the fragment. The last line may continue in the
@@ -4655,6 +4674,9 @@
*/
xclient_allowed =
namadr_list_match(xclient_hosts, state->name, state->addr);
+ smtp_forbid_bare_lf = SMTPD_STAND_ALONE((state)) == 0
+ && var_smtpd_forbid_bare_lf
+ && !namadr_list_match(bare_lf_excl, state->name, state->addr);
/* NOT: tls_reset() */
if (got_helo == 0)
helo_reset(state);
@@ -5446,6 +5468,13 @@
var_myhostname);
break;
+ case SMTP_ERR_LF:
+ state->reason = REASON_BARE_LF;
+ if (vstream_setjmp(state->client) == 0)
+ smtpd_chat_reply(state, "521 5.5.2 %s Error: bare received",
+ var_myhostname);
+ break;
+
case 0:
/*
@@ -5995,6 +6024,13 @@
namadr_list_match(xforward_hosts, state.name, state.addr);
/*
+ * Enforce strict SMTP line endings, with compatibility exclusions.
+ */
+ smtp_forbid_bare_lf = SMTPD_STAND_ALONE((&state)) == 0
+ && var_smtpd_forbid_bare_lf
+ && !namadr_list_match(bare_lf_excl, state.name, state.addr);
+
+ /*
* See if we need to turn on verbose logging for this client.
*/
debug_peer_check(state.name, state.addr);
@@ -6055,6 +6091,10 @@
hogger_list = namadr_list_init(VAR_SMTPD_HOGGERS, MATCH_FLAG_RETURN
| match_parent_style(VAR_SMTPD_HOGGERS),
var_smtpd_hoggers);
+ bare_lf_excl = namadr_list_init(VAR_SMTPD_FORBID_BARE_LF_EXCL,
+ MATCH_FLAG_RETURN
+ | match_parent_style(VAR_MYNETWORKS),
+ var_smtpd_forbid_bare_lf_excl);
/*
* Open maps before dropping privileges so we can read passwords etc.
@@ -6412,6 +6452,7 @@
VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup,
VAR_SMTPD_DELAY_OPEN, DEF_SMTPD_DELAY_OPEN, &var_smtpd_delay_open,
VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log,
+ VAR_SMTPD_FORBID_BARE_LF, DEF_SMTPD_FORBID_BARE_LF, &var_smtpd_forbid_bare_lf,
0,
};
static const CONFIG_NBOOL_TABLE nbool_table[] = {
@@ -6527,6 +6568,7 @@
VAR_SMTPD_POLICY_CONTEXT, DEF_SMTPD_POLICY_CONTEXT, &var_smtpd_policy_context, 0, 0,
VAR_SMTPD_DNS_RE_FILTER, DEF_SMTPD_DNS_RE_FILTER, &var_smtpd_dns_re_filter, 0, 0,
VAR_SMTPD_REJ_FTR_MAPS, DEF_SMTPD_REJ_FTR_MAPS, &var_smtpd_rej_ftr_maps, 0, 0,
+ VAR_SMTPD_FORBID_BARE_LF_EXCL, DEF_SMTPD_FORBID_BARE_LF_EXCL, &var_smtpd_forbid_bare_lf_excl, 0, 0,
0,
};
static const CONFIG_RAW_TABLE raw_table[] = {