From: Wietse Z Venema Date: Wed, 10 Sep 2025 05:00:00 +0000 (-0500) Subject: postfix-3.11-20250910-nonprod X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2F20250906-nonprod;p=thirdparty%2Fpostfix.git postfix-3.11-20250910-nonprod --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 78948c7aa..c3e463897 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -384,6 +384,8 @@ -TTLS_SERVER_INIT_PROPS -TTLS_SERVER_START_PROPS -TTLS_SESS_STATE +-TTLS_STAT +-TTLS_STATS -TTLS_TICKET_KEY -TTLS_TLSA -TTLS_USAGE diff --git a/postfix/HISTORY b/postfix/HISTORY index ca33b511e..c50a459cc 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -29747,63 +29747,30 @@ NONPROD CODE production release; renamed enforce_reqtls to the more meaningful reqtls_level. -TODO - - Make it easy to find out by domain what the REQUIRETLS - success rates are, and what the failure modes are. - - Add optional statistics logging for "REQUIRETLS sent" and - for "all REQUIRETLS requirements pass". But these say - nothing about connections that failed to establish an - acceptable TLS session. - - Other logging: RFC 8689 prescribes enhanced status codes: - - - REQUIRETLS not supported by server: 5.7.30 REQUIRETLS - support required - - - Unable to establish TLS-protected SMTP session: 5.7.10 - Encryption needed - - These will show up in logging. + Completed 20250910: TLS feature policy status logging in + delivery status logging. This shows the TLS security level + enforcement status and, if a message requests REQUIRETLS, + the REQUIRETLS policy enforcement status. Files: .indent.pro, + cleanup/cleanup_bounce.c, cleanup/cleanup_out_recipient.c, + discard/discard.c, error/error.c, global/Makefile.in, + global/bounce.c, global/bounce.h, global/defer.c, global/defer.h, + global/deliver_pass.c, global/log_adhoc.c, global/log_adhoc.h, + global/mail_version.h, global/reject_deliver_request.c, + global/sent.c, global/sent.h, global/tls_stats.c, + global/tls_stats.h, global/trace.c, global/trace.h, + global/verify.c, global/verify.h, local/local.h, + oqmgr/qmgr_bounce.c, oqmgr/qmgr_defer.c, oqmgr/qmgr_message.c, + pipe/pipe.c, qmgr/qmgr_bounce.c, qmgr/qmgr_defer.c, + qmgr/qmgr_message.c, smtp/smtp.h, smtp/smtp_connect.c, + smtp/smtp_proto.c, smtp/smtp_rcpt.c, smtp/smtp_state.c, + smtp/smtp_trouble.c, virtual/virtual.h. - Known failure modes - misc problems at DNS, TCP, or SMTP level - misc errors with TLS policy or REQUIRETLS policy - smtp_connect.c: - TLS policy disables certificate matching - TLS policy disables encryption (opportunistic only) - smtp_proto.c. - STARTTLS rejected (sender requested REQUIRETLS...) - STARTTLS not offered (sender requested REQUIRETLS...) - (TLS is required, but our TLS engine is unavailable) - (TLS is required, but unavailable) - CERT not trusted|matched (sender requested REQUIRETLS...) - - Revert $requiretls in pipe daemon? - - Which enforcement levels can we implement? - - enforce: require that the server supports REQUIRETLS, - and that the connection satisfies RFC XXX requirements. - - no-plaintext: skip servers that don't announce STARTTLS; - request REQUIRETLS if the server supports REQUIRETLS, - otherwise deliver the message as if the sender did not - request REQUIRETLS. - - ????/ignore/disable/none: request REQUIRETLS if the - server supports REQUIRETLS, otherwise deliver the message - as if the sender did not request REQUIRETLS. +TODO Maybe log "server announces REQUIRETLS" if we're not enforcing REQUIRETLS. - Delete $requiretls from pipe daemon? - - Verify that SMTPUTF8 and REQUIRETLS flags propagate from - sendmail(1) and smtpd(8) through cleanup(8) and qmgr(8) to - delivery agents and bounce services. + Remove $requiretls from pipe daemon? Add support to propagate REQUIRETLS (Not: TLS-Required:) header through cleanup to queue files. diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index c0ce2ddb3..26d645f9a 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -784,41 +784,45 @@ SMTP(8) SMTP(8) How the Postfix SMTP and LMTP client will enforce REQUIRETLS for messages received with the REQUIRETLS option. + smtp_log_tls_feature_status (yes) + Enable logging of TLS feature information in delivery status + logging. + OBSOLETE TLS 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) @@ -830,19 +834,19 @@ SMTP(8) SMTP(8) 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) @@ -856,13 +860,13 @@ SMTP(8) SMTP(8) 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) @@ -872,17 +876,17 @@ SMTP(8) SMTP(8) 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) @@ -896,23 +900,23 @@ SMTP(8) SMTP(8) 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: @@ -923,13 +927,13 @@ SMTP(8) SMTP(8) 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. @@ -937,54 +941,54 @@ SMTP(8) SMTP(8) 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) @@ -992,46 +996,46 @@ SMTP(8) SMTP(8) 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 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) @@ -1045,21 +1049,21 @@ SMTP(8) SMTP(8) 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) @@ -1079,7 +1083,7 @@ SMTP(8) SMTP(8) 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: @@ -1091,14 +1095,14 @@ SMTP(8) SMTP(8) 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: @@ -1120,7 +1124,7 @@ SMTP(8) SMTP(8) 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 a9e03fe25..291fcf6f7 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -12223,6 +12223,76 @@ and earlier.

+ + +
smtp_log_tls_feature_status +(default: yes)
+ +

Enable logging of TLS feature information in delivery status +logging. This summarizes how features such as TLS and REQUIRETLS +were used.

+ + + +

Examples:

+ +
+ +
tls=none
A connection that does not use TLS.
+ +
tls=may
Opportunistic TLS after a successful +handshake.
+ +
tls=(disabled:may)
Opportunistic TLS after fallback +to plaintext, because the server did not announce STARTTLS support +or the server rejected the STARTTLS command.
+ +
tls=dane
DANE policy compliant, no downgrade.
+ +
tls=(dane)
Relaxed DANE after allowing MX records +without DNSSEC signature, or after falling back to the TLS security +level 'encrypt'.
+ +
tls=dane/requiretls
DANE and REQUIRETLS policies were +fully enforced, non-error case.
+ +
tls=dane?/requiretls?
DANE and REQUIRETLS policies +were undecided, because the connection failed before or in the TLS +handshake.
+ +
tls=may/(disabled:requiretls)
Opportunistic TLS + +opportunistic REQUIRETLS, server did not announce REQUIRETLS support. +
+ +
+ +

This feature is available in Postfix 3.11 and later.

+ +
smtp_mail_timeout diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index c0ce2ddb3..26d645f9a 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -784,41 +784,45 @@ SMTP(8) SMTP(8) How the Postfix SMTP and LMTP client will enforce REQUIRETLS for messages received with the REQUIRETLS option. + smtp_log_tls_feature_status (yes) + Enable logging of TLS feature information in delivery status + logging. + OBSOLETE TLS 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) @@ -830,19 +834,19 @@ SMTP(8) SMTP(8) 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) @@ -856,13 +860,13 @@ SMTP(8) SMTP(8) 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) @@ -872,17 +876,17 @@ SMTP(8) SMTP(8) 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) @@ -896,23 +900,23 @@ SMTP(8) SMTP(8) 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: @@ -923,13 +927,13 @@ SMTP(8) SMTP(8) 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. @@ -937,54 +941,54 @@ SMTP(8) SMTP(8) 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) @@ -992,46 +996,46 @@ SMTP(8) SMTP(8) 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 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) @@ -1045,21 +1049,21 @@ SMTP(8) SMTP(8) 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) @@ -1079,7 +1083,7 @@ SMTP(8) SMTP(8) 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: @@ -1091,14 +1095,14 @@ SMTP(8) SMTP(8) 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: @@ -1120,7 +1124,7 @@ SMTP(8) SMTP(8) Available in Postfix 3.7 and later: smtp_bind_address_enforce (no) - Defer delivery when the Postfix SMTP client cannot apply the + Defer delivery when the Postfix SMTP client cannot apply the smtp_bind_address or smtp_bind_address6 setting. SEE ALSO diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index e38135a80..e670cdf7e 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -7721,6 +7721,71 @@ The Postfix limit of 998 characters not including is consistent with the SMTP limit of 1000 characters including . The Postfix limit was 990 with Postfix 2.8 and earlier. +.SH smtp_log_tls_feature_status (default: yes) +Enable logging of TLS feature information in delivery status +logging. This summarizes how features such as TLS and REQUIRETLS +were used. +.IP \(bu +The logging is inserted between the "delays=a/b/c/d" and the +"status=" information. +.IP \(bu +The general format is "tls=feature/feature/...". See below for +examples. +.IP \(bu +The first feature name is the TLS security level: 'none', +\&'may',' encrypt', ..., 'dane'. Other features are omitted if not +activated. The REQUIRETLS extension's feature name will be 'requiretls'. +.IP \(bu +When 'disabled:' is prepended to a feature name, the remote server +did not support this feature. +.IP \(bu +When the above is enclosed in "(" and ")", the feature was used +successfully, but policy enforcement was relaxed. +.IP \(bu +When "!" is prepended to the above, the policy for that feature +was not satisfied and the feature was not used.. +.IP \(bu +When "?" is appended to the above, the policy for that feature +is undecided, usually due to a lost connection. +.br +.PP +Examples: +.IP "tls=none" +A connection that does not use TLS. +.br +.IP "tls=may" +Opportunistic TLS after a successful +handshake. +.br +.IP "tls=(disabled:may)" +Opportunistic TLS after fallback +to plaintext, because the server did not announce STARTTLS support +or the server rejected the STARTTLS command. +.br +.IP "tls=dane" +DANE policy compliant, no downgrade. +.br +.IP "tls=(dane)" +Relaxed DANE after allowing MX records +without DNSSEC signature, or after falling back to the TLS security +level 'encrypt'. +.br +.IP "tls=dane/requiretls" +DANE and REQUIRETLS policies were +fully enforced, non\-error case. +.br +.IP "tls=dane?/requiretls?" +DANE and REQUIRETLS policies +were undecided, because the connection failed before or in the TLS +handshake. +.br +.IP "tls=may/(disabled:requiretls)" +Opportunistic TLS + +opportunistic REQUIRETLS, server did not announce REQUIRETLS support. +.br +.br +.PP +This feature is available in Postfix 3.11 and later. .SH smtp_mail_timeout (default: 300s) The Postfix SMTP client time limit for sending the MAIL FROM command, and for receiving the remote SMTP server response. diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index b12529b3e..950ebd7b9 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -701,6 +701,9 @@ FROM" command. .IP "\fBsmtp_requiretls_policy (see 'postconf -d smtp_requiretls_policy' output)\fR" How the Postfix SMTP and LMTP client will enforce REQUIRETLS for messages received with the REQUIRETLS option. +.IP "\fBsmtp_log_tls_feature_status (yes)\fR" +Enable logging of TLS feature information in delivery status +logging. .SH "OBSOLETE TLS CONTROLS" .na .nf diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index a06248a7b..65d1336b4 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -19866,6 +19866,71 @@ configuration parameter. See there for details.

This feature is available in Postfix ≥ 3.11.

+%PARAM smtp_log_tls_feature_status yes + +

Enable logging of TLS feature information in delivery status +logging. This summarizes how features such as TLS and REQUIRETLS +were used.

+ +
    + +
  • The logging is inserted between the "delays=a/b/c/d" and the +"status=" information. + +
  • The general format is "tls=feature/feature/...". See below for +examples. + +
  • The first feature name is the TLS security level: 'none', +'may',' encrypt', ..., 'dane'. Other features are omitted if not +activated. The REQUIRETLS extension's feature name will be 'requiretls'. + +
  • When 'disabled:' is prepended to a feature name, the remote server +did not support this feature. + +
  • When the above is enclosed in "(" and ")", the feature was used +successfully, but policy enforcement was relaxed. + +
  • When "!" is prepended to the above, the policy for that feature +was not satisfied and the feature was not used.. + +
  • When "?" is appended to the above, the policy for that feature +is undecided, usually due to a lost connection. + +
+ +

Examples:

+ +
+ +
tls=none
A connection that does not use TLS.
+ +
tls=may
Opportunistic TLS after a successful +handshake.
+ +
tls=(disabled:may)
Opportunistic TLS after fallback +to plaintext, because the server did not announce STARTTLS support +or the server rejected the STARTTLS command.
+ +
tls=dane
DANE policy compliant, no downgrade.
+ +
tls=(dane)
Relaxed DANE after allowing MX records +without DNSSEC signature, or after falling back to the TLS security +level 'encrypt'.
+ +
tls=dane/requiretls
DANE and REQUIRETLS policies were +fully enforced, non-error case.
+ +
tls=dane?/requiretls?
DANE and REQUIRETLS policies +were undecided, because the connection failed before or in the TLS +handshake.
+ +
tls=may/(disabled:requiretls)
Opportunistic TLS + +opportunistic REQUIRETLS, server did not announce REQUIRETLS support. +
+ +
+ +

This feature is available in Postfix 3.11 and later.

%PARAM smtpd_hide_client_session no

Do not include SMTP client session information in the Postfix diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc index 1da83181c..19f79fbcd 100644 --- a/postfix/proto/stop.double-cc +++ b/postfix/proto/stop.double-cc @@ -346,3 +346,4 @@ encoded encoded text can contain only alpha digit ossl_digest_new ossl_digest_new returns NULL after error ossl_digest_data Richard Hansen rhansen rhansen org long long or long integer +void void tls_stats_revert TLS_STATS tstats diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index f22598ceb..374d15161 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -208,3 +208,8 @@ proto proto COMPATIBILITY_README html smtp smtp h smtp smtp_params c smtp smtp_proto c information Files sendmail sendmail c pickup pickup c postcat postcat c showq showq c + discard discard c error error c global Makefile in + global verify c global verify h local local h + pipe pipe c qmgr qmgr_bounce c qmgr qmgr_defer c + qmgr qmgr_message c smtp smtp h smtp smtp_connect c + smtp smtp_trouble c virtual virtual h diff --git a/postfix/proto/stop.double-proto-html b/postfix/proto/stop.double-proto-html index f0dd67d2c..40bea1784 100644 --- a/postfix/proto/stop.double-proto-html +++ b/postfix/proto/stop.double-proto-html @@ -366,3 +366,4 @@ Postfix Postfix legacy TLS Support The recommended socket location is still to be determined A good socket location would be under the Postfix queue directory for example smtp_tlsrpt_socket_name run tlsrpt tlsrpt sock The advantage of using a relative name is that it enhanced status code and text format 45 number number text opportunistic opportunistic starttls + li The general format is tls feature feature diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index 02b3d46b4..a87c1a5c7 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1874,3 +1874,5 @@ finalizer REQTLS reqtls Esmtp +ENF +tstats diff --git a/postfix/src/cleanup/cleanup_bounce.c b/postfix/src/cleanup/cleanup_bounce.c index 8358d1227..87de4cc62 100644 --- a/postfix/src/cleanup/cleanup_bounce.c +++ b/postfix/src/cleanup/cleanup_bounce.c @@ -75,7 +75,7 @@ static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt, */ if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id, CLEANUP_MSG_STATS(&stats, state), - rcpt, "none", dsn) != 0) { + rcpt, "none", NO_TLS_STATS, dsn) != 0) { state->errs |= CLEANUP_STAT_WRITE; } } diff --git a/postfix/src/cleanup/cleanup_out_recipient.c b/postfix/src/cleanup/cleanup_out_recipient.c index 003983b71..e4b059116 100644 --- a/postfix/src/cleanup/cleanup_out_recipient.c +++ b/postfix/src/cleanup/cleanup_out_recipient.c @@ -104,7 +104,7 @@ static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt, } if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id, CLEANUP_MSG_STATS(&stats, state), - rcpt, "none", dsn) != 0) { + rcpt, "none", NO_TLS_STATS, dsn) != 0) { msg_warn("%s: trace logfile update error", state->queue_id); state->errs |= CLEANUP_STAT_WRITE; } @@ -118,7 +118,7 @@ static void cleanup_verify_append(CLEANUP_STATE *state, RECIPIENT *rcpt, MSG_STATS stats; if (verify_append(state->queue_id, CLEANUP_MSG_STATS(&stats, state), - rcpt, "none", dsn, verify_status) != 0) { + rcpt, "none", NO_TLS_STATS, dsn, verify_status) != 0) { msg_warn("%s: verify service update error", state->queue_id); state->errs |= CLEANUP_STAT_WRITE; } diff --git a/postfix/src/discard/discard.c b/postfix/src/discard/discard.c index f21b95cbc..43b728a9c 100644 --- a/postfix/src/discard/discard.c +++ b/postfix/src/discard/discard.c @@ -186,7 +186,8 @@ static int deliver_message(DELIVER_REQUEST *request) for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; status = sent(BOUNCE_FLAGS(request), request->queue_id, - &request->msg_stats, rcpt, "none", &dsn); + &request->msg_stats, rcpt, "none", + NO_TLS_STATS, &dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(src, rcpt->offset); result |= status; diff --git a/postfix/src/error/error.c b/postfix/src/error/error.c index e1ff1cb7c..a1f70d662 100644 --- a/postfix/src/error/error.c +++ b/postfix/src/error/error.c @@ -148,7 +148,7 @@ static int deliver_message(DELIVER_REQUEST *request, const char *def_dsn, int (*append) (int, const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *)) + const char *, const TLS_STATS *, DSN *)) { const char *myname = "deliver_message"; VSTREAM *src; @@ -194,7 +194,8 @@ static int deliver_message(DELIVER_REQUEST *request, const char *def_dsn, for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; status = append(BOUNCE_FLAGS(request), request->queue_id, - &request->msg_stats, rcpt, "none", &dsn); + &request->msg_stats, rcpt, "none", NO_TLS_STATS, + &dsn); if (status == 0) deliver_completed(src, rcpt->offset); result |= status; diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index cc7086cfa..7a1034cd0 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -37,7 +37,8 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ normalize_mailhost_addr.c map_search.c reject_deliver_request.c \ info_log_addr_form.c sasl_mech_filter.c login_sender_match.c \ test_main.c compat_level.c config_known_tcp_ports.c \ - hfrom_format.c rfc2047_code.c ascii_header_text.c sendopts.c + hfrom_format.c rfc2047_code.c ascii_header_text.c sendopts.c \ + tls_stats.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ @@ -76,7 +77,8 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ normalize_mailhost_addr.o map_search.o reject_deliver_request.o \ info_log_addr_form.o sasl_mech_filter.o login_sender_match.o \ test_main.o compat_level.o config_known_tcp_ports.o \ - hfrom_format.o rfc2047_code.o ascii_header_text.o sendopts.o + hfrom_format.o rfc2047_code.o ascii_header_text.o sendopts.o \ + tls_stats.o # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # otherwise it sets the PLUGIN_* macros. @@ -113,7 +115,8 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ maillog_client.h normalize_mailhost_addr.h map_search.h \ info_log_addr_form.h sasl_mech_filter.h login_sender_match.h \ test_main.h compat_level.h config_known_tcp_ports.h \ - hfrom_format.h rfc2047_code.h ascii_header_text.h sendopts.h + hfrom_format.h rfc2047_code.h ascii_header_text.h sendopts.h \ + tls_stats.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -836,6 +839,7 @@ abounce.o: mail_params.h abounce.o: mail_proto.h abounce.o: msg_stats.h abounce.o: recipient_list.h +abounce.o: tls_stats.h addr_match_list.o: ../../include/argv.h addr_match_list.o: ../../include/check_arg.h addr_match_list.o: ../../include/match_list.h @@ -919,6 +923,7 @@ bounce.o: mail_proto.h bounce.o: msg_stats.h bounce.o: rcpt_print.h bounce.o: recipient_list.h +bounce.o: tls_stats.h bounce.o: trace.h bounce.o: verify.h bounce_log.o: ../../include/attr.h @@ -1109,6 +1114,7 @@ defer.o: mail_queue.h defer.o: msg_stats.h defer.o: rcpt_print.h defer.o: recipient_list.h +defer.o: tls_stats.h defer.o: trace.h defer.o: verify.h deliver_completed.o: ../../include/check_arg.h @@ -1156,6 +1162,7 @@ deliver_pass.o: mail_proto.h deliver_pass.o: msg_stats.h deliver_pass.o: rcpt_print.h deliver_pass.o: recipient_list.h +deliver_pass.o: tls_stats.h deliver_request.o: ../../include/attr.h deliver_request.o: ../../include/check_arg.h deliver_request.o: ../../include/htable.h @@ -1668,6 +1675,7 @@ log_adhoc.o: log_adhoc.h log_adhoc.o: mail_params.h log_adhoc.o: msg_stats.h log_adhoc.o: recipient_list.h +log_adhoc.o: tls_stats.h login_sender_match.o: ../../include/argv.h login_sender_match.o: ../../include/check_arg.h login_sender_match.o: ../../include/dict.h @@ -2574,9 +2582,11 @@ reject_deliver_request.o: deliver_completed.h reject_deliver_request.o: deliver_request.h reject_deliver_request.o: dsn.h reject_deliver_request.o: dsn_buf.h +reject_deliver_request.o: mail_params.h reject_deliver_request.o: msg_stats.h reject_deliver_request.o: recipient_list.h reject_deliver_request.o: reject_deliver_request.c +reject_deliver_request.o: tls_stats.h remove.o: ../../include/check_arg.h remove.o: ../../include/sys_defs.h remove.o: ../../include/vbuf.h @@ -2762,6 +2772,7 @@ sent.o: msg_stats.h sent.o: recipient_list.h sent.o: sent.c sent.o: sent.h +sent.o: tls_stats.h sent.o: trace.h sent.o: verify.h server_acl.o: ../../include/argv.h @@ -2887,6 +2898,12 @@ timed_ipc.o: ../../include/vstream.h timed_ipc.o: mail_params.h timed_ipc.o: timed_ipc.c timed_ipc.o: timed_ipc.h +tls_stats.o: ../../include/msg.h +tls_stats.o: ../../include/mymalloc.h +tls_stats.o: ../../include/sys_defs.h +tls_stats.o: mail_params.h +tls_stats.o: tls_stats.c +tls_stats.o: tls_stats.h tok822_find.o: ../../include/check_arg.h tok822_find.o: ../../include/sys_defs.h tok822_find.o: ../../include/vbuf.h @@ -2968,6 +2985,7 @@ trace.o: mail_proto.h trace.o: msg_stats.h trace.o: rcpt_print.h trace.o: recipient_list.h +trace.o: tls_stats.h trace.o: trace.c trace.o: trace.h user_acl.o: ../../include/argv.h @@ -3016,6 +3034,7 @@ verify.o: mail_params.h verify.o: mail_proto.h verify.o: msg_stats.h verify.o: recipient_list.h +verify.o: tls_stats.h verify.o: verify.c verify.o: verify.h verify.o: verify_clnt.h diff --git a/postfix/src/global/bounce.c b/postfix/src/global/bounce.c index a11ee7da8..47cbc9718 100644 --- a/postfix/src/global/bounce.c +++ b/postfix/src/global/bounce.c @@ -6,12 +6,13 @@ /* SYNOPSIS /* #include /* -/* int bounce_append(flags, id, stats, recipient, relay, dsn) +/* int bounce_append(flags, id, stats, recipient, relay, tstats, dsn) /* int flags; /* const char *id; /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* /* int bounce_flush(flags, queue, id, encoding, sendopts, sender, @@ -38,7 +39,8 @@ /* const char *verp_delims; /* /* int bounce_one(flags, queue, id, encoding, sendopts, sender, -/* dsn_envid, ret, stats, recipient, relay, dsn) +/* dsn_envid, ret, stats, recipient, relay, +/* tstats, dsn) /* int flags; /* const char *queue; /* const char *id; @@ -50,6 +52,7 @@ /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* /* void bounce_client_init(title, maps) @@ -58,15 +61,17 @@ /* INTERNAL API /* DSN_FILTER *delivery_status_filter; /* -/* int bounce_append_intern(flags, id, stats, recipient, relay, dsn) +/* int bounce_append_intern(flags, id, stats, recipient, relay, tstats, dsn) /* int flags; /* const char *id; /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; +/* DSN *dsn; /* /* int bounce_one_intern(flags, queue, id, encoding, sendopts, sender, -/* dsn_envid, ret, stats, recipient, relay, dsn) +/* dsn_envid, ret, stats, recipient, relay, tstats, dsn) /* int flags; /* const char *queue; /* const char *id; @@ -78,6 +83,7 @@ /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* DESCRIPTION /* This module implements the client interface to the message @@ -155,6 +161,8 @@ /* Sender-requested SMTPUTF8 or RequireTLS support. /* .IP sender /* The sender envelope address. +/* .IP tstats +/* TLS per-feature status. /* .IP dsn_envid /* Optional DSN envelope ID. /* .IP dsn_ret @@ -229,7 +237,7 @@ DSN_FILTER *delivery_status_filter; int bounce_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; DSN *dsn_res; @@ -249,17 +257,19 @@ int bounce_append(int flags, const char *id, MSG_STATS *stats, if (delivery_status_filter != 0 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { if (dsn_res->status[0] == '4') - return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + return (defer_append_intern(flags, id, stats, rcpt, relay, tstats, + dsn_res)); my_dsn = *dsn_res; } - return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + return (bounce_append_intern(flags, id, stats, rcpt, relay, tstats, + &my_dsn)); } /* bounce_append_intern - append delivery status to per-message bounce log */ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; int status; @@ -270,7 +280,7 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, */ if (flags & DEL_REQ_FLAG_MTA_VRFY) { my_dsn.action = "undeliverable"; - status = verify_append(id, stats, rcpt, relay, &my_dsn, + status = verify_append(id, stats, rcpt, relay, tstats, &my_dsn, DEL_RCPT_STAT_BOUNCE); return (status); } @@ -281,7 +291,7 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, */ if (flags & DEL_REQ_FLAG_USR_VRFY) { my_dsn.action = "undeliverable"; - status = trace_append(flags, id, stats, rcpt, relay, &my_dsn); + status = trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn); return (status); } @@ -326,9 +336,9 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn), ATTR_TYPE_END) == 0 && ((flags & DEL_REQ_FLAG_RECORD) == 0 - || trace_append(flags, id, stats, rcpt, relay, + || trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn) == 0)) { - log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status); + log_adhoc(id, stats, rcpt, relay, tstats, &my_dsn, log_status); status = (var_soft_bounce ? -1 : 0); } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { VSTRING *junk = vstring_alloc(100); @@ -337,7 +347,8 @@ int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, vstring_sprintf(junk, "%s or %s service failure", var_bounce_service, var_trace_service); my_dsn.reason = vstring_str(junk); - status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn); + status = defer_append_intern(flags, id, stats, rcpt, relay, tstats, + &my_dsn); vstring_free(junk); } else { status = -1; @@ -424,7 +435,7 @@ int bounce_one(int flags, const char *queue, const char *id, const char *encoding, int sendopts, const char *sender, const char *dsn_envid, int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt, - const char *relay, DSN *dsn) + const char *relay, const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; DSN *dsn_res; @@ -443,11 +454,13 @@ int bounce_one(int flags, const char *queue, const char *id, if (delivery_status_filter != 0 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { if (dsn_res->status[0] == '4') - return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + return (defer_append_intern(flags, id, stats, rcpt, relay, tstats, + dsn_res)); my_dsn = *dsn_res; } return (bounce_one_intern(flags, queue, id, encoding, sendopts, sender, - dsn_envid, dsn_ret, stats, rcpt, relay, &my_dsn)); + dsn_envid, dsn_ret, stats, rcpt, relay, tstats, + &my_dsn)); } /* bounce_one_intern - send notice for one recipient */ @@ -457,7 +470,7 @@ int bounce_one_intern(int flags, const char *queue, const char *id, const char *sender, const char *dsn_envid, int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; int status; @@ -468,7 +481,7 @@ int bounce_one_intern(int flags, const char *queue, const char *id, */ if (flags & DEL_REQ_FLAG_MTA_VRFY) { my_dsn.action = "undeliverable"; - status = verify_append(id, stats, rcpt, relay, &my_dsn, + status = verify_append(id, stats, rcpt, relay, tstats, &my_dsn, DEL_RCPT_STAT_BOUNCE); return (status); } @@ -479,7 +492,7 @@ int bounce_one_intern(int flags, const char *queue, const char *id, */ if (flags & DEL_REQ_FLAG_USR_VRFY) { my_dsn.action = "undeliverable"; - status = trace_append(flags, id, stats, rcpt, relay, &my_dsn); + status = trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn); return (status); } @@ -488,7 +501,8 @@ int bounce_one_intern(int flags, const char *queue, const char *id, * based procedure. */ else if (var_soft_bounce) { - return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + return (bounce_append_intern(flags, id, stats, rcpt, relay, tstats, + &my_dsn)); } /* @@ -519,9 +533,9 @@ int bounce_one_intern(int flags, const char *queue, const char *id, SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn), ATTR_TYPE_END) == 0 && ((flags & DEL_REQ_FLAG_RECORD) == 0 - || trace_append(flags, id, stats, rcpt, relay, + || trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn) == 0)) { - log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced"); + log_adhoc(id, stats, rcpt, relay, tstats, &my_dsn, "bounced"); status = 0; } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { VSTRING *junk = vstring_alloc(100); @@ -530,7 +544,8 @@ int bounce_one_intern(int flags, const char *queue, const char *id, vstring_sprintf(junk, "%s or %s service failure", var_bounce_service, var_trace_service); my_dsn.reason = vstring_str(junk); - status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn); + status = defer_append_intern(flags, id, stats, rcpt, relay, tstats, + &my_dsn); vstring_free(junk); } else { status = -1; diff --git a/postfix/src/global/bounce.h b/postfix/src/global/bounce.h index e2d67bd3a..7e99aab75 100644 --- a/postfix/src/global/bounce.h +++ b/postfix/src/global/bounce.h @@ -21,12 +21,13 @@ */ #include #include +#include /* * Client interface. */ extern int bounce_append(int, const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); extern int bounce_flush(int, const char *, const char *, const char *, int, const char *, const char *, int); extern int bounce_flush_verp(int, const char *, const char *, const char *, int, @@ -34,7 +35,7 @@ extern int bounce_flush_verp(int, const char *, const char *, const char *, int, extern int bounce_one(int, const char *, const char *, const char *, int, const char *, const char *, int, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); extern void bounce_client_init(const char *, const char *); /* @@ -77,11 +78,11 @@ extern void bounce_client_init(const char *, const char *); extern DSN_FILTER *delivery_status_filter; extern int bounce_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); extern int bounce_one_intern(int, const char *, const char *, const char *, int, const char *, const char *, int, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); #endif @@ -94,6 +95,9 @@ extern int bounce_one_intern(int, const char *, const char *, const char *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/global/defer.c b/postfix/src/global/defer.c index 919484d63..8c1a17b3e 100644 --- a/postfix/src/global/defer.c +++ b/postfix/src/global/defer.c @@ -6,12 +6,13 @@ /* SYNOPSIS /* #include /* -/* int defer_append(flags, id, stats, rcpt, relay, dsn) +/* int defer_append(flags, id, stats, rcpt, relay, tstats, dsn) /* int flags; /* const char *id; /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* /* int defer_flush(flags, queue, id, encoding, sendopts, sender, @@ -37,7 +38,8 @@ /* int dsn_ret; /* /* int defer_one(flags, queue, id, encoding, sendopts, sender, -/* dsn_envid, ret, stats, recipient, relay, dsn) +/* dsn_envid, ret, stats, recipient, relay, +/* tstats, dsn) /* int flags; /* const char *queue; /* const char *id; @@ -49,14 +51,17 @@ /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* INTERNAL API -/* int defer_append_intern(flags, id, stats, rcpt, relay, dsn) +/* int defer_append_intern(flags, id, stats, rcpt, relay, tstats, dsn) /* int flags; /* const char *id; /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; +/* DSN *dsn; /* DESCRIPTION /* This module implements a client interface to the defer service, /* which maintains a per-message logfile with status records for @@ -124,6 +129,8 @@ /* Recipient information. See recipient_list(3). /* .IP relay /* Host we could not talk to. +/* .IP tstats +/* TLS per-feature status. /* .IP dsn /* Delivery status. See dsn(3). The specified action is ignored. /* .IP encoding @@ -194,7 +201,7 @@ int defer_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; DSN *dsn_res; @@ -213,17 +220,19 @@ int defer_append(int flags, const char *id, MSG_STATS *stats, if (delivery_status_filter != 0 && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { if (dsn_res->status[0] == '5') - return (bounce_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + return (bounce_append_intern(flags, id, stats, rcpt, relay, tstats, + dsn_res)); my_dsn = *dsn_res; } - return (defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + return (defer_append_intern(flags, id, stats, rcpt, relay, tstats, + &my_dsn)); } /* defer_append_intern - defer message delivery */ int defer_append_intern(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { const char *rcpt_domain; DSN my_dsn = *dsn; @@ -235,7 +244,7 @@ int defer_append_intern(int flags, const char *id, MSG_STATS *stats, */ if (flags & DEL_REQ_FLAG_MTA_VRFY) { my_dsn.action = "undeliverable"; - status = verify_append(id, stats, rcpt, relay, &my_dsn, + status = verify_append(id, stats, rcpt, relay, tstats, &my_dsn, DEL_RCPT_STAT_DEFER); return (status); } @@ -246,7 +255,7 @@ int defer_append_intern(int flags, const char *id, MSG_STATS *stats, */ if (flags & DEL_REQ_FLAG_USR_VRFY) { my_dsn.action = "undeliverable"; - status = trace_append(flags, id, stats, rcpt, relay, &my_dsn); + status = trace_append(flags, id, stats, rcpt, relay, tstats, &my_dsn); return (status); } @@ -273,13 +282,14 @@ int defer_append_intern(int flags, const char *id, MSG_STATS *stats, SEND_ATTR_FUNC(dsn_print, (const void *) &my_dsn), ATTR_TYPE_END) != 0) msg_warn("%s: %s service failure", id, var_defer_service); - log_adhoc(id, stats, rcpt, relay, &my_dsn, "deferred"); + log_adhoc(id, stats, rcpt, relay, tstats, &my_dsn, "deferred"); /* * Traced delivery. */ if (flags & DEL_REQ_FLAG_RECORD) - if (trace_append(flags, id, stats, rcpt, relay, &my_dsn) != 0) + if (trace_append(flags, id, stats, rcpt, relay, tstats, + &my_dsn) != 0) msg_warn("%s: %s service failure", id, var_trace_service); /* @@ -358,7 +368,7 @@ int defer_one(int flags, const char *queue, const char *id, const char *encoding, int sendopts, const char *sender, const char *dsn_envid, int dsn_ret, MSG_STATS *stats, RECIPIENT *rcpt, - const char *relay, DSN *dsn) + const char *relay, const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; DSN *dsn_res; @@ -379,8 +389,9 @@ int defer_one(int flags, const char *queue, const char *id, if (dsn_res->status[0] == '5') return (bounce_one_intern(flags, queue, id, encoding, sendopts, sender, dsn_envid, dsn_ret, stats, - rcpt, relay, dsn_res)); + rcpt, relay, tstats, dsn_res)); my_dsn = *dsn_res; } - return (defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + return (defer_append_intern(flags, id, stats, rcpt, relay, tstats, + &my_dsn)); } diff --git a/postfix/src/global/defer.h b/postfix/src/global/defer.h index a015052f5..c0e93b7f6 100644 --- a/postfix/src/global/defer.h +++ b/postfix/src/global/defer.h @@ -20,7 +20,7 @@ * External interface. */ extern int defer_append(int, const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); extern int defer_flush(int, const char *, const char *, const char *, int, const char *, const char *, int); extern int defer_warn(int, const char *, const char *, const char *, int, @@ -28,7 +28,7 @@ extern int defer_warn(int, const char *, const char *, const char *, int, extern int defer_one(int, const char *, const char *, const char *, int, const char *, const char *, int, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); /* * Start of private API. @@ -36,7 +36,7 @@ extern int defer_one(int, const char *, const char *, const char *, int, #ifdef DSN_INTERN extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); #endif @@ -49,6 +49,9 @@ extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/global/deliver_pass.c b/postfix/src/global/deliver_pass.c index 545282945..111e51865 100644 --- a/postfix/src/global/deliver_pass.c +++ b/postfix/src/global/deliver_pass.c @@ -210,13 +210,13 @@ int deliver_pass(const char *class, const char *service, (void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable"); status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, - rcpt, "none", &dsn); + rcpt, "none", NO_TLS_STATS, &dsn); } else if ((status = deliver_pass_final_reply(stream, dsb)) == DELIVER_PASS_UNKNOWN) { (void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error"); status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, - rcpt, "none", &dsn); + rcpt, "none", NO_TLS_STATS, &dsn); } /* diff --git a/postfix/src/global/log_adhoc.c b/postfix/src/global/log_adhoc.c index e00e93054..58d281ddd 100644 --- a/postfix/src/global/log_adhoc.c +++ b/postfix/src/global/log_adhoc.c @@ -6,11 +6,12 @@ /* SYNOPSIS /* #include /* -/* void log_adhoc(id, stats, recipient, relay, dsn, status) +/* void log_adhoc(id, stats, recipient, relay, tstats, dsn, status) /* const char *id; /* MSG_STATS *stats; /* RECIPIENT *recipient; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* const char *status; /* DESCRIPTION @@ -33,6 +34,8 @@ /* Host we could (not) talk to. /* .IP status /* bounced, deferred, sent, and so on. +/* .IP tstats +/* TLS per-feature status. /* .IP dsn /* Delivery status information. See dsn(3). /* BUGS @@ -52,6 +55,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -71,6 +77,7 @@ #include #include #include +#include /* * Don't use "struct timeval" for time differences; use explicit signed @@ -85,8 +92,8 @@ typedef struct { /* log_adhoc - ad-hoc logging */ void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient, - const char *relay, DSN *dsn, - const char *status) + const char *relay, const TLS_STATS *tstats, + DSN *dsn, const char *status) { static VSTRING *buf; DELTA_TIME delay; /* end-to-end delay */ @@ -140,6 +147,8 @@ void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient, * * XXX Apparently, Solaris gettimeofday() can return out-of-range * microsecond values. + * + * TODO(wietse) move this to msg_stats.c */ #define DELTA(x, y, z) \ do { \ @@ -208,6 +217,41 @@ void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient, PRETTY_FORMAT(buf, "/", sdelay); PRETTY_FORMAT(buf, "/", xdelay); + /* + * Optional: TLS per-feature status summary. TODO(wietse) move this to + * tls_stats.c. + */ +#ifdef USE_TLS + if (tstats && tls_stats_used(tstats)) { + int idx; + const TLS_STAT *tstat; + int field_count = 0; + const char *if_disabled[2] = {"", "disabled:"}; + +#define RELAXED(tstat) ((tstat)->enforce == TLS_STAT_ENF_RELAXED) +#define DISABLED(tstat) ((tstat)->status == TLS_STAT_DISABLED) + + vstring_sprintf_append(buf, ", tls="); + for (idx = 0; idx < TLS_STATS_SIZE; idx++) { + tstat = tls_stat_access(tstats, idx); + if (tstat->status == TLS_STAT_INACTIVE) + continue; + if (field_count > 0) + vstring_strcat(buf, "/"); + if (tstat->status == TLS_STAT_VIOLATION) + vstring_strcat(buf, "!"); + vstring_sprintf_append(buf, "%s%s%s%s", + "(" + !RELAXED(tstat), + if_disabled[DISABLED(tstat)], + tstat->name, + ")" + !RELAXED(tstat)); + if (tstat->status == TLS_STAT_UNDECIDED) + vstring_strcat(buf, "?"); + field_count += 1; + } + } +#endif + /* * Finally, the delivery status. */ diff --git a/postfix/src/global/log_adhoc.h b/postfix/src/global/log_adhoc.h index 583cb6122..cb691a120 100644 --- a/postfix/src/global/log_adhoc.h +++ b/postfix/src/global/log_adhoc.h @@ -22,12 +22,13 @@ #include #include #include +#include /* * Client interface. */ extern void log_adhoc(const char *, MSG_STATS *, RECIPIENT *, const char *, - DSN *, const char *); + const TLS_STATS *, DSN *, const char *); /* LICENSE /* .ad @@ -38,6 +39,9 @@ extern void log_adhoc(const char *, MSG_STATS *, RECIPIENT *, const char *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 6ca723257..5b9cf7c01 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -4424,6 +4424,15 @@ extern char *var_smtp_reqtls_policy; #define DEF_REQTLS_REDACT_DSN "yes" extern int var_reqtls_redact_dsn; + /* + * TS per-feature policy status. + */ +#define VAR_SMTP_LOG_TLS_FEATURE_STATUS "smtp_log_tls_feature_status" +#define DEF_SMTP_LOG_TLS_FEATURE_STATUS "yes" +#define VAR_LMTP_LOG_TLS_FEATURE_STATUS "lmtp_log_tls_feature_status" +#define DEF_LMTP_LOG_TLS_FEATURE_STATUS "yes" +extern bool var_log_tls_feature_status; + /* * Workaround for future incompatibility. Our implementation of RFC 2308 * negative reply caching relies on the promise that res_query() and diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 564e97222..afae49718 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20250906" +#define MAIL_RELEASE_DATE "20250910" #define MAIL_VERSION_NUMBER "3.11" #ifdef SNAPSHOT @@ -104,6 +104,9 @@ extern void check_mail_version(const char *); /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/global/reject_deliver_request.c b/postfix/src/global/reject_deliver_request.c index 1b1b2a57a..8a7bcb4fc 100644 --- a/postfix/src/global/reject_deliver_request.c +++ b/postfix/src/global/reject_deliver_request.c @@ -38,6 +38,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* @@ -95,7 +98,7 @@ int reject_deliver_request(const char *service, DELIVER_REQUEST *request, (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, - service, &why->dsn); + service, NO_TLS_STATS, &why->dsn); if (status == 0) deliver_completed(request->fp, rcpt->offset); result |= status; diff --git a/postfix/src/global/sent.c b/postfix/src/global/sent.c index c81ccebdc..66f62d7fa 100644 --- a/postfix/src/global/sent.c +++ b/postfix/src/global/sent.c @@ -6,12 +6,13 @@ /* SYNOPSIS /* #include /* -/* int sent(flags, queue_id, stats, recipient, relay, dsn) +/* int sent(flags, queue_id, stats, recipient, relay, tstats, dsn) /* int flags; /* const char *queue_id; /* MSG_STATS *stats; /* RECIPIENT *recipient; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* DESCRIPTION /* sent() logs that a message was successfully delivered, @@ -44,6 +45,8 @@ /* Recipient information. See recipient_list(3). /* .IP relay /* Name of the host we're talking to. +/* .IP tstats +/* TLS per-feature status. /* .IP dsn /* Delivery status. See dsn(3). The action is ignored in case /* of a probe message. Otherwise, "delivered" is assumed when @@ -69,6 +72,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -99,7 +105,7 @@ int sent(int flags, const char *id, MSG_STATS *stats, RECIPIENT *recipient, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { DSN my_dsn = *dsn; DSN *dsn_res; @@ -126,7 +132,7 @@ int sent(int flags, const char *id, MSG_STATS *stats, */ if (flags & DEL_REQ_FLAG_MTA_VRFY) { my_dsn.action = "deliverable"; - status = verify_append(id, stats, recipient, relay, &my_dsn, + status = verify_append(id, stats, recipient, relay, tstats, &my_dsn, DEL_RCPT_STAT_OK); return (status); } @@ -137,7 +143,8 @@ int sent(int flags, const char *id, MSG_STATS *stats, */ if (flags & DEL_REQ_FLAG_USR_VRFY) { my_dsn.action = "deliverable"; - status = trace_append(flags, id, stats, recipient, relay, &my_dsn); + status = trace_append(flags, id, stats, recipient, relay, tstats, + &my_dsn); return (status); } @@ -156,10 +163,10 @@ int sent(int flags, const char *id, MSG_STATS *stats, my_dsn.action = "delivered"; if (((REC_ALL_SENT(flags) == 0 && REC_DLY_SENT(flags, recipient) == 0) - || trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0) + || trace_append(flags, id, stats, recipient, relay, tstats, &my_dsn) == 0) && ((recipient->dsn_notify & DSN_NOTIFY_SUCCESS) == 0 - || trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)) { - log_adhoc(id, stats, recipient, relay, &my_dsn, "sent"); + || trace_append(flags, id, stats, recipient, relay, tstats, &my_dsn) == 0)) { + log_adhoc(id, stats, recipient, relay, tstats, &my_dsn, "sent"); status = 0; } else { VSTRING *junk = vstring_alloc(100); @@ -168,7 +175,8 @@ int sent(int flags, const char *id, MSG_STATS *stats, id, var_trace_service); my_dsn.reason = vstring_str(junk); my_dsn.status = "4.3.0"; - status = defer_append(flags, id, stats, recipient, relay, &my_dsn); + status = defer_append(flags, id, stats, recipient, relay, tstats, + &my_dsn); vstring_free(junk); } return (status); diff --git a/postfix/src/global/sent.h b/postfix/src/global/sent.h index 2ed3856a5..8846be5dd 100644 --- a/postfix/src/global/sent.h +++ b/postfix/src/global/sent.h @@ -29,7 +29,7 @@ #define SENT_FLAG_NONE (0) extern int sent(int, const char *, MSG_STATS *, RECIPIENT *, const char *, - DSN *); + const TLS_STATS *, DSN *); /* LICENSE /* .ad @@ -40,6 +40,9 @@ extern int sent(int, const char *, MSG_STATS *, RECIPIENT *, const char *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/global/tls_stats.c b/postfix/src/global/tls_stats.c new file mode 100644 index 000000000..e64205ec7 --- /dev/null +++ b/postfix/src/global/tls_stats.c @@ -0,0 +1,188 @@ +/*++ +/* NAME +/* tls_stats 3 +/* SUMMARY +/* manage TLS per-feature policy compliance status +/* SYNOPSIS +/* #include +/* +/* TLS_STATS *tls_stats_create(void) +/* +/* void tls_stats_revert(TLS_STATS *tstats) +/* +/* void tls_stats_free(TLS_STATS *tstats) +/* +/* void tls_stat_activate( +/* TLS_STATS *tstats, +/* int idx, +/* const char *name, +/* bool enforce) +/* +/* void tls_stat_decide( +/* TLS_STATS *tstats, +/* int idx, +/* const char *name, +/* int status, +/* bool enforce) +/* +/* const TLS_STAT *tls_stats_access( +/* TLS_STATS *tstats, +/* int idx) +/* +/* int tls_stats_used(TLS_STATS *tstats) +/* DESCRIPTION +/* This module maintains for each active TLS feature whether +/* the current outbound SMTP connection satisfies the policy +/* requirements for that feature. For example, whether a server +/* certificate matches DANE or STS requirements. +/* +/* tls_stats_create() creates one TLS_STATS instance with all status +/* information set to TLS_STAT_INACTIVE. +/* +/* tls_stats_revert() reverts changes after tls_stats_create(). +/* +/* tls_stats_free() recycles storage for a TLS_STATS instance. +/* +/* Specific status information is accessed with an index. Valid +/* indices are 0 or 1 (the caller decides their purpose). +/* +/* The enforcement level of a feature is either TLS_STAT_ENF_FULL +/* (full enforcement) or TLS_STAT_ENF_RELAXED (some requirements +/* are relaxed). +/* +/* tls_stat_activate() changes the status in tstats at index idx +/* from TLS_STAT_INACTIVE to TLS_STAT_UNDECIDED, and updates the +/* name (pointer copy) and 'enforce' level. Calls with an invalid +/* index result in a panic(), and calls with an already active +/* index result in a warning. +/* +/* tls_stat_decide() updates the status in tstats at index idx from +/* TLS_STAT_UNDECIDED to TLS_STAT_COMPLIANT, TLS_STAT_VIOLATION, +/* or TLS_STAT_DISABLED, and updates its name and enforcement +/* level. Calls with an invalid index or an unexpected decision +/* status result in a panic(), and calls with an inactive or +/* already decided index status result in a warning. +/* +/* tls_stats_access() returns a const pointer to the status +/* information in tstats at index idx. +/* +/* tls_stats_used() returns the number of activated categories for +/* its argument. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* porcupine.org +/*--*/ + +#ifdef USE_TLS + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + +/* tls_stats_create - create an all-inactive TLS_STATS instance */ + +TLS_STATS *tls_stats_create(void) +{ + TLS_STATS *tstats; + TLS_STAT *tp; + +#define TLS_STAT_INIT(tp) do { \ + (tp)->name = 0; \ + (tp)->status = TLS_STAT_INACTIVE; \ + (tp)->enforce = 0; \ + } while (0); + + tstats = (TLS_STATS *) mymalloc(sizeof(*tstats)); + tstats->used = 0; + for (tp = tstats->st; tp < tstats->st + TLS_STATS_SIZE; tp++) + TLS_STAT_INIT(tp); + return tstats; +} + +/* tls_stats_revert - revert changes after tls_stats_create() */ + +void tls_stats_revert(TLS_STATS *tstats) +{ + TLS_STAT *tp; + + tstats->used = 0; + for (tp = tstats->st; tp < tstats->st + TLS_STATS_SIZE; tp++) + if (tp->status != TLS_STAT_INACTIVE) + TLS_STAT_INIT(tp); +} + +/* tls_stats_free - TLS_STATS destructor */ + +void tls_stats_free(TLS_STATS *tstats) +{ + myfree(tstats); +} + +/* tls_stat_activate - activate status at index */ + +void tls_stat_activate(TLS_STATS *tstats, int idx, const char *name, + bool enforce) +{ + TLS_STAT *tls_stat; + + if (idx < 0 || idx >= TLS_STATS_SIZE) + msg_panic("%s: bad index: %d", __func__, idx); + tls_stat = tstats->st + idx; + if (tls_stat->status != TLS_STAT_INACTIVE) + msg_warn("%s: already active TLS_STAT at index %d", __func__, idx); + tls_stat->name = name; + tls_stat->status = TLS_STAT_UNDECIDED; + tls_stat->enforce = enforce; + tstats->used += 1; +} + +/* tls_stat_decide - update undecided status at index */ + +extern void tls_stat_decide(TLS_STATS *tstats, int idx, const char *name, + int status, bool enforce) +{ + TLS_STAT *tls_stat; + + if (status != TLS_STAT_VIOLATION && status != TLS_STAT_COMPLIANT + && status != TLS_STAT_DISABLED) + msg_panic("%s: bad new status: %d", __func__, status); + if (idx < 0 || idx >= TLS_STATS_SIZE) + msg_panic("%s: bad index: %d", __func__, idx); + tls_stat = tstats->st + idx; + if (tls_stat->status != TLS_STAT_UNDECIDED) + msg_warn("%s: unexpected status %d at index %d", + __func__, tls_stat->status, idx); + tls_stat->name = name; + tls_stat->status = status; + tls_stat->enforce = enforce; +} + +/* tls_stat_access - peek at specific TLS_STAT instance. */ + +const TLS_STAT *tls_stat_access(const TLS_STATS *tstats, int idx) +{ + const TLS_STAT *tls_stat; + + if (idx < 0 || idx >= TLS_STATS_SIZE) + msg_panic("%s: bad index: %d", __func__, idx); + tls_stat = tstats->st + idx; + return (tls_stat); +} + +#endif /* USE_TLS */ diff --git a/postfix/src/global/tls_stats.h b/postfix/src/global/tls_stats.h new file mode 100644 index 000000000..93e334c51 --- /dev/null +++ b/postfix/src/global/tls_stats.h @@ -0,0 +1,69 @@ +#ifndef _TLS_STATS_H_INCLUDED_ +#define _TLS_STATS_H_INCLUDED_ + +/*++ +/* NAME +/* tls_stats 3h +/* SUMMARY +/* manage TLS per-feature policy compliance status +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * TODO(wietse) adopt C99 bool. + */ +#include + + /* + * External interface. + */ +typedef struct TLS_STAT { + const char *name; /* Human-readable feature name */ + int status; /* See below */ + bool enforce; /* See below */ +} TLS_STAT; + +#define TLS_STAT_INACTIVE 0 /* No data */ +#define TLS_STAT_UNDECIDED 1 /* Pending decision */ +#define TLS_STAT_VIOLATION 2 /* Definitely did not meet policy */ +#define TLS_STAT_COMPLIANT 3 /* Definitely did meet policy */ +#define TLS_STAT_DISABLED 4 /* Definitely disabled */ +#define TLS_STAT_ENF_FULL 1 /* Full enforcement */ +#define TLS_STAT_ENF_RELAXED 0 /* Relaxed enforcement */ + + /* + * Wrap it in a structure for sanity-checked access. + */ +#define TLS_STATS_SIZE 2 /* TLS level and REQUIRETLS */ + +typedef struct TLS_STATS { + int used; + TLS_STAT st[TLS_STATS_SIZE]; +} TLS_STATS; + +#ifdef USE_TLS + +extern TLS_STATS *tls_stats_create(void); +extern void tls_stats_revert(TLS_STATS *); +extern void tls_stats_free(TLS_STATS *); + +#define tls_stats_used(t) ((t)->used) +extern void tls_stat_activate(TLS_STATS *, int, const char *, bool); +extern void tls_stat_decide(TLS_STATS *, int, const char *, int, bool); +extern const TLS_STAT *tls_stat_access(const TLS_STATS *, int); + +#define NO_TLS_STATS ((TLS_STATS *) 0) + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* porcupine.org +/*--*/ + +#endif /* USE_TLS */ +#endif /* _TLS_STATS_H_INCLUDED_ */ diff --git a/postfix/src/global/trace.c b/postfix/src/global/trace.c index d826a6494..019bd25dd 100644 --- a/postfix/src/global/trace.c +++ b/postfix/src/global/trace.c @@ -6,12 +6,13 @@ /* SYNOPSIS /* #include /* -/* int trace_append(flags, id, stats, rcpt, relay, dsn) +/* int trace_append(flags, id, stats, rcpt, relay, tstats, dsn) /* int flags; /* const char *id; /* MSG_STATS *stats; /* RECIPIENT *rcpt; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* /* int trace_flush(flags, queue, id, encoding, sender, @@ -61,6 +62,8 @@ /* Recipient information. See recipient_list(3). /* .IP relay /* The host we sent the mail to. +/* .IP tstats +/* TLS per-feature status. /* .IP dsn /* Delivery status information. See dsn(3). /* DIAGNOSTICS @@ -84,6 +87,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -110,7 +116,7 @@ int trace_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, - DSN *dsn) + const TLS_STATS *tstats, DSN *dsn) { VSTRING *why = vstring_alloc(100); DSN my_dsn = *dsn; @@ -137,7 +143,7 @@ int trace_append(int flags, const char *id, MSG_STATS *stats, req_stat = -1; } else { if (flags & DEL_REQ_FLAG_USR_VRFY) - log_adhoc(id, stats, rcpt, relay, dsn, my_dsn.action); + log_adhoc(id, stats, rcpt, relay, tstats, dsn, my_dsn.action); req_stat = 0; } vstring_free(why); diff --git a/postfix/src/global/trace.h b/postfix/src/global/trace.h index 201360198..d76811edf 100644 --- a/postfix/src/global/trace.h +++ b/postfix/src/global/trace.h @@ -20,7 +20,7 @@ * External interface. */ extern int trace_append(int, const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *); + const char *, const TLS_STATS *, DSN *); extern int trace_flush(int, const char *, const char *, const char *, const char *, const char *, int); @@ -33,6 +33,9 @@ extern int trace_flush(int, const char *, const char *, const char *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/global/verify.c b/postfix/src/global/verify.c index 2ce091a5d..53f17f16b 100644 --- a/postfix/src/global/verify.c +++ b/postfix/src/global/verify.c @@ -6,12 +6,13 @@ /* SYNOPSIS /* #include /* -/* int verify_append(queue_id, stats, recipient, relay, dsn, +/* int verify_append(queue_id, stats, recipient, relay, tstats, dsn, /* verify_status) /* const char *queue_id; /* MSG_STATS *stats; /* RECIPIENT *recipient; /* const char *relay; +/* const TLS_STATS *tstats; /* DSN *dsn; /* int verify_status; /* DESCRIPTION @@ -32,6 +33,8 @@ /* Recipient information. See recipient_list(3). /* .IP relay /* Name of the host we're talking to. +/* .IP tstats +/* TLS per-feature status. /* .IP dsn /* Delivery status information. See dsn(3). /* The action is one of "deliverable" or "undeliverable". @@ -66,6 +69,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -91,7 +97,8 @@ int verify_append(const char *queue_id, MSG_STATS *stats, RECIPIENT *recipient, const char *relay, - DSN *dsn, int vrfy_stat) + const TLS_STATS *tstats, DSN *dsn, + int vrfy_stat) { int req_stat; DSN my_dsn = *dsn; @@ -120,7 +127,8 @@ int verify_append(const char *queue_id, MSG_STATS *stats, req_stat = VRFY_STAT_OK; } if (req_stat == VRFY_STAT_OK) { - log_adhoc(queue_id, stats, recipient, relay, dsn, my_dsn.action); + log_adhoc(queue_id, stats, recipient, relay, tstats, dsn, + my_dsn.action); req_stat = 0; } else { msg_warn("%s: %s service failure", queue_id, var_verify_service); diff --git a/postfix/src/global/verify.h b/postfix/src/global/verify.h index 250eb6d65..5e09d3622 100644 --- a/postfix/src/global/verify.h +++ b/postfix/src/global/verify.h @@ -20,12 +20,14 @@ * Global library. */ #include +#include /* * External interface. */ extern int verify_append(const char *, MSG_STATS *, RECIPIENT *, - const char *, DSN *, int); + const char *, const TLS_STATS *, + DSN *, int); /* LICENSE /* .ad @@ -36,6 +38,9 @@ extern int verify_append(const char *, MSG_STATS *, RECIPIENT *, /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ #endif diff --git a/postfix/src/local/local.h b/postfix/src/local/local.h index 13d0dc79e..0f024e45f 100644 --- a/postfix/src/local/local.h +++ b/postfix/src/local/local.h @@ -133,15 +133,15 @@ typedef struct LOCAL_STATE { #define BOUNCE_ATTR(attr) \ attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \ - DSN_FROM_DSN_BUF(attr.why) + NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why) #define BOUNCE_ONE_ATTR(attr) \ attr.queue_name, attr.queue_id, attr.encoding, attr.sendopts, \ attr.sender, attr.dsn_envid, attr.dsn_ret, \ &attr.msg_stats, &attr.rcpt, attr.relay, \ - DSN_FROM_DSN_BUF(attr.why) + NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why) #define SENT_ATTR(attr) \ attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \ - DSN_FROM_DSN_BUF(attr.why) + NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why) #define OPENED_ATTR(attr) \ attr.queue_id, attr.sender #define COPY_ATTR(attr) \ diff --git a/postfix/src/oqmgr/qmgr_bounce.c b/postfix/src/oqmgr/qmgr_bounce.c index 00ba885bf..ee9f56863 100644 --- a/postfix/src/oqmgr/qmgr_bounce.c +++ b/postfix/src/oqmgr/qmgr_bounce.c @@ -62,7 +62,7 @@ void qmgr_bounce_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient, status = bounce_append(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, - "none", dsn); + "none", NO_TLS_STATS, dsn); if (status == 0) deliver_completed(message->fp, recipient->offset); diff --git a/postfix/src/oqmgr/qmgr_defer.c b/postfix/src/oqmgr/qmgr_defer.c index dc0319e77..e87fe0b6e 100644 --- a/postfix/src/oqmgr/qmgr_defer.c +++ b/postfix/src/oqmgr/qmgr_defer.c @@ -154,5 +154,5 @@ void qmgr_defer_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient, */ message->flags |= defer_append(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, - "none", dsn); + "none", NO_TLS_STATS, dsn); } diff --git a/postfix/src/oqmgr/qmgr_message.c b/postfix/src/oqmgr/qmgr_message.c index a88c8e5e7..85b0d7141 100644 --- a/postfix/src/oqmgr/qmgr_message.c +++ b/postfix/src/oqmgr/qmgr_message.c @@ -1133,7 +1133,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) && !var_double_bounce_sender[len]) { status = sent(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, - "none", DSN_SIMPLE(&dsn, "2.0.0", + "none", NO_TLS_STATS, DSN_SIMPLE(&dsn, "2.0.0", "undeliverable postmaster notification discarded")); if (status == 0) { deliver_completed(message->fp, recipient->offset); diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index feb35f252..0788f640a 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -1095,7 +1095,7 @@ static int eval_command_status(int command_status, char *service, rcpt = request->rcpt_list.info + n; status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, - service, &why->dsn); + service, NO_TLS_STATS, &why->dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(request->fp, rcpt->offset); result |= status; @@ -1112,7 +1112,7 @@ static int eval_command_status(int command_status, char *service, (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, - service, &why->dsn); + service, NO_TLS_STATS, &why->dsn); if (status == 0) deliver_completed(request->fp, rcpt->offset); result |= status; @@ -1224,7 +1224,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv) rcpt = request->rcpt_list.info + n; status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, - rcpt, service, &why->dsn); + rcpt, service, NO_TLS_STATS, &why->dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(request->fp, rcpt->offset); deliver_status |= status; diff --git a/postfix/src/qmgr/qmgr_bounce.c b/postfix/src/qmgr/qmgr_bounce.c index 00ba885bf..ee9f56863 100644 --- a/postfix/src/qmgr/qmgr_bounce.c +++ b/postfix/src/qmgr/qmgr_bounce.c @@ -62,7 +62,7 @@ void qmgr_bounce_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient, status = bounce_append(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, - "none", dsn); + "none", NO_TLS_STATS, dsn); if (status == 0) deliver_completed(message->fp, recipient->offset); diff --git a/postfix/src/qmgr/qmgr_defer.c b/postfix/src/qmgr/qmgr_defer.c index 79615cc0f..1cce65ca5 100644 --- a/postfix/src/qmgr/qmgr_defer.c +++ b/postfix/src/qmgr/qmgr_defer.c @@ -159,5 +159,5 @@ void qmgr_defer_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient, */ message->flags |= defer_append(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, - "none", dsn); + "none", NO_TLS_STATS, dsn); } diff --git a/postfix/src/qmgr/qmgr_message.c b/postfix/src/qmgr/qmgr_message.c index 2e44c1d22..56533b2a2 100644 --- a/postfix/src/qmgr/qmgr_message.c +++ b/postfix/src/qmgr/qmgr_message.c @@ -1192,7 +1192,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message) && !var_double_bounce_sender[len]) { status = sent(message->tflags, message->queue_id, QMGR_MSG_STATS(&stats, message), recipient, - "none", DSN_SIMPLE(&dsn, "2.0.0", + "none", NO_TLS_STATS, DSN_SIMPLE(&dsn, "2.0.0", "undeliverable postmaster notification discarded")); if (status == 0) { deliver_completed(message->fp, recipient->offset); diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c index 924037091..f50541d59 100644 --- a/postfix/src/smtp/lmtp_params.c +++ b/postfix/src/smtp/lmtp_params.c @@ -142,5 +142,6 @@ VAR_LMTP_TLSRPT_ENABLE, DEF_LMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable, VAR_LMTP_TLSRPT_SKIP_REUSED_HS, DEF_LMTP_TLSRPT_SKIP_REUSED_HS, &var_smtp_tlsrpt_skip_reused_hs, VAR_LMTP_TLS_ENF_STS_MX_PAT, DEF_LMTP_TLS_ENF_STS_MX_PAT, &var_smtp_tls_enf_sts_mx_pat, + VAR_LMTP_LOG_TLS_FEATURE_STATUS, DEF_LMTP_LOG_TLS_FEATURE_STATUS, &var_log_tls_feature_status, 0, }; diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 1b8d5bc39..4ee1250c9 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -667,6 +667,9 @@ /* .IP "\fBsmtp_requiretls_policy (see 'postconf -d smtp_requiretls_policy' output)\fR" /* How the Postfix SMTP and LMTP client will enforce REQUIRETLS /* for messages received with the REQUIRETLS option. +/* .IP "\fBsmtp_log_tls_feature_status (yes)\fR" +/* Enable logging of TLS feature information in delivery status +/* logging. /* OBSOLETE TLS CONTROLS /* .ad /* .fi @@ -1180,6 +1183,7 @@ bool var_smtp_tlsrpt_enable; char *var_smtp_tlsrpt_sockname; bool var_smtp_tlsrpt_skip_reused_hs; char *var_smtp_reqtls_policy; +bool var_log_tls_feature_status; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 8433828fc..dd5018de7 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -33,6 +33,7 @@ #include #include #include +#include /* * Postfix TLS library. @@ -200,7 +201,7 @@ typedef struct SMTP_STATE { SMTP_ITERATOR iterator[1]; /* Usage: state->iterator->member */ /* - * Global iterator. + * TLS policy related. */ #ifdef USE_TLS SMTP_TLS_POLICY tls[1]; /* Usage: state->tls->member */ @@ -208,6 +209,7 @@ typedef struct SMTP_STATE { struct TLSRPT_WRAPPER *tlsrpt; #endif int reqtls_level; /* from smtp_reqtls_policy */ + TLS_STATS *tls_stats; /* policy compliance status */ #endif /* @@ -246,6 +248,28 @@ typedef struct SMTP_STATE { unsigned logged_line_length_limit:1; } SMTP_STATE; +#define SMTP_TLS_STAT_IDX_SEC_LEVEL 0 +#define SMTP_TLS_STAT_IDX_REQTLS 1 + +/* Use the TLS policy name for the TLS security level status feature. */ +#define SMTP_TLS_STAT_NAME_REQTLS "requiretls" + +#define smtp_tls_stat_activate_sec_level(tstats, level, enforce) \ + tls_stat_activate((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \ + str_tls_level(level), (enforce)) + +#define smtp_tls_stat_decide_sec_level(tstats, level, status, enforce) \ + tls_stat_decide((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \ + str_tls_level(level), (status), (enforce)) + +#define smtp_tls_stat_activate_reqtls(tstats, enforce) \ + tls_stat_activate((tstats), SMTP_TLS_STAT_IDX_REQTLS, \ + SMTP_TLS_STAT_NAME_REQTLS, (enforce)) + +#define smtp_tls_stat_decide_reqtls(tstats, status, enforce) \ + tls_stat_decide((tstats), SMTP_TLS_STAT_IDX_REQTLS, \ + SMTP_TLS_STAT_NAME_REQTLS, (status), (enforce)) + #ifdef USE_TLS #define STATE_TLS_NOT_REQUIRED(state) \ (var_tls_required_enable \ diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index ded52ab4a..6ed033e15 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -526,6 +526,10 @@ static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state) switch (state->reqtls_level) { case SMTP_REQTLS_POLICY_ACT_ENFORCE: if (TLS_MUST_MATCH(tls->level) == 0) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_FULL); dsb_simple(why, "5.7.10", "REQUIRETLS Failure: sender " "requested REQUIRETLS, but my configured TLS " "security level '%s' disables certificate " @@ -536,6 +540,10 @@ static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state) break; case SMTP_REQTLS_POLICY_ACT_OPP_TLS: if (tls->level == TLS_LEV_NONE) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_RELAXED); dsb_simple(why, "5.7.10", "REQUIRETLS Failure: sender " "requested REQUIRETLS, but my configured TLS " "security level '%s' disables encryption. The " @@ -1187,6 +1195,15 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, continue; /* XXX Assume there is no code at the end of this loop. */ } + if (state->tls_stats) { + tls_stats_revert(state->tls_stats); + smtp_tls_stat_activate_sec_level(state->tls_stats, + state->tls->level, + state->tls->level != TLS_LEV_MAY); + if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE) + smtp_tls_stat_activate_reqtls(state->tls_stats, + state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE); + } /* Disable TLS when retrying after a handshake failure */ if (retry_plain) { state->tls->level = TLS_LEV_NONE; diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c index 898689e41..5fe33c078 100644 --- a/postfix/src/smtp/smtp_params.c +++ b/postfix/src/smtp/smtp_params.c @@ -146,5 +146,6 @@ VAR_SMTP_TLSRPT_ENABLE, DEF_SMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable, VAR_SMTP_TLSRPT_SKIP_REUSED_HS, DEF_SMTP_TLSRPT_SKIP_REUSED_HS, &var_smtp_tlsrpt_skip_reused_hs, VAR_SMTP_TLS_ENF_STS_MX_PAT, DEF_SMTP_TLS_ENF_STS_MX_PAT, &var_smtp_tls_enf_sts_mx_pat, + VAR_SMTP_LOG_TLS_FEATURE_STATUS, DEF_SMTP_LOG_TLS_FEATURE_STATUS, &var_log_tls_feature_status, 0, }; diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 992763eeb..59142cea2 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -693,25 +693,48 @@ int smtp_helo(SMTP_STATE *state) * that the server does not offer REQUIRETLS. */ #ifdef USE_TLS - if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level) - && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0 - && (session->features & SMTP_FEATURE_REQTLS) == 0) { - switch (state->reqtls_level) { - case SMTP_REQTLS_POLICY_ACT_ENFORCE: - return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, - DSN_BY_LOCAL_MTA, - SMTP_RESP_FAKE(&fake, "5.7.30"), - "REQUIRETLS Failure: sender " - "requested REQUIRETLS, but no " - "server was found that supports " - "REQUIRETLS. The last attempted " - "server was %s", session->namaddr)); - default: + if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE + && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0) { + if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) { + if ((session->features & SMTP_FEATURE_REQTLS) != 0) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_COMPLIANT, + TLS_STAT_ENF_FULL); + } else { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_FULL); + return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, + DSN_BY_LOCAL_MTA, + SMTP_RESP_FAKE(&fake, "5.7.30"), + "REQUIRETLS Failure: sender " + "requested REQUIRETLS, but no " + "server was found that supports " + "REQUIRETLS. The last attempted " + "server was %s", session->namaddr)); + } + } else if ((session->features & SMTP_FEATURE_REQTLS) != 0) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_COMPLIANT, + TLS_STAT_ENF_RELAXED); + } else { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_DISABLED, + TLS_STAT_ENF_RELAXED); msg_info("%s: REQUIRETLS Debug: sender requested REQUIRETLS, " "but REQUIRETLS support was not offered by host " "%s", request->queue_id, session->namaddr); } } + + /* + * TODO(wietse) Maybe log servers that announce REQUIRETLS and whether + * the connection is authenticated? + */ #endif /* @@ -842,6 +865,13 @@ int smtp_helo(SMTP_STATE *state) state->misc_flags &= ~SMTP_MISC_FLAG_IN_STARTTLS; return (tls_helo_status); } +#ifdef USE_TLSRPT + if (state->tlsrpt) + trw_report_failure(state->tlsrpt, + TLSRPT_STARTTLS_NOT_SUPPORTED, + /* additional_info= */ (char *) 0, + /* failure_reason= */ (char *) 0); +#endif /* * Give up if we must use TLS but the server rejects STARTTLS @@ -854,14 +884,23 @@ int smtp_helo(SMTP_STATE *state) session->features &= ~SMTP_FEATURE_STARTTLS; if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level) || TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) { -#ifdef USE_TLSRPT - if (state->tlsrpt) - trw_report_failure(state->tlsrpt, - TLSRPT_STARTTLS_NOT_SUPPORTED, - /* additional_info= */ (char *) 0, - /* failure_reason= */ (char *) 0); -#endif - if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) + /* Before returning, decide all relevant policy status info. */ + if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, + state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE ? + TLS_STAT_ENF_FULL : + TLS_STAT_ENF_RELAXED); + } + if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level)) + if (state->tls_stats) + smtp_tls_stat_decide_sec_level(state->tls_stats, + state->tls->level, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_FULL); + /* Then, REQUIRETLS failure must take precedence over other. */ + if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) { return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "5.7.10"), @@ -870,6 +909,7 @@ int smtp_helo(SMTP_STATE *state) "but host %s refused to " "start TLS: %s", session->namaddr, translit(resp->str, "\n", " "))); + } /* TLS_REQUIRED_BY_SECURITY_LEVEL */ return (smtp_site_fail(state, STR(iter->host), resp, "TLS is required, but host %s refused to start TLS: %s", @@ -900,6 +940,22 @@ int smtp_helo(SMTP_STATE *state) /* additional_info= */ (char *) 0, /* failure_reason= */ (char *) 0); #endif + /* Before returning, decide all relevant policy status info. */ + if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, + state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE ? + TLS_STAT_ENF_FULL : + TLS_STAT_ENF_RELAXED); + } + if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level)) + if (state->tls_stats) + smtp_tls_stat_decide_sec_level(state->tls_stats, + state->tls->level, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_FULL); + /* Then, REQUIRETLS failure must take precedence over other. */ if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, DSN_BY_LOCAL_MTA, @@ -926,6 +982,17 @@ int smtp_helo(SMTP_STATE *state) "TLS is required, but unavailable")); } } + /* Continue in plain-text mode. */ + if (state->tls_stats) { + if (state->tls->level == TLS_LEV_NONE) { + /* TODO(wietse) May be fall-back after TLS handshake failed. */ + smtp_tls_stat_decide_sec_level(state->tls_stats, state->tls->level, + TLS_STAT_COMPLIANT, TLS_STAT_ENF_FULL); + } else { + smtp_tls_stat_decide_sec_level(state->tls_stats, state->tls->level, + TLS_STAT_DISABLED, TLS_STAT_ENF_RELAXED); + } + } } #endif #ifdef USE_SASL_AUTH @@ -944,7 +1011,6 @@ static int smtp_start_tls(SMTP_STATE *state) { SMTP_SESSION *session = state->session; SMTP_ITERATOR *iter = state->iterator; - DELIVER_REQUEST *request = state->request; TLS_CLIENT_START_PROPS start_props; VSTRING *serverid; SMTP_RESP fake; @@ -1211,6 +1277,7 @@ static int smtp_start_tls(SMTP_STATE *state) */ if (PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE) RETRY_AS_PLAINTEXT; + /* Leave all TLS feature policy status info as 'undecided'. */ return (smtp_misc_fail(state, state->tls->level == TLS_LEV_MAY ? SMTP_MISC_FAIL_NONE : SMTP_MISC_FAIL_THROTTLE, DSN_BY_LOCAL_MTA, @@ -1253,13 +1320,22 @@ static int smtp_start_tls(SMTP_STATE *state) /* failure_reason= */ (char *) 0); } #endif + /* Finalize TLS feature policy status info before giving up. */ + if (state->tls_stats) + smtp_tls_stat_decide_sec_level(state->tls_stats, + session->tls_context->level, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_FULL); /* * When the sender requested REQUIRETLS, and REQUIRETLS is * enforced, return the message as undeliverable only when there * are no more alternative MX hosts. */ - if (TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) + if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, TLS_STAT_ENF_FULL); return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "5.7.10"), @@ -1270,11 +1346,15 @@ static int smtp_start_tls(SMTP_STATE *state) "server was %s", trusted ? "matching" : "trusted", session->namaddr)); + } else if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE) { + if (state->tls_stats) + smtp_tls_stat_decide_reqtls(state->tls_stats, + TLS_STAT_VIOLATION, + TLS_STAT_ENF_RELAXED); + } return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "4.7.5"), "Server certificate not verified")); - } else { - /* TODO(wietse) record that this attempt satisfies REQUIRETLS. */ } } @@ -1290,6 +1370,16 @@ static int smtp_start_tls(SMTP_STATE *state) (void) trw_report_success(state->tlsrpt); #endif + /* + * Report relaxed enforcement for the initial TLS level if it was + * degraded. + */ + if (state->tls_stats) + smtp_tls_stat_decide_sec_level(state->tls_stats, + state->tls->level, TLS_STAT_COMPLIANT, + session->tls_context->level < state->tls->level ? + TLS_STAT_ENF_RELAXED : TLS_STAT_ENF_FULL); + /* * At this point we have to re-negotiate the "EHLO" to reget the * feature-list. @@ -1877,7 +1967,6 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE) { if ((session->features & SMTP_FEATURE_REQTLS) != 0) { vstring_strcat(next_command, " REQUIRETLS"); - /* TODO(wietse) record that REQUIRETLS is active. */ } else if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) { msg_panic("Can't happen: must enforce REQUIRETLS, but " diff --git a/postfix/src/smtp/smtp_rcpt.c b/postfix/src/smtp/smtp_rcpt.c index 6608ea880..66b9a41ac 100644 --- a/postfix/src/smtp/smtp_rcpt.c +++ b/postfix/src/smtp/smtp_rcpt.c @@ -179,7 +179,8 @@ void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt) status = sent(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, - session->namaddrport, DSN_FROM_DSN_BUF(why)); + session->namaddrport, state->tls_stats, + DSN_FROM_DSN_BUF(why)); if (status == 0) if (request->flags & DEL_REQ_FLAG_SUCCESS) deliver_completed(state->src, rcpt->offset); diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index 30c3bbbf6..8d61564e5 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -85,6 +85,10 @@ SMTP_STATE *smtp_state_alloc(void) #endif #ifdef USE_TLS state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE; + if (var_log_tls_feature_status) + state->tls_stats = tls_stats_create(); + else + state->tls_stats = 0; #endif if (var_smtp_cache_conn) { state->dest_label = vstring_alloc(10); @@ -112,6 +116,8 @@ void smtp_state_free(SMTP_STATE *state) #ifdef USE_TLS /* The TLS policy cache lifetime is one delivery. */ smtp_tls_policy_cache_flush(); + if (state->tls_stats) + tls_stats_free(state->tls_stats); #endif vstring_free(state->iterator->request_nexthop); vstring_free(state->iterator->dest); diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c index dd87f2979..9c182790e 100644 --- a/postfix/src/smtp/smtp_trouble.c +++ b/postfix/src/smtp/smtp_trouble.c @@ -285,7 +285,8 @@ static int smtp_bulk_fail(SMTP_STATE *state, int flags) status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, - session ? session->namaddrport : "none", &why->dsn); + session ? session->namaddrport : "none", state->tls_stats, + &why->dsn); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); @@ -433,7 +434,8 @@ void smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name, status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, - session ? session->namaddrport : "none", &why->dsn); + session ? session->namaddrport : "none", state->tls_stats, + &why->dsn); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); diff --git a/postfix/src/virtual/virtual.h b/postfix/src/virtual/virtual.h index 75dd6cd2e..cde8035d5 100644 --- a/postfix/src/virtual/virtual.h +++ b/postfix/src/virtual/virtual.h @@ -102,10 +102,10 @@ typedef struct LOCAL_STATE { #define BOUNCE_ATTR(attr) \ attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \ - DSN_FROM_DSN_BUF(attr.why) + NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why) #define SENT_ATTR(attr) \ attr.queue_id, &attr.msg_stats, &attr.rcpt, attr.relay, \ - DSN_FROM_DSN_BUF(attr.why) + NO_TLS_STATS, DSN_FROM_DSN_BUF(attr.why) #define COPY_ATTR(attr) \ attr.sender, attr.rcpt.orig_addr, attr.delivered, attr.fp