From: Wietse Z Venema
Date: Wed, 5 Nov 2025 05:00:00 +0000 (-0500)
Subject: postfix-3.11-20251105
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=41e7f801993315f0f3a29811be112363842a2b64;p=thirdparty%2Fpostfix.git
postfix-3.11-20251105
---
diff --git a/postfix/HISTORY b/postfix/HISTORY
index eba515c89..69a471f42 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -29785,3 +29785,25 @@ Apologies for any names omitted.
avoid problems with infrastructure that mis-handles TLS
hello messages larger than one (Ethernet) TCP segment.
Viktor Dukhovni. Files: global/mail_params.h, proto/postconf.proto.
+
+20251103
+
+ Testing: fix bitrot in TLS DANE tests. Viktor Dukhovni.
+ Files: tls/tls_dane.c, tls/tls_dane.sh.
+
+20251104
+
+ Feature: non_empty_end_of_header_action specifies the
+ cleanup(8) server action when a primary message header is
+ terminated with a non-empty line: 1) insert an empty line
+ before the offending text (the backwards-compatible default),
+ 2) insert a MIME-Error: header before inserting an empty
+ line, or 3) log an error and reject the message. Files:
+ global/mime-state.[hc], global/mime_garb3.ref,
+ global/mail_params.h, cleanup/cleanup_message.c.
+
+20251104
+
+ Documentation: in OVERVIEW_README, added a missing link
+ between smtpd(8) and trivial-rewrite(8). File:
+ proto/OVERVIEW_README.html.
diff --git a/postfix/README_FILES/OVERVIEW b/postfix/README_FILES/OVERVIEW
index 45762f65e..08a7284dc 100644
--- a/postfix/README_FILES/OVERVIEW
+++ b/postfix/README_FILES/OVERVIEW
@@ -24,7 +24,7 @@ the incoming queue. The figure below shows the main processes that are involved
with new mail. Names followed by a number are Postfix commands or server
programs, while unnumbered names inside shaded areas represent Postfix queues.
- trivial-
+ / trivial-
rewrite(8)
Network -> smtpd(8)
diff --git a/postfix/html/OVERVIEW.html b/postfix/html/OVERVIEW.html
index bd7b8c4a6..a1c262923 100644
--- a/postfix/html/OVERVIEW.html
+++ b/postfix/html/OVERVIEW.html
@@ -56,12 +56,16 @@ names inside shaded areas represent Postfix queues.
-| |
+ |
- trivial- rewrite(8) |
+ |
+
+ trivial- rewrite(8) |
+ | | / |
+
| Network | -> |
diff --git a/postfix/html/cleanup.8.html b/postfix/html/cleanup.8.html
index 655275a9e..bc1a835c0 100644
--- a/postfix/html/cleanup.8.html
+++ b/postfix/html/cleanup.8.html
@@ -132,77 +132,81 @@ CLEANUP(8) CLEANUP(8)
output when it automatically generates an RFC 2047 encoded full
name.
+ non_empty_end_of_header_action (fix_quietly)
+ How the cleanup(8) daemon will process a message when the pri-
+ mary message header is terminated with a non-empty line.
+
BUILT-IN CONTENT FILTERING CONTROLS
Postfix built-in content filtering is meant to stop a flood of worms or
viruses. It is not a general content filter.
body_checks (empty)
- Optional lookup tables for content inspection as specified in
+ Optional lookup tables for content inspection as specified in
the body_checks(5) manual page.
header_checks (empty)
- Optional lookup tables for content inspection of primary
- non-MIME message headers, as specified in the header_checks(5)
+ Optional lookup tables for content inspection of primary
+ non-MIME message headers, as specified in the header_checks(5)
manual page.
Available in Postfix version 2.0 and later:
body_checks_size_limit (51200)
- How much text in a message body segment (or attachment, if you
+ How much text in a message body segment (or attachment, if you
prefer to use that term) is subjected to body_checks inspection.
mime_header_checks ($header_checks)
- Optional lookup tables for content inspection of MIME related
- message headers, as described in the header_checks(5) manual
+ Optional lookup tables for content inspection of MIME related
+ message headers, as described in the header_checks(5) manual
page.
nested_header_checks ($header_checks)
- Optional lookup tables for content inspection of non-MIME mes-
- sage headers in attached messages, as described in the
+ Optional lookup tables for content inspection of non-MIME mes-
+ sage headers in attached messages, as described in the
header_checks(5) manual page.
Available in Postfix version 2.3 and later:
message_reject_characters (empty)
- The set of characters that Postfix will reject in message con-
+ The set of characters that Postfix will reject in message con-
tent.
message_strip_characters (empty)
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
+ 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
+ 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-
+ 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,
+ filter) protocol. When mail is not received via the smtpd(8) server,
the cleanup(8) server will simulate SMTP events to the extent that this
is possible. For details see the MILTER_README document.
non_smtpd_milters (empty)
- A list of Milter (mail filter) applications for new mail that
+ A list of Milter (mail filter) applications for new mail that
does not arrive via the Postfix smtpd(8) server.
milter_protocol (6)
- The mail filter protocol version and optional protocol exten-
- sions for communication with a Milter application; prior to
+ The mail filter protocol version and optional protocol exten-
+ sions for communication with a Milter application; prior to
Postfix 2.6 the default protocol is 2.
milter_default_action (tempfail)
- The default action when a Milter (mail filter) response is
- unavailable (for example, bad Postfix configuration or Milter
+ The default action when a Milter (mail filter) response is
+ unavailable (for example, bad Postfix configuration or Milter
failure).
milter_macro_daemon_name ($myhostname)
- The {daemon_name} macro value for Milter (mail filter) applica-
+ The {daemon_name} macro value for Milter (mail filter) applica-
tions.
milter_macro_v ($mail_name $mail_version)
@@ -213,45 +217,45 @@ CLEANUP(8) CLEANUP(8)
tion, and for negotiating protocol options.
milter_command_timeout (30s)
- The time limit for sending an SMTP command to a Milter (mail
+ The time limit for sending an SMTP command to a Milter (mail
filter) application, and for receiving the response.
milter_content_timeout (300s)
- The time limit for sending message content to a Milter (mail
+ The time limit for sending message content to a Milter (mail
filter) application, and for receiving the response.
milter_connect_macros (see 'postconf -d' output)
- The macros that are sent to Milter (mail filter) applications
+ The macros that are sent to Milter (mail filter) applications
after completion of an SMTP connection.
milter_helo_macros (see 'postconf -d' output)
- The macros that are sent to Milter (mail filter) applications
+ The macros that are sent to Milter (mail filter) applications
after the SMTP HELO or EHLO command.
milter_mail_macros (see 'postconf -d' output)
- The macros that are sent to Milter (mail filter) applications
+ The macros that are sent to Milter (mail filter) applications
after the SMTP MAIL FROM command.
milter_rcpt_macros (see 'postconf -d' output)
- The macros that are sent to Milter (mail filter) applications
+ The macros that are sent to Milter (mail filter) applications
after the SMTP RCPT TO command.
milter_data_macros (see 'postconf -d' output)
- The macros that are sent to version 4 or higher Milter (mail
+ The macros that are sent to version 4 or higher Milter (mail
filter) applications after the SMTP DATA command.
milter_unknown_command_macros (see 'postconf -d' output)
- The macros that are sent to version 3 or higher Milter (mail
+ The macros that are sent to version 3 or higher Milter (mail
filter) applications after an unknown SMTP command.
milter_end_of_data_macros (see 'postconf -d' output)
- The macros that are sent to Milter (mail filter) applications
+ The macros that are sent to Milter (mail filter) applications
after the message end-of-data.
Available in Postfix version 2.5 and later:
milter_end_of_header_macros (see 'postconf -d' output)
- The macros that are sent to Milter (mail filter) applications
+ The macros that are sent to Milter (mail filter) applications
after the end of the message header.
Available in Postfix version 2.7 and later:
@@ -263,8 +267,8 @@ CLEANUP(8) CLEANUP(8)
Available in Postfix version 3.1 and later:
milter_macro_defaults (empty)
- Optional list of name=value pairs that specify default values
- for arbitrary macros that Postfix may send to Milter applica-
+ Optional list of name=value pairs that specify default values
+ for arbitrary macros that Postfix may send to Milter applica-
tions.
MIME PROCESSING CONTROLS
@@ -290,74 +294,74 @@ CLEANUP(8) CLEANUP(8)
ing information.
strict_mime_encoding_domain (no)
- Reject mail with invalid Content-Transfer-Encoding: information
+ Reject mail with invalid Content-Transfer-Encoding: information
for the message/* or multipart/* MIME content types.
Available in Postfix version 2.5 and later:
detect_8bit_encoding_header (yes)
- Automatically detect 8BITMIME body content by looking at Con-
- tent-Transfer-Encoding: message headers; historically, this
+ Automatically detect 8BITMIME body content by looking at Con-
+ tent-Transfer-Encoding: message headers; historically, this
behavior was hard-coded to be "always on".
AUTOMATIC BCC RECIPIENT CONTROLS
- Postfix can automatically add BCC (blind carbon copy) when mail enters
+ Postfix can automatically add BCC (blind carbon copy) when mail enters
the mail system:
always_bcc (empty)
- Optional address that receives a "blind carbon copy" of each
+ Optional address that receives a "blind carbon copy" of each
message that is received by the Postfix mail system.
Available in Postfix version 2.1 and later:
sender_bcc_maps (empty)
- Optional BCC (blind carbon-copy) address lookup tables, indexed
+ Optional BCC (blind carbon-copy) address lookup tables, indexed
by envelope sender address.
recipient_bcc_maps (empty)
- Optional BCC (blind carbon-copy) address lookup tables, indexed
+ Optional BCC (blind carbon-copy) address lookup tables, indexed
by envelope recipient address.
ADDRESS TRANSFORMATION CONTROLS
- Address rewriting is delegated to the trivial-rewrite(8) daemon. The
+ Address rewriting is delegated to the trivial-rewrite(8) daemon. The
cleanup(8) server implements table driven address mapping.
empty_address_recipient (MAILER-DAEMON)
The recipient of mail addressed to the null address.
canonical_maps (empty)
- Optional address mapping lookup tables for message headers and
+ Optional address mapping lookup tables for message headers and
envelopes.
recipient_canonical_maps (empty)
- Optional address mapping lookup tables for envelope and header
+ Optional address mapping lookup tables for envelope and header
recipient addresses.
sender_canonical_maps (empty)
- Optional address mapping lookup tables for envelope and header
+ Optional address mapping lookup tables for envelope and header
sender addresses.
masquerade_classes (envelope_sender, header_sender, header_recipient)
What addresses are subject to address masquerading.
masquerade_domains (empty)
- Optional list of domains whose subdomain structure will be
+ Optional list of domains whose subdomain structure will be
stripped off in email addresses.
masquerade_exceptions (empty)
- Optional list of user names that are not subjected to address
- masquerading, even when their addresses match $masquer-
+ Optional list of user names that are not subjected to address
+ masquerading, even when their addresses match $masquer-
ade_domains.
propagate_unmatched_extensions (canonical, virtual)
- What address lookup tables copy an address extension from the
+ What address lookup tables copy an address extension from the
lookup key to the lookup result.
Available before Postfix version 2.0:
virtual_maps (empty)
- Optional lookup tables with a) names of domains for which all
- addresses are aliased to addresses in other local or remote
+ Optional lookup tables with a) names of domains for which all
+ addresses are aliased to addresses in other local or remote
domains, and b) addresses that are aliased to addresses in other
local or remote domains.
@@ -365,19 +369,19 @@ CLEANUP(8) CLEANUP(8)
virtual_alias_maps ($virtual_maps)
Optional lookup tables that are often searched with a full email
- address (including domain) and that apply to all recipients:
- local(8), virtual, and remote; this is unlike alias_maps that
- are only searched with an email address localpart (no domain)
+ address (including domain) and that apply to all recipients:
+ local(8), virtual, and remote; this is unlike alias_maps that
+ are only searched with an email address localpart (no domain)
and that apply only to local(8) recipients.
Available in Postfix version 2.2 and later:
- canonical_classes (envelope_sender, envelope_recipient, header_sender,
+ canonical_classes (envelope_sender, envelope_recipient, header_sender,
header_recipient)
What addresses are subject to canonical_maps address mapping.
recipient_canonical_classes (envelope_recipient, header_recipient)
- What addresses are subject to recipient_canonical_maps address
+ What addresses are subject to recipient_canonical_maps address
mapping.
sender_canonical_classes (envelope_sender, header_sender)
@@ -385,9 +389,9 @@ CLEANUP(8) CLEANUP(8)
ping.
remote_header_rewrite_domain (empty)
- Rewrite or add message headers in mail from remote clients if
- the remote_header_rewrite_domain parameter value is non-empty,
- updating incomplete addresses with the domain specified in the
+ Rewrite or add message headers in mail from remote clients if
+ the remote_header_rewrite_domain parameter value is non-empty,
+ updating incomplete addresses with the domain specified in the
remote_header_rewrite_domain parameter, and adding missing head-
ers.
@@ -398,7 +402,7 @@ CLEANUP(8) CLEANUP(8)
showq(8) queue displays.
header_size_limit (102400)
- The maximal amount of memory in bytes for storing a message
+ The maximal amount of memory in bytes for storing a message
header.
hopcount_limit (50)
@@ -406,17 +410,17 @@ CLEANUP(8) CLEANUP(8)
in the primary message headers.
in_flow_delay (1s)
- Time to pause before accepting a new message, when the message
+ Time to pause before accepting a new message, when the message
arrival rate exceeds the message delivery rate.
message_size_limit (10240000)
- The maximal size in bytes of a message, including envelope
+ The maximal size in bytes of a message, including envelope
information.
Available in Postfix version 2.0 and later:
header_address_token_limit (10240)
- The maximal number of address tokens are allowed in an address
+ The maximal number of address tokens are allowed in an address
message header.
mime_boundary_length_limit (2048)
@@ -432,7 +436,7 @@ CLEANUP(8) CLEANUP(8)
Available in Postfix version 2.1 and later:
virtual_alias_expansion_limit (1000)
- The maximal number of addresses that virtual alias expansion
+ The maximal number of addresses that virtual alias expansion
produces from each original recipient.
virtual_alias_recursion_limit (1000)
@@ -441,25 +445,25 @@ CLEANUP(8) CLEANUP(8)
Available in Postfix version 3.0 and later:
virtual_alias_address_length_limit (1000)
- The maximal length of an email address after virtual alias
+ The maximal length of an email address after virtual alias
expansion.
SMTPUTF8 CONTROLS
Preliminary SMTPUTF8 support is introduced with Postfix 3.0.
smtputf8_enable (yes)
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in RFC 6531, RFC 6532, and RFC 6533.
smtputf8_autodetect_classes (sendmail, verify)
- Detect that a message requires SMTPUTF8 support for the speci-
+ Detect that a message requires SMTPUTF8 support for the speci-
fied mail origin classes.
Available in Postfix version 3.2 and later:
enable_idna2003_compatibility (no)
- Enable 'transitional' compatibility between IDNA2003 and
- IDNA2008, when converting UTF-8 domain names to/from the ASCII
+ Enable 'transitional' compatibility between IDNA2003 and
+ IDNA2008, when converting UTF-8 domain names to/from the ASCII
form that is used for DNS lookups.
TLS SUPPORT
@@ -471,27 +475,27 @@ CLEANUP(8) CLEANUP(8)
MISCELLANEOUS CONTROLS
config_directory (see 'postconf -d' output)
- The default location of the Postfix main.cf and master.cf con-
+ The default location of the Postfix main.cf and master.cf con-
figuration files.
daemon_timeout (18000s)
- How much time a Postfix daemon process may take to handle a
+ How much time a Postfix daemon process may take to handle a
request before it is terminated by a built-in watchdog timer.
delay_logging_resolution_limit (2)
- The maximal number of digits after the decimal point when log-
+ The maximal number of digits after the decimal point when log-
ging delay values.
delay_warning_time (0h)
- The time after which the sender receives a copy of the message
+ The time after which the sender receives a copy of the message
headers of mail that is still queued.
ipc_timeout (3600s)
- The time limit for sending or receiving information over an
+ The time limit for sending or receiving information over an
internal communication channel.
max_idle (100s)
- The maximum amount of time that an idle Postfix daemon process
+ The maximum amount of time that an idle Postfix daemon process
waits for an incoming connection before terminating voluntarily.
max_use (100)
@@ -502,7 +506,7 @@ CLEANUP(8) CLEANUP(8)
The internet hostname of this mail system.
myorigin ($myhostname)
- The domain name that locally-posted mail appears to come from,
+ The domain name that locally-posted mail appears to come from,
and that locally posted mail is delivered to.
process_id (read-only)
@@ -515,21 +519,21 @@ CLEANUP(8) CLEANUP(8)
The location of the Postfix top-level queue directory.
soft_bounce (no)
- Safety net to keep mail queued that would otherwise be returned
+ Safety net to keep mail queued that would otherwise be returned
to the sender.
syslog_facility (mail)
The syslog facility of Postfix logging.
syslog_name (see 'postconf -d' output)
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available in Postfix version 2.1 and later:
enable_original_recipient (yes)
- Enable support for the original recipient address after an
- address is rewritten to a different address (for example with
+ Enable support for the original recipient address after an
+ address is rewritten to a different address (for example with
aliasing or with canonical mapping).
Available in Postfix 3.3 and later:
@@ -540,14 +544,14 @@ CLEANUP(8) CLEANUP(8)
Available in Postfix 3.5 and later:
info_log_address_format (external)
- The email address form that will be used in non-debug logging
+ The email address form that will be used in non-debug logging
(info, warning, etc.).
Available in Postfix 3.9 and later:
force_mime_input_conversion (no)
- Convert body content that claims to be 8-bit into quoted-print-
- able, before header_checks, body_checks, Milters, and before
+ Convert body content that claims to be 8-bit into quoted-print-
+ able, before header_checks, body_checks, Milters, and before
after-queue content filters.
FILES
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index de47f73e7..c6a02d86c 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -8020,6 +8020,42 @@ Sendmail compatibility feature that specifies the location of the
+
+
+non_empty_end_of_header_action
+(default: fix_quietly)
+
+ How the cleanup(8) daemon will process a message when the primary
+message header is terminated with a non-empty line.
+
+
+
+- fix_quietly
-
Default behavior: insert
+an empty line before the erroneous text. This behavior is backwards
+compatible with Postfix ≤ 3.10.
+
+- add_header
-
Insert a MIME-Error:
+header and an empty line before the erroneous text.
+ Example:
+
+MIME-Error: message header was not terminated by empty line
+
+
+- reject
-
Log the erroneous text and
+reject the message content.
+ Example:
+
+queueid reject: mime-error message header was terminated
+with non-empty line: erroneous text; from=<sender>
+to=<recipient>
+
+
+
+
+
+ This feature is available in Postfix ≥ 3.11.
+
+
non_fqdn_reject_code
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index 819d18ae4..1905321c2 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -4913,6 +4913,38 @@ This feature is available in Postfix 2.0 and later.
Sendmail compatibility feature that specifies the location of the
\fBnewaliases\fR(1) command. This command can be used to rebuild the
\fBlocal\fR(8) \fBaliases\fR(5) database.
+.SH non_empty_end_of_header_action (default: fix_quietly)
+How the \fBcleanup\fR(8) daemon will process a message when the primary
+message header is terminated with a non\-empty line.
+.IP "\fB fix_quietly \fR"
+Default behavior: insert
+an empty line before the erroneous text. This behavior is backwards
+compatible with Postfix <= 3.10.
+.br
+.IP "\fB add_header \fR"
+Insert a MIME\-Error:
+header and an empty line before the erroneous text.
+Example:
+.nf
+.na
+MIME\-Error: message header was not terminated by empty line
+.fi
+.ad
+.br
+.IP "\fB reject \fR"
+Log the erroneous text and
+reject the message content.
+Example:
+.nf
+.na
+\fIqueueid\fR reject: mime\-error message header was terminated
+with non\-empty line: \fIerroneous text\fR; from=<\fIsender\fR>
+to=<\fIrecipient\fR>
+.fi
+.ad
+.br
+.PP
+This feature is available in Postfix >= 3.11.
.SH non_fqdn_reject_code (default: 504)
The numerical Postfix SMTP server reply code when a client request
is rejected by the reject_non_fqdn_helo_hostname, reject_non_fqdn_sender
diff --git a/postfix/man/man8/cleanup.8 b/postfix/man/man8/cleanup.8
index 66a757838..916c080f4 100644
--- a/postfix/man/man8/cleanup.8
+++ b/postfix/man/man8/cleanup.8
@@ -146,6 +146,9 @@ Available in Postfix version 3.10 and later:
The character set name (also called "charset") that Postfix
will output when it automatically generates an RFC 2047 encoded
full name.
+.IP "\fBnon_empty_end_of_header_action (fix_quietly)\fR"
+How the \fBcleanup\fR(8) daemon will process a message when the primary
+message header is terminated with a non\-empty line.
.SH "BUILT-IN CONTENT FILTERING CONTROLS"
.na
.nf
diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink
index 3ed499977..e09c450d5 100755
--- a/postfix/mantools/postlink
+++ b/postfix/mantools/postlink
@@ -426,6 +426,7 @@ while (<>) {
s;\breadme_directory\b;$&;g;
s;\breceive_override_options\b;$&;g;
s;\bremote_header_re[-]*\n* *[]*write_domain\b;$&;g;
+ s;\bnon_empty_end_of_header_action\b;$&;g;
s;\bno_unknown_recip[-]*\n* *[]*ient_checks\b;$&;g;
s;\bno_address_mappings\b;$&;g;
s;\bno_header_body_checks\b;$&;g;
diff --git a/postfix/proto/OVERVIEW.html b/postfix/proto/OVERVIEW.html
index 1c52dd696..56500ba5a 100644
--- a/postfix/proto/OVERVIEW.html
+++ b/postfix/proto/OVERVIEW.html
@@ -56,12 +56,16 @@ names inside shaded areas represent Postfix queues.
-| |
+ |
- trivial- rewrite(8) |
+ |
+
+ trivial- rewrite(8) |
+ | | / |
+
| Network | -> |
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 33349122c..f82e4b371 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -19746,3 +19746,35 @@ filter out").
This feature is available in Postfix ≥ 3.11.
+
+%PARAM non_empty_end_of_header_action fix_quietly
+
+ How the cleanup(8) daemon will process a message when the primary
+message header is terminated with a non-empty line.
+
+
+
+- fix_quietly
-
Default behavior: insert
+an empty line before the erroneous text. This behavior is backwards
+compatible with Postfix ≤ 3.10.
+
+- add_header
-
Insert a MIME-Error:
+header and an empty line before the erroneous text.
+ Example:
+
+MIME-Error: message header was not terminated by empty line
+
+
+- reject
-
Log the erroneous text and
+reject the message content.
+ Example:
+
+queueid reject: mime-error message header was terminated
+with non-empty line: erroneous text; from=<sender>
+to=<recipient>
+
+
+
+
+
+ This feature is available in Postfix ≥ 3.11.
diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c
index 46b225218..f9861d1f1 100644
--- a/postfix/src/cleanup/cleanup.c
+++ b/postfix/src/cleanup/cleanup.c
@@ -128,6 +128,9 @@
/* The character set name (also called "charset") that Postfix
/* will output when it automatically generates an RFC 2047 encoded
/* full name.
+/* .IP "\fBnon_empty_end_of_header_action (fix_quietly)\fR"
+/* How the \fBcleanup\fR(8) daemon will process a message when the primary
+/* message header is terminated with a non-empty line.
/* BUILT-IN CONTENT FILTERING CONTROLS
/* .ad
/* .fi
diff --git a/postfix/src/cleanup/cleanup.h b/postfix/src/cleanup/cleanup.h
index 82bd482ef..58b747450 100644
--- a/postfix/src/cleanup/cleanup.h
+++ b/postfix/src/cleanup/cleanup.h
@@ -361,6 +361,16 @@ extern void cleanup_body_edit_free(CLEANUP_STATE *);
*/
extern int cleanup_hfrom_format;
+ /*
+ * How to handle garbage at end of the primary message header.
+ */
+#define NON_EMPTY_EOH_CODE_ERROR -1 /* sentinel */
+#define NON_EMPTY_EOH_CODE_FIX_QUIETLY 0
+#define NON_EMPTY_EOH_CODE_ADD_HDR 1
+#define NON_EMPTY_EOH_CODE_REJECT 2
+
+extern int cleanup_non_empty_eoh_action;
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/cleanup/cleanup_init.c b/postfix/src/cleanup/cleanup_init.c
index 8f6b3e14d..f7872d881 100644
--- a/postfix/src/cleanup/cleanup_init.c
+++ b/postfix/src/cleanup/cleanup_init.c
@@ -77,6 +77,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -177,6 +180,7 @@ char *var_hfrom_format; /* header_from_format */
char *var_full_name_encoding_charset; /* in =?charset?encoding?gibberish=? */
int var_force_mime_iconv; /* force mime downgrade on input */
int var_cleanup_mask_stray_cr_lf; /* replace stray CR or LF with space */
+char *var_non_empty_eoh_action; /* handle non-empty header terminator */
const CONFIG_INT_TABLE cleanup_int_table[] = {
VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
@@ -247,6 +251,7 @@ const CONFIG_STR_TABLE cleanup_str_table[] = {
VAR_MILT_MACRO_DEFLTS, DEF_MILT_MACRO_DEFLTS, &var_milt_macro_deflts, 0, 0,
VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
VAR_FULL_NAME_ENCODING_CHARSET, DEF_FULL_NAME_ENCODING_CHARSET, &var_full_name_encoding_charset, 1, 0,
+ VAR_NON_EMPTY_EOH_ACTION, DEF_NON_EMPTY_EOH_ACTION, &var_non_empty_eoh_action, 1, 0,
0,
};
@@ -291,6 +296,11 @@ MILTERS *cleanup_milters;
*/
int cleanup_hfrom_format;
+ /*
+ * Garbage after message header.
+ */
+int cleanup_non_empty_eoh_action;
+
/* cleanup_all - callback for the runtime error handler */
void cleanup_all(void)
@@ -443,6 +453,12 @@ void cleanup_pre_jail(char *unused_name, char **unused_argv)
void cleanup_post_jail(char *unused_name, char **unused_argv)
{
+ static NAME_CODE non_empty_eoh_actions[] = {
+ {NON_EMPTY_EOH_NAME_FIX_QUIETLY, NON_EMPTY_EOH_CODE_FIX_QUIETLY},
+ {NON_EMPTY_EOH_NAME_ADD_HDR, NON_EMPTY_EOH_CODE_ADD_HDR},
+ {NON_EMPTY_EOH_NAME_REJECT, NON_EMPTY_EOH_CODE_REJECT},
+ {0, NON_EMPTY_EOH_CODE_ERROR},
+ };
/*
* Optionally set the file size resource limit. XXX This limits the
@@ -477,4 +493,13 @@ void cleanup_post_jail(char *unused_name, char **unused_argv)
* From: header formatting.
*/
cleanup_hfrom_format = hfrom_format_parse(VAR_HFROM_FORMAT, var_hfrom_format);
+
+ /*
+ * Garbage after message header.
+ */
+ if ((cleanup_non_empty_eoh_action =
+ name_code(non_empty_eoh_actions, NAME_CODE_FLAG_NONE,
+ var_non_empty_eoh_action)) == NON_EMPTY_EOH_CODE_ERROR)
+ msg_fatal("bad %s value: '%s'", VAR_NON_EMPTY_EOH_ACTION,
+ var_non_empty_eoh_action);
}
diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c
index 7259de188..9f84bdcf3 100644
--- a/postfix/src/cleanup/cleanup_message.c
+++ b/postfix/src/cleanup/cleanup_message.c
@@ -722,6 +722,7 @@ static void cleanup_header_done_callback(void *context)
char time_stamp[1024]; /* XXX locale dependent? */
struct tm *tp;
time_t tv;
+ int mime_errs;
/*
* XXX Workaround: when we reach the end of headers, mime_state_update()
@@ -906,6 +907,15 @@ static void cleanup_header_done_callback(void *context)
msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
state->body_offset = state->append_hdr_pt_target;
}
+
+ /*
+ * Get the current error state before mime_state_update() can return it.
+ */
+ mime_errs = mime_state_status(state->mime_state);
+ if ((mime_errs && MIME_ERR_NON_EMPTY_EOH)
+ && cleanup_non_empty_eoh_action == NON_EMPTY_EOH_CODE_ADD_HDR)
+ cleanup_out_string(state, REC_TYPE_NORM,
+ "MIME-Error: message header was not terminated by empty line");
}
/* cleanup_body_callback - output one body record */
@@ -1047,6 +1057,8 @@ static void cleanup_message_headerbody(CLEANUP_STATE *state, int type,
cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
/* Ignore header truncation after primary message headers. */
state->mime_errs &= ~MIME_ERR_TRUNC_HEADER;
+ if (cleanup_non_empty_eoh_action != NON_EMPTY_EOH_CODE_REJECT)
+ state->mime_errs &= ~MIME_ERR_NON_EMPTY_EOH;
if (state->mime_errs && state->reason == 0) {
state->errs |= CLEANUP_STAT_CONT;
detail = mime_state_detail(state->mime_errs);
@@ -1081,16 +1093,22 @@ static void cleanup_mime_error_callback(void *context, int err_code,
* Message header too large errors are handled after the end of the
* primary message headers.
*/
- if ((err_code & ~MIME_ERR_TRUNC_HEADER) != 0) {
- if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0)
- origin = MAIL_ATTR_ORG_NONE;
-#define TEXT_LEN (len < 100 ? (int) len : 100)
- msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>",
- state->queue_id, mime_state_error(err_code), TEXT_LEN, text,
- origin, info_log_addr_form_sender(state->sender),
- info_log_addr_form_recipient(state->recip ?
- state->recip : "unknown"));
+ switch (err_code) {
+ case MIME_ERR_TRUNC_HEADER:
+ /* TODO(wietse) Unconditional? */
+ return;
+ case MIME_ERR_NON_EMPTY_EOH:
+ if (cleanup_non_empty_eoh_action != NON_EMPTY_EOH_CODE_REJECT)
+ return;
}
+ if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0)
+ origin = MAIL_ATTR_ORG_NONE;
+#define TEXT_LEN (len < 100 ? (int) len : 100)
+ msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>",
+ state->queue_id, mime_state_error(err_code), TEXT_LEN, text,
+ origin, info_log_addr_form_sender(state->sender),
+ info_log_addr_form_recipient(state->recip ?
+ state->recip : "unknown"));
}
/* cleanup_message - initialize message content segment */
@@ -1127,6 +1145,8 @@ void cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t
mime_options |= MIME_OPT_REPORT_8BIT_IN_7BIT_BODY;
if (var_strict_encoding)
mime_options |= MIME_OPT_REPORT_ENCODING_DOMAIN;
+ if (cleanup_non_empty_eoh_action != NON_EMPTY_EOH_CODE_FIX_QUIETLY)
+ mime_options |= MIME_OPT_REPORT_NON_EMPTY_EOH;
if (var_strict_8bitmime || var_strict_7bit_hdrs
|| var_strict_8bit_body || var_strict_encoding
|| *var_header_checks || *var_mimehdr_checks
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 7583c8b55..563e4c625 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -440,6 +440,17 @@ extern char *var_drop_hdrs;
#define DEF_HFROM_FORMAT HFROM_FORMAT_NAME_STD
extern char *var_hfrom_format;
+ /*
+ * How to handle malformed header ending.
+ */
+#define NON_EMPTY_EOH_NAME_FIX_QUIETLY "fix_quietly"
+#define NON_EMPTY_EOH_NAME_ADD_HDR "add_header"
+#define NON_EMPTY_EOH_NAME_REJECT "reject"
+
+#define VAR_NON_EMPTY_EOH_ACTION "non_empty_end_of_header_action"
+#define DEF_NON_EMPTY_EOH_ACTION NON_EMPTY_EOH_NAME_FIX_QUIETLY
+extern char *var_non_empty_eoh_action;
+
/*
* Standards violation: allow/permit RFC 822-style addresses in SMTP
* commands.
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 19e1a0cd0..c51f28067 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20251102"
+#define MAIL_RELEASE_DATE "20251105"
#define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT
diff --git a/postfix/src/global/mime_garb3.ref b/postfix/src/global/mime_garb3.ref
index ee8ef36b8..a5de693e4 100644
--- a/postfix/src/global/mime_garb3.ref
+++ b/postfix/src/global/mime_garb3.ref
@@ -7,6 +7,7 @@ mime_state: header_token: boundary = Boundary-00=_mvhpFky0yqNhsa4
mime_state: PUSH boundary Boundary-00=_mvhpFky0yqNhsa4
MAIN 95 |Content-Type: Multipart/Mixed;
boundary="Boundary-00=_mvhpFky0yqNhsa4"
+mime_state: warning: primary header was terminated with non-empty line: junk in primary header
HEADER END
mime_state: garbage in primary header
BODY N 0 |
@@ -42,4 +43,5 @@ BODY N 135 |Blah
BODY N 140 |
BODY END
mime_state: warning: improper message/* or multipart/* encoding domain
+mime_state: warning: non-empty end-of-header
mime_state: POP boundary Boundary-00=_mvhpFky0yqNhsa4
diff --git a/postfix/src/global/mime_state.c b/postfix/src/global/mime_state.c
index e1b6a65ce..798e13de7 100644
--- a/postfix/src/global/mime_state.c
+++ b/postfix/src/global/mime_state.c
@@ -27,6 +27,9 @@
/* const char *buf;
/* ssize_t len;
/*
+/* int mime_state_status(state)
+/* MIME_STATE *state;
+/*
/* MIME_STATE *mime_state_free(state)
/* MIME_STATE *state;
/*
@@ -73,7 +76,12 @@
/* content transfer encoding domain, or specifies a transformation
/* (quoted-printable, base64) instead of a domain (7bit, 8bit,
/* or binary).
+/* .IP MIME_ERR_NON_EMPTY_EOH
+/* The primary message header was terminated with a non-empty line.
/* .PP
+/* mime_state_status() reports the same result as
+/* mime_state_update(), but without changing state.
+/*
/* mime_state_free() releases storage for a MIME state machine,
/* and conveniently returns a null pointer.
/*
@@ -129,6 +137,9 @@
/* .IP MIME_OPT_REPORT_NESTING
/* Report errors that set the MIME_ERR_NESTING error flag
/* (see above).
+/* .IP MIME_OPT_REPORT_NON_EMPTY_EOH
+/* Report errors that terminate the primary message header with a
+/* non-empty line.
/* .IP MIME_OPT_DOWNGRADE
/* Transform content that claims to be 8-bit into quoted-printable.
/* Where appropriate, update Content-Transfer-Encoding: message
@@ -915,8 +926,13 @@ int mime_state_update(MIME_STATE *state, int rec_type,
* This input terminates a block of message headers. Call the
* optional header end routine at the end of the first header block.
*/
- if (state->curr_state == MIME_STATE_PRIMARY && state->head_end)
- state->head_end(state->app_context);
+ if (state->curr_state == MIME_STATE_PRIMARY) {
+ if (len > 0
+ && (state->static_flags & MIME_OPT_REPORT_NON_EMPTY_EOH))
+ REPORT_ERROR_LEN(state, MIME_ERR_NON_EMPTY_EOH, text, len);
+ if (state->head_end)
+ state->head_end(state->app_context);
+ }
/*
* This is the right place to check if the sender specified an
@@ -1130,6 +1146,13 @@ int mime_state_update(MIME_STATE *state, int rec_type,
}
}
+/* mime_state_status - return mime_state_update() like result */
+
+int mime_state_status(MIME_STATE *state)
+{
+ return (state->err_flags);
+}
+
/*
* Mime error to (DSN, text) mapping. Order matters; more serious errors
* must precede less serious errors, because the error-to-text conversion
@@ -1141,6 +1164,7 @@ static const MIME_STATE_DETAIL mime_err_detail[] = {
MIME_ERR_8BIT_IN_HEADER, "5.6.0", "improper use of 8-bit data in message header",
MIME_ERR_8BIT_IN_7BIT_BODY, "5.6.0", "improper use of 8-bit data in message body",
MIME_ERR_ENCODING_DOMAIN, "5.6.0", "invalid message/* or multipart/* encoding domain",
+ MIME_ERR_NON_EMPTY_EOH, "5.6.0", "primary header was terminated with non-empty line",
0,
};
@@ -1254,6 +1278,7 @@ int main(int unused_argc, char **argv)
| MIME_OPT_REPORT_ENCODING_DOMAIN \
| MIME_OPT_REPORT_TRUNC_HEADER \
| MIME_OPT_REPORT_NESTING \
+ | MIME_OPT_REPORT_NON_EMPTY_EOH \
| MIME_OPT_DOWNGRADE)
msg_vstream_init(basename(argv[0]), VSTREAM_OUT);
@@ -1288,6 +1313,8 @@ int main(int unused_argc, char **argv)
msg_warn("improper use of 8-bit data in message body");
if (err & MIME_ERR_ENCODING_DOMAIN)
msg_warn("improper message/* or multipart/* encoding domain");
+ if (err & MIME_ERR_NON_EMPTY_EOH)
+ msg_warn("non-empty end-of-header");
/*
* Cleanup.
diff --git a/postfix/src/global/mime_state.h b/postfix/src/global/mime_state.h
index 88a4d7cf3..c4514a119 100644
--- a/postfix/src/global/mime_state.h
+++ b/postfix/src/global/mime_state.h
@@ -32,6 +32,7 @@ typedef void (*MIME_STATE_ERR_PRINT) (void *, int, const char *, ssize_t);
extern MIME_STATE *mime_state_alloc(int, MIME_STATE_HEAD_OUT, MIME_STATE_ANY_END, MIME_STATE_BODY_OUT, MIME_STATE_ANY_END, MIME_STATE_ERR_PRINT, void *);
extern int mime_state_update(MIME_STATE *, int, const char *, ssize_t);
+extern int mime_state_status(MIME_STATE *);
extern MIME_STATE *mime_state_free(MIME_STATE *);
/*
@@ -46,6 +47,7 @@ extern MIME_STATE *mime_state_free(MIME_STATE *);
#define MIME_OPT_REPORT_TRUNC_HEADER (1<<5)
#define MIME_OPT_DISABLE_MIME (1<<6)
#define MIME_OPT_REPORT_NESTING (1<<7)
+#define MIME_OPT_REPORT_NON_EMPTY_EOH (1<<8)
/*
* Body encoding domains.
@@ -68,6 +70,7 @@ typedef struct {
#define MIME_ERR_8BIT_IN_HEADER (1<<2)
#define MIME_ERR_8BIT_IN_7BIT_BODY (1<<3)
#define MIME_ERR_ENCODING_DOMAIN (1<<4)
+#define MIME_ERR_NON_EMPTY_EOH (1<<5)
extern const MIME_STATE_DETAIL *mime_state_detail(int);
extern const char *mime_state_error(int);
diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
index c97efb1cb..328e25ae4 100644
--- a/postfix/src/tls/Makefile.in
+++ b/postfix/src/tls/Makefile.in
@@ -45,7 +45,7 @@ Makefile: Makefile.in
test: $(TESTPROG)
-tests: tls_certkey_tests
+tests: tls_certkey_tests tls_dane_tests
tls_certkey_tests: test
@echo Testing loading of keys and certs
@@ -76,6 +76,9 @@ tls_certkey_tests: test
echo " $$pem: OK"; \
done
+tls_dane_tests: tls_dane tls_dane.sh
+ $(SHLIB_ENV) $(VALGRIND) bash tls_dane.sh
+
root_tests:
$(LIB): $(OBJS)
diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c
index 0b62b520b..33b5a21d6 100644
--- a/postfix/src/tls/tls_dane.c
+++ b/postfix/src/tls/tls_dane.c
@@ -1292,9 +1292,6 @@ static SSL_CTX *ctx_init(const char *CAfile)
tls_param_init();
tls_check_version();
- if (!tls_validate_digest(LN_sha1))
- msg_fatal("%s digest algorithm not available", LN_sha1);
-
if (TLScontext_index < 0)
if ((TLScontext_index = SSL_get_ex_new_index(0, 0, 0, 0, 0)) < 0)
msg_fatal("Cannot allocate SSL application data index");
@@ -1339,7 +1336,7 @@ int main(int argc, char *argv[])
tctx = tls_alloc_sess_context(TLS_LOG_NONE, argv[7]);
tctx->namaddr = argv[7];
- tctx->mdalg = LN_sha256;
+ tctx->mdalg = atoi(argv[3]) == 2 ? LN_sha512 : LN_sha256;
tctx->dane = tls_dane_alloc();
if ((fpt_alg = tls_validate_digest(tctx->mdalg)) == 0)
@@ -1376,9 +1373,10 @@ int main(int argc, char *argv[])
peername = argv[7];
msg_info("Verified %s", peername);
} else {
- i = SSL_get_verify_result(tctx->con);
+ int r = SSL_get_verify_result(tctx->con);
+
msg_info("certificate verification failed for %s:%s: num=%d:%s",
- argv[6], argv[7], i, X509_verify_cert_error_string(i));
+ argv[6], argv[7], r, X509_verify_cert_error_string(r));
}
return (i <= 0);
diff --git a/postfix/src/tls/tls_dane.sh b/postfix/src/tls/tls_dane.sh
index a950857bb..ac7b8a228 100644
--- a/postfix/src/tls/tls_dane.sh
+++ b/postfix/src/tls/tls_dane.sh
@@ -23,7 +23,7 @@ req() {
key "$key"
openssl req -new -sha256 -key "${key}.pem" 2>/dev/null \
-config <(printf "[req]\n%s\n%s\n[dn]\nCN=%s\n" \
- "prompt = no" "distinguished_name = dn" "${cn}")
+ "prompt = no" "distinguished_name = dn" "${cn}")
}
req_nocn() {
@@ -32,7 +32,7 @@ req_nocn() {
key "$key"
openssl req -new -sha256 -subj / -key "${key}.pem" 2>/dev/null \
-config <(printf "[req]\n%s\n[dn]\nCN_default =\n" \
- "distinguished_name = dn")
+ "distinguished_name = dn")
}
cert() {
@@ -131,7 +131,7 @@ runtest() {
printf "%d %d %d %-24s %s: " "$usage" "$selector" "$mtype" "$tlsa" "$desc"
if [ -n "$ca" ]; then ca="$ca.pem"; fi
- "$TEST" "$usage" "$selector" "$digest" "$tlsa.pem" "$ca" "$chain.pem" \
+ "$TEST" "$usage" "$selector" "$mtype" "$tlsa.pem" "$ca" "$chain.pem" \
"$@" > /dev/null
}
@@ -191,16 +191,21 @@ done
done
done
-# These tests don't depend in the akid/skid chaining:
+# These tests don't depend on the akid/skid chaining:
#
for s in 0 1
do
checkfail "missing TA" 2 "$s" 1 rootcert "" chain1 "$HOST"
for m in 0 1 2
do
- checkpass "depth 0 TA" 2 "$s" "$m" sscert "" sscert "$HOST"
+ # Special-case "2 1 0" TLSA records. These match as issuer public keys of a
+ # self-signed EE cert. The same key as part of a "2 0 0" certificate would
+ # not match.
+ #
+ if [ "$s" -ne 1 -o "$m" -ne 0 ]; then
+ checkfail "depth 0 TA" 2 "$s" "$m" sscert "" sscert "$HOST"
+ fi
checkfail "non-TA" 2 "$s" "$m" eecert rootcert chain "$HOST"
- checkfail "depth 0 TA namecheck" 2 "$s" "$m" sscert sscert sscert whatever
checkpass "valid EE" 3 "$s" "$m" eecert "" chain whatever
checkpass "key-only EE" 3 "$s" "$m" acert "" acert whatever
@@ -208,4 +213,9 @@ do
done
done
-rm -f *.pem
+rm -f acert.pem akey.pem \
+ eecert.pem eekey.pem \
+ cacert?.pem cakey?.pem \
+ rootcert.pem rootkey.pem \
+ sscert.pem sskey.pem \
+ chain.pem chain1.pem