diff --git a/postfix/.indent.pro b/postfix/.indent.pro
index 8ee03dbe7..d14b89c22 100644
--- a/postfix/.indent.pro
+++ b/postfix/.indent.pro
@@ -360,6 +360,7 @@
-TTEST_CASE
-TTLSMGR_SCACHE
-TTLSP_STATE
+-TTLSRPT_WRAPPER
-TTLS_APPL_STATE
-TTLS_CERTS
-TTLS_CLIENT_INIT_PROPS
diff --git a/postfix/HISTORY b/postfix/HISTORY
index dcf6e2a54..d99d6aa8c 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -28186,6 +28186,27 @@ Apologies for any names omitted.
20240808
Typofix in comment. File: global/normalize_mailhost_addr.c.
+
+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];
20240831
@@ -28237,7 +28258,46 @@ Apologies for any names omitted.
OpenSSL 3.2) SSL_get0_group_name() function. Viktor Dukhovni.
Files: src/tls/tls.h, src/tls/tls_dh.c, src/tls/tls_misc.c.
+20240923
+
+ Cleanup: No user-visible change. Updated TLSRPT related
+ internal comments and internal identifiers; updated error
+ logging after changes in libtlsrpt error-to-string conversion
+ functions; minor changes to improve robustness.
+
20240924
Misc. cleanups. Viktor Dukhovni. Files: src/tls/tls_dh.c,
proto/postconf.proto, src/global/mail_params.h.
+
+ TLSRPT documentation cleanups. File: proto/TLSRPT_README.html.
+
+20240924
+
+ Code health: added proper unit tests to hex_code.c.
+
+ Code health: deduplicated code instances that convert an
+ IPv4-in-IPv6 address (::ffff:a.b.c.d) to IPv4 form, but
+ only if IPv4 support is enabled. Files: myaddrinfo,h,
+ sane_sockaddr_to_hostaddr.c, global/haproxy_srvr.c,
+ postscreen/postscreen_endpt.c, smtp/smtp_tlsrpt.c.
+
+20240925
+
+ TLSRPT support: add routine logging of TLSRPT 'success' and
+ 'failure' events, as there is no other easy way to find out
+ what the Postfix TLSRPT client is doing. Document what this
+ logging looks like, that it is not logged for reused TCP
+ connections, and how to recognize such connections. Files:
+ tls/tlsrpt_wrapper.c, proto/TLSRPT_README.html.
+
+ TLSRPT support: simplified the handling of reused TLS
+ sessions. Report all TLS handshakes regardless of whether
+ or not a session is reused. Note that there is only one TLS
+ handshake for a reused SMTP connection. Files: smtp/smtp_proto.c,
+ tls/tls_client.c, proto/TLSRPT_README.html.
+
+ Cleanup: pre-release checks for snapshot and nonprod releases.
+ File: mantools/check-snapshot-nonprod.
+
+ First general Postfix release with TLSRPT support.
diff --git a/postfix/Makefile.in b/postfix/Makefile.in
index 6f2d06491..d7152b57e 100644
--- a/postfix/Makefile.in
+++ b/postfix/Makefile.in
@@ -115,9 +115,9 @@ manpages:
done library --> generator --> delivery
+ engines
+
+When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable = yes"):
+
+ * The Postfix SMTP and TLS client engines will generate a "success" or
+ "failure" event for each TLS handshake,
+
+ * They will pass those events to an in-process TLSRPT client library that
+ sends data over a local socket to
+
+ * A TLSRPT report generator that produces daily summary reports.
+
+The TLSRPT client library and report generator 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 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 -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:
+
+ 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 settings,
+ then simply append the above settings to the existing CCARGS or AUXLIBS
+ values.
+
+ make -f Makefile.init makefiles \
+ "CCARGS=existing settings... -DUSE_TLSRPT -I/usr/local/include" \
+ "AUXLIBS=existing settings... -L/usr/local/lib -ltlsrpt"
+
+TTuurrnniinngg oonn 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.
+
+ Note: the recommended socket location is still to be determined. A good
+ socket location would be under the Postfix queue directory, for example:
+ "smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock". The advantage of using
+ a relative name is that it will work equally well whether or not Postfix
+ chroot is turned on.
+
+Do not specify a location under a directory such as private or public that is
+already used by Postfix programs. Only Postfix programs should create sockets
+there.
+
+TTLLSSRRPPTT SSttaattuuss llooggggiinngg
+
+With TLSRPT support turned on, the Postfix TLSRPT client will not only report
+an event to an invisible daily success/fail summary queue, but it will also log
+a visible record to the mail logfile.
+
+Below are a few examples of logging from a Postfix SMTP client or tlsproxy
+daemon:
+
+ TLSRPT: status=success, domain=example.com, receiving_mx=mail.example.com
+ [ipaddr]
+
+ TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
+ [ipaddr],
+ failure_type=starttls_not_supported
+
+ TLSRPT: status=failure, domain=example.net, receiving_mx=mail.example.net
+ [ipaddr],
+ failure_type=validation_failure, failure_reason=self-signed_certificate
+
+Note: Postfix logs and reports TLSRPT status only for TLS handshakes on a new
+SMTP connection. There is no TLSRPT status logging for a reused SMTP
+connection. Such connections have Postfix SMTP client logging like this:
+
+ Verified TTLLSS ccoonnnneeccttiioonn rreeuusseedd to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+ Untrusted TTLLSS ccoonnnneeccttiioonn rreeuusseedd to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+Postfix logs certificate verification failures with a level of detail that is
+different for a new or reused TLS session.
+
+ * A new TLS session is logged with certificate verification failure details:
+
+ TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
+ [ipaddr],
+ ffaaiilluurree__ttyyppee==vvaalliiddaattiioonn__ffaaiilluurree, ffaaiilluurree__rreeaassoonn==sseellff--ssiiggnneedd__cceerrttiiffiiccaattee
+
+ * A reused TLS session is indicated as shown below, and has no certificate
+ verification details:
+
+ mail.example.org[ipaddr]:25: rree--uussiinngg sseessssiioonn with untrusted peer
+ credential, look for details earlier in the log
+ TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
+ [ipaddr],
+ ffaaiilluurree__ttyyppee==cceerrttiiffiiccaattee__nnoott__ttrruusstteedd
+
+Some Postfix users may wonder where the difference comes from. So this is why.
+
+DDeelliivveerriinngg TTLLSSRRPPTT ssuummmmaarriieess vviiaa eemmaaiill
+
+RFC 8460 suggests not to enforce strict TLS security when sending daily
+success/failure summaries via email, to avoid delivery delays caused by a
+failure to enforce TLS security. Postfix currently does not have a mechanism to
+disable TLS security enforcement when submitting an email message; this section
+provides a workaround.
+
+By design, TLSRPT is not a real-time notification system; it takes on average
+12 hours before a failure is reported in a daily success/failure summary. If a
+TLS-related delay of a day or more is undesirable, one could set up a transport
+map to make TLS security optional for specific TLSRPT email notification email
+addresses.
+
+ /etc/postfix/main.cf:
+ transport_maps = hash:/etc/postfix/transport
+
+ /etc/postfix/transport:
+ smtp-tls-report@example.com allow-plaintext:
+ ...
+
+ /etc/postfix/master.cf:
+ # service name type private unpriv chroot wakeup maxproc
+ command
+ allow-plaintext unix - - n - - smtp
+ -o smtp_tls_security_level=may
+ -o smtp_tls_policy_maps=static:may
+
+MMTTAA--SSTTSS SSuuppppoorrtt vviiaa ssmmttpp__ttllss__ppoolliiccyy__mmaappss
+
+Postfix supports MTA-STS though an smtp_tls_policy_maps policy plugin. Postfix
+3.10 and later expect a policy 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).
+ With TLSRPT support turned off, Postfix will use the ttl and policy_failure
+ attributes, and will ignore the attributes that are used only for TLSRPT.
+
+ 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 Postfix TLSRPT implementation reports at most one final TLS handshake
+status (either 'success' or 'failure') per connection. Postfix TLSRPT cannot
+report a failure and then later report a final status of 'success' for that
+same connection. The reason is that it's too complicated to filter TLS errors
+and to report error details from the TLS engine back to the SMTP protocol
+engine. It just is not how Postfix works internally.
+
+The Postfix TLSRPT implementation reports only TLS handshake success or
+failure. It does not report failure to connect, or connections that break after
+a successful TLS handshake.
+
+CCrreeddiittss
+
+ * The TLSRPT client library and report generator are implemented and
+ maintained by sys4.
+ * Wietse Venema implemented the integration with Postfix.
+
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index cf7bfc4e0..660f33511 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -25,3 +25,13 @@ now also distributed with the more recent Eclipse Public License
(EPL) 2.0. Recipients can choose to take the software under the
license of their choice. Those who are more comfortable with the
IPL can continue with that license.
+
+[Feature 20240926]
+
+Support for the TLSRPT protocol (defined in RFC 8460). With this,
+an email receiving domain can publish a policy in DNS, and request
+daily summary reports for successful and failed SMTP-over-TLS
+connections to that domain's MX hosts.
+
+Postfix supports TLSRPT summaries for DANE (built-in) and MTA-STS
+(via an smtp_tls_policy_maps plugin). For details, see TLSRPT_README.
diff --git a/postfix/WISHLIST b/postfix/WISHLIST
index baa23fdd1..721fc9a58 100644
--- a/postfix/WISHLIST
+++ b/postfix/WISHLIST
@@ -6,6 +6,11 @@ Wish list:
Disable -DSNAPSHOT and -DNONPROD in makedefs.
+ Add smtp_tlsrpt_allow_list feature (default: static:all) to limit
+ the domains for which Postfix generates TLSRPT daily sumamries.
+
+ Add unit tests for smtp_tlsrpt.c, tlstrpd_wrapper.c, ...
+
Add tests for Message-ID extraction in the cleanup daemon.
When debug logging is enabled, dict_db_open() logs a newline
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/TLSRPT_README.html b/postfix/html/TLSRPT_README.html
new file mode 100644
index 000000000..9e6e6e073
--- /dev/null
+++ b/postfix/html/TLSRPT_README.html
@@ -0,0 +1,410 @@
+
+
+
+
+
The TLSRPT protocol is defined in RFC 8460. With this, an email
+receiving domain can publish a policy in DNS, and request daily
+summary reports for successful and failed SMTP over TLS connections
+to that domain's MX hosts. Support for TLSRPT was added in Postfix
+3.10.
+
+
A policy example looks like this:
+
+
+
+_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com"
+
+
+
+
Translation: email sending systems are requested to generate daily
+summaries of successful and failed SMTP over TLS connections to domain
+example.com, and to report those summaries via email to the
+specified address. Instead of mailto:, a policy may specify an
+https: destination.
+
+
The high-level diagram shows how Postfix reports summaries to
+domains that publish a TLSRPT policy.
+
+
+
+
+
+
Postfix SMTP and TLS
+client engines
-->
+
+
TLSRPT client library
+
-->
+
+
TLSRPT summary generator
+
-->
+
+
Email or HTTP delivery
+
+
+
+
+
+
+
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable
+= yes"):
+
+
+
+
The Postfix SMTP and TLS client engines will generate a
+"success" or "failure" event for each TLS handshake,
+
+
They will pass those events to an in-process TLSRPT client
+library that sends data over a local socket to
+
+
A TLSRPT report generator that produces daily summary
+reports.
+
+
+
+
The TLSRPT client library and report generator 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 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
+settings, then simply append the above settings to the existing CCARGS
+or AUXLIBS values.
Note: the recommended socket location is still to be determined.
+A good socket location would be under the Postfix queue directory,
+for example: "smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock".
+The advantage of using a relative name is that it will work equally
+well whether or not Postfix chroot is turned on.
+
+
+
+
Do not specify a location under a directory such as private
+or public that is already used by Postfix programs. Only Postfix
+programs should create sockets there.
With TLSRPT support turned on, the Postfix TLSRPT client will
+not only report an event to an invisible daily success/fail summary
+queue, but it will also log a visible record to the mail logfile.
+
+
+
Below are a few examples of logging from a Postfix SMTP client
+or tlsproxy daemon:
Note: Postfix logs and reports TLSRPT status only for TLS
+handshakes on a new SMTP connection. There is no TLSRPT status
+logging for a reused SMTP connection. Such connections have
+Postfix SMTP client logging like this:
+
+
+
+Verified TLS connection reused to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+Untrusted TLS connection reused to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+
+
+
Postfix logs certificate verification failures with a level of
+detail that is different for a new or reused TLS session.
+
+
+
+
A new TLS session is logged with certificate verification
+failure details:
RFC 8460 suggests not to enforce strict TLS security when sending
+daily success/failure summaries via email, to avoid delivery delays
+caused by a failure to enforce TLS security. Postfix currently does
+not have a mechanism to disable TLS security enforcement when
+submitting an email message; this section provides a workaround.
+
+
By design, TLSRPT is not a real-time notification system; it
+takes on average 12 hours before a failure is reported in a daily
+success/failure summary. If a TLS-related delay of a day or more
+is undesirable, one could set up a transport map to make TLS security
+optional for specific TLSRPT email notification email addresses.
+
Postfix supports MTA-STS though an smtp_tls_policy_maps
+policy plugin. Postfix 3.10 and later expect a policy 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). With TLSRPT support turned off, Postfix
+will use the ttl and policy_failure attributes,
+and will ignore the attributes that are used only for TLSRPT.
+
+
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 Postfix TLSRPT implementation reports at most one final TLS
+handshake status (either 'success' or 'failure') per connection.
+Postfix TLSRPT cannot report a failure and then later report a final
+status of 'success' for that same connection. The reason is that
+it's too complicated to filter TLS errors and to report error details
+from the TLS engine back to the SMTP protocol engine. It just is
+not how Postfix works internally.
+
+
The Postfix TLSRPT implementation reports only TLS handshake
+success or failure. It does not report failure to connect, or
+connections that break after a successful TLS handshake.
SMTPUTF8 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/postcat.1.html b/postfix/html/postcat.1.html
index 0d228d48a..319c0d90a 100644
--- a/postfix/html/postcat.1.html
+++ b/postfix/html/postcat.1.html
@@ -43,6 +43,8 @@ POSTCAT(1) POSTCAT(1)
-f Prepend the file name to each output line.
+ This feature is available in Postfix 3.10 and later.
+
-h Show message header content. The -h option produces output from
the beginning of the message up to, but not including, the first
non-header line.
diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html
index d3fbf24e3..b703e6601 100644
--- a/postfix/html/postconf.5.html
+++ b/postfix/html/postconf.5.html
@@ -14800,6 +14800,33 @@ Postfix versions.
This feature is available in Postfix 3.0 and later.
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
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/man/man1/postcat.1 b/postfix/man/man1/postcat.1
index ae4ef526c..0b089feb5 100644
--- a/postfix/man/man1/postcat.1
+++ b/postfix/man/man1/postcat.1
@@ -41,6 +41,8 @@ Show message envelope content.
This feature is available in Postfix 2.7 and later.
.IP \fB\-f\fR
Prepend the file name to each output line.
+.sp
+This feature is available in Postfix 3.10 and later.
.IP \fB\-h\fR
Show message header content. The \fB\-h\fR option produces
output from the beginning of the message up to, but not
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5
index 339b38853..a9cf9d060 100644
--- a/postfix/man/man5/postconf.5
+++ b/postfix/man/man5/postconf.5
@@ -9860,6 +9860,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:
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/check-snapshot-nonprod b/postfix/mantools/check-snapshot-nonprod
index b3a038f9b..9ca642b3c 100755
--- a/postfix/mantools/check-snapshot-nonprod
+++ b/postfix/mantools/check-snapshot-nonprod
@@ -6,9 +6,23 @@ postfix-[0-9]*.[0-9]*.[0-9]*)
test -f conf/makedefs.out || {
echo "Error: no conf/makedefs.out" 1>&2; exit 1; }
grep 'CCARGS.*-DSNAPSHOT' conf/makedefs.out && {
- echo "Error: stable release builds with -DSNAPSHOT" 1>&2, exit 1; }
+ echo "Error: stable release builds with -DSNAPSHOT" 1>&2; exit 1; }
grep 'CCARGS.*-DNONPROD' conf/makedefs.out && {
- echo "Error: stable release builds with -DNONPROD" 1>&2, exit 1; }
+ echo "Error: stable release builds with -DNONPROD" 1>&2; exit 1; }
+ mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
+ test "postfix-$mail_version" = "$version" || {
+ echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
+ ;;
+postfix-[0-9]*.[0-9]*-*nonprod*)
+ grep 'CCARGS.*-DNONPROD' conf/makedefs.out || {
+ echo "Error: non-prod release builds without -DNONPROD" 1>&2; exit 1; }
+ mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
+ test "postfix-$mail_version" = "$version" || {
+ echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
+ ;;
+postfix-[0-9]*.[0-9]*-*)
+ grep 'CCARGS.*-DNONPROD' conf/makedefs.out && {
+ echo "Error: snapshot release builds with -DNONPROD" 1>&2; exit 1; }
mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
test "postfix-$mail_version" = "$version" || {
echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
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..8210eae05
--- /dev/null
+++ b/postfix/proto/TLSRPT_README.html
@@ -0,0 +1,410 @@
+
+
+
+
+
+
+Postfix TLSRPT notification Howto
+
+
+
+
+
+
+
+
+
The TLSRPT protocol is defined in RFC 8460. With this, an email
+receiving domain can publish a policy in DNS, and request daily
+summary reports for successful and failed SMTP over TLS connections
+to that domain's MX hosts. Support for TLSRPT was added in Postfix
+3.10.
+
+
A policy example looks like this:
+
+
+
+_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com"
+
+
+
+
Translation: email sending systems are requested to generate daily
+summaries of successful and failed SMTP over TLS connections to domain
+example.com, and to report those summaries via email to the
+specified address. Instead of mailto:, a policy may specify an
+https: destination.
+
+
The high-level diagram shows how Postfix reports summaries to
+domains that publish a TLSRPT policy.
+
+
+
+
+
+
Postfix SMTP and TLS
+client engines
-->
+
+
TLSRPT client library
+
-->
+
+
TLSRPT summary generator
+
-->
+
+
Email or HTTP delivery
+
+
+
+
+
+
+
When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable
+= yes"):
+
+
+
+
The Postfix SMTP and TLS client engines will generate a
+"success" or "failure" event for each TLS handshake,
+
+
They will pass those events to an in-process TLSRPT client
+library that sends data over a local socket to
+
+
A TLSRPT report generator that produces daily summary
+reports.
+
+
+
+
The TLSRPT client library and report generator 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 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
+settings, then simply append the above settings 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.
+
+
+
+
Note: the recommended socket location is still to be determined.
+A good socket location would be under the Postfix queue directory,
+for example: "smtp_tlsrpt_socket_name = run/tlsrpt/tlsrpt.sock".
+The advantage of using a relative name is that it will work equally
+well whether or not Postfix chroot is turned on.
+
+
+
+
Do not specify a location under a directory such as private
+or public that is already used by Postfix programs. Only Postfix
+programs should create sockets there.
With TLSRPT support turned on, the Postfix TLSRPT client will
+not only report an event to an invisible daily success/fail summary
+queue, but it will also log a visible record to the mail logfile.
+
+
+
Below are a few examples of logging from a Postfix SMTP client
+or tlsproxy daemon:
Note: Postfix logs and reports TLSRPT status only for TLS
+handshakes on a new SMTP connection. There is no TLSRPT status
+logging for a reused SMTP connection. Such connections have
+Postfix SMTP client logging like this:
+
+
+
+Verified TLS connection reused to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+Untrusted TLS connection reused to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+
+
+
Postfix logs certificate verification failures with a level of
+detail that is different for a new or reused TLS session.
+
+
+
+
A new TLS session is logged with certificate verification
+failure details:
RFC 8460 suggests not to enforce strict TLS security when sending
+daily success/failure summaries via email, to avoid delivery delays
+caused by a failure to enforce TLS security. Postfix currently does
+not have a mechanism to disable TLS security enforcement when
+submitting an email message; this section provides a workaround.
+
+
By design, TLSRPT is not a real-time notification system; it
+takes on average 12 hours before a failure is reported in a daily
+success/failure summary. If a TLS-related delay of a day or more
+is undesirable, one could set up a transport map to make TLS security
+optional for specific TLSRPT email notification email addresses.
+
Postfix supports MTA-STS though an smtp_tls_policy_maps
+policy plugin. Postfix 3.10 and later expect a policy 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). With TLSRPT support turned off, Postfix
+will use the ttl and policy_failure attributes,
+and will ignore the attributes that are used only for TLSRPT.
+
+
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 Postfix TLSRPT implementation reports at most one final TLS
+handshake status (either 'success' or 'failure') per connection.
+Postfix TLSRPT cannot report a failure and then later report a final
+status of 'success' for that same connection. The reason is that
+it's too complicated to filter TLS errors and to report error details
+from the TLS engine back to the SMTP protocol engine. It just is
+not how Postfix works internally.
+
+
The Postfix TLSRPT implementation reports only TLS handshake
+success or failure. It does not report failure to connect, or
+connections that break after a successful TLS handshake.
The TLSRPT client library and report generator are implemented
+and maintained by sys4.
+
+
Wietse Venema implemented the integration with Postfix.
+
+
+
+
+
+
+
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index d8e908479..0e6cd26b5 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -19408,3 +19408,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 03c799cfa..4755ed24f 100644
--- a/postfix/proto/stop
+++ b/postfix/proto/stop
@@ -1623,3 +1623,31 @@ KEMs
kex
keyshare
pkg
+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
+mailto
+ipaddr
+STS
diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc
index bff4534d1..d0e35864a 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_info additional_info
+ignored ignored
+USE_TLSRPT USE_TLSRPT
diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html
index a4b2332a2..9fb66ec65 100644
--- a/postfix/proto/stop.double-proto-html
+++ b/postfix/proto/stop.double-proto-html
@@ -358,3 +358,8 @@ 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
+ var run tlsrpt tlsrpt sock Relative names will work with and without Postfix chroot support Do not specify a location under a directory such as private or public that is already used by Postfix programs Only Postfix programs should create
+ Note the recommended socket location is still to be determined A good socket location would be under the Postfix queue directory for example smtp_tlsrpt_socket_name run tlsrpt tlsrpt sock The advantage of using a relative name is that
+ with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits
+ TLSv1 2 with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits
diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc
index 7397881e6..e2d5861e3 100644
--- a/postfix/proto/stop.spell-cc
+++ b/postfix/proto/stop.spell-cc
@@ -1841,4 +1841,9 @@ foqvx
ILP
xxfi
optionsv
+rcv
+snd
+sts
+tlsrprt
bdefhnoqv
+deduplicated
diff --git a/postfix/proto/stop.spell-proto-html b/postfix/proto/stop.spell-proto-html
index e76dbfa4d..0bb4f3963 100644
--- a/postfix/proto/stop.spell-proto-html
+++ b/postfix/proto/stop.spell-proto-html
@@ -379,3 +379,11 @@ Dextrous
ar
liveness
superset
+ltlsrpt
+sts
+STS
+STSv
+Sys
+Qsmtp
+Qsts
+gmail
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/haproxy_srvr.c b/postfix/src/global/haproxy_srvr.c
index 63147c1c5..211cf1d1a 100644
--- a/postfix/src/global/haproxy_srvr.c
+++ b/postfix/src/global/haproxy_srvr.c
@@ -250,15 +250,12 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
}
if (err == 0)
err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
- || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
- addr, (MAI_SERVPORT_STR *) 0, 0));
+ || sane_sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
+ addr, (MAI_SERVPORT_STR *) 0, 0));
if (res)
freeaddrinfo(res);
if (err)
return (-1);
- if (addr->buf[0] == ':' && strncasecmp("::ffff:", addr->buf, 7) == 0
- && strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
- memmove(addr->buf, addr->buf + 7, strlen(addr->buf) + 1 - 7);
return (0);
}
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 908341ae3..d209bc036 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -4459,6 +4459,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
@@ -4473,6 +4489,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/global/mail_version.h b/postfix/src/global/mail_version.h
index 3acccc6f6..56082f7d9 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20240924"
+#define MAIL_RELEASE_DATE "20240926"
#define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT
diff --git a/postfix/src/postcat/postcat.c b/postfix/src/postcat/postcat.c
index 1461578fc..44c0ce6ba 100644
--- a/postfix/src/postcat/postcat.c
+++ b/postfix/src/postcat/postcat.c
@@ -35,6 +35,8 @@
/* This feature is available in Postfix 2.7 and later.
/* .IP \fB-f\fR
/* Prepend the file name to each output line.
+/* .sp
+/* This feature is available in Postfix 3.10 and later.
/* .IP \fB-h\fR
/* Show message header content. The \fB-h\fR option produces
/* output from the beginning of the message up to, but not
diff --git a/postfix/src/postscreen/postscreen_endpt.c b/postfix/src/postscreen/postscreen_endpt.c
index 36949e32f..46e657994 100644
--- a/postfix/src/postscreen/postscreen_endpt.c
+++ b/postfix/src/postscreen/postscreen_endpt.c
@@ -110,25 +110,6 @@
static const INET_PROTO_INFO *proto_info;
-/* psc_sockaddr_to_hostaddr - transform endpoint address and port to string */
-
-static int psc_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 ((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);
-}
-
/* psc_endpt_local_lookup - look up local system connection information */
void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
@@ -156,7 +137,7 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
* Convert the remote SMTP client address and port to printable form for
* logging and access control.
*/
- else if ((aierr = psc_sockaddr_to_hostaddr(
+ else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_client_addr,
&smtp_client_port, SOCK_STREAM)) != 0) {
@@ -180,7 +161,7 @@ void psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
* Convert the local SMTP server address and port to printable form for
* logging.
*/
- else if ((aierr = psc_sockaddr_to_hostaddr(
+ else if ((aierr = sane_sockaddr_to_hostaddr(
(struct sockaddr *) &addr_storage,
addr_storage_len, &smtp_server_addr,
&smtp_server_port, SOCK_STREAM)) != 0) {
diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c
index b474a4006..75571f7f2 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,
+ ffail_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,
+ ffail_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..23ce94ebf 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;
@@ -1399,7 +1407,23 @@ static void post_init(char *unused_name, char **argv)
var_disable_dns = (smtp_dns_support == SMTP_DNS_DISABLED);
}
+#if !defined(USE_TLS) || !defined(USE_TLSRPT)
+ if (var_smtp_tlsrpt_enable)
+ msg_warn("TLSRPT is selected, but TLSRPT is not compiled in");
+#endif
#ifdef USE_TLS
+#ifdef USE_TLSRPT
+ if (var_smtp_tlsrpt_enable) {
+ if (smtp_mode) {
+ if (smtp_tlsrpt_post_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;
+ }
+ }
+#endif /* USE_TLSRPT */
if (smtp_mode) {
smtp_tls_insecure_mx_policy =
tls_level_lookup(var_smtp_tls_insecure_mx_policy);
diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h
index 60c68f845..49e111542 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_post_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..ea943fe77 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,18 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
session->state = state;
#ifdef USE_TLS
session->tls_nexthop = domain;
+
+ /*
+ * Update TLSRPT state even if this is a reused SMTP
+ * connection. If for some unlikely reason we must report a
+ * problem, then we must report correct information.
+ */
+#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..1b0222c17 100644
--- a/postfix/src/smtp/smtp_proto.c
+++ b/postfix/src/smtp/smtp_proto.c
@@ -78,6 +78,9 @@
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
+/* Wietse Venema
+/* porcupine.org
+/*
/* Pipelining code in cooperation with:
/* Jon Ribbens
/* Oaktree Internet Solutions Ltd.,
@@ -155,6 +158,9 @@
#include
#include
#include
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+#include
+#endif
/* Application-specific. */
@@ -475,6 +481,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)
+ smtp_tlsrpt_set_ehlo_resp(state, resp->str);
+#endif
}
if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
where = "performing the HELO handshake";
@@ -484,6 +495,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 +813,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_info= */ (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 +838,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_info= */ (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 +972,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
+ ffail_type = 0,
dane = state->tls->dane);
/*
@@ -1065,6 +1101,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
+ ffail_type = state->tls->ext_policy_failure,
dane = state->tls->dane);
/*
@@ -1125,10 +1167,43 @@ 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' event 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_info= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+ } else {
+ (void) trw_report_failure(state->tlsrpt,
+ TLSRPT_CERTIFICATE_HOST_MISMATCH,
+ /* additional_info= */ (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' event only if the TLS engine has not created
+ * TLSRPT event. For example, The TLS engine will create a TLSRPT
+ * 'failure' event when the TLS handshake was be successful, but the
+ * security level was downgraded from opportunistic "dane" to
+ * unauthenticated "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..ec8cc2df3 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 USE_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..501509496 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,98 @@ 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;
+ }
+ /* Only one instance per policy. */
+ 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 != EXT_POLICY_TTL_UNSET
+ || 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 +811,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..cfb8c5545
--- /dev/null
+++ b/postfix/src/smtp/smtp_tlsrpt.c
@@ -0,0 +1,414 @@
+/*++
+/* NAME
+/* smtp_tlsrpt 3
+/* SUMMARY
+/* TLSRPT support for the SMTP protocol engine
+/* SYNOPSIS
+/* #include
+/*
+/* int smtp_tlsrpt_post_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 populates a TLSRPT_WRAPPER object with a)
+/* remote TLSRPT policy information, b) remote TLSA or STS policy
+/* information, and c) selected SMTP connection information. This
+/* object is passed to a TLS protocol engine, which may run in a
+/* different process than the SMTP protocol engine. The TLS protocol
+/* engine uses the TLSRPT_WRAPPER object to report a TLS handshake
+/* error to a TLSRPT library. The SMTP protocol engine uses the
+/* object to report a TLS handshake error or success.
+/*
+/* smtp_tls_post_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 name is used in a
+/* diagnostic message.
+/* .IP sockname_pval
+/* The value of said parameter.
+/* .PP
+/* smtp_tlsrpt_create_wrapper() destroys a 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 for 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
+/* (the U-label form will be converted to A-label internally).
+/* .PP
+/* smtp_tlsrpt_set_tls_policy() updates the TLSRPT_WRAPPER
+/* object 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
+/* object with TCP connection properties.
+/* .PP
+/* smtp_tlsrpt_set_ehlo_resp() updates the TLSRPT_WRAPPER object
+/* 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_post_jail - post-jail configuration sanity check */
+
+int smtp_tlsrpt_post_jail(const char *sockname_pname,
+ const char *sockname_pval)
+{
+ if (smtp_dns_support == SMTP_DNS_DISABLED) {
+ msg_warn("Cannot enable %s: DNS is disabled", smtp_tlsrpt_support);
+ 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 code that actually
+ * needs it.
+ */
+#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-TLSRPTv1 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 TLSRPT policies for %s",
+ smtp_tlsrpt_support, adomain);
+ dns_rr_free(rr_result);
+ rr_result = 0;
+ 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_no_policy - no policy found */
+
+static void smtp_tlsrpt_set_no_policy(SMTP_STATE *state)
+{
+ trw_set_tls_policy(state->tlsrpt, TLSRPT_NO_POLICY_FOUND,
+ /* tls_policy_strings= */ (const char *const *) 0,
+ /* tls_policy_domain= */ (char *) 0,
+ /* mx_host_patterns= */ (const char *const *) 0);
+}
+
+/* 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;
+
+ if (tls->ext_policy_type == 0)
+ msg_panic("smtp_tlsrpt_set_ext_policy: no policy type");
+
+ 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:
+ smtp_tlsrpt_set_no_policy(state);
+ break;
+ default:
+ /* Policy type must be validated in smtp_tls_policy_maps parser. */
+ 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 /* No policy */
+ smtp_tlsrpt_set_no_policy(state);
+ } else if (tls->ext_policy_type) {
+ smtp_tlsrpt_set_ext_policy(state);
+ } else {
+ smtp_tlsrpt_set_no_policy(state);
+ }
+}
+
+/* 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 server-side 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 2e5bbd3d8..ce1a37ee9 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
@@ -366,6 +367,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
@@ -387,6 +389,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
@@ -588,3 +591,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 406246fd5..96eb5a4b3 100644
--- a/postfix/src/tls/tls.h
+++ b/postfix/src/tls/tls.h
@@ -214,7 +214,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 *);
@@ -270,6 +270,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 *ffail_type; /* Forced verification failure */
} TLS_SESS_STATE;
/*
@@ -502,6 +504,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 *ffail_type; /* Forced verification failure */
} TLS_CLIENT_START_PROPS;
extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@@ -525,12 +529,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
@@ -663,7 +668,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..a635767dc 100644
--- a/postfix/src/tls/tls_client.c
+++ b/postfix/src/tls/tls_client.c
@@ -152,6 +152,9 @@
/*
/* Victor Duchovni
/* Morgan Stanley
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -160,6 +163,7 @@
#ifdef USE_TLS
#include
+#include
#ifdef STRCASECMP_IN_STRINGS_H
#include
@@ -363,7 +367,7 @@ 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);
@@ -407,7 +411,7 @@ 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);
@@ -579,6 +583,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 +600,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 +614,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 +629,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 +641,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 +984,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 +1016,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 +1033,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 +1048,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 +1082,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 +1119,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_info= */ (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 +1133,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_info= */ (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_info= */ (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_info= */ (char *) 0,
+ "all-trust-anchors-unusable");
+ }
+#endif
msg_warn("%s: all trust anchors unusable", TLScontext->namaddr);
break;
}
@@ -1194,6 +1236,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 +1256,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->ffail_type) {
+ TLScontext->ffail_type = mystrdup(props->ffail_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 +1298,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_info= */ (char *) 0,
+ "tls-handshake-failure");
+#endif
uncache_session(app_ctx->ssl_ctx, TLScontext);
tls_free_context(TLScontext);
return (0);
@@ -1360,6 +1419,19 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext,
tls_int_seed();
+ /*
+ * Precondition: tls_client_start() is called only for a new TCP
+ * connection. It is never called for a reused TCP connection.
+ *
+ * Inform the caller that they should not generate a TLSRPT 'success' or
+ * 'failure' event: this TLS protocol engine has already generated a
+ * TLSRPT 'failure' event for this session.
+ */
+#ifdef USE_TLSRPT
+ TLScontext->rpt_reported = props->tlsrpt != 0
+ && trw_is_reported(props->tlsrpt);
+#endif
+
return (TLScontext);
}
diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
index 5e34f7a24..3b699ed5d 100644
--- a/postfix/src/tls/tls_misc.c
+++ b/postfix/src/tls/tls_misc.c
@@ -1363,6 +1363,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->ffail_type = 0;
return (TLScontext);
}
@@ -1413,6 +1415,8 @@ void tls_free_context(TLS_SESS_STATE *TLScontext)
myfree((void *) TLScontext->srvr_sig_dgst);
if (TLScontext->errorcert)
X509_free(TLScontext->errorcert);
+ if (TLScontext->ffail_type)
+ myfree(TLScontext->ffail_type);
myfree((void *) TLScontext);
}
diff --git a/postfix/src/tls/tls_proxy.h b/postfix/src/tls/tls_proxy.h
index 65286398f..ae2a8381c 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 TLS_ATTR_FFAIL_TYPE "forced_failure_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..ae378cb7e 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(TLS_ATTR_FFAIL_TYPE,
+ STRING_OR_EMPTY(props->ffail_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..7b25f71af 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->ffail_type)
+ myfree(props->ffail_type);
myfree((void *) props);
}
@@ -419,6 +429,89 @@ 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_TLSRPT, &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 = 0;
+ VSTRING *tls_policy_domain = vstring_alloc(100);
+ ARGV *mx_host_patterns = 0;
+ 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);
+ int trw_flags;
+
+ 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);
+ trw->flags = trw_flags;
+ 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 +530,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 *ffail_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 +567,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(TLS_ATTR_FFAIL_TYPE, ffail_type),
ATTR_TYPE_END);
/* Always construct a well-formed structure. */
props->nexthop = vstring_export(nexthop);
@@ -479,7 +584,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->ffail_type, ffail_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..0f78e24f4 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;
@@ -89,6 +90,9 @@
/*
/* Victor Duchovni
/* Morgan Stanley
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
@@ -107,6 +111,10 @@
/* TLS library. */
+#ifdef USE_TLSRPT
+#include
+#endif
+
#define TLS_INTERNAL
#include
@@ -194,18 +202,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->ffail_type) {
+ msg_info("certificate verification failed for %s: "
+ "external policy failure (%s)",
+ TLScontext->namaddr, TLScontext->ffail_type);
+#ifdef USE_TLSRPT
+ if (tlsrpt) {
+ tlsrpt_failure_t failure_type;
+
+ if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->ffail_type)) < 0)
+ msg_panic("tls_log_verify_error: unexpected failure_reason: %s",
+ TLScontext->ffail_type);
+ trw_report_failure(tlsrpt, failure_type,
+ /* additional_info= */ (char *) 0,
+ /* failure_reason= */ (char *) 0);
+ }
+#endif
+ return;
+ }
+
/*
* Specific causes for verification failure.
*/
@@ -218,10 +258,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_info= */ (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_info= */ (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 +289,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_info= */ (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_info= */ (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_info= */ (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_info= */ (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_info= */ (char *) 0,
+ CERT_ERROR_TO_STRING(err));
+#endif
break;
default:
msg_info("%s certificate verification failed for %s: num=%d:%s",
@@ -263,6 +345,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..176e4015c
--- /dev/null
+++ b/postfix/src/tls/tlsrpt_wrapper.c
@@ -0,0 +1,698 @@
+/*++
+/* NAME
+/* tlsrpt_wrapper 3
+/* SUMMARY
+/* TLSRPT support for the SMTP and TLS protocol engines
+/* 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_info,
+/* 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 */
+/* POSTFIX ARCHITECTURE, BOTTOM-UP VIEW
+/* .ad
+/* .fi
+/* This module encapsulates TLSRPT support for Postfix's
+/* multi-process and multi-layer architecture. The text that follows
+/* explains the purpose of this software layer.
+/*
+/* First, 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.
+/*
+/* Second, some TLS errors are detected in the SMTP protocol
+/* engine (example: a remote SMTP server does not announce STARTTLS
+/* support), while other TLS errors are detected in the TLS protocol
+/* engine (example: certificate verification error).
+/*
+/* Third, the Postfix TLS protocol engine may be located in a
+/* different process than the SMTP protocol engine. And even if the
+/* two are located in the same process, the TLS protocol engine knows
+/* nothing about SMTP. Hence, there needs to be an abstraction that
+/* isolates the TLS protocol engine from the SMTP-specific details
+/* of TLSRPT.
+/*
+/* Fourth, Postfix has a pipelined and layered architecture where
+/* each process (or architectural layer) handles a problem as it
+/* runs into it, instead of reporting problem details back to its
+/* pipeline predecessor (or back to a higher architectural layer).
+/* TLSRPT_WRAPPER IMPLEMENTATION
+/* .ad
+/* .fi
+/* At a high level, the SMTP protocol engine encapsulates SMTP
+/* session and TLS policy information in an opaque TLSRPT_WRAPPER
+/* object, and passes that object to the TLS protocol engine. The
+/* TLS protocol engine can invoke TLSRPT_WRAPPER methods to report a
+/* TLS error through the sys4.de TLSRPT client library. In a similar
+/* manner, the SMTP protocol engine can invoke TLSRPT_WRAPPER object
+/* methods to report a TLS error or success.
+/*
+/* At a low level, 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 local IPC in a tlsproxy(8) process.
+/*
+/* The TLS protocol engine may report a TLS error by invoking
+/* TLSRPT_WRAPPER methods, 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 (for example an error that resulted in
+/* a successful TLS handshake with a downgraded TLS security level).
+/*
+/* The Postfix SMTP protocol engine reports success or failure
+/* by invoking TLSRPT_WRAPPER methods, depending on whether all
+/* matching requirements were satisfied. The SMTP protocol engine
+/* does not report success or failure by invoking TLSRPT_WRAPPER
+/* methods if the TLS protocol engine already reported a failure.
+/* TLSRPT_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,
+/* but this not affect email deliveries.
+/* .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 resets 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 (may be null)
+/* policies[].policy.policy-string[]. Ignored if the tls_policy_type
+/* value is TLSRPT_NO_POLICY_FOUND.
+/* .IP tls_policy_domain (may be null)
+/* 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_info (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 warning when information is missing.
+/* Such warnings will not affect the operation of 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->mx_host_patterns, 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 ((snd_mta_addr || rcv_mta_name || rcv_mta_addr)
+ && ((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 (rcv_mta_ehlo && ((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);
+ }
+
+ /*
+ * Report a tlsrpt library internal error.
+ */
+ else if (tlsrpt_error_code_is_internal(libtlsrpt_errorcode)) {
+ msg_warn("Could not report TLS handshake result to tlsrpt library:"
+ " %s (error %d)", tlsrpt_strerror(libtlsrpt_errorcode),
+ libtlsrpt_errorcode);
+ return (-1);
+ }
+
+ /*
+ * Report a libc error. Do not report success if errno was zero. When
+ * debug logging is enabled, also log some library-internal info.
+ */
+ 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);
+ if (msg_verbose)
+ msg_warn("Error location in tlsrpt library: %s (error %d)",
+ tlsrpt_strerror(libtlsrpt_errorcode),
+ libtlsrpt_errorcode);
+ 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[] = {
+ "starttls_not_supported", TLSRPT_STARTTLS_NOT_SUPPORTED,
+ "certificate_host_mismatch", TLSRPT_CERTIFICATE_HOST_MISMATCH,
+ "certificate_not_trusted", TLSRPT_CERTIFICATE_NOT_TRUSTED,
+ "certificate_expired", TLSRPT_CERTIFICATE_EXPIRED,
+ "validation_failure", TLSRPT_VALIDATION_FAILURE,
+ "sts_policy_fetch_error", TLSRPT_STS_POLICY_FETCH_ERROR,
+ "sts_policy_invalid", TLSRPT_STS_POLICY_INVALID,
+ "sts_webpki_invalid", TLSRPT_STS_WEBPKI_INVALID,
+ "tlsa_invalid", TLSRPT_TLSA_INVALID,
+ "dnssec_invalid", TLSRPT_DNSSEC_INVALID,
+ "dane_required", TLSRPT_DANE_REQUIRED,
+ "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_info,
+ 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;
+
+ /* Give the local admin a clue. */
+ msg_info("TLSRPT: status=failure, domain=%s, receiving_mx=%s[%s],"
+ " failure_type=%s%s%s",
+ trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr,
+ trw_failure_type_to_string(failure_type),
+ failure_reason ? ", failure_reason=" : "",
+ failure_reason ? failure_reason : "");
+
+ 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_info,
+ /* 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;
+
+ /* Give the local admin a clue. */
+ msg_info("TLSRPT: status=success, domain=%s, receiving_mx=%s[%s]",
+ trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr);
+
+ 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) {
+ char **cpp;
+
+ 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_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..57daff16f
--- /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 SMTP and TLS protocol engines
+/* 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_info,
+ 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..1bf449d37 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,20 @@ 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->client_start_props->tlsrpt
+ && (state->flags & TLSP_FLAG_DO_HANDSHAKE)
+ && state->is_server_role == 0)
+ trw_report_failure(state->client_start_props->tlsrpt,
+ TLSRPT_VALIDATION_FAILURE,
+ /* additional_info= */ (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..67bdc3ec7 100644
--- a/postfix/src/util/Makefile.in
+++ b/postfix/src/util/Makefile.in
@@ -45,7 +45,8 @@ 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 \
+ sane_sockaddr_to_hostaddr.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 +93,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 sane_sockaddr_to_hostaddr.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 +2445,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 +2556,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
@@ -2617,6 +2626,10 @@ sane_rename.o: sane_fsops.h
sane_rename.o: sane_rename.c
sane_rename.o: sys_defs.h
sane_rename.o: warn_stat.h
+sane_sockaddr_to_hostaddr.o: inet_proto.h
+sane_sockaddr_to_hostaddr.o: myaddrinfo.h
+sane_sockaddr_to_hostaddr.o: sane_sockaddr_to_hostaddr.c
+sane_sockaddr_to_hostaddr.o: sys_defs.h
sane_socketpair.o: msg.h
sane_socketpair.o: sane_socketpair.c
sane_socketpair.o: sane_socketpair.h
@@ -2883,6 +2896,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..0816430bc 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.
/*
@@ -144,6 +152,9 @@
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System libraries. */
@@ -301,6 +312,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 +630,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 +782,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..feed9f1cd 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]);
@@ -177,67 +183,121 @@ VSTRING *hex_decode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
}
#ifdef TEST
-#include
/*
* Proof-of-concept test program: convert to hexadecimal and back.
*/
-
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
+typedef struct TEST_CASE {
+ const char *label; /* identifies test case */
+ VSTRING *(*func) (VSTRING *, const char *, ssize_t, int);
+ const char *input; /* input string */
+ ssize_t inlen; /* input size */
+ int flags; /* flags */
+ const char *exp_output; /* expected output or null */
+ ssize_t exp_outlen; /* expected size */
+} TEST_CASE;
+
+/*
+ * The test cases.
+ */
+#define OUTPUT_INIT "thrash:" /* output buffer initial content */
+#define OUTPUT_INIT_SZ (sizeof(OUTPUT_INIT) - 1)
+
+static const TEST_CASE test_cases[] = {
+ {"hex_encode_no_options", hex_encode_opt,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ HEX_ENCODE_FLAG_NONE,
+ "7468697320697320612074657374",
+ sizeof("7468697320697320612074657374") - 1,
+ },
+ {"hex_decode_no_options", hex_decode_opt,
+ "7468697320697320612074657374",
+ sizeof("7468697320697320612074657374") - 1,
+ HEX_DECODE_FLAG_NONE,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ },
+ {"hex_decode_no_colon_allow_colon", hex_decode_opt,
+ "7468697320697320612074657374",
+ sizeof("7468697320697320612074657374") - 1,
+ HEX_DECODE_FLAG_ALLOW_COLON,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ },
+ {"hex_encode_appends", hex_encode_opt,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ HEX_ENCODE_FLAG_APPEND,
+ OUTPUT_INIT "7468697320697320612074657374",
+ sizeof(OUTPUT_INIT "7468697320697320612074657374") - 1,
+ },
+ {"hex_encode_with_colon", hex_encode_opt,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ HEX_ENCODE_FLAG_USE_COLON,
+ "74:68:69:73:20:69:73:20:61:20:74:65:73:74",
+ sizeof("74:68:69:73:20:69:73:20:61:20:74:65:73:74") - 1,
+ },
+ {"hex_encode_with_colon_and_append", hex_encode_opt,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ HEX_ENCODE_FLAG_USE_COLON | HEX_ENCODE_FLAG_APPEND,
+ OUTPUT_INIT "74:68:69:73:20:69:73:20:61:20:74:65:73:74",
+ sizeof(OUTPUT_INIT "74:68:69:73:20:69:73:20:61:20:74:65:73:74") - 1,
+ },
+ {"hex_decode_error", hex_decode_opt,
+ "this is a test",
+ sizeof("this is a test") - 1,
+ HEX_DECODE_FLAG_ALLOW_COLON,
+ 0,
+ 0,
+ },
+ {0},
+};
+
int main(int unused_argc, char **unused_argv)
{
- VSTRING *b1 = vstring_alloc(1);
- VSTRING *b2 = vstring_alloc(1);
- char *test = "this is a test";
- ARGV *argv;
+ VSTRING *buf = vstring_alloc(1);
+ int pass = 0;
+ int fail = 0;
+ const TEST_CASE *tp;
-#define DECODE(b,x,l) { \
- if (hex_decode((b),(x),(l)) == 0) \
- msg_panic("bad hex: %s", (x)); \
+ for (tp = test_cases; tp->label != 0; tp++) {
+ VSTRING *out;
+ int ok = 0;
+
+ msg_info("RUN %s", tp->label);
+ vstring_memcpy(buf, OUTPUT_INIT, OUTPUT_INIT_SZ);
+ out = tp->func(buf, tp->input, tp->inlen, tp->flags);
+ if (out == 0 && tp->exp_output == 0) {
+ ok = 1;
+ } else if (out != buf) {
+ msg_warn("got result '%p', want: '%p'",
+ (void *) out, (void *) buf);
+ } else if (LEN(out) != tp->exp_outlen) {
+ msg_warn("got result length '%ld', want: '%ld'",
+ (long) LEN(out), (long) tp->exp_outlen);
+ } else if (memcmp(STR(out), tp->exp_output, tp->exp_outlen) != 0) {
+ msg_warn("got result '%*s', want: '%*s'",
+ (int) LEN(out), STR(out),
+ (int) tp->exp_outlen, tp->exp_output);
+ } else {
+ ok = 1;
+ }
+ if (ok) {
+ msg_info("PASS %s", tp->label);
+ pass++;
+ } else {
+ msg_info("FAIL %s", tp->label);
+ fail++;
+ }
}
-#define VERIFY(b,t) { \
- if (strcmp((b), (t)) != 0) \
- msg_panic("bad test: %s", (b)); \
- }
-
- hex_encode(b1, test, strlen(test));
- DECODE(b2, STR(b1), LEN(b1));
- VERIFY(STR(b2), test);
-
- hex_encode(b1, test, strlen(test));
- hex_encode(b2, STR(b1), LEN(b1));
- hex_encode(b1, STR(b2), LEN(b2));
- DECODE(b2, STR(b1), LEN(b1));
- DECODE(b1, STR(b2), LEN(b2));
- DECODE(b2, STR(b1), LEN(b1));
- VERIFY(STR(b2), test);
-
- hex_encode(b1, test, strlen(test));
- hex_encode(b2, STR(b1), LEN(b1));
- hex_encode(b1, STR(b2), LEN(b2));
- hex_encode(b2, STR(b1), LEN(b1));
- hex_encode(b1, STR(b2), LEN(b2));
- DECODE(b2, STR(b1), LEN(b1));
- DECODE(b1, STR(b2), LEN(b2));
- DECODE(b2, STR(b1), LEN(b1));
- DECODE(b1, STR(b2), LEN(b2));
- DECODE(b2, STR(b1), LEN(b1));
- VERIFY(STR(b2), test);
-
- hex_encode_opt(b1, test, strlen(test), HEX_ENCODE_FLAG_USE_COLON);
- argv = argv_split(STR(b1), ":");
- if (argv->argc != strlen(test))
- msg_panic("HEX_ENCODE_FLAG_USE_COLON");
- if (hex_decode_opt(b2, STR(b1), LEN(b1), HEX_DECODE_FLAG_ALLOW_COLON) == 0)
- msg_panic("HEX_DECODE_FLAG_ALLOW_COLON");
- VERIFY(STR(b2), test);
- argv_free(argv);
-
- vstring_free(b1);
- vstring_free(b2);
- return (0);
+ msg_info("PASS=%d FAIL=%d", pass, fail);
+ return (fail > 0);
}
#endif
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/myaddrinfo.h b/postfix/src/util/myaddrinfo.h
index 94f1e9fa6..cd58a8d4b 100644
--- a/postfix/src/util/myaddrinfo.h
+++ b/postfix/src/util/myaddrinfo.h
@@ -210,6 +210,12 @@ extern void myaddrinfo_control(int,...);
msg_fatal("sockaddr_to_hostname: %s", MAI_STRERROR(_aierr)); \
} while (0)
+ /*
+ * sane_sockaddr_to_hostaddr.c
+ */
+extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(const struct sockaddr *,
+ SOCKADDR_SIZE, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
+
/* LICENSE
/* .ad
/* .fi
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/sane_sockaddr_to_hostaddr.c b/postfix/src/util/sane_sockaddr_to_hostaddr.c
new file mode 100644
index 000000000..919054c0e
--- /dev/null
+++ b/postfix/src/util/sane_sockaddr_to_hostaddr.c
@@ -0,0 +1,76 @@
+/*++
+/* NAME
+/* sane_sockaddr_to_hostaddr 3
+/* SUMMARY
+/* Sanitize IPv4 in IPv6 address
+/* SYNOPSIS
+/* #include
+/*
+/* 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)
+/* DESCRIPTION
+/* sane_sockaddr_to_hostaddr() wraps sockaddr_to_hostaddr() and
+/* converts an IPv4 in IPv6 address to IPv4 form, but only if IPv4
+/* support is available.
+/* HISTORY
+/* .ad
+/* .fi
+/* This implementation was taken from postscreen, and consolidates
+/* multiple instances of similar code across the Postfix code base.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
+/*--*/
+
+ /*
+ * System library.
+ */
+#include
+
+ /*
+ * Utility library.
+ */
+#include
+#include
+
+static const INET_PROTO_INFO *proto_info;
+
+/* sane_sockaddr_to_hostaddr - sanitize IPV4 in IPV6 address */
+
+int sane_sockaddr_to_hostaddr(const 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);
+}
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)