summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAshish Sharma <asharma@mvista.com>2024-01-31 21:53:42 +0530
committerArmin Kuster <akuster808@gmail.com>2024-02-07 18:41:41 -0500
commit667850f0860379fe643beccfb9bb2a7adb036de8 (patch)
tree129e32349e961ea668a008f07cdc190159ca5df3
parentf81b181933f33e477c3e81af7f238f0724c05d33 (diff)
downloadmeta-openembedded-667850f0860379fe643beccfb9bb2a7adb036de8.tar.gz
postfix: Backport fix for CVE-2023-51764
Import patches from ubuntu launchpad fix CVE-2023-51764 Upstream-Status: Backport from [https://launchpad.net/ubuntu/+source/postfix/3.6.4-1ubuntu1.3] Signed-off-by: Ashish Sharma <asharma@mvista.com> Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r--meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-1.patch377
-rw-r--r--meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-2.patch978
-rw-r--r--meta-networking/recipes-daemons/postfix/postfix_3.6.7.bb2
3 files changed, 1357 insertions, 0 deletions
diff --git a/meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-1.patch b/meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-1.patch
new file mode 100644
index 0000000000..65436b704e
--- /dev/null
+++ b/meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-1.patch
@@ -0,0 +1,377 @@
1From a6596ec37a4892e1d9c2498ecbfc4b8e6be5156a Mon Sep 17 00:00:00 2001
2From: Wietse Venema <wietse@porcupine.org>
3Date: Fri, 22 Dec 2023 00:00:00 -0500
4Subject: [PATCH] postfix-3.6.13
5---
6Upstream-Status: Backport from [https://launchpad.net/ubuntu/+source/postfix/3.6.4-1ubuntu1.3]
7CVE: CVE-2023-51764
8Signed-off-by: Ashish Sharma <asharma@mvista.com>
9
10 man/man5/postconf.5 | 55 +++++++++++++++++++++++++++++++++++++++++++++++
11 man/man8/smtpd.8 | 9 +++++++
12 mantools/postlink | 2 +
13 proto/postconf.proto | 52 ++++++++++++++++++++++++++++++++++++++++++++
14 src/global/mail_params.h | 11 ++++++++-
15 src/global/smtp_stream.c | 14 +++++++++++
16 src/global/smtp_stream.h | 2 +
17 src/smtpd/smtpd.c | 42 +++++++++++++++++++++++++++++++++++
18 8 files changed, 185 insertions(+), 2 deletions(-)
19
20--- a/man/man5/postconf.5
21+++ b/man/man5/postconf.5
22@@ -10412,6 +10412,61 @@
23 parameter $name expansion.
24 .PP
25 This feature is available in Postfix 2.0 and later.
26+.SH smtpd_forbid_bare_newline (default: Postfix < 3.9: no)
27+Reply with "Error: bare <LF> received" and disconnect
28+when a remote SMTP client sends a line ending in <LF>, violating
29+the RFC 5321 requirement that lines must end in <CR><LF>.
30+This feature is disbled by default with Postfix < 3.9. Use
31+smtpd_forbid_bare_newline_exclusions to exclude non\-standard clients
32+such as netcat. Specify "smtpd_forbid_bare_newline = no" to disable
33+(not recommended for an Internet\-connected MTA).
34+.PP
35+See
36+https://www.postfix.org/smtp\-smuggling.html for details.
37+.PP
38+Example:
39+.sp
40+.in +4
41+.nf
42+.na
43+.ft C
44+# Disconnect remote SMTP clients that send bare newlines, but allow
45+# local clients with non\-standard SMTP implementations such as netcat,
46+# fax machines, or load balancer health checks.
47+#
48+smtpd_forbid_bare_newline = yes
49+smtpd_forbid_bare_newline_exclusions = $mynetworks
50+.fi
51+.ad
52+.ft R
53+.in -4
54+.PP
55+This feature is available in Postfix >= 3.9, 3.8.4, 3.7.9,
56+3.6.13, and 3.5.23.
57+.SH smtpd_forbid_bare_newline_exclusions (default: $mynetworks)
58+Exclude the specified clients from smtpd_forbid_bare_newline
59+enforcement. It uses the same syntax and parent\-domain matching
60+behavior as mynetworks.
61+.PP
62+Example:
63+.sp
64+.in +4
65+.nf
66+.na
67+.ft C
68+# Disconnect remote SMTP clients that send bare newlines, but allow
69+# local clients with non\-standard SMTP implementations such as netcat,
70+# fax machines, or load balancer health checks.
71+#
72+smtpd_forbid_bare_newline = yes
73+smtpd_forbid_bare_newline_exclusions = $mynetworks
74+.fi
75+.ad
76+.ft R
77+.in -4
78+.PP
79+This feature is available in Postfix >= 3.9, 3.8.4, 3.7.9,
80+3.6.13, and 3.5.23.
81 .SH smtpd_forbidden_commands (default: CONNECT, GET, POST)
82 List of commands that cause the Postfix SMTP server to immediately
83 terminate the session with a 221 code. This can be used to disconnect
84--- a/man/man8/smtpd.8
85+++ b/man/man8/smtpd.8
86@@ -808,6 +808,15 @@
87 The maximal number of AUTH commands that any client is allowed to
88 send to this service per time unit, regardless of whether or not
89 Postfix actually accepts those commands.
90+.PP
91+Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later:
92+.IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR"
93+Reply with "Error: bare <LF> received" and disconnect
94+when a remote SMTP client sends a line ending in <LF>, violating
95+the RFC 5321 requirement that lines must end in <CR><LF>.
96+.IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR"
97+Exclude the specified clients from smtpd_forbid_bare_newline
98+enforcement.
99 .SH "TARPIT CONTROLS"
100 .na
101 .nf
102--- a/mantools/postlink
103+++ b/mantools/postlink
104@@ -547,6 +547,8 @@
105 s;\bsmtpd_error_sleep_time\b;<a href="postconf.5.html#smtpd_error_sleep_time">$&</a>;g;
106 s;\bsmtpd_etrn_restrictions\b;<a href="postconf.5.html#smtpd_etrn_restrictions">$&</a>;g;
107 s;\bsmtpd_expansion_filter\b;<a href="postconf.5.html#smtpd_expansion_filter">$&</a>;g;
108+ s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_newline\b;<a href="postconf.5.html#smtpd_forbi d_bare_newline">$&</a>;g;
109+ s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_newline_exclusions\b;<a href="postconf.5.html# smtpd_forbid_bare_newline_exclusions">$&</a>;g;
110 s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bidden_commands\b;<a href="postconf.5.html#smtpd_forbidden_commands">$&</a>;g;
111 s;\bsmtpd_hard_error_limit\b;<a href="postconf.5.html#smtpd_hard_error_limit">$&</a>;g;
112 s;\bsmtpd_helo_required\b;<a href="postconf.5.html#smtpd_helo_required">$&</a>;g;
113--- a/proto/postconf.proto
114+++ b/proto/postconf.proto
115@@ -18058,3 +18058,55 @@
116 name or port number. </p>
117
118 <p> This feature is available in Postfix 3.6 and later. </p>
119+
120+%PARAM smtpd_forbid_bare_newline Postfix &lt; 3.9: no
121+
122+<p> Reply with "Error: bare &lt;LF&gt; received" and disconnect
123+when a remote SMTP client sends a line ending in &lt;LF&gt;, violating
124+the RFC 5321 requirement that lines must end in &lt;CR&gt;&lt;LF&gt;.
125+This feature is disbled by default with Postfix &lt; 3.9. Use
126+smtpd_forbid_bare_newline_exclusions to exclude non-standard clients
127+such as netcat. Specify "smtpd_forbid_bare_newline = no" to disable
128+(not recommended for an Internet-connected MTA). </p>
129+
130+<p> See <a href="https://www.postfix.org/smtp-smuggling.html">
131+https://www.postfix.org/smtp-smuggling.html</a> for details.
132+
133+<p> Example: </p>
134+
135+<blockquote>
136+<pre>
137+# Disconnect remote SMTP clients that send bare newlines, but allow
138+# local clients with non-standard SMTP implementations such as netcat,
139+# fax machines, or load balancer health checks.
140+#
141+smtpd_forbid_bare_newline = yes
142+smtpd_forbid_bare_newline_exclusions = $mynetworks
143+</pre>
144+</blockquote>
145+
146+<p> This feature is available in Postfix &ge; 3.9, 3.8.4, 3.7.9,
147+3.6.13, and 3.5.23. </p>
148+
149+%PARAM smtpd_forbid_bare_newline_exclusions $mynetworks
150+
151+<p> Exclude the specified clients from smtpd_forbid_bare_newline
152+enforcement. It uses the same syntax and parent-domain matching
153+behavior as mynetworks. </p>
154+
155+<p> Example: </p>
156+
157+<blockquote>
158+<pre>
159+# Disconnect remote SMTP clients that send bare newlines, but allow
160+# local clients with non-standard SMTP implementations such as netcat,
161+# fax machines, or load balancer health checks.
162+#
163+smtpd_forbid_bare_newline = yes
164+smtpd_forbid_bare_newline_exclusions = $mynetworks
165+</pre>
166+</blockquote>
167+
168+<p> This feature is available in Postfix &ge; 3.9, 3.8.4, 3.7.9,
169+3.6.13, and 3.5.23. </p>
170+
171--- a/src/global/mail_params.h
172+++ b/src/global/mail_params.h
173@@ -4170,7 +4170,16 @@
174 extern char *var_smtpd_dns_re_filter;
175
176 /*
177- * Share TLS sessions through tlproxy(8).
178+ * Backwards compatibility.
179+ */
180+#define VAR_SMTPD_FORBID_BARE_LF "smtpd_forbid_bare_newline"
181+#define DEF_SMTPD_FORBID_BARE_LF 0
182+
183+#define VAR_SMTPD_FORBID_BARE_LF_EXCL "smtpd_forbid_bare_newline_exclusions"
184+#define DEF_SMTPD_FORBID_BARE_LF_EXCL "$" VAR_MYNETWORKS
185+
186+ /*
187+ * Share TLS sessions through tlsproxy(8).
188 */
189 #define VAR_SMTP_TLS_CONN_REUSE "smtp_tls_connection_reuse"
190 #define DEF_SMTP_TLS_CONN_REUSE 0
191--- a/src/global/smtp_stream.c
192+++ b/src/global/smtp_stream.c
193@@ -50,6 +50,8 @@
194 /* VSTREAM *stream;
195 /* char *format;
196 /* va_list ap;
197+/*
198+/* int smtp_forbid_bare_lf;
199 /* AUXILIARY API
200 /* int smtp_get_noexcept(vp, stream, maxlen, flags)
201 /* VSTRING *vp;
202@@ -124,11 +126,16 @@
203 /* smtp_vprintf() is the machine underneath smtp_printf().
204 /*
205 /* smtp_get_noexcept() implements the subset of smtp_get()
206-/* without timeouts and without making long jumps. Instead,
207+/* without long jumps for timeout or EOF errors. Instead,
208 /* query the stream status with vstream_feof() etc.
209+/* This function will make a VSTREAM long jump (error code
210+/* SMTP_ERR_LF) when rejecting input with a bare newline byte.
211 /*
212 /* smtp_timeout_setup() is a backwards-compatibility interface
213 /* for programs that don't require per-record deadline support.
214+/*
215+/* smtp_forbid_bare_lf controls whether smtp_get_noexcept()
216+/* will reject input with a bare newline byte.
217 /* DIAGNOSTICS
218 /* .fi
219 /* .ad
220@@ -201,6 +208,8 @@
221
222 #include "smtp_stream.h"
223
224+int smtp_forbid_bare_lf;
225+
226 /* smtp_timeout_reset - reset per-stream error flags, restart deadline timer */
227
228 static void smtp_timeout_reset(VSTREAM *stream)
229@@ -404,6 +413,9 @@
230 */
231 case '\n':
232 vstring_truncate(vp, VSTRING_LEN(vp) - 1);
233+ if (smtp_forbid_bare_lf
234+ && (VSTRING_LEN(vp) == 0 || vstring_end(vp)[-1] != '\r'))
235+ vstream_longjmp(stream, SMTP_ERR_LF);
236 while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
237 vstring_truncate(vp, VSTRING_LEN(vp) - 1);
238 VSTRING_TERMINATE(vp);
239--- a/src/global/smtp_stream.h
240+++ b/src/global/smtp_stream.h
241@@ -32,6 +32,7 @@
242 #define SMTP_ERR_QUIET 3 /* silent cleanup (application) */
243 #define SMTP_ERR_NONE 4 /* non-error case */
244 #define SMTP_ERR_DATA 5 /* application data error */
245+#define SMTP_ERR_LF 6 /* bare <LF> protocol error */
246
247 extern void smtp_stream_setup(VSTREAM *, int, int);
248 extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
249@@ -43,6 +44,7 @@
250 extern void smtp_fwrite(const char *, ssize_t len, VSTREAM *);
251 extern void smtp_fread_buf(VSTRING *, ssize_t len, VSTREAM *);
252 extern void smtp_fputc(int, VSTREAM *);
253+extern int smtp_forbid_bare_lf;
254
255 extern void smtp_vprintf(VSTREAM *, const char *, va_list);
256
257--- a/src/smtpd/smtpd.c
258+++ b/src/smtpd/smtpd.c
259@@ -762,6 +762,15 @@
260 /* The maximal number of AUTH commands that any client is allowed to
261 /* send to this service per time unit, regardless of whether or not
262 /* Postfix actually accepts those commands.
263+/* .PP
264+/* Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later:
265+/* .IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR"
266+/* Reply with "Error: bare <LF> received" and disconnect
267+/* when a remote SMTP client sends a line ending in <LF>, violating
268+/* the RFC 5321 requirement that lines must end in <CR><LF>.
269+/* .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR"
270+/* Exclude the specified clients from smtpd_forbid_bare_newline
271+/* enforcement.
272 /* TARPIT CONTROLS
273 /* .ad
274 /* .fi
275@@ -1467,6 +1476,10 @@
276 int var_smtpd_uproxy_tmout;
277 bool var_relay_before_rcpt_checks;
278
279+bool var_smtpd_forbid_bare_lf;
280+char *var_smtpd_forbid_bare_lf_excl;
281+static NAMADR_LIST *bare_lf_excl;
282+
283 /*
284 * Silly little macros.
285 */
286@@ -1541,6 +1554,7 @@
287 #define REASON_TIMEOUT "timeout"
288 #define REASON_LOST_CONNECTION "lost connection"
289 #define REASON_ERROR_LIMIT "too many errors"
290+#define REASON_BARE_LF "bare <LF> received"
291
292 #ifdef USE_TLS
293
294@@ -3967,6 +3981,7 @@
295 */
296 done = 0;
297 do {
298+ int payload_err;
299
300 /*
301 * Do not skip the smtp_fread_buf() call if read_len == 0. We still
302@@ -3980,6 +3995,10 @@
303 smtp_fread_buf(state->buffer, read_len, state->client);
304 state->bdat_get_stream = vstream_memreopen(
305 state->bdat_get_stream, state->buffer, O_RDONLY);
306+ vstream_control(state->bdat_get_stream, CA_VSTREAM_CTL_EXCEPT,
307+ CA_VSTREAM_CTL_END);
308+ if ((payload_err = vstream_setjmp(state->bdat_get_stream)) != 0)
309+ vstream_longjmp(state->client, payload_err);
310
311 /*
312 * Read lines from the fragment. The last line may continue in the
313@@ -4655,6 +4674,9 @@
314 */
315 xclient_allowed =
316 namadr_list_match(xclient_hosts, state->name, state->addr);
317+ smtp_forbid_bare_lf = SMTPD_STAND_ALONE((state)) == 0
318+ && var_smtpd_forbid_bare_lf
319+ && !namadr_list_match(bare_lf_excl, state->name, state->addr);
320 /* NOT: tls_reset() */
321 if (got_helo == 0)
322 helo_reset(state);
323@@ -5446,6 +5468,13 @@
324 var_myhostname);
325 break;
326
327+ case SMTP_ERR_LF:
328+ state->reason = REASON_BARE_LF;
329+ if (vstream_setjmp(state->client) == 0)
330+ smtpd_chat_reply(state, "521 5.5.2 %s Error: bare <LF> received",
331+ var_myhostname);
332+ break;
333+
334 case 0:
335
336 /*
337@@ -5995,6 +6024,13 @@
338 namadr_list_match(xforward_hosts, state.name, state.addr);
339
340 /*
341+ * Enforce strict SMTP line endings, with compatibility exclusions.
342+ */
343+ smtp_forbid_bare_lf = SMTPD_STAND_ALONE((&state)) == 0
344+ && var_smtpd_forbid_bare_lf
345+ && !namadr_list_match(bare_lf_excl, state.name, state.addr);
346+
347+ /*
348 * See if we need to turn on verbose logging for this client.
349 */
350 debug_peer_check(state.name, state.addr);
351@@ -6055,6 +6091,10 @@
352 hogger_list = namadr_list_init(VAR_SMTPD_HOGGERS, MATCH_FLAG_RETURN
353 | match_parent_style(VAR_SMTPD_HOGGERS),
354 var_smtpd_hoggers);
355+ bare_lf_excl = namadr_list_init(VAR_SMTPD_FORBID_BARE_LF_EXCL,
356+ MATCH_FLAG_RETURN
357+ | match_parent_style(VAR_MYNETWORKS),
358+ var_smtpd_forbid_bare_lf_excl);
359
360 /*
361 * Open maps before dropping privileges so we can read passwords etc.
362@@ -6412,6 +6452,7 @@
363 VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup,
364 VAR_SMTPD_DELAY_OPEN, DEF_SMTPD_DELAY_OPEN, &var_smtpd_delay_open,
365 VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log,
366+ VAR_SMTPD_FORBID_BARE_LF, DEF_SMTPD_FORBID_BARE_LF, &var_smtpd_forbid_bare_lf,
367 0,
368 };
369 static const CONFIG_NBOOL_TABLE nbool_table[] = {
370@@ -6527,6 +6568,7 @@
371 VAR_SMTPD_POLICY_CONTEXT, DEF_SMTPD_POLICY_CONTEXT, &var_smtpd_policy_context, 0, 0,
372 VAR_SMTPD_DNS_RE_FILTER, DEF_SMTPD_DNS_RE_FILTER, &var_smtpd_dns_re_filter, 0, 0,
373 VAR_SMTPD_REJ_FTR_MAPS, DEF_SMTPD_REJ_FTR_MAPS, &var_smtpd_rej_ftr_maps, 0, 0,
374+ VAR_SMTPD_FORBID_BARE_LF_EXCL, DEF_SMTPD_FORBID_BARE_LF_EXCL, &var_smtpd_forbid_bare_lf_excl, 0, 0,
375 0,
376 };
377 static const CONFIG_RAW_TABLE raw_table[] = {
diff --git a/meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-2.patch b/meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-2.patch
new file mode 100644
index 0000000000..e97a088557
--- /dev/null
+++ b/meta-networking/recipes-daemons/postfix/files/CVE-2023-51764-2.patch
@@ -0,0 +1,978 @@
1From cb3b1cbda3dec086a7f4541fe64751d9bb2988bd Mon Sep 17 00:00:00 2001
2From: Wietse Venema <wietse@porcupine.org>
3Date: Sun, 21 Jan 2024 00:00:00 -0500
4Subject: [PATCH] postfix-3.6.14
5
6---
7
8Upstream-Status: Backport from [https://launchpad.net/ubuntu/+source/postfix/3.6.4-1ubuntu1.3]
9CVE: CVE-2023-51764
10Signed-off-by: Ashish Sharma <asharma@mvista.com>
11
12 man/man5/postconf.5 | 173 +++++++++++++++++++++++++++++++++++-------
13 man/man8/cleanup.8 | 8 +
14 man/man8/smtpd.8 | 11 +-
15 mantools/postlink | 6 -
16 proto/postconf.proto | 142 +++++++++++++++++++++++++++-------
17 src/cleanup/cleanup.c | 8 +
18 src/cleanup/cleanup_init.c | 2
19 src/cleanup/cleanup_message.c | 17 ++++
20 src/global/cleanup_strerror.c | 1
21 src/global/cleanup_user.h | 6 +
22 src/global/mail_params.h | 9 +-
23 src/global/smtp_stream.c | 34 +++++---
24 src/global/smtp_stream.h | 4
25 src/smtpd/smtpd.c | 114 ++++++++++++++++++++-------
26 src/smtpd/smtpd_check.c | 14 ++-
27 src/smtpd/smtpd_check.h | 1
28 16 files changed, 443 insertions(+), 107 deletions(-)
29
30--- a/man/man5/postconf.5
31+++ b/man/man5/postconf.5
32@@ -845,6 +845,32 @@
33 .fi
34 .ad
35 .ft R
36+.SH cleanup_replace_stray_cr_lf (default: yes)
37+Replace each stray <CR> or <LF> character in message
38+content with a space character, to prevent outbound SMTP smuggling,
39+and to make the evaluation of Postfix\-added DKIM or other signatures
40+independent from how a remote mail server handles such characters.
41+.PP
42+SMTP does not allow such characters unless they are part of a
43+<CR><LF> sequence, and different mail systems handle
44+such stray characters in an implementation\-dependent manner. Stray
45+<CR> or <LF> characters could be used for outbound
46+SMTP smuggling, where an attacker uses a Postfix server to send
47+message content with a non\-standard End\-of\-DATA sequence that
48+triggers inbound SMTP smuggling at a remote SMTP server.
49+.PP
50+The replacement happens before all other content management,
51+and before Postfix may add a DKIM etc. signature; if the signature
52+were created first, the replacement could invalidate the signature.
53+.PP
54+In addition to preventing SMTP smuggling, replacing stray
55+<CR> or <LF> characters ensures that the result of
56+signature validation by later mail system will not depend on how
57+that mail system handles those stray characters in an
58+implementation\-dependent manner.
59+.PP
60+This feature is available in Postfix >= 3.9, 3.8.5, 3.7.10,
61+3.6.14, and 3.5.24.
62 .SH cleanup_service_name (default: cleanup)
63 The name of the \fBcleanup\fR(8) service. This service rewrites addresses
64 into the standard form, and performs \fBcanonical\fR(5) address mapping
65@@ -10413,60 +10439,153 @@
66 .PP
67 This feature is available in Postfix 2.0 and later.
68 .SH smtpd_forbid_bare_newline (default: Postfix < 3.9: no)
69-Reply with "Error: bare <LF> received" and disconnect
70-when a remote SMTP client sends a line ending in <LF>, violating
71-the RFC 5321 requirement that lines must end in <CR><LF>.
72-This feature is disbled by default with Postfix < 3.9. Use
73-smtpd_forbid_bare_newline_exclusions to exclude non\-standard clients
74-such as netcat. Specify "smtpd_forbid_bare_newline = no" to disable
75-(not recommended for an Internet\-connected MTA).
76-.PP
77-See
78-https://www.postfix.org/smtp\-smuggling.html for details.
79+Reject or restrict input lines from an SMTP client that end in
80+<LF> instead of the standard <CR><LF>. Such line
81+endings are commonly allowed with UNIX\-based SMTP servers, but they
82+violate RFC 5321, and allowing such line endings can make a server
83+vulnerable to
84+SMTP smuggling.
85+.PP
86+Specify one of the following values (case does not matter):
87+.IP "\fBnormalize\fR"
88+Require the standard
89+End\-of\-DATA sequence <CR><LF>.<CR><LF>.
90+Otherwise, allow command or message content lines ending in the
91+non\-standard <LF>, and process them as if the client sent the
92+standard <CR><LF>.
93+.br
94+.br
95+This maintains compatibility
96+with many legitimate SMTP client applications that send a mix of
97+standard and non\-standard line endings, but will fail to receive
98+email from client implementations that do not terminate DATA content
99+with the standard End\-of\-DATA sequence
100+<CR><LF>.<CR><LF>.
101+.br
102+.br
103+Such clients
104+can be excluded with smtpd_forbid_bare_newline_exclusions.
105+.br
106+.IP "\fByes\fR"
107+Compatibility alias for \fBnormalize\fR.
108+.br
109+.IP "\fBreject\fR"
110+Require the standard End\-of\-DATA
111+sequence <CR><LF>.<CR><LF>. Reject a command
112+or message content when a line contains bare <LF>, log a "bare
113+<LF> received" error, and reply with the SMTP status code in
114+$smtpd_forbid_bare_newline_reject_code.
115+.br
116+.br
117+This will reject
118+email from SMTP clients that send any non\-standard line endings
119+such as web applications, netcat, or load balancer health checks.
120+.br
121+.br
122+This will also reject email from services that use BDAT
123+to send MIME text containing a bare newline (RFC 3030 Section 3
124+requires canonical MIME format for text message types, defined in
125+RFC 2045 Sections 2.7 and 2.8).
126+.br
127+.br
128+Such clients can be
129+excluded with smtpd_forbid_bare_newline_exclusions (or, in the case
130+of BDAT violations, BDAT can be selectively disabled with
131+smtpd_discard_ehlo_keyword_address_maps, or globally disabled with
132+smtpd_discard_ehlo_keywords).
133+.br
134+.IP "\fBno\fR (default)"
135+Do not require the standard
136+End\-of\-DATA
137+sequence <CR><LF>.<CR><LF>. Always process
138+a bare <LF> as if the client sent <CR><LF>. This
139+option is fully backwards compatible, but is not recommended for
140+an Internet\-facing SMTP server, because it is vulnerable to SMTP smuggling.
141+.br
142+.br
143 .PP
144-Example:
145+Recommended settings:
146 .sp
147 .in +4
148 .nf
149 .na
150 .ft C
151-# Disconnect remote SMTP clients that send bare newlines, but allow
152-# local clients with non\-standard SMTP implementations such as netcat,
153-# fax machines, or load balancer health checks.
154+# Require the standard End\-of\-DATA sequence <CR><LF>.<CR><LF>.
155+# Otherwise, allow bare <LF> and process it as if the client sent
156+# <CR><LF>.
157 #
158-smtpd_forbid_bare_newline = yes
159+# This maintains compatibility with many legitimate SMTP client
160+# applications that send a mix of standard and non\-standard line
161+# endings, but will fail to receive email from client implementations
162+# that do not terminate DATA content with the standard End\-of\-DATA
163+# sequence <CR><LF>.<CR><LF>.
164+#
165+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
166+# The example below allowlists SMTP clients in trusted networks.
167+#
168+smtpd_forbid_bare_newline = normalize
169 smtpd_forbid_bare_newline_exclusions = $mynetworks
170 .fi
171 .ad
172 .ft R
173 .in -4
174 .PP
175-This feature is available in Postfix >= 3.9, 3.8.4, 3.7.9,
176-3.6.13, and 3.5.23.
177-.SH smtpd_forbid_bare_newline_exclusions (default: $mynetworks)
178-Exclude the specified clients from smtpd_forbid_bare_newline
179-enforcement. It uses the same syntax and parent\-domain matching
180-behavior as mynetworks.
181-.PP
182-Example:
183+Alternative:
184 .sp
185 .in +4
186 .nf
187 .na
188 .ft C
189-# Disconnect remote SMTP clients that send bare newlines, but allow
190-# local clients with non\-standard SMTP implementations such as netcat,
191-# fax machines, or load balancer health checks.
192+# Reject input lines that contain <LF> and log a "bare <LF> received"
193+# error. Require that input lines end in <CR><LF>, and require the
194+# standard End\-of\-DATA sequence <CR><LF>.<CR><LF>.
195+#
196+# This will reject email from SMTP clients that send any non\-standard
197+# line endings such as web applications, netcat, or load balancer
198+# health checks.
199 #
200-smtpd_forbid_bare_newline = yes
201+# This will also reject email from services that use BDAT to send
202+# MIME text containing a bare newline (RFC 3030 Section 3 requires
203+# canonical MIME format for text message types, defined in RFC 2045
204+# Sections 2.7 and 2.8).
205+#
206+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
207+# The example below allowlists SMTP clients in trusted networks.
208+#
209+smtpd_forbid_bare_newline = reject
210 smtpd_forbid_bare_newline_exclusions = $mynetworks
211+#
212+# Alternatively, in the case of BDAT violations, BDAT can be selectively
213+# disabled with smtpd_discard_ehlo_keyword_address_maps, or globally
214+# disabled with smtpd_discard_ehlo_keywords.
215+#
216+# smtpd_discard_ehlo_keyword_address_maps = cidr:/path/to/file
217+# /path/to/file:
218+# 10.0.0.0/24 chunking, silent\-discard
219+# smtpd_discard_ehlo_keywords = chunking, silent\-discard
220 .fi
221 .ad
222 .ft R
223 .in -4
224 .PP
225+This feature with settings \fByes\fR and \fBno\fR is available
226+in Postfix 3.8.4, 3.7.9, 3.6.13, and 3.5.23. Additionally, the
227+settings \fBreject\fR, and \fBnormalize\fR are available with
228+Postfix >= 3.9, 3.8.5, 3.7.10, 3.6.14, and 3.5.24.
229+.SH smtpd_forbid_bare_newline_exclusions (default: $mynetworks)
230+Exclude the specified clients from smtpd_forbid_bare_newline
231+enforcement. This setting uses the same syntax and parent\-domain
232+matching behavior as mynetworks.
233+.PP
234 This feature is available in Postfix >= 3.9, 3.8.4, 3.7.9,
235 3.6.13, and 3.5.23.
236+.SH smtpd_forbid_bare_newline_reject_code (default: 550)
237+The numerical Postfix SMTP server response code when rejecting a
238+request with "smtpd_forbid_bare_newline = reject".
239+Specify a 5XX status code (521 to disconnect).
240+.PP
241+This feature is available in Postfix >= 3.9, 3.8.5, 3.7.10,
242+3.6.14, and 3.5.24.
243 .SH smtpd_forbidden_commands (default: CONNECT, GET, POST)
244 List of commands that cause the Postfix SMTP server to immediately
245 terminate the session with a 221 code. This can be used to disconnect
246--- a/man/man8/cleanup.8
247+++ b/man/man8/cleanup.8
248@@ -163,6 +163,14 @@
249 .IP "\fBmessage_strip_characters (empty)\fR"
250 The set of characters that Postfix will remove from message
251 content.
252+.PP
253+Available in Postfix version 3.9, 3.8.5, 3.7.10, 3.6.14,
254+3.5.24, and later:
255+.IP "\fBcleanup_replace_stray_cr_lf (yes)\fR"
256+Replace each stray <CR> or <LF> character in message
257+content with a space character, to prevent outbound SMTP smuggling,
258+and to make the evaluation of Postfix\-added DKIM or other signatures
259+independent from how a remote mail server handles such characters.
260 .SH "BEFORE QUEUE MILTER CONTROLS"
261 .na
262 .nf
263--- a/man/man8/smtpd.8
264+++ b/man/man8/smtpd.8
265@@ -811,12 +811,17 @@
266 .PP
267 Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later:
268 .IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR"
269-Reply with "Error: bare <LF> received" and disconnect
270-when a remote SMTP client sends a line ending in <LF>, violating
271-the RFC 5321 requirement that lines must end in <CR><LF>.
272+Reject or restrict input lines from an SMTP client that end in
273+<LF> instead of the standard <CR><LF>.
274 .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR"
275 Exclude the specified clients from smtpd_forbid_bare_newline
276 enforcement.
277+.PP
278+Available in Postfix 3.9, 3.8.5, 3.7.10, 3.6.14, 3.5.24 and
279+later:
280+.IP "\fBsmtpd_forbid_bare_newline_reject_code (550)\fR"
281+The numerical Postfix SMTP server response code when rejecting a
282+request with "smtpd_forbid_bare_newline = reject".
283 .SH "TARPIT CONTROLS"
284 .na
285 .nf
286--- a/mantools/postlink
287+++ b/mantools/postlink
288@@ -547,8 +547,10 @@
289 s;\bsmtpd_error_sleep_time\b;<a href="postconf.5.html#smtpd_error_sleep_time">$&</a>;g;
290 s;\bsmtpd_etrn_restrictions\b;<a href="postconf.5.html#smtpd_etrn_restrictions">$&</a>;g;
291 s;\bsmtpd_expansion_filter\b;<a href="postconf.5.html#smtpd_expansion_filter">$&</a>;g;
292- s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_newline\b;<a href="postconf.5.html#smtpd_forbi d_bare_newline">$&</a>;g;
293- s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_newline_exclusions\b;<a href="postconf.5.html# smtpd_forbid_bare_newline_exclusions">$&</a>;g;
294+ s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_new[-</bB>]*\n*[ <bB>]*line\b;<a href="postconf.5.html#smtpd_forbid_bare_newline">$&</a>;g;
295+ s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_new[-</bB>]*\n*[ <bB>]*line_reject_code\b;<a href="postconf.5.html#smtpd_forbid_bare_newline_reject_code">$&</a>;g;
296+ s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bid_bare_new[-</bB>]*\n*[ <bB>]*line_exclusions\b;<a href="postconf.5.html#smtpd_forbid_bare_newline_exclusions">$&</a>;g;
297+ s;\bcleanup_replace_stray_cr_lf\b;<a href="postconf.5.html#cleanup_replace_stray_cr_lf">$&</a>;g;
298 s;\bsmtpd_for[-</bB>]*\n*[ <bB>]*bidden_commands\b;<a href="postconf.5.html#smtpd_forbidden_commands">$&</a>;g;
299 s;\bsmtpd_hard_error_limit\b;<a href="postconf.5.html#smtpd_hard_error_limit">$&</a>;g;
300 s;\bsmtpd_helo_required\b;<a href="postconf.5.html#smtpd_helo_required">$&</a>;g;
301--- a/proto/postconf.proto
302+++ b/proto/postconf.proto
303@@ -18061,52 +18061,138 @@
304
305 %PARAM smtpd_forbid_bare_newline Postfix &lt; 3.9: no
306
307-<p> Reply with "Error: bare &lt;LF&gt; received" and disconnect
308-when a remote SMTP client sends a line ending in &lt;LF&gt;, violating
309-the RFC 5321 requirement that lines must end in &lt;CR&gt;&lt;LF&gt;.
310-This feature is disbled by default with Postfix &lt; 3.9. Use
311-smtpd_forbid_bare_newline_exclusions to exclude non-standard clients
312-such as netcat. Specify "smtpd_forbid_bare_newline = no" to disable
313-(not recommended for an Internet-connected MTA). </p>
314+<p> Reject or restrict input lines from an SMTP client that end in
315+&lt;LF&gt; instead of the standard &lt;CR&gt;&lt;LF&gt;. Such line
316+endings are commonly allowed with UNIX-based SMTP servers, but they
317+violate RFC 5321, and allowing such line endings can make a server
318+vulnerable to <a href="https://www.postfix.org/smtp-smuggling.html">
319+SMTP smuggling</a>. </p>
320+
321+<p> Specify one of the following values (case does not matter): </p>
322+
323+<dl compact>
324+
325+<dt> <b>normalize</b></dt> <dd> Require the standard
326+End-of-DATA sequence &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;.
327+Otherwise, allow command or message content lines ending in the
328+non-standard &lt;LF&gt;, and process them as if the client sent the
329+standard &lt;CR&gt;&lt;LF&gt;. <br> <br> This maintains compatibility
330+with many legitimate SMTP client applications that send a mix of
331+standard and non-standard line endings, but will fail to receive
332+email from client implementations that do not terminate DATA content
333+with the standard End-of-DATA sequence
334+&lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;. <br> <br> Such clients
335+can be excluded with smtpd_forbid_bare_newline_exclusions. </dd>
336+
337+<dt> <b>yes</b> </dt> <dd> Compatibility alias for <b>normalize</b>. </dd>
338+
339+<dt> <b>reject</b> </dt> <dd> Require the standard End-of-DATA
340+sequence &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;. Reject a command
341+or message content when a line contains bare &lt;LF&gt;, log a "bare
342+&lt;LF&gt; received" error, and reply with the SMTP status code in
343+$smtpd_forbid_bare_newline_reject_code. <br> <br> This will reject
344+email from SMTP clients that send any non-standard line endings
345+such as web applications, netcat, or load balancer health checks.
346+<br> <br> This will also reject email from services that use BDAT
347+to send MIME text containing a bare newline (RFC 3030 Section 3
348+requires canonical MIME format for text message types, defined in
349+RFC 2045 Sections 2.7 and 2.8). <br> <br> Such clients can be
350+excluded with smtpd_forbid_bare_newline_exclusions (or, in the case
351+of BDAT violations, BDAT can be selectively disabled with
352+smtpd_discard_ehlo_keyword_address_maps, or globally disabled with
353+smtpd_discard_ehlo_keywords). </dd>
354+
355+<dt> <b>no</b> (default)</dt> <dd> Do not require the standard
356+End-of-DATA
357+sequence &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;. Always process
358+a bare &lt;LF&gt; as if the client sent &lt;CR&gt;&lt;LF&gt;. This
359+option is fully backwards compatible, but is not recommended for
360+an Internet-facing SMTP server, because it is vulnerable to <a
361+href="https://www.postfix.org/smtp-smuggling.html"> SMTP smuggling</a>.
362+</dd>
363
364-<p> See <a href="https://www.postfix.org/smtp-smuggling.html">
365-https://www.postfix.org/smtp-smuggling.html</a> for details.
366+</dl>
367
368-<p> Example: </p>
369+<p> Recommended settings: </p>
370
371 <blockquote>
372 <pre>
373-# Disconnect remote SMTP clients that send bare newlines, but allow
374-# local clients with non-standard SMTP implementations such as netcat,
375-# fax machines, or load balancer health checks.
376+# Require the standard End-of-DATA sequence &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;.
377+# Otherwise, allow bare &lt;LF&gt; and process it as if the client sent
378+# &lt;CR&gt;&lt;LF&gt;.
379 #
380-smtpd_forbid_bare_newline = yes
381+# This maintains compatibility with many legitimate SMTP client
382+# applications that send a mix of standard and non-standard line
383+# endings, but will fail to receive email from client implementations
384+# that do not terminate DATA content with the standard End-of-DATA
385+# sequence &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;.
386+#
387+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
388+# The example below allowlists SMTP clients in trusted networks.
389+#
390+smtpd_forbid_bare_newline = normalize
391 smtpd_forbid_bare_newline_exclusions = $mynetworks
392 </pre>
393 </blockquote>
394
395-<p> This feature is available in Postfix &ge; 3.9, 3.8.4, 3.7.9,
396-3.6.13, and 3.5.23. </p>
397-
398-%PARAM smtpd_forbid_bare_newline_exclusions $mynetworks
399-
400-<p> Exclude the specified clients from smtpd_forbid_bare_newline
401-enforcement. It uses the same syntax and parent-domain matching
402-behavior as mynetworks. </p>
403-
404-<p> Example: </p>
405+<p> Alternative: </p>
406
407 <blockquote>
408 <pre>
409-# Disconnect remote SMTP clients that send bare newlines, but allow
410-# local clients with non-standard SMTP implementations such as netcat,
411-# fax machines, or load balancer health checks.
412+# Reject input lines that contain &lt;LF&gt; and log a "bare &lt;LF&gt; received"
413+# error. Require that input lines end in &lt;CR&gt;&lt;LF&gt;, and require the
414+# standard End-of-DATA sequence &lt;CR&gt;&lt;LF&gt;.&lt;CR&gt;&lt;LF&gt;.
415+#
416+# This will reject email from SMTP clients that send any non-standard
417+# line endings such as web applications, netcat, or load balancer
418+# health checks.
419+#
420+# This will also reject email from services that use BDAT to send
421+# MIME text containing a bare newline (RFC 3030 Section 3 requires
422+# canonical MIME format for text message types, defined in RFC 2045
423+# Sections 2.7 and 2.8).
424+#
425+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
426+# The example below allowlists SMTP clients in trusted networks.
427 #
428-smtpd_forbid_bare_newline = yes
429+smtpd_forbid_bare_newline = reject
430 smtpd_forbid_bare_newline_exclusions = $mynetworks
431+#
432+# Alternatively, in the case of BDAT violations, BDAT can be selectively
433+# disabled with smtpd_discard_ehlo_keyword_address_maps, or globally
434+# disabled with smtpd_discard_ehlo_keywords.
435+#
436+# smtpd_discard_ehlo_keyword_address_maps = cidr:/path/to/file
437+# /path/to/file:
438+# 10.0.0.0/24 chunking, silent-discard
439+# smtpd_discard_ehlo_keywords = chunking, silent-discard
440 </pre>
441 </blockquote>
442
443+<p> This feature with settings <b>yes</b> and <b>no</b> is available
444+in Postfix 3.8.4, 3.7.9, 3.6.13, and 3.5.23. Additionally, the
445+settings <b>reject</b>, and <b>normalize</b> are available with
446+Postfix &ge; 3.9, 3.8.5, 3.7.10, 3.6.14, and 3.5.24. </p>
447+
448+%PARAM smtpd_forbid_bare_newline_exclusions $mynetworks
449+
450+<p> Exclude the specified clients from smtpd_forbid_bare_newline
451+enforcement. This setting uses the same syntax and parent-domain
452+matching behavior as mynetworks. </p>
453+
454 <p> This feature is available in Postfix &ge; 3.9, 3.8.4, 3.7.9,
455 3.6.13, and 3.5.23. </p>
456
457+%PARAM smtpd_forbid_bare_newline_reject_code 550
458+
459+<p>
460+The numerical Postfix SMTP server response code when rejecting a
461+request with "smtpd_forbid_bare_newline = reject".
462+Specify a 5XX status code (521 to disconnect).
463+</p>
464+
465+<p> This feature is available in Postfix &ge; 3.9, 3.8.5, 3.7.10,
466+3.6.14, and 3.5.24. </p>
467+
468+%PARAM cleanup_replace_stray_cr_lf yes
469+
470--- a/src/cleanup/cleanup.c
471+++ b/src/cleanup/cleanup.c
472@@ -145,6 +145,14 @@
473 /* .IP "\fBmessage_strip_characters (empty)\fR"
474 /* The set of characters that Postfix will remove from message
475 /* content.
476+/* .PP
477+/* Available in Postfix version 3.9, 3.8.5, 3.7.10, 3.6.14,
478+/* 3.5.24, and later:
479+/* .IP "\fBcleanup_replace_stray_cr_lf (yes)\fR"
480+/* Replace each stray <CR> or <LF> character in message
481+/* content with a space character, to prevent outbound SMTP smuggling,
482+/* and to make the evaluation of Postfix-added DKIM or other signatures
483+/* independent from how a remote mail server handles such characters.
484 /* BEFORE QUEUE MILTER CONTROLS
485 /* .ad
486 /* .fi
487--- a/src/cleanup/cleanup_init.c
488+++ b/src/cleanup/cleanup_init.c
489@@ -173,6 +173,7 @@
490 int var_always_add_hdrs; /* always add missing headers */
491 int var_virt_addrlen_limit; /* stop exponential growth */
492 char *var_hfrom_format; /* header_from_format */
493+int var_cleanup_mask_stray_cr_lf; /* replace stray CR or LF with space */
494
495 const CONFIG_INT_TABLE cleanup_int_table[] = {
496 VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
497@@ -189,6 +190,7 @@
498 VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off,
499 VAR_AUTO_8BIT_ENC_HDR, DEF_AUTO_8BIT_ENC_HDR, &var_auto_8bit_enc_hdr,
500 VAR_ALWAYS_ADD_HDRS, DEF_ALWAYS_ADD_HDRS, &var_always_add_hdrs,
501+ VAR_CLEANUP_MASK_STRAY_CR_LF, DEF_CLEANUP_MASK_STRAY_CR_LF, &var_cleanup_mask_stray_cr_lf,
502 0,
503 };
504
505--- a/src/cleanup/cleanup_message.c
506+++ b/src/cleanup/cleanup_message.c
507@@ -930,6 +930,23 @@
508 char *dst;
509
510 /*
511+ * Replace each stray CR or LF with one space. These are not allowed in
512+ * SMTP, and can be used to enable outbound (remote) SMTP smuggling.
513+ * Replacing these early ensures that our later DKIM etc. signature will
514+ * not be invalidated. Besides preventing SMTP smuggling, replacing stray
515+ * <CR> or <LF> ensures that the result of signature validation by a
516+ * later mail system will not depend on how that mail system handles
517+ * those stray characters in an implementation-dependent manner.
518+ *
519+ * The input length is not changed, therefore it is safe to overwrite the
520+ * input.
521+ */
522+ if (var_cleanup_mask_stray_cr_lf)
523+ for (dst = (char *) buf; dst < buf + len; dst++)
524+ if (*dst == '\r' || *dst == '\n')
525+ *dst = ' ';
526+
527+ /*
528 * Reject unwanted characters.
529 *
530 * XXX Possible optimization: simplify the loop when the "reject" set
531--- a/src/global/cleanup_strerror.c
532+++ b/src/global/cleanup_strerror.c
533@@ -73,6 +73,7 @@
534 CLEANUP_STAT_CONT, 550, "5.7.1", "message content rejected",
535 CLEANUP_STAT_WRITE, 451, "4.3.0", "queue file write error",
536 CLEANUP_STAT_NOPERM, 550, "5.7.1", "service denied",
537+ CLEANUP_STAT_BARE_LF, 521, "5.5.2", "bare <LF> received",
538 };
539
540 static CLEANUP_STAT_DETAIL cleanup_stat_success = {
541--- a/src/global/cleanup_user.h
542+++ b/src/global/cleanup_user.h
543@@ -65,6 +65,12 @@
544 #define CLEANUP_STAT_NOPERM (1<<9) /* Denied by non-content policy */
545
546 /*
547+ * Non-cleanup errors that live in the same bitmask space, to centralize
548+ * error handling.
549+ */
550+#define CLEANUP_STAT_BARE_LF (1<<16) /* Bare <LF> received */
551+
552+ /*
553 * These are set when we can't bounce even if we were asked to.
554 */
555 #define CLEANUP_STAT_MASK_CANT_BOUNCE \
556--- a/src/global/mail_params.h
557+++ b/src/global/mail_params.h
558@@ -4173,11 +4173,18 @@
559 * Backwards compatibility.
560 */
561 #define VAR_SMTPD_FORBID_BARE_LF "smtpd_forbid_bare_newline"
562-#define DEF_SMTPD_FORBID_BARE_LF 0
563+#define DEF_SMTPD_FORBID_BARE_LF "no"
564
565 #define VAR_SMTPD_FORBID_BARE_LF_EXCL "smtpd_forbid_bare_newline_exclusions"
566 #define DEF_SMTPD_FORBID_BARE_LF_EXCL "$" VAR_MYNETWORKS
567
568+#define VAR_SMTPD_FORBID_BARE_LF_CODE "smtpd_forbid_bare_newline_reject_code"
569+#define DEF_SMTPD_FORBID_BARE_LF_CODE 550
570+
571+#define VAR_CLEANUP_MASK_STRAY_CR_LF "cleanup_replace_stray_cr_lf"
572+#define DEF_CLEANUP_MASK_STRAY_CR_LF 1
573+extern int var_cleanup_mask_stray_cr_lf;
574+
575 /*
576 * Share TLS sessions through tlsproxy(8).
577 */
578--- a/src/global/smtp_stream.c
579+++ b/src/global/smtp_stream.c
580@@ -51,7 +51,8 @@
581 /* char *format;
582 /* va_list ap;
583 /*
584-/* int smtp_forbid_bare_lf;
585+/* int smtp_detect_bare_lf;
586+/* int smtp_got_bare_lf;
587 /* AUXILIARY API
588 /* int smtp_get_noexcept(vp, stream, maxlen, flags)
589 /* VSTRING *vp;
590@@ -126,16 +127,16 @@
591 /* smtp_vprintf() is the machine underneath smtp_printf().
592 /*
593 /* smtp_get_noexcept() implements the subset of smtp_get()
594-/* without long jumps for timeout or EOF errors. Instead,
595+/* without timeouts and without making long jumps. Instead,
596 /* query the stream status with vstream_feof() etc.
597-/* This function will make a VSTREAM long jump (error code
598-/* SMTP_ERR_LF) when rejecting input with a bare newline byte.
599+/*
600+/* This function assigns smtp_got_bare_lf = smtp_detect_bare_lf,
601+/* if smtp_detect_bare_lf is non-zero and the last read line
602+/* was terminated with a bare newline. Otherwise, this function
603+/* sets smtp_got_bare_lf to zero.
604 /*
605 /* smtp_timeout_setup() is a backwards-compatibility interface
606 /* for programs that don't require per-record deadline support.
607-/*
608-/* smtp_forbid_bare_lf controls whether smtp_get_noexcept()
609-/* will reject input with a bare newline byte.
610 /* DIAGNOSTICS
611 /* .fi
612 /* .ad
613@@ -208,7 +209,8 @@
614
615 #include "smtp_stream.h"
616
617-int smtp_forbid_bare_lf;
618+int smtp_detect_bare_lf;
619+int smtp_got_bare_lf;
620
621 /* smtp_timeout_reset - reset per-stream error flags, restart deadline timer */
622
623@@ -371,6 +373,8 @@
624 int last_char;
625 int next_char;
626
627+ smtp_got_bare_lf = 0;
628+
629 /*
630 * It's painful to do I/O with records that may span multiple buffers.
631 * Allow for partial long lines (we will read the remainder later) and
632@@ -413,11 +417,15 @@
633 */
634 case '\n':
635 vstring_truncate(vp, VSTRING_LEN(vp) - 1);
636- if (smtp_forbid_bare_lf
637- && (VSTRING_LEN(vp) == 0 || vstring_end(vp)[-1] != '\r'))
638- vstream_longjmp(stream, SMTP_ERR_LF);
639- while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
640- vstring_truncate(vp, VSTRING_LEN(vp) - 1);
641+ if (smtp_detect_bare_lf) {
642+ if (VSTRING_LEN(vp) == 0 || vstring_end(vp)[-1] != '\r')
643+ smtp_got_bare_lf = smtp_detect_bare_lf;
644+ else
645+ vstring_truncate(vp, VSTRING_LEN(vp) - 1);
646+ } else {
647+ while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
648+ vstring_truncate(vp, VSTRING_LEN(vp) - 1);
649+ }
650 VSTRING_TERMINATE(vp);
651 /* FALLTRHOUGH */
652
653--- a/src/global/smtp_stream.h
654+++ b/src/global/smtp_stream.h
655@@ -32,7 +32,6 @@
656 #define SMTP_ERR_QUIET 3 /* silent cleanup (application) */
657 #define SMTP_ERR_NONE 4 /* non-error case */
658 #define SMTP_ERR_DATA 5 /* application data error */
659-#define SMTP_ERR_LF 6 /* bare <LF> protocol error */
660
661 extern void smtp_stream_setup(VSTREAM *, int, int);
662 extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
663@@ -44,7 +43,8 @@
664 extern void smtp_fwrite(const char *, ssize_t len, VSTREAM *);
665 extern void smtp_fread_buf(VSTRING *, ssize_t len, VSTREAM *);
666 extern void smtp_fputc(int, VSTREAM *);
667-extern int smtp_forbid_bare_lf;
668+extern int smtp_detect_bare_lf;
669+extern int smtp_got_bare_lf;
670
671 extern void smtp_vprintf(VSTREAM *, const char *, va_list);
672
673--- a/src/smtpd/smtpd.c
674+++ b/src/smtpd/smtpd.c
675@@ -765,12 +765,17 @@
676 /* .PP
677 /* Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later:
678 /* .IP "\fBsmtpd_forbid_bare_newline (Postfix < 3.9: no)\fR"
679-/* Reply with "Error: bare <LF> received" and disconnect
680-/* when a remote SMTP client sends a line ending in <LF>, violating
681-/* the RFC 5321 requirement that lines must end in <CR><LF>.
682+/* Reject or restrict input lines from an SMTP client that end in
683+/* <LF> instead of the standard <CR><LF>.
684 /* .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR"
685 /* Exclude the specified clients from smtpd_forbid_bare_newline
686 /* enforcement.
687+/* .PP
688+/* Available in Postfix 3.9, 3.8.5, 3.7.10, 3.6.14, 3.5.24 and
689+/* later:
690+/* .IP "\fBsmtpd_forbid_bare_newline_reject_code (550)\fR"
691+/* The numerical Postfix SMTP server response code when rejecting a
692+/* request with "smtpd_forbid_bare_newline = reject".
693 /* TARPIT CONTROLS
694 /* .ad
695 /* .fi
696@@ -1476,8 +1481,10 @@
697 int var_smtpd_uproxy_tmout;
698 bool var_relay_before_rcpt_checks;
699
700-bool var_smtpd_forbid_bare_lf;
701+char *var_smtpd_forbid_bare_lf;
702 char *var_smtpd_forbid_bare_lf_excl;
703+int var_smtpd_forbid_bare_lf_code;
704+static int bare_lf_mask;
705 static NAMADR_LIST *bare_lf_excl;
706
707 /*
708@@ -1554,7 +1561,6 @@
709 #define REASON_TIMEOUT "timeout"
710 #define REASON_LOST_CONNECTION "lost connection"
711 #define REASON_ERROR_LIMIT "too many errors"
712-#define REASON_BARE_LF "bare <LF> received"
713
714 #ifdef USE_TLS
715
716@@ -1573,6 +1579,40 @@
717 */
718 static DICT *smtpd_cmd_filter;
719
720+ /*
721+ * Bare LF and End-of-DATA controls (bare CR is handled elsewhere).
722+ *
723+ * At the smtp_get*() line reader level, setting any of these flags in the
724+ * smtp_detect_bare_lf variable enables the detection of bare newlines. The
725+ * line reader will set the same flags in the smtp_got_bare_lf variable
726+ * after it detects a bare newline, otherwise it clears smtp_got_bare_lf.
727+ *
728+ * At the SMTP command level, the flags in smtp_got_bare_lf control whether
729+ * commands ending in a bare newline are rejected.
730+ *
731+ * At the DATA and BDAT content level, the flags in smtp_got_bare_lf control
732+ * whether the standard End-of-DATA sequence CRLF.CRLF is required, and
733+ * whether lines ending in bare newlines are rejected.
734+ *
735+ * Postfix implements "delayed reject" after detecting a bare newline in BDAT
736+ * or DATA content. The SMTP server delays a REJECT response until the
737+ * command is finished, instead of replying and hanging up immediately. The
738+ * End-of-DATA detection is secured with BARE_LF_FLAG_WANT_STD_EOD.
739+ */
740+#define BARE_LF_FLAG_WANT_STD_EOD (1<<0) /* Require CRLF.CRLF */
741+#define BARE_LF_FLAG_REPLY_REJECT (1<<1) /* Reject bare newline */
742+
743+#define IS_BARE_LF_WANT_STD_EOD(m) ((m) & BARE_LF_FLAG_WANT_STD_EOD)
744+#define IS_BARE_LF_REPLY_REJECT(m) ((m) & BARE_LF_FLAG_REPLY_REJECT)
745+
746+static const NAME_CODE bare_lf_mask_table[] = {
747+ "normalize", BARE_LF_FLAG_WANT_STD_EOD, /* Default */
748+ "yes", BARE_LF_FLAG_WANT_STD_EOD, /* Migration aid */
749+ "reject", BARE_LF_FLAG_WANT_STD_EOD | BARE_LF_FLAG_REPLY_REJECT,
750+ "no", 0,
751+ 0, -1, /* error */
752+};
753+
754 #ifdef USE_SASL_AUTH
755
756 /*
757@@ -3515,6 +3555,7 @@
758 int curr_rec_type;
759 int prev_rec_type;
760 int first = 1;
761+ int prev_got_bare_lf = 0;
762
763 /*
764 * Copy the message content. If the cleanup process has a problem, keep
765@@ -3528,12 +3569,15 @@
766 * XXX Deal with UNIX-style From_ lines at the start of message content
767 * because sendmail permits it.
768 */
769- for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) {
770+ for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type,
771+ prev_got_bare_lf = smtp_got_bare_lf) {
772 if (smtp_get(state->buffer, state->client, var_line_limit,
773 SMTP_GET_FLAG_NONE) == '\n')
774 curr_rec_type = REC_TYPE_NORM;
775 else
776 curr_rec_type = REC_TYPE_CONT;
777+ if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf))
778+ state->err |= CLEANUP_STAT_BARE_LF;
779 start = vstring_str(state->buffer);
780 len = VSTRING_LEN(state->buffer);
781 if (first) {
782@@ -3546,9 +3590,14 @@
783 if (len > 0 && IS_SPACE_TAB(start[0]))
784 out_record(out_stream, REC_TYPE_NORM, "", 0);
785 }
786- if (prev_rec_type != REC_TYPE_CONT && *start == '.'
787- && (proxy == 0 ? (++start, --len) == 0 : len == 1))
788- break;
789+ if (prev_rec_type != REC_TYPE_CONT && *start == '.') {
790+ if (len == 1 && IS_BARE_LF_WANT_STD_EOD(smtp_detect_bare_lf)
791+ && (smtp_got_bare_lf || prev_got_bare_lf))
792+ /* Do not store or send to proxy filter. */
793+ continue;
794+ if (proxy == 0 ? (++start, --len) == 0 : len == 1)
795+ break;
796+ }
797 if (state->err == CLEANUP_STAT_OK) {
798 if (ENFORCING_SIZE_LIMIT(var_message_limit)
799 && var_message_limit - state->act_size < len + 2) {
800@@ -3701,6 +3750,11 @@
801 else
802 smtpd_chat_reply(state,
803 "250 2.0.0 Ok: queued as %s", state->queue_id);
804+ } else if ((state->err & CLEANUP_STAT_BARE_LF) != 0) {
805+ state->error_mask |= MAIL_ERROR_PROTOCOL;
806+ log_whatsup(state, "reject", "bare <LF> received");
807+ smtpd_chat_reply(state, "%d 5.5.2 %s Error: bare <LF> received",
808+ var_smtpd_forbid_bare_lf_code, var_myhostname);
809 } else if (why && IS_SMTP_REJECT(STR(why))) {
810 state->error_mask |= MAIL_ERROR_POLICY;
811 smtpd_chat_reply(state, "%s", STR(why));
812@@ -3981,7 +4035,6 @@
813 */
814 done = 0;
815 do {
816- int payload_err;
817
818 /*
819 * Do not skip the smtp_fread_buf() call if read_len == 0. We still
820@@ -3995,10 +4048,6 @@
821 smtp_fread_buf(state->buffer, read_len, state->client);
822 state->bdat_get_stream = vstream_memreopen(
823 state->bdat_get_stream, state->buffer, O_RDONLY);
824- vstream_control(state->bdat_get_stream, CA_VSTREAM_CTL_EXCEPT,
825- CA_VSTREAM_CTL_END);
826- if ((payload_err = vstream_setjmp(state->bdat_get_stream)) != 0)
827- vstream_longjmp(state->client, payload_err);
828
829 /*
830 * Read lines from the fragment. The last line may continue in the
831@@ -4023,6 +4072,8 @@
832 /* Skip the out_record() and VSTRING_RESET() calls below. */
833 break;
834 }
835+ if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf))
836+ state->err |= CLEANUP_STAT_BARE_LF;
837 start = vstring_str(state->bdat_get_buffer);
838 len = VSTRING_LEN(state->bdat_get_buffer);
839 if (state->err == CLEANUP_STAT_OK) {
840@@ -4674,9 +4725,9 @@
841 */
842 xclient_allowed =
843 namadr_list_match(xclient_hosts, state->name, state->addr);
844- smtp_forbid_bare_lf = SMTPD_STAND_ALONE((state)) == 0
845- && var_smtpd_forbid_bare_lf
846- && !namadr_list_match(bare_lf_excl, state->name, state->addr);
847+ smtp_detect_bare_lf = (SMTPD_STAND_ALONE((state)) == 0 && bare_lf_mask
848+ && !namadr_list_match(bare_lf_excl, state->name, state->addr)) ?
849+ bare_lf_mask : 0;
850 /* NOT: tls_reset() */
851 if (got_helo == 0)
852 helo_reset(state);
853@@ -5468,13 +5519,6 @@
854 var_myhostname);
855 break;
856
857- case SMTP_ERR_LF:
858- state->reason = REASON_BARE_LF;
859- if (vstream_setjmp(state->client) == 0)
860- smtpd_chat_reply(state, "521 5.5.2 %s Error: bare <LF> received",
861- var_myhostname);
862- break;
863-
864 case 0:
865
866 /*
867@@ -5676,6 +5720,13 @@
868 }
869 watchdog_pat();
870 smtpd_chat_query(state);
871+ if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) {
872+ log_whatsup(state, "reject", "bare <LF> received");
873+ state->error_mask |= MAIL_ERROR_PROTOCOL;
874+ smtpd_chat_reply(state, "%d 5.5.2 %s Error: bare <LF> received",
875+ var_smtpd_forbid_bare_lf_code, var_myhostname);
876+ break;
877+ }
878 /* Safety: protect internal interfaces against malformed UTF-8. */
879 if (var_smtputf8_enable && valid_utf8_string(STR(state->buffer),
880 LEN(state->buffer)) == 0) {
881@@ -6024,11 +6075,11 @@
882 namadr_list_match(xforward_hosts, state.name, state.addr);
883
884 /*
885- * Enforce strict SMTP line endings, with compatibility exclusions.
886+ * Reject or normalize bare LF, with compatibility exclusions.
887 */
888- smtp_forbid_bare_lf = SMTPD_STAND_ALONE((&state)) == 0
889- && var_smtpd_forbid_bare_lf
890- && !namadr_list_match(bare_lf_excl, state.name, state.addr);
891+ smtp_detect_bare_lf = (SMTPD_STAND_ALONE((&state)) == 0 && bare_lf_mask
892+ && !namadr_list_match(bare_lf_excl, state.name, state.addr)) ?
893+ bare_lf_mask : 0;
894
895 /*
896 * See if we need to turn on verbose logging for this client.
897@@ -6095,6 +6146,10 @@
898 MATCH_FLAG_RETURN
899 | match_parent_style(VAR_MYNETWORKS),
900 var_smtpd_forbid_bare_lf_excl);
901+ if ((bare_lf_mask = name_code(bare_lf_mask_table, NAME_CODE_FLAG_NONE,
902+ var_smtpd_forbid_bare_lf)) < 0)
903+ msg_fatal("bad parameter value: '%s = %s'",
904+ VAR_SMTPD_FORBID_BARE_LF, var_smtpd_forbid_bare_lf);
905
906 /*
907 * Open maps before dropping privileges so we can read passwords etc.
908@@ -6390,6 +6445,7 @@
909 VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0,
910 VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0,
911 VAR_PLAINTEXT_CODE, DEF_PLAINTEXT_CODE, &var_plaintext_code, 0, 0,
912+ VAR_SMTPD_FORBID_BARE_LF_CODE, DEF_SMTPD_FORBID_BARE_LF_CODE, &var_smtpd_forbid_bare_lf_code, 500, 599,
913 VAR_SMTPD_CRATE_LIMIT, DEF_SMTPD_CRATE_LIMIT, &var_smtpd_crate_limit, 0, 0,
914 VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0,
915 VAR_SMTPD_CMAIL_LIMIT, DEF_SMTPD_CMAIL_LIMIT, &var_smtpd_cmail_limit, 0, 0,
916@@ -6452,7 +6508,6 @@
917 VAR_SMTPD_PEERNAME_LOOKUP, DEF_SMTPD_PEERNAME_LOOKUP, &var_smtpd_peername_lookup,
918 VAR_SMTPD_DELAY_OPEN, DEF_SMTPD_DELAY_OPEN, &var_smtpd_delay_open,
919 VAR_SMTPD_CLIENT_PORT_LOG, DEF_SMTPD_CLIENT_PORT_LOG, &var_smtpd_client_port_log,
920- VAR_SMTPD_FORBID_BARE_LF, DEF_SMTPD_FORBID_BARE_LF, &var_smtpd_forbid_bare_lf,
921 0,
922 };
923 static const CONFIG_NBOOL_TABLE nbool_table[] = {
924@@ -6569,6 +6624,7 @@
925 VAR_SMTPD_DNS_RE_FILTER, DEF_SMTPD_DNS_RE_FILTER, &var_smtpd_dns_re_filter, 0, 0,
926 VAR_SMTPD_REJ_FTR_MAPS, DEF_SMTPD_REJ_FTR_MAPS, &var_smtpd_rej_ftr_maps, 0, 0,
927 VAR_SMTPD_FORBID_BARE_LF_EXCL, DEF_SMTPD_FORBID_BARE_LF_EXCL, &var_smtpd_forbid_bare_lf_excl, 0, 0,
928+ VAR_SMTPD_FORBID_BARE_LF, DEF_SMTPD_FORBID_BARE_LF, &var_smtpd_forbid_bare_lf, 1, 0,
929 0,
930 };
931 static const CONFIG_RAW_TABLE raw_table[] = {
932--- a/src/smtpd/smtpd_check.c
933+++ b/src/smtpd/smtpd_check.c
934@@ -48,6 +48,11 @@
935 /*
936 /* char *smtpd_check_queue(state)
937 /* SMTPD_STATE *state;
938+/* AUXILIARY FUNCTIONS
939+/* void log_whatsup(state, action, text)
940+/* SMTPD_STATE *state;
941+/* const char *action;
942+/* const char *text;
943 /* DESCRIPTION
944 /* This module implements additional checks on SMTP client requests.
945 /* A client request is validated in the context of the session state.
946@@ -146,6 +151,11 @@
947 /* The recipient address given with the RCPT TO or VRFY command.
948 /* .IP size
949 /* The message size given with the MAIL FROM command (zero if unknown).
950+/* .PP
951+/* log_whatsup() logs "<queueid>: <action>: <protocol state>
952+/* from: <client-name[client-addr]>: <text>" plus the protocol
953+/* (SMTP or ESMTP), and if available, EHLO, MAIL FROM, or RCPT
954+/* TO.
955 /* BUGS
956 /* Policies like these should not be hard-coded in C, but should
957 /* be user-programmable instead.
958@@ -987,8 +997,8 @@
959
960 /* log_whatsup - log as much context as we have */
961
962-static void log_whatsup(SMTPD_STATE *state, const char *whatsup,
963- const char *text)
964+void log_whatsup(SMTPD_STATE *state, const char *whatsup,
965+ const char *text)
966 {
967 VSTRING *buf = vstring_alloc(100);
968
969--- a/src/smtpd/smtpd_check.h
970+++ b/src/smtpd/smtpd_check.h
971@@ -25,6 +25,7 @@
972 extern char *smtpd_check_data(SMTPD_STATE *);
973 extern char *smtpd_check_eod(SMTPD_STATE *);
974 extern char *smtpd_check_policy(SMTPD_STATE *, char *);
975+extern void log_whatsup(SMTPD_STATE *, const char *, const char *);
976
977 /* LICENSE
978 /* .ad
diff --git a/meta-networking/recipes-daemons/postfix/postfix_3.6.7.bb b/meta-networking/recipes-daemons/postfix/postfix_3.6.7.bb
index 17864b8915..fdda2e749e 100644
--- a/meta-networking/recipes-daemons/postfix/postfix_3.6.7.bb
+++ b/meta-networking/recipes-daemons/postfix/postfix_3.6.7.bb
@@ -13,6 +13,8 @@ SRC_URI += "ftp://ftp.porcupine.org/mirrors/postfix-release/official/postfix-${P
13 file://0004-Fix-icu-config.patch \ 13 file://0004-Fix-icu-config.patch \
14 file://0005-makedefs-add-lnsl-and-lresolv-to-SYSLIBS-by-default.patch \ 14 file://0005-makedefs-add-lnsl-and-lresolv-to-SYSLIBS-by-default.patch \
15 file://0006-makedefs-Account-for-linux-6.x-version.patch \ 15 file://0006-makedefs-Account-for-linux-6.x-version.patch \
16 file://CVE-2023-51764-1.patch \
17 file://CVE-2023-51764-2.patch \
16 " 18 "
17SRC_URI[sha256sum] = "e471df7e0eb11c4a1e574b6d7298f635386e2843b6b3584c25a04543d587e07f" 19SRC_URI[sha256sum] = "e471df7e0eb11c4a1e574b6d7298f635386e2843b6b3584c25a04543d587e07f"
18UPSTREAM_CHECK_REGEX = "postfix\-(?P<pver>3\.6(\.\d+)+).tar.gz" 20UPSTREAM_CHECK_REGEX = "postfix\-(?P<pver>3\.6(\.\d+)+).tar.gz"