]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.10-20240926
authorWietse Z Venema <wietse@porcupine.org>
Thu, 26 Sep 2024 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Sat, 28 Sep 2024 19:15:49 +0000 (05:15 +1000)
71 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/Makefile.in
postfix/README_FILES/AAAREADME
postfix/README_FILES/TLSRPT_README [new file with mode: 0644]
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/conf/postfix-files
postfix/html/TLSRPT_README.html [new file with mode: 0644]
postfix/html/index.html
postfix/html/lmtp.8.html
postfix/html/postcat.1.html
postfix/html/postconf.5.html
postfix/html/smtp.8.html
postfix/man/man1/postcat.1
postfix/man/man5/postconf.5
postfix/man/man8/smtp.8
postfix/mantools/check-snapshot-nonprod
postfix/mantools/postlink
postfix/proto/Makefile.in
postfix/proto/TLSRPT_README.html [new file with mode: 0644]
postfix/proto/postconf.proto
postfix/proto/stop
postfix/proto/stop.double-cc
postfix/proto/stop.double-proto-html
postfix/proto/stop.spell-cc
postfix/proto/stop.spell-proto-html
postfix/src/dns/dns.h
postfix/src/dns/dns_rr.c
postfix/src/dns/dns_rr_test.c
postfix/src/global/haproxy_srvr.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/postcat/postcat.c
postfix/src/postscreen/postscreen_endpt.c
postfix/src/posttls-finger/posttls-finger.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_tls_policy.c
postfix/src/smtp/smtp_tlsrpt.c [new file with mode: 0644]
postfix/src/tls/Makefile.in
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_proxy.h
postfix/src/tls/tls_proxy_client_print.c
postfix/src/tls/tls_proxy_client_scan.c
postfix/src/tls/tls_proxy_context_print.c
postfix/src/tls/tls_proxy_context_scan.c
postfix/src/tls/tls_server.c
postfix/src/tls/tls_verify.c
postfix/src/tls/tlsrpt_wrapper.c [new file with mode: 0644]
postfix/src/tls/tlsrpt_wrapper.h [new file with mode: 0644]
postfix/src/tlsproxy/Makefile.in
postfix/src/tlsproxy/tlsproxy.c
postfix/src/util/Makefile.in
postfix/src/util/argv.c
postfix/src/util/argv.h
postfix/src/util/hex_code.c
postfix/src/util/hex_code.h
postfix/src/util/myaddrinfo.h
postfix/src/util/mystrerror.c [new file with mode: 0644]
postfix/src/util/sane_sockaddr_to_hostaddr.c [new file with mode: 0644]
postfix/src/util/stringops.h
postfix/src/util/vbuf_print.c

index 8ee03dbe70d2992b1a03b756ed67d62e4f81fe1a..d14b89c2277831be00c40c63d3a1c235f447d3f4 100644 (file)
 -TTEST_CASE
 -TTLSMGR_SCACHE
 -TTLSP_STATE
+-TTLSRPT_WRAPPER
 -TTLS_APPL_STATE
 -TTLS_CERTS
 -TTLS_CLIENT_INIT_PROPS
index dcf6e2a54fcbf512715ac55d240833bfd7041681..d99d6aa8c7760f7ea85f3c88a08e5522d1a6e99d 100644 (file)
@@ -28186,6 +28186,27 @@ Apologies for any names omitted.
 20240808
 
        Typofix in comment. File: global/normalize_mailhost_addr.c.
+20240730 
+
+       Infrastructure: added argv_addv() function to append an
+       array of strings. File: util/argv.c.
+
+20240809
+
+       Infrastructure: added a dns_rr_detach() function to extract
+       one DNS record from a list. Files: dns/dns_rr.c, dns_rr_test.c.
+
+20240816
+
+       Infrastructure: factored out strerror() wrapper that reports
+       "Application error" instead of "Success" when errno == 0.
+       Files: util/mystrerror.c, util/vbuf_print.c.
+
+20240822
+
+       Infrastructure: added "append to buffer" option to the
+       hex_encode_opt() function. Files: util/hex_encode.[hc];
 
 20240831
 
@@ -28237,7 +28258,46 @@ Apologies for any names omitted.
        OpenSSL 3.2) SSL_get0_group_name() function. Viktor Dukhovni.
        Files: src/tls/tls.h, src/tls/tls_dh.c, src/tls/tls_misc.c.
 
+20240923
+
+       Cleanup: No user-visible change. Updated TLSRPT related
+       internal comments and internal identifiers; updated error
+       logging after changes in libtlsrpt error-to-string conversion
+       functions; minor changes to improve robustness.
+
 20240924
 
        Misc. cleanups. Viktor Dukhovni. Files: src/tls/tls_dh.c,
        proto/postconf.proto, src/global/mail_params.h.
+
+       TLSRPT documentation cleanups. File: proto/TLSRPT_README.html.
+
+20240924
+
+       Code health: added proper unit tests to hex_code.c.
+
+       Code health: deduplicated code instances that convert an
+       IPv4-in-IPv6 address (::ffff:a.b.c.d) to IPv4 form, but
+       only if IPv4 support is enabled. Files: myaddrinfo,h,
+       sane_sockaddr_to_hostaddr.c, global/haproxy_srvr.c,
+       postscreen/postscreen_endpt.c, smtp/smtp_tlsrpt.c.
+
+20240925
+
+       TLSRPT support: add routine logging of TLSRPT 'success' and
+       'failure' events, as there is no other easy way to find out
+       what the Postfix TLSRPT client is doing. Document what this
+       logging looks like, that it is not logged for reused TCP
+       connections, and how to recognize such connections. Files:
+       tls/tlsrpt_wrapper.c, proto/TLSRPT_README.html.
+
+       TLSRPT support: simplified the handling of reused TLS
+       sessions. Report all TLS handshakes regardless of whether
+       or not a session is reused. Note that there is only one TLS
+       handshake for a reused SMTP connection. Files: smtp/smtp_proto.c,
+       tls/tls_client.c, proto/TLSRPT_README.html.
+
+       Cleanup: pre-release checks for snapshot and nonprod releases.
+       File: mantools/check-snapshot-nonprod.
+
+       First general Postfix release with TLSRPT support.
index 6f2d06491777be048e49f2cb5d31ec0570c0b50f..d7152b57e0f522301123189b3f5cbe9494ed6fc6 100644 (file)
@@ -115,9 +115,9 @@ manpages:
        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:
@@ -130,7 +130,10 @@ missing-proxy-read-maps-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 -
index 94d552ece498af240de410ab1e8c210a22139f46..d3b0817eac825afa52d3c73fe9bee11223923b5c 100644 (file)
@@ -11,6 +11,7 @@ G\bGe\ben\bne\ber\bra\bal\bl c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
   * 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
diff --git a/postfix/README_FILES/TLSRPT_README b/postfix/README_FILES/TLSRPT_README
new file mode 100644 (file)
index 0000000..55538a2
--- /dev/null
@@ -0,0 +1,277 @@
+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.
+
index cf7bfc4e08cc638dc7272c44deaf133ba56fa0c4..660f335116d0ed2bf498ba09a6717cabe3864c5b 100644 (file)
@@ -25,3 +25,13 @@ now also distributed with the more recent Eclipse Public License
 (EPL) 2.0. Recipients can choose to take the software under the
 license of their choice. Those who are more comfortable with the
 IPL can continue with that license.
+
+[Feature 20240926]
+
+Support for the TLSRPT protocol (defined in RFC 8460). With this,
+an email receiving domain can publish a policy in DNS, and request
+daily summary reports for successful and failed SMTP-over-TLS
+connections to that domain's MX hosts.
+
+Postfix supports TLSRPT summaries for DANE (built-in) and MTA-STS
+(via an smtp_tls_policy_maps plugin). For details, see TLSRPT_README.
index baa23fdd1d1f0bc145cc115a8a9c41c49bb9432f..721fc9a58269ce041582382d67e77a6311f855c2 100644 (file)
@@ -6,6 +6,11 @@ Wish list:
 
        Disable -DSNAPSHOT and -DNONPROD in makedefs.
 
+       Add smtp_tlsrpt_allow_list feature (default: static:all) to limit
+       the domains for which Postfix generates TLSRPT daily sumamries.
+
+       Add unit tests for smtp_tlsrpt.c, tlstrpd_wrapper.c, ...
+
        Add tests for Message-ID extraction in the cleanup daemon.
 
        When debug logging is enabled, dict_db_open() logs a newline
index 5a939822bcc959bd085fc5a348bc6e1fc6fd1fd4..2467af478b120348d6827e5f94c408fcb10552bc 100644 (file)
@@ -330,6 +330,7 @@ $readme_directory/STANDARD_CONFIGURATION_README:f:root:-:644
 $readme_directory/STRESS_README:f:root:-:644
 $readme_directory/TLS_LEGACY_README:f:root:-:644
 $readme_directory/TLS_README:f:root:-:644
+$readme_directory/TLSRPT_README:f:root:-:644
 $readme_directory/TUNING_README:f:root:-:644
 $readme_directory/ULTRIX_README:f:root:-:644
 $readme_directory/UUCP_README:f:root:-:644
@@ -392,6 +393,7 @@ $html_directory/STANDARD_CONFIGURATION_README.html:f:root:-:644
 $html_directory/STRESS_README.html:f:root:-:644
 $html_directory/TLS_LEGACY_README.html:f:root:-:644
 $html_directory/TLS_README.html:f:root:-:644
+$html_directory/TLSRPT_README.html:f:root:-:644
 $html_directory/TUNING_README.html:f:root:-:644
 $html_directory/ULTRIX_README.html:f:root:-:644:o
 $html_directory/UUCP_README.html:f:root:-:644
diff --git a/postfix/html/TLSRPT_README.html b/postfix/html/TLSRPT_README.html
new file mode 100644 (file)
index 0000000..9e6e6e0
--- /dev/null
@@ -0,0 +1,410 @@
+<!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> --&gt; </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT client <br> library
+</td> <td> <tt> --&gt; </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT summary <br> generator
+</td> <td> <tt> --&gt; </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]
+&nbsp;
+TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
+    failure_type=starttls_not_supported
+&nbsp;
+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)
+&nbsp;
+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
+&nbsp
+/etc/postfix/transport:
+    smtp-tls-report@example.com     allow-plaintext:
+    ...
+&nbsp
+/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>
index a7da6cb510c7e57854b419a0069103ea407e8da6..610fac87ce593e2d75909c108ca57e7d20ca1bde 100644 (file)
@@ -45,6 +45,8 @@ configuration examples </a>
 
 <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>
index 4cc187c75174313bead975256ac969be83787590..dbf71d264c765c9ea55d750e7f2cb689de37aad3 100644 (file)
@@ -746,41 +746,48 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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 &lt; 2.3 control for the Postfix SMTP  client  TLS
+              Obsolete  Postfix  &lt; 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>
@@ -792,19 +799,19 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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>
@@ -818,13 +825,13 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>
@@ -834,17 +841,17 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>
@@ -858,23 +865,23 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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:
@@ -885,13 +892,13 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>.
 
@@ -899,54 +906,54 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
 
        <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>
@@ -954,46 +961,46 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
 
 <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>
@@ -1007,21 +1014,21 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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>
@@ -1041,7 +1048,7 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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:
@@ -1053,14 +1060,14 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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:
@@ -1082,7 +1089,7 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>
index 0d228d48a41b6efbb1826d619fa0afcca8f61099..319c0d90a5838b3ec4af522c49c4f26a02d27555 100644 (file)
@@ -43,6 +43,8 @@ POSTCAT(1)                                                          POSTCAT(1)
 
        <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.
index d3fbf24e34a5c7d847f522af710287996e3523c7..b703e66012866b1a2c3c6ba497eb5785b49c45ef 100644 (file)
@@ -14800,6 +14800,33 @@ Postfix versions. </p>
 <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 &ge; 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 &ge; 3.10. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_use_tls">smtp_use_tls</a>
index 4cc187c75174313bead975256ac969be83787590..dbf71d264c765c9ea55d750e7f2cb689de37aad3 100644 (file)
@@ -746,41 +746,48 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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 &lt; 2.3 control for the Postfix SMTP  client  TLS
+              Obsolete  Postfix  &lt; 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>
@@ -792,19 +799,19 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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>
@@ -818,13 +825,13 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>
@@ -834,17 +841,17 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>
@@ -858,23 +865,23 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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:
@@ -885,13 +892,13 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>.
 
@@ -899,54 +906,54 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
 
        <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>
@@ -954,46 +961,46 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
 
 <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>
@@ -1007,21 +1014,21 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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>
@@ -1041,7 +1048,7 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
               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:
@@ -1053,14 +1060,14 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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:
@@ -1082,7 +1089,7 @@ SMTP,(LMTP)                                                        SMTP,(LMTP)
        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>
index ae4ef526cf4ae9f5df7ebb1b22aed242d3ca5f94..0b089feb5fc92d64aa61dc3cff750d047cbc4082 100644 (file)
@@ -41,6 +41,8 @@ Show message envelope content.
 This feature is available in Postfix 2.7 and later.
 .IP \fB\-f\fR
 Prepend the file name to each output line.
+.sp
+This feature is available in Postfix 3.10 and later.
 .IP \fB\-h\fR
 Show message header content.  The \fB\-h\fR option produces
 output from the beginning of the message up to, but not
index 339b388530aacaa5c5f0db6c711a130c3da3de49..a9cf9d060b913df76aad0b62f06323839a54ca9a 100644 (file)
@@ -9860,6 +9860,21 @@ More examples are in TLS_README, including examples for older
 Postfix versions.
 .PP
 This feature is available in Postfix 3.0 and later.
+.SH smtp_tlsrpt_enable (default: no)
+Enable support for RFC 8460 TLSRPT notifications. A mail receiving
+domain can publish a TLSRPT policy in DNS, to request periodic
+summaries of successful and failed SMTP over TLS connections to
+their mail servers. This feature requires that Postfix is built
+with a TLSRPT supporting library.
+.PP
+This feature is available in Postfix >= 3.10.
+.SH smtp_tlsrpt_socket_name (default: empty)
+The pathname of a UNIX\-domain datagram socket that is managed
+by a local TLSRPT reporting service. This parameter must specify a
+pathname (absolute, or relative to $queue_directory) when
+"smtp_tlsrpt_enable = yes".
+.PP
+This feature is available in Postfix >= 3.10.
 .SH smtp_use_tls (default: no)
 Opportunistic mode: use TLS when a remote SMTP server announces
 STARTTLS support, otherwise send the mail in the clear. Beware:
index 0145350cbd05ab52eb351610c916acf8b470ba11..a55d17636fcec64b2d0e2f8057d292f46012f285 100644 (file)
@@ -672,6 +672,12 @@ Available in Postfix version 3.9 and later:
 .IP "\fBsmtp_tls_enable_rpk (no)\fR"
 Request that remote SMTP servers send an RFC7250 raw public key
 instead of an X.509 certificate.
+.PP Available in Postfix version 3.10 and later:
+.IP "\fBsmtp_tlsrpt_enable (no)\fR"
+Enable support for RFC 8460 TLSRPT notifications.
+.IP "\fBsmtp_tlsrpt_socket_name (empty)\fR"
+The pathname of a UNIX\-domain datagram socket that is managed
+by a local TLSRPT reporting service.
 .SH "OBSOLETE STARTTLS CONTROLS"
 .na
 .nf
index b3a038f9b133c9634a19160ffd5d4ecdb5854321..9ca642b3ce2b051138ce5aaeeae4f6829c02fe2a 100755 (executable)
@@ -6,9 +6,23 @@ postfix-[0-9]*.[0-9]*.[0-9]*)
     test -f conf/makedefs.out || {
        echo "Error: no conf/makedefs.out" 1>&2; exit 1; }
     grep 'CCARGS.*-DSNAPSHOT' conf/makedefs.out && {
-       echo "Error: stable release builds with -DSNAPSHOT" 1>&2, exit 1; }
+       echo "Error: stable release builds with -DSNAPSHOT" 1>&2; exit 1; }
     grep 'CCARGS.*-DNONPROD' conf/makedefs.out && {
-       echo "Error: stable release builds with -DNONPROD" 1>&2, exit 1; }
+       echo "Error: stable release builds with -DNONPROD" 1>&2; exit 1; }
+    mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
+    test "postfix-$mail_version" = "$version" || {
+       echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
+    ;;
+postfix-[0-9]*.[0-9]*-*nonprod*)
+    grep 'CCARGS.*-DNONPROD' conf/makedefs.out || {
+       echo "Error: non-prod release builds without -DNONPROD" 1>&2; exit 1; }
+    mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
+    test "postfix-$mail_version" = "$version" || {
+       echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
+    ;;
+postfix-[0-9]*.[0-9]*-*)
+    grep 'CCARGS.*-DNONPROD' conf/makedefs.out && {
+       echo "Error: snapshot release builds with -DNONPROD" 1>&2; exit 1; }
     mail_version=$(sh postfix-env.sh bin/postconf -h mail_version) || exit 1
     test "postfix-$mail_version" = "$version" || {
        echo "Error: version '$mail_version' in src/global/mail_version.h does not match version in pathname '$(env - pwd)'" 1>&2; exit 1; }
index 6ca24c63dc659801e6de61881ce69a6b2ab3e910..6e7ba6954f11dd7513df905e80896ced5048f20a 100755 (executable)
@@ -721,6 +721,10 @@ while (<>) {
     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;
index f02ab1415744028734d4c6e1f2486055e697d89b..86476adf3755eff0e0143f40e45eacabc6075bba 100644 (file)
@@ -50,6 +50,7 @@ HTML  = ../html/ADDRESS_CLASS_README.html \
        ../html/STANDARD_CONFIGURATION_README.html \
        ../html/STRESS_README.html \
        ../html/TLS_README.html ../html/TLS_LEGACY_README.html \
+       ../html/TLSRPT_README.html \
        ../html/TUNING_README.html \
        ../html/UUCP_README.html \
        ../html/VERP_README.html ../html/VIRTUAL_README.html \
@@ -100,6 +101,7 @@ README      = ../README_FILES/ADDRESS_CLASS_README \
        ../README_FILES/STANDARD_CONFIGURATION_README \
        ../README_FILES/STRESS_README \
        ../README_FILES/TLS_README ../README_FILES/TLS_LEGACY_README \
+       ../README_FILES/TLSRPT_README \
        ../README_FILES/TUNING_README \
        ../README_FILES/UUCP_README \
        ../README_FILES/VERP_README ../README_FILES/VIRTUAL_README \
@@ -343,6 +345,9 @@ clobber:
 ../html/TLS_LEGACY_README.html: TLS_LEGACY_README.html
        $(DETAB) $? | $(POSTLINK) >$@
 
+../html/TLSRPT_README.html: TLSRPT_README.html
+       $(DETAB) $? | $(POSTLINK) >$@
+
 ../README_FILES/ADDRESS_CLASS_README: ADDRESS_CLASS_README.html
        $(DETAB) $? | $(HT2READ) >$@
 
@@ -529,6 +534,9 @@ clobber:
 ../README_FILES/TLS_LEGACY_README: TLS_LEGACY_README.html
        $(DETAB) $? | $(HT2READ) >$@
 
+../README_FILES/TLSRPT_README: TLSRPT_README.html
+       $(DETAB) $? | $(HT2READ) >$@
+
 ../README_FILES/AAAREADME: ../html/index.html $(MAKEAAA)
        $(MAKEAAA) ../html/index.html | $(HT2READ) | $(DETAB) >$@
 
diff --git a/postfix/proto/TLSRPT_README.html b/postfix/proto/TLSRPT_README.html
new file mode 100644 (file)
index 0000000..8210eae
--- /dev/null
@@ -0,0 +1,410 @@
+<!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> --&gt; </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT client <br> library
+</td> <td> <tt> --&gt; </tt> </td>
+
+<td align="center" bgcolor="#f0f0ff"> TLSRPT summary <br> generator
+</td> <td> <tt> --&gt; </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]
+&nbsp;
+TLSRPT: status=failure, domain=example.org, receiving_mx=mail.example.org[ipaddr],
+    failure_type=starttls_not_supported
+&nbsp;
+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)
+&nbsp;
+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
+&nbsp
+/etc/postfix/transport:
+    smtp-tls-report@example.com     allow-plaintext:
+    ...
+&nbsp
+/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>
index d8e9084795d978433f0fde2c9aeaa624a48dbdac..0e6cd26b588efb1139a6bd6b169e2cfb2168c8ce 100644 (file)
@@ -19408,3 +19408,22 @@ announce 8BITMIME support, or when a message line exceeds the SMTP
 length limit. </p>
 
 <p> This feature is available in Postfix &ge; 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 &ge; 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 &ge; 3.10. </p>
index 03c799cfa3b250f552f02eb5b8b33e3c4e45fa8d..4755ed24f610c8024cee73d4173de8f50499e01e 100644 (file)
@@ -1623,3 +1623,31 @@ KEMs
 kex
 keyshare
 pkg
+RPT
+TLSRPT
+TLSRPTv
+TODOS
+WSP
+addv
+bugprone
+errnum
+libtlsrpt
+munge
+mystrerror
+protcol
+punycode
+pval
+rpt
+rua
+sockname
+tlsproxied
+tlsrpt
+trw
+datagram
+RPC
+datatracker
+webpki
+parsable
+mailto
+ipaddr
+STS
index bff4534d17e4ad78f07e758f6d737ba471365293..d0e35864a0e44c9c5381381276782cabddc9978e 100644 (file)
@@ -335,3 +335,10 @@ length  length of 0 31 0 127
 address  address string length 
  whether the standard End of DATA sequence CRLF CRLF is required and
  Require CRLF CRLF 
+ must start with a version field v TLSRPTv1 followed by WSP WSP
+ policies policy policy type 
+ policies policy policy string Ignored if the tls_policy_type
+ policies policy policy domain 
+additional_info  additional_info 
+ignored  ignored 
+USE_TLSRPT  USE_TLSRPT 
index a4b2332a2a387e2d8cccfca228f4007090f98119..9fb66ec655ed722c9215ce5a1433a1c114f2ce59 100644 (file)
@@ -358,3 +358,8 @@ expected to become a list of comma separated names br br This
 Postfix  Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
  CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0 
  dt dt dd 2 Also enable verbose logging in the Postfix TLS
+Postfix  Postfix legacy TLS Support
+ var run tlsrpt tlsrpt sock Relative names will work with and without Postfix chroot support Do not specify a location under a directory such as private or public that is already used by Postfix programs Only Postfix programs should create
+ Note the recommended socket location is still to be determined A good socket location would be under the Postfix queue directory for example smtp_tlsrpt_socket_name run tlsrpt tlsrpt sock The advantage of using a relative name is that
+ with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits 
+ TLSv1 2 with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits 
index 7397881e632938f2aa59e7dd3406c44afa99cde3..e2d5861e3639b22dde93a2c11867972a736f1da9 100644 (file)
@@ -1841,4 +1841,9 @@ foqvx
 ILP
 xxfi
 optionsv
+rcv
+snd
+sts
+tlsrprt
 bdefhnoqv
+deduplicated
index e76dbfa4de1a6178424db6d2ac7e8b9a86f4078d..0bb4f3963e343d49cea36f074042bb65d7e2fe14 100644 (file)
@@ -379,3 +379,11 @@ Dextrous
 ar
 liveness
 superset
+ltlsrpt
+sts
+STS
+STSv
+Sys
+Qsmtp
+Qsts
+gmail
index 987b988f150d767cc9558c058f27627ab963a069..0f8b0b92d5209062807752458a7403ee7605736c 100644 (file)
@@ -221,6 +221,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *);
 extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *);
 extern DNS_RR *dns_rr_shuffle(DNS_RR *);
 extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *);
+extern DNS_RR *dns_rr_detach(DNS_RR *, DNS_RR *);
 extern int var_dns_rr_list_limit;
 
  /*
index 882a42ffbeaa5a6a80df1561024b0e626b3ace77..e5775a276d9ff337b2b10c78663f9d3185e3fa66 100644 (file)
 /*     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
@@ -464,16 +473,24 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list)
 /* dns_rr_remove - remove record from list, return new list */
 
 DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record)
+{
+    list = dns_rr_detach(list, record);
+    dns_rr_free(record);
+    return (list);
+}
+
+/* dns_rr_detach - detach record from list, return new list */
+
+DNS_RR *dns_rr_detach(DNS_RR *list, DNS_RR *record)
 {
     if (list == 0)
-       msg_panic("dns_rr_remove: record not found");
+       msg_panic("dns_rr_detach: record not found");
 
     if (list == record) {
        list = record->next;
        record->next = 0;
-       dns_rr_free(record);
     } else {
-       list->next = dns_rr_remove(list->next, record);
+       list->next = dns_rr_detach(list->next, record);
     }
     return (list);
 }
index 7bbe769270497d512fbfaf74fa49f279d9f81b02..12cf01c58fe7a6d0eef79831cd176ca92cc72e5d 100644 (file)
@@ -361,6 +361,93 @@ static int append_to_elem_from_list_exact_fit(void)
     return (eq_dns_rr_free(got, want));
 }
 
+static int delete_middle_element(void)
+{
+    DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+    DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+    DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+    DNS_RR *got, *want, *list;
+
+    ((list = a)->next = b)->next = c;
+    (want = dns_rr_copy(a))->next = dns_rr_copy(c);
+    got = dns_rr_remove(list, b);
+
+    return (eq_dns_rr_free(got, want));
+}
+
+static int delete_first_element(void)
+{
+    DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+    DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+    DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+    DNS_RR *got, *want, *list;
+
+    ((list = a)->next = b)->next = c;
+    (want = dns_rr_copy(b))->next = dns_rr_copy(c);
+    got = dns_rr_remove(list, a);
+
+    return (eq_dns_rr_free(got, want));
+}
+
+static int delete_last_element(void)
+{
+    DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+    DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+    DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+    DNS_RR *got, *want, *list;
+
+    ((list = a)->next = b)->next = c;
+    (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+    got = dns_rr_remove(list, c);
+
+    return (eq_dns_rr_free(got, want));
+}
+
+static int detach_middle_element(void)
+{
+    DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+    DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+    DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+    DNS_RR *got, *want, *list;
+
+    ((list = a)->next = b)->next = c;
+    (want = dns_rr_copy(a))->next = dns_rr_copy(c);
+    got = dns_rr_detach(list, b);
+    dns_rr_free(b);
+
+    return (eq_dns_rr_free(got, want));
+}
+
+static int detach_first_element(void)
+{
+    DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+    DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+    DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+    DNS_RR *got, *want, *list;
+
+    ((list = a)->next = b)->next = c;
+    (want = dns_rr_copy(b))->next = dns_rr_copy(c);
+    got = dns_rr_detach(list, a);
+    dns_rr_free(a);
+
+    return (eq_dns_rr_free(got, want));
+}
+
+static int detach_last_element(void)
+{
+    DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4);
+    DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4);
+    DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4);
+    DNS_RR *got, *want, *list;
+
+    ((list = a)->next = b)->next = c;
+    (want = dns_rr_copy(a))->next = dns_rr_copy(b);
+    got = dns_rr_detach(list, c);
+    dns_rr_free(c);
+
+    return (eq_dns_rr_free(got, want));
+}
+
  /*
   * The test cases.
   */
@@ -400,9 +487,15 @@ static const TEST_CASE test_cases[] = {
     "append to element from list exact fit", append_to_elem_from_list_exact_fit,
 
     /*
-     * TODO: tests dns_rr_sort(), dns_rr_srv_sort(), dns_rr_remove(),
-     * dns_rr_shuffle(), etc.
+     * TODO: tests for dns_rr_sort(), dns_rr_srv_sort(), dns_rr_shuffle(),
+     * etc.
      */
+    "delete element from list (middle)", delete_middle_element,
+    "delete element from list (first)", delete_first_element,
+    "delete element from list (last)", delete_last_element,
+    "detach element from list (middle)", detach_middle_element,
+    "detach element from list (first)", detach_first_element,
+    "detach element from list (last)", detach_last_element,
     0,
 };
 
index 63147c1c5f863389fa6610adb0ad8a0a619f6861..211cf1d1aeb377d71beb1d5e2460e1398a892947 100644 (file)
@@ -250,15 +250,12 @@ static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
     }
     if (err == 0)
        err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
-              || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
-                                      addr, (MAI_SERVPORT_STR *) 0, 0));
+              || sane_sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
+                                         addr, (MAI_SERVPORT_STR *) 0, 0));
     if (res)
        freeaddrinfo(res);
     if (err)
        return (-1);
-    if (addr->buf[0] == ':' && strncasecmp("::ffff:", addr->buf, 7) == 0
-       && strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
-       memmove(addr->buf, addr->buf + 7, strlen(addr->buf) + 1 - 7);
     return (0);
 }
 
index 908341ae37e8a9f17ee9204c44ea933d10eac97e..d209bc03656191e69a2ce24564c0efb4c4989d3d 100644 (file)
@@ -4459,6 +4459,22 @@ extern bool var_ign_srv_lookup_err;
 #define DEF_ALLOW_SRV_FALLBACK 0
 extern bool var_allow_srv_fallback;
 
+ /*
+  * TLSRPT notification support. The lmtp_ names must be defined because the
+  * build system enforces that every smtp_ parameter has an lmtp_ variant.
+  */
+#define VAR_SMTP_TLSRPT_ENABLE "smtp_tlsrpt_enable"
+#define DEF_SMTP_TLSRPT_ENABLE "no"
+#define VAR_LMTP_TLSRPT_ENABLE "lmtp_tlsrpt_enable"
+#define DEF_LMTP_TLSRPT_ENABLE DEF_SMTP_TLSRPT_ENABLE
+extern bool var_smtp_tlsrpt_enable;
+
+#define VAR_SMTP_TLSRPT_SOCKNAME "smtp_tlsrpt_socket_name"
+#define DEF_SMTP_TLSRPT_SOCKNAME ""
+#define VAR_LMTP_TLSRPT_SOCKNAME "lmtp_tlsrpt_socket_name"
+#define DEF_LMTP_TLSRPT_SOCKNAME DEF_SMTP_TLSRPT_SOCKNAME
+extern char *var_smtp_tlsrpt_sockname;
+
 /* LICENSE
 /* .ad
 /* .fi
@@ -4473,6 +4489,9 @@ extern bool var_allow_srv_fallback;
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 #endif
index 3acccc6f64272cb218ddc7ccb223b7fc915c524f..56082f7d9d0b921b106323e5d10a8efd4bca20d8 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20240924"
+#define MAIL_RELEASE_DATE      "20240926"
 #define MAIL_VERSION_NUMBER    "3.10"
 
 #ifdef SNAPSHOT
index 1461578fc84e2e7509a037e91c796a2cdb076cff..44c0ce6baafa095391a82e61ad8adcbde4f5a3cc 100644 (file)
@@ -35,6 +35,8 @@
 /*     This feature is available in Postfix 2.7 and later.
 /* .IP \fB-f\fR
 /*     Prepend the file name to each output line.
+/* .sp
+/*     This feature is available in Postfix 3.10 and later.
 /* .IP \fB-h\fR
 /*     Show message header content.  The \fB-h\fR option produces
 /*     output from the beginning of the message up to, but not
index 36949e32f384ef7a575587a5eefdfbab171d4adc..46e6579946733a626881e2aeede1871c1c1ba8d3 100644 (file)
 
 static const INET_PROTO_INFO *proto_info;
 
-/* psc_sockaddr_to_hostaddr - transform endpoint address and port to string */
-
-static int psc_sockaddr_to_hostaddr(struct sockaddr *addr_storage,
-                                           SOCKADDR_SIZE addr_storage_len,
-                                           MAI_HOSTADDR_STR *addr_buf,
-                                           MAI_SERVPORT_STR *port_buf,
-                                           int socktype)
-{
-    int     aierr;
-
-    if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len,
-                                     addr_buf, port_buf, socktype)) == 0
-       && strncasecmp("::ffff:", addr_buf->buf, 7) == 0
-       && strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
-       memmove(addr_buf->buf, addr_buf->buf + 7,
-               sizeof(addr_buf->buf) - 7);
-    return (aierr);
-}
-
 /* psc_endpt_local_lookup - look up local system connection information */
 
 void    psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
@@ -156,7 +137,7 @@ void    psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
      * Convert the remote SMTP client address and port to printable form for
      * logging and access control.
      */
-    else if ((aierr = psc_sockaddr_to_hostaddr(
+    else if ((aierr = sane_sockaddr_to_hostaddr(
                                          (struct sockaddr *) &addr_storage,
                                        addr_storage_len, &smtp_client_addr,
                                    &smtp_client_port, SOCK_STREAM)) != 0) {
@@ -180,7 +161,7 @@ void    psc_endpt_local_lookup(VSTREAM *smtp_client_stream,
      * Convert the local SMTP server address and port to printable form for
      * logging.
      */
-    else if ((aierr = psc_sockaddr_to_hostaddr(
+    else if ((aierr = sane_sockaddr_to_hostaddr(
                                          (struct sockaddr *) &addr_storage,
                                        addr_storage_len, &smtp_server_addr,
                                    &smtp_server_port, SOCK_STREAM)) != 0) {
index b474a40066e1521fcd805cf369b73533b5e39f6a..75571f7f2cc64565720d15cac8b2a687a4f3f6a0 100644 (file)
@@ -835,6 +835,8 @@ static int starttls(STATE *state)
                                     = vstring_str(cipher_exclusions),
                                     matchargv = state->match,
                                     mdalg = state->mdalg,
+                                    tlsrpt = 0,
+                                    ffail_type = 0,
                                     dane = state->ddane ?
                                     state->ddane : state->dane);
 
@@ -939,6 +941,8 @@ static int starttls(STATE *state)
                             = vstring_str(cipher_exclusions),
                             matchargv = state->match,
                             mdalg = state->mdalg,
+                            tlsrpt = 0,
+                            ffail_type = 0,
                          dane = state->ddane ? state->ddane : state->dane);
     }                                          /* tlsproxy_mode */
     vstring_free(cipher_exclusions);
index b9f04ca1385d81d037cce4623ee875e62a1b7332..685a72b679d257675e4ff37a4bfa797e2e90fc8d 100644 (file)
@@ -2,11 +2,11 @@ SHELL = /bin/sh
 SRCS   = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
        smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \
        smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
-       smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c
+       smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c smtp_tlsrpt.c
 OBJS   = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
        smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \
        smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
-       smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o
+       smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o smtp_tlsrpt.o
 HDRS   = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -267,6 +267,7 @@ smtp_connect.o: ../../include/timed_connect.h
 smtp_connect.o: ../../include/tls.h
 smtp_connect.o: ../../include/tls_proxy.h
 smtp_connect.o: ../../include/tok822.h
+smtp_connect.o: ../../include/valid_hostname.h
 smtp_connect.o: ../../include/vbuf.h
 smtp_connect.o: ../../include/vstream.h
 smtp_connect.o: ../../include/vstring.h
@@ -447,6 +448,7 @@ smtp_proto.o: ../../include/stringops.h
 smtp_proto.o: ../../include/sys_defs.h
 smtp_proto.o: ../../include/tls.h
 smtp_proto.o: ../../include/tls_proxy.h
+smtp_proto.o: ../../include/tlsrpt_wrapper.h
 smtp_proto.o: ../../include/tok822.h
 smtp_proto.o: ../../include/uxtext.h
 smtp_proto.o: ../../include/vbuf.h
@@ -737,6 +739,7 @@ smtp_state.o: ../../include/string_list.h
 smtp_state.o: ../../include/sys_defs.h
 smtp_state.o: ../../include/tls.h
 smtp_state.o: ../../include/tls_proxy.h
+smtp_state.o: ../../include/tlsrpt_wrapper.h
 smtp_state.o: ../../include/tok822.h
 smtp_state.o: ../../include/vbuf.h
 smtp_state.o: ../../include/vstream.h
@@ -770,6 +773,7 @@ smtp_tls_policy.o: ../../include/name_mask.h
 smtp_tls_policy.o: ../../include/nvtable.h
 smtp_tls_policy.o: ../../include/recipient_list.h
 smtp_tls_policy.o: ../../include/resolve_clnt.h
+smtp_tls_policy.o: ../../include/sane_strtol.h
 smtp_tls_policy.o: ../../include/scache.h
 smtp_tls_policy.o: ../../include/sock_addr.h
 smtp_tls_policy.o: ../../include/string_list.h
@@ -777,6 +781,7 @@ smtp_tls_policy.o: ../../include/stringops.h
 smtp_tls_policy.o: ../../include/sys_defs.h
 smtp_tls_policy.o: ../../include/tls.h
 smtp_tls_policy.o: ../../include/tls_proxy.h
+smtp_tls_policy.o: ../../include/tlsrpt_wrapper.h
 smtp_tls_policy.o: ../../include/tok822.h
 smtp_tls_policy.o: ../../include/valid_hostname.h
 smtp_tls_policy.o: ../../include/valid_utf8_hostname.h
@@ -785,6 +790,48 @@ smtp_tls_policy.o: ../../include/vstream.h
 smtp_tls_policy.o: ../../include/vstring.h
 smtp_tls_policy.o: smtp.h
 smtp_tls_policy.o: smtp_tls_policy.c
+smtp_tlsrpt.o: ../../include/argv.h
+smtp_tlsrpt.o: ../../include/attr.h
+smtp_tlsrpt.o: ../../include/check_arg.h
+smtp_tlsrpt.o: ../../include/deliver_request.h
+smtp_tlsrpt.o: ../../include/dict.h
+smtp_tlsrpt.o: ../../include/dns.h
+smtp_tlsrpt.o: ../../include/dsn.h
+smtp_tlsrpt.o: ../../include/dsn_buf.h
+smtp_tlsrpt.o: ../../include/header_body_checks.h
+smtp_tlsrpt.o: ../../include/header_opts.h
+smtp_tlsrpt.o: ../../include/hex_code.h
+smtp_tlsrpt.o: ../../include/htable.h
+smtp_tlsrpt.o: ../../include/inet_proto.h
+smtp_tlsrpt.o: ../../include/mail_params.h
+smtp_tlsrpt.o: ../../include/maps.h
+smtp_tlsrpt.o: ../../include/match_list.h
+smtp_tlsrpt.o: ../../include/midna_domain.h
+smtp_tlsrpt.o: ../../include/mime_state.h
+smtp_tlsrpt.o: ../../include/msg.h
+smtp_tlsrpt.o: ../../include/msg_stats.h
+smtp_tlsrpt.o: ../../include/myaddrinfo.h
+smtp_tlsrpt.o: ../../include/myflock.h
+smtp_tlsrpt.o: ../../include/mymalloc.h
+smtp_tlsrpt.o: ../../include/name_code.h
+smtp_tlsrpt.o: ../../include/name_mask.h
+smtp_tlsrpt.o: ../../include/nvtable.h
+smtp_tlsrpt.o: ../../include/recipient_list.h
+smtp_tlsrpt.o: ../../include/resolve_clnt.h
+smtp_tlsrpt.o: ../../include/scache.h
+smtp_tlsrpt.o: ../../include/sock_addr.h
+smtp_tlsrpt.o: ../../include/string_list.h
+smtp_tlsrpt.o: ../../include/stringops.h
+smtp_tlsrpt.o: ../../include/sys_defs.h
+smtp_tlsrpt.o: ../../include/tls.h
+smtp_tlsrpt.o: ../../include/tls_proxy.h
+smtp_tlsrpt.o: ../../include/tlsrpt_wrapper.h
+smtp_tlsrpt.o: ../../include/tok822.h
+smtp_tlsrpt.o: ../../include/vbuf.h
+smtp_tlsrpt.o: ../../include/vstream.h
+smtp_tlsrpt.o: ../../include/vstring.h
+smtp_tlsrpt.o: smtp.h
+smtp_tlsrpt.o: smtp_tlsrpt.c
 smtp_trouble.o: ../../include/argv.h
 smtp_trouble.o: ../../include/attr.h
 smtp_trouble.o: ../../include/bounce.h
index 385c81ff4a493a80ce32045b59b1d6d37032dcde..c41cf91aec5d995fd144b49c70d617dce4367656 100644 (file)
@@ -67,6 +67,7 @@
        VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
        VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
        VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
+       VAR_LMTP_TLSRPT_SOCKNAME, DEF_LMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
        0,
     };
     static const CONFIG_TIME_TABLE lmtp_time_table[] = {
     };
     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,
     };
index 51b2e6dba67aa0b70d2788523b0a1b13d63ee83a..23ce94ebfa6aad073a04744e80266a8fc575127b 100644 (file)
 /* .IP "\fBsmtp_tls_enable_rpk (no)\fR"
 /*     Request that remote SMTP servers send an RFC7250 raw public key
 /*     instead of an X.509 certificate.
+/* .PP Available in Postfix version 3.10 and later:
+/* .IP "\fBsmtp_tlsrpt_enable (no)\fR"
+/*     Enable support for RFC 8460 TLSRPT notifications.
+/* .IP "\fBsmtp_tlsrpt_socket_name (empty)\fR"
+/*     The pathname of a UNIX-domain datagram socket that is managed
+/*     by a local TLSRPT reporting service.
 /* OBSOLETE STARTTLS CONTROLS
 /* .ad
 /* .fi
@@ -1146,6 +1152,8 @@ int     var_smtp_min_data_rate;
 char   *var_use_srv_lookup;
 bool    var_ign_srv_lookup_err;
 bool    var_allow_srv_fallback;
+bool    var_smtp_tlsrpt_enable;
+char   *var_smtp_tlsrpt_sockname;
 
  /* Special handling of 535 AUTH errors. */
 char   *var_smtp_sasl_auth_cache_name;
@@ -1399,7 +1407,23 @@ static void post_init(char *unused_name, char **argv)
        var_disable_dns = (smtp_dns_support == SMTP_DNS_DISABLED);
     }
 
+#if !defined(USE_TLS) || !defined(USE_TLSRPT)
+    if (var_smtp_tlsrpt_enable)
+       msg_warn("TLSRPT is selected, but TLSRPT is not compiled in");
+#endif
 #ifdef USE_TLS
+#ifdef USE_TLSRPT
+    if (var_smtp_tlsrpt_enable) {
+       if (smtp_mode) {
+           if (smtp_tlsrpt_post_jail(VAR_SMTP_TLSRPT_SOCKNAME,
+                                     var_smtp_tlsrpt_sockname) < 0)
+               var_smtp_tlsrpt_enable = 0;
+       } else {
+           msg_warn("TLSRPT support is not implemented for LMTP");
+           var_smtp_tlsrpt_enable = 0;
+       }
+    }
+#endif                                         /* USE_TLSRPT */
     if (smtp_mode) {
        smtp_tls_insecure_mx_policy =
            tls_level_lookup(var_smtp_tls_insecure_mx_policy);
index 60c68f845f660d9a14c532cb678979c9bd9ca094..49e1115426010fb2a954bb1ea9beac208485d474 100644 (file)
@@ -108,8 +108,28 @@ typedef struct SMTP_TLS_POLICY {
     char   *sni;                       /* Optional SNI name when not DANE */
     int     conn_reuse;                        /* enable connection reuse */
     int     enable_rpk;                        /* Enable server->client RPK */
+    /* External policy info, for TLSRPT. */
+    int     ext_policy_ttl;            /* TTL from DNS etc. */
+    char   *ext_policy_type;           /* (sts) */
+    ARGV   *ext_policy_strings;                /* policy strings from DNS etc. */
+    char   *ext_policy_domain;         /* policy scope */
+    ARGV   *ext_mx_host_patterns;      /* (sts) MX host patterns */
+    char   *ext_policy_failure;                /* (sts) policy failure */
 } SMTP_TLS_POLICY;
 
+ /*
+  * Names and values for external policy attributes in smtp_tls_policy_maps.
+  * These are not #ifdef USE_TLSRPT, so that a TLSRPT-aware STS plugin can be
+  * used whether or not Postfix was built with TLSRPT support.
+  */
+#define EXT_POLICY_TTL         "policy_ttl"
+#define EXT_POLICY_TTL_UNSET   (-1)
+#define EXT_POLICY_TYPE                "policy_type"
+#define EXT_POLICY_DOMAIN      "policy_domain"
+#define EXT_POLICY_STRING      "policy_string"
+#define EXT_MX_HOST_PATTERN    "mx_host_pattern"
+#define EXT_POLICY_FAILURE     "policy_failure"
+
  /*
   * smtp_tls_policy.c
   */
@@ -144,6 +164,12 @@ extern void smtp_tls_policy_cache_flush(void);
        _tls_policy_init_tmp->sni = 0; \
        _tls_policy_init_tmp->conn_reuse = 0; \
        _tls_policy_init_tmp->enable_rpk = 0; \
+       _tls_policy_init_tmp->ext_policy_ttl = EXT_POLICY_TTL_UNSET; \
+       _tls_policy_init_tmp->ext_policy_type = 0; \
+       _tls_policy_init_tmp->ext_policy_domain = 0; \
+       _tls_policy_init_tmp->ext_policy_strings = 0; \
+       _tls_policy_init_tmp->ext_mx_host_patterns = 0; \
+       _tls_policy_init_tmp->ext_policy_failure = 0; \
     } while (0)
 
 #endif
@@ -171,6 +197,9 @@ typedef struct SMTP_STATE {
      */
 #ifdef USE_TLS
     SMTP_TLS_POLICY tls[1];            /* Usage: state->tls->member */
+#ifdef USE_TLSRPT
+    struct TLSRPT_WRAPPER *tlsrpt;
+#endif
 #endif
 
     /*
@@ -757,6 +786,18 @@ extern void smtp_quote_821_address(VSTRING *, const char *);
   */
 extern int smtp_hfrom_format;
 
+ /*
+  * smtp_tlsrpt.c.
+  */
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+extern int smtp_tlsrpt_post_jail(const char *sockname_pname, const char *sockname_pval);
+extern void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain);
+extern void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state);
+extern void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state);
+extern void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *, const char *ehlo_resp);
+
+#endif                                 /* USE_TLSRPT && USE_TLS */
+
 /* LICENSE
 /* .ad
 /* .fi
index 68faca18ef821ab73c6434d2c2039f64ab0e7e62..ea943fe779f71f3b5969accaa3e6d09478c6384f 100644 (file)
 #include <mail_error.h>
 #include <dsn_buf.h>
 #include <mail_addr.h>
+#include <valid_hostname.h>
 
 /* DNS library. */
 
@@ -911,6 +912,21 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
 
        SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state);
 
+       /*
+        * TODO(wietse) If the domain publishes a TLSRPT policy, they expect
+        * that clients use SMTP over TLS. Should we upgrade a TLS security
+        * level of "may" to "encrypt"? This would disable falling back to
+        * plaintext, and could break interoperability with receivers that
+        * crank up security up to 11.
+        */
+#ifdef USE_TLSRPT
+       if (smtp_mode && var_smtp_tlsrpt_enable
+           && !valid_hostaddr(domain, DONT_GRIPE))
+           smtp_tlsrpt_create_wrapper(state, domain);
+       else
+           state->tlsrpt = 0;
+#endif                                         /* USE_TLSRPT */
+
        /*
         * Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a
         * quoted domain is specified or when DNS lookups are disabled.
@@ -1076,6 +1092,18 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
                session->state = state;
 #ifdef USE_TLS
                session->tls_nexthop = domain;
+
+               /*
+                * Update TLSRPT state even if this is a reused SMTP
+                * connection. If for some unlikely reason we must report a
+                * problem, then we must report correct information.
+                */
+#ifdef USE_TLSRPT
+               if (state->tlsrpt && state->tls->level > TLS_LEV_NONE) {
+                   smtp_tlsrpt_set_tls_policy(state);
+                   smtp_tlsrpt_set_tcp_connection(state);
+               }
+#endif                                         /* USE_TLSRPT */
 #endif
                if (addr->pref == domain_best_pref)
                    session->features |= SMTP_FEATURE_BEST_MX;
index cebff938018da0e3a16f1b7af018d305ebf8ac1d..f58f9eb3461e7e86892b9de9eab18eb58af31e6c 100644 (file)
@@ -68,6 +68,7 @@
        VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0,
        VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
        VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
+       VAR_SMTP_TLSRPT_SOCKNAME, DEF_SMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
        0,
     };
     static const CONFIG_TIME_TABLE smtp_time_table[] = {
     };
     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,
     };
index e022bc2cff262e14a2a43e8c4eb9372c90f3a1c9..1b0222c17691c05433b9538f15a7a19d2e86476c 100644 (file)
@@ -78,6 +78,9 @@
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
 /*
+/*     Wietse Venema
+/*     porcupine.org
+/*
 /*     Pipelining code in cooperation with:
 /*     Jon Ribbens
 /*     Oaktree Internet Solutions Ltd.,
 #include <xtext.h>
 #include <uxtext.h>
 #include <smtputf8.h>
+#if defined(USE_TLS) && defined(USE_TLSRPT)
+#include <tlsrpt_wrapper.h>
+#endif
 
 /* Application-specific. */
 
@@ -475,6 +481,11 @@ int     smtp_helo(SMTP_STATE *state)
                else
                    session->features &= ~SMTP_FEATURE_ESMTP;
            }
+#ifdef USE_TLSRPT
+           if (state->tlsrpt
+               && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0)
+               smtp_tlsrpt_set_ehlo_resp(state, resp->str);
+#endif
        }
        if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
            where = "performing the HELO handshake";
@@ -484,6 +495,10 @@ int     smtp_helo(SMTP_STATE *state)
                                       "host %s refused to talk to me: %s",
                                       session->namaddr,
                                       translit(resp->str, "\n", " ")));
+#ifdef USE_TLSRPT
+           if (state->tlsrpt)
+               trw_set_ehlo_resp(state->tlsrpt, resp->str);
+#endif
        }
     } else {
        where = "performing the LHLO handshake";
@@ -798,11 +813,19 @@ int     smtp_helo(SMTP_STATE *state)
             * although support for it was announced in the EHLO response.
             */
            session->features &= ~SMTP_FEATURE_STARTTLS;
-           if (TLS_REQUIRED(state->tls->level))
+           if (TLS_REQUIRED(state->tls->level)) {
+#ifdef USE_TLSRPT
+               if (state->tlsrpt)
+                   trw_report_failure(state->tlsrpt,
+                                      TLSRPT_STARTTLS_NOT_SUPPORTED,
+                                       /* additional_info= */ (char *) 0,
+                                       /* failure_reason= */ (char *) 0);
+#endif
                return (smtp_site_fail(state, STR(iter->host), resp,
                    "TLS is required, but host %s refused to start TLS: %s",
                                       session->namaddr,
                                       translit(resp->str, "\n", " ")));
+           }
            /* Else try to continue in plain-text mode. */
        }
 
@@ -815,6 +838,13 @@ int     smtp_helo(SMTP_STATE *state)
         */
        if (TLS_REQUIRED(state->tls->level)) {
            if (!(session->features & SMTP_FEATURE_STARTTLS)) {
+#ifdef USE_TLSRPT
+               if (state->tlsrpt)
+                   trw_report_failure(state->tlsrpt,
+                                      TLSRPT_STARTTLS_NOT_SUPPORTED,
+                                       /* additional_info= */ (char *) 0,
+                                       /* failure_reason= */ (char *) 0);
+#endif
                return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                       SMTP_RESP_FAKE(&fake, "4.7.4"),
                          "TLS is required, but was not offered by host %s",
@@ -942,6 +972,12 @@ static int smtp_start_tls(SMTP_STATE *state)
                                     = vstring_str(state->tls->exclusions),
                                     matchargv = state->tls->matchargv,
                                     mdalg = var_smtp_tls_fpt_dgst,
+#ifdef USE_TLSRPT
+                                    tlsrpt = state->tlsrpt,
+#else
+                                    tlsrpt = 0,
+#endif
+                                    ffail_type = 0,
                                     dane = state->tls->dane);
 
        /*
@@ -1065,6 +1101,12 @@ static int smtp_start_tls(SMTP_STATE *state)
                             = vstring_str(state->tls->exclusions),
                             matchargv = state->tls->matchargv,
                             mdalg = var_smtp_tls_fpt_dgst,
+#ifdef USE_TLSRPT
+                            tlsrpt = state->tlsrpt,
+#else
+                            tlsrpt = 0,
+#endif
+                            ffail_type = state->tls->ext_policy_failure,
                             dane = state->tls->dane);
 
        /*
@@ -1125,10 +1167,43 @@ static int smtp_start_tls(SMTP_STATE *state)
      * we must check that here, and not state->tls->level.
      */
     if (TLS_MUST_MATCH(session->tls_context->level))
-       if (!TLS_CERT_IS_MATCHED(session->tls_context))
+       if (!TLS_CERT_IS_MATCHED(session->tls_context)) {
+#ifdef USE_TLSRPT
+
+           /*
+            * Don't create a TLSRPT 'failure' event here, if the TLS engine
+            * already reported a more specific reason.
+            */
+           if (state->tlsrpt && session->tls_context->rpt_reported == 0) {
+               if (!TLS_CERT_IS_TRUSTED(session->tls_context)) {
+                   (void) trw_report_failure(state->tlsrpt,
+                                             TLSRPT_CERTIFICATE_NOT_TRUSTED,
+                                         /* additional_info= */ (char *) 0,
+                                         /* failure_reason= */ (char *) 0);
+               } else {
+                   (void) trw_report_failure(state->tlsrpt,
+                                          TLSRPT_CERTIFICATE_HOST_MISMATCH,
+                                         /* additional_info= */ (char *) 0,
+                                         /* failure_reason= */ (char *) 0);
+               }
+           }
+#endif
            return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                   SMTP_RESP_FAKE(&fake, "4.7.5"),
                                   "Server certificate not verified"));
+       }
+
+    /*
+     * Create a TLSRPT 'success' event only if the TLS engine has not created
+     * TLSRPT event. For example, The TLS engine will create a TLSRPT
+     * 'failure' event when the TLS handshake was be successful, but the
+     * security level was downgraded from opportunistic "dane" to
+     * unauthenticated "encrypt".
+     */
+#ifdef USE_TLSRPT
+    if (state->tlsrpt && session->tls_context->rpt_reported == 0)
+       (void) trw_report_success(state->tlsrpt);
+#endif
 
     /*
      * At this point we have to re-negotiate the "EHLO" to reget the
index 6b81fa4edb45bedc9c5e63b824ce113dff301984..ec8cc2df39bddcf5206f317f68904b61101cf4e7 100644 (file)
 #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"
@@ -73,6 +80,9 @@ SMTP_STATE *smtp_state_alloc(void)
     state->iterator->host = vstring_alloc(100);
     state->iterator->addr = vstring_alloc(100);
     state->iterator->saved_dest = vstring_alloc(100);
+#ifdef USE_TLSRPT
+    state->tlsrpt = 0;
+#endif
     if (var_smtp_cache_conn) {
        state->dest_label = vstring_alloc(10);
        state->dest_prop = vstring_alloc(10);
@@ -105,6 +115,10 @@ void    smtp_state_free(SMTP_STATE *state)
     vstring_free(state->iterator->host);
     vstring_free(state->iterator->addr);
     vstring_free(state->iterator->saved_dest);
+#ifdef USE_TLSRPT
+    if (state->tlsrpt)
+       trw_free(state->tlsrpt);
+#endif
     if (state->dest_label)
        vstring_free(state->dest_label);
     if (state->dest_prop)
index f407d65798ab30d17e2558672199e89d408087bb..5015094965abe8ca34f5279d0a78a9f7f8146a69 100644 (file)
 #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>
@@ -221,15 +226,21 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
 {
     const char *lookup;
     char   *policy;
-    char   *saved_policy;
+    char   *saved_policy = 0;
     char   *tok;
-    const char *err;
     char   *name;
     char   *val;
     static VSTRING *cbuf;
+    char   *free_me = 0;
 
 #undef FREE_RETURN
-#define FREE_RETURN do { myfree(saved_policy); return; } while (0)
+#define FREE_RETURN do { \
+       if (saved_policy) \
+           myfree(saved_policy); \
+       if (free_me) \
+           myfree(free_me); \
+       return; \
+    } while (0)
 
 #define INVALID_RETURN(why, levelp) do { \
            MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0)
@@ -250,7 +261,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
     }
     saved_policy = policy = mystrdup(lookup);
 
-    if ((tok = mystrtok(&policy, CHARS_COMMA_SP)) == 0) {
+    if ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) == 0) {
        msg_warn("%s: invalid empty policy", WHERE);
        INVALID_RETURN(tls->why, site_level);
     }
@@ -265,7 +276,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
      * Warn about ignored attributes when TLS is disabled.
      */
     if (*site_level < TLS_LEV_MAY) {
-       while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0)
+       while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0)
            msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
                     WHERE, tok);
        FREE_RETURN;
@@ -275,8 +286,12 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
      * Errors in attributes may have security consequences, don't ignore
      * errors that can degrade security.
      */
-    while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0) {
-       if ((err = split_nameval(tok, &name, &val)) != 0) {
+    while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
+       const char *err;
+
+       if ((tok[0] == CHARS_BRACE[0]
+            && (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0)
+           || (err = split_nameval(tok, &name, &val)) != 0) {
            msg_warn("%s: malformed attribute/value pair \"%s\": %s",
                     WHERE, tok, err);
            INVALID_RETURN(tls->why, site_level);
@@ -391,6 +406,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
            }
            continue;
        }
+       /* Last one wins. */
        if (!strcasecmp(name, "enable_rpk")) {
            /* Ultimately ignored at some security levels */
            if (strcasecmp(val, "yes") == 0) {
@@ -404,10 +420,98 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level,
            }
            continue;
        }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, EXT_POLICY_TTL)) {
+           char   *end;
+           long    lval;
+
+           if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (!alldig(val) || ((lval = sane_strtol(val, &end, 10)),
+                                ((tls->ext_policy_ttl = lval) != lval))
+               || *end != 0) {
+               msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
+                        WHERE, name, val);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           continue;
+       }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, EXT_POLICY_TYPE)) {
+           if (tls->ext_policy_type) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (!valid_tlsrpt_policy_type(val)) {
+               msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
+                        WHERE, name, val);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           tls->ext_policy_type = mystrdup(val);
+           continue;
+       }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, EXT_POLICY_DOMAIN)) {
+           if (tls->ext_policy_domain) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (!valid_hostname(val, DO_GRIPE)) {
+               msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"",
+                        WHERE, name, val);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           tls->ext_policy_domain = mystrdup(val);
+           continue;
+       }
+       /* Multiple instances per policy are allowed. */
+       if (!strcasecmp(name, EXT_POLICY_STRING)) {
+           if (tls->ext_policy_strings == 0)
+               tls->ext_policy_strings = argv_alloc(1);
+           argv_add(tls->ext_policy_strings, val, (char *) 0);
+           continue;
+       }
+       /* Multiple instances per policy are allowed. */
+       if (!strcasecmp(name, EXT_MX_HOST_PATTERN)) {
+           if (tls->ext_mx_host_patterns == 0)
+               tls->ext_mx_host_patterns = argv_alloc(1);
+           argv_add(tls->ext_mx_host_patterns, val, (char *) 0);
+           continue;
+       }
+       /* Only one instance per policy. */
+       if (!strcasecmp(name, EXT_POLICY_FAILURE)) {
+           if (tls->ext_policy_failure != 0) {
+               msg_warn("%s: attribute \"%s\" is specified multiple times",
+                        WHERE, name);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           if (!valid_tlsrpt_policy_failure(val)) {
+               msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"",
+                        WHERE, name, val);
+               INVALID_RETURN(tls->why, site_level);
+           }
+           tls->ext_policy_failure = mystrdup(val);
+           continue;
+       }
        msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
        INVALID_RETURN(tls->why, site_level);
     }
-
+    if (tls->ext_policy_type == 0) {
+       if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET
+           || tls->ext_policy_strings
+           || tls->ext_policy_domain || tls->ext_mx_host_patterns
+           || tls->ext_policy_failure) {
+           msg_warn("%s: built-in policy has unexpected attribute "
+                    "policy_ttl, policy_domain, policy_string, "
+                    "mx_host_pattern or policy_failure", WHERE);
+           INVALID_RETURN(tls->why, site_level);
+       }
+    }
     FREE_RETURN;
 }
 
@@ -707,6 +811,16 @@ static void policy_delete(void *item, void *unused_context)
     if (tls->dane)
        tls_dane_free(tls->dane);
     dsb_free(tls->why);
+    if (tls->ext_policy_type)
+       myfree(tls->ext_policy_type);
+    if (tls->ext_policy_domain)
+       myfree(tls->ext_policy_domain);
+    if (tls->ext_policy_strings)
+       argv_free(tls->ext_policy_strings);
+    if (tls->ext_mx_host_patterns)
+       argv_free(tls->ext_mx_host_patterns);
+    if (tls->ext_policy_failure)
+       myfree(tls->ext_policy_failure);
 
     myfree((void *) tls);
 }
diff --git a/postfix/src/smtp/smtp_tlsrpt.c b/postfix/src/smtp/smtp_tlsrpt.c
new file mode 100644 (file)
index 0000000..cfb8c55
--- /dev/null
@@ -0,0 +1,414 @@
+/*++
+/* 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 */
index 2e5bbd3d8fa0beb6637f9dc4e2fbc0912277ae23..ce1a37ee9cbd1d812095427646957116438fce10 100644 (file)
@@ -9,7 +9,7 @@ SRCS    = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \
        tls_proxy_server_init_print.c tls_proxy_server_init_scan.c \
        tls_proxy_client_start_print.c tls_proxy_client_start_scan.c \
        tls_proxy_server_start_print.c tls_proxy_server_start_scan.c \
-       tls_proxy_client_misc.c
+       tls_proxy_client_misc.c tlsrpt_wrapper.c
 OBJS   = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
        tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \
        tls_verify.o tls_dane.o tls_certkey.o tls_session.o \
@@ -18,8 +18,8 @@ OBJS  = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \
        tls_proxy_clnt.o tls_proxy_context_print.o tls_proxy_context_scan.o \
        tls_proxy_client_print.o tls_proxy_client_scan.o \
        tls_proxy_server_print.o tls_proxy_server_scan.o \
-       tls_proxy_client_misc.o
-HDRS   = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h
+       tls_proxy_client_misc.o tlsrpt_wrapper.o
+HDRS   = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h tlsrpt_wrapper.h
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -182,6 +182,7 @@ tls_client.o: tls.h
 tls_client.o: tls_client.c
 tls_client.o: tls_mgr.h
 tls_client.o: tls_scache.h
+tls_client.o: tlsrpt_wrapper.h
 tls_dane.o: ../../include/argv.h
 tls_dane.o: ../../include/check_arg.h
 tls_dane.o: ../../include/ctable.h
@@ -366,6 +367,7 @@ tls_proxy_client_print.o: ../../include/vstring.h
 tls_proxy_client_print.o: tls.h
 tls_proxy_client_print.o: tls_proxy.h
 tls_proxy_client_print.o: tls_proxy_client_print.c
+tls_proxy_client_print.o: tlsrpt_wrapper.h
 tls_proxy_client_scan.o: ../../include/argv.h
 tls_proxy_client_scan.o: ../../include/argv_attr.h
 tls_proxy_client_scan.o: ../../include/attr.h
@@ -387,6 +389,7 @@ tls_proxy_client_scan.o: ../../include/vstring.h
 tls_proxy_client_scan.o: tls.h
 tls_proxy_client_scan.o: tls_proxy.h
 tls_proxy_client_scan.o: tls_proxy_client_scan.c
+tls_proxy_client_scan.o: tlsrpt_wrapper.h
 tls_proxy_clnt.o: ../../include/argv.h
 tls_proxy_clnt.o: ../../include/attr.h
 tls_proxy_clnt.o: ../../include/check_arg.h
@@ -588,3 +591,15 @@ tls_verify.o: ../../include/vstream.h
 tls_verify.o: ../../include/vstring.h
 tls_verify.o: tls.h
 tls_verify.o: tls_verify.c
+tls_verify.o: tlsrpt_wrapper.h
+tlsrpt_wrapper.o: ../../include/argv.h
+tlsrpt_wrapper.o: ../../include/check_arg.h
+tlsrpt_wrapper.o: ../../include/msg.h
+tlsrpt_wrapper.o: ../../include/mymalloc.h
+tlsrpt_wrapper.o: ../../include/name_code.h
+tlsrpt_wrapper.o: ../../include/stringops.h
+tlsrpt_wrapper.o: ../../include/sys_defs.h
+tlsrpt_wrapper.o: ../../include/vbuf.h
+tlsrpt_wrapper.o: ../../include/vstring.h
+tlsrpt_wrapper.o: tlsrpt_wrapper.c
+tlsrpt_wrapper.o: tlsrpt_wrapper.h
index 406246fd543631a386e47ccd2c9849fe88703ff6..96eb5a4b385956aebb91219d61d6d550e661b4d7 100644 (file)
@@ -214,7 +214,7 @@ extern TLS_DANE *tls_dane_alloc(void);
 extern void tls_tlsa_free(TLS_TLSA *);
 extern void tls_dane_free(TLS_DANE *);
 extern void tls_dane_add_fpt_digests(TLS_DANE *, int, const char *,
-                                           const char *, int);
+                                            const char *, int);
 extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int);
 extern int tls_dane_load_trustfile(TLS_DANE *, const char *);
 
@@ -270,6 +270,8 @@ typedef struct {
     int     errordepth;                        /* Chain depth of error cert */
     int     errorcode;                 /* First error at error depth */
     int     must_fail;                 /* Failed to load trust settings */
+    int     rpt_reported;              /* Failure was reported with TLSRPT */
+    char   *ffail_type;                        /* Forced verification failure */
 } TLS_SESS_STATE;
 
  /*
@@ -502,6 +504,8 @@ typedef struct {
     const ARGV *matchargv;             /* Cert match patterns */
     const char *mdalg;                 /* default message digest algorithm */
     const TLS_DANE *dane;              /* DANE TLSA verification */
+    struct TLSRPT_WRAPPER *tlsrpt;     /* RFC 8460 reporting */
+    char   *ffail_type;                        /* Forced verification failure */
 } TLS_CLIENT_START_PROPS;
 
 extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *);
@@ -525,12 +529,13 @@ extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *,
     a6, a7, a8, a9, a10, a11, a12, a13, a14))
 
 #define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \
-    a10, a11, a12, a13, a14, a15, a16, a17, a18) \
+    a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) \
     tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \
     ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
     ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
     ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
-    ((props)->a16), ((props)->a17), ((props)->a18), (props)))
+    ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \
+    ((props)->a20), (props)))
 
  /*
   * tls_server.c
@@ -663,7 +668,7 @@ extern void tls_auto_groups(SSL_CTX *, const char *, const char *);
 extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *);
 extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *);
 extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
-extern void tls_log_verify_error(TLS_SESS_STATE *);
+extern void tls_log_verify_error(TLS_SESS_STATE *, struct TLSRPT_WRAPPER *);
 
  /*
   * tls_dane.c
index 3eda859cc0d039b49d4e39ea1a6d6c864c8a08cf..a635767dc656ba1911a8efaf5fd93af57bdb50c6 100644 (file)
 /*
 /*     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>
@@ -363,7 +367,7 @@ static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert,
     if (!TLS_CERT_IS_MATCHED(TLScontext)
        && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
        if (TLScontext->session_reused == 0)
-           tls_log_verify_error(TLScontext);
+           tls_log_verify_error(TLScontext, props->tlsrpt);
        else
            msg_info("%s: re-using session with untrusted peer credential, "
                     "look for details earlier in the log", props->namaddr);
@@ -407,7 +411,7 @@ static void verify_rpk(TLS_SESS_STATE *TLScontext, EVP_PKEY *peerpkey,
     if (!TLS_CERT_IS_MATCHED(TLScontext)
        && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
        if (TLScontext->session_reused == 0)
-           tls_log_verify_error(TLScontext);
+           tls_log_verify_error(TLScontext, props->tlsrpt);
        else
            msg_info("%s: re-using session with untrusted certificate, "
                     "look for details earlier in the log", props->namaddr);
@@ -579,6 +583,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
         * therefore valid for use with SNI.
         */
        if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
+           /* TLSRPT: Local resource error, don't report. */
            msg_warn("%s: error enabling DANE-based certificate validation",
                     TLScontext->namaddr);
            tls_print_errors();
@@ -595,6 +600,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
     case TLS_LEV_FPRINT:
        /* Synthetic DANE for fingerprint security */
        if (SSL_dane_enable(TLScontext->con, 0) <= 0) {
+           /* TLSRPT: Local resource error, don't report. */
            msg_warn("%s: error enabling fingerprint certificate validation",
                     props->namaddr);
            tls_print_errors();
@@ -608,6 +614,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
        if (TLScontext->dane != 0 && TLScontext->dane->tlsa != 0) {
            /* Synthetic DANE for per-destination trust-anchors */
            if (SSL_dane_enable(TLScontext->con, NULL) <= 0) {
+               /* TLSRPT: Local resource error, don't report. */
                msg_warn("%s: error configuring local trust anchors",
                         props->namaddr);
                tls_print_errors();
@@ -622,6 +629,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
 
     if (sni) {
        if (strlen(sni) > TLSEXT_MAXLEN_host_name) {
+           /* TLSRPT: Local configuration error, don't report. */
            msg_warn("%s: ignoring too long SNI hostname: %.100s",
                     props->namaddr, sni);
            return (0);
@@ -633,6 +641,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext,
         * failed to send the SNI name, we have little choice but to abort.
         */
        if (!SSL_set_tlsext_host_name(TLScontext->con, sni)) {
+           /* TLSRPT: Local resource or configuration error, don't report. */
            msg_warn("%s: error setting SNI hostname to: %s", props->namaddr,
                     sni);
            return (0);
@@ -975,6 +984,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      */
     protomask = tls_proto_mask_lims(props->protocols, &min_proto, &max_proto);
     if (protomask == TLS_PROTOCOL_INVALID) {
+       /* TLSRPT: Local configuration error, don't report. */
        /* tls_protocol_mask() logs no warning. */
        msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session",
                 props->namaddr, props->protocols);
@@ -1006,6 +1016,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     TLScontext->level = props->tls_level;
 
     if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) {
+       /* TLSRPT: Local resource error, don't report. */
        msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
        tls_print_errors();
        tls_free_context(TLScontext);
@@ -1022,6 +1033,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     cipher_list = tls_set_ciphers(TLScontext, props->cipher_grade,
                                  props->cipher_exclusions);
     if (cipher_list == 0) {
+       /* TLSRPT: Local configuration error, don't report. */
        /* already warned */
        tls_free_context(TLScontext);
        return (0);
@@ -1036,6 +1048,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     TLScontext->dane = props->dane;
 
     if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
+       /* TLSRPT: Local resource error, don't report. */
        msg_warn("Could not set application data for 'TLScontext->con'");
        tls_print_errors();
        tls_free_context(TLScontext);
@@ -1069,6 +1082,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      * early.
      */
     if (!tls_auth_enable(TLScontext, props)) {
+       /* Already warned and reported TLSRPT result. */
        tls_free_context(TLScontext);
        return (0);
     }
@@ -1105,6 +1119,13 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
            switch (TLScontext->level) {
            case TLS_LEV_HALF_DANE:
            case TLS_LEV_DANE:
+#ifdef USE_TLSRPT
+               if (props->tlsrpt) {
+                   trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID,
+                                       /* additional_info= */ (char *) 0,
+                                      "all-TLSA-records-unusable");
+               }
+#endif
                msg_warn("%s: all TLSA records unusable, fallback to "
                         "unauthenticated TLS", TLScontext->namaddr);
                must_fail = 0;
@@ -1112,13 +1133,34 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
                break;
 
            case TLS_LEV_FPRINT:
+#ifdef USE_TLSRPT
+               if (props->tlsrpt) {
+                   trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
+                                       /* additional_info= */ (char *) 0,
+                                      "all-fingerprints-unusable");
+               }
+#endif
                msg_warn("%s: all fingerprints unusable", TLScontext->namaddr);
                break;
            case TLS_LEV_DANE_ONLY:
+#ifdef USE_TLSRPT
+               if (props->tlsrpt) {
+                   trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID,
+                                       /* additional_info= */ (char *) 0,
+                                      "all-TLSA-records-unusable");
+               }
+#endif
                msg_warn("%s: all TLSA records unusable", TLScontext->namaddr);
                break;
            case TLS_LEV_SECURE:
            case TLS_LEV_VERIFY:
+#ifdef USE_TLSRPT
+               if (props->tlsrpt) {
+                   trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
+                                       /* additional_info= */ (char *) 0,
+                                      "all-trust-anchors-unusable");
+               }
+#endif
                msg_warn("%s: all trust anchors unusable", TLScontext->namaddr);
                break;
            }
@@ -1194,6 +1236,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
      */
     if (SSL_set_fd(TLScontext->con, props->stream == 0 ? props->fd :
                   vstream_fileno(props->stream)) != 1) {
+       /* TLSRPT: Local resource error, don't report. */
        msg_info("SSL_set_fd error to %s", props->namaddr);
        tls_print_errors();
        uncache_session(app_ctx->ssl_ctx, TLScontext);
@@ -1213,6 +1256,16 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
     if (log_mask & TLS_LOG_TLSPKTS)
        tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);
 
+    /*
+     * An external (STS) policy signaled a failure. Prevent false (PKI)
+     * certificate matches in tls_verify.c. TODO(wietse) how was this handled
+     * historically?
+     */
+    if (props->ffail_type) {
+       TLScontext->ffail_type = mystrdup(props->ffail_type);
+       TLScontext->must_fail = 1;
+    }
+
     /*
      * If we don't trigger the handshake in the library, leave control over
      * SSL_connect/read/write/etc with the application.
@@ -1245,6 +1298,12 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props)
            msg_info("SSL_connect error to %s: lost connection",
                     props->namaddr);
        }
+#ifdef USE_TLSRPT
+       if (props->tlsrpt)
+           trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE,
+                               /* additional_info= */ (char *) 0,
+                              "tls-handshake-failure");
+#endif
        uncache_session(app_ctx->ssl_ctx, TLScontext);
        tls_free_context(TLScontext);
        return (0);
@@ -1360,6 +1419,19 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext,
 
     tls_int_seed();
 
+    /*
+     * Precondition: tls_client_start() is called only for a new TCP
+     * connection. It is never called for a reused TCP connection.
+     * 
+     * Inform the caller that they should not generate a TLSRPT 'success' or
+     * 'failure' event: this TLS protocol engine has already generated a
+     * TLSRPT 'failure' event for this session.
+     */
+#ifdef USE_TLSRPT
+    TLScontext->rpt_reported = props->tlsrpt != 0
+       && trw_is_reported(props->tlsrpt);
+#endif
+
     return (TLScontext);
 }
 
index 5e34f7a24b81aba20795ca49632de591d9d86934..3b699ed5df5881327ecc8c9c637c9e4f8e5d3bee 100644 (file)
@@ -1363,6 +1363,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr)
     TLScontext->errordepth = -1;
     TLScontext->errorcode = X509_V_OK;
     TLScontext->errorcert = 0;
+    TLScontext->rpt_reported = 0;
+    TLScontext->ffail_type = 0;
 
     return (TLScontext);
 }
@@ -1413,6 +1415,8 @@ void    tls_free_context(TLS_SESS_STATE *TLScontext)
        myfree((void *) TLScontext->srvr_sig_dgst);
     if (TLScontext->errorcert)
        X509_free(TLScontext->errorcert);
+    if (TLScontext->ffail_type)
+       myfree(TLScontext->ffail_type);
 
     myfree((void *) TLScontext);
 }
index 65286398f2d526166e3d25ba364d68c2bb99e216..ae2a8381c8e17259f0cf4c9b9806ffabbb03e2cc 100644 (file)
@@ -108,11 +108,12 @@ extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *,
     ((props)->a12), ((props)->a13), ((props)->a14))
 
 #define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \
-    a9, a10, a11, a12, a13, a14, a15) \
+    a9, a10, a11, a12, a13, a14, a15, a16, a17) \
     (((props)->a1), ((props)->a2), ((props)->a3), \
     ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \
     ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \
-    ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15))
+    ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \
+    ((props)->a16), ((props)->a17))
 
 extern TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *);
 extern void tls_proxy_context_free(TLS_SESS_STATE *);
@@ -181,6 +182,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
 #define TLS_ATTR_SRVR_SIG_BITS "srvr_signature_bits"
 #define TLS_ATTR_SRVR_SIG_DGST "srvr_signature_digest"
 #define TLS_ATTR_NAMADDR       "namaddr"
+#define TLS_ATTR_RPT_REPORTED  "rpt_reported"
 
  /*
   * TLS_SERVER_INIT_PROPS attributes.
@@ -254,7 +256,9 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *);
 #define TLS_ATTR_CIPHER_EXCLUSIONS "cipher_exclusions"
 #define TLS_ATTR_MATCHARGV     "matchargv"
 #define TLS_ATTR_MDALG         "mdalg"
-#define        TLS_ATTR_DANE           "dane"
+#define TLS_ATTR_DANE          "dane"
+#define TLS_ATTR_TLSRPT                "tlsrpt"
+#define TLS_ATTR_FFAIL_TYPE    "forced_failure_type"
 
  /*
   * TLS_TLSA attributes.
index 81e50b9246fff80a6952e7646c5119b9f36e603d..ae378cb7ec709931f1e51f2d2b66a8aa6044644e 100644 (file)
 #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)
@@ -96,7 +100,7 @@ int     tls_proxy_client_param_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
 
     ret = print_fn(fp, flags | ATTR_FLAG_MORE,
                   SEND_ATTR_STR(TLS_ATTR_CNF_FILE, params->tls_cnf_file),
-                  SEND_ATTR_STR(TLS_ATTR_CNF_NAME,  params->tls_cnf_name),
+                  SEND_ATTR_STR(TLS_ATTR_CNF_NAME, params->tls_cnf_name),
                   SEND_ATTR_STR(VAR_TLS_HIGH_CLIST, params->tls_high_clist),
                   SEND_ATTR_STR(VAR_TLS_MEDIUM_CLIST,
                                 params->tls_medium_clist),
@@ -242,6 +246,59 @@ static int tls_proxy_client_dane_print(ATTR_PRINT_COMMON_FN print_fn,
     return (ret);
 }
 
+#ifdef USE_TLSRPT
+
+/* tls_proxy_client_tlsrpt_print - send TLSRPT_WRAPPER over stream */
+
+static int tls_proxy_client_tlsrpt_print(ATTR_PRINT_COMMON_FN print_fn,
+                                   VSTREAM *fp, int flags, const void *ptr)
+{
+    const TLSRPT_WRAPPER *trw = (const TLSRPT_WRAPPER *) ptr;
+    int     have_trw = trw != 0;
+    int     ret;
+
+    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                  SEND_ATTR_INT(TLS_ATTR_TLSRPT, have_trw),
+                  ATTR_TYPE_END);
+    if (msg_verbose)
+       msg_info("tls_proxy_client_tlsrpt_print have_trw=%d", have_trw);
+
+    if (ret == 0 && have_trw) {
+       ret = print_fn(fp, flags | ATTR_FLAG_MORE,
+                      SEND_ATTR_STR(TRW_RPT_SOCKET_NAME,
+                                    STRING_OR_EMPTY(trw->rpt_socket_name)),
+                      SEND_ATTR_STR(TRW_RPT_POLICY_DOMAIN,
+                                  STRING_OR_EMPTY(trw->rpt_policy_domain)),
+                      SEND_ATTR_STR(TRW_RPT_POLICY_STRING,
+                                  STRING_OR_EMPTY(trw->rpt_policy_string)),
+                      SEND_ATTR_INT(TRW_TLS_POLICY_TYPE,
+                                    (int) trw->tls_policy_type),
+                      SEND_ATTR_FUNC(argv_attr_print,
+                                   (const void *) trw->tls_policy_strings),
+                      SEND_ATTR_STR(TRW_TLS_POLICY_DOMAIN,
+                                  STRING_OR_EMPTY(trw->tls_policy_domain)),
+                      SEND_ATTR_FUNC(argv_attr_print,
+                                     (const void *) trw->mx_host_patterns),
+                      SEND_ATTR_STR(TRW_SRC_MTA_ADDR,
+                                    STRING_OR_EMPTY(trw->snd_mta_addr)),
+                      SEND_ATTR_STR(TRW_DST_MTA_NAME,
+                                    STRING_OR_EMPTY(trw->rcv_mta_name)),
+                      SEND_ATTR_STR(TRW_DST_MTA_ADDR,
+                                    STRING_OR_EMPTY(trw->rcv_mta_addr)),
+                      SEND_ATTR_STR(TRW_DST_MTA_EHLO,
+                                    STRING_OR_EMPTY(trw->rcv_mta_ehlo)),
+                      SEND_ATTR_INT(TRW_FLAGS,
+                                    trw->flags),
+                      ATTR_TYPE_END);
+    }
+    /* Do not flush the stream. */
+    if (msg_verbose)
+       msg_info("tls_proxy_client_tlsrpt_print ret=%d", ret);
+    return (ret);
+}
+
+#endif
+
 /* tls_proxy_client_start_print - send TLS_CLIENT_START_PROPS over stream */
 
 int     tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
@@ -283,6 +340,12 @@ int     tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn,
                                 STRING_OR_EMPTY(props->mdalg)),
                   SEND_ATTR_FUNC(tls_proxy_client_dane_print,
                                  (const void *) props->dane),
+#ifdef USE_TLSRPT
+                  SEND_ATTR_FUNC(tls_proxy_client_tlsrpt_print,
+                                 (const void *) props->tlsrpt),
+#endif
+                  SEND_ATTR_STR(TLS_ATTR_FFAIL_TYPE,
+                                 STRING_OR_EMPTY(props->ffail_type)),
                   ATTR_TYPE_END);
     /* Do not flush the stream. */
     if (msg_verbose)
index d36cf4d7e593a6314f6e12ae9f53d746ac87b971..7b25f71af0c889efdd28f489eaf12cbfe9fff03d 100644 (file)
 #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)
@@ -329,6 +333,12 @@ void    tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props)
     myfree((void *) props->mdalg);
     if (props->dane)
        tls_dane_free((TLS_DANE *) props->dane);
+#ifdef USE_TLSRPT
+    if (props->tlsrpt)
+       trw_free(props->tlsrpt);
+#endif
+    if (props->ffail_type)
+       myfree(props->ffail_type);
     myfree((void *) props);
 }
 
@@ -419,6 +429,89 @@ static int tls_proxy_client_dane_scan(ATTR_SCAN_COMMON_FN scan_fn,
     return (ret);
 }
 
+#define EXPORT_OR_NULL(str, vstr) do { \
+       if (LEN(vstr) > 0) { \
+           (str) = vstring_export(vstr); \
+       } else { \
+           (str) = 0; \
+           vstring_free(vstr); \
+       } \
+    } while (0)
+
+#ifdef USE_TLSRPT
+
+/* tls_proxy_client_tlsrpt_scan - receive TLSRPT_WRAPPER from stream */
+
+static int tls_proxy_client_tlsrpt_scan(ATTR_SCAN_COMMON_FN scan_fn,
+                                         VSTREAM *fp, int flags, void *ptr)
+{
+    TLSRPT_WRAPPER *trw = 0;
+    int     ret;
+    int     have_tlsrpt = 0;
+
+    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                 RECV_ATTR_INT(TLS_ATTR_TLSRPT, &have_tlsrpt),
+                 ATTR_TYPE_END);
+    if (msg_verbose)
+       msg_info("tls_proxy_client_tlsrpt_scan have_tlsrpt=%d", have_tlsrpt);
+
+    if (ret == 1 && have_tlsrpt) {
+       VSTRING *rpt_socket_name = vstring_alloc(100);
+       VSTRING *rpt_policy_domain = vstring_alloc(100);
+       VSTRING *rpt_policy_string = vstring_alloc(100);
+       int     tls_policy_type;
+       ARGV   *tls_policy_strings = 0;
+       VSTRING *tls_policy_domain = vstring_alloc(100);
+       ARGV   *mx_host_patterns = 0;
+       VSTRING *snd_mta_addr = vstring_alloc(100);
+       VSTRING *rcv_mta_name = vstring_alloc(100);
+       VSTRING *rcv_mta_addr = vstring_alloc(100);
+       VSTRING *rcv_mta_ehlo = vstring_alloc(100);
+       int     trw_flags;
+
+       ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
+                     RECV_ATTR_STR(TRW_RPT_SOCKET_NAME, rpt_socket_name),
+                   RECV_ATTR_STR(TRW_RPT_POLICY_DOMAIN, rpt_policy_domain),
+                   RECV_ATTR_STR(TRW_RPT_POLICY_STRING, rpt_policy_string),
+                     RECV_ATTR_INT(TRW_TLS_POLICY_TYPE, &tls_policy_type),
+                     RECV_ATTR_FUNC(argv_attr_scan, &tls_policy_strings),
+                   RECV_ATTR_STR(TRW_TLS_POLICY_DOMAIN, tls_policy_domain),
+                     RECV_ATTR_FUNC(argv_attr_scan, &mx_host_patterns),
+                     RECV_ATTR_STR(TRW_SRC_MTA_ADDR, snd_mta_addr),
+                     RECV_ATTR_STR(TRW_DST_MTA_NAME, rcv_mta_name),
+                     RECV_ATTR_STR(TRW_DST_MTA_ADDR, rcv_mta_addr),
+                     RECV_ATTR_STR(TRW_DST_MTA_EHLO, rcv_mta_ehlo),
+                     RECV_ATTR_INT(TRW_FLAGS, &trw_flags),
+                     ATTR_TYPE_END);
+
+       /* Always construct a well-formed structure. */
+       trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw));
+       trw->rpt_socket_name = vstring_export(rpt_socket_name);
+       trw->rpt_policy_domain = vstring_export(rpt_policy_domain);
+       trw->rpt_policy_string = vstring_export(rpt_policy_string);
+       trw->tls_policy_type = tls_policy_type;
+       trw->tls_policy_strings = tls_policy_strings;
+       EXPORT_OR_NULL(trw->tls_policy_domain, tls_policy_domain);
+       trw->mx_host_patterns = mx_host_patterns;
+       EXPORT_OR_NULL(trw->snd_mta_addr, snd_mta_addr);
+       EXPORT_OR_NULL(trw->rcv_mta_name, rcv_mta_name);
+       EXPORT_OR_NULL(trw->rcv_mta_addr, rcv_mta_addr);
+       EXPORT_OR_NULL(trw->rcv_mta_ehlo, rcv_mta_ehlo);
+       trw->flags = trw_flags;
+       ret = (ret == 12 ? 1 : -1);
+       if (ret != 1) {
+           trw_free(trw);
+           trw = 0;
+       }
+    }
+    *(TLSRPT_WRAPPER **) ptr = trw;
+    if (msg_verbose)
+       msg_info("tls_proxy_client_tlsrpt_scan ret=%d", ret);
+    return (ret);
+}
+
+#endif
+
 /* tls_proxy_client_start_scan - receive TLS_CLIENT_START_PROPS from stream */
 
 int     tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
@@ -437,6 +530,13 @@ int     tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
     VSTRING *cipher_grade = vstring_alloc(25);
     VSTRING *cipher_exclusions = vstring_alloc(25);
     VSTRING *mdalg = vstring_alloc(25);
+    VSTRING *ffail_type = vstring_alloc(25);
+
+#ifdef USE_TLSRPT
+#define EXPECT_START_SCAN_RETURN       17
+#else
+#define EXPECT_START_SCAN_RETURN       16
+#endif
 
     if (msg_verbose)
        msg_info("begin tls_proxy_client_start_scan");
@@ -467,6 +567,11 @@ int     tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
                  RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg),
                  RECV_ATTR_FUNC(tls_proxy_client_dane_scan,
                                 &props->dane),
+#ifdef USE_TLSRPT
+                 RECV_ATTR_FUNC(tls_proxy_client_tlsrpt_scan,
+                                &props->tlsrpt),
+#endif
+                 RECV_ATTR_STR(TLS_ATTR_FFAIL_TYPE, ffail_type),
                  ATTR_TYPE_END);
     /* Always construct a well-formed structure. */
     props->nexthop = vstring_export(nexthop);
@@ -479,7 +584,8 @@ int     tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
     props->cipher_grade = vstring_export(cipher_grade);
     props->cipher_exclusions = vstring_export(cipher_exclusions);
     props->mdalg = vstring_export(mdalg);
-    ret = (ret == 15 ? 1 : -1);
+    EXPORT_OR_NULL(props->ffail_type, ffail_type);
+    ret = (ret == EXPECT_START_SCAN_RETURN ? 1 : -1);
     if (ret != 1) {
        tls_proxy_client_start_free(props);
        props = 0;
index 930410a4661c5cda5db5fba9be4095ce65e81c13..808abbe73ecd64643d8f47e02a6e6220824f9c32 100644 (file)
@@ -110,6 +110,8 @@ int     tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
                                 STRING_OR_EMPTY(tp->srvr_sig_dgst)),
                   SEND_ATTR_STR(TLS_ATTR_NAMADDR,
                                 STRING_OR_EMPTY(tp->namaddr)),
+                  SEND_ATTR_INT(TLS_ATTR_RPT_REPORTED,
+                                tp->rpt_reported),
                   ATTR_TYPE_END);
     /* Do not flush the stream. */
     return (ret);
index 48aaff627460759975e4a8ce5891794823bb51a7..fc23c528a8e4a3e9796574b59ff4885cad2da23b 100644 (file)
@@ -124,6 +124,8 @@ int     tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
         RECV_ATTR_INT(TLS_ATTR_SRVR_SIG_BITS, &tls_context->srvr_sig_bits),
                  RECV_ATTR_STR(TLS_ATTR_SRVR_SIG_DGST, srvr_sig_dgst),
                  RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr),
+                 RECV_ATTR_INT(TLS_ATTR_RPT_REPORTED,
+                               &tls_context->rpt_reported),
                  ATTR_TYPE_END);
     /* Always construct a well-formed structure. */
     tls_context->peer_CN = vstring_export(peer_CN);
@@ -141,7 +143,7 @@ int     tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
     tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve);
     tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst);
     tls_context->namaddr = vstring_export(namaddr);
-    ret = (ret == 24 ? 1 : -1);
+    ret = (ret == 25 ? 1 : -1);
     if (ret != 1) {
        tls_proxy_context_free(tls_context);
        tls_context = 0;
index 88b33264208122b727b9979682aad1e2e898de7f..87433e935fff46f21eb9f03016d4c9d57008dcff 100644 (file)
@@ -1035,7 +1035,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext)
        if (!TLS_CERT_IS_TRUSTED(TLScontext)
            && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) {
            if (TLScontext->session_reused == 0)
-               tls_log_verify_error(TLScontext);
+               tls_log_verify_error(TLScontext, (struct TLSRPT_WRAPPER *) 0);
            else
                msg_info("%s: re-using session with untrusted certificate, "
                         "look for details earlier in the log",
index c643f18382ba2407a066ccf9b28fc1b3ba997e84..0f78e24f41fce52fa58b018d584b568d6e92ccfa 100644 (file)
@@ -11,8 +11,9 @@
 /*     int     ok;
 /*     X509_STORE_CTX *ctx;
 /*
-/*     int     tls_log_verify_error(TLScontext)
+/*     int     tls_log_verify_error(TLScontext, tlsrpt)
 /*     TLS_SESS_STATE *TLScontext;
+/*     struct TLSRPT_WRAPPER *tlsrpt;
 /*
 /*     char *tls_peer_CN(peercert, TLScontext)
 /*     X509   *peercert;
@@ -89,6 +90,9 @@
 /*
 /*     Victor Duchovni
 /*     Morgan Stanley
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
 
 /* TLS library. */
 
+#ifdef USE_TLSRPT
+#include <tlsrpt_wrapper.h>
+#endif
+
 #define TLS_INTERNAL
 #include <tls.h>
 
@@ -194,18 +202,50 @@ int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
 
 /* tls_log_verify_error - Report final verification error status */
 
-void    tls_log_verify_error(TLS_SESS_STATE *TLScontext)
+void    tls_log_verify_error(TLS_SESS_STATE *TLScontext,
+                                    struct TLSRPT_WRAPPER *tlsrpt)
 {
     char    buf[CCERT_BUFSIZ];
     int     err = TLScontext->errorcode;
     X509   *cert = TLScontext->errorcert;
     int     depth = TLScontext->errordepth;
 
+#ifdef USE_TLSRPT
+    VSTRING *err_vstr = vstring_alloc(100);
+
+#define CERT_ERROR_TO_STRING(err) \
+    translit(vstring_str(vstring_strcpy(err_vstr, \
+                                       X509_verify_cert_error_string(err))), \
+            " ", "_")
+#endif
+
 #define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server")
 
     if (err == X509_V_OK)
        return;
 
+    /*
+     * If an external policy flagged an error, report that instead.
+     */
+    if (TLScontext->ffail_type) {
+       msg_info("certificate verification failed for %s: "
+                "external policy failure (%s)",
+                TLScontext->namaddr, TLScontext->ffail_type);
+#ifdef USE_TLSRPT
+       if (tlsrpt) {
+           tlsrpt_failure_t failure_type;
+
+           if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->ffail_type)) < 0)
+               msg_panic("tls_log_verify_error: unexpected failure_reason: %s",
+                         TLScontext->ffail_type);
+           trw_report_failure(tlsrpt, failure_type,
+                               /* additional_info= */ (char *) 0,
+                               /* failure_reason= */ (char *) 0);
+       }
+#endif
+       return;
+    }
+
     /*
      * Specific causes for verification failure.
      */
@@ -218,10 +258,22 @@ void    tls_log_verify_error(TLS_SESS_STATE *TLScontext)
         */
        msg_info("certificate verification failed for %s: "
                 "not trusted by local or TLSA policy", TLScontext->namaddr);
+#ifdef USE_TLSRPT
+       if (tlsrpt)
+           trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED,
+                               /* additional_info= */ (char *) 0,
+                               /* failure_code= */ (char *) 0);
+#endif
        break;
     case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
        msg_info("certificate verification failed for %s: "
                 "self-signed certificate", TLScontext->namaddr);
+#ifdef USE_TLSRPT
+       if (tlsrpt)
+           trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE,
+                               /* additional_info= */ (char *) 0,
+                              CERT_ERROR_TO_STRING(err));
+#endif
        break;
     case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
     case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
@@ -237,25 +289,55 @@ void    tls_log_verify_error(TLS_SESS_STATE *TLScontext)
            strcpy(buf, "<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",
@@ -263,6 +345,9 @@ void    tls_log_verify_error(TLS_SESS_STATE *TLScontext)
                 X509_verify_cert_error_string(err));
        break;
     }
+#ifdef USE_TLSRPT
+    vstring_free(err_vstr);
+#endif
 }
 
 #ifndef DONT_GRIPE
diff --git a/postfix/src/tls/tlsrpt_wrapper.c b/postfix/src/tls/tlsrpt_wrapper.c
new file mode 100644 (file)
index 0000000..176e401
--- /dev/null
@@ -0,0 +1,698 @@
+/*++
+/* 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 */
diff --git a/postfix/src/tls/tlsrpt_wrapper.h b/postfix/src/tls/tlsrpt_wrapper.h
new file mode 100644 (file)
index 0000000..57daff1
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef _TLSRPT_WRAPPER_INCLUDED_
+#define _TLSRPT_WRAPPER_INCLUDED_
+
+/*++
+/* NAME
+/*     tlsrpt_wrapper 3h
+/* SUMMARY
+/*     TLSRPT support for the SMTP and TLS protocol engines
+/* SYNOPSIS
+/*     #include <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_ */
index 51120af063f6c6427bc51a1339074b68c6de5037..2b6d34589595da9138039b806dada683b0eeea85 100644 (file)
@@ -75,6 +75,7 @@ tlsproxy.o: ../../include/split_at.h
 tlsproxy.o: ../../include/sys_defs.h
 tlsproxy.o: ../../include/tls.h
 tlsproxy.o: ../../include/tls_proxy.h
+tlsproxy.o: ../../include/tlsrpt_wrapper.h
 tlsproxy.o: ../../include/vbuf.h
 tlsproxy.o: ../../include/vstream.h
 tlsproxy.o: ../../include/vstring.h
index 0ebf52c68ec81444690f8813a58504b551fb5835..1bf449d370c79703e5270b88dbc48a9f8ebf3131 100644 (file)
 #define TLS_INTERNAL                   /* XXX */
 #include <tls.h>
 #include <tls_proxy.h>
+#include <tlsrpt_wrapper.h>
 
  /*
   * Application-specific.
@@ -731,6 +732,20 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err)
            state->flags |= TLSP_FLAG_NO_MORE_CIPHERTEXT_IO;
            return (TLSP_STAT_OK);
        }
+
+       /*
+        * Report a generic failure only if a more specific failure wasn't
+        * already reported.
+        */
+#ifdef USE_TLSRPT
+       if (state->client_start_props->tlsrpt
+           && (state->flags & TLSP_FLAG_DO_HANDSHAKE)
+           && state->is_server_role == 0)
+           trw_report_failure(state->client_start_props->tlsrpt,
+                              TLSRPT_VALIDATION_FAILURE,
+                               /* additional_info= */ (char *) 0,
+                              "tls-handshake-failure");
+#endif
        tlsp_state_free(state);
        return (TLSP_STAT_ERR);
     }
index bd21e6af3fb90e2d5a050486ff09d00605754b3c..67bdc3ec7cc905d5d22aa96b7caa469010f475bc 100644 (file)
@@ -45,7 +45,8 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \
        sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
        mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
-       inet_addr_sizes.c quote_for_json.c
+       inet_addr_sizes.c quote_for_json.c mystrerror.c \
+       sane_sockaddr_to_hostaddr.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -92,7 +93,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \
        sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
        mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
-       quote_for_json.o
+       quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -2444,6 +2445,12 @@ mymalloc.o: sys_defs.h
 myrand.o: myrand.c
 myrand.o: myrand.h
 myrand.o: sys_defs.h
+mystrerror.o: check_arg.h
+mystrerror.o: mystrerror.c
+mystrerror.o: stringops.h
+mystrerror.o: sys_defs.h
+mystrerror.o: vbuf.h
+mystrerror.o: vstring.h
 mystrtok.o: check_arg.h
 mystrtok.o: msg.h
 mystrtok.o: mystrtok.c
@@ -2549,9 +2556,11 @@ printable.o: stringops.h
 printable.o: sys_defs.h
 printable.o: vbuf.h
 printable.o: vstring.h
+quote_for_json.o: check_arg.h
 quote_for_json.o: quote_for_json.c
 quote_for_json.o: stringops.h
 quote_for_json.o: sys_defs.h
+quote_for_json.o: vbuf.h
 quote_for_json.o: vstring.h
 rand_sleep.o: iostuff.h
 rand_sleep.o: msg.h
@@ -2617,6 +2626,10 @@ sane_rename.o: sane_fsops.h
 sane_rename.o: sane_rename.c
 sane_rename.o: sys_defs.h
 sane_rename.o: warn_stat.h
+sane_sockaddr_to_hostaddr.o: inet_proto.h
+sane_sockaddr_to_hostaddr.o: myaddrinfo.h
+sane_sockaddr_to_hostaddr.o: sane_sockaddr_to_hostaddr.c
+sane_sockaddr_to_hostaddr.o: sys_defs.h
 sane_socketpair.o: msg.h
 sane_socketpair.o: sane_socketpair.c
 sane_socketpair.o: sane_socketpair.h
@@ -2883,6 +2896,7 @@ vbuf.o: vbuf.h
 vbuf_print.o: check_arg.h
 vbuf_print.o: msg.h
 vbuf_print.o: mymalloc.h
+vbuf_print.o: stringops.h
 vbuf_print.o: sys_defs.h
 vbuf_print.o: vbuf.h
 vbuf_print.o: vbuf_print.c
index 332426e88790ba9e51935207c2a7fc931cbd1e35..0816430bc1bac8210b452eebd3d25d2d544ee84b 100644 (file)
 /*     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. */
@@ -301,6 +312,23 @@ void    argv_addn(ARGV *argvp,...)
     argvp->argv[argvp->argc] = 0;
 }
 
+/* argv_addv - optionally create ARGV, append string vector */
+
+ARGV   *argv_addv(ARGV *argvp, const char *const * argv)
+{
+    const char *const * cpp;
+
+    if (argvp == 0) {
+       for (cpp = argv; *cpp; cpp++)
+            /* void */ ;
+       argvp = argv_alloc(cpp - argv);
+    }
+    for (cpp = argv; *cpp; cpp++)
+       argv_add(argvp, *cpp, (char *) 0);
+    argvp->argv[argvp->argc] = 0;
+    return (argvp);
+}
+
 /* argv_terminate - terminate string array */
 
 void    argv_terminate(ARGV *argvp)
@@ -602,6 +630,23 @@ static ARGV *test_argv_join(const TEST_CASE *tp, ARGV *argvp)
     return (argvp);
 }
 
+/* test_argv_addv_appends - populate result */
+
+static ARGV *test_argv_addv_appends(const TEST_CASE *tp, ARGV *argvp)
+{
+    argvp = argv_addv(argvp, tp->inputs);
+    return (argvp);
+}
+
+/* test_argv_addv_creates_appends - populate result */
+
+static ARGV *test_argv_addv_creates(const TEST_CASE *tp, ARGV *argvp)
+{
+    argv_free(argvp);
+    argvp = argv_addv((ARGV *) 0, tp->inputs);
+    return (argvp);
+}
+
 /* test_argv_verify - verify result */
 
 static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp)
@@ -737,6 +782,14 @@ static const TEST_CASE test_cases[] = {
        {0}, 0, test_argv_join,
        0, 1, {"", 0}, ':'
     },
+    {"argv_addv appends to ARGV",
+       {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_appends,
+       0, 3, {"foo", "baz", "bar", 0}
+    },
+    {"argv_addv creates ARGV",
+       {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_creates,
+       0, 3, {"foo", "baz", "bar", 0}
+    },
     0,
 };
 
index f1e746ad89cda6b5a1fbdb715ddc2ea617e706f1..1c479069644e827e227925a5962addca10307095 100644 (file)
@@ -20,7 +20,7 @@ typedef struct ARGV {
     char  **argv;                      /* string array */
 } ARGV;
 
-typedef int (*ARGV_COMPAR_FN)(const void *, const void *);
+typedef int (*ARGV_COMPAR_FN) (const void *, const void *);
 
 extern ARGV *argv_alloc(ssize_t);
 extern ARGV *argv_sort(ARGV *);                /* backwards compatibility */
@@ -28,6 +28,7 @@ extern ARGV *argv_qsort(ARGV *, ARGV_COMPAR_FN);
 extern ARGV *argv_uniq(ARGV *, ARGV_COMPAR_FN);
 extern void argv_add(ARGV *,...);
 extern void argv_addn(ARGV *,...);
+extern ARGV *argv_addv(ARGV *, const char *const *);
 extern void argv_terminate(ARGV *);
 extern void argv_truncate(ARGV *, ssize_t);
 extern void argv_insert_one(ARGV *, ssize_t, const char *);
index 3dfcb9818c2cb481a5e72fe40e0eb7d42b246a0f..feed9f1cd6716592128db360d07c4edfaaa69359 100644 (file)
@@ -44,6 +44,8 @@
 /*     functionality.
 /* .IP HEX_ENCODE_FLAG_USE_COLON
 /*     Inserts one ":" between bytes.
+/* .IP HEX_ENCODE_FLAG_APPEND
+/*     Append output to the buffer.
 /* .PP
 /*     hex_decode_opt() enables extended functionality as controlled
 /*     with \fIflags\fR.
@@ -69,6 +71,9 @@
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
@@ -107,7 +112,8 @@ VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
     int     ch;
     ssize_t count;
 
-    VSTRING_RESET(result);
+    if ((flags & HEX_ENCODE_FLAG_APPEND) == 0)
+       VSTRING_RESET(result);
     for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) {
        ch = *cp;
        VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]);
@@ -177,67 +183,121 @@ VSTRING *hex_decode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
 }
 
 #ifdef TEST
-#include <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
index 720977adb5895ad5f4141221028fe40de245ae3d..ad923c9e24695cf1d33b78e8421a375de45c929b 100644 (file)
@@ -21,6 +21,7 @@
   */
 #define HEX_ENCODE_FLAG_NONE           (0)
 #define HEX_ENCODE_FLAG_USE_COLON      (1<<0)
+#define HEX_ENCODE_FLAG_APPEND         (1<<1)
 
 #define HEX_DECODE_FLAG_NONE   (0)
 #define HEX_DECODE_FLAG_ALLOW_COLON    (1<<0)
@@ -49,6 +50,9 @@ extern VSTRING *WARN_UNUSED_RESULT hex_decode_opt(VSTRING *, const char *, ssize
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 #endif
index 94f1e9fa68fe79b82cb510a77200563f67efc569..cd58a8d4bb929147d95db5706cc54f50bd945f60 100644 (file)
@@ -210,6 +210,12 @@ extern void myaddrinfo_control(int,...);
            msg_fatal("sockaddr_to_hostname: %s", MAI_STRERROR(_aierr)); \
     } while (0)
 
+ /*
+  * sane_sockaddr_to_hostaddr.c
+  */
+extern int WARN_UNUSED_RESULT sane_sockaddr_to_hostaddr(const struct sockaddr *,
+               SOCKADDR_SIZE, MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *, int);
+
 /* LICENSE
 /* .ad
 /* .fi
diff --git a/postfix/src/util/mystrerror.c b/postfix/src/util/mystrerror.c
new file mode 100644 (file)
index 0000000..0de07f1
--- /dev/null
@@ -0,0 +1,39 @@
+/*++
+/* 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");
+}
diff --git a/postfix/src/util/sane_sockaddr_to_hostaddr.c b/postfix/src/util/sane_sockaddr_to_hostaddr.c
new file mode 100644 (file)
index 0000000..919054c
--- /dev/null
@@ -0,0 +1,76 @@
+/*++
+/* 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);
+}
index db56f237a73fdf37096550f88e701a04d4f5ccd7..4c357c81e12f3260b2174b1f876ab2d0355e75a4 100644 (file)
@@ -67,6 +67,7 @@ extern int strcasecmp_utf8x(int, const char *, const char *);
 extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t);
 extern char *quote_for_json(VSTRING *, const char *, ssize_t);
 extern char *quote_for_json_append(VSTRING *, const char *, ssize_t);
+extern const char *mystrerror(int);
 
 #define EXTPAR_FLAG_NONE       (0)
 #define EXTPAR_FLAG_STRIP      (1<<0)  /* "{ text }" -> "text" */
index d7a323f241dd7cd6039bf6272c32da267703a37f..2e3266ed7105d24a57d1bab0a2fe7952c49fa5d9 100644 (file)
@@ -67,6 +67,7 @@
 #include "mymalloc.h"
 #include "vbuf.h"
 #include "vstring.h"
+#include "stringops.h"
 #include "vbuf_print.h"
 
  /*
@@ -290,8 +291,7 @@ VBUF   *vbuf_print(VBUF *bp, const char *format, va_list ap)
                break;
            case 'm':
                /* Ignore the 'l' modifier, width and precision. */
-               VBUF_STRCAT(bp, saved_errno ?
-                           strerror(saved_errno) : "Application error");
+               VBUF_STRCAT(bp, mystrerror(saved_errno));
                break;
            case 'p':
                if (long_flag)