-TTEST_CASE
-TTLSMGR_SCACHE
-TTLSP_STATE
+-TTLSRPT_WRAPPER
-TTLS_APPL_STATE
-TTLS_CERTS
-TTLS_CLIENT_INIT_PROPS
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
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.
done </dev/null
# Some checks require a bin/postconf executable.
-pre-release-checks: typo-check missing-proxy-read-maps-check \
- postlink-check postfix-files-check check-spell-history \
- check-double-history check-table-proto check-see-postconf-d-output \
+pre-release-checks: typo-check double-check missing-proxy-read-maps-check \
+ postlink-check postfix-files-check \
+ check-table-proto check-see-postconf-d-output \
check-snapshot-nonprod
postfix-files-check:
$(SHLIB_ENV) mantools/missing-proxy-read-maps | diff /dev/null -
typo-check: spell-cc spell-install-proto-text spell-proto-html \
- double-cc double-install-proto-text double-proto-html
+ check-spell-history
+
+double-check: double-cc double-install-proto-text double-proto-html \
+ check-double-history
spell-cc:
mantools/check-spell-cc | diff /dev/null -
* SASL_README: SASL Authentication
* TLS_README: TLS Encryption and authentication
* FORWARD_SECRECY_README: TLS Forward Secrecy
+ * TLSRPT_README: TLSRPT Protocol Support
* IPV6_README: IP Version 6 Support
* SMTPUTF8_README: SMTPUTF8 Support
* MAILLOG_README: Postfix logging to file or stdout
--- /dev/null
+P\bPo\bos\bst\btf\bfi\bix\bx T\bTL\bLS\bSR\bRP\bPT\bT H\bHo\bow\bwt\bto\bo
+
+-------------------------------------------------------------------------------
+
+T\bTa\bab\bbl\ble\be o\bof\bf C\bCo\bon\bnt\bte\ben\bnt\bts\bs
+
+ * Introduction
+ * Building Postfix with TLSRPT support
+ * Turning on TLSRPT
+ * TLSRPT Status logging
+ * Delivering TLSRPT summaries via email
+ * MTA-STS Support via smtp_tls_policy_maps
+ * Limitations
+ * Credits
+
+I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
+
+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 TLSRPT client TLSRPT summary Email or HTTP
+ TLS client --> 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.
+
+B\bBu\bui\bil\bld\bdi\bin\bng\bg P\bPo\bos\bst\btf\bfi\bix\bx w\bwi\bit\bth\bh T\bTL\bLS\bSR\bRP\bPT\bT s\bsu\bup\bpp\bpo\bor\brt\bt
+
+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"
+
+T\bTu\bur\brn\bni\bin\bng\bg o\bon\bn T\bTL\bLS\bSR\bRP\bPT\bT
+
+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.
+
+T\bTL\bLS\bSR\bRP\bPT\bT S\bSt\bta\bat\btu\bus\bs l\blo\bog\bgg\bgi\bin\bng\bg
+
+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 T\bTL\bLS\bS c\bco\bon\bnn\bne\bec\bct\bti\bio\bon\bn r\bre\beu\bus\bse\bed\bd to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+ Untrusted T\bTL\bLS\bS c\bco\bon\bnn\bne\bec\bct\bti\bio\bon\bn r\bre\beu\bus\bse\bed\bd 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],
+ f\bfa\bai\bil\blu\bur\bre\be_\b_t\bty\byp\bpe\be=\b=v\bva\bal\bli\bid\bda\bat\bti\bio\bon\bn_\b_f\bfa\bai\bil\blu\bur\bre\be, f\bfa\bai\bil\blu\bur\bre\be_\b_r\bre\bea\bas\bso\bon\bn=\b=s\bse\bel\blf\bf-\b-s\bsi\big\bgn\bne\bed\bd_\b_c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be
+
+ * A reused TLS session is indicated as shown below, and has no certificate
+ verification details:
+
+ mail.example.org[ipaddr]:25: r\bre\be-\b-u\bus\bsi\bin\bng\bg s\bse\bes\bss\bsi\bio\bon\bn with untrusted peer
+ credential, look for details earlier in the log
+ TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org
+ [ipaddr],
+ f\bfa\bai\bil\blu\bur\bre\be_\b_t\bty\byp\bpe\be=\b=c\bce\ber\brt\bti\bif\bfi\bic\bca\bat\bte\be_\b_n\bno\bot\bt_\b_t\btr\bru\bus\bst\bte\bed\bd
+
+Some Postfix users may wonder where the difference comes from. So this is why.
+
+D\bDe\bel\bli\biv\bve\ber\bri\bin\bng\bg T\bTL\bLS\bSR\bRP\bPT\bT s\bsu\bum\bmm\bma\bar\bri\bie\bes\bs v\bvi\bia\ba e\bem\bma\bai\bil\bl
+
+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
+
+M\bMT\bTA\bA-\b-S\bST\bTS\bS S\bSu\bup\bpp\bpo\bor\brt\bt v\bvi\bia\ba s\bsm\bmt\btp\bp_\b_t\btl\bls\bs_\b_p\bpo\bol\bli\bic\bcy\by_\b_m\bma\bap\bps\bs
+
+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
+
+L\bLi\bim\bmi\bit\bta\bat\bti\bio\bon\bns\bs
+
+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.
+
+C\bCr\bre\bed\bdi\bit\bts\bs
+
+ * The TLSRPT client library and report generator are implemented and
+ maintained by sys4.
+ * Wietse Venema implemented the integration with Postfix.
+
(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.
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
$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
$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
--- /dev/null
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix TLSRPT notification Howto</title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix TLSRPT Howto</h1>
+
+<hr>
+
+<h2> Table of Contents </h2>
+
+<ul>
+
+<li> <a href="#intro"> Introduction </a> </li>
+<li> <a href="#building"> Building Postfix with TLSRPT support </a>
+<li> <a href="#using"> Turning on TLSRPT </a> </li>
+<li> <a href="#logging"> TLSRPT Status logging </a> </li>
+<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
+<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
+<li> <a href="#limitations"> Limitations </a></li>
+<li> <a href="#credits"> Credits </a> </li>
+
+</ul>
+
+<h2> <a name="intro"> Introduction </a> </h2>
+
+<p> The TLSRPT protocol is defined in <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a>. 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. </p>
+
+<p> A policy example looks like this: </p>
+
+<blockquote>
+<pre>
+_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com"
+</pre>
+</blockquote>
+
+<p> Translation: email sending systems are requested to generate daily
+summaries of successful and failed SMTP over TLS connections to domain
+<tt>example.com</tt>, and to report those summaries via email to the
+specified address. Instead of <tt>mailto:</tt>, a policy may specify an
+<tt>https:</tt> destination. </p>
+
+<p> The high-level diagram shows how Postfix reports summaries to
+domains that publish a TLSRPT policy.
+
+<blockquote>
+
+<table>
+
+<tr> <td align="center" bgcolor="#f0f0ff"> Postfix SMTP and<br> TLS
+client engines </td> <td> <tt> --> </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT client <br> library
+</td> <td> <tt> --> </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT summary <br> generator
+</td> <td> <tt> --> </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> Email or HTTP <br> delivery
+</td> </tr>
+
+</table>
+
+</blockquote>
+
+<p> When Postfix TLSRPT support is enabled (with "<a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a>
+= yes"): </p>
+
+<ul>
+
+<li> <p> The Postfix SMTP and TLS client engines will generate a
+"success" or "failure" event for each TLS handshake, </p>
+
+<li> <p> They will pass those events to an in-process TLSRPT client
+library that sends data over a local socket to </p>
+
+<li> <p> A TLSRPT report generator that produces daily summary
+reports. </p>
+
+</ul>
+
+<p> The TLSRPT client library and report generator are maintained
+by sys4. </p>
+
+<p> The Postfix implementation supports both DANE (Postfix built-in)
+and MTA-STS (through an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> plug-in).
+</p>
+
+<p> The Postfix <a href="smtp.8.html">smtp(8)</a> client process implements the SMTP client
+engine. With "<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> = no", the <a href="smtp.8.html">smtp(8)</a> client
+process also implements the TLS client engine. With
+"<a href="postconf.5.html#smtp_tls_connection_reuse">smtp_tls_connection_reuse</a> = yes", the <a href="smtp.8.html">smtp(8)</a> client process
+delegates TLS processing to a Postfix <a href="tlsproxy.8.html">tlsproxy(8)</a> process. Either
+way, Postfix will generate the exact same TLSRPT events. </p>
+
+<h2> <a name="building"> Building Postfix with TLSRPT support </a>
+</h2>
+
+<p> These instructions assume that you build Postfix from source
+code as described in the <a href="INSTALL.html">INSTALL</a> document. Some modification may
+be required if you build Postfix from a vendor-specific source
+package. </p>
+
+<p> The Postfix TLSRPT client builds on a TLSRPT client library
+whose source code can be obtained from: </p>
+
+<blockquote>
+ <p> <a href="https://github.com/sys4/tlsrpt">https://github.com/sys4/tlsrpt</a> </p>
+</blockquote>
+
+<p> 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. </p>
+
+<p> In order to build Postfix with TLSRPT support, you will need
+to add compiler options <tt>-DUSE_TLSRPT</tt> (to build with TLSRPT
+support), and <tt>-I</tt> (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: </p>
+
+<blockquote>
+<pre>
+make -f Makefile.init makefiles \
+ "CCARGS=-DUSE_TLSRPT -I/usr/local/include" \
+ "AUXLIBS=-L/usr/local/lib -ltlsrpt"
+</pre>
+</blockquote>
+
+<p> Then, just run 'make'. </p>
+
+<blockquote>
+
+<p> 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. </p>
+
+<blockquote>
+<pre>
+make -f Makefile.init makefiles \
+ "CCARGS=<i>existing settings...</i> -DUSE_TLSRPT -I/usr/local/include" \
+ "AUXLIBS=<i>existing settings...</i> -L/usr/local/lib -ltlsrpt"
+</pre>
+</blockquote>
+
+</blockquote>
+
+<h2> <a name="using"> Turning on TLSRPT </a> </h2>
+
+<p> After installing Postfix TLSRPT support, you can enable TLSRPT
+support in <a href="postconf.5.html">main.cf</a> like this: </p>
+
+<blockquote>
+<pre>
+<a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> = yes
+<a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> = /path/to/socket
+</pre>
+</blockquote>
+
+<p> The <a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> parameter specifies an absolute
+pathname, or a pathname that is relative to $<a href="postconf.5.html#queue_directory">queue_directory</a>. </p>
+
+<blockquote>
+
+<p> Note: the recommended socket location is still to be determined.
+A good socket location would be under the Postfix queue directory,
+for example: "<a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> = 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. </p>
+
+</blockquote>
+
+<p> Do not specify a location under a directory such as <tt>private</tt>
+or <tt>public</tt> that is already used by Postfix programs. Only Postfix
+programs should create sockets there. </p>
+
+<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
+
+<p> 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.
+</p>
+
+<p> Below are a few examples of logging from a Postfix SMTP client
+or tlsproxy daemon: </p>
+
+<blockquote>
+<pre>
+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
+</pre>
+</blockquote>
+
+<p> 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: </p>
+
+<blockquote>
+<pre>
+Verified <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+Untrusted <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+</pre>
+</blockquote>
+
+<p> Postfix logs certificate verification failures with a level of
+detail that is different for a new or reused TLS session. </p>
+
+<ul>
+
+<li> <p> A new TLS session is logged with certificate verification
+failure details: </p>
+
+<pre>
+TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
+ <b>failure_type=validation_failure</b>, <b>failure_reason=self-signed_certificate</b>
+</pre>
+
+<li> <p> A reused TLS session is indicated as shown below, and has
+no certificate verification details: </p>
+
+<pre>
+mail.example.org[ipaddr]:25: <b>re-using session</b> with untrusted peer
+ credential, look for details earlier in the log
+TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
+ <b>failure_type=certificate_not_trusted</b>
+</pre>
+
+</ul>
+
+<p> Some Postfix users may wonder where the difference comes from.
+So this is why. </p>
+
+<h2> <a name="delivering"> Delivering TLSRPT summaries via email</a> </h2>
+
+<p> <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> 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. </p>
+
+<p> 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.
+</p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ <a href="postconf.5.html#transport_maps">transport_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/transport
+ 
+/etc/postfix/transport:
+ smtp-tls-report@example.com allow-plaintext:
+ ...
+ 
+/etc/postfix/<a href="master.5.html">master.cf</a>:
+ # service name type private unpriv chroot wakeup maxproc command
+ allow-plaintext unix - - n - - smtp
+ -o <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a>=may
+ -o <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>=<a href="DATABASE_README.html#types">static</a>:may
+</pre>
+</blockquote>
+
+<h2> <a name="mta-sts"> MTA-STS Support via smtp_tls_policy_maps
+</a></h2>
+
+<p> Postfix supports MTA-STS though an <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a>
+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 <tt>{
+name = value }</tt> when a value may contain whitespace. </p>
+
+<blockquote>
+
+<p> 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 <tt>ttl</tt> and <tt>policy_failure</tt> attributes,
+and will ignore the attributes that are used only for TLSRPT. </p>
+
+<p> Note 2: It is an error to specify these attributes for a non-STS
+policy. </p>
+
+</blockquote>
+
+<p> The examples in the table apply to the MTA-STS policy example
+given in <a href="https://datatracker.ietf.org/doc/html/rfc8460#section-4.5">https://datatracker.ietf.org/doc/html/rfc8460#section-4.5</a>.
+<p>
+
+<ul>
+
+<li> <p> <tt> policy_type=<i>type</i> </tt>
+
+<p> Specify <tt>sts</tt> or <tt>no-policy-found</tt>. </p> </li>
+
+<li> <p> <tt> policy_domain=<i>name</i> </tt> </p>
+
+<p> The domain that the MTA-STS policy applies to. </p> </li>
+
+<li> <p> <tt> policy_ttl=<i>time</i> </tt> </p>
+
+<p> How long (in seconds) a Postfix SMTP client process will cache
+the MTA-STS plugin response. </p> </li>
+
+<li> <p> <tt> { policy_string = <i>value</i> } </tt> </p>
+
+<p> Specify one <tt>policy_string</tt> instance for each MTA-STS
+policy feature, enclosed inside "{" and "}" to protect whitespace
+in attribute values. </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+{ policy_string = version: STSv1 } { policy_string = mode: testing } ...
+</pre>
+</blockquote>
+
+<p> This form ignores whitespace after the opening "{", around the "=",
+and before the closing "}".</p> </li>
+
+<li> <p> <tt> mx_host_pattern=<i>pattern</i> </tt> </p>
+
+<p> Specify one <tt>mx_host_pattern</tt> instance for each "mx:" feature
+in the MTA-STS policy. </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
+</pre>
+</blockquote>
+</li>
+
+<li> <p> <tt> policy_failure=<i>type</i> </tt> </p>
+
+<p> If specified, forces MTA-STS policy enforcement to fail with
+the indicated error, even if a server certificate would satisfy
+conventional PKI constraints. </p>
+
+<p> Valid errors are <tt>sts-policy-fetch-error, sts-policy-invalid</tt>,
+<tt>sts-webpki-invalid</tt>, or the less informative
+<tt>validation-failure</tt>. </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+policy_failure=sts-webpki-invalid
+</pre>
+</blockquote>
+</li>
+
+</ul>
+
+<h2> <a name="limitations"> Limitations </a></h2>
+
+<p> 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. </p>
+
+<p> 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. </p>
+
+<h2> <a name="credits"> Credits </a> </h2>
+
+<ul>
+
+<li> The TLSRPT client library and report generator are implemented
+and maintained by sys4. </li>
+
+<li> Wietse Venema implemented the integration with Postfix.
+</li>
+
+</ul>
+
+</body>
+
+</html>
<li> <a href="FORWARD_SECRECY_README.html"> TLS Forward Secrecy </a>
+<li> <a href="TLSRPT_README.html"> TLSRPT Protocol Support </a>
+
<li> <a href="IPV6_README.html"> IP Version 6 Support </a>
<li> <a href="SMTPUTF8_README.html"> SMTPUTF8 Support </a>
Request that remote SMTP servers send an <a href="https://tools.ietf.org/html/rfc7250">RFC7250</a> raw public key
instead of an X.509 certificate.
+ <b><a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> (no)</b>
+ Enable support for <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> TLSRPT notifications.
+
+ <b><a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> (empty)</b>
+ The pathname of a UNIX-domain datagram socket that is managed by
+ a local TLSRPT reporting service.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
- 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.
<b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
- 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.
<b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
- 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.
<b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
- 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.
<b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
- Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
cipher list.
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
- 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).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- 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.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
mand, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- 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.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- 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.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- 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.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The Postfix SMTP client time limit for sending the SMTP message
+ The Postfix SMTP client time limit for sending the SMTP message
content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
- 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).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- 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
<a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
Available in Postfix version 2.2 and earlier:
<b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
- Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
+ Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
seconds.
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the specified
+ Permanently enable SMTP connection caching for the specified
destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a destination
+ Temporarily enable SMTP connection caching while a destination
has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
<b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- 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:
<b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
- 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:
<b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
- 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:
Available in Postfix version 3.7 and later:
<b><a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a> (no)</b>
- 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.
<b><a href="postconf.5.html#smtp_min_data_rate">smtp_min_data_rate</a> (500)</b>
- 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
<a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a>.
<b><a href="postconf.5.html#transport_destination_concurrency_limit">transport_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
- A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
+ A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
<a href="postconf.5.html#default_destination_concurrency_limit">currency_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b><a href="postconf.5.html#transport_destination_recipient_limit">transport_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipi</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">ent_limit</a>)</b>
A transport-specific override for the <a href="postconf.5.html#default_destination_recipient_limit">default_destination_recip</a>-
- <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+ <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b>SMTPUTF8 CONTROLS</b>
Preliminary SMTPUTF8 support is introduced with Postfix 3.0.
<b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a>, <a href="https://tools.ietf.org/html/rfc6532">RFC 6532</a>, and <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a>.
<b><a href="postconf.5.html#smtputf8_autodetect_classes">smtputf8_autodetect_classes</a> (sendmail, verify)</b>
- 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:
<b><a href="postconf.5.html#enable_idna2003_compatibility">enable_idna2003_compatibility</a> (no)</b>
- 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.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- 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 <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- 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
$<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- 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.
<b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
- What categories of Postfix-generated mail are subject to
- before-queue content inspection by <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
+ What categories of Postfix-generated mail are subject to
+ before-queue content inspection by <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
<a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- 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.
<b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
- 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.
<b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
Disable DNS lookups in the Postfix SMTP and LMTP clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
- The local network interface addresses that this mail system
+ The local network interface addresses that this mail system
receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (see 'postconf -d' output)</b>
- The Internet protocols Postfix will attempt to use when making
+ The Internet protocols Postfix will attempt to use when making
or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
- The time limit for sending or receiving information over an
+ The time limit for sending or receiving information over an
internal communication channel.
<b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
- 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".
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
The default TCP port that the Postfix LMTP client connects to.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- 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.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
The process name of a Postfix command or daemon process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
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.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- 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:
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- 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:
<b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
- 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:
Available in Postfix 3.7 and later:
<b><a href="postconf.5.html#smtp_bind_address_enforce">smtp_bind_address_enforce</a> (no)</b>
- Defer delivery when the Postfix SMTP client cannot apply the
+ Defer delivery when the Postfix SMTP client cannot apply the
<a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> or <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> setting.
<b>SEE ALSO</b>
<b>-f</b> Prepend the file name to each output line.
+ This feature is available in Postfix 3.10 and later.
+
<b>-h</b> Show message header content. The <b>-h</b> option produces output from
the beginning of the message up to, but not including, the first
non-header line.
<p> This feature is available in Postfix 3.0 and later. </p>
+</DD>
+
+<DT><b><a name="smtp_tlsrpt_enable">smtp_tlsrpt_enable</a>
+(default: no)</b></DT><DD>
+
+<p> Enable support for <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> 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. </p>
+
+<p> This feature is available in Postfix ≥ 3.10. </p>
+
+
+</DD>
+
+<DT><b><a name="smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a>
+(default: empty)</b></DT><DD>
+
+<p> 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 $<a href="postconf.5.html#queue_directory">queue_directory</a>) when
+"<a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> = yes". </p>
+
+<p> This feature is available in Postfix ≥ 3.10. </p>
+
+
</DD>
<DT><b><a name="smtp_use_tls">smtp_use_tls</a>
Request that remote SMTP servers send an <a href="https://tools.ietf.org/html/rfc7250">RFC7250</a> raw public key
instead of an X.509 certificate.
+ <b><a href="postconf.5.html#smtp_tlsrpt_enable">smtp_tlsrpt_enable</a> (no)</b>
+ Enable support for <a href="https://tools.ietf.org/html/rfc8460">RFC 8460</a> TLSRPT notifications.
+
+ <b><a href="postconf.5.html#smtp_tlsrpt_socket_name">smtp_tlsrpt_socket_name</a> (empty)</b>
+ The pathname of a UNIX-domain datagram socket that is managed by
+ a local TLSRPT reporting service.
+
<b>OBSOLETE STARTTLS CONTROLS</b>
- 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.
<b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
- 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.
<b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
- 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.
<b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
- 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.
<b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
- Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS
cipher list.
<b>RESOURCE AND RATE CONTROLS</b>
<b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
- 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).
<b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
- 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.
<b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
mand, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- 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.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- 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.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- 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.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The Postfix SMTP client time limit for sending the SMTP message
+ The Postfix SMTP client time limit for sending the SMTP message
content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
- 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).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- 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
<a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
Available in Postfix version 2.2 and earlier:
<b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
- Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
+ Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
seconds.
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the specified
+ Permanently enable SMTP connection caching for the specified
destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a destination
+ Temporarily enable SMTP connection caching while a destination
has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
<b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- 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:
<b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
- 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:
<b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
- 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:
Available in Postfix version 3.7 and later:
<b><a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a> (no)</b>
- 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.
<b><a href="postconf.5.html#smtp_min_data_rate">smtp_min_data_rate</a> (500)</b>
- 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
<a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a>.
<b><a href="postconf.5.html#transport_destination_concurrency_limit">transport_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
- A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
+ A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
<a href="postconf.5.html#default_destination_concurrency_limit">currency_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b><a href="postconf.5.html#transport_destination_recipient_limit">transport_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipi</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">ent_limit</a>)</b>
A transport-specific override for the <a href="postconf.5.html#default_destination_recipient_limit">default_destination_recip</a>-
- <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+ <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b>SMTPUTF8 CONTROLS</b>
Preliminary SMTPUTF8 support is introduced with Postfix 3.0.
<b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
- Enable preliminary SMTPUTF8 support for the protocols described
+ Enable preliminary SMTPUTF8 support for the protocols described
in <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a>, <a href="https://tools.ietf.org/html/rfc6532">RFC 6532</a>, and <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a>.
<b><a href="postconf.5.html#smtputf8_autodetect_classes">smtputf8_autodetect_classes</a> (sendmail, verify)</b>
- 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:
<b><a href="postconf.5.html#enable_idna2003_compatibility">enable_idna2003_compatibility</a> (no)</b>
- 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.
<b>TROUBLE SHOOTING CONTROLS</b>
<b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
- 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 <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
<b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
- 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
$<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
<b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
- 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.
<b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
- What categories of Postfix-generated mail are subject to
- before-queue content inspection by <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
+ What categories of Postfix-generated mail are subject to
+ before-queue content inspection by <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
<a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+ The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
figuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- 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.
<b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
- 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.
<b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
Disable DNS lookups in the Postfix SMTP and LMTP clients.
<b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
- The local network interface addresses that this mail system
+ The local network interface addresses that this mail system
receives mail on.
<b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (see 'postconf -d' output)</b>
- The Internet protocols Postfix will attempt to use when making
+ The Internet protocols Postfix will attempt to use when making
or accepting connections.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
- The time limit for sending or receiving information over an
+ The time limit for sending or receiving information over an
internal communication channel.
<b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
- 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".
<b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
The default TCP port that the Postfix LMTP client connects to.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- 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.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
The process name of a Postfix command or daemon process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
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.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- 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.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- 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:
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- 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:
<b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
- 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:
Available in Postfix 3.7 and later:
<b><a href="postconf.5.html#smtp_bind_address_enforce">smtp_bind_address_enforce</a> (no)</b>
- Defer delivery when the Postfix SMTP client cannot apply the
+ Defer delivery when the Postfix SMTP client cannot apply the
<a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> or <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> setting.
<b>SEE ALSO</b>
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
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:
.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
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; }
s;\bdnssec_probe\b;<a href="postconf.5.html#dnssec_probe">$&</a>;g;
s;\bsmtp_tls_connection_reuse\b;<a href="postconf.5.html#smtp_tls_connection_reuse">$&</a>;g;
s;\blmtp_tls_connection_reuse\b;<a href="postconf.5.html#lmtp_tls_connection_reuse">$&</a>;g;
+ s;\bsmtp_tlsrpt_enable\b;<a href="postconf.5.html#smtp_tlsrpt_enable">$&</a>;g;
+ s;\bsmtp_tlsrpt_socket_name\b;<a href="postconf.5.html#smtp_tlsrpt_socket_name">$&</a>;g;
+ s;\blmtp_tlsrpt_enable\b;<a href="postconf.5.html#lmtp_tlsrpt_enable">$&</a>;g;
+ s;\blmtp_tlsrpt_socket_name\b;<a href="postconf.5.html#lmtp_tlsrpt_socket_name">$&</a>;g;
s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
s;\bsmtpd_sasl_tls_security_options\b;<a href="postconf.5.html#smtpd_sasl_tls_security_options">$&</a>;g;
s;\bsmtpd_sasl_type\b;<a href="postconf.5.html#smtpd_sasl_type">$&</a>;g;
../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 \
../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 \
../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) >$@
../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) >$@
--- /dev/null
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix TLSRPT notification Howto</title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix TLSRPT Howto</h1>
+
+<hr>
+
+<h2> Table of Contents </h2>
+
+<ul>
+
+<li> <a href="#intro"> Introduction </a> </li>
+<li> <a href="#building"> Building Postfix with TLSRPT support </a>
+<li> <a href="#using"> Turning on TLSRPT </a> </li>
+<li> <a href="#logging"> TLSRPT Status logging </a> </li>
+<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
+<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
+<li> <a href="#limitations"> Limitations </a></li>
+<li> <a href="#credits"> Credits </a> </li>
+
+</ul>
+
+<h2> <a name="intro"> Introduction </a> </h2>
+
+<p> 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. </p>
+
+<p> A policy example looks like this: </p>
+
+<blockquote>
+<pre>
+_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.com"
+</pre>
+</blockquote>
+
+<p> Translation: email sending systems are requested to generate daily
+summaries of successful and failed SMTP over TLS connections to domain
+<tt>example.com</tt>, and to report those summaries via email to the
+specified address. Instead of <tt>mailto:</tt>, a policy may specify an
+<tt>https:</tt> destination. </p>
+
+<p> The high-level diagram shows how Postfix reports summaries to
+domains that publish a TLSRPT policy.
+
+<blockquote>
+
+<table>
+
+<tr> <td align="center" bgcolor="#f0f0ff"> Postfix SMTP and<br> TLS
+client engines </td> <td> <tt> --> </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT client <br> library
+</td> <td> <tt> --> </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT summary <br> generator
+</td> <td> <tt> --> </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> Email or HTTP <br> delivery
+</td> </tr>
+
+</table>
+
+</blockquote>
+
+<p> When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable
+= yes"): </p>
+
+<ul>
+
+<li> <p> The Postfix SMTP and TLS client engines will generate a
+"success" or "failure" event for each TLS handshake, </p>
+
+<li> <p> They will pass those events to an in-process TLSRPT client
+library that sends data over a local socket to </p>
+
+<li> <p> A TLSRPT report generator that produces daily summary
+reports. </p>
+
+</ul>
+
+<p> The TLSRPT client library and report generator are maintained
+by sys4. </p>
+
+<p> The Postfix implementation supports both DANE (Postfix built-in)
+and MTA-STS (through an smtp_tls_policy_maps plug-in).
+</p>
+
+<p> 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. </p>
+
+<h2> <a name="building"> Building Postfix with TLSRPT support </a>
+</h2>
+
+<p> 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. </p>
+
+<p> The Postfix TLSRPT client builds on a TLSRPT client library
+whose source code can be obtained from: </p>
+
+<blockquote>
+ <p> https://github.com/sys4/tlsrpt </p>
+</blockquote>
+
+<p> 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. </p>
+
+<p> In order to build Postfix with TLSRPT support, you will need
+to add compiler options <tt>-DUSE_TLSRPT</tt> (to build with TLSRPT
+support), and <tt>-I</tt> (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: </p>
+
+<blockquote>
+<pre>
+make -f Makefile.init makefiles \
+ "CCARGS=-DUSE_TLSRPT -I/usr/local/include" \
+ "AUXLIBS=-L/usr/local/lib -ltlsrpt"
+</pre>
+</blockquote>
+
+<p> Then, just run 'make'. </p>
+
+<blockquote>
+
+<p> 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. </p>
+
+<blockquote>
+<pre>
+make -f Makefile.init makefiles \
+ "CCARGS=<i>existing settings...</i> -DUSE_TLSRPT -I/usr/local/include" \
+ "AUXLIBS=<i>existing settings...</i> -L/usr/local/lib -ltlsrpt"
+</pre>
+</blockquote>
+
+</blockquote>
+
+<h2> <a name="using"> Turning on TLSRPT </a> </h2>
+
+<p> After installing Postfix TLSRPT support, you can enable TLSRPT
+support in main.cf like this: </p>
+
+<blockquote>
+<pre>
+smtp_tlsrpt_enable = yes
+smtp_tlsrpt_socket_name = /path/to/socket
+</pre>
+</blockquote>
+
+<p> The smtp_tlsrpt_socket_name parameter specifies an absolute
+pathname, or a pathname that is relative to $queue_directory. </p>
+
+<blockquote>
+
+<p> 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. </p>
+
+</blockquote>
+
+<p> Do not specify a location under a directory such as <tt>private</tt>
+or <tt>public</tt> that is already used by Postfix programs. Only Postfix
+programs should create sockets there. </p>
+
+<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
+
+<p> 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.
+</p>
+
+<p> Below are a few examples of logging from a Postfix SMTP client
+or tlsproxy daemon: </p>
+
+<blockquote>
+<pre>
+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
+</pre>
+</blockquote>
+
+<p> 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: </p>
+
+<blockquote>
+<pre>
+Verified <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+
+Untrusted <b>TLS connection reused</b> to mail.example.com[ipaddr]:25:
+ TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
+</pre>
+</blockquote>
+
+<p> Postfix logs certificate verification failures with a level of
+detail that is different for a new or reused TLS session. </p>
+
+<ul>
+
+<li> <p> A new TLS session is logged with certificate verification
+failure details: </p>
+
+<pre>
+TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
+ <b>failure_type=validation_failure</b>, <b>failure_reason=self-signed_certificate</b>
+</pre>
+
+<li> <p> A reused TLS session is indicated as shown below, and has
+no certificate verification details: </p>
+
+<pre>
+mail.example.org[ipaddr]:25: <b>re-using session</b> with untrusted peer
+ credential, look for details earlier in the log
+TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
+ <b>failure_type=certificate_not_trusted</b>
+</pre>
+
+</ul>
+
+<p> Some Postfix users may wonder where the difference comes from.
+So this is why. </p>
+
+<h2> <a name="delivering"> Delivering TLSRPT summaries via email</a> </h2>
+
+<p> 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. </p>
+
+<p> 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.
+</p>
+
+<blockquote>
+<pre>
+/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
+</pre>
+</blockquote>
+
+<h2> <a name="mta-sts"> MTA-STS Support via smtp_tls_policy_maps
+</a></h2>
+
+<p> 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 <tt>{
+name = value }</tt> when a value may contain whitespace. </p>
+
+<blockquote>
+
+<p> 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 <tt>ttl</tt> and <tt>policy_failure</tt> attributes,
+and will ignore the attributes that are used only for TLSRPT. </p>
+
+<p> Note 2: It is an error to specify these attributes for a non-STS
+policy. </p>
+
+</blockquote>
+
+<p> The examples in the table apply to the MTA-STS policy example
+given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5.
+<p>
+
+<ul>
+
+<li> <p> <tt> policy_type=<i>type</i> </tt>
+
+<p> Specify <tt>sts</tt> or <tt>no-policy-found</tt>. </p> </li>
+
+<li> <p> <tt> policy_domain=<i>name</i> </tt> </p>
+
+<p> The domain that the MTA-STS policy applies to. </p> </li>
+
+<li> <p> <tt> policy_ttl=<i>time</i> </tt> </p>
+
+<p> How long (in seconds) a Postfix SMTP client process will cache
+the MTA-STS plugin response. </p> </li>
+
+<li> <p> <tt> { policy_string = <i>value</i> } </tt> </p>
+
+<p> Specify one <tt>policy_string</tt> instance for each MTA-STS
+policy feature, enclosed inside "{" and "}" to protect whitespace
+in attribute values. </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+{ policy_string = version: STSv1 } { policy_string = mode: testing } ...
+</pre>
+</blockquote>
+
+<p> This form ignores whitespace after the opening "{", around the "=",
+and before the closing "}".</p> </li>
+
+<li> <p> <tt> mx_host_pattern=<i>pattern</i> </tt> </p>
+
+<p> Specify one <tt>mx_host_pattern</tt> instance for each "mx:" feature
+in the MTA-STS policy. </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+mx_host_pattern=mx1.example.com mx_host_pattern=mx2.example.com ...
+</pre>
+</blockquote>
+</li>
+
+<li> <p> <tt> policy_failure=<i>type</i> </tt> </p>
+
+<p> If specified, forces MTA-STS policy enforcement to fail with
+the indicated error, even if a server certificate would satisfy
+conventional PKI constraints. </p>
+
+<p> Valid errors are <tt>sts-policy-fetch-error, sts-policy-invalid</tt>,
+<tt>sts-webpki-invalid</tt>, or the less informative
+<tt>validation-failure</tt>. </p>
+
+<p> Example: </p>
+
+<blockquote>
+<pre>
+policy_failure=sts-webpki-invalid
+</pre>
+</blockquote>
+</li>
+
+</ul>
+
+<h2> <a name="limitations"> Limitations </a></h2>
+
+<p> 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. </p>
+
+<p> 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. </p>
+
+<h2> <a name="credits"> Credits </a> </h2>
+
+<ul>
+
+<li> The TLSRPT client library and report generator are implemented
+and maintained by sys4. </li>
+
+<li> Wietse Venema implemented the integration with Postfix.
+</li>
+
+</ul>
+
+</body>
+
+</html>
length limit. </p>
<p> This feature is available in Postfix ≥ 3.9. </p>
+
+%PARAM smtp_tlsrpt_enable no
+
+<p> 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. </p>
+
+<p> This feature is available in Postfix ≥ 3.10. </p>
+
+%PARAM smtp_tlsrpt_socket_name
+
+<p> 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". </p>
+
+<p> This feature is available in Postfix ≥ 3.10. </p>
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
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
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
ILP
xxfi
optionsv
+rcv
+snd
+sts
+tlsrprt
bdefhnoqv
+deduplicated
ar
liveness
superset
+ltlsrpt
+sts
+STS
+STSv
+Sys
+Qsmtp
+Qsts
+gmail
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;
/*
/* 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;
/*
/*
/* 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
/* 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);
}
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.
*/
"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,
};
}
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);
}
#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
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
* 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
/* 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
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,
* 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) {
* 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) {
= vstring_str(cipher_exclusions),
matchargv = state->match,
mdalg = state->mdalg,
+ tlsrpt = 0,
+ ffail_type = 0,
dane = state->ddane ?
state->ddane : state->dane);
= 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);
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)
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
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
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
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
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
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
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[] = {
};
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,
};
/* .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
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;
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);
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
*/
_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
*/
#ifdef USE_TLS
SMTP_TLS_POLICY tls[1]; /* Usage: state->tls->member */
+#ifdef USE_TLSRPT
+ struct TLSRPT_WRAPPER *tlsrpt;
+#endif
#endif
/*
*/
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
#include <mail_error.h>
#include <dsn_buf.h>
#include <mail_addr.h>
+#include <valid_hostname.h>
/* DNS library. */
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.
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;
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[] = {
};
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,
};
/* 111 8th Avenue
/* New York, NY 10011, USA
/*
+/* Wietse Venema
+/* porcupine.org
+/*
/* Pipelining code in cooperation with:
/* Jon Ribbens
/* Oaktree Internet Solutions Ltd.,
#include <xtext.h>
#include <uxtext.h>
#include <smtputf8.h>
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+#include <tlsrpt_wrapper.h>
+#endif
/* Application-specific. */
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";
"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";
* 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. */
}
*/
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",
= 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);
/*
= 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);
/*
* 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
#include <mail_params.h>
#include <debug_peer.h>
+ /*
+ * TLS library.
+ */
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+#include <tlsrpt_wrapper.h>
+#endif
+
/* Application-specific. */
#include "smtp.h"
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);
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)
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
+#include <sane_strtol.h>
#include <stringops.h>
#include <valid_hostname.h>
#include <valid_utf8_hostname.h>
#include <maps.h>
#include <dsn_buf.h>
+/* TLS library. */
+
+#include <tlsrpt_wrapper.h>
+
/* DNS library. */
#include <dns.h>
{
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)
}
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);
}
* 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;
* 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);
}
continue;
}
+ /* Last one wins. */
if (!strcasecmp(name, "enable_rpk")) {
/* Ultimately ignored at some security levels */
if (strcasecmp(val, "yes") == 0) {
}
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;
}
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);
}
--- /dev/null
+/*++
+/* NAME
+/* smtp_tlsrpt 3
+/* SUMMARY
+/* TLSRPT support for the SMTP protocol engine
+/* SYNOPSIS
+/* #include <smtp_tlsrpt.h>
+/*
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+
+ /*
+ * Utility library.
+ */
+#include <hex_code.h>
+#include <midna_domain.h>
+#include <msg.h>
+#include <myaddrinfo.h>
+#include <name_code.h>
+#include <stringops.h>
+
+ /*
+ * Global library.
+ */
+#include <mail_params.h>
+
+ /*
+ * TLS library.
+ */
+#include <tls.h>
+#include <tlsrpt_wrapper.h>
+
+ /*
+ * Application-specific.
+ */
+#include <smtp.h>
+
+#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 */
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 \
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)
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
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
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
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
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 *);
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;
/*
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 *);
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
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
/*
/* Victor Duchovni
/* Morgan Stanley
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
#ifdef USE_TLS
#include <string.h>
+#include <tlsrpt_wrapper.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
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);
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);
* 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();
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();
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();
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);
* 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);
*/
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);
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);
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);
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);
* early.
*/
if (!tls_auth_enable(TLScontext, props)) {
+ /* Already warned and reported TLSRPT result. */
tls_free_context(TLScontext);
return (0);
}
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;
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;
}
*/
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);
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.
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);
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);
}
TLScontext->errordepth = -1;
TLScontext->errorcode = X509_V_OK;
TLScontext->errorcert = 0;
+ TLScontext->rpt_reported = 0;
+ TLScontext->ffail_type = 0;
return (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);
}
((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 *);
#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.
#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.
#include <tls.h>
#include <tls_proxy.h>
+#ifdef USE_TLSRPT
+#define TLSRPT_WRAPPER_INTERNAL
+#include <tlsrpt_wrapper.h>
+#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
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),
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,
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)
#define TLS_INTERNAL
#include <tls.h>
#include <tls_proxy.h>
+#ifdef USE_TLSRPT
+#define TLSRPT_WRAPPER_INTERNAL
+#include <tlsrpt_wrapper.h>
+#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
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);
}
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,
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");
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);
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;
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);
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);
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;
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",
/* 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;
/*
/* Victor Duchovni
/* Morgan Stanley
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
/* TLS library. */
+#ifdef USE_TLSRPT
+#include <tlsrpt_wrapper.h>
+#endif
+
#define TLS_INTERNAL
#include <tls.h>
/* 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.
*/
*/
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:
strcpy(buf, "<unknown>");
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",
X509_verify_cert_error_string(err));
break;
}
+#ifdef USE_TLSRPT
+ vstring_free(err_vstr);
+#endif
}
#ifndef DONT_GRIPE
--- /dev/null
+/*++
+/* NAME
+/* tlsrpt_wrapper 3
+/* SUMMARY
+/* TLSRPT support for the SMTP and TLS protocol engines
+/* SYNOPSIS
+/* #include <tlsrpt_wrapper.h>
+/*
+/* #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 <sys_defs.h>
+#include <errno.h>
+#include <string.h>
+#if defined(USE_TLSRPT)
+#include <tlsrpt.h>
+#endif
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+#include <msg.h>
+#include <mymalloc.h>
+#include <name_code.h>
+#include <stringops.h>
+
+ /*
+ * Some functions are not #ifdef USE_TLSRPT.
+ */
+#define TLSRPT_WRAPPER_INTERNAL
+#include <tlsrpt_wrapper.h>
+#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 */
--- /dev/null
+#ifndef _TLSRPT_WRAPPER_INCLUDED_
+#define _TLSRPT_WRAPPER_INCLUDED_
+
+/*++
+/* NAME
+/* tlsrpt_wrapper 3h
+/* SUMMARY
+/* TLSRPT support for the SMTP and TLS protocol engines
+/* SYNOPSIS
+/* #include <tlsrpt_wrapper.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * System library.
+ */
+#if defined(USE_TLS)
+
+#if defined(USE_TLSRPT)
+
+#include <tlsrpt.h>
+
+ /*
+ * 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 <argv.h>
+
+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_ */
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
#define TLS_INTERNAL /* XXX */
#include <tls.h>
#include <tls_proxy.h>
+#include <tlsrpt_wrapper.h>
/*
* Application-specific.
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);
}
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 \
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.
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
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
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
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
/* char *arg;
/* ssize_t arg_len;
/*
+/* ARGV *argv_addv(argvp, argv)
+/* ARGV *argvp;
+/* const char **argv;
+/*
/* void argv_terminate(argvp);
/* ARGV *argvp;
/*
/* 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.
/*
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System libraries. */
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)
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)
{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,
};
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 */
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 *);
/* 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.
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
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]);
}
#ifdef TEST
-#include <argv.h>
/*
* 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;
-
-#define DECODE(b,x,l) { \
- if (hex_decode((b),(x),(l)) == 0) \
- msg_panic("bad hex: %s", (x)); \
- }
-#define VERIFY(b,t) { \
- if (strcmp((b), (t)) != 0) \
- msg_panic("bad test: %s", (b)); \
- }
+ VSTRING *buf = vstring_alloc(1);
+ int pass = 0;
+ int fail = 0;
+ const TEST_CASE *tp;
+
+ for (tp = test_cases; tp->label != 0; tp++) {
+ VSTRING *out;
+ int ok = 0;
- 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("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++;
+ }
+ }
+ msg_info("PASS=%d FAIL=%d", pass, fail);
+ return (fail > 0);
}
#endif
*/
#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)
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
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
--- /dev/null
+/*++
+/* NAME
+/* mystrerror 3
+/* SUMMARY
+/* convert error number to string
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <sys_defs.h>
+#include <string.h>
+
+ /*
+ * Utility library.
+ */
+#include <stringops.h>
+
+/* mystrerror - convert error number to string */
+
+const char *mystrerror(int errnum)
+{
+ return (errnum ? strerror(errnum) : "Application error");
+}
--- /dev/null
+/*++
+/* NAME
+/* sane_sockaddr_to_hostaddr 3
+/* SUMMARY
+/* Sanitize IPv4 in IPv6 address
+/* SYNOPSIS
+/* #include <myaddrinfo.h>
+/*
+/* 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 <sys_defs.h>
+
+ /*
+ * Utility library.
+ */
+#include <myaddrinfo.h>
+#include <inet_proto.h>
+
+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);
+}
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" */
#include "mymalloc.h"
#include "vbuf.h"
#include "vstring.h"
+#include "stringops.h"
#include "vbuf_print.h"
/*
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)