Prereq: "3.6.13" diff -ur --new-file /var/tmp/postfix-3.6.13/src/global/mail_version.h ./src/global/mail_version.h --- /var/tmp/postfix-3.6.13/src/global/mail_version.h 2023-12-21 21:15:16.000000000 -0500 +++ ./src/global/mail_version.h 2024-01-19 13:30:30.000000000 -0500 @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20231222" -#define MAIL_VERSION_NUMBER "3.6.13" +#define MAIL_RELEASE_DATE "20240121" +#define MAIL_VERSION_NUMBER "3.6.14" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -ur --new-file /var/tmp/postfix-3.6.13/HISTORY ./HISTORY --- /var/tmp/postfix-3.6.13/HISTORY 2023-12-21 21:12:15.000000000 -0500 +++ ./HISTORY 2024-01-19 13:29:37.000000000 -0500 @@ -26037,16 +26037,36 @@ a configured recipient delimiter value. Reported by Tod A. Sandman. Files: proto/postconf.proto, local/local_expand.c. -20231221 +20240109 - Security: with "smtpd_forbid_bare_newline = yes" (default - "no" for Postfix < 3.9), reply with "Error: bare - received" and disconnect when an SMTP client sends a line - ending in , violating the RFC 5321 requirement that - lines must end in . This prevents SMTP smuggling - attacks that target a recipient at a Postfix server. For - backwards compatibility, local clients are excluded by + Security (outbound SMTP smuggling): with the default setting + "cleanup_replace_stray_cr_lf = yes" Postfix will replace + stray or characters in message content with a + space character. This prevents Postfix from enabling + outbound (remote) SMTP smuggling, and it also makes evaluation + of Postfix-added DKIM etc. signatures independent from how + a remote mail server handles stray or characters. + Files: global/mail_params.h, cleanup/cleanup.c, + cleanup/cleanup_message.c, mantools/postlink, proto/postconf.proto. + +20240112 + + Security (inbound SMTP smuggling): with "smtpd_forbid_bare_newline + = normalize" (default "no" for Postfix < 3.9), the Postfix + SMTP server requires the standard End-of-DATA sequence + ., and otherwise allows command or message + content lines ending in the non-standard , processing + them as if the client sent the standard . + + The alternative setting, "smtpd_forbid_bare_newline = reject" + will reject any command or message that contains a bare + , and is more likely to cause problems with legitimate + clients. + + For backwards compatibility, local clients are excluded by default with "smtpd_forbid_bare_newline_exclusions = - $mynetworks". Files: mantools/postlink, proto/postconf.proto, + $mynetworks". + + Files: mantools/postlink, proto/postconf.proto, global/mail_params.h, global/smtp_stream.c, global/smtp_stream.h, - smtpd/smtpd.c. + smtpd/smtpd.c, smtpd/smtpd_check.[hc]. diff -ur --new-file /var/tmp/postfix-3.6.13/RELEASE_NOTES ./RELEASE_NOTES --- /var/tmp/postfix-3.6.13/RELEASE_NOTES 2023-12-22 13:27:29.000000000 -0500 +++ ./RELEASE_NOTES 2024-01-21 17:46:01.000000000 -0500 @@ -25,30 +25,103 @@ the software under the license of their choice. Those who are more comfortable with the IPL can continue with that license. -Major changes with Postfix 3.6.13 -================================= +Incompatibility with Postfix 3.8.5, 3.7.10, 3.6.14, and 3.5.24 +============================================================== + +Improvements for outbound SMTP smuggling defense: + +- With "cleanup_replace_stray_cr_lf = yes" (the default), the cleanup + daemon replaces each stray or character in message + content with a space character. The replacement happens before + any other content management (header/body_checks, Milters, etc). + + This prevents outbound SMTP smuggling, where an attacker uses + Postfix to send email containing a non-standard End-of-DATA + sequence, to exploit inbound SMTP smuggling at a vulnerable remote + SMTP server. + + This also improves the remote evaluation of Postfix-added DKIM + and other signatures, as the evaluation result will not depend + on how a remote email server handles stray or characters. + +This feature applies to all email that Postfix locally or remotely +sends out. It is not allowlisted based on client identity. + +Major changes with Postfix 3.8.5, 3.7.10, 3.6.14, and 3.5.24 +============================================================ + +Improvements for inbound SMTP smuggling defense: + +- Better compatibility: the recommended setting "smtpd_forbid_bare_newline + = normalize" requires the standard End-of-DATA sequence + ., but allows bare newlines from SMTP clients, + maintaining more compatibility with existing infrastructure. + +- Improved logging for rejected input (it now includes queue ID, + helo, mail, and rcpt, if available). + +- The setting "smtpd_forbid_bare_newline = reject" requires + that input lines end in , requires the standard End-of-DATA + sequence ., and rejects a command or message that + contains a bare newline. To disconnect the client, specify + "smtpd_forbid_bare_newline_reject_code = 521". + +- The Postfix SMTP server no longer strips extra as in + ., to silence false alarms from test tools + that send attack sequences that real mail servers cannot send. + Details at https://www.postfix.org/false-smuggling-claims.html + +- The old setting "yes" has become an alias for "normalize". -Security: the Postfix SMTP server optionally disconnects a remote -SMTP client that sends a 'bare newline' line ending in SMTP. This -prevents an SMTP smuggling attack on recipients at a Postfix server. -For background, see https://www.postfix.org/smtp-smuggling.html - -Sites concerned about SMTP smuggling attacks should enable this -feature in Internet-facing Postfix servers. For compatibility with -non-standard clients, Postfix by default excludes clients in -mynetworks from this countermeasure. - -The recommended settings are: - - # Optionally 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. +- The old setting "no" has not changed, and allows SMTP smuggling. + +The recommended settings are now: + + # Require the standard End-of-DATA sequence .. + # Otherwise, allow bare and process it as if the client sent + # . + # + # This maintains compatibility with many legitimate SMTP client + # applications that send a mix of standard and non-standard line + # endings, but will fail to receive email from client implementations + # that do not terminate DATA content with the standard End-of-DATA + # sequence .. # - smtpd_forbid_bare_newline = no + # Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions. + # The example below allowlists SMTP clients in trusted networks. + # + smtpd_forbid_bare_newline = normalize smtpd_forbid_bare_newline_exclusions = $mynetworks -This feature is back-ported from Postfix 3.9, with the difference -that "smtpd_forbid_bare_newline = no" by default. +Alternative settings: + + # Reject input lines that contain and log a "bare received" + # error. Require that input lines end in , and require the + # standard End-of-DATA sequence .. + # + # This will reject email from SMTP clients that send any non-standard + # line endings such as web applications, netcat, or load balancer + # health checks. + # + # This will also reject email from services that use BDAT to send + # MIME text containing a bare newline (RFC 3030 Section 3 requires + # canonical MIME format for text message types, defined in RFC 2045 + # Sections 2.7 and 2.8). + # + # Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions. + # The example below allowlists SMTP clients in trusted networks. + # + smtpd_forbid_bare_newline = reject + smtpd_forbid_bare_newline_exclusions = $mynetworks + # + # Alternatively, in the case of BDAT violations, BDAT can be selectively + # disabled with smtpd_discard_ehlo_keyword_address_maps, or globally + # disabled with smtpd_discard_ehlo_keywords. + # + # smtpd_discard_ehlo_keyword_address_maps = cidr:/path/to/file + # /path/to/file: + # 10.0.0.0/24 chunking, silent-discard + # smtpd_discard_ehlo_keywords = chunking, silent-discard Major changes with Postfix 3.6.10 ================================= diff -ur --new-file /var/tmp/postfix-3.6.13/html/cleanup.8.html ./html/cleanup.8.html --- /var/tmp/postfix-3.6.13/html/cleanup.8.html 2020-12-05 18:12:08.000000000 -0500 +++ ./html/cleanup.8.html 2024-01-19 13:54:26.000000000 -0500 @@ -156,6 +156,16 @@ The set of characters that Postfix will remove from message con- tent. + Available in Postfix version 3.9, 3.8.5, 3.7.10, 3.6.14, 3.5.24, and + later: + + cleanup_replace_stray_cr_lf (yes) + Replace each stray <CR> or <LF> character in message content + with a space character, to prevent outbound SMTP smuggling, and + to make the evaluation of Postfix-added DKIM or other signatures + independent from how a remote mail server handles such charac- + ters. + BEFORE QUEUE MILTER CONTROLS As of version 2.3, Postfix supports the Sendmail version 8 Milter (mail filter) protocol. When mail is not received via the smtpd(8) server, diff -ur --new-file /var/tmp/postfix-3.6.13/html/postconf.5.html ./html/postconf.5.html --- /var/tmp/postfix-3.6.13/html/postconf.5.html 2023-12-21 21:23:15.000000000 -0500 +++ ./html/postconf.5.html 2024-01-21 17:26:46.000000000 -0500 @@ -1444,6 +1444,40 @@ +
cleanup_replace_stray_cr_lf +(default: yes)
+ +

Replace each stray <CR> or <LF> character in message +content with a space character, to prevent outbound SMTP smuggling, +and to make the evaluation of Postfix-added DKIM or other signatures +independent from how a remote mail server handles such characters. +

+ +

SMTP does not allow such characters unless they are part of a +<CR><LF> sequence, and different mail systems handle +such stray characters in an implementation-dependent manner. Stray +<CR> or <LF> characters could be used for outbound +SMTP smuggling, where an attacker uses a Postfix server to send +message content with a non-standard End-of-DATA sequence that +triggers inbound SMTP smuggling at a remote SMTP server.

+ +

The replacement happens before all other content management, +and before Postfix may add a DKIM etc. signature; if the signature +were created first, the replacement could invalidate the signature. +

+ +

In addition to preventing SMTP smuggling, replacing stray +<CR> or <LF> characters ensures that the result of +signature validation by later mail system will not depend on how +that mail system handles those stray characters in an +implementation-dependent manner.

+ +

This feature is available in Postfix ≥ 3.9, 3.8.5, 3.7.10, +3.6.14, and 3.5.24.

+ + +
+
cleanup_service_name (default: cleanup)
@@ -15304,32 +15338,118 @@
smtpd_forbid_bare_newline (default: 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).

+

Reject or restrict input lines from an SMTP client that end in +<LF> instead of the standard <CR><LF>. Such line +endings are commonly allowed with UNIX-based SMTP servers, but they +violate RFC 5321, and allowing such line endings can make a server +vulnerable to +SMTP smuggling.

-

See -https://www.postfix.org/smtp-smuggling.html for details. +

Specify one of the following values (case does not matter):

-

Example:

+
+ +
normalize
Require the standard +End-of-DATA sequence <CR><LF>.<CR><LF>. +Otherwise, allow command or message content lines ending in the +non-standard <LF>, and process them as if the client sent the +standard <CR><LF>.

This maintains compatibility +with many legitimate SMTP client applications that send a mix of +standard and non-standard line endings, but will fail to receive +email from client implementations that do not terminate DATA content +with the standard End-of-DATA sequence +<CR><LF>.<CR><LF>.

Such clients +can be excluded with smtpd_forbid_bare_newline_exclusions.
+ +
yes
Compatibility alias for normalize.
+ +
reject
Require the standard End-of-DATA +sequence <CR><LF>.<CR><LF>. Reject a command +or message content when a line contains bare <LF>, log a "bare +<LF> received" error, and reply with the SMTP status code in +$smtpd_forbid_bare_newline_reject_code.

This will reject +email from SMTP clients that send any non-standard line endings +such as web applications, netcat, or load balancer health checks. +

This will also reject email from services that use BDAT +to send MIME text containing a bare newline (RFC 3030 Section 3 +requires canonical MIME format for text message types, defined in +RFC 2045 Sections 2.7 and 2.8).

Such clients can be +excluded with smtpd_forbid_bare_newline_exclusions (or, in the case +of BDAT violations, BDAT can be selectively disabled with +smtpd_discard_ehlo_keyword_address_maps, or globally disabled with +smtpd_discard_ehlo_keywords).
+ +
no (default)
Do not require the standard +End-of-DATA +sequence <CR><LF>.<CR><LF>. Always process +a bare <LF> as if the client sent <CR><LF>. This +option is fully backwards compatible, but is not recommended for +an Internet-facing SMTP server, because it is vulnerable to SMTP smuggling. +
+ +
+ +

Recommended settings:

-# 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.
+# Require the standard End-of-DATA sequence <CR><LF>.<CR><LF>.
+# Otherwise, allow bare <LF> and process it as if the client sent
+# <CR><LF>.
+#
+# This maintains compatibility with many legitimate SMTP client
+# applications that send a mix of standard and non-standard line
+# endings, but will fail to receive email from client implementations
+# that do not terminate DATA content with the standard End-of-DATA
+# sequence <CR><LF>.<CR><LF>.
+#
+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
+# The example below allowlists SMTP clients in trusted networks.
 #
-smtpd_forbid_bare_newline = yes
+smtpd_forbid_bare_newline = normalize
 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.

+

Alternative:

+ +
+
+# Reject input lines that contain <LF> and log a "bare <LF> received"
+# error. Require that input lines end in <CR><LF>, and require the
+# standard End-of-DATA sequence <CR><LF>.<CR><LF>.
+#
+# This will reject email from SMTP clients that send any non-standard
+# line endings such as web applications, netcat, or load balancer
+# health checks.
+#
+# This will also reject email from services that use BDAT to send
+# MIME text containing a bare newline (RFC 3030 Section 3 requires
+# canonical MIME format for text message types, defined in RFC 2045
+# Sections 2.7 and 2.8).
+#
+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
+# The example below allowlists SMTP clients in trusted networks.
+#
+smtpd_forbid_bare_newline = reject
+smtpd_forbid_bare_newline_exclusions = $mynetworks
+#
+# Alternatively, in the case of BDAT violations, BDAT can be selectively
+# disabled with smtpd_discard_ehlo_keyword_address_maps, or globally
+# disabled with smtpd_discard_ehlo_keywords.
+#
+# smtpd_discard_ehlo_keyword_address_maps = cidr:/path/to/file
+# /path/to/file:
+#     10.0.0.0/24 chunking, silent-discard
+# smtpd_discard_ehlo_keywords = chunking, silent-discard
+
+
+ +

This feature with settings yes and no is available +in Postfix 3.8.4, 3.7.9, 3.6.13, and 3.5.23. Additionally, the +settings reject, and normalize are available with +Postfix ≥ 3.9, 3.8.5, 3.7.10, 3.6.14, and 3.5.24.

@@ -15338,21 +15458,8 @@ (default: $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
-
-
+enforcement. This setting uses the same syntax and parent-domain +matching behavior as mynetworks.

This feature is available in Postfix ≥ 3.9, 3.8.4, 3.7.9, 3.6.13, and 3.5.23.

@@ -15360,6 +15467,21 @@
+
smtpd_forbid_bare_newline_reject_code +(default: 550)
+ +

+The numerical Postfix SMTP server response code when rejecting a +request with "smtpd_forbid_bare_newline = reject". +Specify a 5XX status code (521 to disconnect). +

+ +

This feature is available in Postfix ≥ 3.9, 3.8.5, 3.7.10, +3.6.14, and 3.5.24.

+ + +
+
smtpd_forbid_unauth_pipelining (default: Postfix ≥ 3.9: yes)
diff -ur --new-file /var/tmp/postfix-3.6.13/html/smtpd.8.html ./html/smtpd.8.html --- /var/tmp/postfix-3.6.13/html/smtpd.8.html 2023-12-22 13:05:48.000000000 -0500 +++ ./html/smtpd.8.html 2024-01-19 13:55:41.000000000 -0500 @@ -936,17 +936,28 @@ Disconnect remote SMTP clients that violate RFC 2920 (or 5321) command pipelining constraints. + Available in Postfix 3.9, 3.8.1, 3.7.6, 3.6.10, 3.5.20 and later: + + smtpd_forbid_unauth_pipelining (Postfix >= 3.9: yes) + Disconnect remote SMTP clients that violate RFC 2920 (or 5321) + command pipelining constraints. + Available in Postfix 3.9, 3.8.4, 3.7.9, 3.6.13, 3.5.23 and later: 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>. + Reject or restrict input lines from an SMTP client that end in + <LF> instead of the standard <CR><LF>. smtpd_forbid_bare_newline_exclusions ($mynetworks) - Exclude the specified clients from smtpd_forbid_bare_newline + Exclude the specified clients from smtpd_forbid_bare_newline enforcement. + Available in Postfix 3.9, 3.8.5, 3.7.10, 3.6.14, 3.5.24 and later: + + smtpd_forbid_bare_newline_reject_code (550) + The numerical Postfix SMTP server response code when rejecting a + request with "smtpd_forbid_bare_newline = reject". + TARPIT CONTROLS When a remote SMTP client makes errors, the Postfix SMTP server can insert delays before responding. This can help to slow down run-away diff -ur --new-file /var/tmp/postfix-3.6.13/man/man5/postconf.5 ./man/man5/postconf.5 --- /var/tmp/postfix-3.6.13/man/man5/postconf.5 2023-12-21 21:23:15.000000000 -0500 +++ ./man/man5/postconf.5 2024-01-21 17:26:47.000000000 -0500 @@ -845,6 +845,32 @@ .fi .ad .ft R +.SH cleanup_replace_stray_cr_lf (default: yes) +Replace each stray or character in message +content with a space character, to prevent outbound SMTP smuggling, +and to make the evaluation of Postfix\-added DKIM or other signatures +independent from how a remote mail server handles such characters. +.PP +SMTP does not allow such characters unless they are part of a + sequence, and different mail systems handle +such stray characters in an implementation\-dependent manner. Stray + or characters could be used for outbound +SMTP smuggling, where an attacker uses a Postfix server to send +message content with a non\-standard End\-of\-DATA sequence that +triggers inbound SMTP smuggling at a remote SMTP server. +.PP +The replacement happens before all other content management, +and before Postfix may add a DKIM etc. signature; if the signature +were created first, the replacement could invalidate the signature. +.PP +In addition to preventing SMTP smuggling, replacing stray + or characters ensures that the result of +signature validation by later mail system will not depend on how +that mail system handles those stray characters in an +implementation\-dependent manner. +.PP +This feature is available in Postfix >= 3.9, 3.8.5, 3.7.10, +3.6.14, and 3.5.24. .SH cleanup_service_name (default: cleanup) The name of the \fBcleanup\fR(8) service. This service rewrites addresses into the standard form, and performs \fBcanonical\fR(5) address mapping @@ -10416,60 +10442,153 @@ .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. +Reject or restrict input lines from an SMTP client that end in + instead of the standard . Such line +endings are commonly allowed with UNIX\-based SMTP servers, but they +violate RFC 5321, and allowing such line endings can make a server +vulnerable to +SMTP smuggling. +.PP +Specify one of the following values (case does not matter): +.IP "\fBnormalize\fR" +Require the standard +End\-of\-DATA sequence .. +Otherwise, allow command or message content lines ending in the +non\-standard , and process them as if the client sent the +standard . +.br +.br +This maintains compatibility +with many legitimate SMTP client applications that send a mix of +standard and non\-standard line endings, but will fail to receive +email from client implementations that do not terminate DATA content +with the standard End\-of\-DATA sequence +.. +.br +.br +Such clients +can be excluded with smtpd_forbid_bare_newline_exclusions. +.br +.IP "\fByes\fR" +Compatibility alias for \fBnormalize\fR. +.br +.IP "\fBreject\fR" +Require the standard End\-of\-DATA +sequence .. Reject a command +or message content when a line contains bare , log a "bare + received" error, and reply with the SMTP status code in +$smtpd_forbid_bare_newline_reject_code. +.br +.br +This will reject +email from SMTP clients that send any non\-standard line endings +such as web applications, netcat, or load balancer health checks. +.br +.br +This will also reject email from services that use BDAT +to send MIME text containing a bare newline (RFC 3030 Section 3 +requires canonical MIME format for text message types, defined in +RFC 2045 Sections 2.7 and 2.8). +.br +.br +Such clients can be +excluded with smtpd_forbid_bare_newline_exclusions (or, in the case +of BDAT violations, BDAT can be selectively disabled with +smtpd_discard_ehlo_keyword_address_maps, or globally disabled with +smtpd_discard_ehlo_keywords). +.br +.IP "\fBno\fR (default)" +Do not require the standard +End\-of\-DATA +sequence .. Always process +a bare as if the client sent . This +option is fully backwards compatible, but is not recommended for +an Internet\-facing SMTP server, because it is vulnerable to SMTP smuggling. +.br +.br .PP -Example: +Recommended settings: .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. +# Require the standard End\-of\-DATA sequence .. +# Otherwise, allow bare and process it as if the client sent +# . # -smtpd_forbid_bare_newline = yes +# This maintains compatibility with many legitimate SMTP client +# applications that send a mix of standard and non\-standard line +# endings, but will fail to receive email from client implementations +# that do not terminate DATA content with the standard End\-of\-DATA +# sequence .. +# +# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions. +# The example below allowlists SMTP clients in trusted networks. +# +smtpd_forbid_bare_newline = normalize 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: +Alternative: .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. +# Reject input lines that contain and log a "bare received" +# error. Require that input lines end in , and require the +# standard End\-of\-DATA sequence .. +# +# This will reject email from SMTP clients that send any non\-standard +# line endings such as web applications, netcat, or load balancer +# health checks. # -smtpd_forbid_bare_newline = yes +# This will also reject email from services that use BDAT to send +# MIME text containing a bare newline (RFC 3030 Section 3 requires +# canonical MIME format for text message types, defined in RFC 2045 +# Sections 2.7 and 2.8). +# +# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions. +# The example below allowlists SMTP clients in trusted networks. +# +smtpd_forbid_bare_newline = reject smtpd_forbid_bare_newline_exclusions = $mynetworks +# +# Alternatively, in the case of BDAT violations, BDAT can be selectively +# disabled with smtpd_discard_ehlo_keyword_address_maps, or globally +# disabled with smtpd_discard_ehlo_keywords. +# +# smtpd_discard_ehlo_keyword_address_maps = cidr:/path/to/file +# /path/to/file: +# 10.0.0.0/24 chunking, silent\-discard +# smtpd_discard_ehlo_keywords = chunking, silent\-discard .fi .ad .ft R .in -4 .PP +This feature with settings \fByes\fR and \fBno\fR is available +in Postfix 3.8.4, 3.7.9, 3.6.13, and 3.5.23. Additionally, the +settings \fBreject\fR, and \fBnormalize\fR are available with +Postfix >= 3.9, 3.8.5, 3.7.10, 3.6.14, and 3.5.24. +.SH smtpd_forbid_bare_newline_exclusions (default: $mynetworks) +Exclude the specified clients from smtpd_forbid_bare_newline +enforcement. This setting uses the same syntax and parent\-domain +matching behavior as mynetworks. +.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_reject_code (default: 550) +The numerical Postfix SMTP server response code when rejecting a +request with "smtpd_forbid_bare_newline = reject". +Specify a 5XX status code (521 to disconnect). +.PP +This feature is available in Postfix >= 3.9, 3.8.5, 3.7.10, +3.6.14, and 3.5.24. .SH smtpd_forbid_unauth_pipelining (default: Postfix >= 3.9: yes) Disconnect remote SMTP clients that violate RFC 2920 (or 5321) command pipelining constraints. The server replies with "554 5.5.0 diff -ur --new-file /var/tmp/postfix-3.6.13/man/man8/cleanup.8 ./man/man8/cleanup.8 --- /var/tmp/postfix-3.6.13/man/man8/cleanup.8 2020-12-05 18:12:08.000000000 -0500 +++ ./man/man8/cleanup.8 2024-01-19 13:52:26.000000000 -0500 @@ -163,6 +163,14 @@ .IP "\fBmessage_strip_characters (empty)\fR" The set of characters that Postfix will remove from message content. +.PP +Available in Postfix version 3.9, 3.8.5, 3.7.10, 3.6.14, +3.5.24, and later: +.IP "\fBcleanup_replace_stray_cr_lf (yes)\fR" +Replace each stray or character in message +content with a space character, to prevent outbound SMTP smuggling, +and to make the evaluation of Postfix\-added DKIM or other signatures +independent from how a remote mail server handles such characters. .SH "BEFORE QUEUE MILTER CONTROLS" .na .nf diff -ur --new-file /var/tmp/postfix-3.6.13/man/man8/smtpd.8 ./man/man8/smtpd.8 --- /var/tmp/postfix-3.6.13/man/man8/smtpd.8 2023-12-22 13:05:48.000000000 -0500 +++ ./man/man8/smtpd.8 2024-01-19 13:53:30.000000000 -0500 @@ -816,19 +816,19 @@ 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.1, 3.7.6, 3.6.10, 3.5.20 and later: -.IP "\fBsmtpd_forbid_unauth_pipelining (Postfix >= 3.9: yes)\fR" -Disconnect remote SMTP clients that violate RFC 2920 (or 5321) -command pipelining constraints. -.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 . +Reject or restrict input lines from an SMTP client that end in + instead of the standard . .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR" Exclude the specified clients from smtpd_forbid_bare_newline enforcement. +.PP +Available in Postfix 3.9, 3.8.5, 3.7.10, 3.6.14, 3.5.24 and +later: +.IP "\fBsmtpd_forbid_bare_newline_reject_code (550)\fR" +The numerical Postfix SMTP server response code when rejecting a +request with "smtpd_forbid_bare_newline = reject". .SH "TARPIT CONTROLS" .na .nf diff -ur --new-file /var/tmp/postfix-3.6.13/mantools/postlink ./mantools/postlink --- /var/tmp/postfix-3.6.13/mantools/postlink 2023-12-21 21:12:15.000000000 -0500 +++ ./mantools/postlink 2024-01-19 13:29:37.000000000 -0500 @@ -548,8 +548,10 @@ s;\bsmtpd_etrn_restrictions\b;$&;g; s;\bsmtpd_expansion_filter\b;$&;g; s;\bsmtpd_for[-]*\n*[ ]*bidden_commands\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*[ ]*bid_bare_new[-]*\n*[ ]*line\b;$&;g; + s;\bsmtpd_for[-]*\n*[ ]*bid_bare_new[-]*\n*[ ]*line_reject_code\b;$&;g; + s;\bsmtpd_for[-]*\n*[ ]*bid_bare_new[-]*\n*[ ]*line_exclusions\b;$&;g; + s;\bcleanup_replace_stray_cr_lf\b;$&;g; s;\bsmtpd_for[-]*\n*[ ]*bid_unauth_pipelining\b;$&;g; s;\bsmtpd_hard_error_limit\b;$&;g; s;\bsmtpd_helo_required\b;$&;g; diff -ur --new-file /var/tmp/postfix-3.6.13/proto/postconf.proto ./proto/postconf.proto --- /var/tmp/postfix-3.6.13/proto/postconf.proto 2023-12-21 21:12:15.000000000 -0500 +++ ./proto/postconf.proto 2024-01-21 16:21:46.000000000 -0500 @@ -18163,55 +18163,169 @@ %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).

+

Reject or restrict input lines from an SMTP client that end in +<LF> instead of the standard <CR><LF>. Such line +endings are commonly allowed with UNIX-based SMTP servers, but they +violate RFC 5321, and allowing such line endings can make a server +vulnerable to +SMTP smuggling.

+ +

Specify one of the following values (case does not matter):

+ +
+ +
normalize
Require the standard +End-of-DATA sequence <CR><LF>.<CR><LF>. +Otherwise, allow command or message content lines ending in the +non-standard <LF>, and process them as if the client sent the +standard <CR><LF>.

This maintains compatibility +with many legitimate SMTP client applications that send a mix of +standard and non-standard line endings, but will fail to receive +email from client implementations that do not terminate DATA content +with the standard End-of-DATA sequence +<CR><LF>.<CR><LF>.

Such clients +can be excluded with smtpd_forbid_bare_newline_exclusions.
+ +
yes
Compatibility alias for normalize.
+ +
reject
Require the standard End-of-DATA +sequence <CR><LF>.<CR><LF>. Reject a command +or message content when a line contains bare <LF>, log a "bare +<LF> received" error, and reply with the SMTP status code in +$smtpd_forbid_bare_newline_reject_code.

This will reject +email from SMTP clients that send any non-standard line endings +such as web applications, netcat, or load balancer health checks. +

This will also reject email from services that use BDAT +to send MIME text containing a bare newline (RFC 3030 Section 3 +requires canonical MIME format for text message types, defined in +RFC 2045 Sections 2.7 and 2.8).

Such clients can be +excluded with smtpd_forbid_bare_newline_exclusions (or, in the case +of BDAT violations, BDAT can be selectively disabled with +smtpd_discard_ehlo_keyword_address_maps, or globally disabled with +smtpd_discard_ehlo_keywords).
+ +
no (default)
Do not require the standard +End-of-DATA +sequence <CR><LF>.<CR><LF>. Always process +a bare <LF> as if the client sent <CR><LF>. This +option is fully backwards compatible, but is not recommended for +an Internet-facing SMTP server, because it is vulnerable to SMTP smuggling. +
-

See -https://www.postfix.org/smtp-smuggling.html for details. +

-

Example:

+

Recommended settings:

-# 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.
+# Require the standard End-of-DATA sequence <CR><LF>.<CR><LF>.
+# Otherwise, allow bare <LF> and process it as if the client sent
+# <CR><LF>.
 #
-smtpd_forbid_bare_newline = yes
+# This maintains compatibility with many legitimate SMTP client
+# applications that send a mix of standard and non-standard line
+# endings, but will fail to receive email from client implementations
+# that do not terminate DATA content with the standard End-of-DATA
+# sequence <CR><LF>.<CR><LF>.
+#
+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
+# The example below allowlists SMTP clients in trusted networks.
+#
+smtpd_forbid_bare_newline = normalize
 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:

+

Alternative:

-# 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.
+# Reject input lines that contain <LF> and log a "bare <LF> received"
+# error. Require that input lines end in <CR><LF>, and require the
+# standard End-of-DATA sequence <CR><LF>.<CR><LF>.
+#
+# This will reject email from SMTP clients that send any non-standard
+# line endings such as web applications, netcat, or load balancer
+# health checks.
+#
+# This will also reject email from services that use BDAT to send
+# MIME text containing a bare newline (RFC 3030 Section 3 requires
+# canonical MIME format for text message types, defined in RFC 2045
+# Sections 2.7 and 2.8).
 #
-smtpd_forbid_bare_newline = yes
+# Such clients can be allowlisted with smtpd_forbid_bare_newline_exclusions.
+# The example below allowlists SMTP clients in trusted networks.
+#
+smtpd_forbid_bare_newline = reject
 smtpd_forbid_bare_newline_exclusions = $mynetworks
+#
+# Alternatively, in the case of BDAT violations, BDAT can be selectively
+# disabled with smtpd_discard_ehlo_keyword_address_maps, or globally
+# disabled with smtpd_discard_ehlo_keywords.
+#
+# smtpd_discard_ehlo_keyword_address_maps = cidr:/path/to/file
+# /path/to/file:
+#     10.0.0.0/24 chunking, silent-discard
+# smtpd_discard_ehlo_keywords = chunking, silent-discard
 
+

This feature with settings yes and no is available +in Postfix 3.8.4, 3.7.9, 3.6.13, and 3.5.23. Additionally, the +settings reject, and normalize are available with +Postfix ≥ 3.9, 3.8.5, 3.7.10, 3.6.14, and 3.5.24.

+ +%PARAM smtpd_forbid_bare_newline_exclusions $mynetworks + +

Exclude the specified clients from smtpd_forbid_bare_newline +enforcement. This setting uses the same syntax and parent-domain +matching behavior as 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_reject_code 550 + +

+The numerical Postfix SMTP server response code when rejecting a +request with "smtpd_forbid_bare_newline = reject". +Specify a 5XX status code (521 to disconnect). +

+ +

This feature is available in Postfix ≥ 3.9, 3.8.5, 3.7.10, +3.6.14, and 3.5.24.

+ +%PARAM cleanup_replace_stray_cr_lf yes + +

Replace each stray <CR> or <LF> character in message +content with a space character, to prevent outbound SMTP smuggling, +and to make the evaluation of Postfix-added DKIM or other signatures +independent from how a remote mail server handles such characters. +

+ +

SMTP does not allow such characters unless they are part of a +<CR><LF> sequence, and different mail systems handle +such stray characters in an implementation-dependent manner. Stray +<CR> or <LF> characters could be used for outbound +SMTP smuggling, where an attacker uses a Postfix server to send +message content with a non-standard End-of-DATA sequence that +triggers inbound SMTP smuggling at a remote SMTP server.

+ +

The replacement happens before all other content management, +and before Postfix may add a DKIM etc. signature; if the signature +were created first, the replacement could invalidate the signature. +

+ +

In addition to preventing SMTP smuggling, replacing stray +<CR> or <LF> characters ensures that the result of +signature validation by later mail system will not depend on how +that mail system handles those stray characters in an +implementation-dependent manner.

+ +

This feature is available in Postfix ≥ 3.9, 3.8.5, 3.7.10, +3.6.14, and 3.5.24.

+ %PARAM smtpd_forbid_unauth_pipelining Postfix ≥ 3.9: yes

Disconnect remote SMTP clients that violate RFC 2920 (or 5321) diff -ur --new-file /var/tmp/postfix-3.6.13/src/cleanup/cleanup.c ./src/cleanup/cleanup.c --- /var/tmp/postfix-3.6.13/src/cleanup/cleanup.c 2020-12-02 17:39:28.000000000 -0500 +++ ./src/cleanup/cleanup.c 2024-01-19 13:29:37.000000000 -0500 @@ -145,6 +145,14 @@ /* .IP "\fBmessage_strip_characters (empty)\fR" /* The set of characters that Postfix will remove from message /* content. +/* .PP +/* Available in Postfix version 3.9, 3.8.5, 3.7.10, 3.6.14, +/* 3.5.24, and later: +/* .IP "\fBcleanup_replace_stray_cr_lf (yes)\fR" +/* Replace each stray or character in message +/* content with a space character, to prevent outbound SMTP smuggling, +/* and to make the evaluation of Postfix-added DKIM or other signatures +/* independent from how a remote mail server handles such characters. /* BEFORE QUEUE MILTER CONTROLS /* .ad /* .fi diff -ur --new-file /var/tmp/postfix-3.6.13/src/cleanup/cleanup_init.c ./src/cleanup/cleanup_init.c --- /var/tmp/postfix-3.6.13/src/cleanup/cleanup_init.c 2022-04-17 18:04:43.000000000 -0400 +++ ./src/cleanup/cleanup_init.c 2024-01-19 13:29:37.000000000 -0500 @@ -173,6 +173,7 @@ int var_always_add_hdrs; /* always add missing headers */ int var_virt_addrlen_limit; /* stop exponential growth */ char *var_hfrom_format; /* header_from_format */ +int var_cleanup_mask_stray_cr_lf; /* replace stray CR or LF with space */ const CONFIG_INT_TABLE cleanup_int_table[] = { VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0, @@ -189,6 +190,7 @@ VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off, VAR_AUTO_8BIT_ENC_HDR, DEF_AUTO_8BIT_ENC_HDR, &var_auto_8bit_enc_hdr, VAR_ALWAYS_ADD_HDRS, DEF_ALWAYS_ADD_HDRS, &var_always_add_hdrs, + VAR_CLEANUP_MASK_STRAY_CR_LF, DEF_CLEANUP_MASK_STRAY_CR_LF, &var_cleanup_mask_stray_cr_lf, 0, }; diff -ur --new-file /var/tmp/postfix-3.6.13/src/cleanup/cleanup_message.c ./src/cleanup/cleanup_message.c --- /var/tmp/postfix-3.6.13/src/cleanup/cleanup_message.c 2021-07-07 18:09:51.000000000 -0400 +++ ./src/cleanup/cleanup_message.c 2024-01-19 13:29:37.000000000 -0500 @@ -930,6 +930,23 @@ char *dst; /* + * Replace each stray CR or LF with one space. These are not allowed in + * SMTP, and can be used to enable outbound (remote) SMTP smuggling. + * Replacing these early ensures that our later DKIM etc. signature will + * not be invalidated. Besides preventing SMTP smuggling, replacing stray + * or ensures that the result of signature validation by a + * later mail system will not depend on how that mail system handles + * those stray characters in an implementation-dependent manner. + * + * The input length is not changed, therefore it is safe to overwrite the + * input. + */ + if (var_cleanup_mask_stray_cr_lf) + for (dst = (char *) buf; dst < buf + len; dst++) + if (*dst == '\r' || *dst == '\n') + *dst = ' '; + + /* * Reject unwanted characters. * * XXX Possible optimization: simplify the loop when the "reject" set diff -ur --new-file /var/tmp/postfix-3.6.13/src/global/cleanup_strerror.c ./src/global/cleanup_strerror.c --- /var/tmp/postfix-3.6.13/src/global/cleanup_strerror.c 2020-11-04 19:37:47.000000000 -0500 +++ ./src/global/cleanup_strerror.c 2024-01-19 13:29:37.000000000 -0500 @@ -73,6 +73,7 @@ CLEANUP_STAT_CONT, 550, "5.7.1", "message content rejected", CLEANUP_STAT_WRITE, 451, "4.3.0", "queue file write error", CLEANUP_STAT_NOPERM, 550, "5.7.1", "service denied", + CLEANUP_STAT_BARE_LF, 521, "5.5.2", "bare received", }; static CLEANUP_STAT_DETAIL cleanup_stat_success = { diff -ur --new-file /var/tmp/postfix-3.6.13/src/global/cleanup_user.h ./src/global/cleanup_user.h --- /var/tmp/postfix-3.6.13/src/global/cleanup_user.h 2020-11-04 19:37:47.000000000 -0500 +++ ./src/global/cleanup_user.h 2024-01-19 13:29:37.000000000 -0500 @@ -65,6 +65,12 @@ #define CLEANUP_STAT_NOPERM (1<<9) /* Denied by non-content policy */ /* + * Non-cleanup errors that live in the same bitmask space, to centralize + * error handling. + */ +#define CLEANUP_STAT_BARE_LF (1<<16) /* Bare received */ + + /* * These are set when we can't bounce even if we were asked to. */ #define CLEANUP_STAT_MASK_CANT_BOUNCE \ diff -ur --new-file /var/tmp/postfix-3.6.13/src/global/mail_params.h ./src/global/mail_params.h --- /var/tmp/postfix-3.6.13/src/global/mail_params.h 2023-12-21 21:31:04.000000000 -0500 +++ ./src/global/mail_params.h 2024-01-19 13:29:37.000000000 -0500 @@ -4212,11 +4212,18 @@ * Backwards compatibility. */ #define VAR_SMTPD_FORBID_BARE_LF "smtpd_forbid_bare_newline" -#define DEF_SMTPD_FORBID_BARE_LF 0 +#define DEF_SMTPD_FORBID_BARE_LF "no" #define VAR_SMTPD_FORBID_BARE_LF_EXCL "smtpd_forbid_bare_newline_exclusions" #define DEF_SMTPD_FORBID_BARE_LF_EXCL "$" VAR_MYNETWORKS +#define VAR_SMTPD_FORBID_BARE_LF_CODE "smtpd_forbid_bare_newline_reject_code" +#define DEF_SMTPD_FORBID_BARE_LF_CODE 550 + +#define VAR_CLEANUP_MASK_STRAY_CR_LF "cleanup_replace_stray_cr_lf" +#define DEF_CLEANUP_MASK_STRAY_CR_LF 1 +extern int var_cleanup_mask_stray_cr_lf; + /* * Share TLS sessions through tlsproxy(8). */ diff -ur --new-file /var/tmp/postfix-3.6.13/src/global/smtp_stream.c ./src/global/smtp_stream.c --- /var/tmp/postfix-3.6.13/src/global/smtp_stream.c 2023-12-21 21:20:01.000000000 -0500 +++ ./src/global/smtp_stream.c 2024-01-19 13:35:00.000000000 -0500 @@ -51,7 +51,8 @@ /* char *format; /* va_list ap; /* -/* int smtp_forbid_bare_lf; +/* int smtp_detect_bare_lf; +/* int smtp_got_bare_lf; /* AUXILIARY API /* int smtp_get_noexcept(vp, stream, maxlen, flags) /* VSTRING *vp; @@ -126,16 +127,16 @@ /* smtp_vprintf() is the machine underneath smtp_printf(). /* /* smtp_get_noexcept() implements the subset of smtp_get() -/* without long jumps for timeout or EOF errors. Instead, +/* without timeouts and without making long jumps. 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. +/* +/* This function assigns smtp_got_bare_lf = smtp_detect_bare_lf, +/* if smtp_detect_bare_lf is non-zero and the last read line +/* was terminated with a bare newline. Otherwise, this function +/* sets smtp_got_bare_lf to zero. /* /* 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 @@ -208,7 +209,8 @@ #include "smtp_stream.h" -int smtp_forbid_bare_lf; +int smtp_detect_bare_lf; +int smtp_got_bare_lf; /* smtp_timeout_reset - reset per-stream error flags, restart deadline timer */ @@ -371,6 +373,8 @@ int last_char; int next_char; + smtp_got_bare_lf = 0; + /* * It's painful to do I/O with records that may span multiple buffers. * Allow for partial long lines (we will read the remainder later) and @@ -413,11 +417,15 @@ */ 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); + if (smtp_detect_bare_lf) { + if (VSTRING_LEN(vp) == 0 || vstring_end(vp)[-1] != '\r') + smtp_got_bare_lf = smtp_detect_bare_lf; + else + vstring_truncate(vp, VSTRING_LEN(vp) - 1); + } else { + while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r') + vstring_truncate(vp, VSTRING_LEN(vp) - 1); + } VSTRING_TERMINATE(vp); /* FALLTRHOUGH */ diff -ur --new-file /var/tmp/postfix-3.6.13/src/global/smtp_stream.h ./src/global/smtp_stream.h --- /var/tmp/postfix-3.6.13/src/global/smtp_stream.h 2023-12-21 21:12:15.000000000 -0500 +++ ./src/global/smtp_stream.h 2024-01-19 13:29:38.000000000 -0500 @@ -32,7 +32,6 @@ #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 *,...); @@ -44,7 +43,8 @@ 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 int smtp_detect_bare_lf; +extern int smtp_got_bare_lf; extern void smtp_vprintf(VSTREAM *, const char *, va_list); diff -ur --new-file /var/tmp/postfix-3.6.13/src/smtpd/smtpd.c ./src/smtpd/smtpd.c --- /var/tmp/postfix-3.6.13/src/smtpd/smtpd.c 2023-12-22 08:44:32.000000000 -0500 +++ ./src/smtpd/smtpd.c 2024-01-19 13:38:45.000000000 -0500 @@ -777,12 +777,17 @@ /* .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 . +/* Reject or restrict input lines from an SMTP client that end in +/* instead of the standard . /* .IP "\fBsmtpd_forbid_bare_newline_exclusions ($mynetworks)\fR" /* Exclude the specified clients from smtpd_forbid_bare_newline /* enforcement. +/* .PP +/* Available in Postfix 3.9, 3.8.5, 3.7.10, 3.6.14, 3.5.24 and +/* later: +/* .IP "\fBsmtpd_forbid_bare_newline_reject_code (550)\fR" +/* The numerical Postfix SMTP server response code when rejecting a +/* request with "smtpd_forbid_bare_newline = reject". /* TARPIT CONTROLS /* .ad /* .fi @@ -1489,8 +1494,10 @@ int var_smtpd_uproxy_tmout; bool var_relay_before_rcpt_checks; -bool var_smtpd_forbid_bare_lf; +char *var_smtpd_forbid_bare_lf; char *var_smtpd_forbid_bare_lf_excl; +int var_smtpd_forbid_bare_lf_code; +static int bare_lf_mask; static NAMADR_LIST *bare_lf_excl; /* @@ -1567,7 +1574,6 @@ #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 @@ -1586,6 +1592,40 @@ */ static DICT *smtpd_cmd_filter; + /* + * Bare LF and End-of-DATA controls (bare CR is handled elsewhere). + * + * At the smtp_get*() line reader level, setting any of these flags in the + * smtp_detect_bare_lf variable enables the detection of bare newlines. The + * line reader will set the same flags in the smtp_got_bare_lf variable + * after it detects a bare newline, otherwise it clears smtp_got_bare_lf. + * + * At the SMTP command level, the flags in smtp_got_bare_lf control whether + * commands ending in a bare newline are rejected. + * + * At the DATA and BDAT content level, the flags in smtp_got_bare_lf control + * whether the standard End-of-DATA sequence CRLF.CRLF is required, and + * whether lines ending in bare newlines are rejected. + * + * Postfix implements "delayed reject" after detecting a bare newline in BDAT + * or DATA content. The SMTP server delays a REJECT response until the + * command is finished, instead of replying and hanging up immediately. The + * End-of-DATA detection is secured with BARE_LF_FLAG_WANT_STD_EOD. + */ +#define BARE_LF_FLAG_WANT_STD_EOD (1<<0) /* Require CRLF.CRLF */ +#define BARE_LF_FLAG_REPLY_REJECT (1<<1) /* Reject bare newline */ + +#define IS_BARE_LF_WANT_STD_EOD(m) ((m) & BARE_LF_FLAG_WANT_STD_EOD) +#define IS_BARE_LF_REPLY_REJECT(m) ((m) & BARE_LF_FLAG_REPLY_REJECT) + +static const NAME_CODE bare_lf_mask_table[] = { + "normalize", BARE_LF_FLAG_WANT_STD_EOD, /* Default */ + "yes", BARE_LF_FLAG_WANT_STD_EOD, /* Migration aid */ + "reject", BARE_LF_FLAG_WANT_STD_EOD | BARE_LF_FLAG_REPLY_REJECT, + "no", 0, + 0, -1, /* error */ +}; + #ifdef USE_SASL_AUTH /* @@ -3528,6 +3568,7 @@ int curr_rec_type; int prev_rec_type; int first = 1; + int prev_got_bare_lf = 0; /* * Copy the message content. If the cleanup process has a problem, keep @@ -3541,12 +3582,15 @@ * XXX Deal with UNIX-style From_ lines at the start of message content * because sendmail permits it. */ - for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type) { + for (prev_rec_type = 0; /* void */ ; prev_rec_type = curr_rec_type, + prev_got_bare_lf = smtp_got_bare_lf) { if (smtp_get(state->buffer, state->client, var_line_limit, SMTP_GET_FLAG_NONE) == '\n') curr_rec_type = REC_TYPE_NORM; else curr_rec_type = REC_TYPE_CONT; + if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) + state->err |= CLEANUP_STAT_BARE_LF; start = vstring_str(state->buffer); len = VSTRING_LEN(state->buffer); if (first) { @@ -3559,9 +3603,14 @@ if (len > 0 && IS_SPACE_TAB(start[0])) out_record(out_stream, REC_TYPE_NORM, "", 0); } - if (prev_rec_type != REC_TYPE_CONT && *start == '.' - && (proxy == 0 ? (++start, --len) == 0 : len == 1)) - break; + if (prev_rec_type != REC_TYPE_CONT && *start == '.') { + if (len == 1 && IS_BARE_LF_WANT_STD_EOD(smtp_detect_bare_lf) + && (smtp_got_bare_lf || prev_got_bare_lf)) + /* Do not store or send to proxy filter. */ + continue; + if (proxy == 0 ? (++start, --len) == 0 : len == 1) + break; + } if (state->err == CLEANUP_STAT_OK) { if (ENFORCING_SIZE_LIMIT(var_message_limit) && var_message_limit - state->act_size < len + 2) { @@ -3714,6 +3763,11 @@ else smtpd_chat_reply(state, "250 2.0.0 Ok: queued as %s", state->queue_id); + } else if ((state->err & CLEANUP_STAT_BARE_LF) != 0) { + state->error_mask |= MAIL_ERROR_PROTOCOL; + log_whatsup(state, "reject", "bare received"); + smtpd_chat_reply(state, "%d 5.5.2 %s Error: bare received", + var_smtpd_forbid_bare_lf_code, var_myhostname); } else if (why && IS_SMTP_REJECT(STR(why))) { state->error_mask |= MAIL_ERROR_POLICY; smtpd_chat_reply(state, "%s", STR(why)); @@ -3994,7 +4048,6 @@ */ done = 0; do { - int payload_err; /* * Do not skip the smtp_fread_buf() call if read_len == 0. We still @@ -4008,10 +4061,6 @@ 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 @@ -4036,6 +4085,8 @@ /* Skip the out_record() and VSTRING_RESET() calls below. */ break; } + if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) + state->err |= CLEANUP_STAT_BARE_LF; start = vstring_str(state->bdat_get_buffer); len = VSTRING_LEN(state->bdat_get_buffer); if (state->err == CLEANUP_STAT_OK) { @@ -4687,9 +4738,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); + smtp_detect_bare_lf = (SMTPD_STAND_ALONE((state)) == 0 && bare_lf_mask + && !namadr_list_match(bare_lf_excl, state->name, state->addr)) ? + bare_lf_mask : 0; /* NOT: tls_reset() */ if (got_helo == 0) helo_reset(state); @@ -5508,13 +5559,6 @@ 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: /* @@ -5731,6 +5775,13 @@ } watchdog_pat(); smtpd_chat_query(state); + if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) { + log_whatsup(state, "reject", "bare received"); + state->error_mask |= MAIL_ERROR_PROTOCOL; + smtpd_chat_reply(state, "%d 5.5.2 %s Error: bare received", + var_smtpd_forbid_bare_lf_code, var_myhostname); + break; + } /* Safety: protect internal interfaces against malformed UTF-8. */ if (var_smtputf8_enable && valid_utf8_string(STR(state->buffer), LEN(state->buffer)) == 0) { @@ -6074,11 +6125,11 @@ namadr_list_match(xforward_hosts, state.name, state.addr); /* - * Enforce strict SMTP line endings, with compatibility exclusions. + * Reject or normalize bare LF, 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); + smtp_detect_bare_lf = (SMTPD_STAND_ALONE((&state)) == 0 && bare_lf_mask + && !namadr_list_match(bare_lf_excl, state.name, state.addr)) ? + bare_lf_mask : 0; /* * See if we need to turn on verbose logging for this client. @@ -6145,6 +6196,10 @@ MATCH_FLAG_RETURN | match_parent_style(VAR_MYNETWORKS), var_smtpd_forbid_bare_lf_excl); + if ((bare_lf_mask = name_code(bare_lf_mask_table, NAME_CODE_FLAG_NONE, + var_smtpd_forbid_bare_lf)) < 0) + msg_fatal("bad parameter value: '%s = %s'", + VAR_SMTPD_FORBID_BARE_LF, var_smtpd_forbid_bare_lf); /* * Open maps before dropping privileges so we can read passwords etc. @@ -6440,6 +6495,7 @@ VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0, VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0, VAR_PLAINTEXT_CODE, DEF_PLAINTEXT_CODE, &var_plaintext_code, 0, 0, + VAR_SMTPD_FORBID_BARE_LF_CODE, DEF_SMTPD_FORBID_BARE_LF_CODE, &var_smtpd_forbid_bare_lf_code, 500, 599, VAR_SMTPD_CRATE_LIMIT, DEF_SMTPD_CRATE_LIMIT, &var_smtpd_crate_limit, 0, 0, VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0, VAR_SMTPD_CMAIL_LIMIT, DEF_SMTPD_CMAIL_LIMIT, &var_smtpd_cmail_limit, 0, 0, @@ -6503,7 +6559,6 @@ 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_UNAUTH_PIPE, DEF_SMTPD_FORBID_UNAUTH_PIPE, &var_smtpd_forbid_unauth_pipe, - VAR_SMTPD_FORBID_BARE_LF, DEF_SMTPD_FORBID_BARE_LF, &var_smtpd_forbid_bare_lf, 0, }; static const CONFIG_NBOOL_TABLE nbool_table[] = { @@ -6620,6 +6675,7 @@ 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, + VAR_SMTPD_FORBID_BARE_LF, DEF_SMTPD_FORBID_BARE_LF, &var_smtpd_forbid_bare_lf, 1, 0, 0, }; static const CONFIG_RAW_TABLE raw_table[] = { diff -ur --new-file /var/tmp/postfix-3.6.13/src/smtpd/smtpd_check.c ./src/smtpd/smtpd_check.c --- /var/tmp/postfix-3.6.13/src/smtpd/smtpd_check.c 2021-11-06 19:43:54.000000000 -0400 +++ ./src/smtpd/smtpd_check.c 2024-01-19 13:29:38.000000000 -0500 @@ -48,6 +48,11 @@ /* /* char *smtpd_check_queue(state) /* SMTPD_STATE *state; +/* AUXILIARY FUNCTIONS +/* void log_whatsup(state, action, text) +/* SMTPD_STATE *state; +/* const char *action; +/* const char *text; /* DESCRIPTION /* This module implements additional checks on SMTP client requests. /* A client request is validated in the context of the session state. @@ -146,6 +151,11 @@ /* The recipient address given with the RCPT TO or VRFY command. /* .IP size /* The message size given with the MAIL FROM command (zero if unknown). +/* .PP +/* log_whatsup() logs ": : +/* from: : " plus the protocol +/* (SMTP or ESMTP), and if available, EHLO, MAIL FROM, or RCPT +/* TO. /* BUGS /* Policies like these should not be hard-coded in C, but should /* be user-programmable instead. @@ -987,8 +997,8 @@ /* log_whatsup - log as much context as we have */ -static void log_whatsup(SMTPD_STATE *state, const char *whatsup, - const char *text) +void log_whatsup(SMTPD_STATE *state, const char *whatsup, + const char *text) { VSTRING *buf = vstring_alloc(100); diff -ur --new-file /var/tmp/postfix-3.6.13/src/smtpd/smtpd_check.h ./src/smtpd/smtpd_check.h --- /var/tmp/postfix-3.6.13/src/smtpd/smtpd_check.h 2017-12-27 17:29:45.000000000 -0500 +++ ./src/smtpd/smtpd_check.h 2024-01-19 13:29:38.000000000 -0500 @@ -25,6 +25,7 @@ extern char *smtpd_check_data(SMTPD_STATE *); extern char *smtpd_check_eod(SMTPD_STATE *); extern char *smtpd_check_policy(SMTPD_STATE *, char *); +extern void log_whatsup(SMTPD_STATE *, const char *, const char *); /* LICENSE /* .ad