From: Wietse Z Venema Date: Mon, 2 Sep 2024 05:00:00 +0000 (-0500) Subject: postfix-3.10-20240902-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18f96cd64e53558c75136907a70ab7d0caa7c6a7;p=thirdparty%2Fpostfix.git postfix-3.10-20240902-nonprod --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 8ee03dbe7..99ab2e36f 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -370,6 +370,7 @@ -TTLS_PRNG_SEED_INFO -TTLS_PRNG_SRC -TTLS_ROLE +-TTLSRPT_WRAPPER -TTLS_SCACHE -TTLS_SCACHE_ENTRY -TTLS_SERVER_INIT_PROPS diff --git a/postfix/HISTORY b/postfix/HISTORY index 4be10b660..d0f4791f7 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -28200,3 +28200,24 @@ Apologies for any names omitted. matches the envelope sender (MAIL FROM) address. File: proto/postconf.proto. + +20240730 + + Infrastructure: added argv_addv() function to append an + array of strings. File: util/argv.c. + +20240809 + + Infrastructure: added a dns_rr_detach() function to extract + one DNS record from a list. Files: dns/dns_rr.c, dns_rr_test.c. + +20240816 + + Infrastructure: factored out strerror() wrapper that reports + "Application error" instead of "Success" when errno == 0. + Files: util/mystrerror.c, util/vbuf_print.c. + +20240822 + + Infrastructure: added "append to buffer" option to the + hex_encode_opt() function. Files: util/hex_encode.[hc]; diff --git a/postfix/README_FILES/AAAREADME b/postfix/README_FILES/AAAREADME index 94d552ece..f8f2fb7dc 100644 --- a/postfix/README_FILES/AAAREADME +++ b/postfix/README_FILES/AAAREADME @@ -10,6 +10,7 @@ GGeenneerraall ccoonnffiigguurraattiioonn * VIRTUAL_README: Virtual domain hosting * SASL_README: SASL Authentication * TLS_README: TLS Encryption and authentication + * TLSRPT_README: TLSRPT notification * FORWARD_SECRECY_README: TLS Forward Secrecy * IPV6_README: IP Version 6 Support * SMTPUTF8_README: SMTPUTF8 Support diff --git a/postfix/README_FILES/SASL_README b/postfix/README_FILES/SASL_README index 617212e5c..83c0b297f 100644 --- a/postfix/README_FILES/SASL_README +++ b/postfix/README_FILES/SASL_README @@ -185,9 +185,12 @@ You can read more about the following topics: cyrus_sasl_config_path and/or the distribution-specific documentation to determine the expected location. - * Some Debian-based Postfix distributions ignore the "cyrus_sasl_config_path" - parameter setting, and force Postfix to open the file /etc/postfix/sasl/ - smtpd.conf. + * Some Debian-based Postfix distributions patch Postfix to hardcode a non- + default search path, making it impossible to set an alternate search path + via the "cyrus_sasl_config_path" parameter. This is likely to be the case + when the distribution documents a Postfix-specific path (e.g. /etc/postfix/ + sasl/) that is different from the default value of "cyrus_sasl_config_path" + (which then is likely to be empty). NNoottee diff --git a/postfix/README_FILES/TLSRPT_README b/postfix/README_FILES/TLSRPT_README new file mode 100644 index 000000000..7b8af476b --- /dev/null +++ b/postfix/README_FILES/TLSRPT_README @@ -0,0 +1,177 @@ +PPoossttffiixx TTLLSSRRPPTT HHoowwttoo + +------------------------------------------------------------------------------- + +TTOOCC + + * Introduction + * Building Postfix with TLSRPT support + * Using TLSRPT + * MTA-STS Support via smtp_tls_policy_maps + * Limitations + * Credits + +IInnttrroodduuccttiioonn + +The TLSRPT protocol is defined in RFC 8460. With this, an email receiving +domain can publish a policy in DNS to request daily summary reports for +successful and failed TLS connections to that domain. Support for TLSRPT was +added in Postfix 3.10. + +When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable = yes"), the +Postfix SMTP and TLS clients engines will generate "success" and "failure" +events, and will pass those events to a TLSRPT client library and report +generator that are maintained by sys4. The Postfix implementation supports both +DANE (Postfix built-in) and MTA-STS (through an smtp_tls_policy_maps plug-in). + +The high-level diagram shows how Postfix events are reported to domains that +publish a TLSRPT policy. + + Postfix SMTP and --> TLSRPT client --> TLSRPT report --> Email or HTTP + TLS client engines library generator summary + +The Postfix smtp(8) client process implements the SMTP client engine. With +"smtp_tls_connection_reuse = no", the smtp(8) client process also implements +the TLS client engine. With "smtp_tls_connection_reuse = yes", the smtp(8) +client process delegates TLS processing to a Postfix tlsproxy(8) process. +Either way, Postfix will generate the exact same TLSRPT events. + +BBuuiillddiinngg PPoossttffiixx wwiitthh TTLLSSRRPPTT ssuuppppoorrtt + +These instructions assume that you build Postfix from source code as described +in the INSTALL document. Some modification may be required if you build Postfix +from a vendor-specific source package. + +The Postfix TLSRPT client builds on a TLSRPT client library whose source code +can be obtained from: + + https://github.com/sys4/tlsrpt + +The library is typically installed as a header file in /usr/local/include/ +tlsrpt.h and an object library in /usr/local/lib/libtlsrpt.a or /usr/local/lib/ +libtlsrpt.so. The actual pathnames will depend on OS platform conventions. + +In order to build Postfix with TLSRPT support, you will need to add compiler +options --DDUUSSEE__TTLLSSRRPPTT (to build with TLSRPT support), and --II (with the directory +containing the tlsrpt.h header file), and you will need to add linker options +to link with the TLSRPT client library, for example: + + make -f Makefile.init makefiles \ + "CCARGS=-DUSE_TLSRPT -I/usr/local/include" \ + "AUXLIBS=-L/usr/local/lib -ltlsrpt" + +Then, just run 'make'. + + Note: if your build command line already has CCARGS or AUXLIBS options, + then simply append the above options to the existing CCARGS or AUXLIBS + values. + +UUssiinngg TTLLSSRRPPTT + +After installing Postfix TLSRPT support, you can enable TLSRPT support in +main.cf like this: + + smtp_tlsrpt_enable = yes + smtp_tlsrpt_socket_name = /path/to/socket + +The smtp_tlsrpt_socket_name parameter specifies an absolute pathname, or a +pathname that is relative to $queue_directory. + +A good socket location would be under $queue_directory/run/tlsrpt or +$queue_directory/var/run/tlsrpt. These can then be configured in Postfix as a +relative pathname (run/tlsrpt/tlsrpt.sock or var/run/tlsrpt/tlsrpt.sock) so +that the same name will work with and without Postfix chroot support. Do not +specify a location under directory that is already used by Postfix programs. +Only Postfix programs should create sockets there. + +MMTTAA--SSTTSS SSuuppppoorrtt vviiaa ssmmttpp__ttllss__ppoolliiccyy__mmaappss + +Postfix supports MTA-STS though an smtp_tls_policy_maps policy plugin. Postfix +TLSRPT support expects a response with the usual security level and matching +requirements, plus any applicable name=value attributes described below. +Specify { name = value } when a value may contain whitespace. + + Note 1: Postfix 3.10 and later will accept these attributes in an MTA-STS + response even if TLSRPT support is disabled (at build time or run time), + but it will not use most attributes except ttl and policy_failure. + + Note 2: It is an error to specify these attributes for a non-STS policy. + +The examples in the table apply to the MTA-STS policy example given in https:// +datatracker.ietf.org/doc/html/rfc8460#section-4.5. + + * policy_type=type + + Specify sts or no-policy-found. + + * policy_domain=name + + The domain that the MTA-STS policy applies to. + + * policy_ttl=time + + How long (in seconds) a Postfix SMTP client process will cache the MTA-STS + plugin response. + + * { policy_string = value } + + Specify one policy_string instance for each MTA-STS policy feature, + enclosed inside "{" and "}" to protect whitespace in attribute values. + + Example: + + { policy_string = version: STSv1 }, { policy_string = mode: testing }, + ... + + This form ignores whitespace after the opening "{", around the "=", and + before the closing "}". + + * mx_host_pattern=pattern + + Specify one mx_host_pattern instance for each "mx:" feature in the MTA-STS + policy. + + Example: + + mx_host_pattern=mx1.example.com, mx_host_pattern=mx2.example.com, ... + + * policy_failure=type + + If specified, forces MTA-STS policy enforcement to fail with the indicated + error, even if a server certificate would satisfy conventional PKI + constraints. + + Valid errors are sts-policy-fetch-error, sts-policy-invalid, sts-webpki- + invalid, or the less informative validation-failure. + + Example: + + policy_failure=sts-webpki-invalid + +LLiimmiittaattiioonnss + +The following limitations exist primarily because some errors may be reported +by a Postfix smtp(8) client process, and some errors by a Postfix tlsproxy(8) +process. It is too difficult to propagate TLSRPT client library state between +processes. + +The Postfix TLSRPT client reports one final status (either 'success' or +'failure') for each MTA that it is able to connect to. It cannot report a final +status 'success' with some recoverable 'failure'. Specifically: + + * The Postfix TLSRPT client can report either successful TLS policy + compliance, or an unrecoverable failure that prevents TLS policy compliance + (examples: all TLSA records are unusable, or some PKI error during + certificate verification). + + * The Postfix TLSRPT client must not be used to report a potentially + recoverable failure such as a non-parsable TLSA record, because some other + TLSA record for the same host may still allow successful TLS policy + compliance. + +CCrreeddiittss + + * The TLSRPT client library and report generator are implemented and + maintained by sys4 (sys4.de). + * Wietse Venema implemented the integration with Postfix. + diff --git a/postfix/conf/postfix-files b/postfix/conf/postfix-files index 5a939822b..2467af478 100644 --- a/postfix/conf/postfix-files +++ b/postfix/conf/postfix-files @@ -330,6 +330,7 @@ $readme_directory/STANDARD_CONFIGURATION_README:f:root:-:644 $readme_directory/STRESS_README:f:root:-:644 $readme_directory/TLS_LEGACY_README:f:root:-:644 $readme_directory/TLS_README:f:root:-:644 +$readme_directory/TLSRPT_README:f:root:-:644 $readme_directory/TUNING_README:f:root:-:644 $readme_directory/ULTRIX_README:f:root:-:644 $readme_directory/UUCP_README:f:root:-:644 @@ -392,6 +393,7 @@ $html_directory/STANDARD_CONFIGURATION_README.html:f:root:-:644 $html_directory/STRESS_README.html:f:root:-:644 $html_directory/TLS_LEGACY_README.html:f:root:-:644 $html_directory/TLS_README.html:f:root:-:644 +$html_directory/TLSRPT_README.html:f:root:-:644 $html_directory/TUNING_README.html:f:root:-:644 $html_directory/ULTRIX_README.html:f:root:-:644:o $html_directory/UUCP_README.html:f:root:-:644 diff --git a/postfix/html/SASL_README.html b/postfix/html/SASL_README.html index f02208e6e..c95ac8c84 100644 --- a/postfix/html/SASL_README.html +++ b/postfix/html/SASL_README.html @@ -281,9 +281,13 @@ configuration file in /etc/postfix/sasl/, cyrus_sasl_config_path and/or the distribution-specific documentation to determine the expected location.

-
  • Some Debian-based Postfix distributions ignore the -"cyrus_sasl_config_path" parameter setting, and force Postfix to -open the file /etc/postfix/sasl/smtpd.conf.

  • +
  • Some Debian-based Postfix distributions patch Postfix to +hardcode a non-default search path, making it impossible to set an +alternate search path via the "cyrus_sasl_config_path" parameter. This +is likely to be the case when the distribution documents a +Postfix-specific path (e.g. /etc/postfix/sasl/) that is +different from the default value of "cyrus_sasl_config_path" (which +then is likely to be empty).

  • diff --git a/postfix/html/TLSRPT_README.html b/postfix/html/TLSRPT_README.html new file mode 100644 index 000000000..1b153912b --- /dev/null +++ b/postfix/html/TLSRPT_README.html @@ -0,0 +1,276 @@ + + + + + + +Postfix TLSRPT notification Howto + + + + + + + + +

    Postfix TLSRPT Howto

    + +
    + +

    TOC

    + + + +

    Introduction

    + +

    The TLSRPT protocol is defined in RFC 8460. With this, an email +receiving domain can publish a policy in DNS to request daily summary +reports for successful and failed TLS connections to that domain. +Support for TLSRPT was added in Postfix 3.10.

    + +

    When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable += yes"), the Postfix SMTP and TLS clients engines will generate +"success" and "failure" events, and will pass those events to a +TLSRPT client library and report generator that are maintained by +sys4. The Postfix implementation supports both DANE (Postfix built-in) +and MTA-STS (through an smtp_tls_policy_maps plug-in).

    + +

    The high-level diagram shows how Postfix events are reported +to domains that publish a TLSRPT policy. + +

    + + + + + + + + + + + +
    Postfix SMTP and
    TLS client engines +
    --> TLSRPT client library --> + TLSRPT report generator +--> Email or HTTP summary
    + +
    + +

    The Postfix smtp(8) client process implements the SMTP client +engine. With "smtp_tls_connection_reuse = no", the smtp(8) client +process also implements the TLS client engine. With +"smtp_tls_connection_reuse = yes", the smtp(8) client process +delegates TLS processing to a Postfix tlsproxy(8) process. Either +way, Postfix will generate the exact same TLSRPT events.

    + +

    Building Postfix with TLSRPT support +

    + +

    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 +options, then simply append the above options to the existing CCARGS +or AUXLIBS values.

    + +
    + +

    Using TLSRPT

    + +

    After installing Postfix TLSRPT support, you can enable TLSRPT +support in main.cf like this:

    + +
    +
    +smtp_tlsrpt_enable = yes
    +smtp_tlsrpt_socket_name = /path/to/socket
    +
    +
    + +

    The smtp_tlsrpt_socket_name parameter specifies an absolute +pathname, or a pathname that is relative to $queue_directory.

    + +

    A good socket location would be under $queue_directory/run/tlsrpt +or $queue_directory/var/run/tlsrpt. These can then be configured +in Postfix as a relative pathname (run/tlsrpt/tlsrpt.sock or +var/run/tlsrpt/tlsrpt.sock) so that the same name will work with +and without Postfix chroot support. Do not specify a location under +directory that is already used by Postfix programs. Only Postfix +programs should create sockets there.

    + +

    MTA-STS Support via smtp_tls_policy_maps +

    + +

    Postfix supports MTA-STS though an smtp_tls_policy_maps policy +plugin. Postfix TLSRPT support expects a response with the usual +security level and matching requirements, plus any applicable +name=value attributes described below. Specify { name = value } +when a value may contain whitespace.

    + +
    + +

    Note 1: Postfix 3.10 and later will accept these attributes in +an MTA-STS response even if TLSRPT support is disabled (at build +time or run time), but it will not use most attributes except +ttl and policy_failure.

    + +

    Note 2: It is an error to specify these attributes for a non-STS +policy.

    + +
    + +

    The examples in the table apply to the MTA-STS policy example +given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5. +

    + +

    + +

    Limitations

    + +

    The following limitations exist primarily because some errors +may be reported by a Postfix smtp(8) client process, and some errors +by a Postfix tlsproxy(8) process. It is too difficult to propagate +TLSRPT client library state between processes.

    + +

    The Postfix TLSRPT client reports one final status (either +'success' or 'failure') for each MTA that it is able to connect to. +It cannot report a final status 'success' with some recoverable +'failure'. Specifically:

    + + + +

    Credits

    + + + + + + diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index 4d9cc2b0a..427eacf74 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -320,13 +320,9 @@ ACCESS(5) ACCESS(5) address instead of the intended recipient(s). When multiple RE- DIRECT actions fire, only the last one takes effect. - Note 1: this action overrides the FILTER action, and currently + Note: this action overrides the FILTER action, and currently overrides all recipients of the message. - Note 2: a REDIRECT address is subject to canonicalization (add - missing domain) but NOT subject to canonical, masquerade, bcc, - or virtual alias mapping. - This feature is available in Postfix 2.1 and later. INFO optional text... diff --git a/postfix/html/header_checks.5.html b/postfix/html/header_checks.5.html index 579437f5e..c1c8b17cc 100644 --- a/postfix/html/header_checks.5.html +++ b/postfix/html/header_checks.5.html @@ -312,14 +312,10 @@ HEADER_CHECKS(5) HEADER_CHECKS(5) will be sent to the specified address instead of the intended recipient(s). - Note 1: this action overrides the FILTER action, and affects all + Note: this action overrides the FILTER action, and affects all recipients of the message. If multiple REDIRECT actions fire, only the last one is executed. - Note 2: a REDIRECT address is subject to canonicalization (add - missing domain) but NOT subject to canonical, masquerade, bcc, - or virtual alias mapping. - This feature is available in Postfix 2.1 and later. This feature is not supported with smtp header/body checks. diff --git a/postfix/html/index.html b/postfix/html/index.html index a7da6cb51..5fa7a69cd 100644 --- a/postfix/html/index.html +++ b/postfix/html/index.html @@ -43,6 +43,8 @@ configuration examples
  • TLS Encryption and authentication +
  • TLSRPT notification +
  • TLS Forward Secrecy
  • IP Version 6 Support diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index 4cc187c75..dbf71d264 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -746,41 +746,48 @@ SMTP,(LMTP) SMTP,(LMTP) Request that remote SMTP servers send an RFC7250 raw public key instead of an X.509 certificate. + smtp_tlsrpt_enable (no) + Enable support for RFC 8460 TLSRPT notifications. + + smtp_tlsrpt_socket_name (empty) + The pathname of a UNIX-domain datagram socket that is managed by + a local TLSRPT reporting service. + OBSOLETE STARTTLS CONTROLS - The following configuration parameters exist for compatibility with - Postfix versions before 2.3. Support for these will be removed in a + The following configuration parameters exist for compatibility with + Postfix versions before 2.3. Support for these will be removed in a future release. smtp_use_tls (no) - Opportunistic mode: use TLS when a remote SMTP server announces + Opportunistic mode: use TLS when a remote SMTP server announces STARTTLS support, otherwise send the mail in the clear. smtp_enforce_tls (no) - Enforcement mode: require that remote SMTP servers use TLS + Enforcement mode: require that remote SMTP servers use TLS encryption, and never send mail in the clear. smtp_tls_enforce_peername (yes) - With mandatory TLS encryption, require that the remote SMTP - server hostname matches the information in the remote SMTP + With mandatory TLS encryption, require that the remote SMTP + server hostname matches the information in the remote SMTP server certificate. smtp_tls_per_site (empty) - Optional lookup tables with the Postfix SMTP client TLS usage - policy by next-hop destination and by remote SMTP server host- + Optional lookup tables with the Postfix SMTP client TLS usage + policy by next-hop destination and by remote SMTP server host- name. smtp_tls_cipherlist (empty) - Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS + Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS cipher list. RESOURCE AND RATE CONTROLS smtp_connect_timeout (30s) - The Postfix SMTP client time limit for completing a TCP connec- + The Postfix SMTP client time limit for completing a TCP connec- tion, or zero (use the operating system built-in time limit). smtp_helo_timeout (300s) - The Postfix SMTP client time limit for sending the HELO or EHLO - command, and for receiving the initial remote SMTP server + The Postfix SMTP client time limit for sending the HELO or EHLO + command, and for receiving the initial remote SMTP server response. lmtp_lhlo_timeout (300s) @@ -792,19 +799,19 @@ SMTP,(LMTP) SMTP,(LMTP) mand, and for receiving the remote SMTP server response. smtp_mail_timeout (300s) - The Postfix SMTP client time limit for sending the MAIL FROM + The Postfix SMTP client time limit for sending the MAIL FROM command, and for receiving the remote SMTP server response. smtp_rcpt_timeout (300s) - The Postfix SMTP client time limit for sending the SMTP RCPT TO + The Postfix SMTP client time limit for sending the SMTP RCPT TO command, and for receiving the remote SMTP server response. smtp_data_init_timeout (120s) - The Postfix SMTP client time limit for sending the SMTP DATA + The Postfix SMTP client time limit for sending the SMTP DATA command, and for receiving the remote SMTP server response. smtp_data_xfer_timeout (180s) - The Postfix SMTP client time limit for sending the SMTP message + The Postfix SMTP client time limit for sending the SMTP message content. smtp_data_done_timeout (600s) @@ -818,13 +825,13 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 2.1 and later: smtp_mx_address_limit (5) - The maximal number of MX (mail exchanger) IP addresses that can - result from Postfix SMTP client mail exchanger lookups, or zero + The maximal number of MX (mail exchanger) IP addresses that can + result from Postfix SMTP client mail exchanger lookups, or zero (no limit). smtp_mx_session_limit (2) - The maximal number of SMTP sessions per delivery request before - the Postfix SMTP client gives up or delivers to a fall-back + The maximal number of SMTP sessions per delivery request before + the Postfix SMTP client gives up or delivers to a fall-back relay host, or zero (no limit). smtp_rset_timeout (20s) @@ -834,17 +841,17 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 2.2 and earlier: lmtp_cache_connection (yes) - Keep Postfix LMTP client connections open for up to $max_idle + Keep Postfix LMTP client connections open for up to $max_idle seconds. Available in Postfix version 2.2 and later: smtp_connection_cache_destinations (empty) - Permanently enable SMTP connection caching for the specified + Permanently enable SMTP connection caching for the specified destinations. smtp_connection_cache_on_demand (yes) - Temporarily enable SMTP connection caching while a destination + Temporarily enable SMTP connection caching while a destination has a high volume of mail in the active queue. smtp_connection_reuse_time_limit (300s) @@ -858,23 +865,23 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 2.3 and later: connection_cache_protocol_timeout (5s) - Time limit for connection cache connect, send or receive opera- + Time limit for connection cache connect, send or receive opera- tions. Available in Postfix version 2.9 - 3.6: smtp_per_record_deadline (no) - Change the behavior of the smtp_*_timeout time limits, from a - time limit per read or write system call, to a time limit to - send or receive a complete record (an SMTP command line, SMTP - response line, SMTP message content line, or TLS protocol mes- + Change the behavior of the smtp_*_timeout time limits, from a + time limit per read or write system call, to a time limit to + send or receive a complete record (an SMTP command line, SMTP + response line, SMTP message content line, or TLS protocol mes- sage). Available in Postfix version 2.11 and later: smtp_connection_reuse_count_limit (0) - When SMTP connection caching is enabled, the number of times - that an SMTP session may be reused before it is closed, or zero + When SMTP connection caching is enabled, the number of times + that an SMTP session may be reused before it is closed, or zero (no limit). Available in Postfix version 3.4 and later: @@ -885,13 +892,13 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 3.7 and later: smtp_per_request_deadline (no) - Change the behavior of the smtp_*_timeout time limits, from a - time limit per plaintext or TLS read or write call, to a com- - bined time limit for sending a complete SMTP request and for + Change the behavior of the smtp_*_timeout time limits, from a + time limit per plaintext or TLS read or write call, to a com- + bined time limit for sending a complete SMTP request and for receiving a complete SMTP response. smtp_min_data_rate (500) - The minimum plaintext data transfer rate in bytes/second for + The minimum plaintext data transfer rate in bytes/second for DATA requests, when deadlines are enabled with smtp_per_request_deadline. @@ -899,54 +906,54 @@ SMTP,(LMTP) SMTP,(LMTP) transport_destination_concurrency_limit ($default_destination_concur- rency_limit) - A transport-specific override for the default_destination_con- + A transport-specific override for the default_destination_con- currency_limit parameter value, where transport is the master.cf name of the message delivery transport. transport_destination_recipient_limit ($default_destination_recipi- ent_limit) A transport-specific override for the default_destination_recip- - ient_limit parameter value, where transport is the master.cf + ient_limit parameter value, where transport is the master.cf name of the message delivery transport. SMTPUTF8 CONTROLS Preliminary SMTPUTF8 support is introduced with Postfix 3.0. smtputf8_enable (yes) - Enable preliminary SMTPUTF8 support for the protocols described + Enable preliminary SMTPUTF8 support for the protocols described in RFC 6531, RFC 6532, and RFC 6533. smtputf8_autodetect_classes (sendmail, verify) - Detect that a message requires SMTPUTF8 support for the speci- + Detect that a message requires SMTPUTF8 support for the speci- fied mail origin classes. Available in Postfix version 3.2 and later: enable_idna2003_compatibility (no) - Enable 'transitional' compatibility between IDNA2003 and - IDNA2008, when converting UTF-8 domain names to/from the ASCII + Enable 'transitional' compatibility between IDNA2003 and + IDNA2008, when converting UTF-8 domain names to/from the ASCII form that is used for DNS lookups. TROUBLE SHOOTING CONTROLS debug_peer_level (2) - The increment in verbose logging level when a nexthop destina- - tion, remote client or server name or network address matches a + The increment in verbose logging level when a nexthop destina- + tion, remote client or server name or network address matches a pattern given with the debug_peer_list parameter. debug_peer_list (empty) - Optional list of nexthop destination, remote client or server - name or network address patterns that, if matched, cause the - verbose logging level to increase by the amount specified in + Optional list of nexthop destination, remote client or server + name or network address patterns that, if matched, cause the + verbose logging level to increase by the amount specified in $debug_peer_level. error_notice_recipient (postmaster) - The recipient of postmaster notifications about mail delivery + The recipient of postmaster notifications about mail delivery problems that are caused by policy, resource, software or proto- col errors. internal_mail_filter_classes (empty) - What categories of Postfix-generated mail are subject to - before-queue content inspection by non_smtpd_milters, + What categories of Postfix-generated mail are subject to + before-queue content inspection by non_smtpd_milters, header_checks and body_checks. notify_classes (resource, software) @@ -954,46 +961,46 @@ SMTP,(LMTP) SMTP,(LMTP) MISCELLANEOUS CONTROLS best_mx_transport (empty) - Where the Postfix SMTP client should deliver mail when it + Where the Postfix SMTP client should deliver mail when it detects a "mail loops back to myself" error condition. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to handle a + How much time a Postfix daemon process may take to handle a request before it is terminated by a built-in watchdog timer. delay_logging_resolution_limit (2) - The maximal number of digits after the decimal point when log- + The maximal number of digits after the decimal point when log- ging sub-second delay values. disable_dns_lookups (no) Disable DNS lookups in the Postfix SMTP and LMTP clients. inet_interfaces (all) - The local network interface addresses that this mail system + The local network interface addresses that this mail system receives mail on. inet_protocols (see 'postconf -d' output) - The Internet protocols Postfix will attempt to use when making + The Internet protocols Postfix will attempt to use when making or accepting connections. ipc_timeout (3600s) - The time limit for sending or receiving information over an + The time limit for sending or receiving information over an internal communication channel. lmtp_assume_final (no) - When a remote LMTP server announces no DSN support, assume that - the server performs final delivery, and send "delivered" deliv- + When a remote LMTP server announces no DSN support, assume that + the server performs final delivery, and send "delivered" deliv- ery status notifications instead of "relayed". lmtp_tcp_port (24) The default TCP port that the Postfix LMTP client connects to. max_idle (100s) - The maximum amount of time that an idle Postfix daemon process + The maximum amount of time that an idle Postfix daemon process waits for an incoming connection before terminating voluntarily. max_use (100) @@ -1007,21 +1014,21 @@ SMTP,(LMTP) SMTP,(LMTP) The process name of a Postfix command or daemon process. proxy_interfaces (empty) - The remote network interface addresses that this mail system - receives mail on by way of a proxy or network address transla- + The remote network interface addresses that this mail system + receives mail on by way of a proxy or network address transla- tion unit. smtp_address_preference (any) The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP - client will try first, when a destination has IPv6 and IPv4 + client will try first, when a destination has IPv6 and IPv4 addresses with equal MX preference. smtp_bind_address (empty) - An optional numerical network address that the Postfix SMTP + An optional numerical network address that the Postfix SMTP client should bind to when making an IPv4 connection. smtp_bind_address6 (empty) - An optional numerical network address that the Postfix SMTP + An optional numerical network address that the Postfix SMTP client should bind to when making an IPv6 connection. smtp_helo_name ($myhostname) @@ -1041,7 +1048,7 @@ SMTP,(LMTP) SMTP,(LMTP) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - A prefix that is prepended to the process name in syslog + A prefix that is prepended to the process name in syslog records, so that, for example, "smtpd" becomes "prefix/smtpd". Available with Postfix 2.2 and earlier: @@ -1053,14 +1060,14 @@ SMTP,(LMTP) SMTP,(LMTP) Available with Postfix 2.3 and later: smtp_fallback_relay ($fallback_relay) - Optional list of relay destinations that will be used when an - SMTP destination is not found, or when delivery fails due to a + Optional list of relay destinations that will be used when an + SMTP destination is not found, or when delivery fails due to a non-permanent error. Available with Postfix 3.0 and later: smtp_address_verify_target (rcpt) - In the context of email address verification, the SMTP protocol + In the context of email address verification, the SMTP protocol stage that determines whether an email address is deliverable. Available with Postfix 3.1 and later: @@ -1082,7 +1089,7 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix 3.7 and later: smtp_bind_address_enforce (no) - Defer delivery when the Postfix SMTP client cannot apply the + Defer delivery when the Postfix SMTP client cannot apply the smtp_bind_address or smtp_bind_address6 setting. SEE ALSO diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 57b9b1390..d816022fa 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -885,10 +885,6 @@ To avoid mailer loops, automatic BCC recipients are not generated after Postfix forwards mail internally, or after Postfix generates mail itself.

    -

    Note: automatic BCC recipients are subject to address -canonicalization (add missing domain), canonical_maps, masquerade_domains, -and virtual_alias_maps.

    - @@ -1855,10 +1851,6 @@ currently used only to locate the $smt Specify zero or more directories separated by a colon character, or an empty value to use Cyrus SASL's built-in search path.

    -

    Note: some Debian-based Postfix distributions ignore the -"cyrus_sasl_config_path" parameter setting, and force Postfix to -open the file /etc/postfix/sasl/smtpd.conf.

    -

    This feature is available in Postfix 2.5 and later when compiled with Cyrus SASL 2.1.22 or later.

    @@ -6213,11 +6205,6 @@ prepend "uid:" to the numerical UID and use that instead.

    This feature ignores address extensions in the user-specified envelope sender address.

    -

    Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, specify an external filter such as a Milter, -with the non_smtpd_milters parameter. For example: -https://github.com/magcks/milterfrom.

    -

    The following sender patterns are special; these cannot be used as part of a longer pattern.

    @@ -10010,10 +9997,6 @@ To avoid mailer loops, automatic BCC recipients are not generated after Postfix forwards mail internally, or after Postfix generates mail itself.

    -

    Note: automatic BCC recipients are subject to address -canonicalization (add missing domain), canonical_maps, masquerade_domains, -and virtual_alias_maps.

    -

    Example:

    @@ -10752,10 +10735,6 @@ To avoid mailer loops, automatic BCC recipients are not generated after Postfix forwards mail internally, or after Postfix generates mail itself.

    -

    Note: automatic BCC recipients are subject to address -canonicalization (add missing domain), canonical_maps, masquerade_domains, -and virtual_alias_maps.

    -

    Example:

    @@ -14800,6 +14779,33 @@ Postfix versions.

    This feature is available in Postfix 3.0 and later.

    + + +
    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.

    + +

    This feature is available in Postfix ≥ 3.10.

    + + +
    + +
    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".

    + +

    This feature is available in Postfix ≥ 3.10.

    + +
    smtp_use_tls @@ -17780,17 +17786,10 @@ for authentication. The available types are listed with the (default: empty)

    -Optional lookup table with the SASL login names that own the -envelope sender +Optional lookup table with the SASL login names that own the sender (MAIL FROM) addresses.

    -

    Note: to enforce that the From: header address -matches the envelope sender (MAIL FROM) address, use an external -filter such as a Milter, for the submission, submissions, or smtps -services. For example: https://github.com/magcks/milterfrom.

    -
    -

    Specify zero or more "type:name" lookup tables, separated by whitespace or comma. Tables will be searched in the specified order @@ -17893,11 +17892,6 @@ or the SASL login name is not an owner for that address. This prevents an authenticated client from using a MAIL FROM address that they do not explicitly own.
    -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, use an external filter such as a Milter, -for the submission, submissions, or smtps services. For example: -https://github.com/magcks/milterfrom. -
    This feature is available in Postfix version 2.1 and later.

    reject_known_sender_login_mismatch
    @@ -17914,11 +17908,6 @@ This protects any MAIL FROM address that is listed in $smtpd_sender_login_maps, while still allowing a client to use any unlisted MAIL FROM address.
    -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, use an external filter such as a Milter, -for the submission, submissions, or smtps services. For example: -https://github.com/magcks/milterfrom. -
    This feature is available in Postfix version 2.11 and later.
    reject_non_fqdn_sender
    @@ -17960,11 +17949,6 @@ authenticated with SASL. With SASL enabled, this prevents an unauthenticated client from using any MAIL FROM address that is listed in $smtpd_sender_login_maps.
    -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, use an external filter such as a Milter, -for the submission, submissions, or smtps services. For example: -https://github.com/magcks/milterfrom. -
    This feature is available in Postfix version 2.1 and later.
    reject_unknown_sender_domain
    diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 4cc187c75..dbf71d264 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -746,41 +746,48 @@ SMTP,(LMTP) SMTP,(LMTP) Request that remote SMTP servers send an RFC7250 raw public key instead of an X.509 certificate. + smtp_tlsrpt_enable (no) + Enable support for RFC 8460 TLSRPT notifications. + + smtp_tlsrpt_socket_name (empty) + The pathname of a UNIX-domain datagram socket that is managed by + a local TLSRPT reporting service. + OBSOLETE STARTTLS CONTROLS - The following configuration parameters exist for compatibility with - Postfix versions before 2.3. Support for these will be removed in a + The following configuration parameters exist for compatibility with + Postfix versions before 2.3. Support for these will be removed in a future release. smtp_use_tls (no) - Opportunistic mode: use TLS when a remote SMTP server announces + Opportunistic mode: use TLS when a remote SMTP server announces STARTTLS support, otherwise send the mail in the clear. smtp_enforce_tls (no) - Enforcement mode: require that remote SMTP servers use TLS + Enforcement mode: require that remote SMTP servers use TLS encryption, and never send mail in the clear. smtp_tls_enforce_peername (yes) - With mandatory TLS encryption, require that the remote SMTP - server hostname matches the information in the remote SMTP + With mandatory TLS encryption, require that the remote SMTP + server hostname matches the information in the remote SMTP server certificate. smtp_tls_per_site (empty) - Optional lookup tables with the Postfix SMTP client TLS usage - policy by next-hop destination and by remote SMTP server host- + Optional lookup tables with the Postfix SMTP client TLS usage + policy by next-hop destination and by remote SMTP server host- name. smtp_tls_cipherlist (empty) - Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS + Obsolete Postfix < 2.3 control for the Postfix SMTP client TLS cipher list. RESOURCE AND RATE CONTROLS smtp_connect_timeout (30s) - The Postfix SMTP client time limit for completing a TCP connec- + The Postfix SMTP client time limit for completing a TCP connec- tion, or zero (use the operating system built-in time limit). smtp_helo_timeout (300s) - The Postfix SMTP client time limit for sending the HELO or EHLO - command, and for receiving the initial remote SMTP server + The Postfix SMTP client time limit for sending the HELO or EHLO + command, and for receiving the initial remote SMTP server response. lmtp_lhlo_timeout (300s) @@ -792,19 +799,19 @@ SMTP,(LMTP) SMTP,(LMTP) mand, and for receiving the remote SMTP server response. smtp_mail_timeout (300s) - The Postfix SMTP client time limit for sending the MAIL FROM + The Postfix SMTP client time limit for sending the MAIL FROM command, and for receiving the remote SMTP server response. smtp_rcpt_timeout (300s) - The Postfix SMTP client time limit for sending the SMTP RCPT TO + The Postfix SMTP client time limit for sending the SMTP RCPT TO command, and for receiving the remote SMTP server response. smtp_data_init_timeout (120s) - The Postfix SMTP client time limit for sending the SMTP DATA + The Postfix SMTP client time limit for sending the SMTP DATA command, and for receiving the remote SMTP server response. smtp_data_xfer_timeout (180s) - The Postfix SMTP client time limit for sending the SMTP message + The Postfix SMTP client time limit for sending the SMTP message content. smtp_data_done_timeout (600s) @@ -818,13 +825,13 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 2.1 and later: smtp_mx_address_limit (5) - The maximal number of MX (mail exchanger) IP addresses that can - result from Postfix SMTP client mail exchanger lookups, or zero + The maximal number of MX (mail exchanger) IP addresses that can + result from Postfix SMTP client mail exchanger lookups, or zero (no limit). smtp_mx_session_limit (2) - The maximal number of SMTP sessions per delivery request before - the Postfix SMTP client gives up or delivers to a fall-back + The maximal number of SMTP sessions per delivery request before + the Postfix SMTP client gives up or delivers to a fall-back relay host, or zero (no limit). smtp_rset_timeout (20s) @@ -834,17 +841,17 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 2.2 and earlier: lmtp_cache_connection (yes) - Keep Postfix LMTP client connections open for up to $max_idle + Keep Postfix LMTP client connections open for up to $max_idle seconds. Available in Postfix version 2.2 and later: smtp_connection_cache_destinations (empty) - Permanently enable SMTP connection caching for the specified + Permanently enable SMTP connection caching for the specified destinations. smtp_connection_cache_on_demand (yes) - Temporarily enable SMTP connection caching while a destination + Temporarily enable SMTP connection caching while a destination has a high volume of mail in the active queue. smtp_connection_reuse_time_limit (300s) @@ -858,23 +865,23 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 2.3 and later: connection_cache_protocol_timeout (5s) - Time limit for connection cache connect, send or receive opera- + Time limit for connection cache connect, send or receive opera- tions. Available in Postfix version 2.9 - 3.6: smtp_per_record_deadline (no) - Change the behavior of the smtp_*_timeout time limits, from a - time limit per read or write system call, to a time limit to - send or receive a complete record (an SMTP command line, SMTP - response line, SMTP message content line, or TLS protocol mes- + Change the behavior of the smtp_*_timeout time limits, from a + time limit per read or write system call, to a time limit to + send or receive a complete record (an SMTP command line, SMTP + response line, SMTP message content line, or TLS protocol mes- sage). Available in Postfix version 2.11 and later: smtp_connection_reuse_count_limit (0) - When SMTP connection caching is enabled, the number of times - that an SMTP session may be reused before it is closed, or zero + When SMTP connection caching is enabled, the number of times + that an SMTP session may be reused before it is closed, or zero (no limit). Available in Postfix version 3.4 and later: @@ -885,13 +892,13 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix version 3.7 and later: smtp_per_request_deadline (no) - Change the behavior of the smtp_*_timeout time limits, from a - time limit per plaintext or TLS read or write call, to a com- - bined time limit for sending a complete SMTP request and for + Change the behavior of the smtp_*_timeout time limits, from a + time limit per plaintext or TLS read or write call, to a com- + bined time limit for sending a complete SMTP request and for receiving a complete SMTP response. smtp_min_data_rate (500) - The minimum plaintext data transfer rate in bytes/second for + The minimum plaintext data transfer rate in bytes/second for DATA requests, when deadlines are enabled with smtp_per_request_deadline. @@ -899,54 +906,54 @@ SMTP,(LMTP) SMTP,(LMTP) transport_destination_concurrency_limit ($default_destination_concur- rency_limit) - A transport-specific override for the default_destination_con- + A transport-specific override for the default_destination_con- currency_limit parameter value, where transport is the master.cf name of the message delivery transport. transport_destination_recipient_limit ($default_destination_recipi- ent_limit) A transport-specific override for the default_destination_recip- - ient_limit parameter value, where transport is the master.cf + ient_limit parameter value, where transport is the master.cf name of the message delivery transport. SMTPUTF8 CONTROLS Preliminary SMTPUTF8 support is introduced with Postfix 3.0. smtputf8_enable (yes) - Enable preliminary SMTPUTF8 support for the protocols described + Enable preliminary SMTPUTF8 support for the protocols described in RFC 6531, RFC 6532, and RFC 6533. smtputf8_autodetect_classes (sendmail, verify) - Detect that a message requires SMTPUTF8 support for the speci- + Detect that a message requires SMTPUTF8 support for the speci- fied mail origin classes. Available in Postfix version 3.2 and later: enable_idna2003_compatibility (no) - Enable 'transitional' compatibility between IDNA2003 and - IDNA2008, when converting UTF-8 domain names to/from the ASCII + Enable 'transitional' compatibility between IDNA2003 and + IDNA2008, when converting UTF-8 domain names to/from the ASCII form that is used for DNS lookups. TROUBLE SHOOTING CONTROLS debug_peer_level (2) - The increment in verbose logging level when a nexthop destina- - tion, remote client or server name or network address matches a + The increment in verbose logging level when a nexthop destina- + tion, remote client or server name or network address matches a pattern given with the debug_peer_list parameter. debug_peer_list (empty) - Optional list of nexthop destination, remote client or server - name or network address patterns that, if matched, cause the - verbose logging level to increase by the amount specified in + Optional list of nexthop destination, remote client or server + name or network address patterns that, if matched, cause the + verbose logging level to increase by the amount specified in $debug_peer_level. error_notice_recipient (postmaster) - The recipient of postmaster notifications about mail delivery + The recipient of postmaster notifications about mail delivery problems that are caused by policy, resource, software or proto- col errors. internal_mail_filter_classes (empty) - What categories of Postfix-generated mail are subject to - before-queue content inspection by non_smtpd_milters, + What categories of Postfix-generated mail are subject to + before-queue content inspection by non_smtpd_milters, header_checks and body_checks. notify_classes (resource, software) @@ -954,46 +961,46 @@ SMTP,(LMTP) SMTP,(LMTP) MISCELLANEOUS CONTROLS best_mx_transport (empty) - Where the Postfix SMTP client should deliver mail when it + Where the Postfix SMTP client should deliver mail when it detects a "mail loops back to myself" error condition. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con- figuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to handle a + How much time a Postfix daemon process may take to handle a request before it is terminated by a built-in watchdog timer. delay_logging_resolution_limit (2) - The maximal number of digits after the decimal point when log- + The maximal number of digits after the decimal point when log- ging sub-second delay values. disable_dns_lookups (no) Disable DNS lookups in the Postfix SMTP and LMTP clients. inet_interfaces (all) - The local network interface addresses that this mail system + The local network interface addresses that this mail system receives mail on. inet_protocols (see 'postconf -d' output) - The Internet protocols Postfix will attempt to use when making + The Internet protocols Postfix will attempt to use when making or accepting connections. ipc_timeout (3600s) - The time limit for sending or receiving information over an + The time limit for sending or receiving information over an internal communication channel. lmtp_assume_final (no) - When a remote LMTP server announces no DSN support, assume that - the server performs final delivery, and send "delivered" deliv- + When a remote LMTP server announces no DSN support, assume that + the server performs final delivery, and send "delivered" deliv- ery status notifications instead of "relayed". lmtp_tcp_port (24) The default TCP port that the Postfix LMTP client connects to. max_idle (100s) - The maximum amount of time that an idle Postfix daemon process + The maximum amount of time that an idle Postfix daemon process waits for an incoming connection before terminating voluntarily. max_use (100) @@ -1007,21 +1014,21 @@ SMTP,(LMTP) SMTP,(LMTP) The process name of a Postfix command or daemon process. proxy_interfaces (empty) - The remote network interface addresses that this mail system - receives mail on by way of a proxy or network address transla- + The remote network interface addresses that this mail system + receives mail on by way of a proxy or network address transla- tion unit. smtp_address_preference (any) The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP - client will try first, when a destination has IPv6 and IPv4 + client will try first, when a destination has IPv6 and IPv4 addresses with equal MX preference. smtp_bind_address (empty) - An optional numerical network address that the Postfix SMTP + An optional numerical network address that the Postfix SMTP client should bind to when making an IPv4 connection. smtp_bind_address6 (empty) - An optional numerical network address that the Postfix SMTP + An optional numerical network address that the Postfix SMTP client should bind to when making an IPv6 connection. smtp_helo_name ($myhostname) @@ -1041,7 +1048,7 @@ SMTP,(LMTP) SMTP,(LMTP) The syslog facility of Postfix logging. syslog_name (see 'postconf -d' output) - A prefix that is prepended to the process name in syslog + A prefix that is prepended to the process name in syslog records, so that, for example, "smtpd" becomes "prefix/smtpd". Available with Postfix 2.2 and earlier: @@ -1053,14 +1060,14 @@ SMTP,(LMTP) SMTP,(LMTP) Available with Postfix 2.3 and later: smtp_fallback_relay ($fallback_relay) - Optional list of relay destinations that will be used when an - SMTP destination is not found, or when delivery fails due to a + Optional list of relay destinations that will be used when an + SMTP destination is not found, or when delivery fails due to a non-permanent error. Available with Postfix 3.0 and later: smtp_address_verify_target (rcpt) - In the context of email address verification, the SMTP protocol + In the context of email address verification, the SMTP protocol stage that determines whether an email address is deliverable. Available with Postfix 3.1 and later: @@ -1082,7 +1089,7 @@ SMTP,(LMTP) SMTP,(LMTP) Available in Postfix 3.7 and later: smtp_bind_address_enforce (no) - Defer delivery when the Postfix SMTP client cannot apply the + Defer delivery when the Postfix SMTP client cannot apply the smtp_bind_address or smtp_bind_address6 setting. SEE ALSO diff --git a/postfix/makedefs b/postfix/makedefs index e21e648ad..248c0aa8f 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -973,7 +973,7 @@ CCARGS="$CCARGS -DSNAPSHOT" # Non-production: needs thorough testing, or major changes are still # needed before the code stabilizes. -#CCARGS="$CCARGS -DNONPROD" +CCARGS="$CCARGS -DNONPROD" # Workaround: prepend Postfix include files before other include files. CCARGS="-I. -I../../include $CCARGS" diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index efbde6204..e2eb6779e 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -329,13 +329,9 @@ After the message is queued, send the message to the specified address instead of the intended recipient(s). When multiple \fBREDIRECT\fR actions fire, only the last one takes effect. .sp -Note 1: this action overrides the FILTER action, and currently +Note: this action overrides the FILTER action, and currently overrides all recipients of the message. .sp -Note 2: a REDIRECT address is subject to canonicalization -(add missing domain) but NOT subject to canonical, masquerade, -bcc, or virtual alias mapping. -.sp This feature is available in Postfix 2.1 and later. .IP "\fBINFO \fIoptional text...\fR Log an informational record with the optional text, together diff --git a/postfix/man/man5/header_checks.5 b/postfix/man/man5/header_checks.5 index fc9b6503f..92c1de95e 100644 --- a/postfix/man/man5/header_checks.5 +++ b/postfix/man/man5/header_checks.5 @@ -331,14 +331,10 @@ inspect the next input line. After the message is queued, it will be sent to the specified address instead of the intended recipient(s). .sp -Note 1: this action overrides the \fBFILTER\fR action, and affects +Note: this action overrides the \fBFILTER\fR action, and affects all recipients of the message. If multiple \fBREDIRECT\fR actions fire, only the last one is executed. .sp -Note 2: a REDIRECT address is subject to canonicalization -(add missing domain) but NOT subject to canonical, masquerade, -bcc, or virtual alias mapping. -.sp This feature is available in Postfix 2.1 and later. .sp This feature is not supported with smtp header/body checks. diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 91beac06d..19e359d30 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -511,10 +511,6 @@ Note: automatic BCC recipients are produced only for new mail. To avoid mailer loops, automatic BCC recipients are not generated after Postfix forwards mail internally, or after Postfix generates mail itself. -.PP -Note: automatic BCC recipients are subject to address -canonicalization (add missing domain), canonical_maps, masquerade_domains, -and virtual_alias_maps. .SH anvil_rate_time_unit (default: 60s) The time unit over which client connection rates and other rates are calculated. @@ -1115,10 +1111,6 @@ currently used only to locate the $smtpd_sasl_path.conf file. Specify zero or more directories separated by a colon character, or an empty value to use Cyrus SASL's built\-in search path. .PP -Note: some Debian\-based Postfix distributions ignore the -"cyrus_sasl_config_path" parameter setting, and force Postfix to -open the file /etc/postfix/sasl/smtpd.conf. -.PP This feature is available in Postfix 2.5 and later when compiled with Cyrus SASL 2.1.22 or later. .SH daemon_directory (default: see "postconf \-d" output) @@ -3746,11 +3738,6 @@ prepend "\fBuid:\fR" to the numerical UID and use that instead. This feature ignores address extensions in the user\-specified envelope sender address. .PP -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, specify an external filter such as a Milter, -with the non_smtpd_milters parameter. For example: -https://github.com/magcks/milterfrom. -.PP The following sender patterns are special; these cannot be used as part of a longer pattern. .IP "\fB * \fR @@ -6167,10 +6154,6 @@ To avoid mailer loops, automatic BCC recipients are not generated after Postfix forwards mail internally, or after Postfix generates mail itself. .PP -Note: automatic BCC recipients are subject to address -canonicalization (add missing domain), canonical_maps, masquerade_domains, -and virtual_alias_maps. -.PP Example: .PP .nf @@ -6684,10 +6667,6 @@ To avoid mailer loops, automatic BCC recipients are not generated after Postfix forwards mail internally, or after Postfix generates mail itself. .PP -Note: automatic BCC recipients are subject to address -canonicalization (add missing domain), canonical_maps, masquerade_domains, -and virtual_alias_maps. -.PP Example: .PP .nf @@ -9860,6 +9839,21 @@ More examples are in TLS_README, including examples for older Postfix versions. .PP This feature is available in Postfix 3.0 and later. +.SH smtp_tlsrpt_enable (default: no) +Enable support for RFC 8460 TLSRPT notifications. A mail receiving +domain can publish a TLSRPT policy in DNS, to request periodic +summaries of successful and failed SMTP over TLS connections to +their mail servers. This feature requires that Postfix is built +with a TLSRPT supporting library. +.PP +This feature is available in Postfix >= 3.10. +.SH smtp_tlsrpt_socket_name (default: empty) +The pathname of a UNIX\-domain datagram socket that is managed +by a local TLSRPT reporting service. This parameter must specify a +pathname (absolute, or relative to $queue_directory) when +"smtp_tlsrpt_enable = yes". +.PP +This feature is available in Postfix >= 3.10. .SH smtp_use_tls (default: no) Opportunistic mode: use TLS when a remote SMTP server announces STARTTLS support, otherwise send the mail in the clear. Beware: @@ -12052,16 +12046,8 @@ for authentication. The available types are listed with the .PP This feature is available in Postfix 2.3 and later. .SH smtpd_sender_login_maps (default: empty) -Optional lookup table with the SASL login names that own the -envelope sender +Optional lookup table with the SASL login names that own the sender (MAIL FROM) addresses. -.sp -.in +4 -Note: to enforce that the From: header address -matches the envelope sender (MAIL FROM) address, use an external -filter such as a Milter, for the submission, submissions, or smtps -services. For example: https://github.com/magcks/milterfrom. -.in -4 .PP Specify zero or more "type:name" lookup tables, separated by whitespace or comma. Tables will be searched in the specified order @@ -12134,11 +12120,6 @@ or the SASL login name is not an owner for that address. This prevents an authenticated client from using a MAIL FROM address that they do not explicitly own. .br -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, use an external filter such as a Milter, -for the submission, submissions, or smtps services. For example: -https://github.com/magcks/milterfrom. -.br This feature is available in Postfix version 2.1 and later. .br .IP "\fBreject_known_sender_login_mismatch\fR" @@ -12154,11 +12135,6 @@ This protects any MAIL FROM address that is listed in $smtpd_sender_login_maps, while still allowing a client to use any unlisted MAIL FROM address. .br -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, use an external filter such as a Milter, -for the submission, submissions, or smtps services. For example: -https://github.com/magcks/milterfrom. -.br This feature is available in Postfix version 2.11 and later. .br .IP "\fBreject_non_fqdn_sender\fR" @@ -12200,11 +12176,6 @@ authenticated with SASL. With SASL enabled, this prevents an unauthenticated client from using any MAIL FROM address that is listed in $smtpd_sender_login_maps. .br -Note: to enforce that the From: header address matches the envelope -sender (MAIL FROM) address, use an external filter such as a Milter, -for the submission, submissions, or smtps services. For example: -https://github.com/magcks/milterfrom. -.br This feature is available in Postfix version 2.1 and later. .br .IP "\fBreject_unknown_sender_domain\fR" diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index 0145350cb..a55d17636 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -672,6 +672,12 @@ Available in Postfix version 3.9 and later: .IP "\fBsmtp_tls_enable_rpk (no)\fR" Request that remote SMTP servers send an RFC7250 raw public key instead of an X.509 certificate. +.PP Available in Postfix version 3.10 and later: +.IP "\fBsmtp_tlsrpt_enable (no)\fR" +Enable support for RFC 8460 TLSRPT notifications. +.IP "\fBsmtp_tlsrpt_socket_name (empty)\fR" +The pathname of a UNIX\-domain datagram socket that is managed +by a local TLSRPT reporting service. .SH "OBSOLETE STARTTLS CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 6ca24c63d..6e7ba6954 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -721,6 +721,10 @@ while (<>) { s;\bdnssec_probe\b;$&;g; s;\bsmtp_tls_connection_reuse\b;$&;g; s;\blmtp_tls_connection_reuse\b;$&;g; + s;\bsmtp_tlsrpt_enable\b;$&;g; + s;\bsmtp_tlsrpt_socket_name\b;$&;g; + s;\blmtp_tlsrpt_enable\b;$&;g; + s;\blmtp_tlsrpt_socket_name\b;$&;g; s;\bsmtpd_enforce_tls\b;$&;g; s;\bsmtpd_sasl_tls_security_options\b;$&;g; s;\bsmtpd_sasl_type\b;$&;g; diff --git a/postfix/proto/Makefile.in b/postfix/proto/Makefile.in index f02ab1415..86476adf3 100644 --- a/postfix/proto/Makefile.in +++ b/postfix/proto/Makefile.in @@ -50,6 +50,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \ ../html/STANDARD_CONFIGURATION_README.html \ ../html/STRESS_README.html \ ../html/TLS_README.html ../html/TLS_LEGACY_README.html \ + ../html/TLSRPT_README.html \ ../html/TUNING_README.html \ ../html/UUCP_README.html \ ../html/VERP_README.html ../html/VIRTUAL_README.html \ @@ -100,6 +101,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \ ../README_FILES/STANDARD_CONFIGURATION_README \ ../README_FILES/STRESS_README \ ../README_FILES/TLS_README ../README_FILES/TLS_LEGACY_README \ + ../README_FILES/TLSRPT_README \ ../README_FILES/TUNING_README \ ../README_FILES/UUCP_README \ ../README_FILES/VERP_README ../README_FILES/VIRTUAL_README \ @@ -343,6 +345,9 @@ clobber: ../html/TLS_LEGACY_README.html: TLS_LEGACY_README.html $(DETAB) $? | $(POSTLINK) >$@ +../html/TLSRPT_README.html: TLSRPT_README.html + $(DETAB) $? | $(POSTLINK) >$@ + ../README_FILES/ADDRESS_CLASS_README: ADDRESS_CLASS_README.html $(DETAB) $? | $(HT2READ) >$@ @@ -529,6 +534,9 @@ clobber: ../README_FILES/TLS_LEGACY_README: TLS_LEGACY_README.html $(DETAB) $? | $(HT2READ) >$@ +../README_FILES/TLSRPT_README: TLSRPT_README.html + $(DETAB) $? | $(HT2READ) >$@ + ../README_FILES/AAAREADME: ../html/index.html $(MAKEAAA) $(MAKEAAA) ../html/index.html | $(HT2READ) | $(DETAB) >$@ diff --git a/postfix/proto/TLSRPT_README.html b/postfix/proto/TLSRPT_README.html new file mode 100644 index 000000000..13d8aa653 --- /dev/null +++ b/postfix/proto/TLSRPT_README.html @@ -0,0 +1,276 @@ + + + + + + +Postfix TLSRPT notification Howto + + + + + + + + +

    Postfix TLSRPT Howto

    + +
    + +

    TOC

    + + + +

    Introduction

    + +

    The TLSRPT protocol is defined in RFC 8460. With this, an email +receiving domain can publish a policy in DNS to request daily summary +reports for successful and failed TLS connections to that domain. +Support for TLSRPT was added in Postfix 3.10.

    + +

    When Postfix TLSRPT support is enabled (with "smtp_tlsrpt_enable += yes"), the Postfix SMTP and TLS clients engines will generate +"success" and "failure" events, and will pass those events to a +TLSRPT client library and report generator that are maintained by +sys4. The Postfix implementation supports both DANE (Postfix built-in) +and MTA-STS (through an smtp_tls_policy_maps plug-in).

    + +

    The high-level diagram shows how Postfix events are reported +to domains that publish a TLSRPT policy. + +

    + + + + + + + + + + + +
    Postfix SMTP and
    TLS client engines +
    --> TLSRPT client library --> + TLSRPT report generator +--> Email or HTTP summary
    + +
    + +

    The Postfix smtp(8) client process implements the SMTP client +engine. With "smtp_tls_connection_reuse = no", the smtp(8) client +process also implements the TLS client engine. With +"smtp_tls_connection_reuse = yes", the smtp(8) client process +delegates TLS processing to a Postfix tlsproxy(8) process. Either +way, Postfix will generate the exact same TLSRPT events.

    + +

    Building Postfix with TLSRPT support +

    + +

    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 +options, then simply append the above options to the existing CCARGS +or AUXLIBS values.

    + +
    + +

    Using TLSRPT

    + +

    After installing Postfix TLSRPT support, you can enable TLSRPT +support in main.cf like this:

    + +
    +
    +smtp_tlsrpt_enable = yes
    +smtp_tlsrpt_socket_name = /path/to/socket
    +
    +
    + +

    The smtp_tlsrpt_socket_name parameter specifies an absolute +pathname, or a pathname that is relative to $queue_directory.

    + +

    A good socket location would be under $queue_directory/run/tlsrpt +or $queue_directory/var/run/tlsrpt. These can then be configured +in Postfix as a relative pathname (run/tlsrpt/tlsrpt.sock or +var/run/tlsrpt/tlsrpt.sock) so that the same name will work with +and without Postfix chroot support. Do not specify a location under +directory that is already used by Postfix programs. Only Postfix +programs should create sockets there.

    + +

    MTA-STS Support via smtp_tls_policy_maps +

    + +

    Postfix supports MTA-STS though an smtp_tls_policy_maps policy +plugin. Postfix TLSRPT support expects a response with the usual +security level and matching requirements, plus any applicable +name=value attributes described below. Specify { name = value } +when a value may contain whitespace.

    + +
    + +

    Note 1: Postfix 3.10 and later will accept these attributes in +an MTA-STS response even if TLSRPT support is disabled (at build +time or run time), but it will not use most attributes except +ttl and policy_failure.

    + +

    Note 2: It is an error to specify these attributes for a non-STS +policy.

    + +
    + +

    The examples in the table apply to the MTA-STS policy example +given in https://datatracker.ietf.org/doc/html/rfc8460#section-4.5. +

    + +

    + +

    Limitations

    + +

    The following limitations exist primarily because some errors +may be reported by a Postfix smtp(8) client process, and some errors +by a Postfix tlsproxy(8) process. It is too difficult to propagate +TLSRPT client library state between processes.

    + +

    The Postfix TLSRPT client reports one final status (either +'success' or 'failure') for each MTA that it is able to connect to. +It cannot report a final status 'success' with some recoverable +'failure'. Specifically:

    + + + +

    Credits

    + + + + + + diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 24b8295f2..c8704c0d0 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -19402,3 +19402,22 @@ announce 8BITMIME support, or when a message line exceeds the SMTP length limit.

    This feature is available in Postfix ≥ 3.9.

    + +%PARAM smtp_tlsrpt_enable no + +

    Enable support for RFC 8460 TLSRPT notifications. A mail receiving +domain can publish a TLSRPT policy in DNS, to request periodic +summaries of successful and failed SMTP over TLS connections to +their mail servers. This feature requires that Postfix is built +with a TLSRPT supporting library.

    + +

    This feature is available in Postfix ≥ 3.10.

    + +%PARAM smtp_tlsrpt_socket_name + +

    The pathname of a UNIX-domain datagram socket that is managed +by a local TLSRPT reporting service. This parameter must specify a +pathname (absolute, or relative to $queue_directory) when +"smtp_tlsrpt_enable = yes".

    + +

    This feature is available in Postfix ≥ 3.10.

    diff --git a/postfix/proto/stop b/postfix/proto/stop index ec9542f1c..171198f88 100644 --- a/postfix/proto/stop +++ b/postfix/proto/stop @@ -1615,3 +1615,28 @@ milterfrom canonicalization Orlitzky Typofix +RPT +TLSRPT +TLSRPTv +TODOS +WSP +addv +bugprone +errnum +libtlsrpt +munge +mystrerror +protcol +punycode +pval +rpt +rua +sockname +tlsproxied +tlsrpt +trw +datagram +RPC +datatracker +webpki +parsable diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc index bff4534d1..62bac0323 100644 --- a/postfix/proto/stop.double-cc +++ b/postfix/proto/stop.double-cc @@ -335,3 +335,10 @@ length length of 0 31 0 127 address address string length whether the standard End of DATA sequence CRLF CRLF is required and Require CRLF CRLF + must start with a version field v TLSRPTv1 followed by WSP WSP + policies policy policy type + policies policy policy string Ignored if the tls_policy_type + policies policy policy domain +additional_detail additional_detail +ignored ignored +USE_TLSRPT USE_TLSRPT diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html index a4b2332a2..c63bcdaab 100644 --- a/postfix/proto/stop.double-proto-html +++ b/postfix/proto/stop.double-proto-html @@ -358,3 +358,4 @@ expected to become a list of comma separated names br br This Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0 dt dt dd 2 Also enable verbose logging in the Postfix TLS +Postfix Postfix legacy TLS Support diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index 1314e0c0e..e68fbd1c1 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1841,3 +1841,7 @@ foqvx ILP xxfi optionsv +rcv +snd +sts +tlsrprt diff --git a/postfix/proto/stop.spell-proto-html b/postfix/proto/stop.spell-proto-html index e76dbfa4d..65f9fa062 100644 --- a/postfix/proto/stop.spell-proto-html +++ b/postfix/proto/stop.spell-proto-html @@ -379,3 +379,8 @@ Dextrous ar liveness superset +ltlsrpt +sts +STS +STSv +Sys diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index 987b988f1..0f8b0b92d 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -221,6 +221,7 @@ extern int dns_rr_compare_pref_any(DNS_RR *, DNS_RR *); extern int dns_rr_compare_pref(DNS_RR *, DNS_RR *); extern DNS_RR *dns_rr_shuffle(DNS_RR *); extern DNS_RR *dns_rr_remove(DNS_RR *, DNS_RR *); +extern DNS_RR *dns_rr_detach(DNS_RR *, DNS_RR *); extern int var_dns_rr_list_limit; /* diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index 882a42ffb..e5775a276 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -52,6 +52,10 @@ /* DNS_RR *list; /* DNS_RR *record; /* +/* DNS_RR *dns_rr_detach(list, record) +/* DNS_RR *list; +/* DNS_RR *record; +/* /* DNS_RR *dns_srv_rr_sort(list) /* DNS_RR *list; /* @@ -118,10 +122,15 @@ /* /* dns_rr_shuffle() randomly permutes a list of resource records. /* -/* dns_rr_remove() removes the specified record from the specified list. +/* dns_rr_remove() disconnects the specified record from the +/* specified list and destroys it. /* The updated list is the result value. /* The record MUST be a list member. /* +/* dns_rr_detach() disconnects the specified record from the +/* specified list. The updated list is the result value. +/* The record MUST be a list member. +/* /* dns_srv_rr_sort() sorts a list of SRV records according to /* their priority and weight as described in RFC 2782. /* LICENSE @@ -464,16 +473,24 @@ DNS_RR *dns_rr_shuffle(DNS_RR *list) /* dns_rr_remove - remove record from list, return new list */ DNS_RR *dns_rr_remove(DNS_RR *list, DNS_RR *record) +{ + list = dns_rr_detach(list, record); + dns_rr_free(record); + return (list); +} + +/* dns_rr_detach - detach record from list, return new list */ + +DNS_RR *dns_rr_detach(DNS_RR *list, DNS_RR *record) { if (list == 0) - msg_panic("dns_rr_remove: record not found"); + msg_panic("dns_rr_detach: record not found"); if (list == record) { list = record->next; record->next = 0; - dns_rr_free(record); } else { - list->next = dns_rr_remove(list->next, record); + list->next = dns_rr_detach(list->next, record); } return (list); } diff --git a/postfix/src/dns/dns_rr_test.c b/postfix/src/dns/dns_rr_test.c index 7bbe76927..12cf01c58 100644 --- a/postfix/src/dns/dns_rr_test.c +++ b/postfix/src/dns/dns_rr_test.c @@ -361,6 +361,93 @@ static int append_to_elem_from_list_exact_fit(void) return (eq_dns_rr_free(got, want)); } +static int delete_middle_element(void) +{ + DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4); + DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4); + DNS_RR *got, *want, *list; + + ((list = a)->next = b)->next = c; + (want = dns_rr_copy(a))->next = dns_rr_copy(c); + got = dns_rr_remove(list, b); + + return (eq_dns_rr_free(got, want)); +} + +static int delete_first_element(void) +{ + DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4); + DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4); + DNS_RR *got, *want, *list; + + ((list = a)->next = b)->next = c; + (want = dns_rr_copy(b))->next = dns_rr_copy(c); + got = dns_rr_remove(list, a); + + return (eq_dns_rr_free(got, want)); +} + +static int delete_last_element(void) +{ + DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4); + DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4); + DNS_RR *got, *want, *list; + + ((list = a)->next = b)->next = c; + (want = dns_rr_copy(a))->next = dns_rr_copy(b); + got = dns_rr_remove(list, c); + + return (eq_dns_rr_free(got, want)); +} + +static int detach_middle_element(void) +{ + DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4); + DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4); + DNS_RR *got, *want, *list; + + ((list = a)->next = b)->next = c; + (want = dns_rr_copy(a))->next = dns_rr_copy(c); + got = dns_rr_detach(list, b); + dns_rr_free(b); + + return (eq_dns_rr_free(got, want)); +} + +static int detach_first_element(void) +{ + DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4); + DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4); + DNS_RR *got, *want, *list; + + ((list = a)->next = b)->next = c; + (want = dns_rr_copy(b))->next = dns_rr_copy(c); + got = dns_rr_detach(list, a); + dns_rr_free(a); + + return (eq_dns_rr_free(got, want)); +} + +static int detach_last_element(void) +{ + DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + DNS_RR *b = dns_rr_create_noport("qb", "rb", T_MX, C_IN, 3600, 1, "mxb", 4); + DNS_RR *c = dns_rr_create_noport("qc", "rc", T_MX, C_IN, 3600, 1, "mxc", 4); + DNS_RR *got, *want, *list; + + ((list = a)->next = b)->next = c; + (want = dns_rr_copy(a))->next = dns_rr_copy(b); + got = dns_rr_detach(list, c); + dns_rr_free(c); + + return (eq_dns_rr_free(got, want)); +} + /* * The test cases. */ @@ -400,9 +487,15 @@ static const TEST_CASE test_cases[] = { "append to element from list exact fit", append_to_elem_from_list_exact_fit, /* - * TODO: tests dns_rr_sort(), dns_rr_srv_sort(), dns_rr_remove(), - * dns_rr_shuffle(), etc. + * TODO: tests for dns_rr_sort(), dns_rr_srv_sort(), dns_rr_shuffle(), + * etc. */ + "delete element from list (middle)", delete_middle_element, + "delete element from list (first)", delete_first_element, + "delete element from list (last)", delete_last_element, + "detach element from list (middle)", detach_middle_element, + "detach element from list (first)", detach_first_element, + "detach element from list (last)", detach_last_element, 0, }; diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 1f03b0b34..8b52145b0 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -4455,6 +4455,22 @@ extern bool var_ign_srv_lookup_err; #define DEF_ALLOW_SRV_FALLBACK 0 extern bool var_allow_srv_fallback; + /* + * TLSRPT notification support. The lmtp_ names must be defined because the + * build system enforces that every smtp_ parameter has an lmtp_ variant. + */ +#define VAR_SMTP_TLSRPT_ENABLE "smtp_tlsrpt_enable" +#define DEF_SMTP_TLSRPT_ENABLE "no" +#define VAR_LMTP_TLSRPT_ENABLE "lmtp_tlsrpt_enable" +#define DEF_LMTP_TLSRPT_ENABLE DEF_SMTP_TLSRPT_ENABLE +extern bool var_smtp_tlsrpt_enable; + +#define VAR_SMTP_TLSRPT_SOCKNAME "smtp_tlsrpt_socket_name" +#define DEF_SMTP_TLSRPT_SOCKNAME "" +#define VAR_LMTP_TLSRPT_SOCKNAME "lmtp_tlsrpt_socket_name" +#define DEF_LMTP_TLSRPT_SOCKNAME DEF_SMTP_TLSRPT_SOCKNAME +extern char *var_smtp_tlsrpt_sockname; + /* LICENSE /* .ad /* .fi @@ -4469,6 +4485,9 @@ extern bool var_allow_srv_fallback; /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/posttls-finger/posttls-finger.c b/postfix/src/posttls-finger/posttls-finger.c index b474a4006..193588ef4 100644 --- a/postfix/src/posttls-finger/posttls-finger.c +++ b/postfix/src/posttls-finger/posttls-finger.c @@ -835,6 +835,8 @@ static int starttls(STATE *state) = vstring_str(cipher_exclusions), matchargv = state->match, mdalg = state->mdalg, + tlsrpt = 0, + fail_type = 0, dane = state->ddane ? state->ddane : state->dane); @@ -939,6 +941,8 @@ static int starttls(STATE *state) = vstring_str(cipher_exclusions), matchargv = state->match, mdalg = state->mdalg, + tlsrpt = 0, + fail_type = 0, dane = state->ddane ? state->ddane : state->dane); } /* tlsproxy_mode */ vstring_free(cipher_exclusions); diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index b9f04ca13..685a72b67 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -2,11 +2,11 @@ SHELL = /bin/sh SRCS = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \ smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \ smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \ - smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c + smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c smtp_tlsrpt.c OBJS = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \ smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \ smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \ - smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o + smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o smtp_tlsrpt.o HDRS = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) @@ -267,6 +267,7 @@ smtp_connect.o: ../../include/timed_connect.h smtp_connect.o: ../../include/tls.h smtp_connect.o: ../../include/tls_proxy.h smtp_connect.o: ../../include/tok822.h +smtp_connect.o: ../../include/valid_hostname.h smtp_connect.o: ../../include/vbuf.h smtp_connect.o: ../../include/vstream.h smtp_connect.o: ../../include/vstring.h @@ -447,6 +448,7 @@ smtp_proto.o: ../../include/stringops.h smtp_proto.o: ../../include/sys_defs.h smtp_proto.o: ../../include/tls.h smtp_proto.o: ../../include/tls_proxy.h +smtp_proto.o: ../../include/tlsrpt_wrapper.h smtp_proto.o: ../../include/tok822.h smtp_proto.o: ../../include/uxtext.h smtp_proto.o: ../../include/vbuf.h @@ -737,6 +739,7 @@ smtp_state.o: ../../include/string_list.h smtp_state.o: ../../include/sys_defs.h smtp_state.o: ../../include/tls.h smtp_state.o: ../../include/tls_proxy.h +smtp_state.o: ../../include/tlsrpt_wrapper.h smtp_state.o: ../../include/tok822.h smtp_state.o: ../../include/vbuf.h smtp_state.o: ../../include/vstream.h @@ -770,6 +773,7 @@ smtp_tls_policy.o: ../../include/name_mask.h smtp_tls_policy.o: ../../include/nvtable.h smtp_tls_policy.o: ../../include/recipient_list.h smtp_tls_policy.o: ../../include/resolve_clnt.h +smtp_tls_policy.o: ../../include/sane_strtol.h smtp_tls_policy.o: ../../include/scache.h smtp_tls_policy.o: ../../include/sock_addr.h smtp_tls_policy.o: ../../include/string_list.h @@ -777,6 +781,7 @@ smtp_tls_policy.o: ../../include/stringops.h smtp_tls_policy.o: ../../include/sys_defs.h smtp_tls_policy.o: ../../include/tls.h smtp_tls_policy.o: ../../include/tls_proxy.h +smtp_tls_policy.o: ../../include/tlsrpt_wrapper.h smtp_tls_policy.o: ../../include/tok822.h smtp_tls_policy.o: ../../include/valid_hostname.h smtp_tls_policy.o: ../../include/valid_utf8_hostname.h @@ -785,6 +790,48 @@ smtp_tls_policy.o: ../../include/vstream.h smtp_tls_policy.o: ../../include/vstring.h smtp_tls_policy.o: smtp.h smtp_tls_policy.o: smtp_tls_policy.c +smtp_tlsrpt.o: ../../include/argv.h +smtp_tlsrpt.o: ../../include/attr.h +smtp_tlsrpt.o: ../../include/check_arg.h +smtp_tlsrpt.o: ../../include/deliver_request.h +smtp_tlsrpt.o: ../../include/dict.h +smtp_tlsrpt.o: ../../include/dns.h +smtp_tlsrpt.o: ../../include/dsn.h +smtp_tlsrpt.o: ../../include/dsn_buf.h +smtp_tlsrpt.o: ../../include/header_body_checks.h +smtp_tlsrpt.o: ../../include/header_opts.h +smtp_tlsrpt.o: ../../include/hex_code.h +smtp_tlsrpt.o: ../../include/htable.h +smtp_tlsrpt.o: ../../include/inet_proto.h +smtp_tlsrpt.o: ../../include/mail_params.h +smtp_tlsrpt.o: ../../include/maps.h +smtp_tlsrpt.o: ../../include/match_list.h +smtp_tlsrpt.o: ../../include/midna_domain.h +smtp_tlsrpt.o: ../../include/mime_state.h +smtp_tlsrpt.o: ../../include/msg.h +smtp_tlsrpt.o: ../../include/msg_stats.h +smtp_tlsrpt.o: ../../include/myaddrinfo.h +smtp_tlsrpt.o: ../../include/myflock.h +smtp_tlsrpt.o: ../../include/mymalloc.h +smtp_tlsrpt.o: ../../include/name_code.h +smtp_tlsrpt.o: ../../include/name_mask.h +smtp_tlsrpt.o: ../../include/nvtable.h +smtp_tlsrpt.o: ../../include/recipient_list.h +smtp_tlsrpt.o: ../../include/resolve_clnt.h +smtp_tlsrpt.o: ../../include/scache.h +smtp_tlsrpt.o: ../../include/sock_addr.h +smtp_tlsrpt.o: ../../include/string_list.h +smtp_tlsrpt.o: ../../include/stringops.h +smtp_tlsrpt.o: ../../include/sys_defs.h +smtp_tlsrpt.o: ../../include/tls.h +smtp_tlsrpt.o: ../../include/tls_proxy.h +smtp_tlsrpt.o: ../../include/tlsrpt_wrapper.h +smtp_tlsrpt.o: ../../include/tok822.h +smtp_tlsrpt.o: ../../include/vbuf.h +smtp_tlsrpt.o: ../../include/vstream.h +smtp_tlsrpt.o: ../../include/vstring.h +smtp_tlsrpt.o: smtp.h +smtp_tlsrpt.o: smtp_tlsrpt.c smtp_trouble.o: ../../include/argv.h smtp_trouble.o: ../../include/attr.h smtp_trouble.o: ../../include/bounce.h diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c index 385c81ff4..c41cf91ae 100644 --- a/postfix/src/smtp/lmtp_params.c +++ b/postfix/src/smtp/lmtp_params.c @@ -67,6 +67,7 @@ VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0, VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0, + VAR_LMTP_TLSRPT_SOCKNAME, DEF_LMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0, 0, }; static const CONFIG_TIME_TABLE lmtp_time_table[] = { @@ -137,5 +138,6 @@ }; static const CONFIG_NBOOL_TABLE lmtp_nbool_table[] = { VAR_LMTP_REQ_DEADLINE, DEF_LMTP_REQ_DEADLINE, &var_smtp_req_deadline, + VAR_LMTP_TLSRPT_ENABLE, DEF_LMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable, 0, }; diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 51b2e6dba..0c737cbd6 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -638,6 +638,12 @@ /* .IP "\fBsmtp_tls_enable_rpk (no)\fR" /* Request that remote SMTP servers send an RFC7250 raw public key /* instead of an X.509 certificate. +/* .PP Available in Postfix version 3.10 and later: +/* .IP "\fBsmtp_tlsrpt_enable (no)\fR" +/* Enable support for RFC 8460 TLSRPT notifications. +/* .IP "\fBsmtp_tlsrpt_socket_name (empty)\fR" +/* The pathname of a UNIX-domain datagram socket that is managed +/* by a local TLSRPT reporting service. /* OBSOLETE STARTTLS CONTROLS /* .ad /* .fi @@ -1146,6 +1152,8 @@ int var_smtp_min_data_rate; char *var_use_srv_lookup; bool var_ign_srv_lookup_err; bool var_allow_srv_fallback; +bool var_smtp_tlsrpt_enable; +char *var_smtp_tlsrpt_sockname; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; @@ -1575,6 +1583,21 @@ static void pre_init(char *unused_name, char **unused_argv) mdalg = var_smtp_tls_fpt_dgst); smtp_tls_list_init(); tls_dane_loglevel(VAR_LMTP_SMTP(TLS_LOGLEVEL), var_smtp_tls_loglevel); +#ifdef USE_TLSRPT + if (var_smtp_tlsrpt_enable) { + if (smtp_mode) { + if (smtp_tlsrpt_pre_jail(VAR_SMTP_TLSRPT_SOCKNAME, + var_smtp_tlsrpt_sockname) < 0) + var_smtp_tlsrpt_enable = 0; + } else { + msg_warn("TLSRPT support is not implemented for LMTP"); + var_smtp_tlsrpt_enable = 0; + } + } +#else /* no USE_TLSRPT */ + if (var_smtp_tlsrpt_enable) + msg_warn("TLSRPT is selected, but TLSRPT is not compiled in"); +#endif /* USE_TLSRPT */ #else msg_warn("TLS has been selected, but TLS support is not compiled in"); #endif diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 60c68f845..f367ea1e0 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -108,8 +108,28 @@ typedef struct SMTP_TLS_POLICY { char *sni; /* Optional SNI name when not DANE */ int conn_reuse; /* enable connection reuse */ int enable_rpk; /* Enable server->client RPK */ + /* External policy info, for TLSRPT. */ + int ext_policy_ttl; /* TTL from DNS etc. */ + char *ext_policy_type; /* (sts) */ + ARGV *ext_policy_strings; /* policy strings from DNS etc. */ + char *ext_policy_domain; /* policy scope */ + ARGV *ext_mx_host_patterns; /* (sts) MX host patterns */ + char *ext_policy_failure; /* (sts) policy failure */ } SMTP_TLS_POLICY; + /* + * Names and values for external policy attributes in smtp_tls_policy_maps. + * These are not #ifdef USE_TLSRPT, so that a TLSRPT-aware STS plugin can be + * used whether or not Postfix was built with TLSRPT support. + */ +#define EXT_POLICY_TTL "policy_ttl" +#define EXT_POLICY_TTL_UNSET (-1) +#define EXT_POLICY_TYPE "policy_type" +#define EXT_POLICY_DOMAIN "policy_domain" +#define EXT_POLICY_STRING "policy_string" +#define EXT_MX_HOST_PATTERN "mx_host_pattern" +#define EXT_POLICY_FAILURE "policy_failure" + /* * smtp_tls_policy.c */ @@ -144,6 +164,12 @@ extern void smtp_tls_policy_cache_flush(void); _tls_policy_init_tmp->sni = 0; \ _tls_policy_init_tmp->conn_reuse = 0; \ _tls_policy_init_tmp->enable_rpk = 0; \ + _tls_policy_init_tmp->ext_policy_ttl = EXT_POLICY_TTL_UNSET; \ + _tls_policy_init_tmp->ext_policy_type = 0; \ + _tls_policy_init_tmp->ext_policy_domain = 0; \ + _tls_policy_init_tmp->ext_policy_strings = 0; \ + _tls_policy_init_tmp->ext_mx_host_patterns = 0; \ + _tls_policy_init_tmp->ext_policy_failure = 0; \ } while (0) #endif @@ -171,6 +197,9 @@ typedef struct SMTP_STATE { */ #ifdef USE_TLS SMTP_TLS_POLICY tls[1]; /* Usage: state->tls->member */ +#ifdef USE_TLSRPT + struct TLSRPT_WRAPPER *tlsrpt; +#endif #endif /* @@ -757,6 +786,18 @@ extern void smtp_quote_821_address(VSTRING *, const char *); */ extern int smtp_hfrom_format; + /* + * smtp_tlsrpt.c. + */ +#if defined(USE_TLS) && defined(USE_TLSRPT) +extern int smtp_tlsrpt_pre_jail(const char *sockname_pname, const char *sockname_pval); +extern void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain); +extern void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state); +extern void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state); +extern void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *, const char *ehlo_resp); + +#endif /* USE_TLSRPT && USE_TLS */ + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 68faca18e..df26d64ba 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -104,6 +104,7 @@ #include #include #include +#include /* DNS library. */ @@ -911,6 +912,21 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, SMTP_ITER_INIT(iter, dest, NO_HOST, NO_ADDR, port, state); + /* + * TODO(wietse) If the domain publishes a TLSRPT policy, they expect + * that clients use SMTP over TLS. Should we upgrade a TLS security + * level of "may" to "encrypt"? This would disable falling back to + * plaintext, and could break interoperability with receivers that + * crank up security up to 11. + */ +#ifdef USE_TLSRPT + if (smtp_mode && var_smtp_tlsrpt_enable + && !valid_hostaddr(domain, DONT_GRIPE)) + smtp_tlsrpt_create_wrapper(state, domain); + else + state->tlsrpt = 0; +#endif /* USE_TLSRPT */ + /* * Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a * quoted domain is specified or when DNS lookups are disabled. @@ -1076,6 +1092,12 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, session->state = state; #ifdef USE_TLS session->tls_nexthop = domain; +#ifdef USE_TLSRPT + if (state->tlsrpt && state->tls->level > TLS_LEV_NONE) { + smtp_tlsrpt_set_tls_policy(state); + smtp_tlsrpt_set_tcp_connection(state); + } +#endif /* USE_TLSRPT */ #endif if (addr->pref == domain_best_pref) session->features |= SMTP_FEATURE_BEST_MX; diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c index cebff9380..f58f9eb34 100644 --- a/postfix/src/smtp/smtp_params.c +++ b/postfix/src/smtp/smtp_params.c @@ -68,6 +68,7 @@ VAR_TLSPROXY_SERVICE, DEF_TLSPROXY_SERVICE, &var_tlsproxy_service, 1, 0, VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0, + VAR_SMTP_TLSRPT_SOCKNAME, DEF_SMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0, 0, }; static const CONFIG_TIME_TABLE smtp_time_table[] = { @@ -141,5 +142,6 @@ }; static const CONFIG_NBOOL_TABLE smtp_nbool_table[] = { VAR_SMTP_REQ_DEADLINE, DEF_SMTP_REQ_DEADLINE, &var_smtp_req_deadline, + VAR_SMTP_TLSRPT_ENABLE, DEF_SMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable, 0, }; diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index e022bc2cf..bdca86ce8 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -155,6 +155,9 @@ #include #include #include +#if defined(USE_TLS) && defined(USE_TLSRPT) +#include +#endif /* Application-specific. */ @@ -475,6 +478,11 @@ int smtp_helo(SMTP_STATE *state) else session->features &= ~SMTP_FEATURE_ESMTP; } +#ifdef USE_TLSRPT + if (state->tlsrpt + && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) == 0) + trw_set_ehlo_resp(state->tlsrpt, resp->str); +#endif } if ((session->features & SMTP_FEATURE_ESMTP) == 0) { where = "performing the HELO handshake"; @@ -484,6 +492,10 @@ int smtp_helo(SMTP_STATE *state) "host %s refused to talk to me: %s", session->namaddr, translit(resp->str, "\n", " "))); +#ifdef USE_TLSRPT + if (state->tlsrpt) + trw_set_ehlo_resp(state->tlsrpt, resp->str); +#endif } } else { where = "performing the LHLO handshake"; @@ -798,11 +810,19 @@ int smtp_helo(SMTP_STATE *state) * although support for it was announced in the EHLO response. */ session->features &= ~SMTP_FEATURE_STARTTLS; - if (TLS_REQUIRED(state->tls->level)) + if (TLS_REQUIRED(state->tls->level)) { +#ifdef USE_TLSRPT + if (state->tlsrpt) + trw_report_failure(state->tlsrpt, + TLSRPT_STARTTLS_NOT_SUPPORTED, + /* additional_detail= */ (char *) 0, + /* failure_reason= */ (char *) 0); +#endif return (smtp_site_fail(state, STR(iter->host), resp, "TLS is required, but host %s refused to start TLS: %s", session->namaddr, translit(resp->str, "\n", " "))); + } /* Else try to continue in plain-text mode. */ } @@ -815,6 +835,13 @@ int smtp_helo(SMTP_STATE *state) */ if (TLS_REQUIRED(state->tls->level)) { if (!(session->features & SMTP_FEATURE_STARTTLS)) { +#ifdef USE_TLSRPT + if (state->tlsrpt) + trw_report_failure(state->tlsrpt, + TLSRPT_STARTTLS_NOT_SUPPORTED, + /* additional_detail= */ (char *) 0, + /* failure_reason= */ (char *) 0); +#endif return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "4.7.4"), "TLS is required, but was not offered by host %s", @@ -942,6 +969,12 @@ static int smtp_start_tls(SMTP_STATE *state) = vstring_str(state->tls->exclusions), matchargv = state->tls->matchargv, mdalg = var_smtp_tls_fpt_dgst, +#ifdef USE_TLSRPT + tlsrpt = state->tlsrpt, +#else + tlsrpt = 0, +#endif + fail_type = 0, dane = state->tls->dane); /* @@ -1065,6 +1098,12 @@ static int smtp_start_tls(SMTP_STATE *state) = vstring_str(state->tls->exclusions), matchargv = state->tls->matchargv, mdalg = var_smtp_tls_fpt_dgst, +#ifdef USE_TLSRPT + tlsrpt = state->tlsrpt, +#else + tlsrpt = 0, +#endif + fail_type = state->tls->ext_policy_failure, dane = state->tls->dane); /* @@ -1125,10 +1164,42 @@ static int smtp_start_tls(SMTP_STATE *state) * we must check that here, and not state->tls->level. */ if (TLS_MUST_MATCH(session->tls_context->level)) - if (!TLS_CERT_IS_MATCHED(session->tls_context)) + if (!TLS_CERT_IS_MATCHED(session->tls_context)) { +#ifdef USE_TLSRPT + + /* + * Don't create a TLSRPT failure report here, if the TLS engine + * already reported a more specific reason. + */ + if (state->tlsrpt && session->tls_context->rpt_reported == 0) { + if (!TLS_CERT_IS_TRUSTED(session->tls_context)) { + (void) trw_report_failure(state->tlsrpt, + TLSRPT_CERTIFICATE_NOT_TRUSTED, + /* additional_detail= */ (char *) 0, + /* failure_reason= */ (char *) 0); + } else { + (void) trw_report_failure(state->tlsrpt, + TLSRPT_CERTIFICATE_HOST_MISMATCH, + /* additional_detail= */ (char *) 0, + /* failure_reason= */ (char *) 0); + } + } +#endif return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "4.7.5"), "Server certificate not verified")); + } + + /* + * Create a TLSRPT success report only if the TLS engine has not reported + * a failure (For example, the TLS handshake may be successful, but the + * security level was downgraded from opportunistic or half "dane" to + * "encrypt"). + */ +#ifdef USE_TLSRPT + if (state->tlsrpt && session->tls_context->rpt_reported == 0) + (void) trw_report_success(state->tlsrpt); +#endif /* * At this point we have to re-negotiate the "EHLO" to reget the diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index 6b81fa4ed..c2df5a5d5 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -50,6 +50,13 @@ #include #include + /* + * TLS library. + */ +#if defined(USE_TLS) && defined(USE_TLSRPT) +#include +#endif + /* Application-specific. */ #include "smtp.h" @@ -73,6 +80,9 @@ SMTP_STATE *smtp_state_alloc(void) state->iterator->host = vstring_alloc(100); state->iterator->addr = vstring_alloc(100); state->iterator->saved_dest = vstring_alloc(100); +#ifdef TLSRPT + state->tlsrpt = 0; +#endif if (var_smtp_cache_conn) { state->dest_label = vstring_alloc(10); state->dest_prop = vstring_alloc(10); @@ -105,6 +115,10 @@ void smtp_state_free(SMTP_STATE *state) vstring_free(state->iterator->host); vstring_free(state->iterator->addr); vstring_free(state->iterator->saved_dest); +#ifdef USE_TLSRPT + if (state->tlsrpt) + trw_free(state->tlsrpt); +#endif if (state->dest_label) vstring_free(state->dest_label); if (state->dest_prop) diff --git a/postfix/src/smtp/smtp_tls_policy.c b/postfix/src/smtp/smtp_tls_policy.c index f407d6579..9640f27a4 100644 --- a/postfix/src/smtp/smtp_tls_policy.c +++ b/postfix/src/smtp/smtp_tls_policy.c @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,10 @@ #include #include +/* TLS library. */ + +#include + /* DNS library. */ #include @@ -221,15 +226,21 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, { const char *lookup; char *policy; - char *saved_policy; + char *saved_policy = 0; char *tok; - const char *err; char *name; char *val; static VSTRING *cbuf; + char *free_me = 0; #undef FREE_RETURN -#define FREE_RETURN do { myfree(saved_policy); return; } while (0) +#define FREE_RETURN do { \ + if (saved_policy) \ + myfree(saved_policy); \ + if (free_me) \ + myfree(free_me); \ + return; \ + } while (0) #define INVALID_RETURN(why, levelp) do { \ MARK_INVALID((why), (levelp)); FREE_RETURN; } while (0) @@ -250,7 +261,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, } saved_policy = policy = mystrdup(lookup); - if ((tok = mystrtok(&policy, CHARS_COMMA_SP)) == 0) { + if ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) == 0) { msg_warn("%s: invalid empty policy", WHERE); INVALID_RETURN(tls->why, site_level); } @@ -265,7 +276,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, * Warn about ignored attributes when TLS is disabled. */ if (*site_level < TLS_LEV_MAY) { - while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0) + while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) msg_warn("%s: ignoring attribute \"%s\" with TLS disabled", WHERE, tok); FREE_RETURN; @@ -275,8 +286,12 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, * Errors in attributes may have security consequences, don't ignore * errors that can degrade security. */ - while ((tok = mystrtok(&policy, CHARS_COMMA_SP)) != 0) { - if ((err = split_nameval(tok, &name, &val)) != 0) { + while ((tok = mystrtokq(&policy, CHARS_COMMA_SP, CHARS_BRACE)) != 0) { + const char *err; + + if ((tok[0] == CHARS_BRACE[0] + && (err = free_me = extpar(&tok, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0) + || (err = split_nameval(tok, &name, &val)) != 0) { msg_warn("%s: malformed attribute/value pair \"%s\": %s", WHERE, tok, err); INVALID_RETURN(tls->why, site_level); @@ -391,6 +406,7 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, } continue; } + /* Last one wins. */ if (!strcasecmp(name, "enable_rpk")) { /* Ultimately ignored at some security levels */ if (strcasecmp(val, "yes") == 0) { @@ -404,10 +420,96 @@ static void tls_policy_lookup_one(SMTP_TLS_POLICY *tls, int *site_level, } continue; } + /* Only one instance per policy. */ + if (!strcasecmp(name, EXT_POLICY_TTL)) { + char *end; + long lval; + + if (tls->ext_policy_ttl != EXT_POLICY_TTL_UNSET) { + msg_warn("%s: attribute \"%s\" is specified multiple times", + WHERE, name); + INVALID_RETURN(tls->why, site_level); + } + if (!alldig(val) || ((lval = sane_strtol(val, &end, 10)), + ((tls->ext_policy_ttl = lval) != lval)) + || *end != 0) { + msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"", + WHERE, name, val); + INVALID_RETURN(tls->why, site_level); + } + continue; + } + /* Only one instance per policy. */ + if (!strcasecmp(name, EXT_POLICY_TYPE)) { + if (tls->ext_policy_type) { + msg_warn("%s: attribute \"%s\" is specified multiple times", + WHERE, name); + INVALID_RETURN(tls->why, site_level); + } + if (!valid_tlsrpt_policy_type(val)) { + msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"", + WHERE, name, val); + INVALID_RETURN(tls->why, site_level); + } + tls->ext_policy_type = mystrdup(val); + continue; + } + if (!strcasecmp(name, EXT_POLICY_DOMAIN)) { + if (tls->ext_policy_domain) { + msg_warn("%s: attribute \"%s\" is specified multiple times", + WHERE, name); + INVALID_RETURN(tls->why, site_level); + } + if (!valid_hostname(val, DO_GRIPE)) { + msg_warn("%s: attribute \"%s\" has a malformed value: \"%s\"", + WHERE, name, val); + INVALID_RETURN(tls->why, site_level); + } + tls->ext_policy_domain = mystrdup(val); + continue; + } + /* Multiple instances per policy are allowed. */ + if (!strcasecmp(name, EXT_POLICY_STRING)) { + if (tls->ext_policy_strings == 0) + tls->ext_policy_strings = argv_alloc(1); + argv_add(tls->ext_policy_strings, val, (char *) 0); + continue; + } + /* Multiple instances per policy are allowed. */ + if (!strcasecmp(name, EXT_MX_HOST_PATTERN)) { + if (tls->ext_mx_host_patterns == 0) + tls->ext_mx_host_patterns = argv_alloc(1); + argv_add(tls->ext_mx_host_patterns, val, (char *) 0); + continue; + } + /* Only one instance per policy. */ + if (!strcasecmp(name, EXT_POLICY_FAILURE)) { + if (tls->ext_policy_failure != 0) { + msg_warn("%s: attribute \"%s\" is specified multiple times", + WHERE, name); + INVALID_RETURN(tls->why, site_level); + } + if (!valid_tlsrpt_policy_failure(val)) { + msg_warn("%s: attribute \"%s\" has an unexpected value: \"%s\"", + WHERE, name, val); + INVALID_RETURN(tls->why, site_level); + } + tls->ext_policy_failure = mystrdup(val); + continue; + } msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name); INVALID_RETURN(tls->why, site_level); } - + if (tls->ext_policy_type == 0) { + if (tls->ext_policy_ttl || tls->ext_policy_strings + || tls->ext_policy_domain || tls->ext_mx_host_patterns + || tls->ext_policy_failure) { + msg_warn("%s: built-in policy has unexpected attribute " + "policy_ttl, policy_domain, policy_string, " + "mx_host_pattern or policy_failure", WHERE); + INVALID_RETURN(tls->why, site_level); + } + } FREE_RETURN; } @@ -707,6 +809,16 @@ static void policy_delete(void *item, void *unused_context) if (tls->dane) tls_dane_free(tls->dane); dsb_free(tls->why); + if (tls->ext_policy_type) + myfree(tls->ext_policy_type); + if (tls->ext_policy_domain) + myfree(tls->ext_policy_domain); + if (tls->ext_policy_strings) + argv_free(tls->ext_policy_strings); + if (tls->ext_mx_host_patterns) + argv_free(tls->ext_mx_host_patterns); + if (tls->ext_policy_failure) + myfree(tls->ext_policy_failure); myfree((void *) tls); } diff --git a/postfix/src/smtp/smtp_tlsrpt.c b/postfix/src/smtp/smtp_tlsrpt.c new file mode 100644 index 000000000..b0a6af626 --- /dev/null +++ b/postfix/src/smtp/smtp_tlsrpt.c @@ -0,0 +1,421 @@ +/*++ +/* NAME +/* smtp_tlsrpt 3 +/* SUMMARY +/* TLSRPT support for the SMTP protocol engine +/* SYNOPSIS +/* #include +/* +/* int smtp_tlsrpt_pre_jail( +/* const char *sockname_pname, +/* const char *sockname_pval) +/* +/* void smtp_tlsrpt_create_wrapper( +/* SMTP_STATE *state, +/* const char *domain) +/* +/* void smtp_tlsrpt_set_tls_policy( +/* SMTP_STATE *state) +/* +/* void smtp_tlsrpt_set_tcp_connection( +/* SMTP_STATE *state) +/* +/* void smtp_tlsrpt_set_ehlo_resp( +/* SMTP_STATE *state, +/* const char *ehlo_resp) +/* DESCRIPTION +/* This module collects TLSRPT policy information and selected SMTP +/* protocol engine state in a TLSRPT_WRAPPER object. This will be +/* passed to (possibly remote) TLS protocol engine, so that it +/* can report a TLS error to a TLSRPT library. The SMTP protocol +/* engine uses the information to report a TLS error or success. +/* +/* smtp_tls_pre_jail() does configuration sanity checks and +/* returns 0 if successful, i.e. TLSRPT support is properly +/* configured. Otherwise it returns -1 and logs a warning. Arguments: +/* .IP sockname_pname +/* The name of a configuration parameter for the endpoint that is +/* managed by TLSRPT infrastructure. This is used in a diagnostic +/* message. +/* .IP sockname_pval +/* The value of said parameter. +/* .PP +/* smtp_tlsrpt_create_wrapper() destroys any TLSRPT_WRAPPER +/* referenced by state->tlsrpt, and looks for a TLSRPT +/* policy for the specified domain. If one policy exists, +/* smtp_tlsrpt_create_wrapper() attaches a TLSRPT_WRAPPER instance +/* to state->tlsrpt. Otherwise, state->tlsrpt will be null, and +/* other smtp_tlsrpt_* calls must not be made. The TLSRPT_WRAPPER +/* instance may be reused for different SMTP connections with the +/* same TLSRPT policy domain. Arguments: +/* .IP domain +/* The name of a domain that may publish a TLSRPT policy. An +/* internationalized domain name may be in U-label or A-label form +/* (it will be converted to A-label internally). +/* .PP +/* smtp_tlsrpt_set_tls_policy() updates the TLSRPT_WRAPPER with +/* DANE or STS TLS policy information, and clears information +/* that was added with smtp_tlsrpt_set_tcp_connection() or +/* smtp_tlsrpt_set_ehlo_resp(). +/* .PP +/* smtp_tlsrpt_set_tcp_connection() updates the TLSRPT_WRAPPER with +/* TCP connection properties. +/* .PP +/* smtp_tlsrpt_set_ehlo_resp() updates the TLSRPT_WRAPPER with the +/* SMTP server's EHLO response. +/* BUGS +/* This module inherits all limitations from tlsrpt_wrapper(3). +/* SEE ALSO +/* tlsrpt_wrapper(3) TLSRPT support for the TLS protocol engine. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* porcupine.org +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include +#include +#include +#include +#include +#include + + /* + * Global library. + */ +#include + + /* + * TLS library. + */ +#include +#include + + /* + * Application-specific. + */ +#include + +#if defined(USE_TLS) && defined(USE_TLSRPT) + +static const char smtp_tlsrpt_support[] = "TLSRPT support"; + +/* smtp_tlsrpt_pre_jail - pre-jail configuration sanity check */ + +int smtp_tlsrpt_pre_jail(const char *sockname_pname, + const char *sockname_pval) +{ + if (smtp_dns_support == SMTP_DNS_DISABLED) { + msg_warn("Cannot enable TLRPT support: DNS is disabled"); + return (-1); + } + if (*sockname_pval == 0) { + msg_warn("%s: parameter %s has empty value -- %s will be disabled", + smtp_tlsrpt_support, sockname_pname, smtp_tlsrpt_support); + return (-1); + } + return (0); +} + +/* smtp_tlsrpt_find_policy - look up TLSRPT policy and verify version ID */ + +static DNS_RR *smtp_tlsrpt_find_policy(const char *adomain) +{ + VSTRING *why = vstring_alloc(100); + VSTRING *qname = vstring_alloc(100); + DNS_RR *rr_list = 0; + DNS_RR *rr_result = 0; + DNS_RR *rr; + DNS_RR *next; + int res_opt = 0; + int dns_status; + + /* + * Preliminaries. + */ + if (smtp_dns_support == SMTP_DNS_DNSSEC) + res_opt |= RES_USE_DNSSEC; + + /* + * Lexical features: As specified in RFC 8460, a TLSRPT policy record + * must start with a version field ("v=TLSRPTv1") followed by *WSP;*WSP + * and at least one other field (we must not assume that the second field + * will be "rua"). We leave further validation to the TLSRPT library, + * where it belongs. + */ +#define TLSRPTv1_MAGIC "v=TLSRPTv1" +#define TLSRPTv1_MAGIC_LEN (sizeof(TLSRPTv1_MAGIC) - 1) +#define RFC5234_WSP " \t" + + /* + * Look up TXT records. Ignore records that don't start with the expected + * version ID, and require that there is exactly one such DNS record. + */ + vstring_sprintf(qname, "_smtp._tls_.%s", adomain); + dns_status = dns_lookup(STR(qname), T_TXT, res_opt, &rr_list, + (VSTRING *) 0, why); + vstring_free(qname); + if (dns_status != DNS_OK) { + switch (dns_status) { + case DNS_NOTFOUND: + case DNS_POLICY: + /* Expected results. */ + break; + default: + /* Unexpected results. */ + msg_warn("%s: policy lookup failed for %s: %s", + smtp_tlsrpt_support, adomain, STR(why)); + } + } else { + for (rr = rr_list; rr; rr = next) { + char *cp; + + next = rr->next; + if (strncmp(rr->data, TLSRPTv1_MAGIC, TLSRPTv1_MAGIC_LEN) != 0) + /* Ignore non-TLSRPT info. */ + continue; + cp = rr->data + TLSRPTv1_MAGIC_LEN; + + /* + * Should the TLSRPT library validate the entire policy for us? + */ + if (cp[strspn(cp, RFC5234_WSP)] != ';') { + msg_warn("%s: ignoring malformed policy for %s:, \"%s\"", + smtp_tlsrpt_support, adomain, rr->data); + continue; + } + if (rr_result) { + msg_warn("%s: Too many policies for %s", + smtp_tlsrpt_support, adomain); + dns_rr_free(rr_result); + break; + } + rr_result = rr; + rr_list = dns_rr_detach(rr_list, rr); + } + } + vstring_free(why); + if (rr_list) + dns_rr_free(rr_list); + return (rr_result); +} + +/* smtp_tlsrpt_create_wrapper - look up policy and attach TLSRPT_WRAPPER */ + +void smtp_tlsrpt_create_wrapper(SMTP_STATE *state, const char *domain) +{ + const char *adomain; + DNS_RR *rr; + + /* + * TODO(wietse): document in a suitable place that state->tlsrpt exists + * only if the next-hop domain announces a TLSRPT policy. + */ + if (state->tlsrpt) { + trw_free(state->tlsrpt); + state->tlsrpt = 0; + } + + /* + * IDNA support. An internationalized domain name must be in A-label form + * 1) for TLSRPT summaries and 2) for DNS lookups. The A-label lookup + * result comes from a limited-size in-process cache, so it does not + * matter that the SMTP client requests the same mapping later. + */ +#ifndef NO_EAI + if (!allascii(domain) && (adomain = midna_domain_to_ascii(domain)) != 0) { + if (msg_verbose) + msg_info("%s: internationalized domain %s asciified to %s", + smtp_tlsrpt_support, domain, adomain); + } else +#endif + adomain = domain; + + if ((rr = smtp_tlsrpt_find_policy(adomain)) != 0) { + if (msg_verbose) + msg_info("%s: domain %s has policy %.100s", + smtp_tlsrpt_support, domain, rr->data); + state->tlsrpt = trw_create( + /* rpt_socket_name= */ var_smtp_tlsrpt_sockname, + /* rpt_policy_domain= */ adomain, + /* rpt_policy_string= */ rr->data); + dns_rr_free(rr); + } else { + if (msg_verbose) + msg_info("%s: no policy for domain %s", + smtp_tlsrpt_support, domain); + } +} + +/* smtp_tlsrpt_set_dane_policy - add DANE policy properties */ + +static void smtp_tlsrpt_set_dane_policy(SMTP_STATE *state) +{ + VSTRING *buf = vstring_alloc(200); + ARGV *argv = argv_alloc(10); + TLS_DANE *dane = state->tls->dane; + TLS_TLSA *tlsa; + + for (tlsa = dane->tlsa; tlsa != 0; tlsa = tlsa->next) { + vstring_sprintf(buf, "%d %d %d ", tlsa->usage, + tlsa->selector, tlsa->mtype); + hex_encode_opt(buf, (char *) tlsa->data, tlsa->length, + HEX_ENCODE_FLAG_APPEND); + argv_add(argv, STR(buf), (char *) 0); + } + trw_set_tls_policy(state->tlsrpt, TLSRPT_POLICY_TLSA, + (const char *const *) argv->argv, dane->base_domain, + /* mx_host_patterns= */ (const char *const *) 0); + argv_free(argv); + vstring_free(buf); +} + +/* smtp_tlsrpt_set_ext_policy - add external policy from smtp_tls_policy_maps */ + +static void smtp_tlsrpt_set_ext_policy(SMTP_STATE *state) +{ + SMTP_TLS_POLICY *tls = state->tls; + tlsrpt_policy_type_t policy_type_val; + + switch (policy_type_val = convert_tlsrpt_policy_type(tls->ext_policy_type)) { + case TLSRPT_POLICY_STS: + trw_set_tls_policy(state->tlsrpt, policy_type_val, + (const char *const *) tls->ext_policy_strings->argv, + tls->ext_policy_domain, + (const char *const *) tls->ext_mx_host_patterns->argv); + break; + case TLSRPT_NO_POLICY_FOUND: + trw_set_tls_policy(state->tlsrpt, policy_type_val, + /* tls_policy_strings= */ (const char *const *) 0, + /* tls_policy_domain= */ (const char *) 0, + /* mx_host_patterns= */ (const char *const *) 0); + break; + default: + msg_panic("unexpected policy type: \"%s\"", + tls->ext_policy_type); + } + + /* + * TODO(wietse) propagate tls->policy_failure to force policy enforcement + * to fail with the indicated error, and prevent a false positive match + * when a certificate would satisfy conventional PKI constraints. + */ +} + +/* smtp_tlsrpt_set_tls_policy - set built-in or external policy */ + +void smtp_tlsrpt_set_tls_policy(SMTP_STATE *state) +{ + SMTP_TLS_POLICY *tls = state->tls; + + if (TLS_DANE_BASED(tls->level)) { /* Desired by local policy */ + if (tls->dane != 0) /* Actual policy */ + smtp_tlsrpt_set_dane_policy(state); + } else if (tls->ext_policy_type) { + smtp_tlsrpt_set_ext_policy(state); + } +} + +/* sane_sockaddr_to_hostaddr - TODO(wietse) move to library */ + +#include + +static const INET_PROTO_INFO *proto_info; + +static int sane_sockaddr_to_hostaddr(struct sockaddr *addr_storage, + SOCKADDR_SIZE addr_storage_len, + MAI_HOSTADDR_STR *addr_buf, + MAI_SERVPORT_STR *port_buf, + int socktype) +{ + int aierr; + + if (proto_info == 0) + proto_info = inet_proto_info(); + + if ((aierr = sockaddr_to_hostaddr(addr_storage, addr_storage_len, + addr_buf, port_buf, socktype)) == 0 + && strncasecmp("::ffff:", addr_buf->buf, 7) == 0 + && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) + memmove(addr_buf->buf, addr_buf->buf + 7, + sizeof(addr_buf->buf) - 7); + return (aierr); +} + +/* smtp_tlsrpt_set_tcp_connection - set TCP connection info from SMTP_STATE */ + +void smtp_tlsrpt_set_tcp_connection(SMTP_STATE *state) +{ + SMTP_ITERATOR *iter = state->iterator; + SMTP_SESSION *session = state->session; + MAI_HOSTADDR_STR client_addr; + struct sockaddr_storage addr_storage; + SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); + int aierr; + + /* + * Get the IP client address string. The Postfix SMTP_ITERATOR already + * contains strings with other connection information. + */ + if (getsockname(vstream_fileno(session->stream), + (struct sockaddr *) &addr_storage, + &addr_storage_len) < 0) { + msg_warn("%s: getsockname() failed (%m)" + " skipping the ignoring client-side IP address", + smtp_tlsrpt_support); + client_addr.buf[0] = 0; + } else if ((aierr = sane_sockaddr_to_hostaddr( + (struct sockaddr *) &addr_storage, + addr_storage_len, &client_addr, + (MAI_SERVPORT_STR *) 0, + SOCK_STREAM)) != 0) { + msg_warn("%s: cannot convert IP address to string (%s)" + " -- skipping the client-side IP address", + smtp_tlsrpt_support, MAI_STRERROR(aierr)); + client_addr.buf[0] = 0; + } + trw_set_tcp_connection(state->tlsrpt, client_addr.buf, STR(iter->host), + STR(iter->addr)); +} + +/* smtp_tlsrpt_set_ehlo_resp - format and set EHLO response */ + +void smtp_tlsrpt_set_ehlo_resp(SMTP_STATE *state, const char *reply) +{ + ARGV *argv; + VSTRING *buf; + char **cpp; + + /* + * Generate SMTP-style line breaks ("\r\n") for a multiline response. + * Internally, smtp_chat_resp() returns a multiline response as text + * separated with "\n". This is because Postfix by design removes + * protocol-specific line endings on input, uses its own internal form to + * represent text lines, and generates protocol-specific line endings on + * output. The conversion to "\r\n" below is such an output conversion. + */ + buf = vstring_alloc(100); + argv = argv_split(reply, "\n"); + for (cpp = argv->argv; *cpp; cpp++) { + vstring_strcat(buf, *cpp); + if (cpp[1]) + vstring_strcat(buf, "\r\n"); + } + argv_free(argv); + trw_set_ehlo_resp(state->tlsrpt, STR(buf)); + vstring_free(buf); +} + +#endif /* USE_TLSRPT && USE_TLS */ diff --git a/postfix/src/tls/Makefile.in b/postfix/src/tls/Makefile.in index 4b562b3fb..3d3bf6e29 100644 --- a/postfix/src/tls/Makefile.in +++ b/postfix/src/tls/Makefile.in @@ -9,7 +9,7 @@ SRCS = tls_prng_dev.c tls_prng_egd.c tls_prng_file.c tls_fprint.c \ tls_proxy_server_init_print.c tls_proxy_server_init_scan.c \ tls_proxy_client_start_print.c tls_proxy_client_start_scan.c \ tls_proxy_server_start_print.c tls_proxy_server_start_scan.c \ - tls_proxy_client_misc.c + tls_proxy_client_misc.c tlsrpt_wrapper.c OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \ tls_prng_exch.o tls_stream.o tls_bio_ops.o tls_misc.o tls_dh.o \ tls_verify.o tls_dane.o tls_certkey.o tls_session.o \ @@ -18,8 +18,8 @@ OBJS = tls_prng_dev.o tls_prng_egd.o tls_prng_file.o tls_fprint.o \ tls_proxy_clnt.o tls_proxy_context_print.o tls_proxy_context_scan.o \ tls_proxy_client_print.o tls_proxy_client_scan.o \ tls_proxy_server_print.o tls_proxy_server_scan.o \ - tls_proxy_client_misc.o -HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h + tls_proxy_client_misc.o tlsrpt_wrapper.o +HDRS = tls.h tls_prng.h tls_scache.h tls_mgr.h tls_proxy.h tlsrpt_wrapper.h TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -182,6 +182,7 @@ tls_client.o: tls.h tls_client.o: tls_client.c tls_client.o: tls_mgr.h tls_client.o: tls_scache.h +tls_client.o: tlsrpt_wrapper.h tls_dane.o: ../../include/argv.h tls_dane.o: ../../include/check_arg.h tls_dane.o: ../../include/ctable.h @@ -365,6 +366,7 @@ tls_proxy_client_print.o: ../../include/vstring.h tls_proxy_client_print.o: tls.h tls_proxy_client_print.o: tls_proxy.h tls_proxy_client_print.o: tls_proxy_client_print.c +tls_proxy_client_print.o: tlsrpt_wrapper.h tls_proxy_client_scan.o: ../../include/argv.h tls_proxy_client_scan.o: ../../include/argv_attr.h tls_proxy_client_scan.o: ../../include/attr.h @@ -386,6 +388,7 @@ tls_proxy_client_scan.o: ../../include/vstring.h tls_proxy_client_scan.o: tls.h tls_proxy_client_scan.o: tls_proxy.h tls_proxy_client_scan.o: tls_proxy_client_scan.c +tls_proxy_client_scan.o: tlsrpt_wrapper.h tls_proxy_clnt.o: ../../include/argv.h tls_proxy_clnt.o: ../../include/attr.h tls_proxy_clnt.o: ../../include/check_arg.h @@ -587,3 +590,15 @@ tls_verify.o: ../../include/vstream.h tls_verify.o: ../../include/vstring.h tls_verify.o: tls.h tls_verify.o: tls_verify.c +tls_verify.o: tlsrpt_wrapper.h +tlsrpt_wrapper.o: ../../include/argv.h +tlsrpt_wrapper.o: ../../include/check_arg.h +tlsrpt_wrapper.o: ../../include/msg.h +tlsrpt_wrapper.o: ../../include/mymalloc.h +tlsrpt_wrapper.o: ../../include/name_code.h +tlsrpt_wrapper.o: ../../include/stringops.h +tlsrpt_wrapper.o: ../../include/sys_defs.h +tlsrpt_wrapper.o: ../../include/vbuf.h +tlsrpt_wrapper.o: ../../include/vstring.h +tlsrpt_wrapper.o: tlsrpt_wrapper.c +tlsrpt_wrapper.o: tlsrpt_wrapper.h diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 3ec41ba36..b3e5b7cb2 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -205,7 +205,7 @@ extern TLS_DANE *tls_dane_alloc(void); extern void tls_tlsa_free(TLS_TLSA *); extern void tls_dane_free(TLS_DANE *); extern void tls_dane_add_fpt_digests(TLS_DANE *, int, const char *, - const char *, int); + const char *, int); extern TLS_DANE *tls_dane_resolve(unsigned, const char *, DNS_RR *, int); extern int tls_dane_load_trustfile(TLS_DANE *, const char *); @@ -261,6 +261,8 @@ typedef struct { int errordepth; /* Chain depth of error cert */ int errorcode; /* First error at error depth */ int must_fail; /* Failed to load trust settings */ + int rpt_reported; /* Failure was reported with TLSRPT */ + char *fail_type; /* Doomed by policy */ } TLS_SESS_STATE; /* @@ -493,6 +495,8 @@ typedef struct { const ARGV *matchargv; /* Cert match patterns */ const char *mdalg; /* default message digest algorithm */ const TLS_DANE *dane; /* DANE TLSA verification */ + struct TLSRPT_WRAPPER *tlsrpt; /* RFC 8460 reporting */ + char *fail_type; /* verification must fail by policy */ } TLS_CLIENT_START_PROPS; extern TLS_APPL_STATE *tls_client_init(const TLS_CLIENT_INIT_PROPS *); @@ -516,12 +520,13 @@ extern TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *, a6, a7, a8, a9, a10, a11, a12, a13, a14)) #define TLS_CLIENT_START(props, a1, a2, a3, a4, a5, a6, a7, a8, a9, \ - a10, a11, a12, a13, a14, a15, a16, a17, a18) \ + a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) \ tls_client_start((((props)->a1), ((props)->a2), ((props)->a3), \ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \ ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \ - ((props)->a16), ((props)->a17), ((props)->a18), (props))) + ((props)->a16), ((props)->a17), ((props)->a18), ((props)->a19), \ + ((props)->a20), (props))) /* * tls_server.c @@ -654,7 +659,7 @@ extern void tls_auto_groups(SSL_CTX *, const char *, const char *); extern char *tls_peer_CN(X509 *, const TLS_SESS_STATE *); extern char *tls_issuer_CN(X509 *, const TLS_SESS_STATE *); extern int tls_verify_certificate_callback(int, X509_STORE_CTX *); -extern void tls_log_verify_error(TLS_SESS_STATE *); +extern void tls_log_verify_error(TLS_SESS_STATE *, struct TLSRPT_WRAPPER *); /* * tls_dane.c diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 3eda859cc..7834ef8ae 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -160,6 +160,7 @@ #ifdef USE_TLS #include +#include #ifdef STRCASECMP_IN_STRINGS_H #include @@ -363,11 +364,12 @@ static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert, if (!TLS_CERT_IS_MATCHED(TLScontext) && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) { if (TLScontext->session_reused == 0) - tls_log_verify_error(TLScontext); + tls_log_verify_error(TLScontext, props->tlsrpt); else msg_info("%s: re-using session with untrusted peer credential, " "look for details earlier in the log", props->namaddr); } + /* TODO(wietse) In the non-reuse case, tlsrprt the root cause? */ } /* verify_rpk - process RFC7250 raw public key verification status */ @@ -407,11 +409,12 @@ static void verify_rpk(TLS_SESS_STATE *TLScontext, EVP_PKEY *peerpkey, if (!TLS_CERT_IS_MATCHED(TLScontext) && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) { if (TLScontext->session_reused == 0) - tls_log_verify_error(TLScontext); + tls_log_verify_error(TLScontext, props->tlsrpt); else msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); } + /* TODO(wietse) In the non-reuse case, tlsrprt the root cause? */ } /* add_namechecks - tell OpenSSL what names to check */ @@ -579,6 +582,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext, * therefore valid for use with SNI. */ if (SSL_dane_enable(TLScontext->con, 0) <= 0) { + /* TLSRPT: Local resource error, don't report. */ msg_warn("%s: error enabling DANE-based certificate validation", TLScontext->namaddr); tls_print_errors(); @@ -595,6 +599,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext, case TLS_LEV_FPRINT: /* Synthetic DANE for fingerprint security */ if (SSL_dane_enable(TLScontext->con, 0) <= 0) { + /* TLSRPT: Local resource error, don't report. */ msg_warn("%s: error enabling fingerprint certificate validation", props->namaddr); tls_print_errors(); @@ -608,6 +613,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext, if (TLScontext->dane != 0 && TLScontext->dane->tlsa != 0) { /* Synthetic DANE for per-destination trust-anchors */ if (SSL_dane_enable(TLScontext->con, NULL) <= 0) { + /* TLSRPT: Local resource error, don't report. */ msg_warn("%s: error configuring local trust anchors", props->namaddr); tls_print_errors(); @@ -622,6 +628,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext, if (sni) { if (strlen(sni) > TLSEXT_MAXLEN_host_name) { + /* TLSRPT: Local configuration error, don't report. */ msg_warn("%s: ignoring too long SNI hostname: %.100s", props->namaddr, sni); return (0); @@ -633,6 +640,7 @@ static int tls_auth_enable(TLS_SESS_STATE *TLScontext, * failed to send the SNI name, we have little choice but to abort. */ if (!SSL_set_tlsext_host_name(TLScontext->con, sni)) { + /* TLSRPT: Local resource or configuration error, don't report. */ msg_warn("%s: error setting SNI hostname to: %s", props->namaddr, sni); return (0); @@ -975,6 +983,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) */ protomask = tls_proto_mask_lims(props->protocols, &min_proto, &max_proto); if (protomask == TLS_PROTOCOL_INVALID) { + /* TLSRPT: Local configuration error, don't report. */ /* tls_protocol_mask() logs no warning. */ msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session", props->namaddr, props->protocols); @@ -1006,6 +1015,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) TLScontext->level = props->tls_level; if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) { + /* TLSRPT: Local resource error, don't report. */ msg_warn("Could not allocate 'TLScontext->con' with SSL_new()"); tls_print_errors(); tls_free_context(TLScontext); @@ -1022,6 +1032,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) cipher_list = tls_set_ciphers(TLScontext, props->cipher_grade, props->cipher_exclusions); if (cipher_list == 0) { + /* TLSRPT: Local configuration error, don't report. */ /* already warned */ tls_free_context(TLScontext); return (0); @@ -1036,6 +1047,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) TLScontext->dane = props->dane; if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { + /* TLSRPT: Local resource error, don't report. */ msg_warn("Could not set application data for 'TLScontext->con'"); tls_print_errors(); tls_free_context(TLScontext); @@ -1069,6 +1081,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) * early. */ if (!tls_auth_enable(TLScontext, props)) { + /* Already warned and reported TLSRPT result. */ tls_free_context(TLScontext); return (0); } @@ -1105,6 +1118,13 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) switch (TLScontext->level) { case TLS_LEV_HALF_DANE: case TLS_LEV_DANE: +#ifdef USE_TLSRPT + if (props->tlsrpt) { + trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID, + /* additional_detail= */ (char *) 0, + "all-TLSA-records-unusable"); + } +#endif msg_warn("%s: all TLSA records unusable, fallback to " "unauthenticated TLS", TLScontext->namaddr); must_fail = 0; @@ -1112,13 +1132,34 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) break; case TLS_LEV_FPRINT: +#ifdef USE_TLSRPT + if (props->tlsrpt) { + trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + "all-fingerprints-unusable"); + } +#endif msg_warn("%s: all fingerprints unusable", TLScontext->namaddr); break; case TLS_LEV_DANE_ONLY: +#ifdef USE_TLSRPT + if (props->tlsrpt) { + trw_report_failure(props->tlsrpt, TLSRPT_TLSA_INVALID, + /* additional_detail= */ (char *) 0, + "all-TLSA-records-unusable"); + } +#endif msg_warn("%s: all TLSA records unusable", TLScontext->namaddr); break; case TLS_LEV_SECURE: case TLS_LEV_VERIFY: +#ifdef USE_TLSRPT + if (props->tlsrpt) { + trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + "all-trust-anchors-unusable"); + } +#endif msg_warn("%s: all trust anchors unusable", TLScontext->namaddr); break; } @@ -1194,6 +1235,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) */ if (SSL_set_fd(TLScontext->con, props->stream == 0 ? props->fd : vstream_fileno(props->stream)) != 1) { + /* TLSRPT: Local resource error, don't report. */ msg_info("SSL_set_fd error to %s", props->namaddr); tls_print_errors(); uncache_session(app_ctx->ssl_ctx, TLScontext); @@ -1213,6 +1255,16 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) if (log_mask & TLS_LOG_TLSPKTS) tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); + /* + * An external (STS) policy signaled a failure. Prevent false (PKI) + * certificate matches in tls_verify.c. TODO(wietse) how was this handled + * historically? + */ + if (props->fail_type) { + TLScontext->fail_type = mystrdup(props->fail_type); + TLScontext->must_fail = 1; + } + /* * If we don't trigger the handshake in the library, leave control over * SSL_connect/read/write/etc with the application. @@ -1245,6 +1297,12 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) msg_info("SSL_connect error to %s: lost connection", props->namaddr); } +#ifdef USE_TLSRPT + if (props->tlsrpt) + trw_report_failure(props->tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + "tls-handshake-failure"); +#endif uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); @@ -1360,6 +1418,16 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, tls_int_seed(); + /* + * Inform the caller that we already reported a TLSRPT failure, so that + * the caller won't report success after a TLS level was degraded, or + * report some less informative failure reason after a more specific + * reason was already reported. + */ +#ifdef USE_TLSRPT + TLScontext->rpt_reported = trw_is_reported(props->tlsrpt); +#endif + return (TLScontext); } diff --git a/postfix/src/tls/tls_dane.c b/postfix/src/tls/tls_dane.c index ac7f05f94..f8c43870a 100644 --- a/postfix/src/tls/tls_dane.c +++ b/postfix/src/tls/tls_dane.c @@ -593,6 +593,7 @@ static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx) if (n == 0) dane->flags |= TLS_DANE_FLAG_EMPTY; } else + /* TODO(wietse) report non-parsable TLSA records in TLSRPT. */ dane->flags |= TLS_DANE_FLAG_NORRS; if (rrs) diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index f77d778ac..3d31cbbe4 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -1346,6 +1346,8 @@ TLS_SESS_STATE *tls_alloc_sess_context(int log_mask, const char *namaddr) TLScontext->errordepth = -1; TLScontext->errorcode = X509_V_OK; TLScontext->errorcert = 0; + TLScontext->rpt_reported = 0; + TLScontext->fail_type = 0; return (TLScontext); } @@ -1396,6 +1398,8 @@ void tls_free_context(TLS_SESS_STATE *TLScontext) myfree((void *) TLScontext->srvr_sig_dgst); if (TLScontext->errorcert) X509_free(TLScontext->errorcert); + if (TLScontext->fail_type) + myfree(TLScontext->fail_type); myfree((void *) TLScontext); } diff --git a/postfix/src/tls/tls_proxy.h b/postfix/src/tls/tls_proxy.h index 65286398f..8e0c67088 100644 --- a/postfix/src/tls/tls_proxy.h +++ b/postfix/src/tls/tls_proxy.h @@ -108,11 +108,12 @@ extern VSTREAM *tls_proxy_open(const char *, int, VSTREAM *, const char *, ((props)->a12), ((props)->a13), ((props)->a14)) #define TLS_PROXY_CLIENT_START_PROPS(props, a1, a2, a3, a4, a5, a6, a7, a8, \ - a9, a10, a11, a12, a13, a14, a15) \ + a9, a10, a11, a12, a13, a14, a15, a16, a17) \ (((props)->a1), ((props)->a2), ((props)->a3), \ ((props)->a4), ((props)->a5), ((props)->a6), ((props)->a7), \ ((props)->a8), ((props)->a9), ((props)->a10), ((props)->a11), \ - ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15)) + ((props)->a12), ((props)->a13), ((props)->a14), ((props)->a15), \ + ((props)->a16), ((props)->a17)) extern TLS_SESS_STATE *tls_proxy_context_receive(VSTREAM *); extern void tls_proxy_context_free(TLS_SESS_STATE *); @@ -181,6 +182,7 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); #define TLS_ATTR_SRVR_SIG_BITS "srvr_signature_bits" #define TLS_ATTR_SRVR_SIG_DGST "srvr_signature_digest" #define TLS_ATTR_NAMADDR "namaddr" +#define TLS_ATTR_RPT_REPORTED "rpt_reported" /* * TLS_SERVER_INIT_PROPS attributes. @@ -254,7 +256,9 @@ extern void tls_proxy_server_start_free(TLS_SERVER_START_PROPS *); #define TLS_ATTR_CIPHER_EXCLUSIONS "cipher_exclusions" #define TLS_ATTR_MATCHARGV "matchargv" #define TLS_ATTR_MDALG "mdalg" -#define TLS_ATTR_DANE "dane" +#define TLS_ATTR_DANE "dane" +#define TLS_ATTR_TLSRPT "tlsrpt" +#define FAIL_TYPE "fail_type" /* * TLS_TLSA attributes. diff --git a/postfix/src/tls/tls_proxy_client_print.c b/postfix/src/tls/tls_proxy_client_print.c index 81e50b924..b2502ff43 100644 --- a/postfix/src/tls/tls_proxy_client_print.c +++ b/postfix/src/tls/tls_proxy_client_print.c @@ -79,6 +79,10 @@ #include #include +#ifdef USE_TLSRPT +#define TLSRPT_WRAPPER_INTERNAL +#include +#endif #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) @@ -96,7 +100,7 @@ int tls_proxy_client_param_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp, ret = print_fn(fp, flags | ATTR_FLAG_MORE, SEND_ATTR_STR(TLS_ATTR_CNF_FILE, params->tls_cnf_file), - SEND_ATTR_STR(TLS_ATTR_CNF_NAME, params->tls_cnf_name), + SEND_ATTR_STR(TLS_ATTR_CNF_NAME, params->tls_cnf_name), SEND_ATTR_STR(VAR_TLS_HIGH_CLIST, params->tls_high_clist), SEND_ATTR_STR(VAR_TLS_MEDIUM_CLIST, params->tls_medium_clist), @@ -242,6 +246,59 @@ static int tls_proxy_client_dane_print(ATTR_PRINT_COMMON_FN print_fn, return (ret); } +#ifdef USE_TLSRPT + +/* tls_proxy_client_tlsrpt_print - send TLSRPT_WRAPPER over stream */ + +static int tls_proxy_client_tlsrpt_print(ATTR_PRINT_COMMON_FN print_fn, + VSTREAM *fp, int flags, const void *ptr) +{ + const TLSRPT_WRAPPER *trw = (const TLSRPT_WRAPPER *) ptr; + int have_trw = trw != 0; + int ret; + + ret = print_fn(fp, flags | ATTR_FLAG_MORE, + SEND_ATTR_INT(TLS_ATTR_TLSRPT, have_trw), + ATTR_TYPE_END); + if (msg_verbose) + msg_info("tls_proxy_client_tlsrpt_print have_trw=%d", have_trw); + + if (ret == 0 && have_trw) { + ret = print_fn(fp, flags | ATTR_FLAG_MORE, + SEND_ATTR_STR(TRW_RPT_SOCKET_NAME, + STRING_OR_EMPTY(trw->rpt_socket_name)), + SEND_ATTR_STR(TRW_RPT_POLICY_DOMAIN, + STRING_OR_EMPTY(trw->rpt_policy_domain)), + SEND_ATTR_STR(TRW_RPT_POLICY_STRING, + STRING_OR_EMPTY(trw->rpt_policy_string)), + SEND_ATTR_INT(TRW_TLS_POLICY_TYPE, + (int) trw->tls_policy_type), + SEND_ATTR_FUNC(argv_attr_print, + (const void *) trw->tls_policy_strings), + SEND_ATTR_STR(TRW_TLS_POLICY_DOMAIN, + STRING_OR_EMPTY(trw->tls_policy_domain)), + SEND_ATTR_FUNC(argv_attr_print, + (const void *) trw->mx_host_patterns), + SEND_ATTR_STR(TRW_SRC_MTA_ADDR, + STRING_OR_EMPTY(trw->snd_mta_addr)), + SEND_ATTR_STR(TRW_DST_MTA_NAME, + STRING_OR_EMPTY(trw->rcv_mta_name)), + SEND_ATTR_STR(TRW_DST_MTA_ADDR, + STRING_OR_EMPTY(trw->rcv_mta_addr)), + SEND_ATTR_STR(TRW_DST_MTA_EHLO, + STRING_OR_EMPTY(trw->rcv_mta_ehlo)), + SEND_ATTR_INT(TRW_FLAGS, + trw->flags), + ATTR_TYPE_END); + } + /* Do not flush the stream. */ + if (msg_verbose) + msg_info("tls_proxy_client_tlsrpt_print ret=%d", ret); + return (ret); +} + +#endif + /* tls_proxy_client_start_print - send TLS_CLIENT_START_PROPS over stream */ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn, @@ -283,6 +340,12 @@ int tls_proxy_client_start_print(ATTR_PRINT_COMMON_FN print_fn, STRING_OR_EMPTY(props->mdalg)), SEND_ATTR_FUNC(tls_proxy_client_dane_print, (const void *) props->dane), +#ifdef USE_TLSRPT + SEND_ATTR_FUNC(tls_proxy_client_tlsrpt_print, + (const void *) props->tlsrpt), +#endif + SEND_ATTR_STR(FAIL_TYPE, + STRING_OR_EMPTY(props->fail_type)), ATTR_TYPE_END); /* Do not flush the stream. */ if (msg_verbose) diff --git a/postfix/src/tls/tls_proxy_client_scan.c b/postfix/src/tls/tls_proxy_client_scan.c index d36cf4d7e..210704021 100644 --- a/postfix/src/tls/tls_proxy_client_scan.c +++ b/postfix/src/tls/tls_proxy_client_scan.c @@ -113,6 +113,10 @@ #define TLS_INTERNAL #include #include +#ifdef USE_TLSRPT +#define TLSRPT_WRAPPER_INTERNAL +#include +#endif #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) @@ -329,6 +333,12 @@ void tls_proxy_client_start_free(TLS_CLIENT_START_PROPS *props) myfree((void *) props->mdalg); if (props->dane) tls_dane_free((TLS_DANE *) props->dane); +#ifdef USE_TLSRPT + if (props->tlsrpt) + trw_free(props->tlsrpt); +#endif + if (props->fail_type) + myfree(props->fail_type); myfree((void *) props); } @@ -419,6 +429,87 @@ static int tls_proxy_client_dane_scan(ATTR_SCAN_COMMON_FN scan_fn, return (ret); } +#define EXPORT_OR_NULL(str, vstr) do { \ + if (LEN(vstr) > 0) { \ + (str) = vstring_export(vstr); \ + } else { \ + (str) = 0; \ + vstring_free(vstr); \ + } \ + } while (0) + +#ifdef USE_TLSRPT + +/* tls_proxy_client_tlsrpt_scan - receive TLSRPT_WRAPPER from stream */ + +static int tls_proxy_client_tlsrpt_scan(ATTR_SCAN_COMMON_FN scan_fn, + VSTREAM *fp, int flags, void *ptr) +{ + TLSRPT_WRAPPER *trw = 0; + int ret; + int have_tlsrpt = 0; + + ret = scan_fn(fp, flags | ATTR_FLAG_MORE, + RECV_ATTR_INT(TLS_ATTR_DANE, &have_tlsrpt), + ATTR_TYPE_END); + if (msg_verbose) + msg_info("tls_proxy_client_tlsrpt_scan have_tlsrpt=%d", have_tlsrpt); + + if (ret == 1 && have_tlsrpt) { + VSTRING *rpt_socket_name = vstring_alloc(100); + VSTRING *rpt_policy_domain = vstring_alloc(100); + VSTRING *rpt_policy_string = vstring_alloc(100); + int tls_policy_type; + ARGV *tls_policy_strings = argv_alloc(100); + VSTRING *tls_policy_domain = vstring_alloc(100); + ARGV *mx_host_patterns = argv_alloc(100); + VSTRING *snd_mta_addr = vstring_alloc(100); + VSTRING *rcv_mta_name = vstring_alloc(100); + VSTRING *rcv_mta_addr = vstring_alloc(100); + VSTRING *rcv_mta_ehlo = vstring_alloc(100); + + ret = scan_fn(fp, flags | ATTR_FLAG_MORE, + RECV_ATTR_STR(TRW_RPT_SOCKET_NAME, rpt_socket_name), + RECV_ATTR_STR(TRW_RPT_POLICY_DOMAIN, rpt_policy_domain), + RECV_ATTR_STR(TRW_RPT_POLICY_STRING, rpt_policy_string), + RECV_ATTR_INT(TRW_TLS_POLICY_TYPE, &tls_policy_type), + RECV_ATTR_FUNC(argv_attr_scan, tls_policy_strings), + RECV_ATTR_STR(TRW_TLS_POLICY_DOMAIN, tls_policy_domain), + RECV_ATTR_FUNC(argv_attr_scan, mx_host_patterns), + RECV_ATTR_STR(TRW_SRC_MTA_ADDR, snd_mta_addr), + RECV_ATTR_STR(TRW_DST_MTA_NAME, rcv_mta_name), + RECV_ATTR_STR(TRW_DST_MTA_ADDR, rcv_mta_addr), + RECV_ATTR_STR(TRW_DST_MTA_EHLO, rcv_mta_ehlo), + RECV_ATTR_INT(TRW_FLAGS, &trw->flags), + ATTR_TYPE_END); + + /* Always construct a well-formed structure. */ + trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw)); + trw->rpt_socket_name = vstring_export(rpt_socket_name); + trw->rpt_policy_domain = vstring_export(rpt_policy_domain); + trw->rpt_policy_string = vstring_export(rpt_policy_string); + trw->tls_policy_type = tls_policy_type; + trw->tls_policy_strings = tls_policy_strings; + EXPORT_OR_NULL(trw->tls_policy_domain, tls_policy_domain); + trw->mx_host_patterns = mx_host_patterns; + EXPORT_OR_NULL(trw->snd_mta_addr, snd_mta_addr); + EXPORT_OR_NULL(trw->rcv_mta_name, rcv_mta_name); + EXPORT_OR_NULL(trw->rcv_mta_addr, rcv_mta_addr); + EXPORT_OR_NULL(trw->rcv_mta_ehlo, rcv_mta_ehlo); + ret = (ret == 12 ? 1 : -1); + if (ret != 1) { + trw_free(trw); + trw = 0; + } + } + *(TLSRPT_WRAPPER **) ptr = trw; + if (msg_verbose) + msg_info("tls_proxy_client_tlsrpt_scan ret=%d", ret); + return (ret); +} + +#endif + /* tls_proxy_client_start_scan - receive TLS_CLIENT_START_PROPS from stream */ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, @@ -437,6 +528,13 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, VSTRING *cipher_grade = vstring_alloc(25); VSTRING *cipher_exclusions = vstring_alloc(25); VSTRING *mdalg = vstring_alloc(25); + VSTRING *fail_type = vstring_alloc(25); + +#ifdef USE_TLSRPT +#define EXPECT_START_SCAN_RETURN 17 +#else +#define EXPECT_START_SCAN_RETURN 16 +#endif if (msg_verbose) msg_info("begin tls_proxy_client_start_scan"); @@ -467,6 +565,11 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, RECV_ATTR_STR(TLS_ATTR_MDALG, mdalg), RECV_ATTR_FUNC(tls_proxy_client_dane_scan, &props->dane), +#ifdef USE_TLSRPT + RECV_ATTR_FUNC(tls_proxy_client_tlsrpt_scan, + &props->tlsrpt), +#endif + RECV_ATTR_STR(FAIL_TYPE, fail_type), ATTR_TYPE_END); /* Always construct a well-formed structure. */ props->nexthop = vstring_export(nexthop); @@ -479,7 +582,8 @@ int tls_proxy_client_start_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, props->cipher_grade = vstring_export(cipher_grade); props->cipher_exclusions = vstring_export(cipher_exclusions); props->mdalg = vstring_export(mdalg); - ret = (ret == 15 ? 1 : -1); + EXPORT_OR_NULL(props->fail_type, fail_type); + ret = (ret == EXPECT_START_SCAN_RETURN ? 1 : -1); if (ret != 1) { tls_proxy_client_start_free(props); props = 0; diff --git a/postfix/src/tls/tls_proxy_context_print.c b/postfix/src/tls/tls_proxy_context_print.c index 930410a46..808abbe73 100644 --- a/postfix/src/tls/tls_proxy_context_print.c +++ b/postfix/src/tls/tls_proxy_context_print.c @@ -110,6 +110,8 @@ int tls_proxy_context_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp, STRING_OR_EMPTY(tp->srvr_sig_dgst)), SEND_ATTR_STR(TLS_ATTR_NAMADDR, STRING_OR_EMPTY(tp->namaddr)), + SEND_ATTR_INT(TLS_ATTR_RPT_REPORTED, + tp->rpt_reported), ATTR_TYPE_END); /* Do not flush the stream. */ return (ret); diff --git a/postfix/src/tls/tls_proxy_context_scan.c b/postfix/src/tls/tls_proxy_context_scan.c index 48aaff627..fc23c528a 100644 --- a/postfix/src/tls/tls_proxy_context_scan.c +++ b/postfix/src/tls/tls_proxy_context_scan.c @@ -124,6 +124,8 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, RECV_ATTR_INT(TLS_ATTR_SRVR_SIG_BITS, &tls_context->srvr_sig_bits), RECV_ATTR_STR(TLS_ATTR_SRVR_SIG_DGST, srvr_sig_dgst), RECV_ATTR_STR(TLS_ATTR_NAMADDR, namaddr), + RECV_ATTR_INT(TLS_ATTR_RPT_REPORTED, + &tls_context->rpt_reported), ATTR_TYPE_END); /* Always construct a well-formed structure. */ tls_context->peer_CN = vstring_export(peer_CN); @@ -141,7 +143,7 @@ int tls_proxy_context_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp, tls_context->srvr_sig_curve = vstring_export(srvr_sig_curve); tls_context->srvr_sig_dgst = vstring_export(srvr_sig_dgst); tls_context->namaddr = vstring_export(namaddr); - ret = (ret == 24 ? 1 : -1); + ret = (ret == 25 ? 1 : -1); if (ret != 1) { tls_proxy_context_free(tls_context); tls_context = 0; diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 88b332642..87433e935 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -1035,7 +1035,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) if (!TLS_CERT_IS_TRUSTED(TLScontext) && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) { if (TLScontext->session_reused == 0) - tls_log_verify_error(TLScontext); + tls_log_verify_error(TLScontext, (struct TLSRPT_WRAPPER *) 0); else msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", diff --git a/postfix/src/tls/tls_verify.c b/postfix/src/tls/tls_verify.c index c643f1838..a719c2ca9 100644 --- a/postfix/src/tls/tls_verify.c +++ b/postfix/src/tls/tls_verify.c @@ -11,8 +11,9 @@ /* int ok; /* X509_STORE_CTX *ctx; /* -/* int tls_log_verify_error(TLScontext) +/* int tls_log_verify_error(TLScontext, tlsrpt) /* TLS_SESS_STATE *TLScontext; +/* struct TLSRPT_WRAPPER *tlsrpt; /* /* char *tls_peer_CN(peercert, TLScontext) /* X509 *peercert; @@ -107,6 +108,10 @@ /* TLS library. */ +#ifdef USE_TLSRPT +#include +#endif + #define TLS_INTERNAL #include @@ -194,18 +199,50 @@ int tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx) /* tls_log_verify_error - Report final verification error status */ -void tls_log_verify_error(TLS_SESS_STATE *TLScontext) +void tls_log_verify_error(TLS_SESS_STATE *TLScontext, + struct TLSRPT_WRAPPER *tlsrpt) { char buf[CCERT_BUFSIZ]; int err = TLScontext->errorcode; X509 *cert = TLScontext->errorcert; int depth = TLScontext->errordepth; +#ifdef USE_TLSRPT + VSTRING *err_vstr = vstring_alloc(100); + +#define CERT_ERROR_TO_STRING(err) \ + translit(vstring_str(vstring_strcpy(err_vstr, \ + X509_verify_cert_error_string(err))), \ + " ", "_") +#endif + #define PURPOSE ((depth>0) ? "CA": TLScontext->am_server ? "client": "server") if (err == X509_V_OK) return; + /* + * If an external policy flagged an error, report that instead. + */ + if (TLScontext->fail_type) { + msg_info("certificate verification failed for %s: " + "external policy failure (%s)", + TLScontext->namaddr, TLScontext->fail_type); +#ifdef USE_TLSRPT + if (tlsrpt) { + tlsrpt_failure_t failure_type; + + if ((failure_type = convert_tlsrpt_policy_failure(TLScontext->fail_type)) < 0) + msg_panic("tls_log_verify_error: unexpected failure_reason: %s", + TLScontext->fail_type); + trw_report_failure(tlsrpt, failure_type, + /* additional_detail= */ (char *) 0, + /* failure_reason= */ (char *) 0); + } +#endif + return; + } + /* * Specific causes for verification failure. */ @@ -218,10 +255,22 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext) */ msg_info("certificate verification failed for %s: " "not trusted by local or TLSA policy", TLScontext->namaddr); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_NOT_TRUSTED, + /* additional_detail= */ (char *) 0, + /* failure_code= */ (char *) 0); +#endif break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: msg_info("certificate verification failed for %s: " "self-signed certificate", TLScontext->namaddr); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + CERT_ERROR_TO_STRING(err)); +#endif break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: @@ -237,25 +286,55 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext) strcpy(buf, ""); msg_info("certificate verification failed for %s: untrusted issuer %s", TLScontext->namaddr, printable(buf, '?')); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + CERT_ERROR_TO_STRING(err)); +#endif break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: msg_info("%s certificate verification failed for %s: certificate not" " yet valid", PURPOSE, TLScontext->namaddr); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + CERT_ERROR_TO_STRING(err)); +#endif break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: msg_info("%s certificate verification failed for %s: certificate has" " expired", PURPOSE, TLScontext->namaddr); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_CERTIFICATE_EXPIRED, + /* additional_detail= */ (char *) 0, + /* failure_code= */ (char *) 8); +#endif break; case X509_V_ERR_INVALID_PURPOSE: msg_info("certificate verification failed for %s: not designated for " "use as a %s certificate", TLScontext->namaddr, PURPOSE); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + CERT_ERROR_TO_STRING(err)); +#endif break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: msg_info("certificate verification failed for %s: " "certificate chain longer than limit(%d)", TLScontext->namaddr, depth - 1); +#ifdef USE_TLSRPT + if (tlsrpt) + trw_report_failure(tlsrpt, TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + CERT_ERROR_TO_STRING(err)); +#endif break; default: msg_info("%s certificate verification failed for %s: num=%d:%s", @@ -263,6 +342,9 @@ void tls_log_verify_error(TLS_SESS_STATE *TLScontext) X509_verify_cert_error_string(err)); break; } +#ifdef USE_TLSRPT + vstring_free(err_vstr); +#endif } #ifndef DONT_GRIPE diff --git a/postfix/src/tls/tlsrpt_wrapper.c b/postfix/src/tls/tlsrpt_wrapper.c new file mode 100644 index 000000000..4005d8edc --- /dev/null +++ b/postfix/src/tls/tlsrpt_wrapper.c @@ -0,0 +1,647 @@ +/*++ +/* NAME +/* tlsrpt_wrapper 3 +/* SUMMARY +/* TLSRPT support for the TLS protocol engine +/* SYNOPSIS +/* #include +/* +/* #ifdef USE_TLS +/* #ifdef USE_TLSRPT +/* TLS_RPT *trw_create( +/* const char *rpt_socket_name, +/* const char *rpt_policy_domain, +/* const char *rpt_policy_string) +/* +/* void trw_free( +/* TLSRPT_WRAPPER *trw) +/* +/* void trw_set_tls_policy( +/* TLSRPT_WRAPPER *trw, +/* tlsrpt_policy_type_t tls_policy_type, +/* const char *const *tls_policy_strings, +/* const char *tls_policy_domain +/* const char *const *mx_host_patterns) +/* +/* void trw_set_tcp_connection( +/* TLSRPT_WRAPPER *trw, +/* const char *snd_mta_addr, +/* const char *rcv_mta_name, +/* const char *rcv_mta_addr) +/* +/* void trw_set_ehlo_resp( +/* TLSRPT_WRAPPER *trw, +/* const char *rcv_mta_ehlo) +/* +/* void trw_report_failure( +/* TLSRPT_WRAPPER *trw, +/* tlsrpt_failure_t failure_type, +/* const char *additional_detail, +/* const char *failure_reason) +/* +/* void trw_report_success( +/* TLSRPT_WRAPPER *trw) +/* +/* int trw_is_reported( +/* TLSRPT_WRAPPER *trw) +/* +/* tlsrpt_policy_type_t convert_tlsrpt_policy_type( +/* const char *policy_type) +/* +/* tlsrpt_failure_t convert_tlsrpt_policy_failure( +/* const char *policy_failure) +/* #endif /* USE_TLS_RPT */ +/* +/* int valid_tlsrpt_policy_type( +/* const char *type_name) +/* +/* int valid_tlsrpt_policy_failure( +/* const char *failure_name) +/* #endif /* USE_TLS */ +/* ARCHITECTURE, BOTTOM-UP VIEW +/* Postfix TLSRPT support uses the TLSRPT client library from +/* sys4.de. That library makes the reasonable assumption that +/* all calls concerning one SMTP session will be made from within +/* one process. +/* +/* With Postfix, the TLS protocol engine may be located in a +/* different process than the SMTP protocol engine, and both +/* processes need the ability to report a TLS error. +/* +/* To bridge this gap, the SMTP protocol engine forwards SMTP +/* session and TLS policy information to the TLS protocol engine in +/* the form of a TLSRPT_WRAPPER object that contains SMTP and other +/* information that the TLSRPT client library needs. The TLS engine +/* can pass that information to the TLSRPT client library to report +/* a TLS error. The SMTP protocol engine can use that same +/* information to report a TLS error or success. +/* IMPLEMENTATION +/* .ad +/* .fi +/* The Postfix SMTP protocol engine (smtp_proto.c) reports TLS +/* errors when TLS support is required but unavailable, or requests +/* the Postfix TLS protocol engine to perform a TLS protocol +/* handshake over an open SMTP connection. The SMTP protocol engine +/* either calls the TLS protocol engine directly, or calls it over +/* RPC in a tlsproxy(8) process. +/* +/* The TLS protocol engine may report a TLS error through the +/* tlsrpt_wrapper library, and either returns no TLS session object, +/* or a TLS session object for a completed handshake. The TLS +/* session object will indicate if the TLS protocol engine reported +/* any TLS error through TLSRPT. +/* +/* The Postfix SMTP protocol engine reports success or failure +/* through the tlsrpt_wrapper library, depending on whether all +/* matching requirements were satisfied. SMTP protocol engine +/* does not report success or failure through the tlsrpt_wrapper +/* library if the TLS protocol engine already reported a failure. +/* WRAPPER API +/* .ad +/* .fi +/* The functions below must be called in a specific order. All +/* string inputs are copied. If a required call is missing then +/* the request will be ignored, and a warning will be logged. +/* .PP +/* trw_create() must be called before other trw_xxx() requests can +/* be made. Arguments: +/* .IP rpt_socket_name +/* The name of a socket that will be managed by local TLSRPT +/* infrastructure. +/* .IP rpt_policy_domain +/* The TLSRPT policy domain name, i.e. the domain that wishes to +/* receive TLSRPT summary reports. An internationalized domain name +/* must be in A-label form (i.e. punycode). +/* .IP rpt_policy_string +/* The TLSRPT policy record content, i.e. how to submit TLSRPT +/* summary reports. +/* .PP +/* trw_free() destroys storage allocated with other trw_xxx() +/* requests. +/* .PP +/* trw_set_tls_policy() must be called by the SMTP protocol +/* engine after it found a DANE, STS, or no policy, and before it +/* tries to establish a new SMTP connection. This function clears +/* information that was specified earlier with trw_set_tls_policy() +/* or trw_set_tcp_connection(), and whether trw_report_failure() +/* or trw_report_success() were called. Mapping from arguments to +/* TLSRPT report fields: +/* .IP tls_policy_type +/* policies[].policy.policy-type. +/* .IP tls_policy_strings +/* policies[].policy.policy-string[]. Ignored if the tls_policy_type +/* value is TLSRPT_NO_POLICY_FOUND. +/* .IP tls_policy_domain +/* policies[].policy.policy-domain. +/* .IP mx_host_patterns (may be null) +/* policies[].policy.mx-host[]. Ignored if the tls_policy_type +/* value is TLSRPT_NO_POLICY_FOUND. +/* .PP +/* trw_set_tcp_connection() and trw_set_ehlo_resp() are optionally +/* called by the SMTP protcol engine, after it has established +/* a new SMTP connection, before it requests a TLS protocol +/* handshake. Mapping from arguments to TLSRPT report fields: +/* .IP snd_mta_addr (may be null) +/* policies[].failure-details[].sending-mta-ip. +/* .IP rcv_mta_name (may be null) +/* policies[].failure-details[].receiving-mx-hostname. +/* .IP rcv_mta_addr (may be null) +/* policies[].failure-details[].receiving-ip. +/* .PP +/* trw_set_ehlo_resp() is optionally called by the SMTP protcol +/* engine to pass on the EHLO response. Presumably this is the EHLO +/* response before STARTTLS (TLSRPT is primarily interested in +/* pre-handshake and handshake errors). +/* .IP rcv_mta_ehlo (may be null) +/* policies[].failure-details[].receiving-mx-helo. +/* .PP +/* trw_report_failure() is called by the TLS protocol engine or +/* SMTP protocol engine to report a TLS error. The result value +/* is 0 for success, -1 for failure as indicated with the errno +/* value. The call is successfully skipped if information is missing +/* or if failure or success were already reported for the +/* connection. Mapping from arguments to TLSRPT report fields: +/* .IP failure_type +/* policies[].failure-details[].result-type. +/* .IP additional_detail (may be null) +/* policies[].failure-details[].additional-information. +/* .IP failure_reason (may be null) +/* policies[].failure-details[].failure-reason-code +/* .PP +/* trw_report_success() is called by the SMTP protocol engine +/* to report a successful TLS handshake. The result value is +/* 0 for success, -1 for failure with errno indicating the +/* error type. The call is successfully skipped if information if +/* missing or if failure or success were already reported for +/* the connection. +/* .PP +/* trw_is_reported() returns non-zero when the contents of the +/* specified TLSRPT_WRAPPER have been reported. +/* .PP +/* convert_tlsrpt_policy_type() and convert_tlsrpt_policy_failure() +/* convert a valid policy type or failure name to the corresponding +/* enum value. The result is < 0 if the name is not valid. +/* .PP +/* valid_tlsrpt_policy_type() and valid_tlsrpt_policy_failure() +/* return non-zero if the specified policy type or failure name +/* is valid in TLSRPT. These functions do not require that the +/* module is built with TLSRPT support. This allows the names to +/* be used even if TLSRPT is disabled. +/* DIAGNOSTICS +/* Some functions will log a a warning when information is missing, +/* but such warnings will not affect the SMTP or TLS protocol engine. +/* BUGS +/* This implementation is suitable to report successful TLS policy +/* compliance, and to report a failure that prevents TLS policy +/* compliance (example: all TLSA records are unusable). Do not use +/* this implementation to report other errors (example: some TLSA +/* record is non-parsable). +/* SEE ALSO +/* https://github.com/sys4/tlsrpt, TLSRPT client library +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* porcupine.org +/*--*/ + +#if defined(USE_TLS) + + /* + * System library. + */ +#include +#include +#include +#if defined(USE_TLSRPT) +#include +#endif + + /* + * Utility library. + */ +#include +#include +#include +#include +#include + + /* + * Some functions are not #ifdef USE_TLSRPT. + */ +#define TLSRPT_WRAPPER_INTERNAL +#include +#if defined(USE_TLSRPT) + + /* + * Macros to make repetitive code more readable. + */ +#define MYFREE_IF_SET(member) do { \ + if (member) \ + myfree(member); \ + } while (0) + +#define MYFREE_IF_SET_AND_CLEAR(member, value) do { \ + if (member) { \ + myfree(member); \ + (member) = 0; \ + } \ + } while (0) + +#define MYFREE_IF_SET_AND_COPY(member, value) do { \ + MYFREE_IF_SET(member); \ + (member) = (value) ? mystrdup(value) : 0; \ + } while (0) + +#define ARGV_FREE_IF_SET(member) do { \ + if (member) \ + argv_free(member); \ + } while (0) + +#define ARGV_FREE_IF_SET_AND_CLEAR(member) do { \ + if (member) { \ + argv_free(member); \ + (member) = 0; \ + } \ + } while (0) + +#define ARGV_FREE_IF_SET_AND_COPY(member, value) do { \ + ARGV_FREE_IF_SET(member); \ + (member) = (value) ? argv_addv((ARGV *) 0, value) : 0; \ + } while (0) + +/* trw_create - create initial TLSRPT_WRAPPER instance */ + +TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name, + const char *rpt_policy_domain, + const char *rpt_policy_string) +{ + TLSRPT_WRAPPER *trw; + + /* + * memset() is not portable for pointer etc. types. + */ + trw = (TLSRPT_WRAPPER *) mymalloc(sizeof(*trw)); + trw->rpt_socket_name = mystrdup(rpt_socket_name); + trw->rpt_policy_domain = mystrdup(rpt_policy_domain); + trw->rpt_policy_string = mystrdup(rpt_policy_string);; + trw->tls_policy_type = 0; + trw->tls_policy_strings = 0; + trw->tls_policy_domain = 0; + trw->mx_host_patterns = 0; + trw->snd_mta_addr = 0; + trw->rcv_mta_name = 0; + trw->rcv_mta_addr = 0; + trw->rcv_mta_ehlo = 0; + trw->flags = 0; + return (trw); +} + +/* trw_free - destroy TLSRPT_WRAPPER instance. */ + +void trw_free(TLSRPT_WRAPPER *trw) +{ + /* Destroy fields set with trw_create(). */ + myfree(trw->rpt_socket_name); + myfree(trw->rpt_policy_domain); + myfree(trw->rpt_policy_string); + /* Destroy fields set with trw_set_tls_policy(). */ + ARGV_FREE_IF_SET(trw->tls_policy_strings); + MYFREE_IF_SET(trw->tls_policy_domain); + ARGV_FREE_IF_SET(trw->mx_host_patterns); + /* Destroy fields set with trw_set_tcp_connection(). */ + trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0); + /* Destroy fields set with trw_set_ehlo_resp(). */ + trw_set_ehlo_resp(trw, (char *) 0); + /* That's all. */ + myfree((void *) trw); +} + +/* trw_set_tls_policy - set TLS policy info, clear SMTP info */ + +void trw_set_tls_policy(TLSRPT_WRAPPER *trw, + tlsrpt_policy_type_t tls_policy_type, + const char *const * tls_policy_strings, + const char *tls_policy_domain, + const char *const * mx_host_patterns) +{ + trw->tls_policy_type = tls_policy_type; + MYFREE_IF_SET_AND_COPY(trw->tls_policy_domain, tls_policy_domain); + if (tls_policy_type == TLSRPT_NO_POLICY_FOUND) { + ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings); + ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings); + } else { + ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, tls_policy_strings); + ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, mx_host_patterns); + } + trw->flags = TRW_FLAG_HAVE_TLS_POLICY; + trw_set_tcp_connection(trw, (char *) 0, (char *) 0, (char *) 0); + trw_set_ehlo_resp(trw, (char *) 0); +} + +/* trw_set_tcp_connection - set SMTP endpoint info */ + +void trw_set_tcp_connection(TLSRPT_WRAPPER *trw, + const char *snd_mta_addr, + const char *rcv_mta_name, + const char *rcv_mta_addr) +{ + const char myname[] = "trw_set_tcp_connection"; + + /* + * Sanity check: usage errors are not a show stopper. + */ + if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0 + || (trw->flags & TRW_FLAG_REPORTED)) { + msg_warn("%s: missing trw_set_tls_policy call", myname); + return; + } + MYFREE_IF_SET_AND_COPY(trw->snd_mta_addr, snd_mta_addr); + MYFREE_IF_SET_AND_COPY(trw->rcv_mta_name, rcv_mta_name); + MYFREE_IF_SET_AND_COPY(trw->rcv_mta_addr, rcv_mta_addr); +} + +/* trw_set_ehlo_resp - set EHLO response */ + +void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, const char *rcv_mta_ehlo) +{ + const char myname[] = "trw_set_ehlo_resp"; + + /* + * Sanity check: usage errors are not a show stopper. + */ + if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0 + || (trw->flags & TRW_FLAG_REPORTED)) { + msg_warn("%s: missing trw_set_tls_policy call", myname); + return; + } + MYFREE_IF_SET_AND_COPY(trw->rcv_mta_ehlo, rcv_mta_ehlo); +} + +/* trw_munge_report_result - helper to map and log libtlsrpt result value */ + +static int trw_munge_report_result(int libtlsrpt_errorcode) +{ + int err; + + /* + * First, deal with the non-error cases. + */ + if (libtlsrpt_errorcode == 0) { + return (0); + } + + /* + * Do not report success if errno was zero. + */ + else { + err = tlsrpt_errno_from_error_code(libtlsrpt_errorcode); + msg_warn("Could not report TLS handshake result to tlsrpt library:" + " %s (errno %d)", mystrerror(err), err); + errno = err; + return (-1); + } +} + +/* trw_tlsrpt_failure_to_string - make debug logging readable */ + +static const char *trw_failure_type_to_string(tlsrpt_failure_t failure_type) +{ + static const NAME_CODE failure_types[] = { + "TLSRPT_STARTTLS_NOT_SUPPORTED", TLSRPT_STARTTLS_NOT_SUPPORTED, + "TLSRPT_CERTIFICATE_HOST_MISMATCH", TLSRPT_CERTIFICATE_HOST_MISMATCH, + "TLSRPT_CERTIFICATE_NOT_TRUSTED", TLSRPT_CERTIFICATE_NOT_TRUSTED, + "TLSRPT_CERTIFICATE_EXPIRED", TLSRPT_CERTIFICATE_EXPIRED, + "TLSRPT_VALIDATION_FAILURE", TLSRPT_VALIDATION_FAILURE, + "TLSRPT_STS_POLICY_FETCH_ERROR", TLSRPT_STS_POLICY_FETCH_ERROR, + "TLSRPT_STS_POLICY_INVALID", TLSRPT_STS_POLICY_INVALID, + "TLSRPT_STS_WEBPKI_INVALID", TLSRPT_STS_WEBPKI_INVALID, + "TLSRPT_TLSA_INVALID", TLSRPT_TLSA_INVALID, + "TLSRPT_DNSSEC_INVALID", TLSRPT_DNSSEC_INVALID, + "TLSRPT_DANE_REQUIRED", TLSRPT_DANE_REQUIRED, + "TLSRPT_UNFINISHED_POLICY", TLSRPT_UNFINISHED_POLICY, + 0, -1 + }; + const char *cp; + static VSTRING *buf; + + if ((cp = str_name_code(failure_types, failure_type)) == 0) { + if (buf == 0) + buf = vstring_alloc(20); + msg_warn("unknown tlsrpt_failure_t value %d", failure_type); + vstring_sprintf(buf, "failure_type_%d", failure_type); + cp = vstring_str(buf); + } + return (cp); +} + +/* trw_report_failure - one-shot failure reporter */ + +int trw_report_failure(TLSRPT_WRAPPER *trw, + tlsrpt_failure_t failure_type, + const char *additional_detail, + const char *failure_reason) +{ + const char myname[] = "trw_report_failure"; + struct tlsrpt_connection_t *con; + int res; + + /* + * Sanity check: usage errors are not a show stopper. + */ + if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) { + msg_warn("%s: missing trw_set_tls_policy call", myname); + return (0); + } + + /* + * Report a failure only when it is seen first. If a failure was already + * reported by a lower-level function close to the root cause, then skip + * the less detailed failure report from a later caller who is further + * away from the point where trouble was found. + * + * TODO(wietse) Is it worthwhile to distinguish between failure versus + * success already reported? + */ + if (trw->flags & TRW_FLAG_REPORTED) { + if (msg_verbose) + msg_info("%s: success or failure already reported", myname); + return (0); + } + trw->flags |= TRW_FLAG_REPORTED; + + if (msg_verbose) + msg_info("%s: rpt_policy_domain=%s receiving_mx=%s failure_type=%s" + " failure_reason=%s", + myname, trw->rpt_policy_domain, trw->rcv_mta_name, + trw_failure_type_to_string(failure_type), + failure_reason ? failure_reason : "(null)"); + + if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) { + struct tlsrpt_dr_t *dr; + char **cpp; + + if ((res = tlsrpt_init_delivery_request(&dr, con, + trw->rpt_policy_domain, + trw->rpt_policy_string)) == 0) { + if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type, + trw->tls_policy_domain)) == 0) { + if (trw->tls_policy_strings) + for (cpp = trw->tls_policy_strings->argv; + res == 0 && *cpp; cpp++) + res = tlsrpt_add_policy_string(dr, *cpp); + if (trw->mx_host_patterns) + for (cpp = trw->mx_host_patterns->argv; + res == 0 && *cpp; cpp++) + res = tlsrpt_add_mx_host_pattern(dr, *cpp); + if (res == 0) + res = tlsrpt_add_delivery_request_failure(dr, + /* failure_code= */ failure_type, + /* sending_mta_ip= */ trw->snd_mta_addr, + /* receiving_mx_hostname= */ trw->rcv_mta_name, + /* receiving_mx_helo= */ trw->rcv_mta_ehlo, + /* receiving_ip= */ trw->rcv_mta_addr, + /* additional_information= */ additional_detail, + /* failure_reason_code= */ failure_reason); + if (res == 0) + res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_FAILURE); + } + if (res == 0) { + res = tlsrpt_finish_delivery_request(&dr); + } else { + (void) tlsrpt_cancel_delivery_request(&dr); + } + } + (void) tlsrpt_close(&con); + } + return (trw_munge_report_result(res)); +} + +/* trw_report_success - one-shot success reporter */ + +int trw_report_success(TLSRPT_WRAPPER *trw) +{ + const char myname[] = "trw_report_success"; + struct tlsrpt_connection_t *con; + int res; + + /* + * Sanity check: usage errors are not a show stopper. + */ + if ((trw->flags & TRW_FLAG_HAVE_TLS_POLICY) == 0) { + msg_warn("%s: missing trw_set_tls_policy call", myname); + return (0); + } + /* This should not happen. Log a warning. */ + if (trw->flags & TRW_FLAG_REPORTED) { + msg_warn("%s: success or failure was already reported", myname); + return (0); + } + trw->flags |= TRW_FLAG_REPORTED; + + if (msg_verbose) + msg_info("%s: rpt_policy_domain=%s receiving_mx=%s", + myname, trw->rpt_policy_domain, trw->rcv_mta_name); + + if ((res = tlsrpt_open(&con, trw->rpt_socket_name)) == 0) { + struct tlsrpt_dr_t *dr; + + if ((res = tlsrpt_init_delivery_request(&dr, con, + trw->rpt_policy_domain, + trw->rpt_policy_string)) == 0) { + if ((res = tlsrpt_init_policy(dr, trw->tls_policy_type, + trw->tls_policy_domain)) == 0) + res = tlsrpt_finish_policy(dr, TLSRPT_FINAL_SUCCESS); + if (res == 0) { + res = tlsrpt_finish_delivery_request(&dr); + } else { + (void) tlsrpt_cancel_delivery_request(&dr); + } + } + (void) tlsrpt_close(&con); + } + return (trw_munge_report_result(res)); +} + +/* trw_is_reported - trw_report_success() or trw_report_failure() called */ + +int trw_is_reported(const TLSRPT_WRAPPER *trw) +{ + return (trw->flags & TRW_FLAG_REPORTED); +} + +#endif /* USE_TLS_RPT */ + + /* + * Dummy definitions for builds without the TLSRPT library, so that we can + * still validate names. + */ +#if !defined(USE_TLSRPT) +#define TLSRPT_POLICY_DANE 0 +#define TLSRPT_POLICY_STS 0 +#define TLSRPT_NO_POLICY_FOUND 0 + +#define TLSRPT_VALIDATION_FAILURE 0 +#define TLSRPT_STS_POLICY_FETCH_ERROR 0 +#define TLSRPT_STS_POLICY_INVALID 0 +#define TLSRPT_STS_WEBPKI_INVALID 0 +#endif + + /* + * Mapping from RFC 8460 string to libtlsrpt enum for policy types and + * policy failures. The mapping assumes that all enum values are + * non-negative. + */ +const NAME_CODE tlsrpt_policy_type_mapping[] = { + "sts", TLSRPT_POLICY_STS, + "no-policy-found", TLSRPT_NO_POLICY_FOUND, + 0, -1, +}; + +const NAME_CODE tlsrpt_policy_failure_mapping[] = { + "sts-policy-fetch-error", TLSRPT_STS_POLICY_FETCH_ERROR, + "sts-policy-invalid", TLSRPT_STS_POLICY_INVALID, + "sts-webpki-invalid", TLSRPT_STS_WEBPKI_INVALID, + "validation-failure", TLSRPT_VALIDATION_FAILURE, + 0, -1, +}; + +/* valid_tlsrpt_policy_type - validate policy_type attribute value */ + +int valid_tlsrpt_policy_type(const char *policy_type) +{ + return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE, + policy_type) >= 0); +} + +/* valid_tlsrpt_policy_failure - validate policy_failure attribute value */ + +int valid_tlsrpt_policy_failure(const char *policy_failure) +{ + return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE, + policy_failure) >= 0); +} + +#if defined(USE_TLSRPT) + +/* convert_tlsrpt_policy_type - convert string to enum */ + +tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type) +{ + return (name_code(tlsrpt_policy_type_mapping, NAME_CODE_FLAG_NONE, + policy_type)); +} + +/* convert_tlsrpt_policy_failure - convert string to enum */ + +tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure) +{ + return (name_code(tlsrpt_policy_failure_mapping, NAME_CODE_FLAG_NONE, + policy_failure)); +} + +#endif /* USE_TLSRPT */ + +#endif /* USE_TLS */ diff --git a/postfix/src/tls/tlsrpt_wrapper.h b/postfix/src/tls/tlsrpt_wrapper.h new file mode 100644 index 000000000..2107d3a3d --- /dev/null +++ b/postfix/src/tls/tlsrpt_wrapper.h @@ -0,0 +1,122 @@ +#ifndef _TLSRPT_WRAPPER_INCLUDED_ +#define _TLSRPT_WRAPPER_INCLUDED_ + +/*++ +/* NAME +/* tlsrpt_wrapper 3h +/* SUMMARY +/* TLSRPT support for the TLS protocol engine +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#if defined(USE_TLS) + +#if defined(USE_TLSRPT) + +#include + + /* + * External interface, with convenient setters for each SMTP protocol engine + * stage. Many functions have multiple arguments of the same type. Include + * parameter names in function prototypes here, and in call sites include + * comments before parameter values, to prepare for future clang-tidy + * bugprone-argument-comment checks. + */ +typedef struct TLSRPT_WRAPPER TLSRPT_WRAPPER; + +extern TLSRPT_WRAPPER *trw_create(const char *rpt_socket_name, + const char *rpt_policy_domain, + const char *rpt_policy_string); +extern void trw_free(TLSRPT_WRAPPER *trw); +extern void trw_set_tls_policy(TLSRPT_WRAPPER *trw, + tlsrpt_policy_type_t tls_policy_type, + const char *const * tls_policy_strings, + const char *tls_policy_domain, + const char *const * mx_policy_patterns); +extern void trw_set_tcp_connection(TLSRPT_WRAPPER *trw, + const char *snd_mta_addr, + const char *rcv_mta_name, + const char *rcv_mta_addr); +extern void trw_set_ehlo_resp(TLSRPT_WRAPPER *trw, + const char *rcv_mta_ehlo); +extern int trw_report_failure(TLSRPT_WRAPPER *trw, + tlsrpt_failure_t policy_failure, + const char *additional_detail, + const char *failure_reason); +extern int trw_report_success(TLSRPT_WRAPPER *trw); +extern int trw_is_reported(const TLSRPT_WRAPPER *trw); + + /* + * The internals declarations are also needed for functions that transmit + * and receive TLSRPT_WRAPPER objects. + */ +#ifdef TLSRPT_WRAPPER_INTERNAL + + /* + * Utility library. + */ +#include + +struct TLSRPT_WRAPPER { + /* Set with trw_create(). */ + char *rpt_socket_name; + char *rpt_policy_domain; + char *rpt_policy_string; + /* Set with trw_set_policy(). */ + tlsrpt_policy_type_t tls_policy_type; + ARGV *tls_policy_strings; + char *tls_policy_domain; + ARGV *mx_host_patterns; + /* Set with trw_set_tcp_connection(). */ + char *snd_mta_addr; + char *rcv_mta_name; + char *rcv_mta_addr; + /* Set with trw_set_ehlo_resp(). */ + char *rcv_mta_ehlo; + int flags; +}; + +#define TRW_FLAG_HAVE_TLS_POLICY (1<<0) +#define TRW_FLAG_HAVE_TCP_CONN (1<<1) +#define TRW_FLAG_HAVE_EHLO_RESP (1<<2) +#define TRW_FLAG_REPORTED (1<<3) + +#define TRW_RPT_SOCKET_NAME "rpt_socket_name" +#define TRW_RPT_POLICY_DOMAIN "rpt_policy_domain" +#define TRW_RPT_POLICY_STRING "rpt_policy_string" +#define TRW_TLS_POLICY_TYPE "tls_policy_type" +#define TRW_TLS_POLICY_STRINGS "tls_policy_strings" /* XXX Not checked */ +#define TRW_TLS_POLICY_DOMAIN "tls_policy_domain" +#define TRW_MX_HOST_PATTERNS "mx_host_patterns" /* XXX Not checked */ +#define TRW_SRC_MTA_ADDR "snd_mta_addr" +#define TRW_DST_MTA_NAME "rcv_mta_name" +#define TRW_DST_MTA_ADDR "rcv_mta_addr" +#define TRW_DST_MTA_EHLO "rcv_mta_ehlo" /* Optional */ +#define TRW_FLAGS "flags" + +#endif /* TLSRPT_WRAPPER_INTERNAL */ + +extern tlsrpt_policy_type_t convert_tlsrpt_policy_type(const char *policy_type); +extern tlsrpt_failure_t convert_tlsrpt_policy_failure(const char *policy_failure); + +#endif /* USE_TLSRPT */ + +extern int valid_tlsrpt_policy_type(const char *policy_type); +extern int valid_tlsrpt_policy_failure(const char *policy_failure); + +#endif /* USE_TLS */ + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/*--*/ + +#endif /* _TLSRPT_WRAPPER_INCLUDED_ */ diff --git a/postfix/src/tlsproxy/Makefile.in b/postfix/src/tlsproxy/Makefile.in index 51120af06..2b6d34589 100644 --- a/postfix/src/tlsproxy/Makefile.in +++ b/postfix/src/tlsproxy/Makefile.in @@ -75,6 +75,7 @@ tlsproxy.o: ../../include/split_at.h tlsproxy.o: ../../include/sys_defs.h tlsproxy.o: ../../include/tls.h tlsproxy.o: ../../include/tls_proxy.h +tlsproxy.o: ../../include/tlsrpt_wrapper.h tlsproxy.o: ../../include/vbuf.h tlsproxy.o: ../../include/vstream.h tlsproxy.o: ../../include/vstring.h diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index 0ebf52c68..dd35d3e42 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -426,6 +426,7 @@ #define TLS_INTERNAL /* XXX */ #include #include +#include /* * Application-specific. @@ -731,6 +732,19 @@ static int tlsp_eval_tls_error(TLSP_STATE *state, int err) state->flags |= TLSP_FLAG_NO_MORE_CIPHERTEXT_IO; return (TLSP_STAT_OK); } + + /* + * Report a generic failure only if a more specific failure wasn't + * already reported. + */ +#ifdef USE_TLSRPT + if ((state->flags & TLSP_FLAG_DO_HANDSHAKE) + && state->is_server_role == 0) + trw_report_failure(state->client_start_props->tlsrpt, + TLSRPT_VALIDATION_FAILURE, + /* additional_detail= */ (char *) 0, + "tls-handshake-failure"); +#endif tlsp_state_free(state); return (TLSP_STAT_ERR); } diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index bd21e6af3..0bb2c689b 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -45,7 +45,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \ sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \ mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \ - inet_addr_sizes.c quote_for_json.c + inet_addr_sizes.c quote_for_json.c mystrerror.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -92,7 +92,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \ sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \ mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \ - quote_for_json.o + quote_for_json.o mystrerror.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. @@ -2444,6 +2444,12 @@ mymalloc.o: sys_defs.h myrand.o: myrand.c myrand.o: myrand.h myrand.o: sys_defs.h +mystrerror.o: check_arg.h +mystrerror.o: mystrerror.c +mystrerror.o: stringops.h +mystrerror.o: sys_defs.h +mystrerror.o: vbuf.h +mystrerror.o: vstring.h mystrtok.o: check_arg.h mystrtok.o: msg.h mystrtok.o: mystrtok.c @@ -2549,9 +2555,11 @@ printable.o: stringops.h printable.o: sys_defs.h printable.o: vbuf.h printable.o: vstring.h +quote_for_json.o: check_arg.h quote_for_json.o: quote_for_json.c quote_for_json.o: stringops.h quote_for_json.o: sys_defs.h +quote_for_json.o: vbuf.h quote_for_json.o: vstring.h rand_sleep.o: iostuff.h rand_sleep.o: msg.h @@ -2883,6 +2891,7 @@ vbuf.o: vbuf.h vbuf_print.o: check_arg.h vbuf_print.o: msg.h vbuf_print.o: mymalloc.h +vbuf_print.o: stringops.h vbuf_print.o: sys_defs.h vbuf_print.o: vbuf.h vbuf_print.o: vbuf_print.c diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index 332426e88..112603164 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -31,6 +31,10 @@ /* char *arg; /* ssize_t arg_len; /* +/* ARGV *argv_addv(argvp, argv) +/* ARGV *argvp; +/* const char **argv; +/* /* void argv_terminate(argvp); /* ARGV *argvp; /* @@ -94,6 +98,10 @@ /* argv_addn() is like argv_add(), but each string is followed /* by a string length argument. /* +/* argv_addv() optionally creates an ARGV when the first argument +/* is a null pointer, and appends a null-terminated list of +/* strings. The result is null terminated. +/* /* argv_free() releases storage for a string array, and conveniently /* returns a null pointer. /* @@ -301,6 +309,23 @@ void argv_addn(ARGV *argvp,...) argvp->argv[argvp->argc] = 0; } +/* argv_addv - optionally create ARGV, append string vector */ + +ARGV *argv_addv(ARGV *argvp, const char *const * argv) +{ + const char *const * cpp; + + if (argvp == 0) { + for (cpp = argv; *cpp; cpp++) + /* void */ ; + argvp = argv_alloc(cpp - argv); + } + for (cpp = argv; *cpp; cpp++) + argv_add(argvp, *cpp, (char *) 0); + argvp->argv[argvp->argc] = 0; + return (argvp); +} + /* argv_terminate - terminate string array */ void argv_terminate(ARGV *argvp) @@ -602,6 +627,23 @@ static ARGV *test_argv_join(const TEST_CASE *tp, ARGV *argvp) return (argvp); } +/* test_argv_addv_appends - populate result */ + +static ARGV *test_argv_addv_appends(const TEST_CASE *tp, ARGV *argvp) +{ + argvp = argv_addv(argvp, tp->inputs); + return (argvp); +} + +/* test_argv_addv_creates_appends - populate result */ + +static ARGV *test_argv_addv_creates(const TEST_CASE *tp, ARGV *argvp) +{ + argv_free(argvp); + argvp = argv_addv((ARGV *) 0, tp->inputs); + return (argvp); +} + /* test_argv_verify - verify result */ static int test_argv_verify(const TEST_CASE *tp, ARGV *argvp) @@ -737,6 +779,14 @@ static const TEST_CASE test_cases[] = { {0}, 0, test_argv_join, 0, 1, {"", 0}, ':' }, + {"argv_addv appends to ARGV", + {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_appends, + 0, 3, {"foo", "baz", "bar", 0} + }, + {"argv_addv creates ARGV", + {"foo", "baz", "bar", 0}, /* ignored */ 0, test_argv_addv_creates, + 0, 3, {"foo", "baz", "bar", 0} + }, 0, }; diff --git a/postfix/src/util/argv.h b/postfix/src/util/argv.h index f1e746ad8..1c4790696 100644 --- a/postfix/src/util/argv.h +++ b/postfix/src/util/argv.h @@ -20,7 +20,7 @@ typedef struct ARGV { char **argv; /* string array */ } ARGV; -typedef int (*ARGV_COMPAR_FN)(const void *, const void *); +typedef int (*ARGV_COMPAR_FN) (const void *, const void *); extern ARGV *argv_alloc(ssize_t); extern ARGV *argv_sort(ARGV *); /* backwards compatibility */ @@ -28,6 +28,7 @@ extern ARGV *argv_qsort(ARGV *, ARGV_COMPAR_FN); extern ARGV *argv_uniq(ARGV *, ARGV_COMPAR_FN); extern void argv_add(ARGV *,...); extern void argv_addn(ARGV *,...); +extern ARGV *argv_addv(ARGV *, const char *const *); extern void argv_terminate(ARGV *); extern void argv_truncate(ARGV *, ssize_t); extern void argv_insert_one(ARGV *, ssize_t, const char *); diff --git a/postfix/src/util/hex_code.c b/postfix/src/util/hex_code.c index 3dfcb9818..6ea9a5161 100644 --- a/postfix/src/util/hex_code.c +++ b/postfix/src/util/hex_code.c @@ -44,6 +44,8 @@ /* functionality. /* .IP HEX_ENCODE_FLAG_USE_COLON /* Inserts one ":" between bytes. +/* .IP HEX_ENCODE_FLAG_APPEND +/* Append output to the buffer. /* .PP /* hex_decode_opt() enables extended functionality as controlled /* with \fIflags\fR. @@ -69,6 +71,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -107,7 +112,8 @@ VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags) int ch; ssize_t count; - VSTRING_RESET(result); + if ((flags & HEX_ENCODE_FLAG_APPEND) == 0) + VSTRING_RESET(result); for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) { ch = *cp; VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]); diff --git a/postfix/src/util/hex_code.h b/postfix/src/util/hex_code.h index 720977adb..ad923c9e2 100644 --- a/postfix/src/util/hex_code.h +++ b/postfix/src/util/hex_code.h @@ -21,6 +21,7 @@ */ #define HEX_ENCODE_FLAG_NONE (0) #define HEX_ENCODE_FLAG_USE_COLON (1<<0) +#define HEX_ENCODE_FLAG_APPEND (1<<1) #define HEX_DECODE_FLAG_NONE (0) #define HEX_DECODE_FLAG_ALLOW_COLON (1<<0) @@ -49,6 +50,9 @@ extern VSTRING *WARN_UNUSED_RESULT hex_decode_opt(VSTRING *, const char *, ssize /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/util/mystrerror.c b/postfix/src/util/mystrerror.c new file mode 100644 index 000000000..0de07f124 --- /dev/null +++ b/postfix/src/util/mystrerror.c @@ -0,0 +1,39 @@ +/*++ +/* NAME +/* mystrerror 3 +/* SUMMARY +/* convert error number to string +/* SYNOPSIS +/* #include +/* +/* const char *mystrerror() +/* int errnum) +/* DESCRIPTION +/* mystrerror() maps an error number to string. Unlike strerror(3) +/* it returns "Application error" instead of "Success". +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* porcupine.org +/*--*/ + + /* + * System library. + */ +#include +#include + + /* + * Utility library. + */ +#include + +/* mystrerror - convert error number to string */ + +const char *mystrerror(int errnum) +{ + return (errnum ? strerror(errnum) : "Application error"); +} diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h index db56f237a..4c357c81e 100644 --- a/postfix/src/util/stringops.h +++ b/postfix/src/util/stringops.h @@ -67,6 +67,7 @@ extern int strcasecmp_utf8x(int, const char *, const char *); extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t); extern char *quote_for_json(VSTRING *, const char *, ssize_t); extern char *quote_for_json_append(VSTRING *, const char *, ssize_t); +extern const char *mystrerror(int); #define EXTPAR_FLAG_NONE (0) #define EXTPAR_FLAG_STRIP (1<<0) /* "{ text }" -> "text" */ diff --git a/postfix/src/util/vbuf_print.c b/postfix/src/util/vbuf_print.c index d7a323f24..2e3266ed7 100644 --- a/postfix/src/util/vbuf_print.c +++ b/postfix/src/util/vbuf_print.c @@ -67,6 +67,7 @@ #include "mymalloc.h" #include "vbuf.h" #include "vstring.h" +#include "stringops.h" #include "vbuf_print.h" /* @@ -290,8 +291,7 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap) break; case 'm': /* Ignore the 'l' modifier, width and precision. */ - VBUF_STRCAT(bp, saved_errno ? - strerror(saved_errno) : "Application error"); + VBUF_STRCAT(bp, mystrerror(saved_errno)); break; case 'p': if (long_flag)