From: Wietse Z Venema
Date: Mon, 2 Sep 2024 05:00:00 +0000 (-0500)
Subject: postfix-3.10-20240902-nonprod
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18f96cd64e53558c75136907a70ab7d0caa7c6a7;p=thirdparty%2Fpostfix.git
postfix-3.10-20240902-nonprod
---
diff --git a/postfix/.indent.pro b/postfix/.indent.pro
index 8ee03dbe7..99ab2e36f 100644
--- a/postfix/.indent.pro
+++ b/postfix/.indent.pro
@@ -370,6 +370,7 @@
-TTLS_PRNG_SEED_INFO
-TTLS_PRNG_SRC
-TTLS_ROLE
+-TTLSRPT_WRAPPER
-TTLS_SCACHE
-TTLS_SCACHE_ENTRY
-TTLS_SERVER_INIT_PROPS
diff --git a/postfix/HISTORY b/postfix/HISTORY
index 4be10b660..d0f4791f7 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -28200,3 +28200,24 @@ Apologies for any names omitted.
matches the envelope sender (MAIL FROM) address. File:
proto/postconf.proto.
+
+20240730
+
+ Infrastructure: added argv_addv() function to append an
+ array of strings. File: util/argv.c.
+
+20240809
+
+ Infrastructure: added a dns_rr_detach() function to extract
+ one DNS record from a list. Files: dns/dns_rr.c, dns_rr_test.c.
+
+20240816
+
+ Infrastructure: factored out strerror() wrapper that reports
+ "Application error" instead of "Success" when errno == 0.
+ Files: util/mystrerror.c, util/vbuf_print.c.
+
+20240822
+
+ Infrastructure: added "append to buffer" option to the
+ hex_encode_opt() function. Files: util/hex_encode.[hc];
diff --git a/postfix/README_FILES/AAAREADME b/postfix/README_FILES/AAAREADME
index 94d552ece..f8f2fb7dc 100644
--- a/postfix/README_FILES/AAAREADME
+++ b/postfix/README_FILES/AAAREADME
@@ -10,6 +10,7 @@ GGeenneerraall ccoonnffiigguurraattiioonn
* VIRTUAL_README: Virtual domain hosting
* SASL_README: SASL Authentication
* TLS_README: TLS Encryption and authentication
+ * TLSRPT_README: TLSRPT notification
* FORWARD_SECRECY_README: TLS Forward Secrecy
* IPV6_README: IP Version 6 Support
* SMTPUTF8_README: SMTPUTF8 Support
diff --git a/postfix/README_FILES/SASL_README b/postfix/README_FILES/SASL_README
index 617212e5c..83c0b297f 100644
--- a/postfix/README_FILES/SASL_README
+++ b/postfix/README_FILES/SASL_README
@@ -185,9 +185,12 @@ You can read more about the following topics:
cyrus_sasl_config_path and/or the distribution-specific documentation to
determine the expected location.
- * Some Debian-based Postfix distributions ignore the "cyrus_sasl_config_path"
- parameter setting, and force Postfix to open the file /etc/postfix/sasl/
- smtpd.conf.
+ * Some Debian-based Postfix distributions patch Postfix to hardcode a non-
+ default search path, making it impossible to set an alternate search path
+ via the "cyrus_sasl_config_path" parameter. This is likely to be the case
+ when the distribution documents a Postfix-specific path (e.g. /etc/postfix/
+ sasl/) that is different from the default value of "cyrus_sasl_config_path"
+ (which then is likely to be empty).
NNoottee
diff --git a/postfix/README_FILES/TLSRPT_README b/postfix/README_FILES/TLSRPT_README
new file mode 100644
index 000000000..7b8af476b
--- /dev/null
+++ b/postfix/README_FILES/TLSRPT_README
@@ -0,0 +1,177 @@
+PPoossttffiixx TTLLSSRRPPTT HHoowwttoo
+
+-------------------------------------------------------------------------------
+
+TTOOCC
+
+ * Introduction
+ * Building Postfix with TLSRPT support
+ * Using TLSRPT
+ * MTA-STS Support via smtp_tls_policy_maps
+ * Limitations
+ * Credits
+
+IInnttrroodduuccttiioonn
+
+The TLSRPT protocol is defined in RFC 8460. With this, an email receiving
+domain can publish a policy in DNS to request daily summary reports for
+successful and failed TLS connections to that domain. Support for TLSRPT was
+added in Postfix 3.10.
+
+When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable = yes"), the
+Postfix SMTP and TLS clients engines will generate "success" and "failure"
+events, and will pass those events to a TLSRPT client library and report
+generator that are maintained by sys4. The Postfix implementation supports both
+DANE (Postfix built-in) and MTA-STS (through an smtp_tls_policy_maps plug-in).
+
+The high-level diagram shows how Postfix events are reported to domains that
+publish a TLSRPT policy.
+
+ Postfix SMTP and --> TLSRPT client --> TLSRPT report --> Email or HTTP
+ TLS client engines library generator summary
+
+The Postfix smtp(8) client process implements the SMTP client engine. With
+"smtp_tls_connection_reuse = no", the smtp(8) client process also implements
+the TLS client engine. With "smtp_tls_connection_reuse = yes", the smtp(8)
+client process delegates TLS processing to a Postfix tlsproxy(8) process.
+Either way, Postfix will generate the exact same TLSRPT events.
+
+BBuuiillddiinngg PPoossttffiixx wwiitthh TTLLSSRRPPTT ssuuppppoorrtt
+
+These instructions assume that you build Postfix from source code as described
+in the INSTALL document. Some modification may be required if you build Postfix
+from a vendor-specific source package.
+
+The Postfix TLSRPT client builds on a TLSRPT client library whose source code
+can be obtained from:
+
+ https://github.com/sys4/tlsrpt
+
+The library is typically installed as a header file in /usr/local/include/
+tlsrpt.h and an object library in /usr/local/lib/libtlsrpt.a or /usr/local/lib/
+libtlsrpt.so. The actual pathnames will depend on OS platform conventions.
+
+In order to build Postfix with TLSRPT support, you will need to add compiler
+options --DDUUSSEE__TTLLSSRRPPTT (to build with TLSRPT support), and --II (with the directory
+containing the tlsrpt.h header file), and you will need to add linker options
+to link with the TLSRPT client library, for example:
+
+ make -f Makefile.init makefiles \
+ "CCARGS=-DUSE_TLSRPT -I/usr/local/include" \
+ "AUXLIBS=-L/usr/local/lib -ltlsrpt"
+
+Then, just run 'make'.
+
+ Note: if your build command line already has CCARGS or AUXLIBS options,
+ then simply append the above options to the existing CCARGS or AUXLIBS
+ values.
+
+UUssiinngg TTLLSSRRPPTT
+
+After installing Postfix TLSRPT support, you can enable TLSRPT support in
+main.cf like this:
+
+ smtp_tlsrpt_enable = yes
+ smtp_tlsrpt_socket_name = /path/to/socket
+
+The smtp_tlsrpt_socket_name parameter specifies an absolute pathname, or a
+pathname that is relative to $queue_directory.
+
+A good socket location would be under $queue_directory/run/tlsrpt or
+$queue_directory/var/run/tlsrpt. These can then be configured in Postfix as a
+relative pathname (run/tlsrpt/tlsrpt.sock or var/run/tlsrpt/tlsrpt.sock) so
+that the same name will work with and without Postfix chroot support. Do not
+specify a location under directory that is already used by Postfix programs.
+Only Postfix programs should create sockets there.
+
+MMTTAA--SSTTSS SSuuppppoorrtt vviiaa ssmmttpp__ttllss__ppoolliiccyy__mmaappss
+
+Postfix supports MTA-STS though an smtp_tls_policy_maps policy plugin. Postfix
+TLSRPT support expects a response with the usual security level and matching
+requirements, plus any applicable name=value attributes described below.
+Specify { name = value } when a value may contain whitespace.
+
+ Note 1: Postfix 3.10 and later will accept these attributes in an MTA-STS
+ response even if TLSRPT support is disabled (at build time or run time),
+ but it will not use most attributes except ttl and policy_failure.
+
+ Note 2: It is an error to specify these attributes for a non-STS policy.
+
+The examples in the table apply to the MTA-STS policy example given in https://
+datatracker.ietf.org/doc/html/rfc8460#section-4.5.
+
+ * policy_type=type
+
+ Specify sts or no-policy-found.
+
+ * policy_domain=name
+
+ The domain that the MTA-STS policy applies to.
+
+ * policy_ttl=time
+
+ How long (in seconds) a Postfix SMTP client process will cache the MTA-STS
+ plugin response.
+
+ * { policy_string = value }
+
+ Specify one policy_string instance for each MTA-STS policy feature,
+ enclosed inside "{" and "}" to protect whitespace in attribute values.
+
+ Example:
+
+ { policy_string = version: STSv1 }, { policy_string = mode: testing },
+ ...
+
+ This form ignores whitespace after the opening "{", around the "=", and
+ before the closing "}".
+
+ * mx_host_pattern=pattern
+
+ Specify one mx_host_pattern instance for each "mx:" feature in the MTA-STS
+ policy.
+
+ Example:
+
+ mx_host_pattern=mx1.example.com, mx_host_pattern=mx2.example.com, ...
+
+ * policy_failure=type
+
+ If specified, forces MTA-STS policy enforcement to fail with the indicated
+ error, even if a server certificate would satisfy conventional PKI
+ constraints.
+
+ Valid errors are sts-policy-fetch-error, sts-policy-invalid, sts-webpki-
+ invalid, or the less informative validation-failure.
+
+ Example:
+
+ policy_failure=sts-webpki-invalid
+
+LLiimmiittaattiioonnss
+
+The following limitations exist primarily because some errors may be reported
+by a Postfix smtp(8) client process, and some errors by a Postfix tlsproxy(8)
+process. It is too difficult to propagate TLSRPT client library state between
+processes.
+
+The Postfix TLSRPT client reports one final status (either 'success' or
+'failure') for each MTA that it is able to connect to. It cannot report a final
+status 'success' with some recoverable 'failure'. Specifically:
+
+ * The Postfix TLSRPT client can report either successful TLS policy
+ compliance, or an unrecoverable failure that prevents TLS policy compliance
+ (examples: all TLSA records are unusable, or some PKI error during
+ certificate verification).
+
+ * The Postfix TLSRPT client must not be used to report a potentially
+ recoverable failure such as a non-parsable TLSA record, because some other
+ TLSA record for the same host may still allow successful TLS policy
+ compliance.
+
+CCrreeddiittss
+
+ * The TLSRPT client library and report generator are implemented and
+ maintained by sys4 (sys4.de).
+ * Wietse Venema implemented the integration with Postfix.
+
diff --git a/postfix/conf/postfix-files b/postfix/conf/postfix-files
index 5a939822b..2467af478 100644
--- a/postfix/conf/postfix-files
+++ b/postfix/conf/postfix-files
@@ -330,6 +330,7 @@ $readme_directory/STANDARD_CONFIGURATION_README:f:root:-:644
$readme_directory/STRESS_README:f:root:-:644
$readme_directory/TLS_LEGACY_README:f:root:-:644
$readme_directory/TLS_README:f:root:-:644
+$readme_directory/TLSRPT_README:f:root:-:644
$readme_directory/TUNING_README:f:root:-:644
$readme_directory/ULTRIX_README:f:root:-:644
$readme_directory/UUCP_README:f:root:-:644
@@ -392,6 +393,7 @@ $html_directory/STANDARD_CONFIGURATION_README.html:f:root:-:644
$html_directory/STRESS_README.html:f:root:-:644
$html_directory/TLS_LEGACY_README.html:f:root:-:644
$html_directory/TLS_README.html:f:root:-:644
+$html_directory/TLSRPT_README.html:f:root:-:644
$html_directory/TUNING_README.html:f:root:-:644
$html_directory/ULTRIX_README.html:f:root:-:644:o
$html_directory/UUCP_README.html:f:root:-:644
diff --git a/postfix/html/SASL_README.html b/postfix/html/SASL_README.html
index f02208e6e..c95ac8c84 100644
--- a/postfix/html/SASL_README.html
+++ b/postfix/html/SASL_README.html
@@ -281,9 +281,13 @@ configuration file in /etc/postfix/sasl/,
cyrus_sasl_config_path and/or the distribution-specific
documentation to determine the expected location.
-
Some Debian-based Postfix distributions ignore the
-"cyrus_sasl_config_path" parameter setting, and force Postfix to
-open the file /etc/postfix/sasl/smtpd.conf.
+
Some Debian-based Postfix distributions patch Postfix to
+hardcode a non-default search path, making it impossible to set an
+alternate search path via the "cyrus_sasl_config_path" parameter. This
+is likely to be the case when the distribution documents a
+Postfix-specific path (e.g. /etc/postfix/sasl/) that is
+different from the default value of "cyrus_sasl_config_path" (which
+then is likely to be empty).
The TLSRPT protocol is defined in RFC 8460. With this, an email
+receiving domain can publish a policy in DNS to request daily summary
+reports for successful and failed TLS connections to that domain.
+Support for TLSRPT was added in Postfix 3.10.
+
+
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable
+= yes"), the Postfix SMTP and TLS clients engines will generate
+"success" and "failure" events, and will pass those events to a
+TLSRPT client library and report generator that are maintained by
+sys4. The Postfix implementation supports both DANE (Postfix built-in)
+and MTA-STS (through an smtp_tls_policy_maps plug-in).
+
+
The high-level diagram shows how Postfix events are reported
+to domains that publish a TLSRPT policy.
+
+
+
+
+
+
Postfix SMTP and TLS client engines
+
-->
+
+
TLSRPT client library
-->
+
+
+
TLSRPT report generator
+-->
+
+
Email or HTTP summary
+
+
+
+
+
+
The Postfix smtp(8) client process implements the SMTP client
+engine. With "smtp_tls_connection_reuse = no", the smtp(8) client
+process also implements the TLS client engine. With
+"smtp_tls_connection_reuse = yes", the smtp(8) client process
+delegates TLS processing to a Postfix tlsproxy(8) process. Either
+way, Postfix will generate the exact same TLSRPT events.
These instructions assume that you build Postfix from source
+code as described in the INSTALL document. Some modification may
+be required if you build Postfix from a vendor-specific source
+package.
+
+
The Postfix TLSRPT client builds on a TLSRPT client library
+whose source code can be obtained from:
The library is typically installed as a header file in
+/usr/local/include/tlsrpt.h and an object library in
+/usr/local/lib/libtlsrpt.a or /usr/local/lib/libtlsrpt.so. The
+actual pathnames will depend on OS platform conventions.
+
+
In order to build Postfix with TLSRPT support, you will need
+to add compiler options -DUSE_TLSRPT (to build with TLSRPT
+support), and -I (with the directory containing the tlsrpt.h
+header file), and you will need to add linker options to link with
+the TLSRPT client library, for example:
Note: if your build command line already has CCARGS or AUXLIBS
+options, then simply append the above options to the existing CCARGS
+or AUXLIBS values.
A good socket location would be under $queue_directory/run/tlsrpt
+or $queue_directory/var/run/tlsrpt. These can then be configured
+in Postfix as a relative pathname (run/tlsrpt/tlsrpt.sock or
+var/run/tlsrpt/tlsrpt.sock) so that the same name will work with
+and without Postfix chroot support. Do not specify a location under
+directory that is already used by Postfix programs. Only Postfix
+programs should create sockets there.
Postfix supports MTA-STS though an smtp_tls_policy_maps policy
+plugin. Postfix TLSRPT support expects a response with the usual
+security level and matching requirements, plus any applicable
+name=value attributes described below. Specify { name = value }
+when a value may contain whitespace.
+
+
+
+
Note 1: Postfix 3.10 and later will accept these attributes in
+an MTA-STS response even if TLSRPT support is disabled (at build
+time or run time), but it will not use most attributes except
+ttl and policy_failure.
+
+
Note 2: It is an error to specify these attributes for a non-STS
+policy.
If specified, forces MTA-STS policy enforcement to fail with
+the indicated error, even if a server certificate would satisfy
+conventional PKI constraints.
+
+
Valid errors are sts-policy-fetch-error, sts-policy-invalid,
+sts-webpki-invalid, or the less informative
+validation-failure.
The following limitations exist primarily because some errors
+may be reported by a Postfix smtp(8) client process, and some errors
+by a Postfix tlsproxy(8) process. It is too difficult to propagate
+TLSRPT client library state between processes.
+
+
The Postfix TLSRPT client reports one final status (either
+'success' or 'failure') for each MTA that it is able to connect to.
+It cannot report a final status 'success' with some recoverable
+'failure'. Specifically:
+
+
+
+
The Postfix TLSRPT client can report either successful TLS
+policy compliance, or an unrecoverable failure that prevents TLS
+policy compliance (examples: all TLSA records are unusable, or some
+PKI error during certificate verification).
+
+
The Postfix TLSRPT client must not be used to report a
+potentially recoverable failure such as a non-parsable TLSA record,
+because some other TLSA record for the same host may still allow
+successful TLS policy compliance.
The TLSRPT client library and report generator are implemented
+and maintained by sys4 (sys4.de).
+
+
Wietse Venema implemented the integration with Postfix.
+
+
+
+
+
+
+
diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html
index 4d9cc2b0a..427eacf74 100644
--- a/postfix/html/access.5.html
+++ b/postfix/html/access.5.html
@@ -320,13 +320,9 @@ ACCESS(5) ACCESS(5)
address instead of the intended recipient(s). When multiple RE-DIRECT actions fire, only the last one takes effect.
- Note 1: this action overrides the FILTER action, and currently
+ Note: this action overrides the FILTER action, and currently
overrides all recipients of the message.
- Note 2: a REDIRECT address is subject to canonicalization (add
- missing domain) but NOT subject to canonical, masquerade, bcc,
- or virtual alias mapping.
-
This feature is available in Postfix 2.1 and later.
INFOoptional text...
diff --git a/postfix/html/header_checks.5.html b/postfix/html/header_checks.5.html
index 579437f5e..c1c8b17cc 100644
--- a/postfix/html/header_checks.5.html
+++ b/postfix/html/header_checks.5.html
@@ -312,14 +312,10 @@ HEADER_CHECKS(5) HEADER_CHECKS(5)
will be sent to the specified address instead of the intended
recipient(s).
- Note 1: this action overrides the FILTER action, and affects all
+ Note: this action overrides the FILTER action, and affects all
recipients of the message. If multiple REDIRECT actions fire,
only the last one is executed.
- Note 2: a REDIRECT address is subject to canonicalization (add
- missing domain) but NOT subject to canonical, masquerade, bcc,
- or virtual alias mapping.
-
This feature is available in Postfix 2.1 and later.
This feature is not supported with smtp header/body checks.
diff --git a/postfix/html/index.html b/postfix/html/index.html
index a7da6cb51..5fa7a69cd 100644
--- a/postfix/html/index.html
+++ b/postfix/html/index.html
@@ -43,6 +43,8 @@ configuration examples
IP Version 6 Support
diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html
index 4cc187c75..dbf71d264 100644
--- a/postfix/html/lmtp.8.html
+++ b/postfix/html/lmtp.8.html
@@ -746,41 +746,48 @@ SMTP,(LMTP) SMTP,(LMTP)
Request that remote SMTP servers send an RFC7250 raw public key
instead of an X.509 certificate.
+ smtp_tlsrpt_enable (no)
+ Enable support for RFC 8460 TLSRPT notifications.
+
+ smtp_tlsrpt_socket_name (empty)
+ The pathname of a UNIX-domain datagram socket that is managed by
+ a local TLSRPT reporting service.
+
OBSOLETE STARTTLS CONTROLS
- The following configuration parameters exist for compatibility with
- Postfix versions before 2.3. Support for these will be removed in a
+ The following configuration parameters exist for compatibility with
+ Postfix versions before 2.3. Support for these will be removed in a
future release.
smtp_use_tls (no)
- Opportunistic mode: use TLS when a remote SMTP server announces
+ Opportunistic mode: use TLS when a remote SMTP server announces
STARTTLS support, otherwise send the mail in the clear.
smtp_enforce_tls (no)
- Enforcement mode: require that remote SMTP servers use TLS
+ Enforcement mode: require that remote SMTP servers use TLS
encryption, and never send mail in the clear.
smtp_tls_enforce_peername (yes)
- With mandatory TLS encryption, require that the remote SMTP
- server hostname matches the information in the remote SMTP
+ With mandatory TLS encryption, require that the remote SMTP
+ server hostname matches the information in the remote SMTP
server certificate.
smtp_tls_per_site (empty)
- Optional lookup tables with the Postfix SMTP client TLS usage
- policy by next-hop destination and by remote SMTP server host-
+ Optional lookup tables with the Postfix SMTP client TLS usage
+ policy by next-hop destination and by remote SMTP server host-
name.
smtp_tls_cipherlist (empty)
- Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
cipher list.
RESOURCE AND RATE CONTROLSsmtp_connect_timeout (30s)
- The Postfix SMTP client time limit for completing a TCP connec-
+ The Postfix SMTP client time limit for completing a TCP connec-
tion, or zero (use the operating system built-in time limit).
smtp_helo_timeout (300s)
- The Postfix SMTP client time limit for sending the HELO or EHLO
- command, and for receiving the initial remote SMTP server
+ The Postfix SMTP client time limit for sending the HELO or EHLO
+ command, and for receiving the initial remote SMTP server
response.
lmtp_lhlo_timeout (300s)
@@ -792,19 +799,19 @@ SMTP,(LMTP) SMTP,(LMTP)
mand, and for receiving the remote SMTP server response.
smtp_mail_timeout (300s)
- The Postfix SMTP client time limit for sending the MAIL FROM
+ The Postfix SMTP client time limit for sending the MAIL FROM
command, and for receiving the remote SMTP server response.
smtp_rcpt_timeout (300s)
- The Postfix SMTP client time limit for sending the SMTP RCPT TO
+ The Postfix SMTP client time limit for sending the SMTP RCPT TO
command, and for receiving the remote SMTP server response.
smtp_data_init_timeout (120s)
- The Postfix SMTP client time limit for sending the SMTP DATA
+ The Postfix SMTP client time limit for sending the SMTP DATA
command, and for receiving the remote SMTP server response.
smtp_data_xfer_timeout (180s)
- The Postfix SMTP client time limit for sending the SMTP message
+ The Postfix SMTP client time limit for sending the SMTP message
content.
smtp_data_done_timeout (600s)
@@ -818,13 +825,13 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 2.1 and later:
smtp_mx_address_limit (5)
- The maximal number of MX (mail exchanger) IP addresses that can
- result from Postfix SMTP client mail exchanger lookups, or zero
+ The maximal number of MX (mail exchanger) IP addresses that can
+ result from Postfix SMTP client mail exchanger lookups, or zero
(no limit).
smtp_mx_session_limit (2)
- The maximal number of SMTP sessions per delivery request before
- the Postfix SMTP client gives up or delivers to a fall-back
+ The maximal number of SMTP sessions per delivery request before
+ the Postfix SMTP client gives up or delivers to a fall-back
relay host, or zero (no limit).
smtp_rset_timeout (20s)
@@ -834,17 +841,17 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 2.2 and earlier:
lmtp_cache_connection (yes)
- Keep Postfix LMTP client connections open for up to $max_idle
+ Keep Postfix LMTP client connections open for up to $max_idle
seconds.
Available in Postfix version 2.2 and later:
smtp_connection_cache_destinations (empty)
- Permanently enable SMTP connection caching for the specified
+ Permanently enable SMTP connection caching for the specified
destinations.
smtp_connection_cache_on_demand (yes)
- Temporarily enable SMTP connection caching while a destination
+ Temporarily enable SMTP connection caching while a destination
has a high volume of mail in the active queue.
smtp_connection_reuse_time_limit (300s)
@@ -858,23 +865,23 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 2.3 and later:
connection_cache_protocol_timeout (5s)
- Time limit for connection cache connect, send or receive opera-
+ Time limit for connection cache connect, send or receive opera-
tions.
Available in Postfix version 2.9 - 3.6:
smtp_per_record_deadline (no)
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per read or write system call, to a time limit to
- send or receive a complete record (an SMTP command line, SMTP
- response line, SMTP message content line, or TLS protocol mes-
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per read or write system call, to a time limit to
+ send or receive a complete record (an SMTP command line, SMTP
+ response line, SMTP message content line, or TLS protocol mes-
sage).
Available in Postfix version 2.11 and later:
smtp_connection_reuse_count_limit (0)
- When SMTP connection caching is enabled, the number of times
- that an SMTP session may be reused before it is closed, or zero
+ When SMTP connection caching is enabled, the number of times
+ that an SMTP session may be reused before it is closed, or zero
(no limit).
Available in Postfix version 3.4 and later:
@@ -885,13 +892,13 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 3.7 and later:
smtp_per_request_deadline (no)
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per plaintext or TLS read or write call, to a com-
- bined time limit for sending a complete SMTP request and for
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per plaintext or TLS read or write call, to a com-
+ bined time limit for sending a complete SMTP request and for
receiving a complete SMTP response.
smtp_min_data_rate (500)
- The minimum plaintext data transfer rate in bytes/second for
+ The minimum plaintext data transfer rate in bytes/second for
DATA requests, when deadlines are enabled with
smtp_per_request_deadline.
@@ -899,54 +906,54 @@ SMTP,(LMTP) SMTP,(LMTP)
transport_destination_concurrency_limit ($default_destination_concur-rency_limit)
- A transport-specific override for the default_destination_con-
+ A transport-specific override for the default_destination_con-
currency_limit parameter value, where transport is the master.cf
name of the message delivery transport.
transport_destination_recipient_limit ($default_destination_recipi-ent_limit)
A transport-specific override for the default_destination_recip-
- ient_limit parameter value, where transport is the master.cf
+ ient_limit parameter value, where transport is the master.cf
name of the message delivery transport.
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.
TROUBLE SHOOTING CONTROLSdebug_peer_level (2)
- The increment in verbose logging level when a nexthop destina-
- tion, remote client or server name or network address matches a
+ The increment in verbose logging level when a nexthop destina-
+ tion, remote client or server name or network address matches a
pattern given with the debug_peer_list parameter.
debug_peer_list (empty)
- Optional list of nexthop destination, remote client or server
- name or network address patterns that, if matched, cause the
- verbose logging level to increase by the amount specified in
+ Optional list of nexthop destination, remote client or server
+ name or network address patterns that, if matched, cause the
+ verbose logging level to increase by the amount specified in
$debug_peer_level.
error_notice_recipient (postmaster)
- The recipient of postmaster notifications about mail delivery
+ The recipient of postmaster notifications about mail delivery
problems that are caused by policy, resource, software or proto-
col errors.
internal_mail_filter_classes (empty)
- What categories of Postfix-generated mail are subject to
- before-queue content inspection by non_smtpd_milters,
+ What categories of Postfix-generated mail are subject to
+ before-queue content inspection by non_smtpd_milters,
header_checks and body_checks.
notify_classes (resource, software)
@@ -954,46 +961,46 @@ SMTP,(LMTP) SMTP,(LMTP)
MISCELLANEOUS CONTROLSbest_mx_transport (empty)
- Where the Postfix SMTP client should deliver mail when it
+ Where the Postfix SMTP client should deliver mail when it
detects a "mail loops back to myself" error condition.
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 sub-second delay values.
disable_dns_lookups (no)
Disable DNS lookups in the Postfix SMTP and LMTP clients.
inet_interfaces (all)
- The local network interface addresses that this mail system
+ The local network interface addresses that this mail system
receives mail on.
inet_protocols (see 'postconf -d' output)
- The Internet protocols Postfix will attempt to use when making
+ The Internet protocols Postfix will attempt to use when making
or accepting connections.
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.
lmtp_assume_final (no)
- When a remote LMTP server announces no DSN support, assume that
- the server performs final delivery, and send "delivered" deliv-
+ When a remote LMTP server announces no DSN support, assume that
+ the server performs final delivery, and send "delivered" deliv-
ery status notifications instead of "relayed".
lmtp_tcp_port (24)
The default TCP port that the Postfix LMTP client connects to.
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)
@@ -1007,21 +1014,21 @@ SMTP,(LMTP) SMTP,(LMTP)
The process name of a Postfix command or daemon process.
proxy_interfaces (empty)
- The remote network interface addresses that this mail system
- receives mail on by way of a proxy or network address transla-
+ The remote network interface addresses that this mail system
+ receives mail on by way of a proxy or network address transla-
tion unit.
smtp_address_preference (any)
The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
- client will try first, when a destination has IPv6 and IPv4
+ client will try first, when a destination has IPv6 and IPv4
addresses with equal MX preference.
smtp_bind_address (empty)
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv4 connection.
smtp_bind_address6 (empty)
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv6 connection.
smtp_helo_name ($myhostname)
@@ -1041,7 +1048,7 @@ SMTP,(LMTP) SMTP,(LMTP)
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 with Postfix 2.2 and earlier:
@@ -1053,14 +1060,14 @@ SMTP,(LMTP) SMTP,(LMTP)
Available with Postfix 2.3 and later:
smtp_fallback_relay ($fallback_relay)
- Optional list of relay destinations that will be used when an
- SMTP destination is not found, or when delivery fails due to a
+ Optional list of relay destinations that will be used when an
+ SMTP destination is not found, or when delivery fails due to a
non-permanent error.
Available with Postfix 3.0 and later:
smtp_address_verify_target (rcpt)
- In the context of email address verification, the SMTP protocol
+ In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Available with Postfix 3.1 and later:
@@ -1082,7 +1089,7 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix 3.7 and later:
smtp_bind_address_enforce (no)
- Defer delivery when the Postfix SMTP client cannot apply the
+ Defer delivery when the Postfix SMTP client cannot apply the
smtp_bind_address or smtp_bind_address6 setting.
SEE ALSO
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index 57b9b1390..d816022fa 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -885,10 +885,6 @@ To avoid mailer loops, automatic BCC recipients are not generated
after Postfix forwards mail internally, or after Postfix generates
mail itself.
-
This feature is available in Postfix 2.5 and later when compiled
with Cyrus SASL 2.1.22 or later.
@@ -6213,11 +6205,6 @@ prepend "uid:" to the numerical UID and use that instead.
This feature ignores address extensions in the user-specified
envelope sender address.
-
Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, specify an external filter such as a Milter,
-with the non_smtpd_milters parameter. For example:
-https://github.com/magcks/milterfrom.
-
The following sender patterns are special; these cannot be used
as part of a longer pattern.
@@ -10010,10 +9997,6 @@ To avoid mailer loops, automatic BCC recipients are not generated
after Postfix forwards mail internally, or after Postfix generates
mail itself.
-
@@ -10752,10 +10735,6 @@ To avoid mailer loops, automatic BCC recipients are not generated
after Postfix forwards mail internally, or after Postfix generates
mail itself.
-
Enable support for RFC 8460 TLSRPT notifications. A mail receiving
+domain can publish a TLSRPT policy in DNS, to request periodic
+summaries of successful and failed SMTP over TLS connections to
+their mail servers. This feature requires that Postfix is built
+with a TLSRPT supporting library.
The pathname of a UNIX-domain datagram socket that is managed
+by a local TLSRPT reporting service. This parameter must specify a
+pathname (absolute, or relative to $queue_directory) when
+"smtp_tlsrpt_enable = yes".
+
+
This feature is available in Postfix ≥ 3.10.
+
+
smtp_use_tls
@@ -17780,17 +17786,10 @@ for authentication. The available types are listed with the
(default: empty)
-Optional lookup table with the SASL login names that own the
-envelope sender
+Optional lookup table with the SASL login names that own the sender
(MAIL FROM) addresses.
-
Note: to enforce that the From: header address
-matches the envelope sender (MAIL FROM) address, use an external
-filter such as a Milter, for the submission, submissions, or smtps
-services. For example: https://github.com/magcks/milterfrom.
-
-
Specify zero or more "type:name" lookup tables, separated by
whitespace or comma. Tables will be searched in the specified order
@@ -17893,11 +17892,6 @@ or the SASL login name is not an owner for that address.
This prevents an authenticated client from using a MAIL FROM address
that they do not explicitly own.
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, use an external filter such as a Milter,
-for the submission, submissions, or smtps services. For example:
-https://github.com/magcks/milterfrom.
-
This feature is available in Postfix version 2.1 and later.
@@ -17914,11 +17908,6 @@ This protects any MAIL FROM address that is listed in
$smtpd_sender_login_maps, while still allowing a client to use any
unlisted MAIL FROM address.
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, use an external filter such as a Milter,
-for the submission, submissions, or smtps services. For example:
-https://github.com/magcks/milterfrom.
-
This feature is available in Postfix version 2.11 and later.
@@ -17960,11 +17949,6 @@ authenticated with SASL.
With SASL enabled, this prevents an unauthenticated client from
using any MAIL FROM address that is listed in $smtpd_sender_login_maps.
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, use an external filter such as a Milter,
-for the submission, submissions, or smtps services. For example:
-https://github.com/magcks/milterfrom.
-
This feature is available in Postfix version 2.1 and later.
diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html
index 4cc187c75..dbf71d264 100644
--- a/postfix/html/smtp.8.html
+++ b/postfix/html/smtp.8.html
@@ -746,41 +746,48 @@ SMTP,(LMTP) SMTP,(LMTP)
Request that remote SMTP servers send an RFC7250 raw public key
instead of an X.509 certificate.
+ smtp_tlsrpt_enable (no)
+ Enable support for RFC 8460 TLSRPT notifications.
+
+ smtp_tlsrpt_socket_name (empty)
+ The pathname of a UNIX-domain datagram socket that is managed by
+ a local TLSRPT reporting service.
+
OBSOLETE STARTTLS CONTROLS
- The following configuration parameters exist for compatibility with
- Postfix versions before 2.3. Support for these will be removed in a
+ The following configuration parameters exist for compatibility with
+ Postfix versions before 2.3. Support for these will be removed in a
future release.
smtp_use_tls (no)
- Opportunistic mode: use TLS when a remote SMTP server announces
+ Opportunistic mode: use TLS when a remote SMTP server announces
STARTTLS support, otherwise send the mail in the clear.
smtp_enforce_tls (no)
- Enforcement mode: require that remote SMTP servers use TLS
+ Enforcement mode: require that remote SMTP servers use TLS
encryption, and never send mail in the clear.
smtp_tls_enforce_peername (yes)
- With mandatory TLS encryption, require that the remote SMTP
- server hostname matches the information in the remote SMTP
+ With mandatory TLS encryption, require that the remote SMTP
+ server hostname matches the information in the remote SMTP
server certificate.
smtp_tls_per_site (empty)
- Optional lookup tables with the Postfix SMTP client TLS usage
- policy by next-hop destination and by remote SMTP server host-
+ Optional lookup tables with the Postfix SMTP client TLS usage
+ policy by next-hop destination and by remote SMTP server host-
name.
smtp_tls_cipherlist (empty)
- Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
cipher list.
RESOURCE AND RATE CONTROLSsmtp_connect_timeout (30s)
- The Postfix SMTP client time limit for completing a TCP connec-
+ The Postfix SMTP client time limit for completing a TCP connec-
tion, or zero (use the operating system built-in time limit).
smtp_helo_timeout (300s)
- The Postfix SMTP client time limit for sending the HELO or EHLO
- command, and for receiving the initial remote SMTP server
+ The Postfix SMTP client time limit for sending the HELO or EHLO
+ command, and for receiving the initial remote SMTP server
response.
lmtp_lhlo_timeout (300s)
@@ -792,19 +799,19 @@ SMTP,(LMTP) SMTP,(LMTP)
mand, and for receiving the remote SMTP server response.
smtp_mail_timeout (300s)
- The Postfix SMTP client time limit for sending the MAIL FROM
+ The Postfix SMTP client time limit for sending the MAIL FROM
command, and for receiving the remote SMTP server response.
smtp_rcpt_timeout (300s)
- The Postfix SMTP client time limit for sending the SMTP RCPT TO
+ The Postfix SMTP client time limit for sending the SMTP RCPT TO
command, and for receiving the remote SMTP server response.
smtp_data_init_timeout (120s)
- The Postfix SMTP client time limit for sending the SMTP DATA
+ The Postfix SMTP client time limit for sending the SMTP DATA
command, and for receiving the remote SMTP server response.
smtp_data_xfer_timeout (180s)
- The Postfix SMTP client time limit for sending the SMTP message
+ The Postfix SMTP client time limit for sending the SMTP message
content.
smtp_data_done_timeout (600s)
@@ -818,13 +825,13 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 2.1 and later:
smtp_mx_address_limit (5)
- The maximal number of MX (mail exchanger) IP addresses that can
- result from Postfix SMTP client mail exchanger lookups, or zero
+ The maximal number of MX (mail exchanger) IP addresses that can
+ result from Postfix SMTP client mail exchanger lookups, or zero
(no limit).
smtp_mx_session_limit (2)
- The maximal number of SMTP sessions per delivery request before
- the Postfix SMTP client gives up or delivers to a fall-back
+ The maximal number of SMTP sessions per delivery request before
+ the Postfix SMTP client gives up or delivers to a fall-back
relay host, or zero (no limit).
smtp_rset_timeout (20s)
@@ -834,17 +841,17 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 2.2 and earlier:
lmtp_cache_connection (yes)
- Keep Postfix LMTP client connections open for up to $max_idle
+ Keep Postfix LMTP client connections open for up to $max_idle
seconds.
Available in Postfix version 2.2 and later:
smtp_connection_cache_destinations (empty)
- Permanently enable SMTP connection caching for the specified
+ Permanently enable SMTP connection caching for the specified
destinations.
smtp_connection_cache_on_demand (yes)
- Temporarily enable SMTP connection caching while a destination
+ Temporarily enable SMTP connection caching while a destination
has a high volume of mail in the active queue.
smtp_connection_reuse_time_limit (300s)
@@ -858,23 +865,23 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 2.3 and later:
connection_cache_protocol_timeout (5s)
- Time limit for connection cache connect, send or receive opera-
+ Time limit for connection cache connect, send or receive opera-
tions.
Available in Postfix version 2.9 - 3.6:
smtp_per_record_deadline (no)
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per read or write system call, to a time limit to
- send or receive a complete record (an SMTP command line, SMTP
- response line, SMTP message content line, or TLS protocol mes-
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per read or write system call, to a time limit to
+ send or receive a complete record (an SMTP command line, SMTP
+ response line, SMTP message content line, or TLS protocol mes-
sage).
Available in Postfix version 2.11 and later:
smtp_connection_reuse_count_limit (0)
- When SMTP connection caching is enabled, the number of times
- that an SMTP session may be reused before it is closed, or zero
+ When SMTP connection caching is enabled, the number of times
+ that an SMTP session may be reused before it is closed, or zero
(no limit).
Available in Postfix version 3.4 and later:
@@ -885,13 +892,13 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix version 3.7 and later:
smtp_per_request_deadline (no)
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per plaintext or TLS read or write call, to a com-
- bined time limit for sending a complete SMTP request and for
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per plaintext or TLS read or write call, to a com-
+ bined time limit for sending a complete SMTP request and for
receiving a complete SMTP response.
smtp_min_data_rate (500)
- The minimum plaintext data transfer rate in bytes/second for
+ The minimum plaintext data transfer rate in bytes/second for
DATA requests, when deadlines are enabled with
smtp_per_request_deadline.
@@ -899,54 +906,54 @@ SMTP,(LMTP) SMTP,(LMTP)
transport_destination_concurrency_limit ($default_destination_concur-rency_limit)
- A transport-specific override for the default_destination_con-
+ A transport-specific override for the default_destination_con-
currency_limit parameter value, where transport is the master.cf
name of the message delivery transport.
transport_destination_recipient_limit ($default_destination_recipi-ent_limit)
A transport-specific override for the default_destination_recip-
- ient_limit parameter value, where transport is the master.cf
+ ient_limit parameter value, where transport is the master.cf
name of the message delivery transport.
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.
TROUBLE SHOOTING CONTROLSdebug_peer_level (2)
- The increment in verbose logging level when a nexthop destina-
- tion, remote client or server name or network address matches a
+ The increment in verbose logging level when a nexthop destina-
+ tion, remote client or server name or network address matches a
pattern given with the debug_peer_list parameter.
debug_peer_list (empty)
- Optional list of nexthop destination, remote client or server
- name or network address patterns that, if matched, cause the
- verbose logging level to increase by the amount specified in
+ Optional list of nexthop destination, remote client or server
+ name or network address patterns that, if matched, cause the
+ verbose logging level to increase by the amount specified in
$debug_peer_level.
error_notice_recipient (postmaster)
- The recipient of postmaster notifications about mail delivery
+ The recipient of postmaster notifications about mail delivery
problems that are caused by policy, resource, software or proto-
col errors.
internal_mail_filter_classes (empty)
- What categories of Postfix-generated mail are subject to
- before-queue content inspection by non_smtpd_milters,
+ What categories of Postfix-generated mail are subject to
+ before-queue content inspection by non_smtpd_milters,
header_checks and body_checks.
notify_classes (resource, software)
@@ -954,46 +961,46 @@ SMTP,(LMTP) SMTP,(LMTP)
MISCELLANEOUS CONTROLSbest_mx_transport (empty)
- Where the Postfix SMTP client should deliver mail when it
+ Where the Postfix SMTP client should deliver mail when it
detects a "mail loops back to myself" error condition.
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 sub-second delay values.
disable_dns_lookups (no)
Disable DNS lookups in the Postfix SMTP and LMTP clients.
inet_interfaces (all)
- The local network interface addresses that this mail system
+ The local network interface addresses that this mail system
receives mail on.
inet_protocols (see 'postconf -d' output)
- The Internet protocols Postfix will attempt to use when making
+ The Internet protocols Postfix will attempt to use when making
or accepting connections.
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.
lmtp_assume_final (no)
- When a remote LMTP server announces no DSN support, assume that
- the server performs final delivery, and send "delivered" deliv-
+ When a remote LMTP server announces no DSN support, assume that
+ the server performs final delivery, and send "delivered" deliv-
ery status notifications instead of "relayed".
lmtp_tcp_port (24)
The default TCP port that the Postfix LMTP client connects to.
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)
@@ -1007,21 +1014,21 @@ SMTP,(LMTP) SMTP,(LMTP)
The process name of a Postfix command or daemon process.
proxy_interfaces (empty)
- The remote network interface addresses that this mail system
- receives mail on by way of a proxy or network address transla-
+ The remote network interface addresses that this mail system
+ receives mail on by way of a proxy or network address transla-
tion unit.
smtp_address_preference (any)
The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
- client will try first, when a destination has IPv6 and IPv4
+ client will try first, when a destination has IPv6 and IPv4
addresses with equal MX preference.
smtp_bind_address (empty)
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv4 connection.
smtp_bind_address6 (empty)
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv6 connection.
smtp_helo_name ($myhostname)
@@ -1041,7 +1048,7 @@ SMTP,(LMTP) SMTP,(LMTP)
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 with Postfix 2.2 and earlier:
@@ -1053,14 +1060,14 @@ SMTP,(LMTP) SMTP,(LMTP)
Available with Postfix 2.3 and later:
smtp_fallback_relay ($fallback_relay)
- Optional list of relay destinations that will be used when an
- SMTP destination is not found, or when delivery fails due to a
+ Optional list of relay destinations that will be used when an
+ SMTP destination is not found, or when delivery fails due to a
non-permanent error.
Available with Postfix 3.0 and later:
smtp_address_verify_target (rcpt)
- In the context of email address verification, the SMTP protocol
+ In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Available with Postfix 3.1 and later:
@@ -1082,7 +1089,7 @@ SMTP,(LMTP) SMTP,(LMTP)
Available in Postfix 3.7 and later:
smtp_bind_address_enforce (no)
- Defer delivery when the Postfix SMTP client cannot apply the
+ Defer delivery when the Postfix SMTP client cannot apply the
smtp_bind_address or smtp_bind_address6 setting.
SEE ALSO
diff --git a/postfix/makedefs b/postfix/makedefs
index e21e648ad..248c0aa8f 100644
--- a/postfix/makedefs
+++ b/postfix/makedefs
@@ -973,7 +973,7 @@ CCARGS="$CCARGS -DSNAPSHOT"
# Non-production: needs thorough testing, or major changes are still
# needed before the code stabilizes.
-#CCARGS="$CCARGS -DNONPROD"
+CCARGS="$CCARGS -DNONPROD"
# Workaround: prepend Postfix include files before other include files.
CCARGS="-I. -I../../include $CCARGS"
diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5
index efbde6204..e2eb6779e 100644
--- a/postfix/man/man5/access.5
+++ b/postfix/man/man5/access.5
@@ -329,13 +329,9 @@ After the message is queued, send the message to the specified
address instead of the intended recipient(s). When multiple
\fBREDIRECT\fR actions fire, only the last one takes effect.
.sp
-Note 1: this action overrides the FILTER action, and currently
+Note: this action overrides the FILTER action, and currently
overrides all recipients of the message.
.sp
-Note 2: a REDIRECT address is subject to canonicalization
-(add missing domain) but NOT subject to canonical, masquerade,
-bcc, or virtual alias mapping.
-.sp
This feature is available in Postfix 2.1 and later.
.IP "\fBINFO \fIoptional text...\fR
Log an informational record with the optional text, together
diff --git a/postfix/man/man5/header_checks.5 b/postfix/man/man5/header_checks.5
index fc9b6503f..92c1de95e 100644
--- a/postfix/man/man5/header_checks.5
+++ b/postfix/man/man5/header_checks.5
@@ -331,14 +331,10 @@ inspect the next input line. After the message is queued,
it will be sent to the specified address instead of the
intended recipient(s).
.sp
-Note 1: this action overrides the \fBFILTER\fR action, and affects
+Note: this action overrides the \fBFILTER\fR action, and affects
all recipients of the message. If multiple \fBREDIRECT\fR actions
fire, only the last one is executed.
.sp
-Note 2: a REDIRECT address is subject to canonicalization
-(add missing domain) but NOT subject to canonical, masquerade,
-bcc, or virtual alias mapping.
-.sp
This feature is available in Postfix 2.1 and later.
.sp
This feature is not supported with smtp header/body checks.
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index 91beac06d..19e359d30 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -511,10 +511,6 @@ Note: automatic BCC recipients are produced only for new mail.
To avoid mailer loops, automatic BCC recipients are not generated
after Postfix forwards mail internally, or after Postfix generates
mail itself.
-.PP
-Note: automatic BCC recipients are subject to address
-canonicalization (add missing domain), canonical_maps, masquerade_domains,
-and virtual_alias_maps.
.SH anvil_rate_time_unit (default: 60s)
The time unit over which client connection rates and other rates
are calculated.
@@ -1115,10 +1111,6 @@ currently used only to locate the $smtpd_sasl_path.conf file.
Specify zero or more directories separated by a colon character,
or an empty value to use Cyrus SASL's built\-in search path.
.PP
-Note: some Debian\-based Postfix distributions ignore the
-"cyrus_sasl_config_path" parameter setting, and force Postfix to
-open the file /etc/postfix/sasl/smtpd.conf.
-.PP
This feature is available in Postfix 2.5 and later when compiled
with Cyrus SASL 2.1.22 or later.
.SH daemon_directory (default: see "postconf \-d" output)
@@ -3746,11 +3738,6 @@ prepend "\fBuid:\fR" to the numerical UID and use that instead.
This feature ignores address extensions in the user\-specified
envelope sender address.
.PP
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, specify an external filter such as a Milter,
-with the non_smtpd_milters parameter. For example:
-https://github.com/magcks/milterfrom.
-.PP
The following sender patterns are special; these cannot be used
as part of a longer pattern.
.IP "\fB * \fR
@@ -6167,10 +6154,6 @@ To avoid mailer loops, automatic BCC recipients are not generated
after Postfix forwards mail internally, or after Postfix generates
mail itself.
.PP
-Note: automatic BCC recipients are subject to address
-canonicalization (add missing domain), canonical_maps, masquerade_domains,
-and virtual_alias_maps.
-.PP
Example:
.PP
.nf
@@ -6684,10 +6667,6 @@ To avoid mailer loops, automatic BCC recipients are not generated
after Postfix forwards mail internally, or after Postfix generates
mail itself.
.PP
-Note: automatic BCC recipients are subject to address
-canonicalization (add missing domain), canonical_maps, masquerade_domains,
-and virtual_alias_maps.
-.PP
Example:
.PP
.nf
@@ -9860,6 +9839,21 @@ More examples are in TLS_README, including examples for older
Postfix versions.
.PP
This feature is available in Postfix 3.0 and later.
+.SH smtp_tlsrpt_enable (default: no)
+Enable support for RFC 8460 TLSRPT notifications. A mail receiving
+domain can publish a TLSRPT policy in DNS, to request periodic
+summaries of successful and failed SMTP over TLS connections to
+their mail servers. This feature requires that Postfix is built
+with a TLSRPT supporting library.
+.PP
+This feature is available in Postfix >= 3.10.
+.SH smtp_tlsrpt_socket_name (default: empty)
+The pathname of a UNIX\-domain datagram socket that is managed
+by a local TLSRPT reporting service. This parameter must specify a
+pathname (absolute, or relative to $queue_directory) when
+"smtp_tlsrpt_enable = yes".
+.PP
+This feature is available in Postfix >= 3.10.
.SH smtp_use_tls (default: no)
Opportunistic mode: use TLS when a remote SMTP server announces
STARTTLS support, otherwise send the mail in the clear. Beware:
@@ -12052,16 +12046,8 @@ for authentication. The available types are listed with the
.PP
This feature is available in Postfix 2.3 and later.
.SH smtpd_sender_login_maps (default: empty)
-Optional lookup table with the SASL login names that own the
-envelope sender
+Optional lookup table with the SASL login names that own the sender
(MAIL FROM) addresses.
-.sp
-.in +4
-Note: to enforce that the From: header address
-matches the envelope sender (MAIL FROM) address, use an external
-filter such as a Milter, for the submission, submissions, or smtps
-services. For example: https://github.com/magcks/milterfrom.
-.in -4
.PP
Specify zero or more "type:name" lookup tables, separated by
whitespace or comma. Tables will be searched in the specified order
@@ -12134,11 +12120,6 @@ or the SASL login name is not an owner for that address.
This prevents an authenticated client from using a MAIL FROM address
that they do not explicitly own.
.br
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, use an external filter such as a Milter,
-for the submission, submissions, or smtps services. For example:
-https://github.com/magcks/milterfrom.
-.br
This feature is available in Postfix version 2.1 and later.
.br
.IP "\fBreject_known_sender_login_mismatch\fR"
@@ -12154,11 +12135,6 @@ This protects any MAIL FROM address that is listed in
$smtpd_sender_login_maps, while still allowing a client to use any
unlisted MAIL FROM address.
.br
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, use an external filter such as a Milter,
-for the submission, submissions, or smtps services. For example:
-https://github.com/magcks/milterfrom.
-.br
This feature is available in Postfix version 2.11 and later.
.br
.IP "\fBreject_non_fqdn_sender\fR"
@@ -12200,11 +12176,6 @@ authenticated with SASL.
With SASL enabled, this prevents an unauthenticated client from
using any MAIL FROM address that is listed in $smtpd_sender_login_maps.
.br
-Note: to enforce that the From: header address matches the envelope
-sender (MAIL FROM) address, use an external filter such as a Milter,
-for the submission, submissions, or smtps services. For example:
-https://github.com/magcks/milterfrom.
-.br
This feature is available in Postfix version 2.1 and later.
.br
.IP "\fBreject_unknown_sender_domain\fR"
diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8
index 0145350cb..a55d17636 100644
--- a/postfix/man/man8/smtp.8
+++ b/postfix/man/man8/smtp.8
@@ -672,6 +672,12 @@ Available in Postfix version 3.9 and later:
.IP "\fBsmtp_tls_enable_rpk (no)\fR"
Request that remote SMTP servers send an RFC7250 raw public key
instead of an X.509 certificate.
+.PP Available in Postfix version 3.10 and later:
+.IP "\fBsmtp_tlsrpt_enable (no)\fR"
+Enable support for RFC 8460 TLSRPT notifications.
+.IP "\fBsmtp_tlsrpt_socket_name (empty)\fR"
+The pathname of a UNIX\-domain datagram socket that is managed
+by a local TLSRPT reporting service.
.SH "OBSOLETE STARTTLS CONTROLS"
.na
.nf
diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink
index 6ca24c63d..6e7ba6954 100755
--- a/postfix/mantools/postlink
+++ b/postfix/mantools/postlink
@@ -721,6 +721,10 @@ while (<>) {
s;\bdnssec_probe\b;$&;g;
s;\bsmtp_tls_connection_reuse\b;$&;g;
s;\blmtp_tls_connection_reuse\b;$&;g;
+ s;\bsmtp_tlsrpt_enable\b;$&;g;
+ s;\bsmtp_tlsrpt_socket_name\b;$&;g;
+ s;\blmtp_tlsrpt_enable\b;$&;g;
+ s;\blmtp_tlsrpt_socket_name\b;$&;g;
s;\bsmtpd_enforce_tls\b;$&;g;
s;\bsmtpd_sasl_tls_security_options\b;$&;g;
s;\bsmtpd_sasl_type\b;$&;g;
diff --git a/postfix/proto/Makefile.in b/postfix/proto/Makefile.in
index f02ab1415..86476adf3 100644
--- a/postfix/proto/Makefile.in
+++ b/postfix/proto/Makefile.in
@@ -50,6 +50,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \
../html/STANDARD_CONFIGURATION_README.html \
../html/STRESS_README.html \
../html/TLS_README.html ../html/TLS_LEGACY_README.html \
+ ../html/TLSRPT_README.html \
../html/TUNING_README.html \
../html/UUCP_README.html \
../html/VERP_README.html ../html/VIRTUAL_README.html \
@@ -100,6 +101,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \
../README_FILES/STANDARD_CONFIGURATION_README \
../README_FILES/STRESS_README \
../README_FILES/TLS_README ../README_FILES/TLS_LEGACY_README \
+ ../README_FILES/TLSRPT_README \
../README_FILES/TUNING_README \
../README_FILES/UUCP_README \
../README_FILES/VERP_README ../README_FILES/VIRTUAL_README \
@@ -343,6 +345,9 @@ clobber:
../html/TLS_LEGACY_README.html: TLS_LEGACY_README.html
$(DETAB) $? | $(POSTLINK) >$@
+../html/TLSRPT_README.html: TLSRPT_README.html
+ $(DETAB) $? | $(POSTLINK) >$@
+
../README_FILES/ADDRESS_CLASS_README: ADDRESS_CLASS_README.html
$(DETAB) $? | $(HT2READ) >$@
@@ -529,6 +534,9 @@ clobber:
../README_FILES/TLS_LEGACY_README: TLS_LEGACY_README.html
$(DETAB) $? | $(HT2READ) >$@
+../README_FILES/TLSRPT_README: TLSRPT_README.html
+ $(DETAB) $? | $(HT2READ) >$@
+
../README_FILES/AAAREADME: ../html/index.html $(MAKEAAA)
$(MAKEAAA) ../html/index.html | $(HT2READ) | $(DETAB) >$@
diff --git a/postfix/proto/TLSRPT_README.html b/postfix/proto/TLSRPT_README.html
new file mode 100644
index 000000000..13d8aa653
--- /dev/null
+++ b/postfix/proto/TLSRPT_README.html
@@ -0,0 +1,276 @@
+
+
+
+
+
+
+Postfix TLSRPT notification Howto
+
+
+
+
+
+
+
+
+
The TLSRPT protocol is defined in RFC 8460. With this, an email
+receiving domain can publish a policy in DNS to request daily summary
+reports for successful and failed TLS connections to that domain.
+Support for TLSRPT was added in Postfix 3.10.
+
+
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable
+= yes"), the Postfix SMTP and TLS clients engines will generate
+"success" and "failure" events, and will pass those events to a
+TLSRPT client library and report generator that are maintained by
+sys4. The Postfix implementation supports both DANE (Postfix built-in)
+and MTA-STS (through an smtp_tls_policy_maps plug-in).
+
+
The high-level diagram shows how Postfix events are reported
+to domains that publish a TLSRPT policy.
+
+
+
+
+
+
Postfix SMTP and TLS client engines
+
-->
+
+
TLSRPT client library
-->
+
+
+
TLSRPT report generator
+-->
+
+
Email or HTTP summary
+
+
+
+
+
+
The Postfix smtp(8) client process implements the SMTP client
+engine. With "smtp_tls_connection_reuse = no", the smtp(8) client
+process also implements the TLS client engine. With
+"smtp_tls_connection_reuse = yes", the smtp(8) client process
+delegates TLS processing to a Postfix tlsproxy(8) process. Either
+way, Postfix will generate the exact same TLSRPT events.
These instructions assume that you build Postfix from source
+code as described in the INSTALL document. Some modification may
+be required if you build Postfix from a vendor-specific source
+package.
+
+
The Postfix TLSRPT client builds on a TLSRPT client library
+whose source code can be obtained from:
+
+
+
https://github.com/sys4/tlsrpt
+
+
+
The library is typically installed as a header file in
+/usr/local/include/tlsrpt.h and an object library in
+/usr/local/lib/libtlsrpt.a or /usr/local/lib/libtlsrpt.so. The
+actual pathnames will depend on OS platform conventions.
+
+
In order to build Postfix with TLSRPT support, you will need
+to add compiler options -DUSE_TLSRPT (to build with TLSRPT
+support), and -I (with the directory containing the tlsrpt.h
+header file), and you will need to add linker options to link with
+the TLSRPT client library, for example:
Note: if your build command line already has CCARGS or AUXLIBS
+options, then simply append the above options to the existing CCARGS
+or AUXLIBS values.
The smtp_tlsrpt_socket_name parameter specifies an absolute
+pathname, or a pathname that is relative to $queue_directory.
+
+
A good socket location would be under $queue_directory/run/tlsrpt
+or $queue_directory/var/run/tlsrpt. These can then be configured
+in Postfix as a relative pathname (run/tlsrpt/tlsrpt.sock or
+var/run/tlsrpt/tlsrpt.sock) so that the same name will work with
+and without Postfix chroot support. Do not specify a location under
+directory that is already used by Postfix programs. Only Postfix
+programs should create sockets there.
Postfix supports MTA-STS though an smtp_tls_policy_maps policy
+plugin. Postfix TLSRPT support expects a response with the usual
+security level and matching requirements, plus any applicable
+name=value attributes described below. Specify { name = value }
+when a value may contain whitespace.
+
+
+
+
Note 1: Postfix 3.10 and later will accept these attributes in
+an MTA-STS response even if TLSRPT support is disabled (at build
+time or run time), but it will not use most attributes except
+ttl and policy_failure.
+
+
Note 2: It is an error to specify these attributes for a non-STS
+policy.
+
+
+
+
The examples in the table apply to the MTA-STS policy example
+given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5.
+
+
+
+
+
policy_type=type
+
+
Specify sts or no-policy-found.
+
+
policy_domain=name
+
+
The domain that the MTA-STS policy applies to.
+
+
policy_ttl=time
+
+
How long (in seconds) a Postfix SMTP client process will cache
+the MTA-STS plugin response.
+
+
{ policy_string = value }
+
+
Specify one policy_string instance for each MTA-STS
+policy feature, enclosed inside "{" and "}" to protect whitespace
+in attribute values.
If specified, forces MTA-STS policy enforcement to fail with
+the indicated error, even if a server certificate would satisfy
+conventional PKI constraints.
+
+
Valid errors are sts-policy-fetch-error, sts-policy-invalid,
+sts-webpki-invalid, or the less informative
+validation-failure.
The following limitations exist primarily because some errors
+may be reported by a Postfix smtp(8) client process, and some errors
+by a Postfix tlsproxy(8) process. It is too difficult to propagate
+TLSRPT client library state between processes.
+
+
The Postfix TLSRPT client reports one final status (either
+'success' or 'failure') for each MTA that it is able to connect to.
+It cannot report a final status 'success' with some recoverable
+'failure'. Specifically:
+
+
+
+
The Postfix TLSRPT client can report either successful TLS
+policy compliance, or an unrecoverable failure that prevents TLS
+policy compliance (examples: all TLSA records are unusable, or some
+PKI error during certificate verification).
+
+
The Postfix TLSRPT client must not be used to report a
+potentially recoverable failure such as a non-parsable TLSA record,
+because some other TLSA record for the same host may still allow
+successful TLS policy compliance.
The TLSRPT client library and report generator are implemented
+and maintained by sys4 (sys4.de).
+
+
Wietse Venema implemented the integration with Postfix.
+
+
+
+
+
+
+
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index 24b8295f2..c8704c0d0 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -19402,3 +19402,22 @@ announce 8BITMIME support, or when a message line exceeds the SMTP
length limit.
This feature is available in Postfix ≥ 3.9.
+
+%PARAM smtp_tlsrpt_enable no
+
+
Enable support for RFC 8460 TLSRPT notifications. A mail receiving
+domain can publish a TLSRPT policy in DNS, to request periodic
+summaries of successful and failed SMTP over TLS connections to
+their mail servers. This feature requires that Postfix is built
+with a TLSRPT supporting library.
+
+
This feature is available in Postfix ≥ 3.10.
+
+%PARAM smtp_tlsrpt_socket_name
+
+
The pathname of a UNIX-domain datagram socket that is managed
+by a local TLSRPT reporting service. This parameter must specify a
+pathname (absolute, or relative to $queue_directory) when
+"smtp_tlsrpt_enable = yes".
+
+
This feature is available in Postfix ≥ 3.10.
diff --git a/postfix/proto/stop b/postfix/proto/stop
index ec9542f1c..171198f88 100644
--- a/postfix/proto/stop
+++ b/postfix/proto/stop
@@ -1615,3 +1615,28 @@ milterfrom
canonicalization
Orlitzky
Typofix
+RPT
+TLSRPT
+TLSRPTv
+TODOS
+WSP
+addv
+bugprone
+errnum
+libtlsrpt
+munge
+mystrerror
+protcol
+punycode
+pval
+rpt
+rua
+sockname
+tlsproxied
+tlsrpt
+trw
+datagram
+RPC
+datatracker
+webpki
+parsable
diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc
index bff4534d1..62bac0323 100644
--- a/postfix/proto/stop.double-cc
+++ b/postfix/proto/stop.double-cc
@@ -335,3 +335,10 @@ length length of 0 31 0 127
address address string length
whether the standard End of DATA sequence CRLF CRLF is required and
Require CRLF CRLF
+ must start with a version field v TLSRPTv1 followed by WSP WSP
+ policies policy policy type
+ policies policy policy string Ignored if the tls_policy_type
+ policies policy policy domain
+additional_detail additional_detail
+ignored ignored
+USE_TLSRPT USE_TLSRPT
diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html
index a4b2332a2..c63bcdaab 100644
--- a/postfix/proto/stop.double-proto-html
+++ b/postfix/proto/stop.double-proto-html
@@ -358,3 +358,4 @@ expected to become a list of comma separated names br br This
Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0
dt dt dd 2 Also enable verbose logging in the Postfix TLS
+Postfix Postfix legacy TLS Support
diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc
index 1314e0c0e..e68fbd1c1 100644
--- a/postfix/proto/stop.spell-cc
+++ b/postfix/proto/stop.spell-cc
@@ -1841,3 +1841,7 @@ foqvx
ILP
xxfi
optionsv
+rcv
+snd
+sts
+tlsrprt
diff --git a/postfix/proto/stop.spell-proto-html b/postfix/proto/stop.spell-proto-html
index e76dbfa4d..65f9fa062 100644
--- a/postfix/proto/stop.spell-proto-html
+++ b/postfix/proto/stop.spell-proto-html
@@ -379,3 +379,8 @@ Dextrous
ar
liveness
superset
+ltlsrpt
+sts
+STS
+STSv
+Sys
diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h
index 987b988f1..0f8b0b92d 100644
--- a/postfix/src/dns/dns.h
+++ b/postfix/src/dns/dns.h
@@ -221,6 +221,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
extern DNS_RR *dns_rr_shuffle(DNS_RR *);
extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
+extern DNS_RR *dns_rr_detach(DNS_RR *, DNS_RR *);
extern int var_dns_rr_list_limit;
/*
diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c
index 882a42ffb..e5775a276 100644
--- a/postfix/src/dns/dns_rr.c
+++ b/postfix/src/dns/dns_rr.c
@@ -52,6 +52,10 @@
/* DNS_RR *list;
/* DNS_RR *record;
/*
+/* DNS_RR *dns_rr_detach(list, record)
+/* DNS_RR *list;
+/* DNS_RR *record;
+/*
/* DNS_RR *dns_srv_rr_sort(list)
/* DNS_RR *list;
/*
@@ -118,10 +122,15 @@
/*
/* dns_rr_shuffle() randomly permutes a list of resource records.
/*
-/* dns_rr_remove() removes the specified record from the specified list.
+/* dns_rr_remove() disconnects the specified record from the
+/* specified list and destroys it.
/* The updated list is the result value.
/* The record MUST be a list member.
/*
+/* dns_rr_detach() disconnects the specified record from the
+/* specified list. The updated list is the result value.
+/* The record MUST be a list member.
+/*
/* dns_srv_rr_sort() sorts a list of SRV records according to
/* their priority and weight as described in RFC 2782.
/* LICENSE
@@ -464,16 +473,24 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list)
/* dns_rr_remove - remove record from list, return new list */
DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
+{
+ list = dns_rr_detach(list, record);
+ dns_rr_free(record);
+ return (list);
+}
+
+/* dns_rr_detach - detach record from list, return new list */
+
+DNS_RR *dns_rr_detach(DNS_RR *list, DNS_RR *record)
{
if (list == 0)
- msg_panic("dns_rr_remove: record not found");
+ msg_panic("dns_rr_detach: record not found");
if (list == record) {
list = record->next;
record->next = 0;
- dns_rr_free(record);
} else {
- list->next = dns_rr_remove(list->next, record);
+ list->next = dns_rr_detach(list->next, record);
}
return (list);
}
diff --git a/postfix/src/dns/dns_rr_test.c b/postfix/src/dns/dns_rr_test.c
index 7bbe76927..12cf01c58 100644
--- a/postfix/src/dns/dns_rr_test.c
+++ b/postfix/src/dns/dns_rr_test.c
@@ -361,6 +361,93 @@ static int append_to_elem_from_list_exact_fit(void)
return (eq_dns_rr_free(got, want));
}
+static int delete_middle_element(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+ DNS_RR *got, *want, *list;
+
+ ((list = a)->next = b)->next = c;
+ (want = dns_rr_copy(a))->next = dns_rr_copy(c);
+ got = dns_rr_remove(list, b);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int delete_first_element(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+ DNS_RR *got, *want, *list;
+
+ ((list = a)->next = b)->next = c;
+ (want = dns_rr_copy(b))->next = dns_rr_copy(c);
+ got = dns_rr_remove(list, a);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int delete_last_element(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+ DNS_RR *got, *want, *list;
+
+ ((list = a)->next = b)->next = c;
+ (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+ got = dns_rr_remove(list, c);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int detach_middle_element(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+ DNS_RR *got, *want, *list;
+
+ ((list = a)->next = b)->next = c;
+ (want = dns_rr_copy(a))->next = dns_rr_copy(c);
+ got = dns_rr_detach(list, b);
+ dns_rr_free(b);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int detach_first_element(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+ DNS_RR *got, *want, *list;
+
+ ((list = a)->next = b)->next = c;
+ (want = dns_rr_copy(b))->next = dns_rr_copy(c);
+ got = dns_rr_detach(list, a);
+ dns_rr_free(a);
+
+ return (eq_dns_rr_free(got, want));
+}
+
+static int detach_last_element(void)
+{
+ DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+ DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+ DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+ DNS_RR *got, *want, *list;
+
+ ((list = a)->next = b)->next = c;
+ (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+ got = dns_rr_detach(list, c);
+ dns_rr_free(c);
+
+ return (eq_dns_rr_free(got, want));
+}
+
/*
* The test cases.
*/
@@ -400,9 +487,15 @@ static const TEST_CASE test_cases[] = {
"append to element from list exact fit", append_to_elem_from_list_exact_fit,
/*
- * TODO: tests dns_rr_sort(), dns_rr_srv_sort(), dns_rr_remove(),
- * dns_rr_shuffle(), etc.
+ * TODO: tests for dns_rr_sort(), dns_rr_srv_sort(), dns_rr_shuffle(),
+ * etc.
*/
+ "delete element from list (middle)", delete_middle_element,
+ "delete element from list (first)", delete_first_element,
+ "delete element from list (last)", delete_last_element,
+ "detach element from list (middle)", detach_middle_element,
+ "detach element from list (first)", detach_first_element,
+ "detach element from list (last)", detach_last_element,
0,
};
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 1f03b0b34..8b52145b0 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -4455,6 +4455,22 @@ extern bool var_ign_srv_lookup_err;
#define DEF_ALLOW_SRV_FALLBACK 0
extern bool var_allow_srv_fallback;
+ /*
+ * TLSRPT notification support. The lmtp_ names must be defined because the
+ * build system enforces that every smtp_ parameter has an lmtp_ variant.
+ */
+#define VAR_SMTP_TLSRPT_ENABLE "smtp_tlsrpt_enable"
+#define DEF_SMTP_TLSRPT_ENABLE "no"
+#define VAR_LMTP_TLSRPT_ENABLE "lmtp_tlsrpt_enable"
+#define DEF_LMTP_TLSRPT_ENABLE DEF_SMTP_TLSRPT_ENABLE
+extern bool var_smtp_tlsrpt_enable;
+
+#define VAR_SMTP_TLSRPT_SOCKNAME "smtp_tlsrpt_socket_name"
+#define DEF_SMTP_TLSRPT_SOCKNAME ""
+#define VAR_LMTP_TLSRPT_SOCKNAME "lmtp_tlsrpt_socket_name"
+#define DEF_LMTP_TLSRPT_SOCKNAME DEF_SMTP_TLSRPT_SOCKNAME
+extern char *var_smtp_tlsrpt_sockname;
+
/* LICENSE
/* .ad
/* .fi
@@ -4469,6 +4485,9 @@ extern bool var_allow_srv_fallback;
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c
index b474a4006..193588ef4 100644
--- a/postfix/src/posttls-finger/posttls-finger.c
+++ b/postfix/src/posttls-finger/posttls-finger.c
@@ -835,6 +835,8 @@ static int starttls(STATE *state)
= vstring_str(cipher_exclusions),
matchargv = state->match,
mdalg = state->mdalg,
+ tlsrpt = 0,
+ fail_type = 0,
dane = state->ddane ?
state->ddane : state->dane);
@@ -939,6 +941,8 @@ static int starttls(STATE *state)
= vstring_str(cipher_exclusions),
matchargv = state->match,
mdalg = state->mdalg,
+ tlsrpt = 0,
+ fail_type = 0,
dane = state->ddane ? state->ddane : state->dane);
} /* tlsproxy_mode */
vstring_free(cipher_exclusions);
diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in
index b9f04ca13..685a72b67 100644
--- a/postfix/src/smtp/Makefile.in
+++ b/postfix/src/smtp/Makefile.in
@@ -2,11 +2,11 @@ SHELL = /bin/sh
SRCS = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \
smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
- smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c
+ smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c smtp_tlsrpt.c
OBJS = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \
smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
- smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o
+ smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o smtp_tlsrpt.o
HDRS = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -267,6 +267,7 @@ smtp_connect.o: ../../include/timed_connect.h
smtp_connect.o: ../../include/tls.h
smtp_connect.o: ../../include/tls_proxy.h
smtp_connect.o: ../../include/tok822.h
+smtp_connect.o: ../../include/valid_hostname.h
smtp_connect.o: ../../include/vbuf.h
smtp_connect.o: ../../include/vstream.h
smtp_connect.o: ../../include/vstring.h
@@ -447,6 +448,7 @@ smtp_proto.o: ../../include/stringops.h
smtp_proto.o: ../../include/sys_defs.h
smtp_proto.o: ../../include/tls.h
smtp_proto.o: ../../include/tls_proxy.h
+smtp_proto.o: ../../include/tlsrpt_wrapper.h
smtp_proto.o: ../../include/tok822.h
smtp_proto.o: ../../include/uxtext.h
smtp_proto.o: ../../include/vbuf.h
@@ -737,6 +739,7 @@ smtp_state.o: ../../include/string_list.h
smtp_state.o: ../../include/sys_defs.h
smtp_state.o: ../../include/tls.h
smtp_state.o: ../../include/tls_proxy.h
+smtp_state.o: ../../include/tlsrpt_wrapper.h
smtp_state.o: ../../include/tok822.h
smtp_state.o: ../../include/vbuf.h
smtp_state.o: ../../include/vstream.h
@@ -770,6 +773,7 @@ smtp_tls_policy.o: ../../include/name_mask.h
smtp_tls_policy.o: ../../include/nvtable.h
smtp_tls_policy.o: ../../include/recipient_list.h
smtp_tls_policy.o: ../../include/resolve_clnt.h
+smtp_tls_policy.o: ../../include/sane_strtol.h
smtp_tls_policy.o: ../../include/scache.h
smtp_tls_policy.o: ../../include/sock_addr.h
smtp_tls_policy.o: ../../include/string_list.h
@@ -777,6 +781,7 @@ smtp_tls_policy.o: ../../include/stringops.h
smtp_tls_policy.o: ../../include/sys_defs.h
smtp_tls_policy.o: ../../include/tls.h
smtp_tls_policy.o: ../../include/tls_proxy.h
+smtp_tls_policy.o: ../../include/tlsrpt_wrapper.h
smtp_tls_policy.o: ../../include/tok822.h
smtp_tls_policy.o: ../../include/valid_hostname.h
smtp_tls_policy.o: ../../include/valid_utf8_hostname.h
@@ -785,6 +790,48 @@ smtp_tls_policy.o: ../../include/vstream.h
smtp_tls_policy.o: ../../include/vstring.h
smtp_tls_policy.o: smtp.h
smtp_tls_policy.o: smtp_tls_policy.c
+smtp_tlsrpt.o: ../../include/argv.h
+smtp_tlsrpt.o: ../../include/attr.h
+smtp_tlsrpt.o: ../../include/check_arg.h
+smtp_tlsrpt.o: ../../include/deliver_request.h
+smtp_tlsrpt.o: ../../include/dict.h
+smtp_tlsrpt.o: ../../include/dns.h
+smtp_tlsrpt.o: ../../include/dsn.h
+smtp_tlsrpt.o: ../../include/dsn_buf.h
+smtp_tlsrpt.o: ../../include/header_body_checks.h
+smtp_tlsrpt.o: ../../include/header_opts.h
+smtp_tlsrpt.o: ../../include/hex_code.h
+smtp_tlsrpt.o: ../../include/htable.h
+smtp_tlsrpt.o: ../../include/inet_proto.h
+smtp_tlsrpt.o: ../../include/mail_params.h
+smtp_tlsrpt.o: ../../include/maps.h
+smtp_tlsrpt.o: ../../include/match_list.h
+smtp_tlsrpt.o: ../../include/midna_domain.h
+smtp_tlsrpt.o: ../../include/mime_state.h
+smtp_tlsrpt.o: ../../include/msg.h
+smtp_tlsrpt.o: ../../include/msg_stats.h
+smtp_tlsrpt.o: ../../include/myaddrinfo.h
+smtp_tlsrpt.o: ../../include/myflock.h
+smtp_tlsrpt.o: ../../include/mymalloc.h
+smtp_tlsrpt.o: ../../include/name_code.h
+smtp_tlsrpt.o: ../../include/name_mask.h
+smtp_tlsrpt.o: ../../include/nvtable.h
+smtp_tlsrpt.o: ../../include/recipient_list.h
+smtp_tlsrpt.o: ../../include/resolve_clnt.h
+smtp_tlsrpt.o: ../../include/scache.h
+smtp_tlsrpt.o: ../../include/sock_addr.h
+smtp_tlsrpt.o: ../../include/string_list.h
+smtp_tlsrpt.o: ../../include/stringops.h
+smtp_tlsrpt.o: ../../include/sys_defs.h
+smtp_tlsrpt.o: ../../include/tls.h
+smtp_tlsrpt.o: ../../include/tls_proxy.h
+smtp_tlsrpt.o: ../../include/tlsrpt_wrapper.h
+smtp_tlsrpt.o: ../../include/tok822.h
+smtp_tlsrpt.o: ../../include/vbuf.h
+smtp_tlsrpt.o: ../../include/vstream.h
+smtp_tlsrpt.o: ../../include/vstring.h
+smtp_tlsrpt.o: smtp.h
+smtp_tlsrpt.o: smtp_tlsrpt.c
smtp_trouble.o: ../../include/argv.h
smtp_trouble.o: ../../include/attr.h
smtp_trouble.o: ../../include/bounce.h
diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c
index 385c81ff4..c41cf91ae 100644
--- a/postfix/src/smtp/lmtp_params.c
+++ b/postfix/src/smtp/lmtp_params.c
@@ -67,6 +67,7 @@
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
+ VAR_LMTP_TLSRPT_SOCKNAME, DEF_LMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
0,
};
static const CONFIG_TIME_TABLE lmtp_time_table[] = {
@@ -137,5 +138,6 @@
};
static const CONFIG_NBOOL_TABLE lmtp_nbool_table[] = {
VAR_LMTP_REQ_DEADLINE, DEF_LMTP_REQ_DEADLINE, &var_smtp_req_deadline,
+ VAR_LMTP_TLSRPT_ENABLE, DEF_LMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable,
0,
};
diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c
index 51b2e6dba..0c737cbd6 100644
--- a/postfix/src/smtp/smtp.c
+++ b/postfix/src/smtp/smtp.c
@@ -638,6 +638,12 @@
/* .IP "\fBsmtp_tls_enable_rpk (no)\fR"
/* Request that remote SMTP servers send an RFC7250 raw public key
/* instead of an X.509 certificate.
+/* .PP Available in Postfix version 3.10 and later:
+/* .IP "\fBsmtp_tlsrpt_enable (no)\fR"
+/* Enable support for RFC 8460 TLSRPT notifications.
+/* .IP "\fBsmtp_tlsrpt_socket_name (empty)\fR"
+/* The pathname of a UNIX-domain datagram socket that is managed
+/* by a local TLSRPT reporting service.
/* OBSOLETE STARTTLS CONTROLS
/* .ad
/* .fi
@@ -1146,6 +1152,8 @@ int var_smtp_min_data_rate;
char *var_use_srv_lookup;
bool var_ign_srv_lookup_err;
bool var_allow_srv_fallback;
+bool var_smtp_tlsrpt_enable;
+char *var_smtp_tlsrpt_sockname;
/* Special handling of 535 AUTH errors. */
char *var_smtp_sasl_auth_cache_name;
@@ -1575,6 +1583,21 @@ static void pre_init(char *unused_name, char **unused_argv)
mdalg = var_smtp_tls_fpt_dgst);
smtp_tls_list_init();
tls_dane_loglevel(VAR_LMTP_SMTP(TLS_LOGLEVEL), var_smtp_tls_loglevel);
+#ifdef USE_TLSRPT
+ if (var_smtp_tlsrpt_enable) {
+ if (smtp_mode) {
+ if (smtp_tlsrpt_pre_jail(VAR_SMTP_TLSRPT_SOCKNAME,
+ var_smtp_tlsrpt_sockname) < 0)
+ var_smtp_tlsrpt_enable = 0;
+ } else {
+ msg_warn("TLSRPT support is not implemented for LMTP");
+ var_smtp_tlsrpt_enable = 0;
+ }
+ }
+#else /* no USE_TLSRPT */
+ if (var_smtp_tlsrpt_enable)
+ msg_warn("TLSRPT is selected, but TLSRPT is not compiled in");
+#endif /* USE_TLSRPT */
#else
msg_warn("TLS has been selected, but TLS support is not compiled in");
#endif
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index 60c68f845..f367ea1e0 100644
--- a/postfix/src/smtp/smtp.h
+++ b/postfix/src/smtp/smtp.h
@@ -108,8 +108,28 @@ typedef struct SMTP_TLS_POLICY {
char *sni; /* Optional SNI name when not DANE */
int conn_reuse; /* enable connection reuse */
int enable_rpk; /* Enable server->client RPK */
+ /* External policy info, for TLSRPT. */
+ int ext_policy_ttl; /* TTL from DNS etc. */
+ char *ext_policy_type; /* (sts) */
+ ARGV *ext_policy_strings; /* policy strings from DNS etc. */
+ char *ext_policy_domain; /* policy scope */
+ ARGV *ext_mx_host_patterns; /* (sts) MX host patterns */
+ char *ext_policy_failure; /* (sts) policy failure */
} SMTP_TLS_POLICY;
+ /*
+ * Names and values for external policy attributes in smtp_tls_policy_maps.
+ * These are not #ifdef USE_TLSRPT, so that a TLSRPT-aware STS plugin can be
+ * used whether or not Postfix was built with TLSRPT support.
+ */
+#define EXT_POLICY_TTL "policy_ttl"
+#define EXT_POLICY_TTL_UNSET (-1)
+#define EXT_POLICY_TYPE "policy_type"
+#define EXT_POLICY_DOMAIN "policy_domain"
+#define EXT_POLICY_STRING "policy_string"
+#define EXT_MX_HOST_PATTERN "mx_host_pattern"
+#define EXT_POLICY_FAILURE "policy_failure"
+
/*
* smtp_tls_policy.c
*/
@@ -144,6 +164,12 @@ extern void smtp_tls_policy_cache_flush(void);
_tls_policy_init_tmp->sni = 0; \
_tls_policy_init_tmp->conn_reuse = 0; \
_tls_policy_init_tmp->enable_rpk = 0; \
+ _tls_policy_init_tmp->ext_policy_ttl = EXT_POLICY_TTL_UNSET; \
+ _tls_policy_init_tmp->ext_policy_type = 0; \
+ _tls_policy_init_tmp->ext_policy_domain = 0; \
+ _tls_policy_init_tmp->ext_policy_strings = 0; \
+ _tls_policy_init_tmp->ext_mx_host_patterns = 0; \
+ _tls_policy_init_tmp->ext_policy_failure = 0; \
} while (0)
#endif
@@ -171,6 +197,9 @@ typedef struct SMTP_STATE {
*/
#ifdef USE_TLS
SMTP_TLS_POLICY tls[1]; /* Usage: state->tls->member */
+#ifdef USE_TLSRPT
+ struct TLSRPT_WRAPPER *tlsrpt;
+#endif
#endif
/*
@@ -757,6 +786,18 @@ extern void smtp_quote_821_address(VSTRING *, const char *);
*/
extern int smtp_hfrom_format;
+ /*
+ * smtp_tlsrpt.c.
+ */
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+extern int smtp_tlsrpt_pre_jail(const char *sockname_pname, const char *sockname_pval);
+extern void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain);
+extern void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state);
+extern void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state);
+extern void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *, const char *ehlo_resp);
+
+#endif /* USE_TLSRPT && USE_TLS */
+
/* LICENSE
/* .ad
/* .fi
diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
index 68faca18e..df26d64ba 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -104,6 +104,7 @@
#include
#include
#include
+#include
/* DNS library. */
@@ -911,6 +912,21 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state);
+ /*
+ * TODO(wietse) If the domain publishes a TLSRPT policy, they expect
+ * that clients use SMTP over TLS. Should we upgrade a TLS security
+ * level of "may" to "encrypt"? This would disable falling back to
+ * plaintext, and could break interoperability with receivers that
+ * crank up security up to 11.
+ */
+#ifdef USE_TLSRPT
+ if (smtp_mode && var_smtp_tlsrpt_enable
+ && !valid_hostaddr(domain, DONT_GRIPE))
+ smtp_tlsrpt_create_wrapper(state, domain);
+ else
+ state->tlsrpt = 0;
+#endif /* USE_TLSRPT */
+
/*
* Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a
* quoted domain is specified or when DNS lookups are disabled.
@@ -1076,6 +1092,12 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
session->state = state;
#ifdef USE_TLS
session->tls_nexthop = domain;
+#ifdef USE_TLSRPT
+ if (state->tlsrpt && state->tls->level > TLS_LEV_NONE) {
+ smtp_tlsrpt_set_tls_policy(state);
+ smtp_tlsrpt_set_tcp_connection(state);
+ }
+#endif /* USE_TLSRPT */
#endif
if (addr->pref == domain_best_pref)
session->features |= SMTP_FEATURE_BEST_MX;
diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c
index cebff9380..f58f9eb34 100644
--- a/postfix/src/smtp/smtp_params.c
+++ b/postfix/src/smtp/smtp_params.c
@@ -68,6 +68,7 @@
VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
+ VAR_SMTP_TLSRPT_SOCKNAME, DEF_SMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
0,
};
static const CONFIG_TIME_TABLE smtp_time_table[] = {
@@ -141,5 +142,6 @@
};
static const CONFIG_NBOOL_TABLE smtp_nbool_table[] = {
VAR_SMTP_REQ_DEADLINE, DEF_SMTP_REQ_DEADLINE, &var_smtp_req_deadline,
+ VAR_SMTP_TLSRPT_ENABLE, DEF_SMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable,
0,
};
diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c
index e022bc2cf..bdca86ce8 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -155,6 +155,9 @@
#include
#include
#include
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+#include
+#endif
/* Application-specific. */
@@ -475,6 +478,11 @@ int smtp_helo(SMTP_STATE *state)
else
session->features &= ~SMTP_FEATURE_ESMTP;
}
+#ifdef USE_TLSRPT
+ if (state->tlsrpt
+ && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0)
+ trw_set_ehlo_resp(state->tlsrpt, resp->str);
+#endif
}
if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
where = "performing the HELO handshake";
@@ -484,6 +492,10 @@ int smtp_helo(SMTP_STATE *state)
"host %s refused to talk to me: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
+#ifdef USE_TLSRPT
+ if (state->tlsrpt)
+ trw_set_ehlo_resp(state->tlsrpt, resp->str);
+#endif
}
} else {
where = "performing the LHLO handshake";
@@ -798,11 +810,19 @@ int smtp_helo(SMTP_STATE *state)
* although support for it was announced in the EHLO response.
*/
session->features &= ~SMTP_FEATURE_STARTTLS;
- if (TLS_REQUIRED(state->tls->level))
+ if (TLS_REQUIRED(state->tls->level)) {
+#ifdef USE_TLSRPT
+ if (state->tlsrpt)
+ trw_report_failure(state->tlsrpt,
+ TLSRPT_STARTTLS_NOT_SUPPORTED,
+ /* additional_detail= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+#endif
return (smtp_site_fail(state, STR(iter->host), resp,
"TLS is required, but host %s refused to start TLS: %s",
session->namaddr,
translit(resp->str, "\n", " ")));
+ }
/* Else try to continue in plain-text mode. */
}
@@ -815,6 +835,13 @@ int smtp_helo(SMTP_STATE *state)
*/
if (TLS_REQUIRED(state->tls->level)) {
if (!(session->features & SMTP_FEATURE_STARTTLS)) {
+#ifdef USE_TLSRPT
+ if (state->tlsrpt)
+ trw_report_failure(state->tlsrpt,
+ TLSRPT_STARTTLS_NOT_SUPPORTED,
+ /* additional_detail= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+#endif
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.4"),
"TLS is required, but was not offered by host %s",
@@ -942,6 +969,12 @@ static int smtp_start_tls(SMTP_STATE *state)
= vstring_str(state->tls->exclusions),
matchargv = state->tls->matchargv,
mdalg = var_smtp_tls_fpt_dgst,
+#ifdef USE_TLSRPT
+ tlsrpt = state->tlsrpt,
+#else
+ tlsrpt = 0,
+#endif
+ fail_type = 0,
dane = state->tls->dane);
/*
@@ -1065,6 +1098,12 @@ static int smtp_start_tls(SMTP_STATE *state)
= vstring_str(state->tls->exclusions),
matchargv = state->tls->matchargv,
mdalg = var_smtp_tls_fpt_dgst,
+#ifdef USE_TLSRPT
+ tlsrpt = state->tlsrpt,
+#else
+ tlsrpt = 0,
+#endif
+ fail_type = state->tls->ext_policy_failure,
dane = state->tls->dane);
/*
@@ -1125,10 +1164,42 @@ static int smtp_start_tls(SMTP_STATE *state)
* we must check that here, and not state->tls->level.
*/
if (TLS_MUST_MATCH(session->tls_context->level))
- if (!TLS_CERT_IS_MATCHED(session->tls_context))
+ if (!TLS_CERT_IS_MATCHED(session->tls_context)) {
+#ifdef USE_TLSRPT
+
+ /*
+ * Don't create a TLSRPT failure report here, if the TLS engine
+ * already reported a more specific reason.
+ */
+ if (state->tlsrpt && session->tls_context->rpt_reported == 0) {
+ if (!TLS_CERT_IS_TRUSTED(session->tls_context)) {
+ (void) trw_report_failure(state->tlsrpt,
+ TLSRPT_CERTIFICATE_NOT_TRUSTED,
+ /* additional_detail= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+ } else {
+ (void) trw_report_failure(state->tlsrpt,
+ TLSRPT_CERTIFICATE_HOST_MISMATCH,
+ /* additional_detail= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+ }
+ }
+#endif
return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "4.7.5"),
"Server certificate not verified"));
+ }
+
+ /*
+ * Create a TLSRPT success report only if the TLS engine has not reported
+ * a failure (For example, the TLS handshake may be successful, but the
+ * security level was downgraded from opportunistic or half "dane" to
+ * "encrypt").
+ */
+#ifdef USE_TLSRPT
+ if (state->tlsrpt && session->tls_context->rpt_reported == 0)
+ (void) trw_report_success(state->tlsrpt);
+#endif
/*
* At this point we have to re-negotiate the "EHLO" to reget the
diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c
index 6b81fa4ed..c2df5a5d5 100644
--- a/postfix/src/smtp/smtp_state.c
+++ b/postfix/src/smtp/smtp_state.c
@@ -50,6 +50,13 @@
#include
#include
+ /*
+ * TLS library.
+ */
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+#include
+#endif
+
/* Application-specific. */
#include "smtp.h"
@@ -73,6 +80,9 @@ SMTP_STATE *smtp_state_alloc(void)
state->iterator->host = vstring_alloc(100);
state->iterator->addr = vstring_alloc(100);
state->iterator->saved_dest = vstring_alloc(100);
+#ifdef TLSRPT
+ state->tlsrpt = 0;
+#endif
if (var_smtp_cache_conn) {
state->dest_label = vstring_alloc(10);
state->dest_prop = vstring_alloc(10);
@@ -105,6 +115,10 @@ void smtp_state_free(SMTP_STATE *state)
vstring_free(state->iterator->host);
vstring_free(state->iterator->addr);
vstring_free(state->iterator->saved_dest);
+#ifdef USE_TLSRPT
+ if (state->tlsrpt)
+ trw_free(state->tlsrpt);
+#endif
if (state->dest_label)
vstring_free(state->dest_label);
if (state->dest_prop)
diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c
index f407d6579..9640f27a4 100644
--- a/postfix/src/smtp/smtp_tls_policy.c
+++ b/postfix/src/smtp/smtp_tls_policy.c
@@ -102,6 +102,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -113,6 +114,10 @@
#include
#include
+/* TLS library. */
+
+#include
+
/* DNS library. */
#include
@@ -221,15 +226,21 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
{
const char *lookup;
char *policy;
- char *saved_policy;
+ char *saved_policy = 0;
char *tok;
- const char *err;
char *name;
char *val;
static VSTRING *cbuf;
+ char *free_me = 0;
#undef FREE_RETURN
-#define FREE_RETURN do { myfree(saved_policy); return; } while (0)
+#define FREE_RETURN do { \
+ if (saved_policy) \
+ myfree(saved_policy); \
+ if (free_me) \
+ myfree(free_me); \
+ return; \
+ } while (0)
#define INVALID_RETURN(why, levelp) do { \
MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0)
@@ -250,7 +261,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
saved_policy = policy = mystrdup(lookup);
- if ((tok = mystrtok(&policy, CHARS_COMMA_SP)) == 0) {
+ if ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) == 0) {
msg_warn("%s: invalid empty policy", WHERE);
INVALID_RETURN(tls->why, site_level);
}
@@ -265,7 +276,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
* Warn about ignored attributes when TLS is disabled.
*/
if (*site_level < TLS_LEV_MAY) {
- while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0)
+ while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0)
msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
WHERE, tok);
FREE_RETURN;
@@ -275,8 +286,12 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
* Errors in attributes may have security consequences, don't ignore
* errors that can degrade security.
*/
- while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0) {
- if ((err = split_nameval(tok, &name, &val)) != 0) {
+ while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
+ const char *err;
+
+ if ((tok[0] == CHARS_BRACE[0]
+ && (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0)
+ || (err = split_nameval(tok, &name, &val)) != 0) {
msg_warn("%s: malformed attribute/value pair \"%s\": %s",
WHERE, tok, err);
INVALID_RETURN(tls->why, site_level);
@@ -391,6 +406,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
continue;
}
+ /* Last one wins. */
if (!strcasecmp(name, "enable_rpk")) {
/* Ultimately ignored at some security levels */
if (strcasecmp(val, "yes") == 0) {
@@ -404,10 +420,96 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
}
continue;
}
+ /* Only one instance per policy. */
+ if (!strcasecmp(name, EXT_POLICY_TTL)) {
+ char *end;
+ long lval;
+
+ if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET) {
+ msg_warn("%s: attribute \"%s\" is specified multiple times",
+ WHERE, name);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ if (!alldig(val) || ((lval = sane_strtol(val, &end, 10)),
+ ((tls->ext_policy_ttl = lval) != lval))
+ || *end != 0) {
+ msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
+ WHERE, name, val);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ continue;
+ }
+ /* Only one instance per policy. */
+ if (!strcasecmp(name, EXT_POLICY_TYPE)) {
+ if (tls->ext_policy_type) {
+ msg_warn("%s: attribute \"%s\" is specified multiple times",
+ WHERE, name);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ if (!valid_tlsrpt_policy_type(val)) {
+ msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
+ WHERE, name, val);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ tls->ext_policy_type = mystrdup(val);
+ continue;
+ }
+ if (!strcasecmp(name, EXT_POLICY_DOMAIN)) {
+ if (tls->ext_policy_domain) {
+ msg_warn("%s: attribute \"%s\" is specified multiple times",
+ WHERE, name);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ if (!valid_hostname(val, DO_GRIPE)) {
+ msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
+ WHERE, name, val);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ tls->ext_policy_domain = mystrdup(val);
+ continue;
+ }
+ /* Multiple instances per policy are allowed. */
+ if (!strcasecmp(name, EXT_POLICY_STRING)) {
+ if (tls->ext_policy_strings == 0)
+ tls->ext_policy_strings = argv_alloc(1);
+ argv_add(tls->ext_policy_strings, val, (char *) 0);
+ continue;
+ }
+ /* Multiple instances per policy are allowed. */
+ if (!strcasecmp(name, EXT_MX_HOST_PATTERN)) {
+ if (tls->ext_mx_host_patterns == 0)
+ tls->ext_mx_host_patterns = argv_alloc(1);
+ argv_add(tls->ext_mx_host_patterns, val, (char *) 0);
+ continue;
+ }
+ /* Only one instance per policy. */
+ if (!strcasecmp(name, EXT_POLICY_FAILURE)) {
+ if (tls->ext_policy_failure != 0) {
+ msg_warn("%s: attribute \"%s\" is specified multiple times",
+ WHERE, name);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ if (!valid_tlsrpt_policy_failure(val)) {
+ msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
+ WHERE, name, val);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ tls->ext_policy_failure = mystrdup(val);
+ continue;
+ }
msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
INVALID_RETURN(tls->why, site_level);
}
-
+ if (tls->ext_policy_type == 0) {
+ if (tls->ext_policy_ttl || tls->ext_policy_strings
+ || tls->ext_policy_domain || tls->ext_mx_host_patterns
+ || tls->ext_policy_failure) {
+ msg_warn("%s: built-in policy has unexpected attribute "
+ "policy_ttl, policy_domain, policy_string, "
+ "mx_host_pattern or policy_failure", WHERE);
+ INVALID_RETURN(tls->why, site_level);
+ }
+ }
FREE_RETURN;
}
@@ -707,6 +809,16 @@ static void policy_delete(void *item, void *unused_context)
if (tls->dane)
tls_dane_free(tls->dane);
dsb_free(tls->why);
+ if (tls->ext_policy_type)
+ myfree(tls->ext_policy_type);
+ if (tls->ext_policy_domain)
+ myfree(tls->ext_policy_domain);
+ if (tls->ext_policy_strings)
+ argv_free(tls->ext_policy_strings);
+ if (tls->ext_mx_host_patterns)
+ argv_free(tls->ext_mx_host_patterns);
+ if (tls->ext_policy_failure)
+ myfree(tls->ext_policy_failure);
myfree((void *) tls);
}
diff --git a/postfix/src/smtp/smtp_tlsrpt.c b/postfix/src/smtp/smtp_tlsrpt.c
new file mode 100644
index 000000000..b0a6af626
--- /dev/null
+++ b/postfix/src/smtp/smtp_tlsrpt.c
@@ -0,0 +1,421 @@
+/*++
+/* NAME
+/* smtp_tlsrpt 3
+/* SUMMARY
+/* TLSRPT support for the SMTP protocol engine
+/* SYNOPSIS
+/* #include
+/*
+/* int smtp_tlsrpt_pre_jail(
+/* const char *sockname_pname,
+/* const char *sockname_pval)
+/*
+/* void smtp_tlsrpt_create_wrapper(
+/* SMTP_STATE *state,
+/* const char *domain)
+/*
+/* void smtp_tlsrpt_set_tls_policy(
+/* SMTP_STATE *state)
+/*
+/* void smtp_tlsrpt_set_tcp_connection(
+/* SMTP_STATE *state)
+/*
+/* void smtp_tlsrpt_set_ehlo_resp(
+/* SMTP_STATE *state,
+/* const char *ehlo_resp)
+/* DESCRIPTION
+/* This module collects TLSRPT policy information and selected SMTP
+/* protocol engine state in a TLSRPT_WRAPPER object. This will be
+/* passed to (possibly remote) TLS protocol engine, so that it
+/* can report a TLS error to a TLSRPT library. The SMTP protocol
+/* engine uses the information to report a TLS error or success.
+/*
+/* smtp_tls_pre_jail() does configuration sanity checks and
+/* returns 0 if successful, i.e. TLSRPT support is properly
+/* configured. Otherwise it returns -1 and logs a warning. Arguments:
+/* .IP sockname_pname
+/* The name of a configuration parameter for the endpoint that is
+/* managed by TLSRPT infrastructure. This is used in a diagnostic
+/* message.
+/* .IP sockname_pval
+/* The value of said parameter.
+/* .PP
+/* smtp_tlsrpt_create_wrapper() destroys any TLSRPT_WRAPPER
+/* referenced by state->tlsrpt, and looks for a TLSRPT
+/* policy for the specified domain. If one policy exists,
+/* smtp_tlsrpt_create_wrapper() attaches a TLSRPT_WRAPPER instance
+/* to state->tlsrpt. Otherwise, state->tlsrpt will be null, and
+/* other smtp_tlsrpt_* calls must not be made. The TLSRPT_WRAPPER
+/* instance may be reused for different SMTP connections with the
+/* same TLSRPT policy domain. Arguments:
+/* .IP domain
+/* The name of a domain that may publish a TLSRPT policy. An
+/* internationalized domain name may be in U-label or A-label form
+/* (it will be converted to A-label internally).
+/* .PP
+/* smtp_tlsrpt_set_tls_policy() updates the TLSRPT_WRAPPER with
+/* DANE or STS TLS policy information, and clears information
+/* that was added with smtp_tlsrpt_set_tcp_connection() or
+/* smtp_tlsrpt_set_ehlo_resp().
+/* .PP
+/* smtp_tlsrpt_set_tcp_connection() updates the TLSRPT_WRAPPER with
+/* TCP connection properties.
+/* .PP
+/* smtp_tlsrpt_set_ehlo_resp() updates the TLSRPT_WRAPPER with the
+/* SMTP server's EHLO response.
+/* BUGS
+/* This module inherits all limitations from tlsrpt_wrapper(3).
+/* SEE ALSO
+/* tlsrpt_wrapper(3) TLSRPT support for the TLS protocol engine.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+ /*
+ * System library.
+ */
+#include
+#include
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+ /*
+ * Global library.
+ */
+#include
+
+ /*
+ * TLS library.
+ */
+#include
+#include
+
+ /*
+ * Application-specific.
+ */
+#include
+
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+
+static const char smtp_tlsrpt_support[] = "TLSRPT support";
+
+/* smtp_tlsrpt_pre_jail - pre-jail configuration sanity check */
+
+int smtp_tlsrpt_pre_jail(const char *sockname_pname,
+ const char *sockname_pval)
+{
+ if (smtp_dns_support == SMTP_DNS_DISABLED) {
+ msg_warn("Cannot enable TLRPT support: DNS is disabled");
+ return (-1);
+ }
+ if (*sockname_pval == 0) {
+ msg_warn("%s: parameter %s has empty value -- %s will be disabled",
+ smtp_tlsrpt_support, sockname_pname, smtp_tlsrpt_support);
+ return (-1);
+ }
+ return (0);
+}
+
+/* smtp_tlsrpt_find_policy - look up TLSRPT policy and verify version ID */
+
+static DNS_RR *smtp_tlsrpt_find_policy(const char *adomain)
+{
+ VSTRING *why = vstring_alloc(100);
+ VSTRING *qname = vstring_alloc(100);
+ DNS_RR *rr_list = 0;
+ DNS_RR *rr_result = 0;
+ DNS_RR *rr;
+ DNS_RR *next;
+ int res_opt = 0;
+ int dns_status;
+
+ /*
+ * Preliminaries.
+ */
+ if (smtp_dns_support == SMTP_DNS_DNSSEC)
+ res_opt |= RES_USE_DNSSEC;
+
+ /*
+ * Lexical features: As specified in RFC 8460, a TLSRPT policy record
+ * must start with a version field ("v=TLSRPTv1") followed by *WSP;*WSP
+ * and at least one other field (we must not assume that the second field
+ * will be "rua"). We leave further validation to the TLSRPT library,
+ * where it belongs.
+ */
+#define TLSRPTv1_MAGIC "v=TLSRPTv1"
+#define TLSRPTv1_MAGIC_LEN (sizeof(TLSRPTv1_MAGIC) - 1)
+#define RFC5234_WSP " \t"
+
+ /*
+ * Look up TXT records. Ignore records that don't start with the expected
+ * version ID, and require that there is exactly one such DNS record.
+ */
+ vstring_sprintf(qname, "_smtp._tls_.%s", adomain);
+ dns_status = dns_lookup(STR(qname), T_TXT, res_opt, &rr_list,
+ (VSTRING *) 0, why);
+ vstring_free(qname);
+ if (dns_status != DNS_OK) {
+ switch (dns_status) {
+ case DNS_NOTFOUND:
+ case DNS_POLICY:
+ /* Expected results. */
+ break;
+ default:
+ /* Unexpected results. */
+ msg_warn("%s: policy lookup failed for %s: %s",
+ smtp_tlsrpt_support, adomain, STR(why));
+ }
+ } else {
+ for (rr = rr_list; rr; rr = next) {
+ char *cp;
+
+ next = rr->next;
+ if (strncmp(rr->data, TLSRPTv1_MAGIC, TLSRPTv1_MAGIC_LEN) != 0)
+ /* Ignore non-TLSRPT info. */
+ continue;
+ cp = rr->data + TLSRPTv1_MAGIC_LEN;
+
+ /*
+ * Should the TLSRPT library validate the entire policy for us?
+ */
+ if (cp[strspn(cp, RFC5234_WSP)] != ';') {
+ msg_warn("%s: ignoring malformed policy for %s:, \"%s\"",
+ smtp_tlsrpt_support, adomain, rr->data);
+ continue;
+ }
+ if (rr_result) {
+ msg_warn("%s: Too many policies for %s",
+ smtp_tlsrpt_support, adomain);
+ dns_rr_free(rr_result);
+ break;
+ }
+ rr_result = rr;
+ rr_list = dns_rr_detach(rr_list, rr);
+ }
+ }
+ vstring_free(why);
+ if (rr_list)
+ dns_rr_free(rr_list);
+ return (rr_result);
+}
+
+/* smtp_tlsrpt_create_wrapper - look up policy and attach TLSRPT_WRAPPER */
+
+void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain)
+{
+ const char *adomain;
+ DNS_RR *rr;
+
+ /*
+ * TODO(wietse): document in a suitable place that state->tlsrpt exists
+ * only if the next-hop domain announces a TLSRPT policy.
+ */
+ if (state->tlsrpt) {
+ trw_free(state->tlsrpt);
+ state->tlsrpt = 0;
+ }
+
+ /*
+ * IDNA support. An internationalized domain name must be in A-label form
+ * 1) for TLSRPT summaries and 2) for DNS lookups. The A-label lookup
+ * result comes from a limited-size in-process cache, so it does not
+ * matter that the SMTP client requests the same mapping later.
+ */
+#ifndef NO_EAI
+ if (!allascii(domain) && (adomain = midna_domain_to_ascii(domain)) != 0) {
+ if (msg_verbose)
+ msg_info("%s: internationalized domain %s asciified to %s",
+ smtp_tlsrpt_support, domain, adomain);
+ } else
+#endif
+ adomain = domain;
+
+ if ((rr = smtp_tlsrpt_find_policy(adomain)) != 0) {
+ if (msg_verbose)
+ msg_info("%s: domain %s has policy %.100s",
+ smtp_tlsrpt_support, domain, rr->data);
+ state->tlsrpt = trw_create(
+ /* rpt_socket_name= */ var_smtp_tlsrpt_sockname,
+ /* rpt_policy_domain= */ adomain,
+ /* rpt_policy_string= */ rr->data);
+ dns_rr_free(rr);
+ } else {
+ if (msg_verbose)
+ msg_info("%s: no policy for domain %s",
+ smtp_tlsrpt_support, domain);
+ }
+}
+
+/* smtp_tlsrpt_set_dane_policy - add DANE policy properties */
+
+static void smtp_tlsrpt_set_dane_policy(SMTP_STATE *state)
+{
+ VSTRING *buf = vstring_alloc(200);
+ ARGV *argv = argv_alloc(10);
+ TLS_DANE *dane = state->tls->dane;
+ TLS_TLSA *tlsa;
+
+ for (tlsa = dane->tlsa; tlsa != 0; tlsa = tlsa->next) {
+ vstring_sprintf(buf, "%d %d %d ", tlsa->usage,
+ tlsa->selector, tlsa->mtype);
+ hex_encode_opt(buf, (char *) tlsa->data, tlsa->length,
+ HEX_ENCODE_FLAG_APPEND);
+ argv_add(argv, STR(buf), (char *) 0);
+ }
+ trw_set_tls_policy(state->tlsrpt, TLSRPT_POLICY_TLSA,
+ (const char *const *) argv->argv, dane->base_domain,
+ /* mx_host_patterns= */ (const char *const *) 0);
+ argv_free(argv);
+ vstring_free(buf);
+}
+
+/* smtp_tlsrpt_set_ext_policy - add external policy from smtp_tls_policy_maps */
+
+static void smtp_tlsrpt_set_ext_policy(SMTP_STATE *state)
+{
+ SMTP_TLS_POLICY *tls = state->tls;
+ tlsrpt_policy_type_t policy_type_val;
+
+ switch (policy_type_val = convert_tlsrpt_policy_type(tls->ext_policy_type)) {
+ case TLSRPT_POLICY_STS:
+ trw_set_tls_policy(state->tlsrpt, policy_type_val,
+ (const char *const *) tls->ext_policy_strings->argv,
+ tls->ext_policy_domain,
+ (const char *const *) tls->ext_mx_host_patterns->argv);
+ break;
+ case TLSRPT_NO_POLICY_FOUND:
+ trw_set_tls_policy(state->tlsrpt, policy_type_val,
+ /* tls_policy_strings= */ (const char *const *) 0,
+ /* tls_policy_domain= */ (const char *) 0,
+ /* mx_host_patterns= */ (const char *const *) 0);
+ break;
+ default:
+ msg_panic("unexpected policy type: \"%s\"",
+ tls->ext_policy_type);
+ }
+
+ /*
+ * TODO(wietse) propagate tls->policy_failure to force policy enforcement
+ * to fail with the indicated error, and prevent a false positive match
+ * when a certificate would satisfy conventional PKI constraints.
+ */
+}
+
+/* smtp_tlsrpt_set_tls_policy - set built-in or external policy */
+
+void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state)
+{
+ SMTP_TLS_POLICY *tls = state->tls;
+
+ if (TLS_DANE_BASED(tls->level)) { /* Desired by local policy */
+ if (tls->dane != 0) /* Actual policy */
+ smtp_tlsrpt_set_dane_policy(state);
+ } else if (tls->ext_policy_type) {
+ smtp_tlsrpt_set_ext_policy(state);
+ }
+}
+
+/* sane_sockaddr_to_hostaddr - TODO(wietse) move to library */
+
+#include
+
+static const INET_PROTO_INFO *proto_info;
+
+static int sane_sockaddr_to_hostaddr(struct sockaddr *addr_storage,
+ SOCKADDR_SIZE addr_storage_len,
+ MAI_HOSTADDR_STR *addr_buf,
+ MAI_SERVPORT_STR *port_buf,
+ int socktype)
+{
+ int aierr;
+
+ if (proto_info == 0)
+ proto_info = inet_proto_info();
+
+ if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len,
+ addr_buf, port_buf, socktype)) == 0
+ && strncasecmp("::ffff:", addr_buf->buf, 7) == 0
+ && strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
+ memmove(addr_buf->buf, addr_buf->buf + 7,
+ sizeof(addr_buf->buf) - 7);
+ return (aierr);
+}
+
+/* smtp_tlsrpt_set_tcp_connection - set TCP connection info from SMTP_STATE */
+
+void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state)
+{
+ SMTP_ITERATOR *iter = state->iterator;
+ SMTP_SESSION *session = state->session;
+ MAI_HOSTADDR_STR client_addr;
+ struct sockaddr_storage addr_storage;
+ SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage);
+ int aierr;
+
+ /*
+ * Get the IP client address string. The Postfix SMTP_ITERATOR already
+ * contains strings with other connection information.
+ */
+ if (getsockname(vstream_fileno(session->stream),
+ (struct sockaddr *) &addr_storage,
+ &addr_storage_len) < 0) {
+ msg_warn("%s: getsockname() failed (%m)"
+ " skipping the ignoring client-side IP address",
+ smtp_tlsrpt_support);
+ client_addr.buf[0] = 0;
+ } else if ((aierr = sane_sockaddr_to_hostaddr(
+ (struct sockaddr *) &addr_storage,
+ addr_storage_len, &client_addr,
+ (MAI_SERVPORT_STR *) 0,
+ SOCK_STREAM)) != 0) {
+ msg_warn("%s: cannot convert IP address to string (%s)"
+ " -- skipping the client-side IP address",
+ smtp_tlsrpt_support, MAI_STRERROR(aierr));
+ client_addr.buf[0] = 0;
+ }
+ trw_set_tcp_connection(state->tlsrpt, client_addr.buf, STR(iter->host),
+ STR(iter->addr));
+}
+
+/* smtp_tlsrpt_set_ehlo_resp - format and set EHLO response */
+
+void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *state, const char *reply)
+{
+ ARGV *argv;
+ VSTRING *buf;
+ char **cpp;
+
+ /*
+ * Generate SMTP-style line breaks ("\r\n") for a multiline response.
+ * Internally, smtp_chat_resp() returns a multiline response as text
+ * separated with "\n". This is because Postfix by design removes
+ * protocol-specific line endings on input, uses its own internal form to
+ * represent text lines, and generates protocol-specific line endings on
+ * output. The conversion to "\r\n" below is such an output conversion.
+ */
+ buf = vstring_alloc(100);
+ argv = argv_split(reply, "\n");
+ for (cpp = argv->argv; *cpp; cpp++) {
+ vstring_strcat(buf, *cpp);
+ if (cpp[1])
+ vstring_strcat(buf, "\r\n");
+ }
+ argv_free(argv);
+ trw_set_ehlo_resp(state->tlsrpt, STR(buf));
+ vstring_free(buf);
+}
+
+#endif /* USE_TLSRPT && USE_TLS */
diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in
index 4b562b3fb..3d3bf6e29 100644
--- a/postfix/src/tls/Makefile.in
+++ b/postfix/src/tls/Makefile.in
@@ -9,7 +9,7 @@ SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
tls_proxy_server_init_print.c tls_proxy_server_init_scan.c \
tls_proxy_client_start_print.c tls_proxy_client_start_scan.c \
tls_proxy_server_start_print.c tls_proxy_server_start_scan.c \
- tls_proxy_client_misc.c
+ tls_proxy_client_misc.c tlsrpt_wrapper.c
OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
tls_verify.o tls_dane.o tls_certkey.o tls_session.o \
@@ -18,8 +18,8 @@ OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
tls_proxy_clnt.o tls_proxy_context_print.o tls_proxy_context_scan.o \
tls_proxy_client_print.o tls_proxy_client_scan.o \
tls_proxy_server_print.o tls_proxy_server_scan.o \
- tls_proxy_client_misc.o
-HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h
+ tls_proxy_client_misc.o tlsrpt_wrapper.o
+HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h tlsrpt_wrapper.h
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -182,6 +182,7 @@ tls_client.o: tls.h
tls_client.o: tls_client.c
tls_client.o: tls_mgr.h
tls_client.o: tls_scache.h
+tls_client.o: tlsrpt_wrapper.h
tls_dane.o: ../../include/argv.h
tls_dane.o: ../../include/check_arg.h
tls_dane.o: ../../include/ctable.h
@@ -365,6 +366,7 @@ tls_proxy_client_print.o: ../../include/vstring.h
tls_proxy_client_print.o: tls.h
tls_proxy_client_print.o: tls_proxy.h
tls_proxy_client_print.o: tls_proxy_client_print.c
+tls_proxy_client_print.o: tlsrpt_wrapper.h
tls_proxy_client_scan.o: ../../include/argv.h
tls_proxy_client_scan.o: ../../include/argv_attr.h
tls_proxy_client_scan.o: ../../include/attr.h
@@ -386,6 +388,7 @@ tls_proxy_client_scan.o: ../../include/vstring.h
tls_proxy_client_scan.o: tls.h
tls_proxy_client_scan.o: tls_proxy.h
tls_proxy_client_scan.o: tls_proxy_client_scan.c
+tls_proxy_client_scan.o: tlsrpt_wrapper.h
tls_proxy_clnt.o: ../../include/argv.h
tls_proxy_clnt.o: ../../include/attr.h
tls_proxy_clnt.o: ../../include/check_arg.h
@@ -587,3 +590,15 @@ tls_verify.o: ../../include/vstream.h
tls_verify.o: ../../include/vstring.h
tls_verify.o: tls.h
tls_verify.o: tls_verify.c
+tls_verify.o: tlsrpt_wrapper.h
+tlsrpt_wrapper.o: ../../include/argv.h
+tlsrpt_wrapper.o: ../../include/check_arg.h
+tlsrpt_wrapper.o: ../../include/msg.h
+tlsrpt_wrapper.o: ../../include/mymalloc.h
+tlsrpt_wrapper.o: ../../include/name_code.h
+tlsrpt_wrapper.o: ../../include/stringops.h
+tlsrpt_wrapper.o: ../../include/sys_defs.h
+tlsrpt_wrapper.o: ../../include/vbuf.h
+tlsrpt_wrapper.o: ../../include/vstring.h
+tlsrpt_wrapper.o: tlsrpt_wrapper.c
+tlsrpt_wrapper.o: tlsrpt_wrapper.h
diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h
index 3ec41ba36..b3e5b7cb2 100644
--- a/postfix/src/tls/tls.h
+++ b/postfix/src/tls/tls.h
@@ -205,7 +205,7 @@ extern TLS_DANE *tls_dane_alloc(void);
extern void tls_tlsa_free(TLS_TLSA *);
extern void tls_dane_free(TLS_DANE *);
extern void tls_dane_add_fpt_digests(TLS_DANE *, int, const char *,
- const char *, int);
+ const char *, int);
extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int);
extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
@@ -261,6 +261,8 @@ typedef struct {
int errordepth; /* Chain depth of error cert */
int errorcode; /* First error at error depth */
int must_fail; /* Failed to load trust settings */
+ int rpt_reported; /* Failure was reported with TLSRPT */
+ char *fail_type; /* Doomed by policy */
} TLS_SESS_STATE;
/*
@@ -493,6 +495,8 @@ typedef struct {
const ARGV *matchargv; /* Cert match patterns */
const char *mdalg; /* default message digest algorithm */
const TLS_DANE *dane; /* DANE TLSA verification */
+ struct TLSRPT_WRAPPER *tlsrpt; /* RFC 8460 reporting */
+ char *fail_type; /* verification must fail by policy */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@@ -516,12 +520,13 @@ extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *,
a6, a7, a8, a9, a10, a11, a12, a13, a14))
#define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
- a10, a11, a12, a13, a14, a15, a16, a17, a18) \
+ a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) \
tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
- ((props)->a16), ((props)->a17), ((props)->a18), (props)))
+ ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \
+ ((props)->a20), (props)))
/*
* tls_server.c
@@ -654,7 +659,7 @@ extern void tls_auto_groups(SSL_CTX *, const char *, const char *);
extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
-extern void tls_log_verify_error(TLS_SESS_STATE *);
+extern void tls_log_verify_error(TLS_SESS_STATE *, struct TLSRPT_WRAPPER *);
/*
* tls_dane.c
diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c
index 3eda859cc..7834ef8ae 100644
--- a/postfix/src/tls/tls_client.c
+++ b/postfix/src/tls/tls_client.c
@@ -160,6 +160,7 @@
#ifdef USE_TLS
#include
+#include
#ifdef STRCASECMP_IN_STRINGS_H
#include
@@ -363,11 +364,12 @@ static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert,
if (!TLS_CERT_IS_MATCHED(TLScontext)
&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
if (TLScontext->session_reused == 0)
- tls_log_verify_error(TLScontext);
+ tls_log_verify_error(TLScontext, props->tlsrpt);
else
msg_info("%s: re-using session with untrusted peer credential, "
"look for details earlier in the log", props->namaddr);
}
+ /* TODO(wietse) In the non-reuse case, tlsrprt the root cause? */
}
/* verify_rpk - process RFC7250 raw public key verification status */
@@ -407,11 +409,12 @@ static void verify_rpk(TLS_SESS_STATE *TLScontext, EVP_PKEY *peerpkey,
if (!TLS_CERT_IS_MATCHED(TLScontext)
&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
if (TLScontext->session_reused == 0)
- tls_log_verify_error(TLScontext);
+ tls_log_verify_error(TLScontext, props->tlsrpt);
else
msg_info("%s: re-using session with untrusted certificate, "
"look for details earlier in the log", props->namaddr);
}
+ /* TODO(wietse) In the non-reuse case, tlsrprt the root cause? */
}
/* add_namechecks - tell OpenSSL what names to check */
@@ -579,6 +582,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
* therefore valid for use with SNI.
*/
if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
+ /* TLSRPT: Local resource error, don't report. */
msg_warn("%s: error enabling DANE-based certificate validation",
TLScontext->namaddr);
tls_print_errors();
@@ -595,6 +599,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
case TLS_LEV_FPRINT:
/* Synthetic DANE for fingerprint security */
if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
+ /* TLSRPT: Local resource error, don't report. */
msg_warn("%s: error enabling fingerprint certificate validation",
props->namaddr);
tls_print_errors();
@@ -608,6 +613,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
if (TLScontext->dane != 0 && TLScontext->dane->tlsa != 0) {
/* Synthetic DANE for per-destination trust-anchors */
if (SSL_dane_enable(TLScontext->con, NULL) <= 0) {
+ /* TLSRPT: Local resource error, don't report. */
msg_warn("%s: error configuring local trust anchors",
props->namaddr);
tls_print_errors();
@@ -622,6 +628,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
if (sni) {
if (strlen(sni) > TLSEXT_MAXLEN_host_name) {
+ /* TLSRPT: Local configuration error, don't report. */
msg_warn("%s: ignoring too long SNI hostname: %.100s",
props->namaddr, sni);
return (0);
@@ -633,6 +640,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
* failed to send the SNI name, we have little choice but to abort.
*/
if (!SSL_set_tlsext_host_name(TLScontext->con, sni)) {
+ /* TLSRPT: Local resource or configuration error, don't report. */
msg_warn("%s: error setting SNI hostname to: %s", props->namaddr,
sni);
return (0);
@@ -975,6 +983,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
*/
protomask = tls_proto_mask_lims(props->protocols, &min_proto, &max_proto);
if (protomask == TLS_PROTOCOL_INVALID) {
+ /* TLSRPT: Local configuration error, don't report. */
/* tls_protocol_mask() logs no warning. */
msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session",
props->namaddr, props->protocols);
@@ -1006,6 +1015,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
TLScontext->level = props->tls_level;
if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
+ /* TLSRPT: Local resource error, don't report. */
msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
tls_print_errors();
tls_free_context(TLScontext);
@@ -1022,6 +1032,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
cipher_list = tls_set_ciphers(TLScontext, props->cipher_grade,
props->cipher_exclusions);
if (cipher_list == 0) {
+ /* TLSRPT: Local configuration error, don't report. */
/* already warned */
tls_free_context(TLScontext);
return (0);
@@ -1036,6 +1047,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
TLScontext->dane = props->dane;
if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
+ /* TLSRPT: Local resource error, don't report. */
msg_warn("Could not set application data for 'TLScontext->con'");
tls_print_errors();
tls_free_context(TLScontext);
@@ -1069,6 +1081,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
* early.
*/
if (!tls_auth_enable(TLScontext, props)) {
+ /* Already warned and reported TLSRPT result. */
tls_free_context(TLScontext);
return (0);
}
@@ -1105,6 +1118,13 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
switch (TLScontext->level) {
case TLS_LEV_HALF_DANE:
case TLS_LEV_DANE:
+#ifdef USE_TLSRPT
+ if (props->tlsrpt) {
+ trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID,
+ /* additional_detail= */ (char *) 0,
+ "all-TLSA-records-unusable");
+ }
+#endif
msg_warn("%s: all TLSA records unusable, fallback to "
"unauthenticated TLS", TLScontext->namaddr);
must_fail = 0;
@@ -1112,13 +1132,34 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
break;
case TLS_LEV_FPRINT:
+#ifdef USE_TLSRPT
+ if (props->tlsrpt) {
+ trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ "all-fingerprints-unusable");
+ }
+#endif
msg_warn("%s: all fingerprints unusable", TLScontext->namaddr);
break;
case TLS_LEV_DANE_ONLY:
+#ifdef USE_TLSRPT
+ if (props->tlsrpt) {
+ trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID,
+ /* additional_detail= */ (char *) 0,
+ "all-TLSA-records-unusable");
+ }
+#endif
msg_warn("%s: all TLSA records unusable", TLScontext->namaddr);
break;
case TLS_LEV_SECURE:
case TLS_LEV_VERIFY:
+#ifdef USE_TLSRPT
+ if (props->tlsrpt) {
+ trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ "all-trust-anchors-unusable");
+ }
+#endif
msg_warn("%s: all trust anchors unusable", TLScontext->namaddr);
break;
}
@@ -1194,6 +1235,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
*/
if (SSL_set_fd(TLScontext->con, props->stream == 0 ? props->fd :
vstream_fileno(props->stream)) != 1) {
+ /* TLSRPT: Local resource error, don't report. */
msg_info("SSL_set_fd error to %s", props->namaddr);
tls_print_errors();
uncache_session(app_ctx->ssl_ctx, TLScontext);
@@ -1213,6 +1255,16 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
if (log_mask & TLS_LOG_TLSPKTS)
tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
+ /*
+ * An external (STS) policy signaled a failure. Prevent false (PKI)
+ * certificate matches in tls_verify.c. TODO(wietse) how was this handled
+ * historically?
+ */
+ if (props->fail_type) {
+ TLScontext->fail_type = mystrdup(props->fail_type);
+ TLScontext->must_fail = 1;
+ }
+
/*
* If we don't trigger the handshake in the library, leave control over
* SSL_connect/read/write/etc with the application.
@@ -1245,6 +1297,12 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
msg_info("SSL_connect error to %s: lost connection",
props->namaddr);
}
+#ifdef USE_TLSRPT
+ if (props->tlsrpt)
+ trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ "tls-handshake-failure");
+#endif
uncache_session(app_ctx->ssl_ctx, TLScontext);
tls_free_context(TLScontext);
return (0);
@@ -1360,6 +1418,16 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext,
tls_int_seed();
+ /*
+ * Inform the caller that we already reported a TLSRPT failure, so that
+ * the caller won't report success after a TLS level was degraded, or
+ * report some less informative failure reason after a more specific
+ * reason was already reported.
+ */
+#ifdef USE_TLSRPT
+ TLScontext->rpt_reported = trw_is_reported(props->tlsrpt);
+#endif
+
return (TLScontext);
}
diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c
index ac7f05f94..f8c43870a 100644
--- a/postfix/src/tls/tls_dane.c
+++ b/postfix/src/tls/tls_dane.c
@@ -593,6 +593,7 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
if (n == 0)
dane->flags |= TLS_DANE_FLAG_EMPTY;
} else
+ /* TODO(wietse) report non-parsable TLSA records in TLSRPT. */
dane->flags |= TLS_DANE_FLAG_NORRS;
if (rrs)
diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
index f77d778ac..3d31cbbe4 100644
--- a/postfix/src/tls/tls_misc.c
+++ b/postfix/src/tls/tls_misc.c
@@ -1346,6 +1346,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
TLScontext->errordepth = -1;
TLScontext->errorcode = X509_V_OK;
TLScontext->errorcert = 0;
+ TLScontext->rpt_reported = 0;
+ TLScontext->fail_type = 0;
return (TLScontext);
}
@@ -1396,6 +1398,8 @@ void tls_free_context(TLS_SESS_STATE *TLScontext)
myfree((void *) TLScontext->srvr_sig_dgst);
if (TLScontext->errorcert)
X509_free(TLScontext->errorcert);
+ if (TLScontext->fail_type)
+ myfree(TLScontext->fail_type);
myfree((void *) TLScontext);
}
diff --git a/postfix/src/tls/tls_proxy.h b/postfix/src/tls/tls_proxy.h
index 65286398f..8e0c67088 100644
--- a/postfix/src/tls/tls_proxy.h
+++ b/postfix/src/tls/tls_proxy.h
@@ -108,11 +108,12 @@ extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *,
((props)->a12), ((props)->a13), ((props)->a14))
#define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
- a9, a10, a11, a12, a13, a14, a15) \
+ a9, a10, a11, a12, a13, a14, a15, a16, a17) \
(((props)->a1), ((props)->a2), ((props)->a3), \
((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
- ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15))
+ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
+ ((props)->a16), ((props)->a17))
extern TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *);
extern void tls_proxy_context_free(TLS_SESS_STATE *);
@@ -181,6 +182,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
#define TLS_ATTR_SRVR_SIG_BITS "srvr_signature_bits"
#define TLS_ATTR_SRVR_SIG_DGST "srvr_signature_digest"
#define TLS_ATTR_NAMADDR "namaddr"
+#define TLS_ATTR_RPT_REPORTED "rpt_reported"
/*
* TLS_SERVER_INIT_PROPS attributes.
@@ -254,7 +256,9 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
#define TLS_ATTR_CIPHER_EXCLUSIONS "cipher_exclusions"
#define TLS_ATTR_MATCHARGV "matchargv"
#define TLS_ATTR_MDALG "mdalg"
-#define TLS_ATTR_DANE "dane"
+#define TLS_ATTR_DANE "dane"
+#define TLS_ATTR_TLSRPT "tlsrpt"
+#define FAIL_TYPE "fail_type"
/*
* TLS_TLSA attributes.
diff --git a/postfix/src/tls/tls_proxy_client_print.c b/postfix/src/tls/tls_proxy_client_print.c
index 81e50b924..b2502ff43 100644
--- a/postfix/src/tls/tls_proxy_client_print.c
+++ b/postfix/src/tls/tls_proxy_client_print.c
@@ -79,6 +79,10 @@
#include
#include
+#ifdef USE_TLSRPT
+#define TLSRPT_WRAPPER_INTERNAL
+#include
+#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
@@ -96,7 +100,7 @@ int tls_proxy_client_param_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
ret = print_fn(fp, flags | ATTR_FLAG_MORE,
SEND_ATTR_STR(TLS_ATTR_CNF_FILE, params->tls_cnf_file),
- SEND_ATTR_STR(TLS_ATTR_CNF_NAME, params->tls_cnf_name),
+ SEND_ATTR_STR(TLS_ATTR_CNF_NAME, params->tls_cnf_name),
SEND_ATTR_STR(VAR_TLS_HIGH_CLIST, params->tls_high_clist),
SEND_ATTR_STR(VAR_TLS_MEDIUM_CLIST,
params->tls_medium_clist),
@@ -242,6 +246,59 @@ static int tls_proxy_client_dane_print(ATTR_PRINT_COMMON_FN print_fn,
return (ret);
}
+#ifdef USE_TLSRPT
+
+/* tls_proxy_client_tlsrpt_print - send TLSRPT_WRAPPER over stream */
+
+static int tls_proxy_client_tlsrpt_print(ATTR_PRINT_COMMON_FN print_fn,
+ VSTREAM *fp, int flags, const void *ptr)
+{
+ const TLSRPT_WRAPPER *trw = (const TLSRPT_WRAPPER *) ptr;
+ int have_trw = trw != 0;
+ int ret;
+
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_INT(TLS_ATTR_TLSRPT, have_trw),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsrpt_print have_trw=%d", have_trw);
+
+ if (ret == 0 && have_trw) {
+ ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+ SEND_ATTR_STR(TRW_RPT_SOCKET_NAME,
+ STRING_OR_EMPTY(trw->rpt_socket_name)),
+ SEND_ATTR_STR(TRW_RPT_POLICY_DOMAIN,
+ STRING_OR_EMPTY(trw->rpt_policy_domain)),
+ SEND_ATTR_STR(TRW_RPT_POLICY_STRING,
+ STRING_OR_EMPTY(trw->rpt_policy_string)),
+ SEND_ATTR_INT(TRW_TLS_POLICY_TYPE,
+ (int) trw->tls_policy_type),
+ SEND_ATTR_FUNC(argv_attr_print,
+ (const void *) trw->tls_policy_strings),
+ SEND_ATTR_STR(TRW_TLS_POLICY_DOMAIN,
+ STRING_OR_EMPTY(trw->tls_policy_domain)),
+ SEND_ATTR_FUNC(argv_attr_print,
+ (const void *) trw->mx_host_patterns),
+ SEND_ATTR_STR(TRW_SRC_MTA_ADDR,
+ STRING_OR_EMPTY(trw->snd_mta_addr)),
+ SEND_ATTR_STR(TRW_DST_MTA_NAME,
+ STRING_OR_EMPTY(trw->rcv_mta_name)),
+ SEND_ATTR_STR(TRW_DST_MTA_ADDR,
+ STRING_OR_EMPTY(trw->rcv_mta_addr)),
+ SEND_ATTR_STR(TRW_DST_MTA_EHLO,
+ STRING_OR_EMPTY(trw->rcv_mta_ehlo)),
+ SEND_ATTR_INT(TRW_FLAGS,
+ trw->flags),
+ ATTR_TYPE_END);
+ }
+ /* Do not flush the stream. */
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsrpt_print ret=%d", ret);
+ return (ret);
+}
+
+#endif
+
/* tls_proxy_client_start_print - send TLS_CLIENT_START_PROPS over stream */
int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
@@ -283,6 +340,12 @@ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
STRING_OR_EMPTY(props->mdalg)),
SEND_ATTR_FUNC(tls_proxy_client_dane_print,
(const void *) props->dane),
+#ifdef USE_TLSRPT
+ SEND_ATTR_FUNC(tls_proxy_client_tlsrpt_print,
+ (const void *) props->tlsrpt),
+#endif
+ SEND_ATTR_STR(FAIL_TYPE,
+ STRING_OR_EMPTY(props->fail_type)),
ATTR_TYPE_END);
/* Do not flush the stream. */
if (msg_verbose)
diff --git a/postfix/src/tls/tls_proxy_client_scan.c b/postfix/src/tls/tls_proxy_client_scan.c
index d36cf4d7e..210704021 100644
--- a/postfix/src/tls/tls_proxy_client_scan.c
+++ b/postfix/src/tls/tls_proxy_client_scan.c
@@ -113,6 +113,10 @@
#define TLS_INTERNAL
#include
#include
+#ifdef USE_TLSRPT
+#define TLSRPT_WRAPPER_INTERNAL
+#include
+#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
@@ -329,6 +333,12 @@ void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props)
myfree((void *) props->mdalg);
if (props->dane)
tls_dane_free((TLS_DANE *) props->dane);
+#ifdef USE_TLSRPT
+ if (props->tlsrpt)
+ trw_free(props->tlsrpt);
+#endif
+ if (props->fail_type)
+ myfree(props->fail_type);
myfree((void *) props);
}
@@ -419,6 +429,87 @@ static int tls_proxy_client_dane_scan(ATTR_SCAN_COMMON_FN scan_fn,
return (ret);
}
+#define EXPORT_OR_NULL(str, vstr) do { \
+ if (LEN(vstr) > 0) { \
+ (str) = vstring_export(vstr); \
+ } else { \
+ (str) = 0; \
+ vstring_free(vstr); \
+ } \
+ } while (0)
+
+#ifdef USE_TLSRPT
+
+/* tls_proxy_client_tlsrpt_scan - receive TLSRPT_WRAPPER from stream */
+
+static int tls_proxy_client_tlsrpt_scan(ATTR_SCAN_COMMON_FN scan_fn,
+ VSTREAM *fp, int flags, void *ptr)
+{
+ TLSRPT_WRAPPER *trw = 0;
+ int ret;
+ int have_tlsrpt = 0;
+
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(TLS_ATTR_DANE, &have_tlsrpt),
+ ATTR_TYPE_END);
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsrpt_scan have_tlsrpt=%d", have_tlsrpt);
+
+ if (ret == 1 && have_tlsrpt) {
+ VSTRING *rpt_socket_name = vstring_alloc(100);
+ VSTRING *rpt_policy_domain = vstring_alloc(100);
+ VSTRING *rpt_policy_string = vstring_alloc(100);
+ int tls_policy_type;
+ ARGV *tls_policy_strings = argv_alloc(100);
+ VSTRING *tls_policy_domain = vstring_alloc(100);
+ ARGV *mx_host_patterns = argv_alloc(100);
+ VSTRING *snd_mta_addr = vstring_alloc(100);
+ VSTRING *rcv_mta_name = vstring_alloc(100);
+ VSTRING *rcv_mta_addr = vstring_alloc(100);
+ VSTRING *rcv_mta_ehlo = vstring_alloc(100);
+
+ ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+ RECV_ATTR_STR(TRW_RPT_SOCKET_NAME, rpt_socket_name),
+ RECV_ATTR_STR(TRW_RPT_POLICY_DOMAIN, rpt_policy_domain),
+ RECV_ATTR_STR(TRW_RPT_POLICY_STRING, rpt_policy_string),
+ RECV_ATTR_INT(TRW_TLS_POLICY_TYPE, &tls_policy_type),
+ RECV_ATTR_FUNC(argv_attr_scan, tls_policy_strings),
+ RECV_ATTR_STR(TRW_TLS_POLICY_DOMAIN, tls_policy_domain),
+ RECV_ATTR_FUNC(argv_attr_scan, mx_host_patterns),
+ RECV_ATTR_STR(TRW_SRC_MTA_ADDR, snd_mta_addr),
+ RECV_ATTR_STR(TRW_DST_MTA_NAME, rcv_mta_name),
+ RECV_ATTR_STR(TRW_DST_MTA_ADDR, rcv_mta_addr),
+ RECV_ATTR_STR(TRW_DST_MTA_EHLO, rcv_mta_ehlo),
+ RECV_ATTR_INT(TRW_FLAGS, &trw->flags),
+ ATTR_TYPE_END);
+
+ /* Always construct a well-formed structure. */
+ trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
+ trw->rpt_socket_name = vstring_export(rpt_socket_name);
+ trw->rpt_policy_domain = vstring_export(rpt_policy_domain);
+ trw->rpt_policy_string = vstring_export(rpt_policy_string);
+ trw->tls_policy_type = tls_policy_type;
+ trw->tls_policy_strings = tls_policy_strings;
+ EXPORT_OR_NULL(trw->tls_policy_domain, tls_policy_domain);
+ trw->mx_host_patterns = mx_host_patterns;
+ EXPORT_OR_NULL(trw->snd_mta_addr, snd_mta_addr);
+ EXPORT_OR_NULL(trw->rcv_mta_name, rcv_mta_name);
+ EXPORT_OR_NULL(trw->rcv_mta_addr, rcv_mta_addr);
+ EXPORT_OR_NULL(trw->rcv_mta_ehlo, rcv_mta_ehlo);
+ ret = (ret == 12 ? 1 : -1);
+ if (ret != 1) {
+ trw_free(trw);
+ trw = 0;
+ }
+ }
+ *(TLSRPT_WRAPPER **) ptr = trw;
+ if (msg_verbose)
+ msg_info("tls_proxy_client_tlsrpt_scan ret=%d", ret);
+ return (ret);
+}
+
+#endif
+
/* tls_proxy_client_start_scan - receive TLS_CLIENT_START_PROPS from stream */
int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
@@ -437,6 +528,13 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
VSTRING *cipher_grade = vstring_alloc(25);
VSTRING *cipher_exclusions = vstring_alloc(25);
VSTRING *mdalg = vstring_alloc(25);
+ VSTRING *fail_type = vstring_alloc(25);
+
+#ifdef USE_TLSRPT
+#define EXPECT_START_SCAN_RETURN 17
+#else
+#define EXPECT_START_SCAN_RETURN 16
+#endif
if (msg_verbose)
msg_info("begin tls_proxy_client_start_scan");
@@ -467,6 +565,11 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
RECV_ATTR_FUNC(tls_proxy_client_dane_scan,
&props->dane),
+#ifdef USE_TLSRPT
+ RECV_ATTR_FUNC(tls_proxy_client_tlsrpt_scan,
+ &props->tlsrpt),
+#endif
+ RECV_ATTR_STR(FAIL_TYPE, fail_type),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
props->nexthop = vstring_export(nexthop);
@@ -479,7 +582,8 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
props->cipher_grade = vstring_export(cipher_grade);
props->cipher_exclusions = vstring_export(cipher_exclusions);
props->mdalg = vstring_export(mdalg);
- ret = (ret == 15 ? 1 : -1);
+ EXPORT_OR_NULL(props->fail_type, fail_type);
+ ret = (ret == EXPECT_START_SCAN_RETURN ? 1 : -1);
if (ret != 1) {
tls_proxy_client_start_free(props);
props = 0;
diff --git a/postfix/src/tls/tls_proxy_context_print.c b/postfix/src/tls/tls_proxy_context_print.c
index 930410a46..808abbe73 100644
--- a/postfix/src/tls/tls_proxy_context_print.c
+++ b/postfix/src/tls/tls_proxy_context_print.c
@@ -110,6 +110,8 @@ int tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
STRING_OR_EMPTY(tp->srvr_sig_dgst)),
SEND_ATTR_STR(TLS_ATTR_NAMADDR,
STRING_OR_EMPTY(tp->namaddr)),
+ SEND_ATTR_INT(TLS_ATTR_RPT_REPORTED,
+ tp->rpt_reported),
ATTR_TYPE_END);
/* Do not flush the stream. */
return (ret);
diff --git a/postfix/src/tls/tls_proxy_context_scan.c b/postfix/src/tls/tls_proxy_context_scan.c
index 48aaff627..fc23c528a 100644
--- a/postfix/src/tls/tls_proxy_context_scan.c
+++ b/postfix/src/tls/tls_proxy_context_scan.c
@@ -124,6 +124,8 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
RECV_ATTR_INT(TLS_ATTR_SRVR_SIG_BITS, &tls_context->srvr_sig_bits),
RECV_ATTR_STR(TLS_ATTR_SRVR_SIG_DGST, srvr_sig_dgst),
RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
+ RECV_ATTR_INT(TLS_ATTR_RPT_REPORTED,
+ &tls_context->rpt_reported),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
tls_context->peer_CN = vstring_export(peer_CN);
@@ -141,7 +143,7 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve);
tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst);
tls_context->namaddr = vstring_export(namaddr);
- ret = (ret == 24 ? 1 : -1);
+ ret = (ret == 25 ? 1 : -1);
if (ret != 1) {
tls_proxy_context_free(tls_context);
tls_context = 0;
diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c
index 88b332642..87433e935 100644
--- a/postfix/src/tls/tls_server.c
+++ b/postfix/src/tls/tls_server.c
@@ -1035,7 +1035,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
if (!TLS_CERT_IS_TRUSTED(TLScontext)
&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
if (TLScontext->session_reused == 0)
- tls_log_verify_error(TLScontext);
+ tls_log_verify_error(TLScontext, (struct TLSRPT_WRAPPER *) 0);
else
msg_info("%s: re-using session with untrusted certificate, "
"look for details earlier in the log",
diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c
index c643f1838..a719c2ca9 100644
--- a/postfix/src/tls/tls_verify.c
+++ b/postfix/src/tls/tls_verify.c
@@ -11,8 +11,9 @@
/* int ok;
/* X509_STORE_CTX *ctx;
/*
-/* int tls_log_verify_error(TLScontext)
+/* int tls_log_verify_error(TLScontext, tlsrpt)
/* TLS_SESS_STATE *TLScontext;
+/* struct TLSRPT_WRAPPER *tlsrpt;
/*
/* char *tls_peer_CN(peercert, TLScontext)
/* X509 *peercert;
@@ -107,6 +108,10 @@
/* TLS library. */
+#ifdef USE_TLSRPT
+#include
+#endif
+
#define TLS_INTERNAL
#include
@@ -194,18 +199,50 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
/* tls_log_verify_error - Report final verification error status */
-void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
+void tls_log_verify_error(TLS_SESS_STATE *TLScontext,
+ struct TLSRPT_WRAPPER *tlsrpt)
{
char buf[CCERT_BUFSIZ];
int err = TLScontext->errorcode;
X509 *cert = TLScontext->errorcert;
int depth = TLScontext->errordepth;
+#ifdef USE_TLSRPT
+ VSTRING *err_vstr = vstring_alloc(100);
+
+#define CERT_ERROR_TO_STRING(err) \
+ translit(vstring_str(vstring_strcpy(err_vstr, \
+ X509_verify_cert_error_string(err))), \
+ " ", "_")
+#endif
+
#define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server")
if (err == X509_V_OK)
return;
+ /*
+ * If an external policy flagged an error, report that instead.
+ */
+ if (TLScontext->fail_type) {
+ msg_info("certificate verification failed for %s: "
+ "external policy failure (%s)",
+ TLScontext->namaddr, TLScontext->fail_type);
+#ifdef USE_TLSRPT
+ if (tlsrpt) {
+ tlsrpt_failure_t failure_type;
+
+ if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->fail_type)) < 0)
+ msg_panic("tls_log_verify_error: unexpected failure_reason: %s",
+ TLScontext->fail_type);
+ trw_report_failure(tlsrpt, failure_type,
+ /* additional_detail= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+ }
+#endif
+ return;
+ }
+
/*
* Specific causes for verification failure.
*/
@@ -218,10 +255,22 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
*/
msg_info("certificate verification failed for %s: "
"not trusted by local or TLSA policy", TLScontext->namaddr);
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED,
+ /* additional_detail= */ (char *) 0,
+ /* failure_code= */ (char *) 0);
+#endif
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
msg_info("certificate verification failed for %s: "
"self-signed certificate", TLScontext->namaddr);
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ CERT_ERROR_TO_STRING(err));
+#endif
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
@@ -237,25 +286,55 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
strcpy(buf, "");
msg_info("certificate verification failed for %s: untrusted issuer %s",
TLScontext->namaddr, printable(buf, '?'));
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ CERT_ERROR_TO_STRING(err));
+#endif
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
msg_info("%s certificate verification failed for %s: certificate not"
" yet valid", PURPOSE, TLScontext->namaddr);
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ CERT_ERROR_TO_STRING(err));
+#endif
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
msg_info("%s certificate verification failed for %s: certificate has"
" expired", PURPOSE, TLScontext->namaddr);
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_EXPIRED,
+ /* additional_detail= */ (char *) 0,
+ /* failure_code= */ (char *) 8);
+#endif
break;
case X509_V_ERR_INVALID_PURPOSE:
msg_info("certificate verification failed for %s: not designated for "
"use as a %s certificate", TLScontext->namaddr, PURPOSE);
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ CERT_ERROR_TO_STRING(err));
+#endif
break;
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
msg_info("certificate verification failed for %s: "
"certificate chain longer than limit(%d)",
TLScontext->namaddr, depth - 1);
+#ifdef USE_TLSRPT
+ if (tlsrpt)
+ trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ CERT_ERROR_TO_STRING(err));
+#endif
break;
default:
msg_info("%s certificate verification failed for %s: num=%d:%s",
@@ -263,6 +342,9 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext)
X509_verify_cert_error_string(err));
break;
}
+#ifdef USE_TLSRPT
+ vstring_free(err_vstr);
+#endif
}
#ifndef DONT_GRIPE
diff --git a/postfix/src/tls/tlsrpt_wrapper.c b/postfix/src/tls/tlsrpt_wrapper.c
new file mode 100644
index 000000000..4005d8edc
--- /dev/null
+++ b/postfix/src/tls/tlsrpt_wrapper.c
@@ -0,0 +1,647 @@
+/*++
+/* NAME
+/* tlsrpt_wrapper 3
+/* SUMMARY
+/* TLSRPT support for the TLS protocol engine
+/* SYNOPSIS
+/* #include
+/*
+/* #ifdef USE_TLS
+/* #ifdef USE_TLSRPT
+/* TLS_RPT *trw_create(
+/* const char *rpt_socket_name,
+/* const char *rpt_policy_domain,
+/* const char *rpt_policy_string)
+/*
+/* void trw_free(
+/* TLSRPT_WRAPPER *trw)
+/*
+/* void trw_set_tls_policy(
+/* TLSRPT_WRAPPER *trw,
+/* tlsrpt_policy_type_t tls_policy_type,
+/* const char *const *tls_policy_strings,
+/* const char *tls_policy_domain
+/* const char *const *mx_host_patterns)
+/*
+/* void trw_set_tcp_connection(
+/* TLSRPT_WRAPPER *trw,
+/* const char *snd_mta_addr,
+/* const char *rcv_mta_name,
+/* const char *rcv_mta_addr)
+/*
+/* void trw_set_ehlo_resp(
+/* TLSRPT_WRAPPER *trw,
+/* const char *rcv_mta_ehlo)
+/*
+/* void trw_report_failure(
+/* TLSRPT_WRAPPER *trw,
+/* tlsrpt_failure_t failure_type,
+/* const char *additional_detail,
+/* const char *failure_reason)
+/*
+/* void trw_report_success(
+/* TLSRPT_WRAPPER *trw)
+/*
+/* int trw_is_reported(
+/* TLSRPT_WRAPPER *trw)
+/*
+/* tlsrpt_policy_type_t convert_tlsrpt_policy_type(
+/* const char *policy_type)
+/*
+/* tlsrpt_failure_t convert_tlsrpt_policy_failure(
+/* const char *policy_failure)
+/* #endif /* USE_TLS_RPT */
+/*
+/* int valid_tlsrpt_policy_type(
+/* const char *type_name)
+/*
+/* int valid_tlsrpt_policy_failure(
+/* const char *failure_name)
+/* #endif /* USE_TLS */
+/* ARCHITECTURE, BOTTOM-UP VIEW
+/* Postfix TLSRPT support uses the TLSRPT client library from
+/* sys4.de. That library makes the reasonable assumption that
+/* all calls concerning one SMTP session will be made from within
+/* one process.
+/*
+/* With Postfix, the TLS protocol engine may be located in a
+/* different process than the SMTP protocol engine, and both
+/* processes need the ability to report a TLS error.
+/*
+/* To bridge this gap, the SMTP protocol engine forwards SMTP
+/* session and TLS policy information to the TLS protocol engine in
+/* the form of a TLSRPT_WRAPPER object that contains SMTP and other
+/* information that the TLSRPT client library needs. The TLS engine
+/* can pass that information to the TLSRPT client library to report
+/* a TLS error. The SMTP protocol engine can use that same
+/* information to report a TLS error or success.
+/* IMPLEMENTATION
+/* .ad
+/* .fi
+/* The Postfix SMTP protocol engine (smtp_proto.c) reports TLS
+/* errors when TLS support is required but unavailable, or requests
+/* the Postfix TLS protocol engine to perform a TLS protocol
+/* handshake over an open SMTP connection. The SMTP protocol engine
+/* either calls the TLS protocol engine directly, or calls it over
+/* RPC in a tlsproxy(8) process.
+/*
+/* The TLS protocol engine may report a TLS error through the
+/* tlsrpt_wrapper library, and either returns no TLS session object,
+/* or a TLS session object for a completed handshake. The TLS
+/* session object will indicate if the TLS protocol engine reported
+/* any TLS error through TLSRPT.
+/*
+/* The Postfix SMTP protocol engine reports success or failure
+/* through the tlsrpt_wrapper library, depending on whether all
+/* matching requirements were satisfied. SMTP protocol engine
+/* does not report success or failure through the tlsrpt_wrapper
+/* library if the TLS protocol engine already reported a failure.
+/* WRAPPER API
+/* .ad
+/* .fi
+/* The functions below must be called in a specific order. All
+/* string inputs are copied. If a required call is missing then
+/* the request will be ignored, and a warning will be logged.
+/* .PP
+/* trw_create() must be called before other trw_xxx() requests can
+/* be made. Arguments:
+/* .IP rpt_socket_name
+/* The name of a socket that will be managed by local TLSRPT
+/* infrastructure.
+/* .IP rpt_policy_domain
+/* The TLSRPT policy domain name, i.e. the domain that wishes to
+/* receive TLSRPT summary reports. An internationalized domain name
+/* must be in A-label form (i.e. punycode).
+/* .IP rpt_policy_string
+/* The TLSRPT policy record content, i.e. how to submit TLSRPT
+/* summary reports.
+/* .PP
+/* trw_free() destroys storage allocated with other trw_xxx()
+/* requests.
+/* .PP
+/* trw_set_tls_policy() must be called by the SMTP protocol
+/* engine after it found a DANE, STS, or no policy, and before it
+/* tries to establish a new SMTP connection. This function clears
+/* information that was specified earlier with trw_set_tls_policy()
+/* or trw_set_tcp_connection(), and whether trw_report_failure()
+/* or trw_report_success() were called. Mapping from arguments to
+/* TLSRPT report fields:
+/* .IP tls_policy_type
+/* policies[].policy.policy-type.
+/* .IP tls_policy_strings
+/* policies[].policy.policy-string[]. Ignored if the tls_policy_type
+/* value is TLSRPT_NO_POLICY_FOUND.
+/* .IP tls_policy_domain
+/* policies[].policy.policy-domain.
+/* .IP mx_host_patterns (may be null)
+/* policies[].policy.mx-host[]. Ignored if the tls_policy_type
+/* value is TLSRPT_NO_POLICY_FOUND.
+/* .PP
+/* trw_set_tcp_connection() and trw_set_ehlo_resp() are optionally
+/* called by the SMTP protcol engine, after it has established
+/* a new SMTP connection, before it requests a TLS protocol
+/* handshake. Mapping from arguments to TLSRPT report fields:
+/* .IP snd_mta_addr (may be null)
+/* policies[].failure-details[].sending-mta-ip.
+/* .IP rcv_mta_name (may be null)
+/* policies[].failure-details[].receiving-mx-hostname.
+/* .IP rcv_mta_addr (may be null)
+/* policies[].failure-details[].receiving-ip.
+/* .PP
+/* trw_set_ehlo_resp() is optionally called by the SMTP protcol
+/* engine to pass on the EHLO response. Presumably this is the EHLO
+/* response before STARTTLS (TLSRPT is primarily interested in
+/* pre-handshake and handshake errors).
+/* .IP rcv_mta_ehlo (may be null)
+/* policies[].failure-details[].receiving-mx-helo.
+/* .PP
+/* trw_report_failure() is called by the TLS protocol engine or
+/* SMTP protocol engine to report a TLS error. The result value
+/* is 0 for success, -1 for failure as indicated with the errno
+/* value. The call is successfully skipped if information is missing
+/* or if failure or success were already reported for the
+/* connection. Mapping from arguments to TLSRPT report fields:
+/* .IP failure_type
+/* policies[].failure-details[].result-type.
+/* .IP additional_detail (may be null)
+/* policies[].failure-details[].additional-information.
+/* .IP failure_reason (may be null)
+/* policies[].failure-details[].failure-reason-code
+/* .PP
+/* trw_report_success() is called by the SMTP protocol engine
+/* to report a successful TLS handshake. The result value is
+/* 0 for success, -1 for failure with errno indicating the
+/* error type. The call is successfully skipped if information if
+/* missing or if failure or success were already reported for
+/* the connection.
+/* .PP
+/* trw_is_reported() returns non-zero when the contents of the
+/* specified TLSRPT_WRAPPER have been reported.
+/* .PP
+/* convert_tlsrpt_policy_type() and convert_tlsrpt_policy_failure()
+/* convert a valid policy type or failure name to the corresponding
+/* enum value. The result is < 0 if the name is not valid.
+/* .PP
+/* valid_tlsrpt_policy_type() and valid_tlsrpt_policy_failure()
+/* return non-zero if the specified policy type or failure name
+/* is valid in TLSRPT. These functions do not require that the
+/* module is built with TLSRPT support. This allows the names to
+/* be used even if TLSRPT is disabled.
+/* DIAGNOSTICS
+/* Some functions will log a a warning when information is missing,
+/* but such warnings will not affect the SMTP or TLS protocol engine.
+/* BUGS
+/* This implementation is suitable to report successful TLS policy
+/* compliance, and to report a failure that prevents TLS policy
+/* compliance (example: all TLSA records are unusable). Do not use
+/* this implementation to report other errors (example: some TLSA
+/* record is non-parsable).
+/* SEE ALSO
+/* https://github.com/sys4/tlsrpt, TLSRPT client library
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+#if defined(USE_TLS)
+
+ /*
+ * System library.
+ */
+#include
+#include
+#include
+#if defined(USE_TLSRPT)
+#include
+#endif
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+#include
+#include
+#include
+
+ /*
+ * Some functions are not #ifdef USE_TLSRPT.
+ */
+#define TLSRPT_WRAPPER_INTERNAL
+#include
+#if defined(USE_TLSRPT)
+
+ /*
+ * Macros to make repetitive code more readable.
+ */
+#define MYFREE_IF_SET(member) do { \
+ if (member) \
+ myfree(member); \
+ } while (0)
+
+#define MYFREE_IF_SET_AND_CLEAR(member, value) do { \
+ if (member) { \
+ myfree(member); \
+ (member) = 0; \
+ } \
+ } while (0)
+
+#define MYFREE_IF_SET_AND_COPY(member, value) do { \
+ MYFREE_IF_SET(member); \
+ (member) = (value) ? mystrdup(value) : 0; \
+ } while (0)
+
+#define ARGV_FREE_IF_SET(member) do { \
+ if (member) \
+ argv_free(member); \
+ } while (0)
+
+#define ARGV_FREE_IF_SET_AND_CLEAR(member) do { \
+ if (member) { \
+ argv_free(member); \
+ (member) = 0; \
+ } \
+ } while (0)
+
+#define ARGV_FREE_IF_SET_AND_COPY(member, value) do { \
+ ARGV_FREE_IF_SET(member); \
+ (member) = (value) ? argv_addv((ARGV *) 0, value) : 0; \
+ } while (0)
+
+/* trw_create - create initial TLSRPT_WRAPPER instance */
+
+TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name,
+ const char *rpt_policy_domain,
+ const char *rpt_policy_string)
+{
+ TLSRPT_WRAPPER *trw;
+
+ /*
+ * memset() is not portable for pointer etc. types.
+ */
+ trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
+ trw->rpt_socket_name = mystrdup(rpt_socket_name);
+ trw->rpt_policy_domain = mystrdup(rpt_policy_domain);
+ trw->rpt_policy_string = mystrdup(rpt_policy_string);;
+ trw->tls_policy_type = 0;
+ trw->tls_policy_strings = 0;
+ trw->tls_policy_domain = 0;
+ trw->mx_host_patterns = 0;
+ trw->snd_mta_addr = 0;
+ trw->rcv_mta_name = 0;
+ trw->rcv_mta_addr = 0;
+ trw->rcv_mta_ehlo = 0;
+ trw->flags = 0;
+ return (trw);
+}
+
+/* trw_free - destroy TLSRPT_WRAPPER instance. */
+
+void trw_free(TLSRPT_WRAPPER *trw)
+{
+ /* Destroy fields set with trw_create(). */
+ myfree(trw->rpt_socket_name);
+ myfree(trw->rpt_policy_domain);
+ myfree(trw->rpt_policy_string);
+ /* Destroy fields set with trw_set_tls_policy(). */
+ ARGV_FREE_IF_SET(trw->tls_policy_strings);
+ MYFREE_IF_SET(trw->tls_policy_domain);
+ ARGV_FREE_IF_SET(trw->mx_host_patterns);
+ /* Destroy fields set with trw_set_tcp_connection(). */
+ trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
+ /* Destroy fields set with trw_set_ehlo_resp(). */
+ trw_set_ehlo_resp(trw, (char *) 0);
+ /* That's all. */
+ myfree((void *) trw);
+}
+
+/* trw_set_tls_policy - set TLS policy info, clear SMTP info */
+
+void trw_set_tls_policy(TLSRPT_WRAPPER *trw,
+ tlsrpt_policy_type_t tls_policy_type,
+ const char *const * tls_policy_strings,
+ const char *tls_policy_domain,
+ const char *const * mx_host_patterns)
+{
+ trw->tls_policy_type = tls_policy_type;
+ MYFREE_IF_SET_AND_COPY(trw->tls_policy_domain, tls_policy_domain);
+ if (tls_policy_type == TLSRPT_NO_POLICY_FOUND) {
+ ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
+ ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings);
+ } else {
+ ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, tls_policy_strings);
+ ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, mx_host_patterns);
+ }
+ trw->flags = TRW_FLAG_HAVE_TLS_POLICY;
+ trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0);
+ trw_set_ehlo_resp(trw, (char *) 0);
+}
+
+/* trw_set_tcp_connection - set SMTP endpoint info */
+
+void trw_set_tcp_connection(TLSRPT_WRAPPER *trw,
+ const char *snd_mta_addr,
+ const char *rcv_mta_name,
+ const char *rcv_mta_addr)
+{
+ const char myname[] = "trw_set_tcp_connection";
+
+ /*
+ * Sanity check: usage errors are not a show stopper.
+ */
+ if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
+ || (trw->flags & TRW_FLAG_REPORTED)) {
+ msg_warn("%s: missing trw_set_tls_policy call", myname);
+ return;
+ }
+ MYFREE_IF_SET_AND_COPY(trw->snd_mta_addr, snd_mta_addr);
+ MYFREE_IF_SET_AND_COPY(trw->rcv_mta_name, rcv_mta_name);
+ MYFREE_IF_SET_AND_COPY(trw->rcv_mta_addr, rcv_mta_addr);
+}
+
+/* trw_set_ehlo_resp - set EHLO response */
+
+void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, const char *rcv_mta_ehlo)
+{
+ const char myname[] = "trw_set_ehlo_resp";
+
+ /*
+ * Sanity check: usage errors are not a show stopper.
+ */
+ if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0
+ || (trw->flags & TRW_FLAG_REPORTED)) {
+ msg_warn("%s: missing trw_set_tls_policy call", myname);
+ return;
+ }
+ MYFREE_IF_SET_AND_COPY(trw->rcv_mta_ehlo, rcv_mta_ehlo);
+}
+
+/* trw_munge_report_result - helper to map and log libtlsrpt result value */
+
+static int trw_munge_report_result(int libtlsrpt_errorcode)
+{
+ int err;
+
+ /*
+ * First, deal with the non-error cases.
+ */
+ if (libtlsrpt_errorcode == 0) {
+ return (0);
+ }
+
+ /*
+ * Do not report success if errno was zero.
+ */
+ else {
+ err = tlsrpt_errno_from_error_code(libtlsrpt_errorcode);
+ msg_warn("Could not report TLS handshake result to tlsrpt library:"
+ " %s (errno %d)", mystrerror(err), err);
+ errno = err;
+ return (-1);
+ }
+}
+
+/* trw_tlsrpt_failure_to_string - make debug logging readable */
+
+static const char *trw_failure_type_to_string(tlsrpt_failure_t failure_type)
+{
+ static const NAME_CODE failure_types[] = {
+ "TLSRPT_STARTTLS_NOT_SUPPORTED", TLSRPT_STARTTLS_NOT_SUPPORTED,
+ "TLSRPT_CERTIFICATE_HOST_MISMATCH", TLSRPT_CERTIFICATE_HOST_MISMATCH,
+ "TLSRPT_CERTIFICATE_NOT_TRUSTED", TLSRPT_CERTIFICATE_NOT_TRUSTED,
+ "TLSRPT_CERTIFICATE_EXPIRED", TLSRPT_CERTIFICATE_EXPIRED,
+ "TLSRPT_VALIDATION_FAILURE", TLSRPT_VALIDATION_FAILURE,
+ "TLSRPT_STS_POLICY_FETCH_ERROR", TLSRPT_STS_POLICY_FETCH_ERROR,
+ "TLSRPT_STS_POLICY_INVALID", TLSRPT_STS_POLICY_INVALID,
+ "TLSRPT_STS_WEBPKI_INVALID", TLSRPT_STS_WEBPKI_INVALID,
+ "TLSRPT_TLSA_INVALID", TLSRPT_TLSA_INVALID,
+ "TLSRPT_DNSSEC_INVALID", TLSRPT_DNSSEC_INVALID,
+ "TLSRPT_DANE_REQUIRED", TLSRPT_DANE_REQUIRED,
+ "TLSRPT_UNFINISHED_POLICY", TLSRPT_UNFINISHED_POLICY,
+ 0, -1
+ };
+ const char *cp;
+ static VSTRING *buf;
+
+ if ((cp = str_name_code(failure_types, failure_type)) == 0) {
+ if (buf == 0)
+ buf = vstring_alloc(20);
+ msg_warn("unknown tlsrpt_failure_t value %d", failure_type);
+ vstring_sprintf(buf, "failure_type_%d", failure_type);
+ cp = vstring_str(buf);
+ }
+ return (cp);
+}
+
+/* trw_report_failure - one-shot failure reporter */
+
+int trw_report_failure(TLSRPT_WRAPPER *trw,
+ tlsrpt_failure_t failure_type,
+ const char *additional_detail,
+ const char *failure_reason)
+{
+ const char myname[] = "trw_report_failure";
+ struct tlsrpt_connection_t *con;
+ int res;
+
+ /*
+ * Sanity check: usage errors are not a show stopper.
+ */
+ if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
+ msg_warn("%s: missing trw_set_tls_policy call", myname);
+ return (0);
+ }
+
+ /*
+ * Report a failure only when it is seen first. If a failure was already
+ * reported by a lower-level function close to the root cause, then skip
+ * the less detailed failure report from a later caller who is further
+ * away from the point where trouble was found.
+ *
+ * TODO(wietse) Is it worthwhile to distinguish between failure versus
+ * success already reported?
+ */
+ if (trw->flags & TRW_FLAG_REPORTED) {
+ if (msg_verbose)
+ msg_info("%s: success or failure already reported", myname);
+ return (0);
+ }
+ trw->flags |= TRW_FLAG_REPORTED;
+
+ if (msg_verbose)
+ msg_info("%s: rpt_policy_domain=%s receiving_mx=%s failure_type=%s"
+ " failure_reason=%s",
+ myname, trw->rpt_policy_domain, trw->rcv_mta_name,
+ trw_failure_type_to_string(failure_type),
+ failure_reason ? failure_reason : "(null)");
+
+ if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
+ struct tlsrpt_dr_t *dr;
+ char **cpp;
+
+ if ((res = tlsrpt_init_delivery_request(&dr, con,
+ trw->rpt_policy_domain,
+ trw->rpt_policy_string)) == 0) {
+ if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
+ trw->tls_policy_domain)) == 0) {
+ if (trw->tls_policy_strings)
+ for (cpp = trw->tls_policy_strings->argv;
+ res == 0 && *cpp; cpp++)
+ res = tlsrpt_add_policy_string(dr, *cpp);
+ if (trw->mx_host_patterns)
+ for (cpp = trw->mx_host_patterns->argv;
+ res == 0 && *cpp; cpp++)
+ res = tlsrpt_add_mx_host_pattern(dr, *cpp);
+ if (res == 0)
+ res = tlsrpt_add_delivery_request_failure(dr,
+ /* failure_code= */ failure_type,
+ /* sending_mta_ip= */ trw->snd_mta_addr,
+ /* receiving_mx_hostname= */ trw->rcv_mta_name,
+ /* receiving_mx_helo= */ trw->rcv_mta_ehlo,
+ /* receiving_ip= */ trw->rcv_mta_addr,
+ /* additional_information= */ additional_detail,
+ /* failure_reason_code= */ failure_reason);
+ if (res == 0)
+ res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_FAILURE);
+ }
+ if (res == 0) {
+ res = tlsrpt_finish_delivery_request(&dr);
+ } else {
+ (void) tlsrpt_cancel_delivery_request(&dr);
+ }
+ }
+ (void) tlsrpt_close(&con);
+ }
+ return (trw_munge_report_result(res));
+}
+
+/* trw_report_success - one-shot success reporter */
+
+int trw_report_success(TLSRPT_WRAPPER *trw)
+{
+ const char myname[] = "trw_report_success";
+ struct tlsrpt_connection_t *con;
+ int res;
+
+ /*
+ * Sanity check: usage errors are not a show stopper.
+ */
+ if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) {
+ msg_warn("%s: missing trw_set_tls_policy call", myname);
+ return (0);
+ }
+ /* This should not happen. Log a warning. */
+ if (trw->flags & TRW_FLAG_REPORTED) {
+ msg_warn("%s: success or failure was already reported", myname);
+ return (0);
+ }
+ trw->flags |= TRW_FLAG_REPORTED;
+
+ if (msg_verbose)
+ msg_info("%s: rpt_policy_domain=%s receiving_mx=%s",
+ myname, trw->rpt_policy_domain, trw->rcv_mta_name);
+
+ if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) {
+ struct tlsrpt_dr_t *dr;
+
+ if ((res = tlsrpt_init_delivery_request(&dr, con,
+ trw->rpt_policy_domain,
+ trw->rpt_policy_string)) == 0) {
+ if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type,
+ trw->tls_policy_domain)) == 0)
+ res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_SUCCESS);
+ if (res == 0) {
+ res = tlsrpt_finish_delivery_request(&dr);
+ } else {
+ (void) tlsrpt_cancel_delivery_request(&dr);
+ }
+ }
+ (void) tlsrpt_close(&con);
+ }
+ return (trw_munge_report_result(res));
+}
+
+/* trw_is_reported - trw_report_success() or trw_report_failure() called */
+
+int trw_is_reported(const TLSRPT_WRAPPER *trw)
+{
+ return (trw->flags & TRW_FLAG_REPORTED);
+}
+
+#endif /* USE_TLS_RPT */
+
+ /*
+ * Dummy definitions for builds without the TLSRPT library, so that we can
+ * still validate names.
+ */
+#if !defined(USE_TLSRPT)
+#define TLSRPT_POLICY_DANE 0
+#define TLSRPT_POLICY_STS 0
+#define TLSRPT_NO_POLICY_FOUND 0
+
+#define TLSRPT_VALIDATION_FAILURE 0
+#define TLSRPT_STS_POLICY_FETCH_ERROR 0
+#define TLSRPT_STS_POLICY_INVALID 0
+#define TLSRPT_STS_WEBPKI_INVALID 0
+#endif
+
+ /*
+ * Mapping from RFC 8460 string to libtlsrpt enum for policy types and
+ * policy failures. The mapping assumes that all enum values are
+ * non-negative.
+ */
+const NAME_CODE tlsrpt_policy_type_mapping[] = {
+ "sts", TLSRPT_POLICY_STS,
+ "no-policy-found", TLSRPT_NO_POLICY_FOUND,
+ 0, -1,
+};
+
+const NAME_CODE tlsrpt_policy_failure_mapping[] = {
+ "sts-policy-fetch-error", TLSRPT_STS_POLICY_FETCH_ERROR,
+ "sts-policy-invalid", TLSRPT_STS_POLICY_INVALID,
+ "sts-webpki-invalid", TLSRPT_STS_WEBPKI_INVALID,
+ "validation-failure", TLSRPT_VALIDATION_FAILURE,
+ 0, -1,
+};
+
+/* valid_tlsrpt_policy_type - validate policy_type attribute value */
+
+int valid_tlsrpt_policy_type(const char *policy_type)
+{
+ return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
+ policy_type) >= 0);
+}
+
+/* valid_tlsrpt_policy_failure - validate policy_failure attribute value */
+
+int valid_tlsrpt_policy_failure(const char *policy_failure)
+{
+ return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
+ policy_failure) >= 0);
+}
+
+#if defined(USE_TLSRPT)
+
+/* convert_tlsrpt_policy_type - convert string to enum */
+
+tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type)
+{
+ return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE,
+ policy_type));
+}
+
+/* convert_tlsrpt_policy_failure - convert string to enum */
+
+tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure)
+{
+ return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE,
+ policy_failure));
+}
+
+#endif /* USE_TLSRPT */
+
+#endif /* USE_TLS */
diff --git a/postfix/src/tls/tlsrpt_wrapper.h b/postfix/src/tls/tlsrpt_wrapper.h
new file mode 100644
index 000000000..2107d3a3d
--- /dev/null
+++ b/postfix/src/tls/tlsrpt_wrapper.h
@@ -0,0 +1,122 @@
+#ifndef _TLSRPT_WRAPPER_INCLUDED_
+#define _TLSRPT_WRAPPER_INCLUDED_
+
+/*++
+/* NAME
+/* tlsrpt_wrapper 3h
+/* SUMMARY
+/* TLSRPT support for the TLS protocol engine
+/* SYNOPSIS
+/* #include
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#if defined(USE_TLS)
+
+#if defined(USE_TLSRPT)
+
+#include
+
+ /*
+ * External interface, with convenient setters for each SMTP protocol engine
+ * stage. Many functions have multiple arguments of the same type. Include
+ * parameter names in function prototypes here, and in call sites include
+ * comments before parameter values, to prepare for future clang-tidy
+ * bugprone-argument-comment checks.
+ */
+typedef struct TLSRPT_WRAPPER TLSRPT_WRAPPER;
+
+extern TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name,
+ const char *rpt_policy_domain,
+ const char *rpt_policy_string);
+extern void trw_free(TLSRPT_WRAPPER *trw);
+extern void trw_set_tls_policy(TLSRPT_WRAPPER *trw,
+ tlsrpt_policy_type_t tls_policy_type,
+ const char *const * tls_policy_strings,
+ const char *tls_policy_domain,
+ const char *const * mx_policy_patterns);
+extern void trw_set_tcp_connection(TLSRPT_WRAPPER *trw,
+ const char *snd_mta_addr,
+ const char *rcv_mta_name,
+ const char *rcv_mta_addr);
+extern void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw,
+ const char *rcv_mta_ehlo);
+extern int trw_report_failure(TLSRPT_WRAPPER *trw,
+ tlsrpt_failure_t policy_failure,
+ const char *additional_detail,
+ const char *failure_reason);
+extern int trw_report_success(TLSRPT_WRAPPER *trw);
+extern int trw_is_reported(const TLSRPT_WRAPPER *trw);
+
+ /*
+ * The internals declarations are also needed for functions that transmit
+ * and receive TLSRPT_WRAPPER objects.
+ */
+#ifdef TLSRPT_WRAPPER_INTERNAL
+
+ /*
+ * Utility library.
+ */
+#include
+
+struct TLSRPT_WRAPPER {
+ /* Set with trw_create(). */
+ char *rpt_socket_name;
+ char *rpt_policy_domain;
+ char *rpt_policy_string;
+ /* Set with trw_set_policy(). */
+ tlsrpt_policy_type_t tls_policy_type;
+ ARGV *tls_policy_strings;
+ char *tls_policy_domain;
+ ARGV *mx_host_patterns;
+ /* Set with trw_set_tcp_connection(). */
+ char *snd_mta_addr;
+ char *rcv_mta_name;
+ char *rcv_mta_addr;
+ /* Set with trw_set_ehlo_resp(). */
+ char *rcv_mta_ehlo;
+ int flags;
+};
+
+#define TRW_FLAG_HAVE_TLS_POLICY (1<<0)
+#define TRW_FLAG_HAVE_TCP_CONN (1<<1)
+#define TRW_FLAG_HAVE_EHLO_RESP (1<<2)
+#define TRW_FLAG_REPORTED (1<<3)
+
+#define TRW_RPT_SOCKET_NAME "rpt_socket_name"
+#define TRW_RPT_POLICY_DOMAIN "rpt_policy_domain"
+#define TRW_RPT_POLICY_STRING "rpt_policy_string"
+#define TRW_TLS_POLICY_TYPE "tls_policy_type"
+#define TRW_TLS_POLICY_STRINGS "tls_policy_strings" /* XXX Not checked */
+#define TRW_TLS_POLICY_DOMAIN "tls_policy_domain"
+#define TRW_MX_HOST_PATTERNS "mx_host_patterns" /* XXX Not checked */
+#define TRW_SRC_MTA_ADDR "snd_mta_addr"
+#define TRW_DST_MTA_NAME "rcv_mta_name"
+#define TRW_DST_MTA_ADDR "rcv_mta_addr"
+#define TRW_DST_MTA_EHLO "rcv_mta_ehlo" /* Optional */
+#define TRW_FLAGS "flags"
+
+#endif /* TLSRPT_WRAPPER_INTERNAL */
+
+extern tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type);
+extern tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure);
+
+#endif /* USE_TLSRPT */
+
+extern int valid_tlsrpt_policy_type(const char *policy_type);
+extern int valid_tlsrpt_policy_failure(const char *policy_failure);
+
+#endif /* USE_TLS */
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/*--*/
+
+#endif /* _TLSRPT_WRAPPER_INCLUDED_ */
diff --git a/postfix/src/tlsproxy/Makefile.in b/postfix/src/tlsproxy/Makefile.in
index 51120af06..2b6d34589 100644
--- a/postfix/src/tlsproxy/Makefile.in
+++ b/postfix/src/tlsproxy/Makefile.in
@@ -75,6 +75,7 @@ tlsproxy.o: ../../include/split_at.h
tlsproxy.o: ../../include/sys_defs.h
tlsproxy.o: ../../include/tls.h
tlsproxy.o: ../../include/tls_proxy.h
+tlsproxy.o: ../../include/tlsrpt_wrapper.h
tlsproxy.o: ../../include/vbuf.h
tlsproxy.o: ../../include/vstream.h
tlsproxy.o: ../../include/vstring.h
diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c
index 0ebf52c68..dd35d3e42 100644
--- a/postfix/src/tlsproxy/tlsproxy.c
+++ b/postfix/src/tlsproxy/tlsproxy.c
@@ -426,6 +426,7 @@
#define TLS_INTERNAL /* XXX */
#include
#include
+#include
/*
* Application-specific.
@@ -731,6 +732,19 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
state->flags |= TLSP_FLAG_NO_MORE_CIPHERTEXT_IO;
return (TLSP_STAT_OK);
}
+
+ /*
+ * Report a generic failure only if a more specific failure wasn't
+ * already reported.
+ */
+#ifdef USE_TLSRPT
+ if ((state->flags & TLSP_FLAG_DO_HANDSHAKE)
+ && state->is_server_role == 0)
+ trw_report_failure(state->client_start_props->tlsrpt,
+ TLSRPT_VALIDATION_FAILURE,
+ /* additional_detail= */ (char *) 0,
+ "tls-handshake-failure");
+#endif
tlsp_state_free(state);
return (TLSP_STAT_ERR);
}
diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in
index bd21e6af3..0bb2c689b 100644
--- a/postfix/src/util/Makefile.in
+++ b/postfix/src/util/Makefile.in
@@ -45,7 +45,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \
sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
- inet_addr_sizes.c quote_for_json.c
+ inet_addr_sizes.c quote_for_json.c mystrerror.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -92,7 +92,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
- quote_for_json.o
+ quote_for_json.o mystrerror.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@@ -2444,6 +2444,12 @@ mymalloc.o: sys_defs.h
myrand.o: myrand.c
myrand.o: myrand.h
myrand.o: sys_defs.h
+mystrerror.o: check_arg.h
+mystrerror.o: mystrerror.c
+mystrerror.o: stringops.h
+mystrerror.o: sys_defs.h
+mystrerror.o: vbuf.h
+mystrerror.o: vstring.h
mystrtok.o: check_arg.h
mystrtok.o: msg.h
mystrtok.o: mystrtok.c
@@ -2549,9 +2555,11 @@ printable.o: stringops.h
printable.o: sys_defs.h
printable.o: vbuf.h
printable.o: vstring.h
+quote_for_json.o: check_arg.h
quote_for_json.o: quote_for_json.c
quote_for_json.o: stringops.h
quote_for_json.o: sys_defs.h
+quote_for_json.o: vbuf.h
quote_for_json.o: vstring.h
rand_sleep.o: iostuff.h
rand_sleep.o: msg.h
@@ -2883,6 +2891,7 @@ vbuf.o: vbuf.h
vbuf_print.o: check_arg.h
vbuf_print.o: msg.h
vbuf_print.o: mymalloc.h
+vbuf_print.o: stringops.h
vbuf_print.o: sys_defs.h
vbuf_print.o: vbuf.h
vbuf_print.o: vbuf_print.c
diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c
index 332426e88..112603164 100644
--- a/postfix/src/util/argv.c
+++ b/postfix/src/util/argv.c
@@ -31,6 +31,10 @@
/* char *arg;
/* ssize_t arg_len;
/*
+/* ARGV *argv_addv(argvp, argv)
+/* ARGV *argvp;
+/* const char **argv;
+/*
/* void argv_terminate(argvp);
/* ARGV *argvp;
/*
@@ -94,6 +98,10 @@
/* argv_addn() is like argv_add(), but each string is followed
/* by a string length argument.
/*
+/* argv_addv() optionally creates an ARGV when the first argument
+/* is a null pointer, and appends a null-terminated list of
+/* strings. The result is null terminated.
+/*
/* argv_free() releases storage for a string array, and conveniently
/* returns a null pointer.
/*
@@ -301,6 +309,23 @@ void argv_addn(ARGV *argvp,...)
argvp->argv[argvp->argc] = 0;
}
+/* argv_addv - optionally create ARGV, append string vector */
+
+ARGV *argv_addv(ARGV *argvp, const char *const * argv)
+{
+ const char *const * cpp;
+
+ if (argvp == 0) {
+ for (cpp = argv; *cpp; cpp++)
+ /* void */ ;
+ argvp = argv_alloc(cpp - argv);
+ }
+ for (cpp = argv; *cpp; cpp++)
+ argv_add(argvp, *cpp, (char *) 0);
+ argvp->argv[argvp->argc] = 0;
+ return (argvp);
+}
+
/* argv_terminate - terminate string array */
void argv_terminate(ARGV *argvp)
@@ -602,6 +627,23 @@ static ARGV *test_argv_join(const TEST_CASE *tp, ARGV *argvp)
return (argvp);
}
+/* test_argv_addv_appends - populate result */
+
+static ARGV *test_argv_addv_appends(const TEST_CASE *tp, ARGV *argvp)
+{
+ argvp = argv_addv(argvp, tp->inputs);
+ return (argvp);
+}
+
+/* test_argv_addv_creates_appends - populate result */
+
+static ARGV *test_argv_addv_creates(const TEST_CASE *tp, ARGV *argvp)
+{
+ argv_free(argvp);
+ argvp = argv_addv((ARGV *) 0, tp->inputs);
+ return (argvp);
+}
+
/* test_argv_verify - verify result */
static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp)
@@ -737,6 +779,14 @@ static const TEST_CASE test_cases[] = {
{0}, 0, test_argv_join,
0, 1, {"", 0}, ':'
},
+ {"argv_addv appends to ARGV",
+ {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_appends,
+ 0, 3, {"foo", "baz", "bar", 0}
+ },
+ {"argv_addv creates ARGV",
+ {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_creates,
+ 0, 3, {"foo", "baz", "bar", 0}
+ },
0,
};
diff --git a/postfix/src/util/argv.h b/postfix/src/util/argv.h
index f1e746ad8..1c4790696 100644
--- a/postfix/src/util/argv.h
+++ b/postfix/src/util/argv.h
@@ -20,7 +20,7 @@ typedef struct ARGV {
char **argv; /* string array */
} ARGV;
-typedef int (*ARGV_COMPAR_FN)(const void *, const void *);
+typedef int (*ARGV_COMPAR_FN) (const void *, const void *);
extern ARGV *argv_alloc(ssize_t);
extern ARGV *argv_sort(ARGV *); /* backwards compatibility */
@@ -28,6 +28,7 @@ extern ARGV *argv_qsort(ARGV *, ARGV_COMPAR_FN);
extern ARGV *argv_uniq(ARGV *, ARGV_COMPAR_FN);
extern void argv_add(ARGV *,...);
extern void argv_addn(ARGV *,...);
+extern ARGV *argv_addv(ARGV *, const char *const *);
extern void argv_terminate(ARGV *);
extern void argv_truncate(ARGV *, ssize_t);
extern void argv_insert_one(ARGV *, ssize_t, const char *);
diff --git a/postfix/src/util/hex_code.c b/postfix/src/util/hex_code.c
index 3dfcb9818..6ea9a5161 100644
--- a/postfix/src/util/hex_code.c
+++ b/postfix/src/util/hex_code.c
@@ -44,6 +44,8 @@
/* functionality.
/* .IP HEX_ENCODE_FLAG_USE_COLON
/* Inserts one ":" between bytes.
+/* .IP HEX_ENCODE_FLAG_APPEND
+/* Append output to the buffer.
/* .PP
/* hex_decode_opt() enables extended functionality as controlled
/* with \fIflags\fR.
@@ -69,6 +71,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -107,7 +112,8 @@ VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
int ch;
ssize_t count;
- VSTRING_RESET(result);
+ if ((flags & HEX_ENCODE_FLAG_APPEND) == 0)
+ VSTRING_RESET(result);
for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) {
ch = *cp;
VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]);
diff --git a/postfix/src/util/hex_code.h b/postfix/src/util/hex_code.h
index 720977adb..ad923c9e2 100644
--- a/postfix/src/util/hex_code.h
+++ b/postfix/src/util/hex_code.h
@@ -21,6 +21,7 @@
*/
#define HEX_ENCODE_FLAG_NONE (0)
#define HEX_ENCODE_FLAG_USE_COLON (1<<0)
+#define HEX_ENCODE_FLAG_APPEND (1<<1)
#define HEX_DECODE_FLAG_NONE (0)
#define HEX_DECODE_FLAG_ALLOW_COLON (1<<0)
@@ -49,6 +50,9 @@ extern VSTRING *WARN_UNUSED_RESULT hex_decode_opt(VSTRING *, const char *, ssize
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
diff --git a/postfix/src/util/mystrerror.c b/postfix/src/util/mystrerror.c
new file mode 100644
index 000000000..0de07f124
--- /dev/null
+++ b/postfix/src/util/mystrerror.c
@@ -0,0 +1,39 @@
+/*++
+/* NAME
+/* mystrerror 3
+/* SUMMARY
+/* convert error number to string
+/* SYNOPSIS
+/* #include
+/*
+/* const char *mystrerror()
+/* int errnum)
+/* DESCRIPTION
+/* mystrerror() maps an error number to string. Unlike strerror(3)
+/* it returns "Application error" instead of "Success".
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+ /*
+ * System library.
+ */
+#include
+#include
+
+ /*
+ * Utility library.
+ */
+#include
+
+/* mystrerror - convert error number to string */
+
+const char *mystrerror(int errnum)
+{
+ return (errnum ? strerror(errnum) : "Application error");
+}
diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h
index db56f237a..4c357c81e 100644
--- a/postfix/src/util/stringops.h
+++ b/postfix/src/util/stringops.h
@@ -67,6 +67,7 @@ extern int strcasecmp_utf8x(int, const char *, const char *);
extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t);
extern char *quote_for_json(VSTRING *, const char *, ssize_t);
extern char *quote_for_json_append(VSTRING *, const char *, ssize_t);
+extern const char *mystrerror(int);
#define EXTPAR_FLAG_NONE (0)
#define EXTPAR_FLAG_STRIP (1<<0) /* "{ text }" -> "text" */
diff --git a/postfix/src/util/vbuf_print.c b/postfix/src/util/vbuf_print.c
index d7a323f24..2e3266ed7 100644
--- a/postfix/src/util/vbuf_print.c
+++ b/postfix/src/util/vbuf_print.c
@@ -67,6 +67,7 @@
#include "mymalloc.h"
#include "vbuf.h"
#include "vstring.h"
+#include "stringops.h"
#include "vbuf_print.h"
/*
@@ -290,8 +291,7 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
break;
case 'm':
/* Ignore the 'l' modifier, width and precision. */
- VBUF_STRCAT(bp, saved_errno ?
- strerror(saved_errno) : "Application error");
+ VBUF_STRCAT(bp, mystrerror(saved_errno));
break;
case 'p':
if (long_flag)