]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.11-20251114
authorWietse Z Venema <wietse@porcupine.org>
Fri, 14 Nov 2025 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Sat, 15 Nov 2025 11:27:25 +0000 (22:27 +1100)
155 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/AAAREADME
postfix/README_FILES/REQUIRETLS_README [new file with mode: 0644]
postfix/conf/postfix-files
postfix/html/REQUIRETLS_README.html [new file with mode: 0644]
postfix/html/bounce.8.html
postfix/html/cleanup.8.html
postfix/html/defer.8.html
postfix/html/index.html
postfix/html/lmtp.8.html
postfix/html/mailq.1.html
postfix/html/newaliases.1.html
postfix/html/postconf.5.html
postfix/html/relocated.5.html
postfix/html/sendmail.1.html
postfix/html/smtp.8.html
postfix/html/smtpd.8.html
postfix/html/trace.8.html
postfix/man/man1/sendmail.1
postfix/man/man5/postconf.5
postfix/man/man8/bounce.8
postfix/man/man8/cleanup.8
postfix/man/man8/smtp.8
postfix/man/man8/smtpd.8
postfix/mantools/check-postconf-unimplemented
postfix/mantools/postconf2man
postfix/mantools/postlink
postfix/proto/Makefile.in
postfix/proto/REQUIRETLS_README.html [new file with mode: 0644]
postfix/proto/index.html
postfix/proto/postconf.html.prolog
postfix/proto/postconf.man.prolog
postfix/proto/postconf.proto
postfix/proto/stop
postfix/proto/stop.double-cc
postfix/proto/stop.double-history
postfix/proto/stop.double-install-proto-text
postfix/proto/stop.double-proto-html
postfix/proto/stop.spell-cc
postfix/proto/stop.spell-history
postfix/proto/stop.spell-proto-html
postfix/src/bounce/Makefile.in
postfix/src/bounce/bounce.c
postfix/src/bounce/bounce_notify_util.c
postfix/src/cleanup/Makefile.in
postfix/src/cleanup/cleanup.c
postfix/src/cleanup/cleanup.h
postfix/src/cleanup/cleanup_api.c
postfix/src/cleanup/cleanup_bounce.c
postfix/src/cleanup/cleanup_init.c
postfix/src/cleanup/cleanup_message.c
postfix/src/cleanup/cleanup_out_recipient.c
postfix/src/cleanup/cleanup_state.c
postfix/src/discard/Makefile.in
postfix/src/discard/discard.c
postfix/src/error/Makefile.in
postfix/src/error/error.c
postfix/src/global/Makefile.in
postfix/src/global/bounce.c
postfix/src/global/bounce.h
postfix/src/global/cleanup_strflags.c
postfix/src/global/cleanup_user.h
postfix/src/global/defer.c
postfix/src/global/defer.h
postfix/src/global/deliver_pass.c
postfix/src/global/ehlo_mask.c
postfix/src/global/ehlo_mask.h
postfix/src/global/ehlo_mask.in [deleted file]
postfix/src/global/ehlo_mask.ref [deleted file]
postfix/src/global/ehlo_mask_test.c [new file with mode: 0644]
postfix/src/global/header_opts.c
postfix/src/global/header_opts.h
postfix/src/global/log_adhoc.c
postfix/src/global/log_adhoc.h
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/global/pol_stats.c [new file with mode: 0644]
postfix/src/global/pol_stats.h [new file with mode: 0644]
postfix/src/global/pol_stats_test.c [new file with mode: 0644]
postfix/src/global/post_mail.c
postfix/src/global/rec_type.h
postfix/src/global/reject_deliver_request.c
postfix/src/global/sent.c
postfix/src/global/sent.h
postfix/src/global/trace.c
postfix/src/global/trace.h
postfix/src/global/verify.c
postfix/src/global/verify.h
postfix/src/local/Makefile.in
postfix/src/local/forward.c
postfix/src/local/local.h
postfix/src/master/Makefile.in
postfix/src/oqmgr/Makefile.in
postfix/src/oqmgr/qmgr_bounce.c
postfix/src/oqmgr/qmgr_defer.c
postfix/src/oqmgr/qmgr_message.c
postfix/src/pipe/Makefile.in
postfix/src/pipe/pipe.c
postfix/src/postcat/postcat.c
postfix/src/postconf/Makefile.in
postfix/src/postconf/postconf.c
postfix/src/postconf/postconf_builtin.c
postfix/src/postscreen/postscreen.c
postfix/src/posttls-finger/posttls-finger.c
postfix/src/qmgr/Makefile.in
postfix/src/qmgr/qmgr_bounce.c
postfix/src/qmgr/qmgr_defer.c
postfix/src/qmgr/qmgr_message.c
postfix/src/sendmail/Makefile.in
postfix/src/sendmail/sendmail.c
postfix/src/showq/showq.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/lmtp_params.c
postfix/src/smtp/smtp.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_key.c
postfix/src/smtp/smtp_params.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_rcpt.c
postfix/src/smtp/smtp_reqtls_policy.c [new file with mode: 0644]
postfix/src/smtp/smtp_reqtls_policy.h [new file with mode: 0644]
postfix/src/smtp/smtp_reqtls_policy_test.c [new file with mode: 0644]
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_tls_policy_test.c
postfix/src/smtp/smtp_trouble.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_proxy.c
postfix/src/smtpd/smtpd_proxy.h
postfix/src/tls/tls.h
postfix/src/tls/tls_dane.c
postfix/src/util/Makefile.in
postfix/src/util/alldig.c
postfix/src/util/argv.c
postfix/src/util/argv.h
postfix/src/util/dict_debug_test.sh [changed mode: 0755->0644]
postfix/src/util/inet_addr_list.c
postfix/src/util/inet_prefix_top.c
postfix/src/util/mac_expand.c
postfix/src/util/mac_expand.h
postfix/src/util/mac_expand.in
postfix/src/util/mac_expand.ref
postfix/src/util/mac_midna.c [new file with mode: 0644]
postfix/src/util/mac_midna.h [new file with mode: 0644]
postfix/src/util/name_mask.c
postfix/src/util/name_mask.h
postfix/src/util/name_mask.ref5
postfix/src/util/name_mask.ref6
postfix/src/util/stringops.h
postfix/src/virtual/Makefile.in
postfix/src/virtual/virtual.h

index 9fa65a8f98523a03973139a85d187c7608d4395d..f645854e9ab1111ca3df77cebe4b6f5ee2b0e987 100644 (file)
 -TPIPE_STATE
 -TPLMYSQL
 -TPLPGSQL
+-TPOL_STAT
+-TPOL_STATS
 -TPOSTMAP_KEY_STATE
 -TPOST_MAIL_FCLOSE_STATE
 -TPOST_MAIL_STATE
 -TSMTP_CLI_ATTR
 -TSMTP_CMD
 -TSMTP_ITERATOR
+-TSMTP_REQTLS_POLICY
 -TSMTP_RESP
 -TSMTP_SASL_AUTH_CACHE
 -TSMTP_SESSION
index 69a471f42f2b968334cb7512a685c1ab8c9b3772..4900500a9082ee056a9cdbafb19b844334564b83 100644 (file)
@@ -28491,6 +28491,13 @@ Apologies for any names omitted.
        non-URI target and "dbname" was not set. Instead, it should
        return a surrogate dictionary. File: global/dict_pgsql.c.
 
+       Non-production REQUIRETLS branch: support for the REQUIRETLS
+       verb in SMTP.  According to RFC 8689, this requires TLS
+       server certificate matching. Files: cleanup/cleanup_api.c,
+       global/cleanup_strflags.c, global/post_mail.c, global/post_mail.c,
+       global/ehlo_mask.[hc], global/ehlo_mask_test.c, local/forward.c,
+       smtpd/smtpd.c, smtp/smtp_connect.c, smtp/smtp_proto.c.
+
 20241112
 
        Logging: the cleanup server now logs "queueid: removed
@@ -28879,6 +28886,37 @@ Apologies for any names omitted.
        Cleanup: memory leaks in test code. Files: util/hex_code.c,
        util/argv.c.
 
+20250119
+
+       Non-production REQUIRETLS branch: configuration parameter
+       "requiretls_enable" (default: yes). Files: cleanup/cleanup_api.c,
+       global/cleanup_strflags.c, global/post_mail.c, global/post_mail.c,
+       global/ehlo_mask.[hc], global/ehlo_mask_test.c, local/forward.c,
+       smtpd/smtpd.c, smtp/smtp_connect.c, smtp/smtp_proto.c.
+
+20250120
+
+       Non-production REQUIRETLS branch: After a certificate check
+       fails, or a remote SMTP server does not announce REQUIRETLS
+       support, the Postfix SMTP client will override the RFC 8689
+       5.x.x.  status and treat it as a soft error, until there
+       are no more alternate MX servers to try. Files: smtp/smtp.h,
+       smtp/smtp_proto.c, smtp/smtp_trouble.c.
+
+20250122
+
+       Non-production REQUIRETLS branch: new Postfix sendmail
+       command option "-O requiretls" to request that deliveries
+       over SMTP use the REQUIRETLS extension. The string "requiretls"
+       is case-insensitive.  Files: sendmail/sendmail.c,
+       global/rec_types.h, pickup/pickup.c.
+
+       Non-production REQUIRETLS branch: new Postfix sendmail
+       command option "-O smtputf8" to request that deliveries
+       over SMTP use the SMTPUTF8 extension. This reuses logic
+       that was introduced for REQUIRETLS. The string "smtputf8"
+       is case-insensitive.  Files: sendmail/sendmail.c.
+
 20250127
 
        Cleanup: broken non-TLS builds because of a missing #ifdef
@@ -28932,7 +28970,7 @@ Apologies for any names omitted.
        Documentation: updated TLSRPT_README, added postfix-tlspol
        policy plugin, deprecated the policy_ttl attribute. File:
        proto/TLSRPT_README.html.
-
 20250207
 
        Performance: when a mysql: or pgsql: configuration specifies
@@ -29576,6 +29614,14 @@ Apologies for any names omitted.
        commands with "bad syntax" and "bad UTF-8 syntax" errors.
        File: smtpd/smtpd.c.
 
+20250818
+
+       Non-production REQUIRETLS branch: infrastructure that will
+       use message headers to propagate REQUIRETLS through non-Milter
+       content filters. Files: global/x_esmtp_verb.[hc],
+       global/x_esmtp_verb_test.c. This was later renamed  to
+       requiretls_esmtp_header (no smtputf8 support).
+
 20250819
 
        Bugfix: the 20250717 workaround broke DBM library support
@@ -29587,6 +29633,33 @@ Apologies for any names omitted.
        posttls-finger logged a zero port number. Viktor Dukhovni.
        File: posttls-finger/posttls-finger.c.
 
+20250824
+
+       Non-production REQUIRETLS branch: when a message needs to
+       be delivered with SMTPUTF8, but a remote server does not
+       support it, try an alternate server. File: smtp/smtp_proto.c.
+
+       Non-production REQUIRETLS branch: better handling of line
+       breaks in indented paragraphs in the postconf(5) conversion
+       from HTML to 'man' format. File: mantools/postconf2man.
+
+       Non-production REQUIRETLS branch: requiretls_redact_dsn
+       (default: yes) as described in RFC 8689 section 5, to produce
+       bounce messages that don't need REQUIRETLS support on every
+       hop in the return path. Files: proto/postconf.proto,
+       global/mail_params.h, bounce/bounce.c.
+
+       Non-production REQUIRETLS branch: smtp_requiretls_policy
+       and lmtp_requiretls_policy for responsible REQUIRETLS policy
+       enforcement. Files: proto/postconf.proto, global/mail_params.h,
+       smtp/lmtp_params.c, smtp/smtp.c, smtp/smtp_connect.c,
+       smtp/smtp.h, smtp/smtp_params.c, smtp/smtp_proto.c,
+       smtp/smtp_reqtls_policy.c, smtp/smtp_reqtls_policy.h,
+       smtp/smtp_reqtls_policy_test.c, smtp/smtp_state.c.
+
+       Non-production REQUIRETLS branch: cleaned up some test code.
+       Files: util/inet_prefix_top.c, util/inet_addr_list.c.
+
 20250825
 
        Bugfix (defect introduced: 20250626): panic() in dict_register()
@@ -29594,6 +29667,20 @@ Apologies for any names omitted.
        changed the name of a dictionary on-the-fly by skipping a
        proxy: prefix). File: util/dict_open.c.
 
+20250826
+
+       Non-production REQUIRETLS branch: renamed all the new
+       identifiers introduced for REQUIRETLS support to ...REQTLS... and
+       ...reqtls... and re-indented the code. minimize differences with
+       the production release.
+
+20250827
+
+       Non-production REQUIRETLS branch: logic to skip destinations
+       whose TLS policy level cannot satisfy the REQUIRETLS policy,
+       or to log what would fail if REQUIRETLS were fully enforced.
+       File: smtp/smtp_connect.c.
+
 20250906
 
        Workaround for an interface mis-match between the Postfix
@@ -29615,6 +29702,26 @@ Apologies for any names omitted.
        smtp/smtp.h, smtp/smtp_connect.c, smtp/smtp_params.c,
        smtp/smtp_tls_policy.c, smtp/smtp_tls_policy_test.c.
 
+20250910
+
+       Non-production REQUIRETLS branch: 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.
+
 20250911
 
        Bugfix (defect introduced: Postfix 3.0): the Postfix SMTP
@@ -29629,6 +29736,44 @@ Apologies for any names omitted.
        required but the server does not support that feature.
        Files: smtp/smtp.h, smtp/smtp_key.c, smtp/smtp_proto.c.
 
+       Non-production REQUIRETLS branch: Include the REQUIRETLS
+       enforcement level in the SMTP connection cache storage key and
+       cached connection properties, and don't cache connections that
+       don't satisfy connection requirements. Files: smtp/smtp.h,
+       smtp/smtp_key.c, smtp_proto.c, smtp/smtp_trouble.c.
+
+20250914
+
+       Non-production REQUIRETLS branch: smtp_log_tls_feature_status
+       logging now shows the initial and final security levels for a
+       relaxed feature, for example dane:encrypt when opportunistic
+       'dane' is downgraded to 'encrypt' after all TLSA records
+       were found to be unusable. Files: proto/postconf.proto,
+       global/log_adhoc.c, global/pol_stats.c, global/pol_stats.h,
+       global/pol_stats_test.c, smtp/smtp.h, smtp/smtp_connect.c,
+       smtp/smtp_proto.c.
+
+20250916
+
+       Non-production REQUIRETLS branch: support for a
+       "Require-TLS-ESMTP: yes" header to propagate an ESMTP REQUIRETLS
+       request through a FILTER_README or SMTPD_PROXY_README style
+       content filter.  This header is detected or added by the
+       cleanup daemon.  This feature is enabled by default with
+       "requiretls_esmtp_header = yes". The Require-TLS-ESMTP header
+       will be visible to local and remote recipients. This feature can
+       safely be disabled when a configuration does not use REQUIRETLS,
+       or does not use FILTER_README or SMTPD_PROXY_README style content
+       filters. Files: cleanup/cleanup.c, cleanup/cleanup_message.c,
+       global/header_opts.[hc].
+
+20250917
+
+       Non-production REQUIRETLS branch: SMTP server support to add
+       a "Require-TLS-ESMTP: yes" header to messages received with
+       REQUIRETLS when they are sent through an SMTPD_PROXY_README
+       style content filter. Files: smtpd/smtpd.c, smtp/smtpd_proto.c.
+
 20250919
 
        Cleanup: unnecessary header parsing code. Viktor Dukhovni.
@@ -29643,14 +29788,43 @@ Apologies for any names omitted.
        the stock main.cf file. Problem diagnosed by Eray Aslan.
        File: conf/main.cf.
 
-20240924
+2025921
 
+       Non-production REQUIRETLS branch: with TLS policy status
+       logging, always log the actual REQUIRETLS state. For example,
+       when the TLS security level "encrypt" disables certificate
+       matches, then that is a policy violation for strict REQUIRETLS
+       enforcement (logged as "!requiretls:nocertmatch"), while
+       that is permitted with opportunistic REQUIRETLS (logged as
+       "requiretls:nocertmatch").  Files: proto/postconf.proto,
+       global/pol_stats.c, global/pol_stats_test.c, smtp/smtp.h,
+       smtp/smtp_connect.c, smtp/smtp_proto.c.
+
+2025922
+
+       Non-production REQUIRETLS branch: more testing and quality
+       control, resulting in various code and documentation fixes
+       for TLS feature status logging. Files: smtp/smtp_connect.c,
+       proto/postconf.proto.
+
+20240924
        TLSRPT Workaround: when policies[*].policy.policy-type is
        "no-policy-found", report the TLSRPT policy domain name as
        the policies[*].policy.policy-domain value. This ignores
        that TLSA policies must be reported with different policy-domain
        values than STS policies. File: tls/tlsrpt_wrapper.c.
 
+       Non-production REQUIRETLS branch: restore deleted support for the
+       "sendmail -O requiretls=yes|no command-line option", and support
+       for preliminary SIZE records in showq and postcat. Files:
+       sendmail/sendmail.c, global/rec_types.h, postcat/postcat.c,
+       showq/showq.c. Ditto for "sendmail -O smtputf8=yes|no".
+
+       Non-production REQUIRETLS branch: simplified the policy
+       status management API and removed unused code. Files:
+       global/pol_stats.[hc], global/pol_stats_test.c.
+
 20250927
 
        Updated documentation for smtp_tls_enforce_sts_mx_patterns.
@@ -29662,6 +29836,13 @@ Apologies for any names omitted.
        with the smtp_cname_overrides_servername setting. File:
        smtp/smtp_connect.c.
 
+       Non-production REQUIRETLS branch: by default enforce REQUIRETLS
+       fully for remote destinations; better default patterns to
+       exclude local destinations from REQUIRETLS enforcement. Files:
+       global/mail_params.h. mantools/postconf2man, proto/postconf.proto,
+       proto/REQUIRETLS_README.html, smtp/smtp_reqtls_policy.c,
+       smtp/smtp_reqtls_policy_test.c.
+
 20251001
 
        Cleanup: missing stdlib.h include. Viktor Dukhovni. File:
@@ -29791,6 +29972,16 @@ Apologies for any names omitted.
        Testing: fix bitrot in TLS DANE tests. Viktor Dukhovni.
        Files: tls/tls_dane.c, tls/tls_dane.sh.
 
+       Non-production REQUIRETLS branch: Make the smtp_requiretls_policy
+       searches deterministic by converting an internationalized next-hop
+       domain to ASCII form before querying the policy. In the default
+       policy, specify ${domain_to_ascii{$mydomain}} which returns
+       Punycode when $mydomain contains non-ASCII, and which otherwise
+       returns $mydomain. Files: mantools/postlink, proto/postconf.proto,
+       proto/REQUIRETLS_README.html, global/mail_params.[hc],
+       util/mac_midna.[hc], util/mac_expand.[hc], util/mac_expand.in,
+       util/mac_expand.ref, proto/postconf.*.proto, proto/Makefile.in.
+
 20251104
 
        Feature: non_empty_end_of_header_action specifies the
@@ -29807,3 +29998,28 @@ Apologies for any names omitted.
        Documentation: in OVERVIEW_README, added a missing link
        between smtpd(8) and trivial-rewrite(8). File:
        proto/OVERVIEW_README.html.
+
+20251114
+
+       Documentation: added 'halfdane' to the TLS status logging
+       documentation. File: proto/postconf.proto.
+
+       Bugfix (defect introduced: Postfix 2.8, date: 20110107):
+       the postscreen_reject_footer parameter did not support the
+       form ${smtpd_reject_footer}. Problem reported by pgnd. The
+       postscreen_expansion_filter parameter had the same problem.
+       File: postscreen/postscreen.c.
+
+       Cleanup: strip whitespace around the value in ${ function
+       { value } }. Files: util/mac_expand.c, util/mac_expand.in,
+       util/mac_expand.ref.
+       Merged the non-production REQUIRETLS branch.
+
+TODO
+
+       Decide whether or how to enable REQUIRETLS for non-SMTP
+       inputs: qmqpd.c ot pickup (sendmail). The smtputf8 auto-detect
+       strategy is not applicable because REQUIRETLS trumps
+       "TLS-Required: no". It should be easy for sendmail submissions
+       to choose whether TLS shall be required or optional.
index d3b0817eac825afa52d3c73fe9bee11223923b5c..12c1b25792ad8cb4131d98a7d071a362d0c67035 100644 (file)
@@ -14,6 +14,7 @@ G\bGe\ben\bne\ber\bra\bal\bl c\bco\bon\bnf\bfi\big\bgu\bur\bra\bat\bti\bio\bon\bn
   * TLSRPT_README: TLSRPT Protocol Support
   * IPV6_README: IP Version 6 Support
   * SMTPUTF8_README: SMTPUTF8 Support
+  * REQUIRETLS_README: REQUIRETLS Support
   * MAILLOG_README: Postfix logging to file or stdout
   * COMPATIBILITY_README: Backwards-Compatibility Safety Net
   * DEPRECATION_README: Deprecated features and alternatives
diff --git a/postfix/README_FILES/REQUIRETLS_README b/postfix/README_FILES/REQUIRETLS_README
new file mode 100644 (file)
index 0000000..0078447
--- /dev/null
@@ -0,0 +1,418 @@
+P\bPo\bos\bst\btf\bfi\bix\bx R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS S\bSu\bup\bpp\bpo\bor\brt\bt
+
+-------------------------------------------------------------------------------
+
+T\bTa\bab\bbl\ble\be o\bof\bf C\bCo\bon\bnt\bte\ben\bnt\bts\bs
+
+  * Introduction
+  * REQUIRETLS for a perimeter MTA
+
+      o Receiving inbound messages with REQUIRETLS requests
+      o LMTP and SMTP-based message stores and content filters content filters
+      o Non-SMTP and non-LMTP content filters
+      o Communication with external servers
+      o Relaxing REQUIRETLS for external deliveries
+
+  * An experiment: testing REQUIRETLS support
+  * Requesting REQUIRETLS without SMTP
+  * Non-delivery notifications
+  * REQUIRETLS quick summary
+  * Credits
+
+I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
+
+(For background information, see below for a REQUIRETLS quick summary.)
+
+This document covers the Postfix default settings for using the REQUIRETLS
+extension. The purpose of these defaults is to make REQUIRETLS support usable
+in an existing environment, with a path towards the future.
+
+The main issues with deploying REQUIRETLS are a lack of support in existing
+infrastructure:
+
+  * REQUIRETLS requires that server certificates are authenticated. When email
+    is sent across the Internet, this involves a DANE or MTA-STS policy that is
+    published by a mail receiving domain, using DNSSEC or HTTPS. At this time,
+    many domains do not publish such a policy.
+
+  * REQUIRETLS is historically not supported by existing local infrastructure
+    such as internal message stores or Postfix content filters, and may be
+    over-kill for connections that happen behind a perimeter MTA within a
+    trusted internal network.
+
+R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS f\bfo\bor\br a\ba p\bpe\ber\bri\bim\bme\bet\bte\ber\br M\bMT\bTA\bA
+
+In this text, a perimeter MTA is a mail system that operates on the boundary of
+an administrative domain. It receives email messages for the domain, and/or
+delivers email messages on behalf of the domain.
+
+R\bRe\bec\bce\bei\biv\bvi\bin\bng\bg i\bin\bnb\bbo\bou\bun\bnd\bd m\bme\bes\bss\bsa\bag\bge\bes\bs w\bwi\bit\bth\bh R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS r\bre\beq\bqu\bue\bes\bst\bts\bs
+
+Postfix has one global parameter setting that controls REQUIRETLS support in
+all Postfix processes. The default setting is:
+
+    /etc/postfix/main.cf:
+        requiretls_enable = yes
+
+With this, the Postfix SMTP server will announce REQUIRETLS support, and more
+importantly, will receive messages from senders that for some reason request
+REQUIRETLS support -- messages that you would otherwise not receive, assuming
+that the domain already publishes a valid DANE and/or STS policy.
+
+If all you need is to receive messages with REQUIRETLS, and you do not insist
+on enforcing REQUIRETLS when sending or forwarding messages, then you can stop
+reading this document after adding the additional settings below.
+
+    NOTE: The configuration below may be suitable for a personal domain, where
+    the owner can decide what happens with all messages. For domains that
+    receive messages for other people, a less radical approach may be better,
+    as described in the sections that follow.
+
+    1 /etc/postfix/main.cf:
+    2     # Don't enforce REQUIRETLS when delivering mail with SMTP or LMTP.
+    3     smtp_requiretls_policy = opportunistic
+    4     lmtp_requiretls_policy = opportunistic
+    5
+    6     # Don't detect or add a "Require-TLS-ESMTP: yes" header.
+    7     requiretls_esmtp_header = no
+
+  * Lines 3-4: These relax REQUIRETLS enforcement when delivering a email to a
+    message store, content filter, or other destination that may not support
+    REQUIRETLS. If a server does not support STARTTLS or REQUIRETLS, then
+    Postfix will simply deliver the message as if the sender did not request
+    REQUIRETLS.
+
+  * Line 7: The requiretls_esmtp_header feature enables support for a message
+    header "Require-TLS-ESMTP: yes" that allows Postfix to propagate the
+    sender's REQUIRETLS request through a content filter based on
+    SMTPD_PROXY_README or FILTER_README. This feature can safely be disabled if
+    the domain does not need to enforce REQUIRETLS while delivering or
+    forwarding messages.
+
+L\bLM\bMT\bTP\bP a\ban\bnd\bd S\bSM\bMT\bTP\bP-\b-b\bba\bas\bse\bed\bd m\bme\bes\bss\bsa\bag\bge\be s\bst\bto\bor\bre\bes\bs a\ban\bnd\bd c\bco\bon\bnt\bte\ben\bnt\bt f\bfi\bil\blt\bte\ber\brs\bs
+
+REQUIRETLS is historically not supported by message stores such as Dovecot, and
+by content filters based on FILTER_README or SMTPD_PROXY_README. The settings
+below allow for that reality, while also preparing for future REQUIRETLS
+support.
+
+The Postfix SMTP (LMTP) client supports a permissive REQUIRETLS policy that is
+suitable for communication with internal message stores and content filters
+based on FILTER_README or SMTPD_PROXY_README.
+
+  * o\bop\bpp\bpo\bor\brt\btu\bun\bni\bis\bst\bti\bic\bc: STARTTLS and REQUIRETLS support are optional. When the
+    sender requests REQUIRETLS, and an SMTP or LMTP server supports STARTTLS
+    and REQUIRETLS, then send REQUIRETLS, otherwise simply deliver the message
+    as if the sender did not request REQUIRETLS.
+
+For a more complete definition of this enforcement level, see the
+smtp_requiretls_policy parameter documentation.
+
+For REQUIRETLS, the relevant Postfix 3.11 configuration default settings are:
+
+     1 /etc/postfix/main.cf:
+     2     smtp_tls_security_level = may
+     3     requiretls_esmtp_header = yes
+     4     lmtp_requiretls_policy = opportunistic
+     5     smtp_requiretls_policy =
+     6         inline:{
+     7             {${domain_to_ascii{$mydomain}} = opportunistic}
+     8             {.${domain_to_ascii{$mydomain}} = opportunistic}
+     9             {localhost = opportunistic} }
+    10         cidr:{
+    11             {0.0.0.0/0 opportunistic}
+    12             {::/0 opportunistic} }
+    13       ...to be completed in section "Communication with external
+    servers"...
+
+  * Line 3: The requiretls_esmtp_header setting enables support for a message
+    header "Require-TLS-ESMTP: yes" that allows Postfix to propagate the
+    sender's REQUIRETLS request through a content filter. This feature can
+    safely be disabled if there is no need for content inspection based on
+    SMTPD_PROXY_README or FILTER_README.
+
+  * Lines 5-12: These make REQUIRETLS support optional for internal
+    destinations and content filters that are specified as a symbolic name
+    (lines 6-9) or as a numerical IP address (lines 10-12).
+
+  * Lines 7 and 8 use ${domain_to_ascii{$mydomain}} instead of $mydomain. The
+    function domain_to_ascii{} returns $mydomain if that contains only (7-bit)
+    ASCII. If the mydomain value contains non-ASCII characters, then
+    domain_to_ascii{} returns the xn--mumble-mumble Punycode (A-label) form
+    that Postfix needs. This works around a limitation that may be eliminated
+    in a future Postfix version.
+
+  * Note: if you specify a domain list outside main.cf, then the automatic
+    $name expansions and Punycode conversions will not happen; you will need to
+    enter real domain names and will need to convert non-ASCII domains to
+    Punycode.
+
+N\bNo\bon\bn-\b-S\bSM\bMT\bTP\bP a\ban\bnd\bd n\bno\bon\bn-\b-L\bLM\bMT\bTP\bP c\bco\bon\bnt\bte\ben\bnt\bt f\bfi\bil\blt\bte\ber\brs\bs
+
+Postfix FILTER_README describes content inspection based on a pipe-to-command
+approach. For REQUIRETLS, the relevant Postfix 3.11 default setting is:
+
+    /etc/postfix/main.cf:
+        requiretls_esmtp_header = yes
+
+The requiretls_esmtp_header feature enables support for a message header
+"Require-TLS-ESMTP: yes" that allows Postfix to propagate the sender's
+REQUIRETLS request through a content filter. This feature can safely be
+disabled if there is no need for content inspection based on SMTPD_PROXY_README
+or FILTER_README.
+
+C\bCo\bom\bmm\bmu\bun\bni\bic\bca\bat\bti\bio\bon\bn w\bwi\bit\bth\bh e\bex\bxt\bte\ber\brn\bna\bal\bl s\bse\ber\brv\bve\ber\brs\bs
+
+For communication with external servers, the Postfix SMTP client supports
+multiple enforcement levels:
+
+  * e\ben\bnf\bfo\bor\brc\bce\be: When the sender requests REQUIRETLS, require secure lookup of MX
+    hosts (for example, using DNSSEC or HTTPS), require a server certificate
+    match (for example, based on a published DANE or STS policy), and require
+    that the remote server supports REQUIRETLS. Otherwise return the message as
+    undeliverable.
+
+    NOTE: this is also used implicitly when no REQUIRETLS policy match is
+    found.
+
+  * o\bop\bpp\bpo\bor\brt\btu\bun\bni\bis\bst\bti\bic\bc+\b+s\bst\bta\bar\brt\btt\btl\bls\bs: When the sender requests REQUIRETLS, require that
+    the server supports STARTTLS. Send REQUIRETLS if the server supports
+    REQUIRETLS, otherwise simply deliver the message as if the sender did not
+    request REQUIRETLS.
+
+  * o\bop\bpp\bpo\bor\brt\btu\bun\bni\bis\bst\bti\bic\bc: STARTTLS and REQUIRETLS support are optional. When the
+    sender requests REQUIRETLS, and an SMTP or LMTP server supports STARTTLS
+    and REQUIRETLS, then send REQUIRETLS, otherwise simply deliver the message
+    as if the sender did not request REQUIRETLS.
+
+For a more complete definition of these enforcement levels, see the
+smtp_requiretls_policy parameter documentation.
+
+For sending mail with REQUIRETLS, the relevant Postfix 3.11 default settings
+are shown below, with one suggested setting in a comment (line 2).
+
+The default settings below complete the earlier configuration for message
+stores and content filters, with an 'enforce' policy for external deliveries
+(line 13). You can disable the requiretls_esmtp_header feature (line 4) if a
+configuration does not use content inspection based on SMTPD_PROXY_README or
+FILTER_README.
+
+     1 /etc/postfix/main.cf:
+     2     # smtp_tls_policy_maps = ...dane/sts plugin...
+     3     smtp_tls_security_level = may
+     4     requiretls_esmtp_header = yes
+     5     smtp_requiretls_policy =
+     6         inline:{
+     7             {${domain_to_ascii{$mydomain}} = opportunistic}
+     8             {.${domain_to_ascii{$mydomain}} = opportunistic}
+     9             {localhost = opportunistic} }
+    10         cidr:{
+    11             {0.0.0.0/0 opportunistic}
+    12             {::/0 opportunistic} }
+    13         enforce
+
+  * New at line 13: The 'enforce' policy for external destinations is
+    technically correct, but is likely to suffer from delivery failures because
+    many domains do not publish a DANE or STS policy, and many MTAs support
+    STARTTLS but not REQUIRETLS. A perhaps more practical policy may be found
+    in the section Relaxing REQUIRETLS for external deliveries.
+
+  * (Same as before) Line 3: The requiretls_esmtp_header setting enables
+    support for a message header "Require-TLS-ESMTP: yes" that allows Postfix
+    to propagate the sender's REQUIRETLS request through a content filter. This
+    feature can safely be disabled if there is no need for content inspection
+    based on SMTPD_PROXY_README or FILTER_README.
+
+  * (Same as before) Lines 5-12: These make REQUIRETLS support optional for
+    internal destinations and content filters that are specified as a symbolic
+    name (lines 6-9) or as a numerical IP address (lines 10-12).
+
+  * (Same as before) Lines 7 and 8 use ${domain_to_ascii{$mydomain}} instead of
+    $mydomain. The function domain_to_ascii{} returns $mydomain if that
+    contains only (7-bit) ASCII. If the mydomain value contains non-ASCII
+    characters, then domain_to_ascii{} returns the xn--mumble-mumble Punycode
+    (A-label) form that Postfix needs. This works around a limitation that may
+    be eliminated in a future Postfix version.
+
+  * (Same as before) Note: if you specify a domain list outside main.cf, then
+    the automatic $name expansions and Punycode conversions will not happen;
+    you will need to enter real domain names and will need to convert non-ASCII
+    domains to Punycode.)
+
+R\bRe\bel\bla\bax\bxi\bin\bng\bg R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS f\bfo\bor\br e\bex\bxt\bte\ber\brn\bna\bal\bl d\bde\bel\bli\biv\bve\ber\bri\bie\bes\bs
+
+It may be desirable to make REQUIRETLS work with today's infrastructure, by
+keeping the requirement for TLS, but relaxing the requirements that a remote
+server supports REQUIRETLS and that its server certificate matches a DANE or
+STS policy. The configuration below makes that change by replacing the default
+'enforce' with 'opportunistic+starttls' (line 13).
+
+     1 /etc/postfix/main.cf:
+     2     smtp_tls_security_level = may
+     3     # smtp_tls_policy_maps = ...dane/sts plugin...
+     4     requiretls_esmtp_header = yes
+     5     smtp_requiretls_policy =
+     6         inline:{
+     7             {${domain_to_ascii{$mydomain}} = opportunistic}
+     8             {.${domain_to_ascii{$mydomain}} = opportunistic}
+     9             {localhost = opportunistic} }
+    10         cidr:{
+    11             {0.0.0.0/0 opportunistic}
+    12             {::/0 opportunistic} }
+    13         opportunistic+starttls
+
+  * New at line 13: the 'opportunistic+starttls' policy relaxes the requirement
+    that every MTA in the forward path of a message supports REQUIRETLS, but in
+    practice only one network hop needs to be secured: from a sender's
+    perimeter MTA to a receiver's perimeter MTA. The network connections
+    between user agents and their respective perimeters are assumed to be
+    already secure.
+
+  * (Same as before) Line 3: The requiretls_esmtp_header setting enables
+    support for a message header "Require-TLS-ESMTP: yes" that allows Postfix
+    to propagate the sender's REQUIRETLS request through a content filter. This
+    feature can safely be disabled if there is no need for content inspection
+    based on SMTPD_PROXY_README or FILTER_README.
+
+  * (Same as before) Lines 5-12: These make REQUIRETLS support optional for
+    internal destinations and content filters that are specified as a symbolic
+    name (lines 6-9) or as a numerical IP address (lines 10-12).
+
+  * (Same as before) Lines 7 and 8 use ${domain_to_ascii{$mydomain}} instead of
+    $mydomain. The function domain_to_ascii{} returns $mydomain if that
+    contains only (7-bit) ASCII. If the mydomain value contains non-ASCII
+    characters, then domain_to_ascii{} returns the xn--mumble-mumble Punycode
+    (A-label) form that Postfix needs. This works around a limitation that may
+    be eliminated in a future Postfix version.
+
+  * (Same as before) Note: if you specify a domain list outside main.cf, then
+    the automatic $name expansions and Punycode conversions will not happen;
+    you will need to enter real domain names and will need to convert non-ASCII
+    domains to Punycode.)
+
+A\bAn\bn e\bex\bxp\bpe\ber\bri\bim\bme\ben\bnt\bt:\b: t\bte\bes\bst\bti\bin\bng\bg R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS s\bsu\bup\bpp\bpo\bor\brt\bt
+
+The 'opportunistic' enforcement level may be useful to discover REQUIRETLS
+support globally. The idea is to turn on REQUIRETLS for all outbound mail, and
+watch in Postfix TLS status logging how often delivery is logged as
+"requiretls" (all requirements satisfied), "requiretls:nocertmatch" (no DANE or
+STS policy, or certificate not trusted or not matched), "requiretls:none" (no
+REQUIRETLS support), or "requiretls:nostarttls". For more details on this
+logging format, see smtp_log_tls_feature_status.
+
+R\bRe\beq\bqu\bue\bes\bst\bti\bin\bng\bg R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS w\bwi\bit\bth\bho\bou\but\bt S\bSM\bMT\bTP\bP
+
+There are two options:
+
+  * Specify the Postfix-specific "s\bse\ben\bnd\bdm\bma\bai\bil\bl -\b-O\bOr\bre\beq\bqu\bui\bir\bre\bet\btl\bls\bs=\b=y\bye\bes\bs" command-line
+    option. This option is always available, but may not be convenient to use.
+
+  * Add a Postfix-specific "R\bRe\beq\bqu\bui\bir\bre\be-\b-T\bTL\bLS\bS-\b-E\bES\bSM\bMT\bTP\bP:\b: y\bye\bes\bs" message header. This is
+    easier to use, but requires the setting "requiretls_esmtp_header = yes"
+    which is not recommended for systems without content filters based on
+    SMTPD_PROXY_README or FILTER_README.
+
+    Q\bQu\bue\bes\bst\bti\bio\bon\bn: perhaps there needs to be a parameter setting to request
+    REQUIRETLS for specific email sources or contexts?
+
+N\bNo\bon\bn-\b-d\bde\bel\bli\biv\bve\ber\bry\by n\bno\bot\bti\bif\bfi\bic\bca\bat\bti\bio\bon\bns\bs
+
+By default, Postfix redacts an undeliverable REQUIRETLS message as described in
+RFC 8689, before returning it to the sender:
+
+  * Remove the label "this message needs REQUIRETLS". The purpose is to avoid
+    loss of notifications when a reverse path does not support REQUIRETLS, even
+    though the forward path supported it.
+
+  * Return only the message header, as if the message was received with the RFC
+    3461 DSN option "RET=HDRS". The purpose is to limit the amount of
+    information that may be exposed in plaintext.
+
+The relevant default setting is:
+
+    /etc/postfix/main.cf:
+        requiretls_redact_dsn = yes
+
+When a message was received with a "TLS-Required: no" header, and REQUIRETLS
+was not requested, the "TLS-Required: no" header is copied to the delivery
+status notification.
+
+R\bRE\bEQ\bQU\bUI\bIR\bRE\bET\bTL\bLS\bS q\bqu\bui\bic\bck\bk s\bsu\bum\bmm\bma\bar\bry\by
+
+The REQUIRETLS extension in ESMTP allows a sender to request that a message
+will be sent over connections that are protected with TLS. RFC 8689 defines two
+SMTP features:
+
+  * A message header "TLS-Required: no" that disables TLS enforcement: do not
+    require a server certificate match, and allow falling back to plaintext if
+    TLS is unavailable. This may be useful to report a TLS problem, as
+    described in TLSRPT_README. This feature has lower precedence than
+    REQUIRETLS, and is not discussed further in this document.
+
+  * An ESMTP protocol extension named "REQUIRETLS" that an SMTP server may list
+    in its EHLO response, and that an SMTP client may request in a MAIL FROM
+    command. This extension can be used only in an encrypted session, as
+    illustrated with the fragment below, where C=client and S=server.
+
+    . . .
+    C: STARTTLS
+    S: 220 Ready to start TLS
+    C: EHLO client.example.org
+    S: 250-mail.example.com
+       . . .
+       250 REQUIRETLS
+    C: MAIL FROM:<sender@example.org> REQUIRETLS
+    S: 250 OK
+    . . .
+
+  * RFC 8689 applies equally to message relay [RFC 5321], submission [RFC
+    6409], and the LMTP Local Mail Transfer Protocol [RFC 2033].
+
+  * REQUIRETLS is an end-to-end feature, unlike SMTP which is hop-by-hop. When
+    a sender requests REQUIRETLS, each MTA in the forward path must support
+    REQUIRETLS.
+
+  * Each connection in the forward path must be made to an MX server that has
+    been looked up securely (for example, with DNSSEC or HTTPS).
+
+  * Each server certificate must be verified. To match a server certificate,
+    the Postfix SMTP client needs to use an appropriate policy type:
+
+      o A TLS policy type 'secure' or 'verify', with certificate name matching
+        info. For example, a policy returned by an MTA-STS plugin that looks up
+        certificate matching info using HTTPS;
+
+      o A TLS policy type 'dane-only', which looks up certificate or public-key
+        matching info using DNSSEC. For example, a policy that is returned by a
+        DANE+STS plugin;
+
+      o A TLS policy type 'dane', provided that both the nexthop domain and its
+        MX hosts are in DNSSEC-signed zones, and usable DNSSEC-signed TLSA
+        records are discovered. In other words, the effective TLS policy
+        remains DANE and is not downgraded because the destination lacks DNSSEC
+        and/or usable TLSA records;
+
+      o A TLS policy type 'fingerprint', with digital fingerprints. This is a
+        non-scalable solution for special deployments, mentioned here only for
+        completeness.
+
+  * A message that requires REQUIRETLS must be returned to the sender if any of
+    the above requirements is not satisfied (no STARTTLS support, no secure
+    lookup of MX servers, no trusted or no matching server certificate, or no
+    server that announces REQUIRETLS support).
+
+  * Returning an undeliverable message that requires REQUIRETLS comes with its
+    own challenges: the return path may differ from the forward path, and the
+    return path may not support REQUIRETLS all the way back to the sender, even
+    if the forward path supported REQUIRETLS.
+
+C\bCr\bre\bed\bdi\bit\bts\bs
+
+  * In Postfix 3.10, Wietse Venema refactored SMTPUTF8 support and extended it
+    to propagate REQUIRETLS and "TLS-Required: no" information.
+  * In Postfix 3.11, Wietse added REQUIRETLS support to the Postfix SMTP
+    client; added a "tls=status/requiretls=status" field to the Postfix
+    delivery status logging; added smtp_requiretls_policy support; added
+    support for the "Require-TLS-ESMTP: yes" header to propagate REQUIRETLS
+    through non-Postfix programs, specifically content filters.
+
index 2467af478b120348d6827e5f94c408fcb10552bc..0d3ae794621d8c30935d7a0af4b5a64c60137376 100644 (file)
@@ -320,6 +320,7 @@ $readme_directory/QMQP_README:f:root:-:644:o
 $readme_directory/QSHAPE_README:f:root:-:644
 $readme_directory/RELEASE_NOTES:f:root:-:644
 $readme_directory/RESTRICTION_CLASS_README:f:root:-:644
+$readme_directory/REQUIRETLS_README:f:root:-:644
 $readme_directory/SASL_README:f:root:-:644
 $readme_directory/SCHEDULER_README:f:root:-:644
 $readme_directory/SMTPD_ACCESS_README:f:root:-:644
@@ -383,6 +384,7 @@ $html_directory/POSTSCREEN_README.html:f:root:-:644
 $html_directory/QMQP_README.html:f:root:-:644:o
 $html_directory/QSHAPE_README.html:f:root:-:644
 $html_directory/RESTRICTION_CLASS_README.html:f:root:-:644
+$html_directory/REQUIRETLS_README.html:f:root:-:644
 $html_directory/SASL_README.html:f:root:-:644
 $html_directory/SCHEDULER_README.html:f:root:-:644
 $html_directory/SMTPD_ACCESS_README.html:f:root:-:644
diff --git a/postfix/html/REQUIRETLS_README.html b/postfix/html/REQUIRETLS_README.html
new file mode 100644 (file)
index 0000000..70ab2bd
--- /dev/null
@@ -0,0 +1,553 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "https://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix REQUIRETLS Support</title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix REQUIRETLS Support</h1>
+
+<hr>
+
+<h2> Table of Contents </h2>
+<ul>
+
+<li> <a href="#intro"> Introduction </a> </li>
+<li> <a href="#perimeter"> REQUIRETLS for a perimeter MTA </a>
+<ul>
+<li> <a href="#inbound"> Receiving inbound messages with REQUIRETLS requests </a>
+<li> <a href="#lmtp-smtp"> LMTP and SMTP-based message stores and content filters content filters </a>
+<li> <a href="#non-smtp"> Non-SMTP and non-LMTP content filters </a>
+<li> <a href="#external"> Communication with external servers </a>
+<li> <a href="#relax"> Relaxing REQUIRETLS for external deliveries </a>
+</ul>
+<li> <a href="#census"> An experiment: testing REQUIRETLS support </a>
+<li> <a href="#requesting"> Requesting REQUIRETLS without SMTP </a>
+<li> <a href="#ndrs"> Non-delivery notifications </a> </li>
+<li> <a href="#summary"> REQUIRETLS quick summary </a> </li>
+<li> <a href="#credits"> Credits </a> </li>
+
+</ul>
+
+<h2> <a name="intro"> Introduction </a> </h2>
+
+<p> (For background information, see below for a <a href="#summary">
+REQUIRETLS quick summary</a>.) </p>
+
+<p> This document covers the Postfix default settings for using the
+REQUIRETLS extension. The purpose of these defaults is to make REQUIRETLS
+support usable in an existing environment, with a path towards the
+future. </p>
+
+<p> The main issues with deploying REQUIRETLS are a lack of support in
+existing infrastructure:  </p>
+
+<ul>
+
+<li> <p> REQUIRETLS requires that server certificates are authenticated.
+When email is sent across the Internet, this involves a DANE or MTA-STS
+policy that is published by a mail receiving domain, using DNSSEC or
+HTTPS. At this time, many domains do not publish such a policy. </p>
+
+<li> <p> REQUIRETLS is historically not supported by existing local
+infrastructure such as internal message stores or Postfix content
+filters, and may be over-kill for connections that happen behind a
+perimeter MTA within a trusted internal network. </p>
+
+</ul>
+
+<h2> <a name="perimeter"> REQUIRETLS for a perimeter MTA </a> </h2>
+
+<p> In this text, a perimeter MTA is a mail system that operates
+on the boundary of an administrative domain. It receives email
+messages for the domain, and/or delivers email messages on behalf
+of the domain. </p>
+
+<h3> <a name="inbound"> Receiving inbound messages with REQUIRETLS requests </a> </h3>
+
+<p> Postfix has one global parameter setting that controls REQUIRETLS
+support in all Postfix processes. The default setting is:
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#requiretls_enable">requiretls_enable</a> = yes
+</pre>
+</blockquote>
+
+<p> With this, the Postfix SMTP server will announce REQUIRETLS
+support, and more importantly, will receive messages from senders
+that for some reason request REQUIRETLS support -- messages that
+you would otherwise not receive, assuming that the domain already
+publishes a valid DANE and/or STS policy. </p>
+
+<p> If all you need is to receive messages with REQUIRETLS, and
+you do not insist on enforcing REQUIRETLS when sending or forwarding
+messages, then you can stop reading this document after adding the
+additional settings below. </p>
+
+<blockquote> <p> NOTE: The configuration below may be suitable for
+a personal domain, where the owner can decide what happens with all
+messages. For domains that receive messages for other people, a
+less radical approach may be better, as described in the sections
+that follow.  </p> </blockquote>
+
+<blockquote>
+<pre>
+1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+2     # Don't enforce REQUIRETLS when delivering mail with SMTP or LMTP.
+3     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> = opportunistic
+4     <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a> = opportunistic
+5     
+6     # Don't detect or add a "Require-TLS-ESMTP: yes" header.
+7     <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> = no
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> Lines 3-4: These relax REQUIRETLS enforcement when delivering
+a email to a message store, content filter, or other destination
+that may not support REQUIRETLS. If a server does not support
+STARTTLS or REQUIRETLS, then Postfix will simply deliver the message
+as if the sender did not request REQUIRETLS.
+
+<li> <p> Line 7: The <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> feature enables support
+for a message header "Require-TLS-ESMTP: yes" that allows Postfix
+to propagate the sender's REQUIRETLS request through a content
+filter based on <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or <a href="FILTER_README.html">FILTER_README</a>. This feature
+can safely be disabled if the domain does not need to enforce
+REQUIRETLS while delivering or forwarding messages. </p>
+
+</ul>
+
+<h3> <a name="lmtp-smtp"> LMTP and SMTP-based message stores and content filters </a> </h3>
+
+<p> REQUIRETLS is historically not supported by message stores such
+as Dovecot, and by content filters based on <a href="FILTER_README.html">FILTER_README</a> or
+<a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a>. The settings below allow for that reality, while
+also preparing for future REQUIRETLS support. <p>
+
+<p> The Postfix SMTP (LMTP) client supports a permissive REQUIRETLS
+policy that is suitable for communication with internal message stores
+and content filters based on <a href="FILTER_README.html">FILTER_README</a> or <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a>. </p>
+
+<ul> 
+
+<li> <p> <b>opportunistic</b>: STARTTLS and REQUIRETLS support are
+optional. When the sender requests REQUIRETLS, and an SMTP or LMTP
+server supports STARTTLS and REQUIRETLS, then send REQUIRETLS,
+otherwise simply deliver the message as if the sender did not request
+REQUIRETLS. </p>
+
+</ul>
+
+<p> For a more complete definition of this enforcement level, see
+the <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> parameter documentation. </p>
+
+<p> For REQUIRETLS, the relevant Postfix 3.11 configuration default
+settings are: </p>
+
+<blockquote>
+<pre>
+ 1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2     <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+ 3     <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> = yes
+ 4     <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a> = opportunistic
+ 5     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> =
+ 6         <a href="DATABASE_README.html#types">inline</a>:{
+ 7             {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 8             {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 9             {localhost = opportunistic} }
+10         <a href="cidr_table.5.html">cidr</a>:{
+11             {0.0.0.0/0 opportunistic}
+12             {::/0 opportunistic} }
+13       ...to be completed in section "<a href="#external">Communication with external servers</a>"...
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> Line 3: The <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> setting enables support
+for a message header "Require-TLS-ESMTP: yes" that allows Postfix
+to propagate the sender's REQUIRETLS request through a content
+filter. This feature can safely be disabled if there is no need for
+content inspection based on <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or <a href="FILTER_README.html">FILTER_README</a>.
+</p>
+
+<li> <p> Lines 5-12: These make REQUIRETLS support optional for
+internal destinations and content filters that are specified as a
+symbolic name (lines 6-9) or as a numerical IP address (lines 10-12).
+</p>
+
+<li> <p> Lines 7 and 8 use ${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} instead
+of $<a href="postconf.5.html#mydomain">mydomain</a>. The function <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns $<a href="postconf.5.html#mydomain">mydomain</a> if
+that contains only (7-bit) ASCII. If the <a href="postconf.5.html#mydomain">mydomain</a> value contains
+non-ASCII characters, then <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns the
+<tt>xn--mumble-mumble</tt> Punycode (A-label) form that Postfix
+needs. This works around a limitation that may be eliminated in a
+future Postfix version. </p>
+
+<li> <p>  Note: if you specify a domain list outside <a href="postconf.5.html">main.cf</a>, then
+the automatic $<i>name</i> expansions and Punycode conversions will
+not happen; you will need to enter real domain names and will need
+to convert non-ASCII domains to Punycode. </p>
+
+</ul>
+
+<h3> <a name="non-smtp"> Non-SMTP and non-LMTP content filters </a> </h3>
+
+<p> Postfix <a href="FILTER_README.html">FILTER_README</a> describes content inspection based on a
+pipe-to-command approach. For REQUIRETLS, the relevant Postfix 3.11
+default setting is: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> = yes
+</pre>
+</blockquote>
+
+<p> The <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> feature enables support for a message
+header "Require-TLS-ESMTP: yes" that allows Postfix to propagate the
+sender's REQUIRETLS request through a content filter. This feature can
+safely be disabled if there is no need for content inspection based on
+<a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or <a href="FILTER_README.html">FILTER_README</a>. </p>
+
+<h3> <a name="external"> Communication with external servers </a> </h3>
+
+<p> For communication with external servers, the Postfix SMTP client
+supports multiple enforcement levels: </p>
+
+<ul>
+
+<li> <p> <b>enforce</b>: When the sender requests REQUIRETLS, require
+secure lookup of MX hosts (for example, using DNSSEC or HTTPS),
+require a server certificate match (for example, based on a published
+DANE or STS policy), and require that the remote server supports
+REQUIRETLS. Otherwise return the message as undeliverable. </p> <p>
+NOTE: this is also used implicitly when no REQUIRETLS policy match
+is found. </p>
+
+<li> <p> <b>opportunistic+starttls</b>: When the sender requests
+REQUIRETLS, require that the server supports STARTTLS. Send REQUIRETLS
+if the server supports REQUIRETLS, otherwise simply deliver the
+message as if the sender did not request REQUIRETLS. </p>
+
+<li> <p> <b>opportunistic</b>: STARTTLS and REQUIRETLS support are
+optional. When the sender requests REQUIRETLS, and an SMTP or LMTP
+server supports STARTTLS and REQUIRETLS, then send REQUIRETLS,
+otherwise simply deliver the message as if the sender did not request
+REQUIRETLS. </p>
+
+</ul>
+
+<p> For a more complete definition of these enforcement levels,
+see the <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> parameter documentation. </p>
+
+<p> For sending mail with REQUIRETLS, the relevant Postfix 3.11
+default settings are shown below, with one suggested setting in a
+comment (line 2).
+
+<p> The default settings below complete the earlier configuration
+for <a href="#lmtp-smtp">message stores and content filters</a>,
+with an 'enforce' policy for external deliveries (line 13). You can
+disable the <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> feature (line 4) if a configuration
+does not use content inspection based on <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or
+<a href="FILTER_README.html">FILTER_README</a>. </p>
+
+<blockquote>
+<pre>
+ 1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2     # <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ...dane/sts plugin...
+ 3     <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+ 4     <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> = yes
+ 5     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> =
+ 6         <a href="DATABASE_README.html#types">inline</a>:{
+ 7             {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 8             {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 9             {localhost = opportunistic} }
+10         <a href="cidr_table.5.html">cidr</a>:{
+11             {0.0.0.0/0 opportunistic}
+12             {::/0 opportunistic} }
+13         enforce
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> New at line 13: The 'enforce' policy for external
+destinations is technically correct, but is likely to suffer from
+delivery failures because many domains do not publish a DANE or STS
+policy, and many MTAs support STARTTLS but not REQUIRETLS. A perhaps
+more practical policy may be found in the section <a href="#relax">
+Relaxing REQUIRETLS for external deliveries</a>. </p>
+
+<li> <p> (Same as before) Line 3: The <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> setting
+enables support for a message header "Require-TLS-ESMTP: yes" that
+allows Postfix to propagate the sender's REQUIRETLS request through
+a content filter. This feature can safely be disabled if there is
+no need for content inspection based on <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or
+<a href="FILTER_README.html">FILTER_README</a>.  </p>
+
+<li> <p> (Same as before) Lines 5-12: These make REQUIRETLS support
+optional for internal destinations and content filters that are
+specified as a symbolic name (lines 6-9) or as a numerical IP address
+(lines 10-12).
+
+<li> <p> (Same as before) Lines 7 and 8 use ${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}}
+instead of $<a href="postconf.5.html#mydomain">mydomain</a>. The function <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns $<a href="postconf.5.html#mydomain">mydomain</a>
+if that contains only (7-bit) ASCII. If the <a href="postconf.5.html#mydomain">mydomain</a> value contains
+non-ASCII characters, then <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns the
+<tt>xn--mumble-mumble</tt> Punycode (A-label) form that Postfix
+needs. This works around a limitation that may be eliminated in a
+future Postfix version. </p>
+
+<li> <p> (Same as before) Note: if you specify a domain list outside
+<a href="postconf.5.html">main.cf</a>, then the automatic $<i>name</i> expansions and Punycode
+conversions will not happen; you will need to enter real domain
+names and will need to convert non-ASCII domains to Punycode.) </p>
+
+</ul>
+
+<h3> <a name="relax"> Relaxing REQUIRETLS for external deliveries </a> </h3>
+
+<p> It may be desirable to make REQUIRETLS work with today's
+infrastructure, by keeping the requirement for TLS, but relaxing
+the requirements that a remote server supports REQUIRETLS and that
+its server certificate matches a DANE or STS policy. The configuration
+below makes that change by replacing the default 'enforce' with
+'opportunistic+starttls' (line 13). </p>
+
+<blockquote>
+<pre>
+ 1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2     <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+ 3     # <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ...dane/sts plugin...
+ 4     <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> = yes
+ 5     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> =
+ 6         <a href="DATABASE_README.html#types">inline</a>:{
+ 7             {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 8             {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 9             {localhost = opportunistic} }
+10         <a href="cidr_table.5.html">cidr</a>:{
+11             {0.0.0.0/0 opportunistic}
+12             {::/0 opportunistic} }
+13         opportunistic+starttls
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> New at line 13: the 'opportunistic+starttls' policy relaxes
+the requirement that every MTA in the forward path of a message
+supports REQUIRETLS, but in practice only one network hop needs to
+be secured: from a sender's perimeter MTA to a receiver's perimeter
+MTA. The network connections between user agents and their respective
+perimeters are assumed to be already secure. </p>
+
+<li> <p> (Same as before) Line 3: The <a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> setting
+enables support for a message header "Require-TLS-ESMTP: yes" that
+allows Postfix to propagate the sender's REQUIRETLS request through
+a content filter. This feature can safely be disabled if there is
+no need for content inspection based on <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or
+<a href="FILTER_README.html">FILTER_README</a>.  </p>
+
+<li> <p> (Same as before) Lines 5-12: These make REQUIRETLS support
+optional for internal destinations and content filters that are
+specified as a symbolic name (lines 6-9) or as a numerical IP address
+(lines 10-12).
+
+<li> <p> (Same as before) Lines 7 and 8 use ${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}}
+instead of $<a href="postconf.5.html#mydomain">mydomain</a>. The function <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns $<a href="postconf.5.html#mydomain">mydomain</a>
+if that contains only (7-bit) ASCII. If the <a href="postconf.5.html#mydomain">mydomain</a> value contains
+non-ASCII characters, then <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns the
+<tt>xn--mumble-mumble</tt> Punycode (A-label) form that Postfix
+needs. This works around a limitation that may be eliminated in a
+future Postfix version. </p>
+
+<li> <p> (Same as before) Note: if you specify a domain list outside
+<a href="postconf.5.html">main.cf</a>, then the automatic $<i>name</i> expansions and Punycode
+conversions will not happen; you will need to enter real domain
+names and will need to convert non-ASCII domains to Punycode.) </p>
+
+</ul>
+
+<h2> <a name="census"> An experiment: testing REQUIRETLS support </a> </h2>
+
+<p> The 'opportunistic' enforcement level may be useful to discover
+REQUIRETLS support globally. The idea is to turn on REQUIRETLS for
+all outbound mail, and watch in Postfix TLS status logging how often
+delivery is logged as "requiretls" (all requirements satisfied),
+"requiretls:nocertmatch" (no DANE or STS policy, or certificate not
+trusted or not matched), "requiretls:none" (no REQUIRETLS support),
+or "requiretls:nostarttls". For more details on this logging format,
+see <a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a>.</p>
+
+<h2> <a name="requesting"> Requesting REQUIRETLS without SMTP </a> </h2>
+
+<p> There are two options: </p>
+
+<ul>
+
+<li> <p> Specify the Postfix-specific "<b>sendmail -Orequiretls=yes</b>"
+command-line option. This option is always available, but may not
+be convenient to use. <p>
+
+<li> <p> Add a Postfix-specific "<b>Require-TLS-ESMTP: yes</b>"
+message header. This is easier to use, but requires the setting
+"<a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> = yes" which is not recommended for systems
+without content filters based on <a href="SMTPD_PROXY_README.html">SMTPD_PROXY_README</a> or <a href="FILTER_README.html">FILTER_README</a>.
+</p>
+
+</ul>
+
+<blockquote> <b>Question</b>: perhaps there needs to be a parameter
+setting to request REQUIRETLS for specific email sources or contexts?
+</blockquote>
+
+<h2> <a name="ndrs"> Non-delivery notifications </a> </h2>
+
+<p> By default, Postfix redacts an undeliverable REQUIRETLS message as
+described in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>,  before returning it to the sender: </p>
+
+<ul>
+
+<li> <p> Remove the label "this message needs REQUIRETLS". The
+purpose is to avoid loss of notifications when a reverse path does
+not support REQUIRETLS, even though the forward path supported it.
+</p>
+
+<li> <p> Return only the message header, as if the message was
+received with the <a href="https://tools.ietf.org/html/rfc3461">RFC 3461</a> DSN option "<tt>RET=HDRS</tt>". The
+purpose is to limit the amount of information that may be exposed
+in plaintext. </p>
+
+</ul>
+
+<p> The relevant default setting is: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#requiretls_redact_dsn">requiretls_redact_dsn</a> = yes
+</pre>
+</blockquote>
+
+<p> When a message was received with a "<tt>TLS-Required: no</tt>"
+header, and REQUIRETLS was not requested, the "T<tt>LS-Required:
+no</tt>" header is copied to the delivery status notification. </p>
+
+<h2> <a name="summary"> REQUIRETLS quick summary </a> </h2>
+
+<p> The REQUIRETLS extension in ESMTP allows a sender to request
+that a message will be sent over connections that are protected
+with TLS. <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> defines two SMTP features: </p>
+
+<ul>
+
+<li> <p> A message header "TLS-Required: no" that disables TLS
+enforcement: do not require a server certificate match, and allow
+falling back to plaintext if TLS is unavailable. This may be useful
+to report a TLS problem, as described in <a href="TLSRPT_README.html">TLSRPT_README</a>. This feature
+has lower precedence than REQUIRETLS, and is not discussed further
+in this document. </p>
+
+<li> <p> An ESMTP protocol extension named "REQUIRETLS" that an SMTP
+server may list in its EHLO response, and that an SMTP client may request
+in a MAIL FROM command. This extension can be used only in an encrypted
+session, as illustrated with the fragment below, where <tt>C</tt>=client
+and <tt>S</tt>=server. </p>
+
+<pre>
+. . .
+C: STARTTLS
+S: 220 Ready to start TLS
+C: EHLO client.example.org
+S: 250-mail.example.com 
+   . . .
+   250 REQUIRETLS
+C: MAIL FROM:&lt;sender@example.org&gt; REQUIRETLS
+S: 250 OK
+. . .
+</pre>
+
+<li> <p> <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> applies equally to message relay [<a href="https://tools.ietf.org/html/rfc5321">RFC 5321</a>], submission
+[<a href="https://tools.ietf.org/html/rfc6409">RFC 6409</a>], and the LMTP Local Mail Transfer Protocol [<a href="https://tools.ietf.org/html/rfc2033">RFC 2033</a>]. </p>
+
+<li> <p> REQUIRETLS is an <i>end-to-end</i> feature, unlike SMTP
+which is <i>hop-by-hop</i>. When a sender requests REQUIRETLS, each
+MTA in the forward path must support REQUIRETLS. </p>
+
+<li> <p> Each connection in the forward path must be made to an MX
+server that has been looked up securely (for example, with DNSSEC
+or HTTPS).
+
+<li> <p> Each server certificate must be verified. To match a server
+certificate, the Postfix SMTP client needs to use an appropriate policy
+type: </p>
+
+<ul>
+
+<li> <p> A TLS policy type 'secure' or 'verify', with certificate name
+matching info. For example, a policy returned by an MTA-STS plugin that
+looks up certificate matching info using HTTPS;
+
+<li> <p> A TLS policy type 'dane-only', which looks up certificate or
+public-key matching info using DNSSEC. For example, a policy that is
+returned by a DANE+STS plugin; </p>
+
+<li> <p> A TLS policy type 'dane', provided that both the nexthop
+domain and its MX hosts are in DNSSEC-signed zones, and usable
+DNSSEC-signed TLSA records are discovered.  In other words, the
+effective TLS policy remains DANE and is not downgraded because the
+destination lacks DNSSEC and/or usable TLSA records; </p>
+
+<li> <p> A TLS policy type 'fingerprint', with digital fingerprints.
+This is a non-scalable solution for special deployments, mentioned
+here only for completeness. </p>
+
+</ul>
+
+<li> <p> A message that requires REQUIRETLS must be returned to the
+sender if any of the above requirements is not satisfied (no STARTTLS
+support, no secure lookup of MX servers, no trusted or no matching
+server certificate, or no server that announces REQUIRETLS support).
+</p>
+
+<li> <p> Returning an undeliverable message that requires REQUIRETLS
+comes with its own challenges: the return path may differ from the
+forward path, and the return path may not support REQUIRETLS all
+the way back to the sender, even if the forward path supported
+REQUIRETLS. </p>
+
+</ul>
+
+<h2> <a name="credits"> Credits </a> </h2>
+
+<ul>
+
+<li> In Postfix 3.10, Wietse Venema refactored SMTPUTF8 support and
+extended it to propagate REQUIRETLS and "TLS-Required: no" information.
+
+<li> In Postfix 3.11, Wietse added REQUIRETLS support to the Postfix
+SMTP client; added a "<tt>tls=<i>status</i>/requiretls=<i>status</i></tt>"
+field to the Postfix delivery status logging; added <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>
+support; added support for the "Require-TLS-ESMTP: yes" header to
+propagate REQUIRETLS through non-Postfix programs, specifically
+content filters. </li>
+
+</ul>
+
+</body>
+
+</html>
index 695478942e1e9e645d85d7b519f7551ac0117659..44f4b26087e0c681c47ec2a8c29e22dd2d35fd80 100644 (file)
@@ -172,6 +172,15 @@ BOUNCE(8)                                                            BOUNCE(8)
               Enable  support  for  the  "TLS-Required:  no"  message  header,
               defined in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>.
 
+       Available in Postfix 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_redact_dsn">requiretls_redact_dsn</a> (yes)</b>
+              When sending a delivery status notification for an original mes-
+              sage received with the REQUIRETLS option, do not send the origi-
+              nal   message  body  (as  if  that  message  was  received  with
+              "RET=HDRS") and do not enforce REQUIRETLS (as  if  that  message
+              was received without REQUIRETLS).
+
 <b><a name="files">FILES</a></b>
        /var/spool/postfix/bounce/* non-delivery records
        /var/spool/postfix/defer/* non-delivery records
index bc1a835c04a842186bc0aa84bdd8b99c68222f35..421543134fd6174cd6d3cde340ad0f5c22333679 100644 (file)
@@ -473,29 +473,33 @@ CLEANUP(8)                                                          CLEANUP(8)
               Enable  support  for  the  "TLS-Required:  no"  message  header,
               defined in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>.
 
+       <b><a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> (yes)</b>
+              Record  the  ESMTP  REQUIRETLS  request in a "Require-TLS-ESMTP:
+              yes" message header.
+
 <b><a name="miscellaneous_controls">MISCELLANEOUS CONTROLS</a></b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
               figuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How much time a Postfix daemon process  may  take  to  handle  a
+              How  much  time  a  Postfix  daemon process may take to handle a
               request before it is terminated by a built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The  maximal  number of digits after the decimal point when log-
+              The maximal number of digits after the decimal point  when  log-
               ging delay values.
 
        <b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
-              The time after which the sender receives a copy of  the  message
+              The  time  after which the sender receives a copy of the message
               headers of mail that is still queued.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
-              The  time  limit  for  sending  or receiving information over an
+              The time limit for sending  or  receiving  information  over  an
               internal communication channel.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time that an idle Postfix  daemon  process
+              The  maximum  amount of time that an idle Postfix daemon process
               waits for an incoming connection before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
@@ -506,7 +510,7 @@ CLEANUP(8)                                                          CLEANUP(8)
               The internet hostname of this mail system.
 
        <b><a href="postconf.5.html#myorigin">myorigin</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
-              The  domain  name that locally-posted mail appears to come from,
+              The domain name that locally-posted mail appears to  come  from,
               and that locally posted mail is delivered to.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
@@ -519,21 +523,21 @@ CLEANUP(8)                                                          CLEANUP(8)
               The location of the Postfix top-level queue directory.
 
        <b><a href="postconf.5.html#soft_bounce">soft_bounce</a> (no)</b>
-              Safety net to keep mail queued that would otherwise be  returned
+              Safety  net to keep mail queued that would otherwise be returned
               to the sender.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              A  prefix  that  is  prepended  to  the  process  name in syslog
+              A prefix that  is  prepended  to  the  process  name  in  syslog
               records, so that, for example, "smtpd" becomes "prefix/smtpd".
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#enable_original_recipient">enable_original_recipient</a> (yes)</b>
-              Enable support for  the  original  recipient  address  after  an
-              address  is  rewritten  to a different address (for example with
+              Enable  support  for  the  original  recipient  address after an
+              address is rewritten to a different address  (for  example  with
               aliasing or with canonical mapping).
 
        Available in Postfix 3.3 and later:
@@ -544,14 +548,14 @@ CLEANUP(8)                                                          CLEANUP(8)
        Available in Postfix 3.5 and later:
 
        <b><a href="postconf.5.html#info_log_address_format">info_log_address_format</a> (external)</b>
-              The email address form that will be used  in  non-debug  logging
+              The  email  address  form that will be used in non-debug logging
               (info, warning, etc.).
 
        Available in Postfix 3.9 and later:
 
        <b><a href="postconf.5.html#force_mime_input_conversion">force_mime_input_conversion</a> (no)</b>
-              Convert  body content that claims to be 8-bit into quoted-print-
-              able, before <a href="postconf.5.html#header_checks">header_checks</a>,  <a href="postconf.5.html#body_checks">body_checks</a>,  Milters,  and  before
+              Convert body content that claims to be 8-bit into  quoted-print-
+              able,  before  <a href="postconf.5.html#header_checks">header_checks</a>,  <a href="postconf.5.html#body_checks">body_checks</a>,  Milters, and before
               after-queue content filters.
 
 <b><a name="files">FILES</a></b>
index 695478942e1e9e645d85d7b519f7551ac0117659..44f4b26087e0c681c47ec2a8c29e22dd2d35fd80 100644 (file)
@@ -172,6 +172,15 @@ BOUNCE(8)                                                            BOUNCE(8)
               Enable  support  for  the  "TLS-Required:  no"  message  header,
               defined in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>.
 
+       Available in Postfix 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_redact_dsn">requiretls_redact_dsn</a> (yes)</b>
+              When sending a delivery status notification for an original mes-
+              sage received with the REQUIRETLS option, do not send the origi-
+              nal   message  body  (as  if  that  message  was  received  with
+              "RET=HDRS") and do not enforce REQUIRETLS (as  if  that  message
+              was received without REQUIRETLS).
+
 <b><a name="files">FILES</a></b>
        /var/spool/postfix/bounce/* non-delivery records
        /var/spool/postfix/defer/* non-delivery records
index bbe7794e721a6fd3e632cae140f55ecc641c769b..c9d07330a312e6206e048ebf7a5fe809c670b804 100644 (file)
@@ -51,6 +51,8 @@ configuration examples </a>
 
 <li> <a href="SMTPUTF8_README.html"> SMTPUTF8 Support </a>
 
+<li> <a href="REQUIRETLS_README.html"> REQUIRETLS Support </a>
+
 <li> <a href="MAILLOG_README.html"> Postfix logging to file or stdout </a>
 
 <li> <a href="COMPATIBILITY_README.html"> Backwards-Compatibility Safety Net</a>
index 37a089ab1f389f5ecc6854db2d1ba05c0f1211f7..a0bebb0e376f074db46fe3046c3eb9e8e543aa8a 100644 (file)
@@ -174,7 +174,7 @@ SMTP(8)                                                                SMTP(8)
        <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a> (Internationalized SMTP)
        <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a> (Internationalized Delivery Status Notifications)
        <a href="https://tools.ietf.org/html/rfc7672">RFC 7672</a> (SMTP security via opportunistic DANE TLS)
-       <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> (TLS-Required message header)
+       <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> (SMTP REQUIRETLS extension, TLS-Required header)
 
 <b><a name="diagnostics">DIAGNOSTICS</a></b>
        Problems  and  transactions  are  logged  to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>.
@@ -507,7 +507,7 @@ SMTP(8)                                                                SMTP(8)
               The delimiter between username and password in  sasl_passwd_maps
               lookup results.
 
-<b><a name="starttls_support_controls">STARTTLS SUPPORT CONTROLS</a></b>
+<b><a name="tls_support_controls">TLS SUPPORT CONTROLS</a></b>
        Detailed  information  about STARTTLS configuration may be found in the
        <a href="TLS_README.html">TLS_README</a> document.
 
@@ -773,41 +773,56 @@ SMTP(8)                                                                SMTP(8)
               an  MX host only if its name matches any STS policy MX host pat-
               tern, and match the server certificate against the MX  hostname.
 
-<b><a name="obsolete_starttls_controls">OBSOLETE STARTTLS CONTROLS</a></b>
-       The  following  configuration  parameters  exist for compatibility with
-       Postfix versions before 2.3. Support for these will  be  removed  in  a
+       Available in Postfix version 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_enable">requiretls_enable</a> (yes)</b>
+              Enable  support  for  the  ESMTP  verb "REQUIRETLS" in the "MAIL
+              FROM" command.
+
+       <b><a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> (see 'postconf -d  <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>'  out-</b>
+       <b>put)</b>
+              How the Postfix SMTP and LMTP client will enforce REQUIRETLS for
+              messages received with the REQUIRETLS option.
+
+       <b><a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a> (yes)</b>
+              Enable  logging  of  TLS  feature information in delivery status
+              logging.
+
+<b><a name="obsolete_tls_controls">OBSOLETE TLS CONTROLS</a></b>
+       The following configuration parameters  exist  for  compatibility  with
+       Postfix  versions  before  2.3.  Support for these will be removed in a
        future release.
 
        <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic  mode: use TLS when a remote SMTP server announces
+              Opportunistic mode: use TLS when a remote SMTP server  announces
               STARTTLS support, otherwise send the mail in the clear.
 
        <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement mode: require  that  remote  SMTP  servers  use  TLS
+              Enforcement  mode:  require  that  remote  SMTP  servers use TLS
               encryption, and never send mail in the clear.
 
        <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              With  mandatory  TLS  encryption,  require  that the remote SMTP
-              server hostname matches  the  information  in  the  remote  SMTP
+              With mandatory TLS encryption,  require  that  the  remote  SMTP
+              server  hostname  matches  the  information  in  the remote SMTP
               server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
-              Optional  lookup  tables  with the Postfix SMTP client TLS usage
-              policy by next-hop destination and by remote SMTP  server  host-
+              Optional lookup tables with the Postfix SMTP  client  TLS  usage
+              policy  by  next-hop destination and by remote SMTP server host-
               name.
 
        <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
-              Obsolete  Postfix  &lt; 2.3 control for the Postfix SMTP client TLS
+              Obsolete Postfix &lt; 2.3 control for the Postfix SMTP  client  TLS
               cipher list.
 
 <b><a name="resource_and_rate_controls">RESOURCE AND RATE CONTROLS</a></b>
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
-              The Postfix SMTP client time limit for completing a TCP  connec-
+              The  Postfix SMTP client time limit for completing a TCP connec-
               tion, or zero (use the operating system built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the HELO or EHLO
-              command, and  for  receiving  the  initial  remote  SMTP  server
+              The Postfix SMTP client time limit for sending the HELO or  EHLO
+              command,  and  for  receiving  the  initial  remote  SMTP server
               response.
 
        <b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
@@ -819,19 +834,19 @@ SMTP(8)                                                                SMTP(8)
               mand, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The  Postfix  SMTP  client  time limit for sending the MAIL FROM
+              The Postfix SMTP client time limit for  sending  the  MAIL  FROM
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The Postfix SMTP client time limit for sending the SMTP RCPT  TO
+              The  Postfix SMTP client time limit for sending the SMTP RCPT TO
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The  Postfix  SMTP  client  time limit for sending the SMTP DATA
+              The Postfix SMTP client time limit for  sending  the  SMTP  DATA
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The Postfix SMTP client time limit for sending the SMTP  message
+              The  Postfix SMTP client time limit for sending the SMTP message
               content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
@@ -845,13 +860,13 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
-              The  maximal number of MX (mail exchanger) IP addresses that can
-              result from Postfix SMTP client mail exchanger lookups, or  zero
+              The maximal number of MX (mail exchanger) IP addresses that  can
+              result  from Postfix SMTP client mail exchanger lookups, or zero
               (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The  maximal number of SMTP sessions per delivery request before
-              the Postfix SMTP client gives up  or  delivers  to  a  fall-back
+              The maximal number of SMTP sessions per delivery request  before
+              the  Postfix  SMTP  client  gives  up or delivers to a fall-back
               <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
@@ -861,17 +876,17 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.2 and earlier:
 
        <b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
-              Keep Postfix LMTP client connections open for  up  to  $<a href="postconf.5.html#max_idle">max_idle</a>
+              Keep  Postfix  LMTP  client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
               seconds.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
-              Permanently  enable  SMTP  connection  caching for the specified
+              Permanently enable SMTP connection  caching  for  the  specified
               destinations.
 
        <b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
-              Temporarily enable SMTP connection caching while  a  destination
+              Temporarily  enable  SMTP connection caching while a destination
               has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
 
        <b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
@@ -885,23 +900,23 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
-              Time  limit for connection cache connect, send or receive opera-
+              Time limit for connection cache connect, send or receive  opera-
               tions.
 
        Available in Postfix version 2.9 - 3.6:
 
        <b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
-              Change the behavior of the smtp_*_timeout time  limits,  from  a
-              time  limit  per  read  or write system call, to a time limit to
-              send or receive a complete record (an SMTP  command  line,  SMTP
-              response  line,  SMTP message content line, or TLS protocol mes-
+              Change  the  behavior  of the smtp_*_timeout time limits, from a
+              time limit per read or write system call, to  a  time  limit  to
+              send  or  receive  a complete record (an SMTP command line, SMTP
+              response line, SMTP message content line, or TLS  protocol  mes-
               sage).
 
        Available in Postfix version 2.11 and later:
 
        <b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
-              When SMTP connection caching is enabled,  the  number  of  times
-              that  an SMTP session may be reused before it is closed, or zero
+              When  SMTP  connection  caching  is enabled, the number of times
+              that an SMTP session may be reused before it is closed, or  zero
               (no limit).
 
        Available in Postfix version 3.4 and later:
@@ -912,13 +927,13 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 3.7 and later:
 
        <b><a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a> (no)</b>
-              Change the behavior of the smtp_*_timeout time  limits,  from  a
-              time  limit  per  plaintext or TLS read or write call, to a com-
-              bined time limit for sending a complete  SMTP  request  and  for
+              Change  the  behavior  of the smtp_*_timeout time limits, from a
+              time limit per plaintext or TLS read or write call,  to  a  com-
+              bined  time  limit  for  sending a complete SMTP request and for
               receiving a complete SMTP response.
 
        <b><a href="postconf.5.html#smtp_min_data_rate">smtp_min_data_rate</a> (500)</b>
-              The  minimum  plaintext  data  transfer rate in bytes/second for
+              The minimum plaintext data transfer  rate  in  bytes/second  for
               DATA    requests,    when    deadlines    are    enabled    with
               <a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a>.
 
@@ -926,54 +941,54 @@ SMTP(8)                                                                SMTP(8)
 
        <b><a href="postconf.5.html#transport_destination_concurrency_limit">transport_destination_concurrency_limit</a>   ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
-              A  transport-specific  override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
+              A transport-specific override for  the  <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
               <a href="postconf.5.html#default_destination_concurrency_limit">currency_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
               name of the message delivery transport.
 
        <b><a href="postconf.5.html#transport_destination_recipient_limit">transport_destination_recipient_limit</a>     ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipi</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">ent_limit</a>)</b>
               A transport-specific override for the <a href="postconf.5.html#default_destination_recipient_limit">default_destination_recip</a>-
-              <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where  <i>transport</i>  is  the  <a href="master.5.html">master.cf</a>
+              <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a>  parameter  value,  where  <i>transport</i> is the <a href="master.5.html">master.cf</a>
               name of the message delivery transport.
 
 <b><a name="smtputf8_controls">SMTPUTF8 CONTROLS</a></b>
        Preliminary SMTPUTF8 support is introduced with Postfix 3.0.
 
        <b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
-              Enable  preliminary SMTPUTF8 support for the protocols described
+              Enable preliminary SMTPUTF8 support for the protocols  described
               in <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a>, <a href="https://tools.ietf.org/html/rfc6532">RFC 6532</a>, and <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a>.
 
        <b><a href="postconf.5.html#smtputf8_autodetect_classes">smtputf8_autodetect_classes</a> (sendmail, verify)</b>
-              Detect that a message requires SMTPUTF8 support for  the  speci-
+              Detect  that  a message requires SMTPUTF8 support for the speci-
               fied mail origin classes.
 
        Available in Postfix version 3.2 and later:
 
        <b><a href="postconf.5.html#enable_idna2003_compatibility">enable_idna2003_compatibility</a> (no)</b>
-              Enable   'transitional'   compatibility   between  IDNA2003  and
-              IDNA2008, when converting UTF-8 domain names to/from  the  ASCII
+              Enable  'transitional'  compatibility   between   IDNA2003   and
+              IDNA2008,  when  converting UTF-8 domain names to/from the ASCII
               form that is used for DNS lookups.
 
 <b><a name="trouble_shooting_controls">TROUBLE SHOOTING CONTROLS</a></b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The  increment  in verbose logging level when a nexthop destina-
-              tion, remote client or server name or network address matches  a
+              The increment in verbose logging level when a  nexthop  destina-
+              tion,  remote client or server name or network address matches a
               pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional  list  of  nexthop destination, remote client or server
-              name or network address patterns that,  if  matched,  cause  the
-              verbose  logging  level  to  increase by the amount specified in
+              Optional list of nexthop destination, remote  client  or  server
+              name  or  network  address  patterns that, if matched, cause the
+              verbose logging level to increase by  the  amount  specified  in
               $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The recipient of postmaster notifications  about  mail  delivery
+              The  recipient  of  postmaster notifications about mail delivery
               problems that are caused by policy, resource, software or proto-
               col errors.
 
        <b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
-              What  categories  of  Postfix-generated  mail  are  subject   to
-              before-queue    content    inspection    by   <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
+              What   categories  of  Postfix-generated  mail  are  subject  to
+              before-queue   content    inspection    by    <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
               <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
 
        <b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
@@ -981,46 +996,46 @@ SMTP(8)                                                                SMTP(8)
 
 <b><a name="miscellaneous_controls">MISCELLANEOUS CONTROLS</a></b>
        <b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
-              Where the Postfix  SMTP  client  should  deliver  mail  when  it
+              Where  the  Postfix  SMTP  client  should  deliver  mail when it
               detects a "mail loops back to myself" error condition.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
               figuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How much time a Postfix daemon process  may  take  to  handle  a
+              How  much  time  a  Postfix  daemon process may take to handle a
               request before it is terminated by a built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The  maximal  number of digits after the decimal point when log-
+              The maximal number of digits after the decimal point  when  log-
               ging delay values.
 
        <b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
               Disable DNS lookups in the Postfix SMTP and LMTP clients.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
-              The local network interface  addresses  that  this  mail  system
+              The  local  network  interface  addresses  that this mail system
               receives mail on.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (see 'postconf -d' output)</b>
-              The  Internet  protocols Postfix will attempt to use when making
+              The Internet protocols Postfix will attempt to use  when  making
               or accepting connections.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
-              The time limit for sending  or  receiving  information  over  an
+              The  time  limit  for  sending  or receiving information over an
               internal communication channel.
 
        <b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
-              When  a remote LMTP server announces no DSN support, assume that
-              the server performs final delivery, and send "delivered"  deliv-
+              When a remote LMTP server announces no DSN support, assume  that
+              the  server performs final delivery, and send "delivered" deliv-
               ery status notifications instead of "relayed".
 
        <b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
               The default TCP port that the Postfix LMTP client connects to.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The  maximum  amount of time that an idle Postfix daemon process
+              The maximum amount of time that an idle Postfix  daemon  process
               waits for an incoming connection before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
@@ -1034,21 +1049,21 @@ SMTP(8)                                                                SMTP(8)
               The process name of a Postfix command or daemon process.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
-              The remote network interface addresses  that  this  mail  system
-              receives  mail  on by way of a proxy or network address transla-
+              The  remote  network  interface  addresses that this mail system
+              receives mail on by way of a proxy or network  address  transla-
               tion unit.
 
        <b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
               The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
-              client  will  try  first,  when  a destination has IPv6 and IPv4
+              client will try first, when a  destination  has  IPv6  and  IPv4
               addresses with equal MX preference.
 
        <b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
-              An optional numerical network  address  that  the  Postfix  SMTP
+              An  optional  numerical  network  address  that the Postfix SMTP
               client should bind to when making an IPv4 connection.
 
        <b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
-              An  optional  numerical  network  address  that the Postfix SMTP
+              An optional numerical network  address  that  the  Postfix  SMTP
               client should bind to when making an IPv6 connection.
 
        <b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
@@ -1068,7 +1083,7 @@ SMTP(8)                                                                SMTP(8)
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              A  prefix  that  is  prepended  to  the  process  name in syslog
+              A prefix that  is  prepended  to  the  process  name  in  syslog
               records, so that, for example, "smtpd" becomes "prefix/smtpd".
 
        Available with Postfix 2.2 and earlier:
@@ -1080,14 +1095,14 @@ SMTP(8)                                                                SMTP(8)
        Available with Postfix 2.3 and later:
 
        <b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
-              Optional  list  of  relay destinations that will be used when an
-              SMTP destination is not found, or when delivery fails due  to  a
+              Optional list of relay destinations that will be  used  when  an
+              SMTP  destination  is not found, or when delivery fails due to a
               non-permanent error.
 
        Available with Postfix 3.0 and later:
 
        <b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
-              In  the context of email address verification, the SMTP protocol
+              In the context of email address verification, the SMTP  protocol
               stage that determines whether an email address is deliverable.
 
        Available with Postfix 3.1 and later:
@@ -1109,7 +1124,7 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix 3.7 and later:
 
        <b><a href="postconf.5.html#smtp_bind_address_enforce">smtp_bind_address_enforce</a> (no)</b>
-              Defer  delivery  when  the  Postfix SMTP client cannot apply the
+              Defer delivery when the Postfix SMTP  client  cannot  apply  the
               <a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> or <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> setting.
 
 <b><a name="see_also">SEE ALSO</a></b>
index 4645b733aa92da0bd14758ceab565dabf9f18ddf..ef7344b09b8dee7fc3a3f14098452ab0605415dd 100644 (file)
@@ -177,14 +177,36 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        <b>-n</b> (ignored)
               Backwards compatibility.
 
-       <b>-oA</b><i>alias</i><b>_</b><i>database</i>
-              Non-default alias database. Specify <i>pathname</i>  or  <i>type</i>:<i>pathname</i>.
-              See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+       <b>-O requiretls=yes</b>
+
+       <b>-O requiretls=no</b>
+              When delivering a message to an SMTP or LMTP server, the connec-
+              tion  must  use TLS with a verified server certificate, and that
+              server must support REQUIRETLS. The "requiretls" name and option
+              value are case-insensitive. REQUIRETLS enforcement is controlled
+              with    the    configuration    parameters    <a href="postconf.5.html#requiretls_enable">requiretls_enable</a>,
+              <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>, and <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a>.
+
+              This feature is available in Postfix 3.11 and later.
+
+       <b>-O smtputf8=yes</b>
+
+       <b>-O smtputf8=no</b>
+              When  delivering  a  message  to  an SMTP or LMTP server, and an
+              envelope address or message  header  contains  UTF8  text,  that
+              server  must  support  SMTPUTF8.  The "smtputf8" option name and
+              value are case-insensitive.
+
+              This feature is available in Postfix 3.11 and later.
 
        <b>-O</b> <i>option=value</i> (ignored)
-              Set  the named <i>option</i> to <i>value</i>. Use the equivalent configuration
+              Set the named <i>option</i> to <i>value</i>. Use the equivalent  configuration
               parameter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
 
+       <b>-oA</b><i>alias</i><b>_</b><i>database</i>
+              Non-default  alias  database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
+              See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+
        <b>-o7</b> (ignored)
 
        <b>-o8</b> (ignored)
@@ -483,6 +505,12 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               the  default  Postfix  instance,  and that are started, stopped,
               etc., together with the default Postfix instance.
 
+       Postfix 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_enable">requiretls_enable</a> (yes)</b>
+              Enable support for the ESMTP  verb  "REQUIRETLS"  in  the  "MAIL
+              FROM" command.
+
 <b><a name="files">FILES</a></b>
        /var/spool/postfix, mail queue
        /etc/postfix, configuration files
@@ -501,7 +529,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        syslogd(8), system logging
 
 <b><a name="readme_files">README_FILES</a></b>
-       Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to  locate
+       Use  "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
        this information.
        <a href="DEBUG_README.html">DEBUG_README</a>, Postfix debugging howto
        <a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
index 4645b733aa92da0bd14758ceab565dabf9f18ddf..ef7344b09b8dee7fc3a3f14098452ab0605415dd 100644 (file)
@@ -177,14 +177,36 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        <b>-n</b> (ignored)
               Backwards compatibility.
 
-       <b>-oA</b><i>alias</i><b>_</b><i>database</i>
-              Non-default alias database. Specify <i>pathname</i>  or  <i>type</i>:<i>pathname</i>.
-              See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+       <b>-O requiretls=yes</b>
+
+       <b>-O requiretls=no</b>
+              When delivering a message to an SMTP or LMTP server, the connec-
+              tion  must  use TLS with a verified server certificate, and that
+              server must support REQUIRETLS. The "requiretls" name and option
+              value are case-insensitive. REQUIRETLS enforcement is controlled
+              with    the    configuration    parameters    <a href="postconf.5.html#requiretls_enable">requiretls_enable</a>,
+              <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>, and <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a>.
+
+              This feature is available in Postfix 3.11 and later.
+
+       <b>-O smtputf8=yes</b>
+
+       <b>-O smtputf8=no</b>
+              When  delivering  a  message  to  an SMTP or LMTP server, and an
+              envelope address or message  header  contains  UTF8  text,  that
+              server  must  support  SMTPUTF8.  The "smtputf8" option name and
+              value are case-insensitive.
+
+              This feature is available in Postfix 3.11 and later.
 
        <b>-O</b> <i>option=value</i> (ignored)
-              Set  the named <i>option</i> to <i>value</i>. Use the equivalent configuration
+              Set the named <i>option</i> to <i>value</i>. Use the equivalent  configuration
               parameter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
 
+       <b>-oA</b><i>alias</i><b>_</b><i>database</i>
+              Non-default  alias  database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
+              See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+
        <b>-o7</b> (ignored)
 
        <b>-o8</b> (ignored)
@@ -483,6 +505,12 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               the  default  Postfix  instance,  and that are started, stopped,
               etc., together with the default Postfix instance.
 
+       Postfix 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_enable">requiretls_enable</a> (yes)</b>
+              Enable support for the ESMTP  verb  "REQUIRETLS"  in  the  "MAIL
+              FROM" command.
+
 <b><a name="files">FILES</a></b>
        /var/spool/postfix, mail queue
        /etc/postfix, configuration files
@@ -501,7 +529,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        syslogd(8), system logging
 
 <b><a name="readme_files">README_FILES</a></b>
-       Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to  locate
+       Use  "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
        this information.
        <a href="DEBUG_README.html">DEBUG_README</a>, Postfix debugging howto
        <a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
index c6a02d86c83e8048a60f74abedbffc14c5badfa0..516becd144b479bb5c11245e63a5fc8652684761 100644 (file)
@@ -12,7 +12,7 @@
 
 <script type="text/javascript">
 
-// Kludge for https://support.google.com/chrome/thread/11993079
+// Kludge for <a href="https://support.google.com/chrome/thread/11993079">https://support.google.com/chrome/thread/11993079</a>
 const isChrome = /Chrome/.test(navigator.userAgent)
     && /Google Inc/.test(navigator.vendor);
 const hash = window.location.hash;
@@ -35,12 +35,12 @@ if (hash && isChrome) {
 
 <h2> Postfix main.cf file format </h2>
 
-<p> The Postfix main.cf configuration file specifies a very small
+<p> The Postfix <a href="postconf.5.html">main.cf</a> configuration file specifies a very small
 subset of all the parameters that control the operation of the
 Postfix mail system. Parameters not explicitly specified are left
 at their default values. </p>
 
-<p> The general format of the main.cf file is as follows: </p>
+<p> The general format of the <a href="postconf.5.html">main.cf</a> file is as follows: </p>
 
 <ul>
 
@@ -54,7 +54,7 @@ lines whose first non-whitespace character is a `#'. </p>
 <li> <p> A logical line starts with non-whitespace text. A line
 that starts with whitespace continues a logical line. </p>
 
-<li> <p> A parameter value may refer to other parameters. </p>
+<li> <p> A parameter value may refer to functions or other parameters. </p>
 
 <ul>
 
@@ -89,6 +89,11 @@ and "&gt;". The comparison is numerical when both operands are all
 digits, otherwise the comparison is lexicographical. These forms
 are supported with Postfix versions &ge; 3.0. </p>
 
+<li> <p> The expression "${name{value}}" is replaced with the result
+from calling the function <i>name</i> with the argument <i>value</i>
+after stripping whitespace betwen the "{", the value, and the "}".
+An example is the <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} function. </p>
+
 <li> <p> Each "value" is subject to recursive named parameter and
 relational expression evaluation, except where noted.  </p>
 
@@ -104,7 +109,7 @@ form "${...}". </p>
 <li> <p> When the same parameter is defined multiple times, only
 the last instance is remembered. </p>
 
-<li> <p> Otherwise, the order of main.cf parameter definitions does
+<li> <p> Otherwise, the order of <a href="postconf.5.html">main.cf</a> parameter definitions does
 not matter. </p>
 
 </ul>
@@ -3247,6 +3252,57 @@ value to disable the feature. </p>
 to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21. </p>
 
 
+</DD>
+
+<DT><b><a name="domain_to_ascii">domain_to_ascii</a>
+(default: no default)</b></DT><DD>
+
+<p> A function that returns the ASCII representation of its domain
+name argument. If the argument contains only (7-bit) ASCII characters
+(A-label form), then <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} will return the same value.
+If the argument contains a valid non-ASCII domain name (U-label
+form), then <a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} will return the corresponding Punycode
+(A-label form). Other argument values may result in a program
+start-up error. </p>
+
+<p> Examples: </p>
+
+<ul>
+
+<li> <p> The examples below assume that <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>
+contains the default setting: </p>
+<pre>
+<a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> = <a href="DATABASE_README.html#types">inline</a>:{
+    {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic },
+    {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic },
+     ... }
+</pre>
+
+<li> <p> Example 1: when <a href="postconf.5.html#mydomain">mydomain</a> contains only ASCII characters,
+<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns the same value. </p>
+<pre>
+# postconf -o '<a href="postconf.5.html#mydomain">mydomain</a>=foo.example' -x <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>
+<a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> = <a href="DATABASE_README.html#types">inline</a>:{
+    {foo.example = opportunistic},
+    {.foo.example = opportunistic},
+     ... }
+</pre>
+
+<li> <p> Example 2: when <a href="postconf.5.html#mydomain">mydomain</a> contains non-ASCII characters,
+<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{} returns the Punycode representation. </p>
+<pre>
+# postconf -o '<a href="postconf.5.html#mydomain">mydomain</a>=Ï€.example' -x <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>
+<a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> = <a href="DATABASE_README.html#types">inline</a>:{
+    {xn--1xa.example = opportunistic},
+    {.xn--1xa.example = opportunistic},
+    ... }
+</pre>
+
+</ul>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+
 </DD>
 
 <DT><b><a name="dont_remove">dont_remove</a>
@@ -5171,6 +5227,17 @@ configuration parameter.  See there for details. </p>
 <p> This feature is available in Postfix 2.3 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_log_tls_feature_status">lmtp_log_tls_feature_status</a>
+(default: yes)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_mail_timeout">lmtp_mail_timeout</a>
@@ -5373,6 +5440,17 @@ configuration parameter.  See there for details. </p>
 <p> This feature is available in Postfix 2.7 and later. </p>
 
 
+</DD>
+
+<DT><b><a name="lmtp_requiretls_policy">lmtp_requiretls_policy</a>
+(default: opportunistic)</b></DT><DD>
+
+<p> The LMTP-specific version of the <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+
 </DD>
 
 <DT><b><a name="lmtp_rset_timeout">lmtp_rset_timeout</a>
@@ -10647,6 +10725,93 @@ the mail server (IMPORTING HOME DIRECTORIES IS NOT RECOMMENDED).
 </p>
 
 
+</DD>
+
+<DT><b><a name="requiretls_enable">requiretls_enable</a>
+(default: yes)</b></DT><DD>
+
+<p> Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+FROM" command. As defined in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>, when a message specifies
+REQUIRETLS: </p>
+
+<ul>
+
+<li> <p> deliveries with SMTP or LMTP must use a TLS connection, </p>
+
+<li> <p> to a securely looked up MX server (e.g., DNSSEC or MTA-STS),
+</p>
+
+<li> <p> with a matched server certificate (Postfix SMTP or LMTP
+client TLS security levels "secure", "verify", "fingerprint",
+dane-only, or opportunistic "dane"), </p>
+
+<li> <p> and the server must announce "REQUIRETLS" support after
+the STARTTLS handshake. </p>
+
+</ul>
+
+<p> When delivering a message that specifies REQUIRETLS, the Postfix
+SMTP client will try one or more servers, limited by the
+<a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> and <a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> parameters, until
+it finds an MX server that satisfies the above requirements. If
+such a server is not found, the Postfix SMTP or LMTP client returns
+the message as undeliverable. </p>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> With the Postfix SMTP or LMTP clients, REQUIRETLS enforcement
+is controlled with <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> or <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a>. It
+is initially not enforced for deliveries to local servers, including
+LMTP message stores and local content filters. </p>
+
+<li> <p> The ESMTP REQUIRETLS option overrides the "TLS-Required:
+no" message header. </p>
+
+</ul>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+
+</DD>
+
+<DT><b><a name="requiretls_esmtp_header">requiretls_esmtp_header</a>
+(default: yes)</b></DT><DD>
+
+<p> Record the ESMTP REQUIRETLS request in a "Require-TLS-ESMTP:
+yes" message header. This is needed to propagate the REQUIRETLS
+request through an external content filter that is configured with
+<a href="postconf.5.html#smtpd_proxy_filter">smtpd_proxy_filter</a>, with <a href="postconf.5.html#content_filter">content_filter</a>, or with a FILTER action in
+an SMTPD <a href="access.5.html">access(5)</a> table, <a href="header_checks.5.html">header_checks(5)</a> or <a href="header_checks.5.html">body_checks(5)</a>. The
+header is not needed with Milter-based content filters. </p>
+
+<p> This header will be visible to remote and local recipients.  It
+can safely be disabled if a configuration does not use any of the
+above content filters that need the header. </p>
+
+<p> This feature is available in Postfix 3.11 and later. </p>
+
+
+</DD>
+
+<DT><b><a name="requiretls_redact_dsn">requiretls_redact_dsn</a>
+(default: yes)</b></DT><DD>
+
+<p> When sending a delivery status notification for an original
+message received with the REQUIRETLS option, do not send the original
+message body (as if that message was received with "RET=HDRS") and
+do not enforce REQUIRETLS (as if that message was received without
+REQUIRETLS). For a detailed discussion see <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> section 5. </p>
+
+<p> Note: the 'reverse' path for sending a delivery status notification
+may differ from the 'forward' path for receiving the original message.
+Not every hop in the reverse path may support REQUIRETLS, even
+though every hop in the forward path supported it. The setting
+"<a href="postconf.5.html#requiretls_redact_dsn">requiretls_redact_dsn</a> = no" may therefore result in the loss of a
+delivery status notification. </p>
+
+
 </DD>
 
 <DT><b><a name="reset_owner_alias">reset_owner_alias</a>
@@ -12177,6 +12342,127 @@ and earlier.
 </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_log_tls_feature_status">smtp_log_tls_feature_status</a>
+(default: yes)</b></DT><DD>
+
+<p> Enable logging of TLS feature information in delivery status
+logging. This summarizes how features such as TLS and REQUIRETLS
+were used. A list of examples is at the end of this text. </p>
+
+<ul>
+
+<li> <p> The logging is inserted between the "<tt>delays=a/b/c/d</tt>"
+and the "<tt>dsn=x.y.z, status=...</tt>" information. </p>
+
+<li> <p> The general format is "<tt>tls=feature/feature/...</tt>".
+See below for examples. </p>
+
+<li> <p> The first feature name is the TLS security level:
+'<tt>none</tt>', '<tt>may</tt>', '<tt>encrypt</tt>', etc.  Other
+features are shown only if activated. The REQUIRETLS extension's
+feature name is '<tt>requiretls</tt>'. </p>
+
+<li> <p> When a '<tt>:<i>downgrade-level</i></tt>' is appended to
+a feature name, the feature was downgraded to the indicated level.
+A downgrade-level <tt><i>none</i></tt> indicates that the feature
+was unavailable. The result of a downgrade may violate a strict
+policy, but may still be compliant with a permissive policy. </p>
+
+<li> <p> When "<tt>!</tt>" is prepended to a feature, the policy
+for that feature was violated and the connection was not used.
+</p>
+
+<li> <p> When "?" is appended to a feature, the policy for that
+feature was undecided. This is typically the result of a lost
+connection or an incorrect configuration. </p>
+
+</ul>
+
+<p> Examples for TLS security levels: </p>
+
+<dl>
+
+<dt> tls=none </dt> <dd> A connection that did not use TLS. </dd>
+
+<dt> tls=may </dt> <dd> Opportunistic TLS. The connection was used
+after a successful TLS handshake. </dd>
+
+<dt> tls=may? </dt> <dd> Opportunistic TLS. The status was undecided
+because no connection was made, or no server could be reached. </dd>
+
+<dt> tls=may:none </dt> <dd> The client was willing to use TLS, but
+the remote server did not support STARTTLS, and the connection was
+used in plaintext as permitted by the opportunistic TLS policy. </dd>
+
+<dt> tls=dane </dt> <dd> DANE policy compliant, no downgrade. </dd>
+
+<dt> tls=(dane:halfdane) </dt> <dd> Opportunistic DANE. The connection
+security was downgraded to '<tt>halfdane</tt>' to indicate that
+mail server records were not DNSSEC-signed. </dd>
+
+<dt> tls=dane:encrypt </dt> <dd> Opportunistic DANE. The connection
+security was downgraded to '<tt>encrypt</tt>' because none of the TLSA
+records were usable. </dd>
+
+<dt> tls=dane? </dt> <dd> DANE policy status was undecided, because the
+connection failed before or in the TLS handshake. </dd>
+
+</dl>
+
+<p> Examples for REQUIRETLS policies (set with <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>),
+where "<tt><i>xxx</i></tt>" is a TLS security level: </p>
+
+<dl>
+
+<dt> tls=xxx/requiretls </dt> <dd> 'Enforce' policy compliant.
+After a successful TLS handshake that required a certificate match,
+the remote server announced REQUIRETLS support, and the client sent
+REQUIRETLS.
+
+<dt> tls=xxx/!requiretls:nocertmatch </dt> <dd> 'Enforce' policy
+violation. The connection was not used because the remote server
+certificate did not match as required by the TLS security policy,
+or no connection was made because the TLS security policy disabled
+server certificate matching. </dd>
+
+<dt> tls=xxx/requiretls:nocertmatch </dt> <dd> 'Opportunistic+starttls'
+or 'opportunistic' policy compliant. After a successful TLS handshake
+that did not require a server certificate match, the remote server
+announced REQUIRETLS support, and the client sent REQUIRETLS. </dd>
+
+<dt> tls=xxx/!requiretls:nostarttls </dt> <dd> 'Enforce' or
+'opportunistic+starttls' policy violation. The connection was not
+used because the remote server did not support STARTTLS. </dd>
+
+<dt> tls=xxx/!requiretls:noencryption </dt> <dd> 'Enforce' or
+'opportunistic+starttls' policy violation. No connection was made
+used because the TLS security policy disabled encryption. </dd>
+
+<dt> tls=xxx/!requiretls:none </dt> <dd> 'Enforce' policy violation.
+After a successful TLS handshake, the connection was not used because
+the remote server did not support REQUIRETLS. </dd>
+
+<dt> tls=xxx/requiretls:none </dt> <dd> 'Opportunistic+starttls'
+policy compliant. After a successful TLS handshake, the remote
+server did not announce REQUIRETLS support, and the connection was
+used without sending REQUIRETLS. </dd>
+
+<dt> tls=xxx/requiretls:none </dt> <dd> 'Opportunistic' policy
+compliant. The remote server did not announce support for STARTTLS
+or REQUIRETLS, and the connection was used without sending REQUIRETLS.
+</dd>
+
+<dt> tls=xxx/requiretls? </dt> <dd> Policy status was undecided,
+because the connection failed before or in the TLS handshake, or
+no connection was made due to a policy configuration error. </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 3.11 and later. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_mail_timeout">smtp_mail_timeout</a>
@@ -12562,6 +12848,251 @@ line. </p>
 <p> This feature is available in Postfix 2.7. </p>
 
 
+</DD>
+
+<DT><b><a name="smtp_requiretls_policy">smtp_requiretls_policy</a>
+(default: see 'postconf -d <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>' output)</b></DT><DD>
+
+<p> How the Postfix SMTP and LMTP client will enforce REQUIRETLS
+for messages received with the REQUIRETLS option. Policy examples
+for SMTP and LMTP are at the end. </p>
+
+<ul>
+
+<li> <p> Specify a list of items, separated with whitespace or
+comma; continue a long line by starting the next line with whitespace.
+</p>
+
+<li> <p> Each item must be an action (see below), or a <a href="DATABASE_README.html">type:table</a>
+lookup table that must return an action (it must not return a
+<a href="DATABASE_README.html">type:table</a>). </p>
+
+<li> <p> In the text that follows, the TLS next-hop destination for
+TCP connections is the recipient domain by default, but this can
+be overruled with destinations specified with <a href="postconf.5.html#transport_maps">transport_maps</a>,
+<a href="postconf.5.html#relayhost">relayhost</a>, <a href="postconf.5.html#content_filter">content_filter</a>, or other routing features. The next-hop
+destination for LMTP over UNIX-domain connections is always the
+value of <a href="postconf.5.html#myhostname">myhostname</a>. </p>
+
+<li> <p> A <a href="DATABASE_README.html">type:table</a> lookup table is searched with the TLS next-hop
+destination (without any [ ], <i>:service</i>, or <i>:port</i>).
+A non-ASCII next-hop destination is converted into ASCII form
+(Punycode) before making a policy query; lookup tables must answer
+ASCII-form queries. </p>
+
+<li> <p> A fixed-string table (such as <a href="DATABASE_README.html#types">hash</a>:, <a href="DATABASE_README.html#types">btree</a>:, or <a href="lmdb_table.5.html">lmdb</a>:) is
+also searched with the next-hop ".parent" domains (in a table key,
+prepend a '.' to a parent domain name). These ".parent" domain
+queries are not made with pattern-based lookup tables such as <a href="regexp_table.5.html">regexp</a>:
+or <a href="pcre_table.5.html">pcre</a>: or with <a href="socketmap_table.html">socketmap</a>: or <a href="tcp_table.5.html">tcp</a>: tables.  </p>
+
+</ul>
+
+<p> Supported actions, assuming that TLS feature status logging is
+enabled with "<a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a> = yes" (the default): </p>
+
+<dl>
+
+<dt> <b> enforce </b> </dt> <dd> <p> Skip servers that do not support
+STARTTLS, skip connections without a server certificate match, and
+skip servers that don't support REQUIRETLS after STARTTLS. </p> <p>
+If a suitable server is found, request REQUIRETLS, deliver the
+message, and log "<tt>tls=<i>level</i>/requiretls</tt>" in delivery
+status logging, where <tt><i>level</i></tt> shows the current SMTP
+client TLS security level. </p> <p> If no suitable server is found,
+return the message as undeliverable and log a policy violation
+"<tt>tls=<i>level</i>/!requiretls:nocertmatch</tt>",
+"<tt>tls=<i>level</i>/!requiretls:noencryption</tt>", or
+"<tt>tls=<i>level</i>/!requiretls:nostarttls</tt>", as described
+under <a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a>. </p> <p> NOTE: this is also used
+implicitly when no REQUIRETLS policy match is found. </p> </dd>
+
+<dt> <b> opportunistic+starttls </b> </dt> <dd> <p> Skip servers
+that don't announce STARTTLS support, but do not require a server
+certificate match or remote REQUIRETLS support. </p> <p> If a
+suitable server is found, send REQUIRETLS if that server supports
+REQUIRETLS and log "<tt>tls=<i>level</i>/requiretls</tt>" or
+"<tt>tls=<i>level</i>/requiretls:nomatch</tt>" in delivery status
+logging, or simply deliver the message as if the sender did not
+request REQUIRETLS and log "<tt>tls=<i>level</i>/requiretls:none</tt>,
+where <tt><i>level</i></tt> shows the current SMTP client TLS
+security level. </p> <p> If no suitable server is found, return the
+message as undeliverable and log a policy violation
+"<tt>tls=<i>level</i>/!requiretls:noencryption</tt>" or
+"<tt>tls=<i>level</i>/!requiretls:nostarttls</tt>" as described
+under <a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a>. </p> <p> This relaxed policy can
+be appropriate for an outbound perimeter MTA, when forwarding
+messages from internal systems to the Internet, at a time that many
+domains support TLS but have no DANE or MTA-STS policies. </p> </dd>
+
+<dt> <b> opportunistic </b> </dt> <dd> <p> Do not require STARTTLS,
+a server certificate match, or remote REQUIRETLS support. </p> <p>
+Request REQUIRETLS if the server supports both STARTTLS and REQUIRETLS
+and log "<tt>tls=<i>level</i>/requiretls</tt>" or
+"<tt>tls=<i>level</i>/requiretls:nomatch</tt>" in delivery status
+logging, otherwise simply deliver the message as if the sender did
+not request REQUIRETLS and log "<tt>tls=<i>level</i>/requiretls:none</tt>,
+where <tt><i>level</i></tt> shows the current SMTP client TLS
+security level. </p> <p> This REQUIRETLS policy is non-intrusive,
+because there can be no REQUIRETLS policy violation. </p> <p> This
+weak policy can be appropriate for an inbound perimeter MTA, when
+forwarding messages from the Internet to internal servers or content
+filters that may not support STARTTLS or REQUIRETLS, using internal
+connections instead of the public Internet. This setting may also
+be useful for an outbound MTA to discover what destinations support
+some form of REQUIRETLS without risking mail delivery problems.
+</p> </dd>
+
+<dt> <b> disable </b> </dt> <dd> <p> Disable REQUIRETLS support.
+Deliver all messages as if the sender did not request REQUIRETLS.
+This may be used as a last-resort workaround when a server announces
+REQUIRETLS support, but the support is inoperable. </p></dd>
+
+</dl>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> To match any name below the domain "example.com" specify
+a table entry with the storage key ".example.com" in <a href="DATABASE_README.html">type:table</a>
+lookup tables that need an exact match. This is appropriate, for
+example, with <a href="DATABASE_README.html#types">hash</a>:, <a href="DATABASE_README.html#types">btree</a>: or <a href="lmdb_table.5.html">lmdb</a>:. </p>
+
+<li> <p> Do not specify a match pattern for ".domain" with <a href="regexp_table.5.html">regexp</a>:,
+<a href="pcre_table.5.html">pcre</a>:, <a href="socketmap_table.html">socketmap</a>:, or <a href="tcp_table.5.html">tcp</a>:, as <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> will
+not query those tables with that form. </p>
+
+</ul>
+
+<p> SMTP client examples. See <a href="REQUIRETLS_README.html">REQUIRETLS_README</a> for discussion. </p>
+
+<ul>
+
+<li> <p> The simplest policy: when a sender requests REQUIRETLS,
+request REQUIRETLS when an external or internal server supports
+both STARTTLS and REQUIRETLS (lines 4, 5), and enforce a TLS
+certificate match only if the destination publishes a DANE or STS
+policy (line 3). When the sender requests REQUIRETLS, and an external
+or internal server does not support both STARTTLS and REQUIRETLS,
+simply deliver the message as if the sender did not request REQUIRETLS
+(lines 4, 5). </p>
+<pre>
+1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+2     <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+3     <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ...dane/sts plugin...
+4     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> = opportunistic
+5     <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a> = opportunistic
+</pre>
+
+<li> <p> The default SMTP client REQUIRETLS policy (lines 4-12):
+when a sender requests REQUIRETLS, require that an external server
+supports STARTTLS and REQUIRETLS, and require that its server
+certificate matches a DANE or STS policy (lines 3, 12); make
+REQUIRETLS support optional for delivery to internal destinations
+or content filters specified as a symbolic name (lines 5-8) or as
+a numeric address (lines 9-11). An internationalized domain name
+must be specified in ASCII form (lines 6-7); the default policy
+below uses ${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} which returns $<a href="postconf.5.html#mydomain">mydomain</a>
+when the domain name contains only ASCII characters, and which
+returns Punycode (<tt>xn--mumble</tt>) when $<a href="postconf.5.html#mydomain">mydomain</a> contains
+non-ASCII. Note: if you specify a domain list outside <a href="postconf.5.html">main.cf</a>, then
+the automatic $<i>name</i> expansions and Punycode conversions will
+not happen, and you will need to enter explicit ASCII domain
+names.</p>
+<pre>
+ 1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2     <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+ 3     <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ...dane/sts plugin...
+ 4     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> =
+ 5         <a href="DATABASE_README.html#types">inline</a>:{
+ 6             {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 7             {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 8             {localhost = opportunistic} }
+ 9         <a href="cidr_table.5.html">cidr</a>:{
+10             {0.0.0.0/0 opportunistic},
+11             {::/0 opportunistic} },
+12         enforce
+</pre>
+
+<li> <p> An SMTP client REQUIRETLS policy that is relaxed for
+external destinations (line 12): when a sender requests REQUIRETLS,
+require that an external server supports STARTTLS but enforce a TLS
+certificate match only if the receiver publishes a DANE or STS
+policy (line 3), and do not require that an external server supports
+REQUIRETLS. Copy the above policy for internal destinations or
+content filters (lines 5-11). Again, an internationalized domain
+name must be specified in ASCII form (lines 6-7); the policy uses
+${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} which returns $<a href="postconf.5.html#mydomain">mydomain</a> when the
+domain name contains only ASCII characters, and which returns
+Punycode (<tt>xn--mumble</tt>) when $<a href="postconf.5.html#mydomain">mydomain</a> contains non-ASCII.
+Note: if you specify a domain list outside <a href="postconf.5.html">main.cf</a>, then the automatic
+$<i>name</i> expansions and Punycode conversions will not happen,
+and you will need to enter explicit ASCII domain names.</p>
+<pre>
+ 1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
+ 2     <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+ 3     <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ...dane/sts plugin...
+ 4     <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> =
+ 5         <a href="DATABASE_README.html#types">inline</a>:{
+ 6             {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 7             {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+ 8             {localhost = opportunistic} }
+ 9         <a href="cidr_table.5.html">cidr</a>:{
+10             {0.0.0.0/0 opportunistic},
+11             {::/0 opportunistic} },
+12         opportunistic+starttls
+</pre>
+
+<li> <p> As above, with external destinations listed in a separate
+file for easier maintenance. </p>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#smtp_tls_security_level">smtp_tls_security_level</a> = may
+    <a href="postconf.5.html#smtp_tls_policy_maps">smtp_tls_policy_maps</a> = ...dane/sts plugin...
+    <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> =
+        <a href="DATABASE_README.html#types">inline</a>:{
+            {${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+            {.${<a href="postconf.5.html#domain_to_ascii">domain_to_ascii</a>{$<a href="postconf.5.html#mydomain">mydomain</a>}} = opportunistic}
+            {localhost = opportunistic} }
+        <a href="cidr_table.5.html">cidr</a>:{
+            {0.0.0.0/0 opportunistic},
+            {::/0 opportunistic} },
+        <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/requiretls-per-site
+        opportunistic+starttls
+</pre>
+<pre>
+/etc/postfix/requiretls-per-site:
+    one.example         enforce
+    two.example         enforce
+    three.example       opportunistic
+    ...
+</pre>
+
+</ul>
+
+<p>
+LMTP client examples. See <a href="REQUIRETLS_README.html">REQUIRETLS_README</a> for discussion.
+</p>
+
+<ul>
+
+<li> <p> The default LMTP client REQUIRETLS policy: when a sender
+requests REQUIRETLS, request REQUIRETLS if the server supports
+REQUIRETLS, otherwise deliver the message as if the sender did not
+request REQUIRETLS. Note: with deliveries over a UNIX-domain socket,
+the next-hop destination for <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a> lookups will
+be the <a href="postconf.5.html#myhostname">myhostname</a> parameter value. </p>
+<pre>
+/etc/postfix/<a href="postconf.5.html">main.cf</a>:
+    <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a> = opportunistic
+</pre>
+
+</ul>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+
 </DD>
 
 <DT><b><a name="smtp_rset_timeout">smtp_rset_timeout</a>
@@ -20937,6 +21468,9 @@ If a message contains a "TLS-Required: no" header, then Postfix
 will add that header to a delivery status notification for that
 message. </p>
 
+<p> Note: the ESMTP REQUIRETLS option overrides the "TLS-Required:
+no" message header.  </p>
+
 <p> This feature is available in Postfix &ge; 3.10. </p>
 
 
index 600ee757c462ebd6c10f8d3331e733229a83e008..a6397fe62b8c867e026cc11b678c3928c2e8072f 100644 (file)
@@ -5,7 +5,7 @@
 <link rel='stylesheet' type='text/css' href='postfix-doc.css'>
 <title> Postfix manual - relocated(5) </title>
 </head> <body> <pre>
-<i>RELOCATED</i>(5)                  File Formats Manual                 <i>RELOCATED</i>(5)
+RELOCATED(5)                                                      RELOCATED(5)
 
 <b><a name="name">NAME</a></b>
        relocated - Postfix relocated table format
 
        Normally, the <a href="relocated.5.html"><b>relocated</b>(5)</a> table is  specified  as  a  text  file  that
        serves as input to the <a href="postmap.1.html"><b>postmap</b>(1)</a> command.  The result, an indexed file
-       in <b>dbm</b> or <b>db</b> format, is used for fast searching by the mail system. Ex-
-       ecute  the  command  "<b>postmap /etc/postfix/relocated</b>" to rebuild an in-
-       dexed file after changing the corresponding relocated table.
+       in <b>dbm</b> or <b>db</b> format, is used for fast searching  by  the  mail  system.
+       Execute  the  command  "<b>postmap  /etc/postfix/relocated</b>"  to rebuild an
+       indexed file after changing the corresponding relocated table.
 
        When the table is provided via other means such as NIS,  LDAP  or  SQL,
        the same lookups are done as for ordinary indexed files.
 
        Alternatively,  the  table  can be provided as a regular-expression map
-       where patterns are given as regular expressions, or lookups can be  di-
-       rected  to a TCP-based server. In those case, the lookups are done in a
-       slightly different way as described below under "REGULAR EXPRESSION TA-
-       BLES" or "TCP-BASED TABLES".
+       where patterns are given as regular  expressions,  or  lookups  can  be
+       directed  to a TCP-based server. In those case, the lookups are done in
+       a slightly different way as described below under  "REGULAR  EXPRESSION
+       TABLES" or "TCP-BASED TABLES".
 
        Table lookups are case insensitive.
 
 <b><a name="case_folding">CASE FOLDING</a></b>
-       The search string is folded to lowercase before database lookup. As  of
-       Postfix  2.3,  the search string is not case folded with database types
-       such as <a href="regexp_table.5.html">regexp</a>: or <a href="pcre_table.5.html">pcre</a>: whose lookup fields can match both  upper  and
+       The  search string is folded to lowercase before database lookup. As of
+       Postfix 2.3, the search string is not case folded with  database  types
+       such  as  <a href="regexp_table.5.html">regexp</a>: or <a href="pcre_table.5.html">pcre</a>: whose lookup fields can match both upper and
        lower case.
 
 <b><a name="table_format">TABLE FORMAT</a></b>
                    <i>pattern      new</i><b>_</b><i>location</i>
 
               Where <i>new</i><b>_</b><i>location</i> specifies  contact  information  such  as  an
-              email address, or perhaps a street address or telephone number.
+              email  address, or perhaps a street address or telephone number.
 
-       <b>o</b>      Postfix  3.11  and  later  can optionally disable the hard-coded
-              prefix. Specify "<a href="postconf.5.html#relocated_prefix_enable">relocated_prefix_enable</a> = no" in  <a href="postconf.5.html">main.cf</a>,  and
-              specify  <a href="postconf.5.html#relocated_maps">relocated_maps</a> entries with your own <a href="https://tools.ietf.org/html/rfc3463">RFC 3463</a>-compliant
+       <b>o</b>      Postfix 3.11 and later can  optionally  disable  the  hard-coded
+              prefix.  Specify  "<a href="postconf.5.html#relocated_prefix_enable">relocated_prefix_enable</a> = no" in <a href="postconf.5.html">main.cf</a>, and
+              specify <a href="postconf.5.html#relocated_maps">relocated_maps</a> entries with your own <a href="https://tools.ietf.org/html/rfc3463">RFC  3463</a>-compliant
               enhanced status code and text, for example:
 
                    <i>pattern</i>      5.2.0 Mailbox is unavailable
                    <i>pattern</i>      5.2.1 Mailbox is disabled
 
-       <b>o</b>      Empty lines and whitespace-only lines are ignored, as are  lines
+       <b>o</b>      Empty  lines and whitespace-only lines are ignored, as are lines
               whose first non-whitespace character is a `#'.
 
-       <b>o</b>      A  logical  line  starts  with  non-whitespace text. A line that
+       <b>o</b>      A logical line starts with  non-whitespace  text.  A  line  that
               starts with whitespace continues a logical line.
 
 <b><a name="table_search_order">TABLE SEARCH ORDER</a></b>
-       With lookups from indexed files such as DB or DBM,  or  from  networked
-       tables  such  as  NIS,  LDAP or SQL, patterns are tried in the order as
+       With  lookups  from  indexed files such as DB or DBM, or from networked
+       tables such as NIS, LDAP or SQL, patterns are tried  in  the  order  as
        listed below:
 
        <i>user</i>@<i>domain</i>
-              Matches <i>user</i>@<i>domain</i>. This form has  precedence  over  all  other
+              Matches  <i>user</i>@<i>domain</i>.  This  form  has precedence over all other
               forms.
 
        <i>user</i>   Matches <i>user</i>@<i>site</i> when <i>site</i> is $<b><a href="postconf.5.html#myorigin">myorigin</a></b>, when <i>site</i> is listed in
 
 <b><a name="address_extension">ADDRESS EXTENSION</a></b>
        When a mail address localpart contains the optional recipient delimiter
-       (e.g.,  <i>user+foo</i>@<i>domain</i>),  the  lookup  order becomes: <i>user+foo</i>@<i>domain</i>,
+       (e.g., <i>user+foo</i>@<i>domain</i>), the  lookup  order  becomes:  <i>user+foo</i>@<i>domain</i>,
        <i>user</i>@<i>domain</i>, <i>user+foo</i>, <i>user</i>, and @<i>domain</i>.
 
 <b><a name="regular_expression_tables">REGULAR EXPRESSION TABLES</a></b>
-       This section describes how the table lookups change when the  table  is
-       given  in  the form of regular expressions or when lookups are directed
-       to a TCP-based server. For a description of regular  expression  lookup
-       table  syntax,  see <a href="regexp_table.5.html"><b>regexp_table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>. For a description
+       This  section  describes how the table lookups change when the table is
+       given in the form of regular expressions or when lookups  are  directed
+       to  a  TCP-based server. For a description of regular expression lookup
+       table syntax, see <a href="regexp_table.5.html"><b>regexp_table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>. For  a  description
        of the TCP client/server table lookup protocol, see <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>.  This
        feature is available in Postfix 2.5 and later.
 
-       Each pattern is a regular expression that is applied to the entire  ad-
-       dress  being looked up. Thus, <i>user@domain</i> mail addresses are not broken
-       up into their <i>user</i> and <i>@domain</i> constituent parts, nor is <i>user+foo</i>  bro-
-       ken up into <i>user</i> and <i>foo</i>.
+       Each pattern is a regular expression that  is  applied  to  the  entire
+       address  being looked up. Thus, <i>user@domain</i> mail addresses are not bro-
+       ken up into their <i>user</i> and <i>@domain</i> constituent parts, nor  is  <i>user+foo</i>
+       broken up into <i>user</i> and <i>foo</i>.
 
        Patterns  are  applied  in the order as specified in the table, until a
        pattern is found that matches the search string.
        The table format does not understand quoting conventions.
 
 <b><a name="configuration_parameters">CONFIGURATION PARAMETERS</a></b>
-       The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant.  The text be-
-       low provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details
-       including examples.
+       The  following  <a href="postconf.5.html"><b>main.cf</b></a>  parameters  are especially relevant.  The text
+       below provides only a  parameter  summary.  See  <a href="postconf.5.html"><b>postconf</b>(5)</a>  for  more
+       details including examples.
 
        <b><a href="postconf.5.html#relocated_maps">relocated_maps</a> (empty)</b>
               Optional lookup tables with new contact information for users or
        Available with Postfix version 3.11 and later:
 
        <b><a href="postconf.5.html#relocated_prefix_enable">relocated_prefix_enable</a> (yes)</b>
-              Prepend  the  prefix  "<b>5.1.6  User  has  moved to</b> " to all relo-
+              Prepend the prefix "<b>5.1.6 User has  moved  to</b>  "  to  all  relo-
               cated_maps lookup results.
 
        Other parameters of interest:
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
-              The local network interface addresses that this mail system  re-
-              ceives mail on.
+              The  local  network  interface  addresses  that this mail system
+              receives mail on.
 
        <b><a href="postconf.5.html#mydestination">mydestination</a> ($<a href="postconf.5.html#myhostname">myhostname</a>, localhost.$<a href="postconf.5.html#mydomain">mydomain</a>, localhost)</b>
-              The  list of domains that are delivered via the $<a href="postconf.5.html#local_transport">local_transport</a>
+              The list of domains that are delivered via the  $<a href="postconf.5.html#local_transport">local_transport</a>
               mail delivery transport.
 
        <b><a href="postconf.5.html#myorigin">myorigin</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
-              The domain name that locally-posted mail appears to  come  from,
+              The  domain  name that locally-posted mail appears to come from,
               and that locally posted mail is delivered to.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
-              The remote network interface addresses that this mail system re-
-              ceives  mail on by way of a proxy or network address translation
-              unit.
+              The remote network interface addresses  that  this  mail  system
+              receives  mail  on by way of a proxy or network address transla-
+              tion unit.
 
 <b><a name="see_also">SEE ALSO</a></b>
        <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address resolver
        111 8th Avenue
        New York, NY 10011, USA
 
-                                                                  <i>RELOCATED</i>(5)
+                                                                  RELOCATED(5)
 </pre> </body> </html>
index 4645b733aa92da0bd14758ceab565dabf9f18ddf..ef7344b09b8dee7fc3a3f14098452ab0605415dd 100644 (file)
@@ -177,14 +177,36 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        <b>-n</b> (ignored)
               Backwards compatibility.
 
-       <b>-oA</b><i>alias</i><b>_</b><i>database</i>
-              Non-default alias database. Specify <i>pathname</i>  or  <i>type</i>:<i>pathname</i>.
-              See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+       <b>-O requiretls=yes</b>
+
+       <b>-O requiretls=no</b>
+              When delivering a message to an SMTP or LMTP server, the connec-
+              tion  must  use TLS with a verified server certificate, and that
+              server must support REQUIRETLS. The "requiretls" name and option
+              value are case-insensitive. REQUIRETLS enforcement is controlled
+              with    the    configuration    parameters    <a href="postconf.5.html#requiretls_enable">requiretls_enable</a>,
+              <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>, and <a href="postconf.5.html#lmtp_requiretls_policy">lmtp_requiretls_policy</a>.
+
+              This feature is available in Postfix 3.11 and later.
+
+       <b>-O smtputf8=yes</b>
+
+       <b>-O smtputf8=no</b>
+              When  delivering  a  message  to  an SMTP or LMTP server, and an
+              envelope address or message  header  contains  UTF8  text,  that
+              server  must  support  SMTPUTF8.  The "smtputf8" option name and
+              value are case-insensitive.
+
+              This feature is available in Postfix 3.11 and later.
 
        <b>-O</b> <i>option=value</i> (ignored)
-              Set  the named <i>option</i> to <i>value</i>. Use the equivalent configuration
+              Set the named <i>option</i> to <i>value</i>. Use the equivalent  configuration
               parameter in <a href="postconf.5.html"><b>main.cf</b></a> instead.
 
+       <b>-oA</b><i>alias</i><b>_</b><i>database</i>
+              Non-default  alias  database. Specify <i>pathname</i> or <i>type</i>:<i>pathname</i>.
+              See <a href="postalias.1.html"><b>postalias</b>(1)</a> for details.
+
        <b>-o7</b> (ignored)
 
        <b>-o8</b> (ignored)
@@ -483,6 +505,12 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               the  default  Postfix  instance,  and that are started, stopped,
               etc., together with the default Postfix instance.
 
+       Postfix 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_enable">requiretls_enable</a> (yes)</b>
+              Enable support for the ESMTP  verb  "REQUIRETLS"  in  the  "MAIL
+              FROM" command.
+
 <b><a name="files">FILES</a></b>
        /var/spool/postfix, mail queue
        /etc/postfix, configuration files
@@ -501,7 +529,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        syslogd(8), system logging
 
 <b><a name="readme_files">README_FILES</a></b>
-       Use "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to  locate
+       Use  "<b>postconf <a href="postconf.5.html#readme_directory">readme_directory</a></b>" or "<b>postconf <a href="postconf.5.html#html_directory">html_directory</a></b>" to locate
        this information.
        <a href="DEBUG_README.html">DEBUG_README</a>, Postfix debugging howto
        <a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
index 37a089ab1f389f5ecc6854db2d1ba05c0f1211f7..a0bebb0e376f074db46fe3046c3eb9e8e543aa8a 100644 (file)
@@ -174,7 +174,7 @@ SMTP(8)                                                                SMTP(8)
        <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a> (Internationalized SMTP)
        <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a> (Internationalized Delivery Status Notifications)
        <a href="https://tools.ietf.org/html/rfc7672">RFC 7672</a> (SMTP security via opportunistic DANE TLS)
-       <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> (TLS-Required message header)
+       <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> (SMTP REQUIRETLS extension, TLS-Required header)
 
 <b><a name="diagnostics">DIAGNOSTICS</a></b>
        Problems  and  transactions  are  logged  to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>.
@@ -507,7 +507,7 @@ SMTP(8)                                                                SMTP(8)
               The delimiter between username and password in  sasl_passwd_maps
               lookup results.
 
-<b><a name="starttls_support_controls">STARTTLS SUPPORT CONTROLS</a></b>
+<b><a name="tls_support_controls">TLS SUPPORT CONTROLS</a></b>
        Detailed  information  about STARTTLS configuration may be found in the
        <a href="TLS_README.html">TLS_README</a> document.
 
@@ -773,41 +773,56 @@ SMTP(8)                                                                SMTP(8)
               an  MX host only if its name matches any STS policy MX host pat-
               tern, and match the server certificate against the MX  hostname.
 
-<b><a name="obsolete_starttls_controls">OBSOLETE STARTTLS CONTROLS</a></b>
-       The  following  configuration  parameters  exist for compatibility with
-       Postfix versions before 2.3. Support for these will  be  removed  in  a
+       Available in Postfix version 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_enable">requiretls_enable</a> (yes)</b>
+              Enable  support  for  the  ESMTP  verb "REQUIRETLS" in the "MAIL
+              FROM" command.
+
+       <b><a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a> (see 'postconf -d  <a href="postconf.5.html#smtp_requiretls_policy">smtp_requiretls_policy</a>'  out-</b>
+       <b>put)</b>
+              How the Postfix SMTP and LMTP client will enforce REQUIRETLS for
+              messages received with the REQUIRETLS option.
+
+       <b><a href="postconf.5.html#smtp_log_tls_feature_status">smtp_log_tls_feature_status</a> (yes)</b>
+              Enable  logging  of  TLS  feature information in delivery status
+              logging.
+
+<b><a name="obsolete_tls_controls">OBSOLETE TLS CONTROLS</a></b>
+       The following configuration parameters  exist  for  compatibility  with
+       Postfix  versions  before  2.3.  Support for these will be removed in a
        future release.
 
        <b><a href="postconf.5.html#smtp_use_tls">smtp_use_tls</a> (no)</b>
-              Opportunistic  mode: use TLS when a remote SMTP server announces
+              Opportunistic mode: use TLS when a remote SMTP server  announces
               STARTTLS support, otherwise send the mail in the clear.
 
        <b><a href="postconf.5.html#smtp_enforce_tls">smtp_enforce_tls</a> (no)</b>
-              Enforcement mode: require  that  remote  SMTP  servers  use  TLS
+              Enforcement  mode:  require  that  remote  SMTP  servers use TLS
               encryption, and never send mail in the clear.
 
        <b><a href="postconf.5.html#smtp_tls_enforce_peername">smtp_tls_enforce_peername</a> (yes)</b>
-              With  mandatory  TLS  encryption,  require  that the remote SMTP
-              server hostname matches  the  information  in  the  remote  SMTP
+              With mandatory TLS encryption,  require  that  the  remote  SMTP
+              server  hostname  matches  the  information  in  the remote SMTP
               server certificate.
 
        <b><a href="postconf.5.html#smtp_tls_per_site">smtp_tls_per_site</a> (empty)</b>
-              Optional  lookup  tables  with the Postfix SMTP client TLS usage
-              policy by next-hop destination and by remote SMTP  server  host-
+              Optional lookup tables with the Postfix SMTP  client  TLS  usage
+              policy  by  next-hop destination and by remote SMTP server host-
               name.
 
        <b><a href="postconf.5.html#smtp_tls_cipherlist">smtp_tls_cipherlist</a> (empty)</b>
-              Obsolete  Postfix  &lt; 2.3 control for the Postfix SMTP client TLS
+              Obsolete Postfix &lt; 2.3 control for the Postfix SMTP  client  TLS
               cipher list.
 
 <b><a name="resource_and_rate_controls">RESOURCE AND RATE CONTROLS</a></b>
        <b><a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a> (30s)</b>
-              The Postfix SMTP client time limit for completing a TCP  connec-
+              The  Postfix SMTP client time limit for completing a TCP connec-
               tion, or zero (use the operating system built-in time limit).
 
        <b><a href="postconf.5.html#smtp_helo_timeout">smtp_helo_timeout</a> (300s)</b>
-              The  Postfix SMTP client time limit for sending the HELO or EHLO
-              command, and  for  receiving  the  initial  remote  SMTP  server
+              The Postfix SMTP client time limit for sending the HELO or  EHLO
+              command,  and  for  receiving  the  initial  remote  SMTP server
               response.
 
        <b><a href="postconf.5.html#lmtp_lhlo_timeout">lmtp_lhlo_timeout</a> (300s)</b>
@@ -819,19 +834,19 @@ SMTP(8)                                                                SMTP(8)
               mand, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
-              The  Postfix  SMTP  client  time limit for sending the MAIL FROM
+              The Postfix SMTP client time limit for  sending  the  MAIL  FROM
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
-              The Postfix SMTP client time limit for sending the SMTP RCPT  TO
+              The  Postfix SMTP client time limit for sending the SMTP RCPT TO
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
-              The  Postfix  SMTP  client  time limit for sending the SMTP DATA
+              The Postfix SMTP client time limit for  sending  the  SMTP  DATA
               command, and for receiving the remote SMTP server response.
 
        <b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
-              The Postfix SMTP client time limit for sending the SMTP  message
+              The  Postfix SMTP client time limit for sending the SMTP message
               content.
 
        <b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
@@ -845,13 +860,13 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
-              The  maximal number of MX (mail exchanger) IP addresses that can
-              result from Postfix SMTP client mail exchanger lookups, or  zero
+              The maximal number of MX (mail exchanger) IP addresses that  can
+              result  from Postfix SMTP client mail exchanger lookups, or zero
               (no limit).
 
        <b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
-              The  maximal number of SMTP sessions per delivery request before
-              the Postfix SMTP client gives up  or  delivers  to  a  fall-back
+              The maximal number of SMTP sessions per delivery request  before
+              the  Postfix  SMTP  client  gives  up or delivers to a fall-back
               <a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
 
        <b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
@@ -861,17 +876,17 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.2 and earlier:
 
        <b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
-              Keep Postfix LMTP client connections open for  up  to  $<a href="postconf.5.html#max_idle">max_idle</a>
+              Keep  Postfix  LMTP  client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
               seconds.
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
-              Permanently  enable  SMTP  connection  caching for the specified
+              Permanently enable SMTP connection  caching  for  the  specified
               destinations.
 
        <b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
-              Temporarily enable SMTP connection caching while  a  destination
+              Temporarily  enable  SMTP connection caching while a destination
               has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
 
        <b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
@@ -885,23 +900,23 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 2.3 and later:
 
        <b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
-              Time  limit for connection cache connect, send or receive opera-
+              Time limit for connection cache connect, send or receive  opera-
               tions.
 
        Available in Postfix version 2.9 - 3.6:
 
        <b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
-              Change the behavior of the smtp_*_timeout time  limits,  from  a
-              time  limit  per  read  or write system call, to a time limit to
-              send or receive a complete record (an SMTP  command  line,  SMTP
-              response  line,  SMTP message content line, or TLS protocol mes-
+              Change  the  behavior  of the smtp_*_timeout time limits, from a
+              time limit per read or write system call, to  a  time  limit  to
+              send  or  receive  a complete record (an SMTP command line, SMTP
+              response line, SMTP message content line, or TLS  protocol  mes-
               sage).
 
        Available in Postfix version 2.11 and later:
 
        <b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
-              When SMTP connection caching is enabled,  the  number  of  times
-              that  an SMTP session may be reused before it is closed, or zero
+              When  SMTP  connection  caching  is enabled, the number of times
+              that an SMTP session may be reused before it is closed, or  zero
               (no limit).
 
        Available in Postfix version 3.4 and later:
@@ -912,13 +927,13 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix version 3.7 and later:
 
        <b><a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a> (no)</b>
-              Change the behavior of the smtp_*_timeout time  limits,  from  a
-              time  limit  per  plaintext or TLS read or write call, to a com-
-              bined time limit for sending a complete  SMTP  request  and  for
+              Change  the  behavior  of the smtp_*_timeout time limits, from a
+              time limit per plaintext or TLS read or write call,  to  a  com-
+              bined  time  limit  for  sending a complete SMTP request and for
               receiving a complete SMTP response.
 
        <b><a href="postconf.5.html#smtp_min_data_rate">smtp_min_data_rate</a> (500)</b>
-              The  minimum  plaintext  data  transfer rate in bytes/second for
+              The minimum plaintext data transfer  rate  in  bytes/second  for
               DATA    requests,    when    deadlines    are    enabled    with
               <a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a>.
 
@@ -926,54 +941,54 @@ SMTP(8)                                                                SMTP(8)
 
        <b><a href="postconf.5.html#transport_destination_concurrency_limit">transport_destination_concurrency_limit</a>   ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
-              A  transport-specific  override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
+              A transport-specific override for  the  <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
               <a href="postconf.5.html#default_destination_concurrency_limit">currency_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
               name of the message delivery transport.
 
        <b><a href="postconf.5.html#transport_destination_recipient_limit">transport_destination_recipient_limit</a>     ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipi</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">ent_limit</a>)</b>
               A transport-specific override for the <a href="postconf.5.html#default_destination_recipient_limit">default_destination_recip</a>-
-              <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where  <i>transport</i>  is  the  <a href="master.5.html">master.cf</a>
+              <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a>  parameter  value,  where  <i>transport</i> is the <a href="master.5.html">master.cf</a>
               name of the message delivery transport.
 
 <b><a name="smtputf8_controls">SMTPUTF8 CONTROLS</a></b>
        Preliminary SMTPUTF8 support is introduced with Postfix 3.0.
 
        <b><a href="postconf.5.html#smtputf8_enable">smtputf8_enable</a> (yes)</b>
-              Enable  preliminary SMTPUTF8 support for the protocols described
+              Enable preliminary SMTPUTF8 support for the protocols  described
               in <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a>, <a href="https://tools.ietf.org/html/rfc6532">RFC 6532</a>, and <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a>.
 
        <b><a href="postconf.5.html#smtputf8_autodetect_classes">smtputf8_autodetect_classes</a> (sendmail, verify)</b>
-              Detect that a message requires SMTPUTF8 support for  the  speci-
+              Detect  that  a message requires SMTPUTF8 support for the speci-
               fied mail origin classes.
 
        Available in Postfix version 3.2 and later:
 
        <b><a href="postconf.5.html#enable_idna2003_compatibility">enable_idna2003_compatibility</a> (no)</b>
-              Enable   'transitional'   compatibility   between  IDNA2003  and
-              IDNA2008, when converting UTF-8 domain names to/from  the  ASCII
+              Enable  'transitional'  compatibility   between   IDNA2003   and
+              IDNA2008,  when  converting UTF-8 domain names to/from the ASCII
               form that is used for DNS lookups.
 
 <b><a name="trouble_shooting_controls">TROUBLE SHOOTING CONTROLS</a></b>
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The  increment  in verbose logging level when a nexthop destina-
-              tion, remote client or server name or network address matches  a
+              The increment in verbose logging level when a  nexthop  destina-
+              tion,  remote client or server name or network address matches a
               pattern given with the <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional  list  of  nexthop destination, remote client or server
-              name or network address patterns that,  if  matched,  cause  the
-              verbose  logging  level  to  increase by the amount specified in
+              Optional list of nexthop destination, remote  client  or  server
+              name  or  network  address  patterns that, if matched, cause the
+              verbose logging level to increase by  the  amount  specified  in
               $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
        <b><a href="postconf.5.html#error_notice_recipient">error_notice_recipient</a> (postmaster)</b>
-              The recipient of postmaster notifications  about  mail  delivery
+              The  recipient  of  postmaster notifications about mail delivery
               problems that are caused by policy, resource, software or proto-
               col errors.
 
        <b><a href="postconf.5.html#internal_mail_filter_classes">internal_mail_filter_classes</a> (empty)</b>
-              What  categories  of  Postfix-generated  mail  are  subject   to
-              before-queue    content    inspection    by   <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
+              What   categories  of  Postfix-generated  mail  are  subject  to
+              before-queue   content    inspection    by    <a href="postconf.5.html#non_smtpd_milters">non_smtpd_milters</a>,
               <a href="postconf.5.html#header_checks">header_checks</a> and <a href="postconf.5.html#body_checks">body_checks</a>.
 
        <b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
@@ -981,46 +996,46 @@ SMTP(8)                                                                SMTP(8)
 
 <b><a name="miscellaneous_controls">MISCELLANEOUS CONTROLS</a></b>
        <b><a href="postconf.5.html#best_mx_transport">best_mx_transport</a> (empty)</b>
-              Where the Postfix  SMTP  client  should  deliver  mail  when  it
+              Where  the  Postfix  SMTP  client  should  deliver  mail when it
               detects a "mail loops back to myself" error condition.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con-
               figuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How much time a Postfix daemon process  may  take  to  handle  a
+              How  much  time  a  Postfix  daemon process may take to handle a
               request before it is terminated by a built-in watchdog timer.
 
        <b><a href="postconf.5.html#delay_logging_resolution_limit">delay_logging_resolution_limit</a> (2)</b>
-              The  maximal  number of digits after the decimal point when log-
+              The maximal number of digits after the decimal point  when  log-
               ging delay values.
 
        <b><a href="postconf.5.html#disable_dns_lookups">disable_dns_lookups</a> (no)</b>
               Disable DNS lookups in the Postfix SMTP and LMTP clients.
 
        <b><a href="postconf.5.html#inet_interfaces">inet_interfaces</a> (all)</b>
-              The local network interface  addresses  that  this  mail  system
+              The  local  network  interface  addresses  that this mail system
               receives mail on.
 
        <b><a href="postconf.5.html#inet_protocols">inet_protocols</a> (see 'postconf -d' output)</b>
-              The  Internet  protocols Postfix will attempt to use when making
+              The Internet protocols Postfix will attempt to use  when  making
               or accepting connections.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
-              The time limit for sending  or  receiving  information  over  an
+              The  time  limit  for  sending  or receiving information over an
               internal communication channel.
 
        <b><a href="postconf.5.html#lmtp_assume_final">lmtp_assume_final</a> (no)</b>
-              When  a remote LMTP server announces no DSN support, assume that
-              the server performs final delivery, and send "delivered"  deliv-
+              When a remote LMTP server announces no DSN support, assume  that
+              the  server performs final delivery, and send "delivered" deliv-
               ery status notifications instead of "relayed".
 
        <b><a href="postconf.5.html#lmtp_tcp_port">lmtp_tcp_port</a> (24)</b>
               The default TCP port that the Postfix LMTP client connects to.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The  maximum  amount of time that an idle Postfix daemon process
+              The maximum amount of time that an idle Postfix  daemon  process
               waits for an incoming connection before terminating voluntarily.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
@@ -1034,21 +1049,21 @@ SMTP(8)                                                                SMTP(8)
               The process name of a Postfix command or daemon process.
 
        <b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
-              The remote network interface addresses  that  this  mail  system
-              receives  mail  on by way of a proxy or network address transla-
+              The  remote  network  interface  addresses that this mail system
+              receives mail on by way of a proxy or network  address  transla-
               tion unit.
 
        <b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
               The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
-              client  will  try  first,  when  a destination has IPv6 and IPv4
+              client will try first, when a  destination  has  IPv6  and  IPv4
               addresses with equal MX preference.
 
        <b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
-              An optional numerical network  address  that  the  Postfix  SMTP
+              An  optional  numerical  network  address  that the Postfix SMTP
               client should bind to when making an IPv4 connection.
 
        <b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
-              An  optional  numerical  network  address  that the Postfix SMTP
+              An optional numerical network  address  that  the  Postfix  SMTP
               client should bind to when making an IPv6 connection.
 
        <b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
@@ -1068,7 +1083,7 @@ SMTP(8)                                                                SMTP(8)
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
-              A  prefix  that  is  prepended  to  the  process  name in syslog
+              A prefix that  is  prepended  to  the  process  name  in  syslog
               records, so that, for example, "smtpd" becomes "prefix/smtpd".
 
        Available with Postfix 2.2 and earlier:
@@ -1080,14 +1095,14 @@ SMTP(8)                                                                SMTP(8)
        Available with Postfix 2.3 and later:
 
        <b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
-              Optional  list  of  relay destinations that will be used when an
-              SMTP destination is not found, or when delivery fails due  to  a
+              Optional list of relay destinations that will be  used  when  an
+              SMTP  destination  is not found, or when delivery fails due to a
               non-permanent error.
 
        Available with Postfix 3.0 and later:
 
        <b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
-              In  the context of email address verification, the SMTP protocol
+              In the context of email address verification, the SMTP  protocol
               stage that determines whether an email address is deliverable.
 
        Available with Postfix 3.1 and later:
@@ -1109,7 +1124,7 @@ SMTP(8)                                                                SMTP(8)
        Available in Postfix 3.7 and later:
 
        <b><a href="postconf.5.html#smtp_bind_address_enforce">smtp_bind_address_enforce</a> (no)</b>
-              Defer  delivery  when  the  Postfix SMTP client cannot apply the
+              Defer delivery when the Postfix SMTP  client  cannot  apply  the
               <a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> or <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> setting.
 
 <b><a name="see_also">SEE ALSO</a></b>
index 8465bbf2529157c6c7e8a9c0a7726116aa46448c..9ea4b210d43093be882743fbf5427b0a6a75aa4d 100644 (file)
@@ -62,6 +62,7 @@ SMTPD(8)                                                              SMTPD(8)
        <a href="https://tools.ietf.org/html/rfc6531">RFC 6531</a> (Internationalized SMTP)
        <a href="https://tools.ietf.org/html/rfc6533">RFC 6533</a> (Internationalized Delivery Status Notifications)
        <a href="https://tools.ietf.org/html/rfc7505">RFC 7505</a> ("Null MX" No Service Resource Record)
+       <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a> (SMTP REQUIRETLS extension)
 
 <b><a name="diagnostics">DIAGNOSTICS</a></b>
        Problems and transactions are logged to <b>syslogd</b>(8) or <a href="postlogd.8.html"><b>postlogd</b>(8)</a>.
@@ -430,7 +431,7 @@ SMTPD(8)                                                              SMTPD(8)
               If  non-empty,  a  filter  for the SASL mechanism names that the
               Postfix SMTP server will announce in the EHLO response.
 
-<b><a name="starttls_support_controls">STARTTLS SUPPORT CONTROLS</a></b>
+<b><a name="tls_support_controls">TLS SUPPORT CONTROLS</a></b>
        Detailed information about STARTTLS configuration may be found  in  the
        <a href="TLS_README.html">TLS_README</a> document.
 
@@ -655,7 +656,17 @@ SMTPD(8)                                                              SMTPD(8)
               instead of an X.509 certificate, when asking  for  or  requiring
               client authentication.
 
-<b><a name="obsolete_starttls_controls">OBSOLETE STARTTLS CONTROLS</a></b>
+       Available in Postfix version 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_enable">requiretls_enable</a> (yes)</b>
+              Enable  support  for  the  ESMTP  verb "REQUIRETLS" in the "MAIL
+              FROM" command.
+
+       <b><a href="postconf.5.html#requiretls_esmtp_header">requiretls_esmtp_header</a> (yes)</b>
+              Record the ESMTP REQUIRETLS  request  in  a  "Require-TLS-ESMTP:
+              yes" message header.
+
+<b><a name="obsolete_tls_controls">OBSOLETE TLS CONTROLS</a></b>
        The  following  configuration  parameters  exist for compatibility with
        Postfix versions before 2.3. Support for these will  be  removed  in  a
        future release.
index 695478942e1e9e645d85d7b519f7551ac0117659..44f4b26087e0c681c47ec2a8c29e22dd2d35fd80 100644 (file)
@@ -172,6 +172,15 @@ BOUNCE(8)                                                            BOUNCE(8)
               Enable  support  for  the  "TLS-Required:  no"  message  header,
               defined in <a href="https://tools.ietf.org/html/rfc8689">RFC 8689</a>.
 
+       Available in Postfix 3.11 and later:
+
+       <b><a href="postconf.5.html#requiretls_redact_dsn">requiretls_redact_dsn</a> (yes)</b>
+              When sending a delivery status notification for an original mes-
+              sage received with the REQUIRETLS option, do not send the origi-
+              nal   message  body  (as  if  that  message  was  received  with
+              "RET=HDRS") and do not enforce REQUIRETLS (as  if  that  message
+              was received without REQUIRETLS).
+
 <b><a name="files">FILES</a></b>
        /var/spool/postfix/bounce/* non-delivery records
        /var/spool/postfix/defer/* non-delivery records
index e3bc7a639eb5dd456f94b31ee2b4f8ad87237d18..ee22c6827b30771bb6a6ba81a6c16db9e505ca97 100644 (file)
@@ -164,13 +164,30 @@ notification when delivery is delayed), or \fBsuccess\fR
 This feature is available in Postfix 2.3 and later.
 .IP "\fB\-n\fR (ignored)"
 Backwards compatibility.
-.IP "\fB\-oA\fIalias_database\fR"
-Non\-default alias database. Specify \fIpathname\fR or
-\fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for
-details.
+.IP "\fB\-O requiretls=yes\fR"
+.IP "\fB\-O requiretls=no\fR"
+When delivering a message to an SMTP or LMTP server, the
+connection must use TLS with a verified server certificate,
+and that server must support REQUIRETLS. The "requiretls" name
+and option value are case\-insensitive. REQUIRETLS enforcement
+is controlled with the configuration parameters requiretls_enable,
+smtp_requiretls_policy, and lmtp_requiretls_policy.
+
+This feature is available in Postfix 3.11 and later.
+.IP "\fB\-O smtputf8=yes\fR"
+.IP "\fB\-O smtputf8=no\fR"
+When delivering a message to an SMTP or LMTP server, and an
+envelope address or message header contains UTF8 text, that server
+must support SMTPUTF8. The "smtputf8" option name and value
+are case\-insensitive.
+
+This feature is available in Postfix 3.11 and later.
 .IP "\fB\-O \fIoption=value\fR (ignored)"
 Set the named \fIoption\fR to \fIvalue\fR. Use the equivalent
 configuration parameter in \fBmain.cf\fR instead.
+.IP "\fB\-oA\fIalias_database\fR"
+Non\-default alias database. Specify \fIpathname\fR or
+\fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for details.
 .IP "\fB\-o7\fR (ignored)"
 .IP "\fB\-o8\fR (ignored)"
 To send 8\-bit or binary content, use an appropriate MIME encapsulation
@@ -462,6 +479,11 @@ these directories belong to additional Postfix instances that share
 the Postfix executable files and documentation with the default
 Postfix instance, and that are started, stopped, etc., together
 with the default Postfix instance.
+.PP
+Postfix 3.11 and later:
+.IP "\fBrequiretls_enable (yes)\fR"
+Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+FROM" command.
 .SH "FILES"
 .na
 .nf
index 1905321c2eb04120fef4310aa753cae7a0eea8d5..dd4caaa789e81537c9dfbdafb2659d110fd96cc2 100644 (file)
@@ -29,7 +29,7 @@ whose first non-whitespace character is a `#'.
 A logical line starts with non-whitespace text. A line that starts
 with whitespace continues a logical line.
 .IP \(bu
-A parameter value may refer to other parameters.
+A parameter value may refer to functions or other parameters.
 .RS
 .IP \(bu 
 The expressions "$name" and "${name}" are recursively replaced with
@@ -60,6 +60,11 @@ comparison is numerical when both operands are all digits, otherwise
 the comparison is lexicographical. These forms are supported with
 Postfix versions >= 3.0.
 .IP \(bu
+The expression "${name{value}}" is replaced with the result from
+calling the function \fIname\fR with the argument \fIvalue\fR
+after stripping whitespace betwen the "{", the value, and the "}".
+An example is the domain_to_ascii{} function.
+.IP \(bu
 Each "value" is subject to recursive named parameter and relational
 expression evaluation, except where noted.
 .IP \(bu
@@ -2035,6 +2040,57 @@ value to disable the feature.
 .PP
 This feature is available in Postfix 3.6 and later. It was backported
 to Postfix versions 3.5.9, 3.4.19, 3.3.16. 3.2.21.
+.SH domain_to_ascii (default: no default)
+A function that returns the ASCII representation of its domain
+name argument. If the argument contains only (7\-bit) ASCII characters
+(A\-label form), then domain_to_ascii{} will return the same value.
+If the argument contains a valid non\-ASCII domain name (U\-label
+form), then domain_to_ascii{} will return the corresponding Punycode
+(A\-label form). Other argument values may result in a program
+start\-up error.
+.PP
+Examples:
+.IP \(bu
+The examples below assume that smtp_requiretls_policy
+contains the default setting:
+.sp
+.nf
+.na
+smtp_requiretls_policy = inline:{
+    {${domain_to_ascii{$mydomain}} = opportunistic },
+    {.${domain_to_ascii{$mydomain}} = opportunistic },
+     ... }
+.fi
+.ad
+.IP \(bu
+Example 1: when mydomain contains only ASCII characters,
+domain_to_ascii{} returns the same value.
+.sp
+.nf
+.na
+# postconf \-o 'mydomain=foo.example' \-x smtp_requiretls_policy
+smtp_requiretls_policy = inline:{
+    {foo.example = opportunistic},
+    {.foo.example = opportunistic},
+     ... }
+.fi
+.ad
+.IP \(bu
+Example 2: when mydomain contains non\-ASCII characters,
+domain_to_ascii{} returns the Punycode representation.
+.sp
+.nf
+.na
+# postconf \-o 'mydomain=Ï€.example' \-x smtp_requiretls_policy
+smtp_requiretls_policy = inline:{
+    {xn\-\-1xa.example = opportunistic},
+    {.xn\-\-1xa.example = opportunistic},
+    ... }
+.fi
+.ad
+.br
+.PP
+This feature is available in Postfix >= 3.11.
 .SH dont_remove (default: 0)
 Don't remove queue files and save them to the "saved" mail queue.
 This is a debugging aid.  To inspect the envelope information and
@@ -3208,6 +3264,11 @@ The LMTP\-specific version of the smtp_line_length_limit
 configuration parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.3 and later.
+.SH lmtp_log_tls_feature_status (default: yes)
+The LMTP\-specific version of the smtp_log_tls_feature_status
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix >= 3.11.
 .SH lmtp_mail_timeout (default: 300s)
 The Postfix LMTP client time limit for sending the MAIL FROM command,
 and for receiving the remote LMTP server response.
@@ -3302,6 +3363,11 @@ The LMTP\-specific version of the smtp_reply_filter
 configuration parameter.  See there for details.
 .PP
 This feature is available in Postfix 2.7 and later.
+.SH lmtp_requiretls_policy (default: opportunistic)
+The LMTP\-specific version of the smtp_requiretls_policy
+configuration parameter. See there for details.
+.PP
+This feature is available in Postfix >= 3.11.
 .SH lmtp_rset_timeout (default: 20s)
 The Postfix LMTP client time limit for sending the RSET command,
 and for receiving the remote LMTP server response. The LMTP client
@@ -4924,7 +4990,9 @@ compatible with Postfix <= 3.10.
 .IP "\fB add_header \fR"
 Insert a MIME\-Error:
 header and an empty line before the erroneous text.
+.sp
 Example:
+.sp
 .nf
 .na
 MIME\-Error: message header was not terminated by empty line
@@ -4934,7 +5002,9 @@ MIME\-Error: message header was not terminated by empty line
 .IP "\fB reject \fR"
 Log the erroneous text and
 reject the message content.
+.sp
 Example:
+.sp
 .nf
 .na
 \fIqueueid\fR reject: mime\-error message header was terminated
@@ -6639,6 +6709,68 @@ Require that a \fBlocal\fR(8) recipient's home directory exists
 before mail delivery is attempted. By default this test is disabled.
 It can be useful for environments that import home directories to
 the mail server (IMPORTING HOME DIRECTORIES IS NOT RECOMMENDED).
+.SH requiretls_enable (default: yes)
+Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+FROM" command. As defined in RFC 8689, when a message specifies
+REQUIRETLS:
+.IP \(bu
+deliveries with SMTP or LMTP must use a TLS connection,
+.IP \(bu
+to a securely looked up MX server (e.g., DNSSEC or MTA\-STS),
+.IP \(bu
+with a matched server certificate (Postfix SMTP or LMTP
+client TLS security levels "secure", "verify", "fingerprint",
+dane\-only, or opportunistic "dane"),
+.IP \(bu
+and the server must announce "REQUIRETLS" support after
+the STARTTLS handshake.
+.br
+.PP
+When delivering a message that specifies REQUIRETLS, the Postfix
+SMTP client will try one or more servers, limited by the
+smtp_mx_address_limit and smtp_mx_session_limit parameters, until
+it finds an MX server that satisfies the above requirements. If
+such a server is not found, the Postfix SMTP or LMTP client returns
+the message as undeliverable.
+.PP
+Notes:
+.IP \(bu
+With the Postfix SMTP or LMTP clients, REQUIRETLS enforcement
+is controlled with smtp_requiretls_policy or lmtp_requiretls_policy. It
+is initially not enforced for deliveries to local servers, including
+LMTP message stores and local content filters.
+.IP \(bu
+The ESMTP REQUIRETLS option overrides the "TLS\-Required:
+no" message header.
+.br
+.PP
+This feature is available in Postfix >= 3.11.
+.SH requiretls_esmtp_header (default: yes)
+Record the ESMTP REQUIRETLS request in a "Require\-TLS\-ESMTP:
+yes" message header. This is needed to propagate the REQUIRETLS
+request through an external content filter that is configured with
+smtpd_proxy_filter, with content_filter, or with a FILTER action in
+an SMTPD \fBaccess\fR(5) table, \fBheader_checks\fR(5) or \fBbody_checks\fR(5). The
+header is not needed with Milter\-based content filters.
+.PP
+This header will be visible to remote and local recipients.  It
+can safely be disabled if a configuration does not use any of the
+above content filters that need the header.
+.PP
+This feature is available in Postfix 3.11 and later.
+.SH requiretls_redact_dsn (default: yes)
+When sending a delivery status notification for an original
+message received with the REQUIRETLS option, do not send the original
+message body (as if that message was received with "RET=HDRS") and
+do not enforce REQUIRETLS (as if that message was received without
+REQUIRETLS). For a detailed discussion see RFC 8689 section 5.
+.PP
+Note: the 'reverse' path for sending a delivery status notification
+may differ from the 'forward' path for receiving the original message.
+Not every hop in the reverse path may support REQUIRETLS, even
+though every hop in the forward path supported it. The setting
+"requiretls_redact_dsn = no" may therefore result in the loss of a
+delivery status notification.
 .SH reset_owner_alias (default: no)
 Reset the \fBlocal\fR(8) delivery agent's idea of the owner\-alias
 attribute, when delivering mail to a child alias that does not have
@@ -7696,6 +7828,126 @@ The Postfix limit of 998 characters not including <CR><LF>
 is consistent with the SMTP limit of 1000 characters including
 <CR><LF>.  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. A list of examples is at the end of this text.
+.IP \(bu
+The logging is inserted between the "delays=a/b/c/d"
+and the "dsn=x.y.z, 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', etc.  Other
+features are shown only if activated. The REQUIRETLS extension's
+feature name is 'requiretls'.
+.IP \(bu
+When a ':\fIdowngrade\-level\fR' is appended to
+a feature name, the feature was downgraded to the indicated level.
+A downgrade\-level \fInone\fR indicates that the feature
+was unavailable. The result of a downgrade may violate a strict
+policy, but may still be compliant with a permissive policy.
+.IP \(bu
+When "!" is prepended to a feature, the policy
+for that feature was violated and the connection was not used.
+.IP \(bu
+When "?" is appended to a feature, the policy for that
+feature was undecided. This is typically the result of a lost
+connection or an incorrect configuration.
+.br
+.PP
+Examples for TLS security levels:
+.IP "tls=none"
+A connection that did not use TLS.
+.br
+.IP "tls=may"
+Opportunistic TLS. The connection was used
+after a successful TLS handshake.
+.br
+.IP "tls=may?"
+Opportunistic TLS. The status was undecided
+because no connection was made, or no server could be reached.
+.br
+.IP "tls=may:none"
+The client was willing to use TLS, but
+the remote server did not support STARTTLS, and the connection was
+used in plaintext as permitted by the opportunistic TLS policy.
+.br
+.IP "tls=dane"
+DANE policy compliant, no downgrade.
+.br
+.IP "tls=(dane:halfdane)"
+Opportunistic DANE. The connection
+security was downgraded to 'halfdane' to indicate that
+mail server records were not DNSSEC\-signed.
+.br
+.IP "tls=dane:encrypt"
+Opportunistic DANE. The connection
+security was downgraded to 'encrypt' because none of the TLSA
+records were usable.
+.br
+.IP "tls=dane?"
+DANE policy status was undecided, because the
+connection failed before or in the TLS handshake.
+.br
+.br
+.PP
+Examples for REQUIRETLS policies (set with smtp_requiretls_policy),
+where "\fIxxx\fR" is a TLS security level:
+.IP "tls=xxx/requiretls"
+'Enforce' policy compliant.
+After a successful TLS handshake that required a certificate match,
+the remote server announced REQUIRETLS support, and the client sent
+REQUIRETLS.
+.IP "tls=xxx/!requiretls:nocertmatch"
+'Enforce' policy
+violation. The connection was not used because the remote server
+certificate did not match as required by the TLS security policy,
+or no connection was made because the TLS security policy disabled
+server certificate matching.
+.br
+.IP "tls=xxx/requiretls:nocertmatch"
+'Opportunistic+starttls'
+or 'opportunistic' policy compliant. After a successful TLS handshake
+that did not require a server certificate match, the remote server
+announced REQUIRETLS support, and the client sent REQUIRETLS.
+.br
+.IP "tls=xxx/!requiretls:nostarttls"
+'Enforce' or
+\&'opportunistic+starttls' policy violation. The connection was not
+used because the remote server did not support STARTTLS.
+.br
+.IP "tls=xxx/!requiretls:noencryption"
+'Enforce' or
+\&'opportunistic+starttls' policy violation. No connection was made
+used because the TLS security policy disabled encryption.
+.br
+.IP "tls=xxx/!requiretls:none"
+'Enforce' policy violation.
+After a successful TLS handshake, the connection was not used because
+the remote server did not support REQUIRETLS.
+.br
+.IP "tls=xxx/requiretls:none"
+'Opportunistic+starttls'
+policy compliant. After a successful TLS handshake, the remote
+server did not announce REQUIRETLS support, and the connection was
+used without sending REQUIRETLS.
+.br
+.IP "tls=xxx/requiretls:none"
+'Opportunistic' policy
+compliant. The remote server did not announce support for STARTTLS
+or REQUIRETLS, and the connection was used without sending REQUIRETLS.
+.br
+.IP "tls=xxx/requiretls?"
+Policy status was undecided,
+because the connection failed before or in the TLS handshake, or
+no connection was made due to a policy configuration error.
+.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.
@@ -7946,6 +8198,260 @@ Examples:
 .ad
 .PP
 This feature is available in Postfix 2.7.
+.SH smtp_requiretls_policy (default: see 'postconf \-d smtp_requiretls_policy' output)
+How the Postfix SMTP and LMTP client will enforce REQUIRETLS
+for messages received with the REQUIRETLS option. Policy examples
+for SMTP and LMTP are at the end.
+.IP \(bu
+Specify a list of items, separated with whitespace or
+comma; continue a long line by starting the next line with whitespace.
+.IP \(bu
+Each item must be an action (see below), or a type:table
+lookup table that must return an action (it must not return a
+type:table).
+.IP \(bu
+In the text that follows, the TLS next\-hop destination for
+TCP connections is the recipient domain by default, but this can
+be overruled with destinations specified with transport_maps,
+relayhost, content_filter, or other routing features. The next\-hop
+destination for LMTP over UNIX\-domain connections is always the
+value of myhostname.
+.IP \(bu
+A type:table lookup table is searched with the TLS next\-hop
+destination (without any [ ], \fI:service\fR, or \fI:port\fR).
+A non\-ASCII next\-hop destination is converted into ASCII form
+(Punycode) before making a policy query; lookup tables must answer
+ASCII\-form queries.
+.IP \(bu
+A fixed\-string table (such as hash:, btree:, or lmdb:) is
+also searched with the next\-hop ".parent" domains (in a table key,
+prepend a '.' to a parent domain name). These ".parent" domain
+queries are not made with pattern\-based lookup tables such as regexp:
+or pcre: or with socketmap: or tcp: tables.
+.br
+.PP
+Supported actions, assuming that TLS feature status logging is
+enabled with "smtp_log_tls_feature_status = yes" (the default):
+.IP "\fB enforce \fR"
+Skip servers that do not support
+STARTTLS, skip connections without a server certificate match, and
+skip servers that don't support REQUIRETLS after STARTTLS.
+.sp
+If a suitable server is found, request REQUIRETLS, deliver the
+message, and log "tls=\fIlevel\fR/requiretls" in delivery
+status logging, where \fIlevel\fR shows the current SMTP
+client TLS security level.
+.sp
+If no suitable server is found,
+return the message as undeliverable and log a policy violation
+"tls=\fIlevel\fR/!requiretls:nocertmatch",
+"tls=\fIlevel\fR/!requiretls:noencryption", or
+"tls=\fIlevel\fR/!requiretls:nostarttls", as described
+under smtp_log_tls_feature_status.
+.sp
+NOTE: this is also used
+implicitly when no REQUIRETLS policy match is found.
+.br
+.IP "\fB opportunistic+starttls \fR"
+Skip servers
+that don't announce STARTTLS support, but do not require a server
+certificate match or remote REQUIRETLS support.
+.sp
+If a
+suitable server is found, send REQUIRETLS if that server supports
+REQUIRETLS and log "tls=\fIlevel\fR/requiretls" or
+"tls=\fIlevel\fR/requiretls:nomatch" in delivery status
+logging, or simply deliver the message as if the sender did not
+request REQUIRETLS and log "tls=\fIlevel\fR/requiretls:none,
+where \fIlevel\fR shows the current SMTP client TLS
+security level.
+.sp
+If no suitable server is found, return the
+message as undeliverable and log a policy violation
+"tls=\fIlevel\fR/!requiretls:noencryption" or
+"tls=\fIlevel\fR/!requiretls:nostarttls" as described
+under smtp_log_tls_feature_status.
+.sp
+This relaxed policy can
+be appropriate for an outbound perimeter MTA, when forwarding
+messages from internal systems to the Internet, at a time that many
+domains support TLS but have no DANE or MTA\-STS policies.
+.br
+.IP "\fB opportunistic \fR"
+Do not require STARTTLS,
+a server certificate match, or remote REQUIRETLS support.
+.sp
+Request REQUIRETLS if the server supports both STARTTLS and REQUIRETLS
+and log "tls=\fIlevel\fR/requiretls" or
+"tls=\fIlevel\fR/requiretls:nomatch" in delivery status
+logging, otherwise simply deliver the message as if the sender did
+not request REQUIRETLS and log "tls=\fIlevel\fR/requiretls:none,
+where \fIlevel\fR shows the current SMTP client TLS
+security level.
+.sp
+This REQUIRETLS policy is non\-intrusive,
+because there can be no REQUIRETLS policy violation.
+.sp
+This
+weak policy can be appropriate for an inbound perimeter MTA, when
+forwarding messages from the Internet to internal servers or content
+filters that may not support STARTTLS or REQUIRETLS, using internal
+connections instead of the public Internet. This setting may also
+be useful for an outbound MTA to discover what destinations support
+some form of REQUIRETLS without risking mail delivery problems.
+.br
+.IP "\fB disable \fR"
+Disable REQUIRETLS support.
+Deliver all messages as if the sender did not request REQUIRETLS.
+This may be used as a last\-resort workaround when a server announces
+REQUIRETLS support, but the support is inoperable.
+.br
+.br
+.PP
+Notes:
+.IP \(bu
+To match any name below the domain "example.com" specify
+a table entry with the storage key ".example.com" in type:table
+lookup tables that need an exact match. This is appropriate, for
+example, with hash:, btree: or lmdb:.
+.IP \(bu
+Do not specify a match pattern for ".domain" with regexp:,
+pcre:, socketmap:, or tcp:, as smtp_requiretls_policy will
+not query those tables with that form.
+.br
+.PP
+SMTP client examples. See REQUIRETLS_README for discussion.
+.IP \(bu
+The simplest policy: when a sender requests REQUIRETLS,
+request REQUIRETLS when an external or internal server supports
+both STARTTLS and REQUIRETLS (lines 4, 5), and enforce a TLS
+certificate match only if the destination publishes a DANE or STS
+policy (line 3). When the sender requests REQUIRETLS, and an external
+or internal server does not support both STARTTLS and REQUIRETLS,
+simply deliver the message as if the sender did not request REQUIRETLS
+(lines 4, 5).
+.sp
+.nf
+.na
+1 /etc/postfix/main.cf:
+2     smtp_tls_security_level = may
+3     smtp_tls_policy_maps = ...dane/sts plugin...
+4     smtp_requiretls_policy = opportunistic
+5     lmtp_requiretls_policy = opportunistic
+.fi
+.ad
+.IP \(bu
+The default SMTP client REQUIRETLS policy (lines 4\-12):
+when a sender requests REQUIRETLS, require that an external server
+supports STARTTLS and REQUIRETLS, and require that its server
+certificate matches a DANE or STS policy (lines 3, 12); make
+REQUIRETLS support optional for delivery to internal destinations
+or content filters specified as a symbolic name (lines 5\-8) or as
+a numeric address (lines 9\-11). An internationalized domain name
+must be specified in ASCII form (lines 6\-7); the default policy
+below uses ${domain_to_ascii{$mydomain}} which returns $mydomain
+when the domain name contains only ASCII characters, and which
+returns Punycode (xn\-\-mumble) when $mydomain contains
+non\-ASCII. Note: if you specify a domain list outside main.cf, then
+the automatic $\fIname\fR expansions and Punycode conversions will
+not happen, and you will need to enter explicit ASCII domain
+names.
+.sp
+.nf
+.na
+ 1 /etc/postfix/main.cf:
+ 2     smtp_tls_security_level = may
+ 3     smtp_tls_policy_maps = ...dane/sts plugin...
+ 4     smtp_requiretls_policy =
+ 5         inline:{
+ 6             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 7             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {localhost = opportunistic} }
+ 9         cidr:{
+10             {0.0.0.0/0 opportunistic},
+11             {::/0 opportunistic} },
+12         enforce
+.fi
+.ad
+.IP \(bu
+An SMTP client REQUIRETLS policy that is relaxed for
+external destinations (line 12): when a sender requests REQUIRETLS,
+require that an external server supports STARTTLS but enforce a TLS
+certificate match only if the receiver publishes a DANE or STS
+policy (line 3), and do not require that an external server supports
+REQUIRETLS. Copy the above policy for internal destinations or
+content filters (lines 5\-11). Again, an internationalized domain
+name must be specified in ASCII form (lines 6\-7); the policy uses
+${domain_to_ascii{$mydomain}} which returns $mydomain when the
+domain name contains only ASCII characters, and which returns
+Punycode (xn\-\-mumble) when $mydomain contains non\-ASCII.
+Note: if you specify a domain list outside main.cf, then the automatic
+$\fIname\fR expansions and Punycode conversions will not happen,
+and you will need to enter explicit ASCII domain names.
+.sp
+.nf
+.na
+ 1 /etc/postfix/main.cf:
+ 2     smtp_tls_security_level = may
+ 3     smtp_tls_policy_maps = ...dane/sts plugin...
+ 4     smtp_requiretls_policy =
+ 5         inline:{
+ 6             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 7             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {localhost = opportunistic} }
+ 9         cidr:{
+10             {0.0.0.0/0 opportunistic},
+11             {::/0 opportunistic} },
+12         opportunistic+starttls
+.fi
+.ad
+.IP \(bu
+As above, with external destinations listed in a separate
+file for easier maintenance.
+.sp
+.nf
+.na
+/etc/postfix/main.cf:
+    smtp_tls_security_level = may
+    smtp_tls_policy_maps = ...dane/sts plugin...
+    smtp_requiretls_policy =
+        inline:{
+            {${domain_to_ascii{$mydomain}} = opportunistic}
+            {.${domain_to_ascii{$mydomain}} = opportunistic}
+            {localhost = opportunistic} }
+        cidr:{
+            {0.0.0.0/0 opportunistic},
+            {::/0 opportunistic} },
+        hash:/etc/postfix/requiretls\-per\-site
+        opportunistic+starttls
+.sp
+/etc/postfix/requiretls\-per\-site:
+    one.example         enforce
+    two.example         enforce
+    three.example       opportunistic
+    ...
+.fi
+.ad
+.br
+.PP
+LMTP client examples. See REQUIRETLS_README for discussion.
+.IP \(bu
+The default LMTP client REQUIRETLS policy: when a sender
+requests REQUIRETLS, request REQUIRETLS if the server supports
+REQUIRETLS, otherwise deliver the message as if the sender did not
+request REQUIRETLS. Note: with deliveries over a UNIX\-domain socket,
+the next\-hop destination for lmtp_requiretls_policy lookups will
+be the myhostname parameter value.
+.sp
+.nf
+.na
+/etc/postfix/main.cf:
+    lmtp_requiretls_policy = opportunistic
+.fi
+.ad
+.br
+.PP
+This feature is available in Postfix >= 3.11.
 .SH smtp_rset_timeout (default: 20s)
 The Postfix SMTP client time limit for sending the RSET command,
 and for receiving the remote SMTP server response. The SMTP client
@@ -9145,6 +9651,7 @@ See the documentation of the smtp_tls_policy_maps parameter and
 TLS_README for more information about security levels.
 .PP
 Example:
+.sp
 .nf
 .na
 # Preferred syntax with Postfix >= 3.6:
@@ -9537,6 +10044,7 @@ this protocol via "!TLSv1.3" is supported since Postfix 3.4 (or patch
 releases >= 3.0.14, 3.1.10, 3.2.7 and 3.3.2).
 .PP
 Example:
+.sp
 .nf
 .na
 # Preferred syntax with Postfix >= 3.6:
@@ -13352,10 +13860,7 @@ Example: client\-certificate access table, with sha256 fingerprints:
     smtpd_client_restrictions =
         check_ccert_access hash:/etc/postfix/access,
         reject
-.fi
-.ad
-.nf
-.na
+.sp
 /etc/postfix/access:
     # Action folded to next line...
     AF:88:7C:AD:51:95:6F:36:96:...:01:FB:2E:48:CD:AB:49:25:A2:3B
@@ -13619,6 +14124,7 @@ this protocol via "!TLSv1.3" is supported since Postfix 3.4 (or patch
 releases >= 3.0.14, 3.1.10, 3.2.7 and 3.3.2).
 .PP
 Example:
+.sp
 .nf
 .na
 # Preferred syntax with Postfix >= 3.6:
@@ -14651,6 +15157,9 @@ If a message contains a "TLS\-Required: no" header, then Postfix
 will add that header to a delivery status notification for that
 message.
 .PP
+Note: the ESMTP REQUIRETLS option overrides the "TLS\-Required:
+no" message header.
+.PP
 This feature is available in Postfix >= 3.10.
 .SH tls_server_sni_maps (default: empty)
 Optional lookup tables that map names received from remote SMTP
index 781eb86b2e2e0a43aa5d4b374114f40f87e914e1..2e15904ed9e85abff9e45f96ad73a64ecf59fb6d 100644 (file)
@@ -151,6 +151,14 @@ Available in Postfix 3.10 and later:
 .IP "\fBtls_required_enable (yes)\fR"
 Enable support for the "TLS\-Required: no" message header, defined
 in RFC 8689.
+.PP
+Available in Postfix 3.11 and later:
+.IP "\fBrequiretls_redact_dsn (yes)\fR"
+When sending a delivery status notification for an original
+message received with the REQUIRETLS option, do not send the original
+message body (as if that message was received with "RET=HDRS") and
+do not enforce REQUIRETLS (as if that message was received without
+REQUIRETLS).
 .SH "FILES"
 .na
 .nf
index 916c080f4bc9d689f08c3c0444077f32b2cdcc70..a0d093bec8adf5dcbed1fd7ee410081d41e7e11c 100644 (file)
@@ -435,6 +435,9 @@ Available in Postfix version 3.10 and later:
 .IP "\fBtls_required_enable (yes)\fR"
 Enable support for the "TLS\-Required: no" message header, defined
 in RFC 8689.
+.IP "\fBrequiretls_esmtp_header (yes)\fR"
+Record the ESMTP REQUIRETLS request in a "Require\-TLS\-ESMTP:
+yes" message header.
 .SH "MISCELLANEOUS CONTROLS"
 .na
 .nf
index 80183b92f2357b5ffb65311b7b65c1e2a9e52d06..20a6734d088f367c6595effd584095ebdaa606c3 100644 (file)
@@ -190,7 +190,7 @@ RFC 5321 (SMTP protocol)
 RFC 6531 (Internationalized SMTP)
 RFC 6533 (Internationalized Delivery Status Notifications)
 RFC 7672 (SMTP security via opportunistic DANE TLS)
-RFC 8689 (TLS\-Required message header)
+RFC 8689 (SMTP REQUIRETLS extension, TLS\-Required header)
 .SH DIAGNOSTICS
 .ad
 .fi
@@ -485,7 +485,7 @@ Available in Postfix version 3.9 and later:
 .IP "\fBsmtp_sasl_password_result_delimiter (:)\fR"
 The delimiter between username and password in sasl_passwd_maps lookup
 results.
-.SH "STARTTLS SUPPORT CONTROLS"
+.SH "TLS SUPPORT CONTROLS"
 .na
 .nf
 .ad
@@ -693,7 +693,18 @@ Available in Postfix version 3.10.5 and later:
 Transform the TLS policy from an STS policy plugin: connect to
 an MX host only if its name matches any STS policy MX host pattern,
 and match the server certificate against the MX hostname.
-.SH "OBSOLETE STARTTLS CONTROLS"
+.PP
+Available in Postfix version 3.11 and later:
+.IP "\fBrequiretls_enable (yes)\fR"
+Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+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
 .ad
index 616a4a14af5209ef3eb8a12661703d40dfc69aef..3324eceb0af3df471e62f53b68db0b45036aae90 100644 (file)
@@ -67,6 +67,7 @@ RFC 5321 (SMTP protocol)
 RFC 6531 (Internationalized SMTP)
 RFC 6533 (Internationalized Delivery Status Notifications)
 RFC 7505 ("Null MX" No Service Resource Record)
+RFC 8689 (SMTP REQUIRETLS extension)
 .SH DIAGNOSTICS
 .ad
 .fi
@@ -402,7 +403,7 @@ Available in Postfix 3.6 and later:
 .IP "\fBsmtpd_sasl_mechanism_filter (!external, static:rest)\fR"
 If non\-empty, a filter for the SASL mechanism names that the
 Postfix SMTP server will announce in the EHLO response.
-.SH "STARTTLS SUPPORT CONTROLS"
+.SH "TLS SUPPORT CONTROLS"
 .na
 .nf
 .ad
@@ -578,7 +579,15 @@ Available in Postfix version 3.9 and later:
 Request that remote SMTP clients send an RFC7250 raw public key
 instead of an X.509 certificate, when asking for or requiring client
 authentication.
-.SH "OBSOLETE STARTTLS CONTROLS"
+.PP
+Available in Postfix version 3.11 and later:
+.IP "\fBrequiretls_enable (yes)\fR"
+Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+FROM" command.
+.IP "\fBrequiretls_esmtp_header (yes)\fR"
+Record the ESMTP REQUIRETLS request in a "Require\-TLS\-ESMTP:
+yes" message header.
+.SH "OBSOLETE TLS CONTROLS"
 .na
 .nf
 .ad
index e6214f26771a210ab49db39cba29b78e93e47f86..27915285ae38f90d97e3adf67041af908009c24a 100755 (executable)
@@ -75,6 +75,14 @@ tlsproxy_tls_session_cache_timeout
 virtual_maps
 EOF
 
+# Eliminate config functions. The are documented in the postconf(5)
+# manpage, but have no parameter name.
+
+cat >>stoplist.tmp <<'EOF'
+domain_to_ascii
+domain_to_utf8
+EOF
+
 # Extract parameters from the postconf(5) manpage.
 
 awk '/^%PARAM/ { print $2 }' proto/postconf.proto | 
index 83a0beeee6e724aeafb9a8a10c44209aa38f77ff..f8d7cd29e3a28a330f9072c74c60f5eaf789d7f5 100755 (executable)
@@ -55,6 +55,10 @@ while(<>) {
     $block =~ s/<i>/\\fI/g;
     $block =~ s/<\/b>/\\fR/g;
     $block =~ s/<\/i>/\\fR/g;
+    $block =~ s/\s*<\/p>\s*<pre>\s*\n/\n.sp\n.nf\n.na\n/g;
+    $block =~ s/\s*<\/pre>\s*<p>\s*/\n.sp\n.fi\n.ad\n/g;
+    $block =~ s/\s*<\/pre>\s*<pre>\s*\n/\n.sp\n/g;
+    $block =~ s/\s*<\/p>\s*<p>\s*/\n.sp\n/g;
     $block =~ s/^(<p(re)?>)/.PP\n\1/ if ($wantpp);
     $block =~ s/<p> */\n/g;
     $block =~ s/ *<\/p>/\n/g;
index e09c450d5403bb20fd5cc417547bf3247483a2eb..63670377ae102e123f7bff88e224aca6817ff68d 100755 (executable)
@@ -367,6 +367,7 @@ while (<>) {
     s;\bmydes[-</bB>]*\n*[ <bB>]*ti[-</bB>]*\n*[ <bB>]*na[-</bB>]*\n*[ <bB>]*tion\b;<a href="postconf.5.html#mydestination">$&</a>;g;
     s;\bmydo[-</bB>]*\n* *[<bB>]*main\b;<a href="postconf.5.html#mydomain">$&</a>;g;
     s;\bmyhostname\b;<a href="postconf.5.html#myhostname">$&</a>;g;
+    s;\bdomain_to_ascii\b;<a href="postconf.5.html#domain_to_ascii">$&</a>;g;
     s;\bmynet[-</bB>]*\n* *[<bB>]*works\b;<a href="postconf.5.html#mynetworks">$&</a>;g;
     s;\bmynetworks_style\b;<a href="postconf.5.html#mynetworks_style">$&</a>;g;
     s;\bmyo[-</bB>]*\n*[ <bB>]*rigin\b;<a href="postconf.5.html#myorigin">$&</a>;g;
@@ -733,6 +734,8 @@ while (<>) {
     s;\blmtp_tlsrpt_skip_reused_handshakes\b;<a href="postconf.5.html#lmtp_tlsrpt_skip_reused_handshakes">$&</a>;g;
     s;\blmtp_tls_enforce_sts_mx_patterns\b;<a href="postconf.5.html#lmtp_tls_enforce_sts_mx_patterns">$&</a>;g;
     s;\bsmtp_tls_enforce_sts_mx_patterns\b;<a href="postconf.5.html#smtp_tls_enforce_sts_mx_patterns">$&</a>;g;
+    s;\blmtp_log_tls_feature_status\b;<a href="postconf.5.html#lmtp_log_tls_feature_status">$&</a>;g;
+    s;\bsmtp_log_tls_feature_status\b;<a href="postconf.5.html#smtp_log_tls_feature_status">$&</a>;g;
     s;\bsmtpd_enforce_tls\b;<a href="postconf.5.html#smtpd_enforce_tls">$&</a>;g;
     s;\bsmtpd_sasl_tls_security_options\b;<a href="postconf.5.html#smtpd_sasl_tls_security_options">$&</a>;g;
     s;\bsmtpd_sasl_type\b;<a href="postconf.5.html#smtpd_sasl_type">$&</a>;g;
@@ -1193,6 +1196,11 @@ while (<>) {
     s;\bignore_srv_lookup_error\b;<a href="postconf.5.html#ignore_srv_lookup_error">$&</a>;g;
 
     s;\btls_required_enable\b;<a href="postconf.5.html#tls_required_enable">$&</a>;g;
+    s;\brequiretls_enable\b;<a href="postconf.5.html#requiretls_enable">$&</a>;g;
+    s;\brequiretls_esmtp_header\b;<a href="postconf.5.html#requiretls_esmtp_header">$&</a>;g;
+    s;\bsmtp_requiretls_policy\b;<a href="postconf.5.html#smtp_requiretls_policy">$&</a>;g;
+    s;\blmtp_requiretls_policy\b;<a href="postconf.5.html#lmtp_requiretls_policy">$&</a>;g;
+    s;\brequiretls_redact_dsn\b;<a href="postconf.5.html#requiretls_redact_dsn">$&</a>;g;
     s;\bfull_name_encoding_charset\b;<a href="postconf.5.html#full_name_encoding_charset">$&</a>;g;
     s;\bsmtpd_hide_client_session\b;<a href="postconf.5.html#smtpd_hide_client_session">$&</a>;g;
 
index 86476adf3755eff0e0143f40e45eacabc6075bba..1dc1dec576dc15ac8ab6d3e79049c731d32f128d 100644 (file)
@@ -40,7 +40,9 @@ HTML  = ../html/ADDRESS_CLASS_README.html \
        ../html/POSTSCREEN_3_5_README.html \
        ../html/POSTSCREEN_README.html \
        ../html/QSHAPE_README.html \
-       ../html/RESTRICTION_CLASS_README.html ../html/SASL_README.html \
+       ../html/RESTRICTION_CLASS_README.html \
+       ../html/REQUIRETLS_README.html \
+       ../html/SASL_README.html \
        ../html/SCHEDULER_README.html ../html/SMTPD_ACCESS_README.html \
        ../html/SMTPD_POLICY_README.html \
        ../html/SMTPD_PROXY_README.html \
@@ -92,6 +94,7 @@ README        = ../README_FILES/ADDRESS_CLASS_README \
        ../README_FILES/POSTSCREEN_README \
        ../README_FILES/QSHAPE_README \
        ../README_FILES/RESTRICTION_CLASS_README \
+       ../README_FILES/REQUIRETLS_README \
        ../README_FILES/SASL_README ../README_FILES/SCHEDULER_README \
        ../README_FILES/SMTPD_ACCESS_README \
        ../README_FILES/SMTPD_POLICY_README ../README_FILES/SMTPD_PROXY_README \
@@ -288,6 +291,9 @@ clobber:
 ../html/RESTRICTION_CLASS_README.html: RESTRICTION_CLASS_README.html
        $(DETAB) $? | $(POSTLINK) >$@
 
+../html/REQUIRETLS_README.html: REQUIRETLS_README.html
+       $(DETAB) $? | $(POSTLINK) >$@
+
 ../html/SASL_README.html: SASL_README.html
        $(DETAB) $? | $(POSTLINK) >$@
 
@@ -477,6 +483,9 @@ clobber:
 ../README_FILES/RESTRICTION_CLASS_README: RESTRICTION_CLASS_README.html
        $(DETAB) $? | $(HT2READ) >$@
 
+../README_FILES/REQUIRETLS_README: REQUIRETLS_README.html
+       $(DETAB) $? | $(HT2READ) >$@
+
 ../README_FILES/SASL_README: SASL_README.html
        $(DETAB) $? | $(HT2READ) >$@
 
@@ -550,9 +559,9 @@ clobber:
 ../html/postconf.5.html: postconf.html.prolog postconf.proto \
        postconf.html.epilog ../mantools/xpostconf ../mantools/postconf2html \
        ../mantools/postlink
-       (cat postconf.html.prolog; ../mantools/xpostconf postconf.proto | \
-           ../mantools/postconf2html | ../mantools/postlink; \
-           cat postconf.html.epilog ) | $(DETAB) > $@
+       (cat postconf.html.prolog; \
+           ../mantools/xpostconf postconf.proto | ../mantools/postconf2html; \
+           cat postconf.html.epilog ) | ../mantools/postlink | $(DETAB) > $@
 
 ../INSTALL: ../README_FILES/INSTALL
        rm -f $@
diff --git a/postfix/proto/REQUIRETLS_README.html b/postfix/proto/REQUIRETLS_README.html
new file mode 100644 (file)
index 0000000..42c4dfc
--- /dev/null
@@ -0,0 +1,553 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "https://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix REQUIRETLS Support</title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix REQUIRETLS Support</h1>
+
+<hr>
+
+<h2> Table of Contents </h2>
+<ul>
+
+<li> <a href="#intro"> Introduction </a> </li>
+<li> <a href="#perimeter"> REQUIRETLS for a perimeter MTA </a>
+<ul>
+<li> <a href="#inbound"> Receiving inbound messages with REQUIRETLS requests </a>
+<li> <a href="#lmtp-smtp"> LMTP and SMTP-based message stores and content filters content filters </a>
+<li> <a href="#non-smtp"> Non-SMTP and non-LMTP content filters </a>
+<li> <a href="#external"> Communication with external servers </a>
+<li> <a href="#relax"> Relaxing REQUIRETLS for external deliveries </a>
+</ul>
+<li> <a href="#census"> An experiment: testing REQUIRETLS support </a>
+<li> <a href="#requesting"> Requesting REQUIRETLS without SMTP </a>
+<li> <a href="#ndrs"> Non-delivery notifications </a> </li>
+<li> <a href="#summary"> REQUIRETLS quick summary </a> </li>
+<li> <a href="#credits"> Credits </a> </li>
+
+</ul>
+
+<h2> <a name="intro"> Introduction </a> </h2>
+
+<p> (For background information, see below for a <a href="#summary">
+REQUIRETLS quick summary</a>.) </p>
+
+<p> This document covers the Postfix default settings for using the
+REQUIRETLS extension. The purpose of these defaults is to make REQUIRETLS
+support usable in an existing environment, with a path towards the
+future. </p>
+
+<p> The main issues with deploying REQUIRETLS are a lack of support in
+existing infrastructure:  </p>
+
+<ul>
+
+<li> <p> REQUIRETLS requires that server certificates are authenticated.
+When email is sent across the Internet, this involves a DANE or MTA-STS
+policy that is published by a mail receiving domain, using DNSSEC or
+HTTPS. At this time, many domains do not publish such a policy. </p>
+
+<li> <p> REQUIRETLS is historically not supported by existing local
+infrastructure such as internal message stores or Postfix content
+filters, and may be over-kill for connections that happen behind a
+perimeter MTA within a trusted internal network. </p>
+
+</ul>
+
+<h2> <a name="perimeter"> REQUIRETLS for a perimeter MTA </a> </h2>
+
+<p> In this text, a perimeter MTA is a mail system that operates
+on the boundary of an administrative domain. It receives email
+messages for the domain, and/or delivers email messages on behalf
+of the domain. </p>
+
+<h3> <a name="inbound"> Receiving inbound messages with REQUIRETLS requests </a> </h3>
+
+<p> Postfix has one global parameter setting that controls REQUIRETLS
+support in all Postfix processes. The default setting is:
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    requiretls_enable = yes
+</pre>
+</blockquote>
+
+<p> With this, the Postfix SMTP server will announce REQUIRETLS
+support, and more importantly, will receive messages from senders
+that for some reason request REQUIRETLS support -- messages that
+you would otherwise not receive, assuming that the domain already
+publishes a valid DANE and/or STS policy. </p>
+
+<p> If all you need is to receive messages with REQUIRETLS, and
+you do not insist on enforcing REQUIRETLS when sending or forwarding
+messages, then you can stop reading this document after adding the
+additional settings below. </p>
+
+<blockquote> <p> NOTE: The configuration below may be suitable for
+a personal domain, where the owner can decide what happens with all
+messages. For domains that receive messages for other people, a
+less radical approach may be better, as described in the sections
+that follow.  </p> </blockquote>
+
+<blockquote>
+<pre>
+1 /etc/postfix/main.cf:
+2     # Don't enforce REQUIRETLS when delivering mail with SMTP or LMTP.
+3     smtp_requiretls_policy = opportunistic
+4     lmtp_requiretls_policy = opportunistic
+5     
+6     # Don't detect or add a "Require-TLS-ESMTP: yes" header.
+7     requiretls_esmtp_header = no
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> Lines 3-4: These relax REQUIRETLS enforcement when delivering
+a email to a message store, content filter, or other destination
+that may not support REQUIRETLS. If a server does not support
+STARTTLS or REQUIRETLS, then Postfix will simply deliver the message
+as if the sender did not request REQUIRETLS.
+
+<li> <p> Line 7: The requiretls_esmtp_header feature enables support
+for a message header "Require-TLS-ESMTP: yes" that allows Postfix
+to propagate the sender's REQUIRETLS request through a content
+filter based on SMTPD_PROXY_README or FILTER_README. This feature
+can safely be disabled if the domain does not need to enforce
+REQUIRETLS while delivering or forwarding messages. </p>
+
+</ul>
+
+<h3> <a name="lmtp-smtp"> LMTP and SMTP-based message stores and content filters </a> </h3>
+
+<p> REQUIRETLS is historically not supported by message stores such
+as Dovecot, and by content filters based on FILTER_README or
+SMTPD_PROXY_README. The settings below allow for that reality, while
+also preparing for future REQUIRETLS support. <p>
+
+<p> The Postfix SMTP (LMTP) client supports a permissive REQUIRETLS
+policy that is suitable for communication with internal message stores
+and content filters based on FILTER_README or SMTPD_PROXY_README. </p>
+
+<ul> 
+
+<li> <p> <b>opportunistic</b>: STARTTLS and REQUIRETLS support are
+optional. When the sender requests REQUIRETLS, and an SMTP or LMTP
+server supports STARTTLS and REQUIRETLS, then send REQUIRETLS,
+otherwise simply deliver the message as if the sender did not request
+REQUIRETLS. </p>
+
+</ul>
+
+<p> For a more complete definition of this enforcement level, see
+the smtp_requiretls_policy parameter documentation. </p>
+
+<p> For REQUIRETLS, the relevant Postfix 3.11 configuration default
+settings are: </p>
+
+<blockquote>
+<pre>
+ 1 /etc/postfix/main.cf:
+ 2     smtp_tls_security_level = may
+ 3     requiretls_esmtp_header = yes
+ 4     lmtp_requiretls_policy = opportunistic
+ 5     smtp_requiretls_policy =
+ 6         inline:{
+ 7             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 9             {localhost = opportunistic} }
+10         cidr:{
+11             {0.0.0.0/0 opportunistic}
+12             {::/0 opportunistic} }
+13       ...to be completed in section "<a href="#external">Communication with external servers</a>"...
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> Line 3: The requiretls_esmtp_header setting enables support
+for a message header "Require-TLS-ESMTP: yes" that allows Postfix
+to propagate the sender's REQUIRETLS request through a content
+filter. This feature can safely be disabled if there is no need for
+content inspection based on SMTPD_PROXY_README or FILTER_README.
+</p>
+
+<li> <p> Lines 5-12: These make REQUIRETLS support optional for
+internal destinations and content filters that are specified as a
+symbolic name (lines 6-9) or as a numerical IP address (lines 10-12).
+</p>
+
+<li> <p> Lines 7 and 8 use ${domain_to_ascii{$mydomain}} instead
+of $mydomain. The function domain_to_ascii{} returns $mydomain if
+that contains only (7-bit) ASCII. If the mydomain value contains
+non-ASCII characters, then domain_to_ascii{} returns the
+<tt>xn--mumble-mumble</tt> Punycode (A-label) form that Postfix
+needs. This works around a limitation that may be eliminated in a
+future Postfix version. </p>
+
+<li> <p>  Note: if you specify a domain list outside main.cf, then
+the automatic $<i>name</i> expansions and Punycode conversions will
+not happen; you will need to enter real domain names and will need
+to convert non-ASCII domains to Punycode. </p>
+
+</ul>
+
+<h3> <a name="non-smtp"> Non-SMTP and non-LMTP content filters </a> </h3>
+
+<p> Postfix FILTER_README describes content inspection based on a
+pipe-to-command approach. For REQUIRETLS, the relevant Postfix 3.11
+default setting is: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    requiretls_esmtp_header = yes
+</pre>
+</blockquote>
+
+<p> The requiretls_esmtp_header feature enables support for a message
+header "Require-TLS-ESMTP: yes" that allows Postfix to propagate the
+sender's REQUIRETLS request through a content filter. This feature can
+safely be disabled if there is no need for content inspection based on
+SMTPD_PROXY_README or FILTER_README. </p>
+
+<h3> <a name="external"> Communication with external servers </a> </h3>
+
+<p> For communication with external servers, the Postfix SMTP client
+supports multiple enforcement levels: </p>
+
+<ul>
+
+<li> <p> <b>enforce</b>: When the sender requests REQUIRETLS, require
+secure lookup of MX hosts (for example, using DNSSEC or HTTPS),
+require a server certificate match (for example, based on a published
+DANE or STS policy), and require that the remote server supports
+REQUIRETLS. Otherwise return the message as undeliverable. </p> <p>
+NOTE: this is also used implicitly when no REQUIRETLS policy match
+is found. </p>
+
+<li> <p> <b>opportunistic+starttls</b>: When the sender requests
+REQUIRETLS, require that the server supports STARTTLS. Send REQUIRETLS
+if the server supports REQUIRETLS, otherwise simply deliver the
+message as if the sender did not request REQUIRETLS. </p>
+
+<li> <p> <b>opportunistic</b>: STARTTLS and REQUIRETLS support are
+optional. When the sender requests REQUIRETLS, and an SMTP or LMTP
+server supports STARTTLS and REQUIRETLS, then send REQUIRETLS,
+otherwise simply deliver the message as if the sender did not request
+REQUIRETLS. </p>
+
+</ul>
+
+<p> For a more complete definition of these enforcement levels,
+see the smtp_requiretls_policy parameter documentation. </p>
+
+<p> For sending mail with REQUIRETLS, the relevant Postfix 3.11
+default settings are shown below, with one suggested setting in a
+comment (line 2).
+
+<p> The default settings below complete the earlier configuration
+for <a href="#lmtp-smtp">message stores and content filters</a>,
+with an 'enforce' policy for external deliveries (line 13). You can
+disable the requiretls_esmtp_header feature (line 4) if a configuration
+does not use content inspection based on SMTPD_PROXY_README or
+FILTER_README. </p>
+
+<blockquote>
+<pre>
+ 1 /etc/postfix/main.cf:
+ 2     # smtp_tls_policy_maps = ...dane/sts plugin...
+ 3     smtp_tls_security_level = may
+ 4     requiretls_esmtp_header = yes
+ 5     smtp_requiretls_policy =
+ 6         inline:{
+ 7             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 9             {localhost = opportunistic} }
+10         cidr:{
+11             {0.0.0.0/0 opportunistic}
+12             {::/0 opportunistic} }
+13         enforce
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> New at line 13: The 'enforce' policy for external
+destinations is technically correct, but is likely to suffer from
+delivery failures because many domains do not publish a DANE or STS
+policy, and many MTAs support STARTTLS but not REQUIRETLS. A perhaps
+more practical policy may be found in the section <a href="#relax">
+Relaxing REQUIRETLS for external deliveries</a>. </p>
+
+<li> <p> (Same as before) Line 3: The requiretls_esmtp_header setting
+enables support for a message header "Require-TLS-ESMTP: yes" that
+allows Postfix to propagate the sender's REQUIRETLS request through
+a content filter. This feature can safely be disabled if there is
+no need for content inspection based on SMTPD_PROXY_README or
+FILTER_README.  </p>
+
+<li> <p> (Same as before) Lines 5-12: These make REQUIRETLS support
+optional for internal destinations and content filters that are
+specified as a symbolic name (lines 6-9) or as a numerical IP address
+(lines 10-12).
+
+<li> <p> (Same as before) Lines 7 and 8 use ${domain_to_ascii{$mydomain}}
+instead of $mydomain. The function domain_to_ascii{} returns $mydomain
+if that contains only (7-bit) ASCII. If the mydomain value contains
+non-ASCII characters, then domain_to_ascii{} returns the
+<tt>xn--mumble-mumble</tt> Punycode (A-label) form that Postfix
+needs. This works around a limitation that may be eliminated in a
+future Postfix version. </p>
+
+<li> <p> (Same as before) Note: if you specify a domain list outside
+main.cf, then the automatic $<i>name</i> expansions and Punycode
+conversions will not happen; you will need to enter real domain
+names and will need to convert non-ASCII domains to Punycode.) </p>
+
+</ul>
+
+<h3> <a name="relax"> Relaxing REQUIRETLS for external deliveries </a> </h3>
+
+<p> It may be desirable to make REQUIRETLS work with today's
+infrastructure, by keeping the requirement for TLS, but relaxing
+the requirements that a remote server supports REQUIRETLS and that
+its server certificate matches a DANE or STS policy. The configuration
+below makes that change by replacing the default 'enforce' with
+'opportunistic+starttls' (line 13). </p>
+
+<blockquote>
+<pre>
+ 1 /etc/postfix/main.cf:
+ 2     smtp_tls_security_level = may
+ 3     # smtp_tls_policy_maps = ...dane/sts plugin...
+ 4     requiretls_esmtp_header = yes
+ 5     smtp_requiretls_policy =
+ 6         inline:{
+ 7             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 9             {localhost = opportunistic} }
+10         cidr:{
+11             {0.0.0.0/0 opportunistic}
+12             {::/0 opportunistic} }
+13         opportunistic+starttls
+</pre>
+</blockquote>
+
+<ul>
+
+<li> <p> New at line 13: the 'opportunistic+starttls' policy relaxes
+the requirement that every MTA in the forward path of a message
+supports REQUIRETLS, but in practice only one network hop needs to
+be secured: from a sender's perimeter MTA to a receiver's perimeter
+MTA. The network connections between user agents and their respective
+perimeters are assumed to be already secure. </p>
+
+<li> <p> (Same as before) Line 3: The requiretls_esmtp_header setting
+enables support for a message header "Require-TLS-ESMTP: yes" that
+allows Postfix to propagate the sender's REQUIRETLS request through
+a content filter. This feature can safely be disabled if there is
+no need for content inspection based on SMTPD_PROXY_README or
+FILTER_README.  </p>
+
+<li> <p> (Same as before) Lines 5-12: These make REQUIRETLS support
+optional for internal destinations and content filters that are
+specified as a symbolic name (lines 6-9) or as a numerical IP address
+(lines 10-12).
+
+<li> <p> (Same as before) Lines 7 and 8 use ${domain_to_ascii{$mydomain}}
+instead of $mydomain. The function domain_to_ascii{} returns $mydomain
+if that contains only (7-bit) ASCII. If the mydomain value contains
+non-ASCII characters, then domain_to_ascii{} returns the
+<tt>xn--mumble-mumble</tt> Punycode (A-label) form that Postfix
+needs. This works around a limitation that may be eliminated in a
+future Postfix version. </p>
+
+<li> <p> (Same as before) Note: if you specify a domain list outside
+main.cf, then the automatic $<i>name</i> expansions and Punycode
+conversions will not happen; you will need to enter real domain
+names and will need to convert non-ASCII domains to Punycode.) </p>
+
+</ul>
+
+<h2> <a name="census"> An experiment: testing REQUIRETLS support </a> </h2>
+
+<p> The 'opportunistic' enforcement level may be useful to discover
+REQUIRETLS support globally. The idea is to turn on REQUIRETLS for
+all outbound mail, and watch in Postfix TLS status logging how often
+delivery is logged as "requiretls" (all requirements satisfied),
+"requiretls:nocertmatch" (no DANE or STS policy, or certificate not
+trusted or not matched), "requiretls:none" (no REQUIRETLS support),
+or "requiretls:nostarttls". For more details on this logging format,
+see smtp_log_tls_feature_status.</p>
+
+<h2> <a name="requesting"> Requesting REQUIRETLS without SMTP </a> </h2>
+
+<p> There are two options: </p>
+
+<ul>
+
+<li> <p> Specify the Postfix-specific "<b>sendmail -Orequiretls=yes</b>"
+command-line option. This option is always available, but may not
+be convenient to use. <p>
+
+<li> <p> Add a Postfix-specific "<b>Require-TLS-ESMTP: yes</b>"
+message header. This is easier to use, but requires the setting
+"requiretls_esmtp_header = yes" which is not recommended for systems
+without content filters based on SMTPD_PROXY_README or FILTER_README.
+</p>
+
+</ul>
+
+<blockquote> <b>Question</b>: perhaps there needs to be a parameter
+setting to request REQUIRETLS for specific email sources or contexts?
+</blockquote>
+
+<h2> <a name="ndrs"> Non-delivery notifications </a> </h2>
+
+<p> By default, Postfix redacts an undeliverable REQUIRETLS message as
+described in RFC 8689,  before returning it to the sender: </p>
+
+<ul>
+
+<li> <p> Remove the label "this message needs REQUIRETLS". The
+purpose is to avoid loss of notifications when a reverse path does
+not support REQUIRETLS, even though the forward path supported it.
+</p>
+
+<li> <p> Return only the message header, as if the message was
+received with the RFC 3461 DSN option "<tt>RET=HDRS</tt>". The
+purpose is to limit the amount of information that may be exposed
+in plaintext. </p>
+
+</ul>
+
+<p> The relevant default setting is: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    requiretls_redact_dsn = yes
+</pre>
+</blockquote>
+
+<p> When a message was received with a "<tt>TLS-Required: no</tt>"
+header, and REQUIRETLS was not requested, the "T<tt>LS-Required:
+no</tt>" header is copied to the delivery status notification. </p>
+
+<h2> <a name="summary"> REQUIRETLS quick summary </a> </h2>
+
+<p> The REQUIRETLS extension in ESMTP allows a sender to request
+that a message will be sent over connections that are protected
+with TLS. RFC 8689 defines two SMTP features: </p>
+
+<ul>
+
+<li> <p> A message header "TLS-Required: no" that disables TLS
+enforcement: do not require a server certificate match, and allow
+falling back to plaintext if TLS is unavailable. This may be useful
+to report a TLS problem, as described in TLSRPT_README. This feature
+has lower precedence than REQUIRETLS, and is not discussed further
+in this document. </p>
+
+<li> <p> An ESMTP protocol extension named "REQUIRETLS" that an SMTP
+server may list in its EHLO response, and that an SMTP client may request
+in a MAIL FROM command. This extension can be used only in an encrypted
+session, as illustrated with the fragment below, where <tt>C</tt>=client
+and <tt>S</tt>=server. </p>
+
+<pre>
+. . .
+C: STARTTLS
+S: 220 Ready to start TLS
+C: EHLO client.example.org
+S: 250-mail.example.com 
+   . . .
+   250 REQUIRETLS
+C: MAIL FROM:&lt;sender@example.org&gt; REQUIRETLS
+S: 250 OK
+. . .
+</pre>
+
+<li> <p> RFC 8689 applies equally to message relay [RFC 5321], submission
+[RFC 6409], and the LMTP Local Mail Transfer Protocol [RFC 2033]. </p>
+
+<li> <p> REQUIRETLS is an <i>end-to-end</i> feature, unlike SMTP
+which is <i>hop-by-hop</i>. When a sender requests REQUIRETLS, each
+MTA in the forward path must support REQUIRETLS. </p>
+
+<li> <p> Each connection in the forward path must be made to an MX
+server that has been looked up securely (for example, with DNSSEC
+or HTTPS).
+
+<li> <p> Each server certificate must be verified. To match a server
+certificate, the Postfix SMTP client needs to use an appropriate policy
+type: </p>
+
+<ul>
+
+<li> <p> A TLS policy type 'secure' or 'verify', with certificate name
+matching info. For example, a policy returned by an MTA-STS plugin that
+looks up certificate matching info using HTTPS;
+
+<li> <p> A TLS policy type 'dane-only', which looks up certificate or
+public-key matching info using DNSSEC. For example, a policy that is
+returned by a DANE+STS plugin; </p>
+
+<li> <p> A TLS policy type 'dane', provided that both the nexthop
+domain and its MX hosts are in DNSSEC-signed zones, and usable
+DNSSEC-signed TLSA records are discovered.  In other words, the
+effective TLS policy remains DANE and is not downgraded because the
+destination lacks DNSSEC and/or usable TLSA records; </p>
+
+<li> <p> A TLS policy type 'fingerprint', with digital fingerprints.
+This is a non-scalable solution for special deployments, mentioned
+here only for completeness. </p>
+
+</ul>
+
+<li> <p> A message that requires REQUIRETLS must be returned to the
+sender if any of the above requirements is not satisfied (no STARTTLS
+support, no secure lookup of MX servers, no trusted or no matching
+server certificate, or no server that announces REQUIRETLS support).
+</p>
+
+<li> <p> Returning an undeliverable message that requires REQUIRETLS
+comes with its own challenges: the return path may differ from the
+forward path, and the return path may not support REQUIRETLS all
+the way back to the sender, even if the forward path supported
+REQUIRETLS. </p>
+
+</ul>
+
+<h2> <a name="credits"> Credits </a> </h2>
+
+<ul>
+
+<li> In Postfix 3.10, Wietse Venema refactored SMTPUTF8 support and
+extended it to propagate REQUIRETLS and "TLS-Required: no" information.
+
+<li> In Postfix 3.11, Wietse added REQUIRETLS support to the Postfix
+SMTP client; added a "<tt>tls=<i>status</i>/requiretls=<i>status</i></tt>"
+field to the Postfix delivery status logging; added smtp_requiretls_policy
+support; added support for the "Require-TLS-ESMTP: yes" header to
+propagate REQUIRETLS through non-Postfix programs, specifically
+content filters. </li>
+
+</ul>
+
+</body>
+
+</html>
index bbe7794e721a6fd3e632cae140f55ecc641c769b..c9d07330a312e6206e048ebf7a5fe809c670b804 100644 (file)
@@ -51,6 +51,8 @@ configuration examples </a>
 
 <li> <a href="SMTPUTF8_README.html"> SMTPUTF8 Support </a>
 
+<li> <a href="REQUIRETLS_README.html"> REQUIRETLS Support </a>
+
 <li> <a href="MAILLOG_README.html"> Postfix logging to file or stdout </a>
 
 <li> <a href="COMPATIBILITY_README.html"> Backwards-Compatibility Safety Net</a>
index 73cfb1607c5b6bff99697035d358b0dc08b428f9..021f7c151f100233d9bf338fc698973c4981e20a 100644 (file)
@@ -54,7 +54,7 @@ lines whose first non-whitespace character is a `#'. </p>
 <li> <p> A logical line starts with non-whitespace text. A line
 that starts with whitespace continues a logical line. </p>
 
-<li> <p> A parameter value may refer to other parameters. </p>
+<li> <p> A parameter value may refer to functions or other parameters. </p>
 
 <ul>
 
@@ -89,6 +89,11 @@ and "&gt;". The comparison is numerical when both operands are all
 digits, otherwise the comparison is lexicographical. These forms
 are supported with Postfix versions &ge; 3.0. </p>
 
+<li> <p> The expression "${name{value}}" is replaced with the result
+from calling the function <i>name</i> with the argument <i>value</i>
+after stripping whitespace betwen the "{", the value, and the "}".
+An example is the domain_to_ascii{} function. </p>
+
 <li> <p> Each "value" is subject to recursive named parameter and
 relational expression evaluation, except where noted.  </p>
 
index 64b1e973c6011b15de576f266848331ec47f3116..8686603846f4b01df95320a005882bdd00d05613 100644 (file)
@@ -29,7 +29,7 @@ whose first non-whitespace character is a `#'.
 A logical line starts with non-whitespace text. A line that starts
 with whitespace continues a logical line.
 .IP \(bu
-A parameter value may refer to other parameters.
+A parameter value may refer to functions or other parameters.
 .RS
 .IP \(bu 
 The expressions "$name" and "${name}" are recursively replaced with
@@ -60,6 +60,11 @@ comparison is numerical when both operands are all digits, otherwise
 the comparison is lexicographical. These forms are supported with
 Postfix versions >= 3.0.
 .IP \(bu
+The expression "${name{value}}" is replaced with the result from
+calling the function \fIname\fR with the argument \fIvalue\fR
+after stripping whitespace betwen the "{", the value, and the "}".
+An example is the domain_to_ascii{} function.
+.IP \(bu
 Each "value" is subject to recursive named parameter and relational
 expression evaluation, except where noted.
 .IP \(bu
index f82e4b37192e2fdabb8fd03bb1c5f0203c6d1a5c..a352a322e516e8454c9930c3699371cea54c6b94 100644 (file)
@@ -19655,12 +19655,462 @@ no enforcement of TLS policy. This disables TLS policy lookup, and
 limits the Postfix SMTP client
 TLS security level to "may", that is, do not verify remote SMTP
 server certificates, and fall back to plaintext if TLS is unavailable.
-If a message contains a "TLS-Required: no" header, then Postfix
-will add that header to a delivery status notification for that
+If a message contains a "TLS-Required: no" header, then Postfix 
+will add that header to a delivery status notification for that 
 message. </p>
 
+<p> Note: the ESMTP REQUIRETLS option overrides the "TLS-Required:
+no" message header.  </p>
+
 <p> This feature is available in Postfix &ge; 3.10. </p>
 
+%PARAM requiretls_enable yes
+
+<p> Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+FROM" command. As defined in RFC 8689, when a message specifies
+REQUIRETLS: </p>
+
+<ul>
+
+<li> <p> deliveries with SMTP or LMTP must use a TLS connection, </p>
+
+<li> <p> to a securely looked up MX server (e.g., DNSSEC or MTA-STS),
+</p>
+
+<li> <p> with a matched server certificate (Postfix SMTP or LMTP
+client TLS security levels "secure", "verify", "fingerprint",
+dane-only, or opportunistic "dane"), </p>
+
+<li> <p> and the server must announce "REQUIRETLS" support after
+the STARTTLS handshake. </p>
+
+</ul>
+
+<p> When delivering a message that specifies REQUIRETLS, the Postfix
+SMTP client will try one or more servers, limited by the
+smtp_mx_address_limit and smtp_mx_session_limit parameters, until
+it finds an MX server that satisfies the above requirements. If
+such a server is not found, the Postfix SMTP or LMTP client returns
+the message as undeliverable. </p>
+
+<p> Notes: </p>
+
+<ul>
+
+<li> <p> With the Postfix SMTP or LMTP clients, REQUIRETLS enforcement
+is controlled with smtp_requiretls_policy or lmtp_requiretls_policy. It
+is initially not enforced for deliveries to local servers, including
+LMTP message stores and local content filters. </p>
+
+<li> <p> The ESMTP REQUIRETLS option overrides the "TLS-Required:
+no" message header. </p>
+
+</ul>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+%PARAM requiretls_redact_dsn yes
+
+<p> When sending a delivery status notification for an original
+message received with the REQUIRETLS option, do not send the original
+message body (as if that message was received with "RET=HDRS") and
+do not enforce REQUIRETLS (as if that message was received without
+REQUIRETLS). For a detailed discussion see RFC 8689 section 5. </p>
+
+<p> Note: the 'reverse' path for sending a delivery status notification
+may differ from the 'forward' path for receiving the original message. 
+Not every hop in the reverse path may support REQUIRETLS, even
+though every hop in the forward path supported it. The setting
+"requiretls_redact_dsn = no" may therefore result in the loss of a
+delivery status notification. </p>
+
+%PARAM smtp_requiretls_policy see 'postconf -d smtp_requiretls_policy' output
+
+<p> How the Postfix SMTP and LMTP client will enforce REQUIRETLS
+for messages received with the REQUIRETLS option. Policy examples
+for SMTP and LMTP are at the end. </p>
+
+<ul>
+
+<li> <p> Specify a list of items, separated with whitespace or
+comma; continue a long line by starting the next line with whitespace.
+</p>
+
+<li> <p> Each item must be an action (see below), or a type:table
+lookup table that must return an action (it must not return a
+type:table). </p>
+
+<li> <p> In the text that follows, the TLS next-hop destination for
+TCP connections is the recipient domain by default, but this can
+be overruled with destinations specified with transport_maps,
+relayhost, content_filter, or other routing features. The next-hop
+destination for LMTP over UNIX-domain connections is always the
+value of myhostname. </p>
+
+<li> <p> A type:table lookup table is searched with the TLS next-hop
+destination (without any [ ], <i>:service</i>, or <i>:port</i>).
+A non-ASCII next-hop destination is converted into ASCII form
+(Punycode) before making a policy query; lookup tables must answer
+ASCII-form queries. </p>
+
+<li> <p> A fixed-string table (such as hash:, btree:, or lmdb:) is
+also searched with the next-hop ".parent" domains (in a table key,
+prepend a '.' to a parent domain name). These ".parent" domain
+queries are not made with pattern-based lookup tables such as regexp:
+or pcre: or with socketmap: or tcp: tables.  </p>
+
+</ul>
+
+<p> Supported actions, assuming that TLS feature status logging is
+enabled with "smtp_log_tls_feature_status = yes" (the default): </p>
+
+<dl>
+
+<dt> <b> enforce </b> </dt> <dd> <p> Skip servers that do not support
+STARTTLS, skip connections without a server certificate match, and
+skip servers that don't support REQUIRETLS after STARTTLS. </p> <p>
+If a suitable server is found, request REQUIRETLS, deliver the
+message, and log "<tt>tls=<i>level</i>/requiretls</tt>" in delivery
+status logging, where <tt><i>level</i></tt> shows the current SMTP
+client TLS security level. </p> <p> If no suitable server is found,
+return the message as undeliverable and log a policy violation
+"<tt>tls=<i>level</i>/!requiretls:nocertmatch</tt>",
+"<tt>tls=<i>level</i>/!requiretls:noencryption</tt>", or
+"<tt>tls=<i>level</i>/!requiretls:nostarttls</tt>", as described
+under smtp_log_tls_feature_status. </p> <p> NOTE: this is also used
+implicitly when no REQUIRETLS policy match is found. </p> </dd>
+
+<dt> <b> opportunistic+starttls </b> </dt> <dd> <p> Skip servers
+that don't announce STARTTLS support, but do not require a server
+certificate match or remote REQUIRETLS support. </p> <p> If a
+suitable server is found, send REQUIRETLS if that server supports
+REQUIRETLS and log "<tt>tls=<i>level</i>/requiretls</tt>" or
+"<tt>tls=<i>level</i>/requiretls:nomatch</tt>" in delivery status
+logging, or simply deliver the message as if the sender did not
+request REQUIRETLS and log "<tt>tls=<i>level</i>/requiretls:none</tt>,
+where <tt><i>level</i></tt> shows the current SMTP client TLS
+security level. </p> <p> If no suitable server is found, return the
+message as undeliverable and log a policy violation
+"<tt>tls=<i>level</i>/!requiretls:noencryption</tt>" or
+"<tt>tls=<i>level</i>/!requiretls:nostarttls</tt>" as described
+under smtp_log_tls_feature_status. </p> <p> This relaxed policy can
+be appropriate for an outbound perimeter MTA, when forwarding
+messages from internal systems to the Internet, at a time that many
+domains support TLS but have no DANE or MTA-STS policies. </p> </dd>
+
+<dt> <b> opportunistic </b> </dt> <dd> <p> Do not require STARTTLS,
+a server certificate match, or remote REQUIRETLS support. </p> <p>
+Request REQUIRETLS if the server supports both STARTTLS and REQUIRETLS
+and log "<tt>tls=<i>level</i>/requiretls</tt>" or
+"<tt>tls=<i>level</i>/requiretls:nomatch</tt>" in delivery status
+logging, otherwise simply deliver the message as if the sender did
+not request REQUIRETLS and log "<tt>tls=<i>level</i>/requiretls:none</tt>,
+where <tt><i>level</i></tt> shows the current SMTP client TLS
+security level. </p> <p> This REQUIRETLS policy is non-intrusive,
+because there can be no REQUIRETLS policy violation. </p> <p> This
+weak policy can be appropriate for an inbound perimeter MTA, when
+forwarding messages from the Internet to internal servers or content
+filters that may not support STARTTLS or REQUIRETLS, using internal
+connections instead of the public Internet. This setting may also
+be useful for an outbound MTA to discover what destinations support
+some form of REQUIRETLS without risking mail delivery problems.
+</p> </dd>
+
+<dt> <b> disable </b> </dt> <dd> <p> Disable REQUIRETLS support.
+Deliver all messages as if the sender did not request REQUIRETLS.
+This may be used as a last-resort workaround when a server announces
+REQUIRETLS support, but the support is inoperable. </p></dd>
+
+</dl>
+
+<p> Notes: </p>
+
+<ul> 
+
+<li> <p> To match any name below the domain "example.com" specify
+a table entry with the storage key ".example.com" in type:table
+lookup tables that need an exact match. This is appropriate, for
+example, with hash:, btree: or lmdb:. </p>
+
+<li> <p> Do not specify a match pattern for ".domain" with regexp:,
+pcre:, socketmap:, or tcp:, as smtp_requiretls_policy will
+not query those tables with that form. </p>
+
+</ul>
+
+<p> SMTP client examples. See REQUIRETLS_README for discussion. </p>
+
+<ul>
+
+<li> <p> The simplest policy: when a sender requests REQUIRETLS,
+request REQUIRETLS when an external or internal server supports
+both STARTTLS and REQUIRETLS (lines 4, 5), and enforce a TLS
+certificate match only if the destination publishes a DANE or STS
+policy (line 3). When the sender requests REQUIRETLS, and an external
+or internal server does not support both STARTTLS and REQUIRETLS,
+simply deliver the message as if the sender did not request REQUIRETLS
+(lines 4, 5). </p>
+<pre>
+1 /etc/postfix/main.cf:
+2     smtp_tls_security_level = may
+3     smtp_tls_policy_maps = ...dane/sts plugin...
+4     smtp_requiretls_policy = opportunistic
+5     lmtp_requiretls_policy = opportunistic
+</pre>
+
+<li> <p> The default SMTP client REQUIRETLS policy (lines 4-12):
+when a sender requests REQUIRETLS, require that an external server
+supports STARTTLS and REQUIRETLS, and require that its server
+certificate matches a DANE or STS policy (lines 3, 12); make
+REQUIRETLS support optional for delivery to internal destinations
+or content filters specified as a symbolic name (lines 5-8) or as
+a numeric address (lines 9-11). An internationalized domain name
+must be specified in ASCII form (lines 6-7); the default policy
+below uses ${domain_to_ascii{$mydomain}} which returns $mydomain
+when the domain name contains only ASCII characters, and which
+returns Punycode (<tt>xn--mumble</tt>) when $mydomain contains
+non-ASCII. Note: if you specify a domain list outside main.cf, then
+the automatic $<i>name</i> expansions and Punycode conversions will
+not happen, and you will need to enter explicit ASCII domain
+names.</p>
+<pre>
+ 1 /etc/postfix/main.cf:
+ 2     smtp_tls_security_level = may
+ 3     smtp_tls_policy_maps = ...dane/sts plugin...
+ 4     smtp_requiretls_policy =
+ 5         inline:{
+ 6             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 7             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {localhost = opportunistic} }
+ 9         cidr:{
+10             {0.0.0.0/0 opportunistic},
+11             {::/0 opportunistic} },
+12         enforce
+</pre>
+
+<li> <p> An SMTP client REQUIRETLS policy that is relaxed for
+external destinations (line 12): when a sender requests REQUIRETLS,
+require that an external server supports STARTTLS but enforce a TLS
+certificate match only if the receiver publishes a DANE or STS
+policy (line 3), and do not require that an external server supports
+REQUIRETLS. Copy the above policy for internal destinations or
+content filters (lines 5-11). Again, an internationalized domain
+name must be specified in ASCII form (lines 6-7); the policy uses
+${domain_to_ascii{$mydomain}} which returns $mydomain when the
+domain name contains only ASCII characters, and which returns
+Punycode (<tt>xn--mumble</tt>) when $mydomain contains non-ASCII.
+Note: if you specify a domain list outside main.cf, then the automatic
+$<i>name</i> expansions and Punycode conversions will not happen,
+and you will need to enter explicit ASCII domain names.</p>
+<pre>
+ 1 /etc/postfix/main.cf:
+ 2     smtp_tls_security_level = may
+ 3     smtp_tls_policy_maps = ...dane/sts plugin...
+ 4     smtp_requiretls_policy =
+ 5         inline:{
+ 6             {${domain_to_ascii{$mydomain}} = opportunistic}
+ 7             {.${domain_to_ascii{$mydomain}} = opportunistic}
+ 8             {localhost = opportunistic} }
+ 9         cidr:{
+10             {0.0.0.0/0 opportunistic},
+11             {::/0 opportunistic} },
+12         opportunistic+starttls
+</pre>
+
+<li> <p> As above, with external destinations listed in a separate
+file for easier maintenance. </p>
+<pre>
+/etc/postfix/main.cf:
+    smtp_tls_security_level = may
+    smtp_tls_policy_maps = ...dane/sts plugin...
+    smtp_requiretls_policy =
+        inline:{
+            {${domain_to_ascii{$mydomain}} = opportunistic}
+            {.${domain_to_ascii{$mydomain}} = opportunistic}
+            {localhost = opportunistic} }
+        cidr:{
+            {0.0.0.0/0 opportunistic},
+            {::/0 opportunistic} },
+       hash:/etc/postfix/requiretls-per-site
+       opportunistic+starttls
+</pre>
+<pre>
+/etc/postfix/requiretls-per-site:
+    one.example        enforce
+    two.example        enforce
+    three.example      opportunistic
+    ...
+</pre>
+
+</ul>
+
+<p>
+LMTP client examples. See REQUIRETLS_README for discussion.
+</p>
+
+<ul>
+
+<li> <p> The default LMTP client REQUIRETLS policy: when a sender
+requests REQUIRETLS, request REQUIRETLS if the server supports
+REQUIRETLS, otherwise deliver the message as if the sender did not
+request REQUIRETLS. Note: with deliveries over a UNIX-domain socket,
+the next-hop destination for lmtp_requiretls_policy lookups will
+be the myhostname parameter value. </p>
+<pre>
+/etc/postfix/main.cf:
+    lmtp_requiretls_policy = opportunistic
+</pre>
+
+</ul>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+%PARAM lmtp_requiretls_policy opportunistic
+
+<p> The LMTP-specific version of the smtp_requiretls_policy
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+%PARAM lmtp_log_tls_feature_status yes
+
+<p> The LMTP-specific version of the smtp_log_tls_feature_status
+configuration parameter. See there for details. </p>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
+%PARAM smtp_log_tls_feature_status yes
+
+<p> Enable logging of TLS feature information in delivery status
+logging. This summarizes how features such as TLS and REQUIRETLS
+were used. A list of examples is at the end of this text. </p>
+
+<ul>
+
+<li> <p> The logging is inserted between the "<tt>delays=a/b/c/d</tt>"
+and the "<tt>dsn=x.y.z, status=...</tt>" information. </p>
+
+<li> <p> The general format is "<tt>tls=feature/feature/...</tt>".
+See below for examples. </p>
+
+<li> <p> The first feature name is the TLS security level:
+'<tt>none</tt>', '<tt>may</tt>', '<tt>encrypt</tt>', etc.  Other
+features are shown only if activated. The REQUIRETLS extension's
+feature name is '<tt>requiretls</tt>'. </p>
+
+<li> <p> When a '<tt>:<i>downgrade-level</i></tt>' is appended to
+a feature name, the feature was downgraded to the indicated level.
+A downgrade-level <tt><i>none</i></tt> indicates that the feature
+was unavailable. The result of a downgrade may violate a strict
+policy, but may still be compliant with a permissive policy. </p>
+
+<li> <p> When "<tt>!</tt>" is prepended to a feature, the policy
+for that feature was violated and the connection was not used.
+</p>
+
+<li> <p> When "?" is appended to a feature, the policy for that
+feature was undecided. This is typically the result of a lost
+connection or an incorrect configuration. </p>
+
+</ul>
+
+<p> Examples for TLS security levels: </p>
+
+<dl>
+
+<dt> tls=none </dt> <dd> A connection that did not use TLS. </dd>
+
+<dt> tls=may </dt> <dd> Opportunistic TLS. The connection was used
+after a successful TLS handshake. </dd>
+
+<dt> tls=may? </dt> <dd> Opportunistic TLS. The status was undecided
+because no connection was made, or no server could be reached. </dd>
+
+<dt> tls=may:none </dt> <dd> The client was willing to use TLS, but
+the remote server did not support STARTTLS, and the connection was
+used in plaintext as permitted by the opportunistic TLS policy. </dd>
+
+<dt> tls=dane </dt> <dd> DANE policy compliant, no downgrade. </dd>
+
+<dt> tls=(dane:halfdane) </dt> <dd> Opportunistic DANE. The connection
+security was downgraded to '<tt>halfdane</tt>' to indicate that
+mail server records were not DNSSEC-signed. </dd>
+
+<dt> tls=dane:encrypt </dt> <dd> Opportunistic DANE. The connection
+security was downgraded to '<tt>encrypt</tt>' because none of the TLSA
+records were usable. </dd>
+
+<dt> tls=dane? </dt> <dd> DANE policy status was undecided, because the
+connection failed before or in the TLS handshake. </dd>
+
+</dl>
+
+<p> Examples for REQUIRETLS policies (set with smtp_requiretls_policy),
+where "<tt><i>xxx</i></tt>" is a TLS security level: </p>
+
+<dl>
+
+<dt> tls=xxx/requiretls </dt> <dd> 'Enforce' policy compliant.
+After a successful TLS handshake that required a certificate match,
+the remote server announced REQUIRETLS support, and the client sent
+REQUIRETLS.
+
+<dt> tls=xxx/!requiretls:nocertmatch </dt> <dd> 'Enforce' policy
+violation. The connection was not used because the remote server
+certificate did not match as required by the TLS security policy,
+or no connection was made because the TLS security policy disabled
+server certificate matching. </dd>
+
+<dt> tls=xxx/requiretls:nocertmatch </dt> <dd> 'Opportunistic+starttls'
+or 'opportunistic' policy compliant. After a successful TLS handshake
+that did not require a server certificate match, the remote server
+announced REQUIRETLS support, and the client sent REQUIRETLS. </dd>
+
+<dt> tls=xxx/!requiretls:nostarttls </dt> <dd> 'Enforce' or
+'opportunistic+starttls' policy violation. The connection was not
+used because the remote server did not support STARTTLS. </dd>
+
+<dt> tls=xxx/!requiretls:noencryption </dt> <dd> 'Enforce' or
+'opportunistic+starttls' policy violation. No connection was made
+used because the TLS security policy disabled encryption. </dd>
+
+<dt> tls=xxx/!requiretls:none </dt> <dd> 'Enforce' policy violation.
+After a successful TLS handshake, the connection was not used because
+the remote server did not support REQUIRETLS. </dd>
+
+<dt> tls=xxx/requiretls:none </dt> <dd> 'Opportunistic+starttls'
+policy compliant. After a successful TLS handshake, the remote
+server did not announce REQUIRETLS support, and the connection was
+used without sending REQUIRETLS. </dd>
+
+<dt> tls=xxx/requiretls:none </dt> <dd> 'Opportunistic' policy
+compliant. The remote server did not announce support for STARTTLS
+or REQUIRETLS, and the connection was used without sending REQUIRETLS.
+</dd>
+
+<dt> tls=xxx/requiretls? </dt> <dd> Policy status was undecided,
+because the connection failed before or in the TLS handshake, or
+no connection was made due to a policy configuration error. </dd>
+
+</dl>
+
+<p> This feature is available in Postfix 3.11 and later. </p> 
+
+%PARAM requiretls_esmtp_header yes
+
+<p> Record the ESMTP REQUIRETLS request in a "Require-TLS-ESMTP:
+yes" message header. This is needed to propagate the REQUIRETLS
+request through an external content filter that is configured with
+smtpd_proxy_filter, with content_filter, or with a FILTER action in
+an SMTPD access(5) table, header_checks(5) or body_checks(5). The
+header is not needed with Milter-based content filters. </p>
+
+<p> This header will be visible to remote and local recipients.  It
+can safely be disabled if a configuration does not use any of the
+above content filters that need the header. </p>
+
+<p> This feature is available in Postfix 3.11 and later. </p>
+
 %PARAM smtpd_hide_client_session no
 
 <p> Do not include SMTP client session information in the Postfix
@@ -19747,6 +20197,53 @@ filter out").
 
 <p> This feature is available in Postfix &ge; 3.11. </p>
 
+%PARAM domain_to_ascii no default
+
+<p> A function that returns the ASCII representation of its domain
+name argument. If the argument contains only (7-bit) ASCII characters
+(A-label form), then domain_to_ascii{} will return the same value.
+If the argument contains a valid non-ASCII domain name (U-label
+form), then domain_to_ascii{} will return the corresponding Punycode
+(A-label form). Other argument values may result in a program
+start-up error. </p>
+
+<p> Examples: </p>
+
+<ul>
+
+<li> <p> The examples below assume that smtp_requiretls_policy
+contains the default setting: </p>
+<pre>
+smtp_requiretls_policy = inline:{
+    {${domain_to_ascii{$mydomain}} = opportunistic },
+    {.${domain_to_ascii{$mydomain}} = opportunistic },
+     ... }
+</pre>
+
+<li> <p> Example 1: when mydomain contains only ASCII characters,
+domain_to_ascii{} returns the same value. </p>
+<pre>
+# postconf -o 'mydomain=foo.example' -x smtp_requiretls_policy
+smtp_requiretls_policy = inline:{
+    {foo.example = opportunistic},
+    {.foo.example = opportunistic}, 
+     ... }
+</pre>
+
+<li> <p> Example 2: when mydomain contains non-ASCII characters,
+domain_to_ascii{} returns the Punycode representation. </p>
+<pre>
+# postconf -o 'mydomain=Ï€.example' -x smtp_requiretls_policy
+smtp_requiretls_policy = inline:{
+    {xn--1xa.example = opportunistic},
+    {.xn--1xa.example = opportunistic},
+    ... }
+</pre>
+
+</ul>
+
+<p> This feature is available in Postfix &ge; 3.11. </p>
+
 %PARAM non_empty_end_of_header_action fix_quietly
 
 <p> How the cleanup(8) daemon will process a message when the primary
index 3b3c4b93fb3a27dd5558ebd8c6e8b0be80653094..9c70fdfe6dcd78066d750de65318d36946bc3a9c 100644 (file)
@@ -1664,6 +1664,9 @@ REQUIRETLS
 RequireTLS
 requiretls
 sendopts
+TODO
+Onoop
+Orequiretls
 tz
 GID
 SIGKILL
@@ -1685,3 +1688,11 @@ PRELOAD
 rhansen
 XDG
 crosstalk
+HDRS
+deliverability
+certmatch
+Punycode
+unnormalized
+xa
+ascii
+halfdane
index e55d6dedfa2d06caa842deac91c44184f3eee54e..22de7611b49645152ec9c42f74c4928fe0d47521 100644 (file)
@@ -346,4 +346,8 @@ 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 
+void  void pol_stats_revert POL_STATS pstats 
+ feature feature etc 
+ feature feature etc where 
  policies policy policy domain If null this defaults to the
index 3534e30a55ad6a9cc1a623fc71b9972c0a199c6c..9385bcb4a612129d1a2446d161806ddc1a8ae36f 100644 (file)
@@ -159,10 +159,17 @@ proto  proto socketmap_table
  qmgr qmgr_deliver c qmgr qmgr_message c qmqpd qmqpd c 
  smtp smtp_proto c smtpd smtpd c verify verify c 
  operations Files cleanup cleanup h cleanup cleanup_message c 
- proto postconf proto pipe pipe c 
+ global ehlo_mask_test c local forward c smtpd smtpd c 
+ more alternate MX servers to try Files smtp smtp h 
+ Files sendmail sendmail c global rec_types h 
+ Files sendmail sendmail c 
+ Files sendmail sendmail c global rec_types h pickup pickup c 
+ pipe pipe c sendmail sendmail c 
+ smtp smtp c smtp smtp_proto c 
  bounce bounce c bounce bounce_notify_util c cleanup cleanup c 
  cleanup cleanup_message c smtp smtp c smtp smtp_connect c 
  Documentation edited for clarity Files pipe pipe c 
+ servers to try Files smtp smtp h smtp smtp_proto c 
  global mail_params h smtpd smtpd c 
  global mail_params h proto postconf proto smtp smtp c 
  proto postconf proto proto TLS_README html 
@@ -196,14 +203,50 @@ proto  proto COMPATIBILITY_README html
  request Reported by John Doe File tlsproxy tlsproxy c 
  smtpd smtpd c smtpd smtpd_chat c global mail_params h 
  Files Makefile in smtp smtp h smtp smtp_connect c 
- smtp smtp c smtp smtp h smtp smtp_connect c smtp smtp_params c 
+ global mail_params h bounce bounce c 
+ smtp lmtp_params c smtp smtp c smtp smtp_connect c 
+ 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 
+ connection requirements Files smtp smtp h smtp smtp_key c 
  Files smtp smtp h smtp smtp_key c smtp smtp_proto c 
+ smtp smtp c smtp smtp h smtp smtp_connect c smtp smtp_params c 
+ smtp smtp h smtp smtp_connect c smtp smtp_proto c 
+ sendmail sendmail c pipe pipe c postcat postcat c 
+ showq showq c 
+ content filters Files cleanup cleanup c 
+ style content filter Files smtpd smtpd c smtp smtpd_proto c 
+ global pol_stats_test c smtp smtp h smtp smtp_connect c 
  global mail_params h smtp lmtp_params c smtp smtp c 
+proto  proto REQUIRETLS_README html 
+proto  proto REQUIRETLS_README html global mail_params c 
+proto  proto REQUIRETLS_README html smtp smtp_reqtls_policy c 
+ sendmail sendmail c global rec_types h postcat postcat c 
  smtp smtp h smtp smtp_connect c smtp smtp_params c 
  the policies policy policy domain value This ignores
  TLSRPT Workaround when policies policy policy type is
+ proto postconf proto proto REQUIRETLS_README html 
  Christophe Kalt Files postqueue postqueue c 
  postqueue showq_compat c postqueue showq_json c showq showq c 
  role is received File tlsproxy tlsproxy c 
  Files proto postconf proto proto DEPRECATION_README html 
  features File postqueue postqueue c 
+proto  proto Makefile in 
+ are no more alternate MX servers to try Files smtp smtp h 
+ cleanup cleanup_out_recipient c discard discard c error error c 
+ don t satisfy connection requirements Files smtp smtp h 
+ File postscreen postscreen c 
+ filters Files cleanup cleanup c cleanup cleanup_message c 
+ global pol_stats c global pol_stats_test c smtp smtp h 
+ global rec_types h pickup pickup c 
+ global trace h global verify c global verify h local local h 
+ is case insensitive Files sendmail sendmail c 
+proto  proto REQUIRETLS_README html global mail_params hc 
+ showq showq c Ditto for sendmail O smtputf8 yes no 
+ smtpd smtpd c smtp smtp_connect c smtp smtp_proto c 
+ util mac_expand ref proto postconf proto proto Makefile in 
index d14f1018dd4ad15bfb51e653a185da41512812bf..6557a8e1f2935d4e21c99603546949d120f5afd3 100644 (file)
@@ -47,3 +47,4 @@ Inbound SMTP smuggling don t strip extra CR in CR LF CR CR LF
  pattern number number number text 
 to  to the lookup result With Postfix 3 11 and later specify
  has moved to to a table lookup result and the format for a
+after stripping whitespace betwen the the value and the 
index bed72b65e968902d6064b0397b054993237382f8..f2035aa3cd705c12b472d3e9bc447d695d2da7f6 100644 (file)
@@ -365,3 +365,19 @@ Postfix  Postfix legacy TLS Support
  TLSv1 2 with cipher ECDHE RSA AES256 GCM SHA384 256 256 bits 
  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 
+ li p The general format is tt tls feature feature tt 
+ li p When a feature is enclosed in tt tt and tt tt 
+ li p When tt tt is prepended to a feature the policy
+ tt none tt tt may tt tt encrypt tt etc Other
+ tt tls i level i requiretls tt where tt tt 
+where tt tt indicates the kind of policy violation described
+tt  tt tls i level i requiretls noencryption tt or
+ xn mumble mumble Punycode A label form that Postfix needs 
+ xn mumble mumble Punycode A label form that Postfix needs Note if you specify the domain list outside main cf then the automatic name expansions and Punycode conversions will not happen you will need to enter real domain names and will
+ pre pre 
+ contains the xn mumble mumble Punycode A label form that Postfix needs 
+ domain_to_ascii returns the xn mumble mumble Punycode A label form that Postfix needs This works around a limitation that may be eliminated in a future Postfix version 
+smtp_requiretls_policy smtp_requiretls_policy inline 
+ xn mumble mumble Punycode A label form that Postfix needs This works around a limitation that may be eliminated in a future Postfix version 
index ee926cca751477993da5ef9bbcd9673f4147f945..28311c39f99ee5095022893425ebf7177b792429 100644 (file)
@@ -1865,12 +1865,20 @@ DIGEST
 OSSL
 ossl
 deduplicates
+crosstalk
 intmax
 lflag
 REPLYCODE
 PTEST
 finalizer
+REQTLS
+reqtls
+Esmtp
+ENF
+tstats
+pstats
 enf
+allalnumus
 Christophe
 Kalt
 stdlib
index b63bed7d84bc7ba2b3ba9e4e480eb63b98e6430e..8713df0fb3f3ed315e58702010b85a0244aa199d 100644 (file)
@@ -100,6 +100,7 @@ Roessner
 bitflags
 Schulze
 tlspol
+TlsRequired
 Gueven
 Oemer
 Kozmenko
@@ -114,3 +115,5 @@ Jiaying
 PRI
 YP
 Natalenko
+nocertmatch
+pgnd
index b5bd4adf344f902fd53feed6f0718d17a4df86bf..77069db6b4b202b3a0145b4dfbe904117739cd34 100644 (file)
@@ -400,3 +400,7 @@ collectd
 Snawoot
 Zuplu
 tlspol
+nocertmatch
+noencryption
+nomatch
+nostarttls
index 469db866ae547a343456a0d8dcfe5035c4a9bcf3..957ccbed40ff806af1d08c66fb96a51ebeec5382 100644 (file)
@@ -182,6 +182,7 @@ bounce.o: ../../include/deliver_request.h
 bounce.o: ../../include/dsb_scan.h
 bounce.o: ../../include/dsn.h
 bounce.o: ../../include/dsn_buf.h
+bounce.o: ../../include/dsn_mask.h
 bounce.o: ../../include/hfrom_format.h
 bounce.o: ../../include/htable.h
 bounce.o: ../../include/iostuff.h
@@ -197,8 +198,10 @@ bounce.o: ../../include/msg.h
 bounce.o: ../../include/msg_stats.h
 bounce.o: ../../include/mymalloc.h
 bounce.o: ../../include/nvtable.h
+bounce.o: ../../include/pol_stats.h
 bounce.o: ../../include/rcpt_buf.h
 bounce.o: ../../include/recipient_list.h
+bounce.o: ../../include/sendopts.h
 bounce.o: ../../include/stringops.h
 bounce.o: ../../include/sys_defs.h
 bounce.o: ../../include/vbuf.h
@@ -275,6 +278,7 @@ bounce_notify_service.o: ../../include/msg_stats.h
 bounce_notify_service.o: ../../include/mymalloc.h
 bounce_notify_service.o: ../../include/name_mask.h
 bounce_notify_service.o: ../../include/nvtable.h
+bounce_notify_service.o: ../../include/pol_stats.h
 bounce_notify_service.o: ../../include/post_mail.h
 bounce_notify_service.o: ../../include/rcpt_buf.h
 bounce_notify_service.o: ../../include/rec_type.h
@@ -380,6 +384,7 @@ bounce_notify_verp.o: ../../include/msg_stats.h
 bounce_notify_verp.o: ../../include/mymalloc.h
 bounce_notify_verp.o: ../../include/name_mask.h
 bounce_notify_verp.o: ../../include/nvtable.h
+bounce_notify_verp.o: ../../include/pol_stats.h
 bounce_notify_verp.o: ../../include/post_mail.h
 bounce_notify_verp.o: ../../include/rcpt_buf.h
 bounce_notify_verp.o: ../../include/rec_type.h
@@ -416,6 +421,7 @@ bounce_one_service.o: ../../include/msg_stats.h
 bounce_one_service.o: ../../include/mymalloc.h
 bounce_one_service.o: ../../include/name_mask.h
 bounce_one_service.o: ../../include/nvtable.h
+bounce_one_service.o: ../../include/pol_stats.h
 bounce_one_service.o: ../../include/post_mail.h
 bounce_one_service.o: ../../include/rcpt_buf.h
 bounce_one_service.o: ../../include/rec_type.h
index 04f51553baffc8824a818d588835402630552b10..969db53389885b4593042c0354aa63373940d1cc 100644 (file)
 /* .IP "\fBtls_required_enable (yes)\fR"
 /*     Enable support for the "TLS-Required: no" message header, defined
 /*     in RFC 8689.
+/* .PP
+/*     Available in Postfix 3.11 and later:
+/* .IP "\fBrequiretls_redact_dsn (yes)\fR"
+/*     When sending a delivery status notification for an original
+/*     message received with the REQUIRETLS option, do not send the original
+/*     message body (as if that message was received with "RET=HDRS") and
+/*     do not enforce REQUIRETLS (as if that message was received without
+/*     REQUIRETLS).
 /* FILES
 /*     /var/spool/postfix/bounce/* non-delivery records
 /*     /var/spool/postfix/defer/* non-delivery records
 #include <rcpt_buf.h>
 #include <dsb_scan.h>
 #include <hfrom_format.h>
+#include <sendopts.h>
+#include <dsn_mask.h>
 
 /* Single-threaded server skeleton. */
 
@@ -218,6 +228,7 @@ char   *var_delay_rcpt;
 char   *var_bounce_tmpl;
 bool    var_threaded_bounce;
 char   *var_hfrom_format;              /* header_from_format */
+int     var_reqtls_redact_dsn;
 
  /*
   * We're single threaded, so we can avoid some memory allocation overhead.
@@ -312,6 +323,27 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
                                  &rcpt_buf->rcpt, &dsn_buf->dsn));
 }
 
+/* edit_notification_properties - bounce message filter */
+
+static void edit_notification_properties(int *sendopts, int *dsn_ret)
+{
+
+    /*
+     * If REQUIRETLS is requested, do not propagate "TLS-Required: no". See
+     * also down-stream code in bounce_header().
+     */
+    if (*sendopts & SOPT_REQUIRETLS_ESMTP)
+       *sendopts &= ~SOPT_REQUIRETLS_HEADER;
+
+    /*
+     * Redact delivery status notification for REQUIRETLS message.
+     */
+    if (var_reqtls_redact_dsn && (*sendopts & SOPT_REQUIRETLS_ESMTP)) {
+       *sendopts &= ~SOPT_REQUIRETLS_ESMTP;
+       *dsn_ret = DSN_RET_HDRS;
+    }
+}
+
 /* bounce_notify_proto - bounce_notify server protocol */
 
 static int bounce_notify_proto(char *service_name, VSTREAM *client,
@@ -368,6 +400,12 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
     if (flags & BOUNCE_FLAG_CLEAN)
        bounce_cleanup_register(service_name, STR(queue_id));
 
+    /*
+     * Handle REQUIRETLS etc. matters.
+     */
+    if (var_reqtls_enable && (sendopts & SOPT_REQUIRETLS_ALL))
+       edit_notification_properties(&sendopts, &dsn_ret);
+
     /*
      * Execute the request.
      */
@@ -436,6 +474,12 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
     if (flags & BOUNCE_FLAG_CLEAN)
        bounce_cleanup_register(service_name, STR(queue_id));
 
+    /*
+     * Handle REQUIRETLS etc. matters.
+     */
+    if (var_reqtls_enable && (sendopts & SOPT_REQUIRETLS_ALL))
+       edit_notification_properties(&sendopts, &dsn_ret);
+
     /*
      * Execute the request. Fall back to traditional notification if a bounce
      * was returned as undeliverable, because we don't want to VERPify those.
@@ -528,6 +572,12 @@ static int bounce_one_proto(char *service_name, VSTREAM *client)
                 rcpt_buf->dsn_notify, STR(dsn_buf->status),
                 STR(dsn_buf->action), STR(dsn_buf->reason));
 
+    /*
+     * Handle REQUIRETLS etc. matters.
+     */
+    if (var_reqtls_enable && (sendopts & SOPT_REQUIRETLS_ALL))
+       edit_notification_properties(&sendopts, &dsn_ret);
+
     /*
      * Execute the request.
      */
@@ -698,6 +748,7 @@ int     main(int argc, char **argv)
     };
     static const CONFIG_NBOOL_TABLE nbool_table[] = {
        VAR_THREADED_BOUNCE, DEF_THREADED_BOUNCE, &var_threaded_bounce,
+       VAR_REQTLS_REDACT_DSN, DEF_REQTLS_REDACT_DSN, &var_reqtls_redact_dsn,
        0,
     };
 
index f089e4c045d3b5bdfcc32be11463c691a4f8cf00..38112f8adedda1851fc9366ffc7e08d778428f92 100644 (file)
@@ -534,7 +534,8 @@ int     bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
     }
 
     /*
-     * Trade confidentiality against availability.
+     * Trade confidentiality against availability. See also up-stream code in
+     * edit_notification_properties().
      */
     if (var_tls_required_enable
        && (bounce_info->sendopts & SOPT_REQUIRETLS_HEADER) != 0)
index 306d3b537587436044f2ffa9f561e9d73f84b3e0..859a030cb66a2145bbef10b5cab1fc2ec09468c1 100644 (file)
@@ -827,6 +827,7 @@ cleanup_api.o: ../../include/msg_stats.h
 cleanup_api.o: ../../include/myflock.h
 cleanup_api.o: ../../include/mymalloc.h
 cleanup_api.o: ../../include/nvtable.h
+cleanup_api.o: ../../include/pol_stats.h
 cleanup_api.o: ../../include/rec_type.h
 cleanup_api.o: ../../include/recipient_list.h
 cleanup_api.o: ../../include/resolve_clnt.h
@@ -901,6 +902,7 @@ cleanup_bounce.o: ../../include/msg_stats.h
 cleanup_bounce.o: ../../include/myflock.h
 cleanup_bounce.o: ../../include/mymalloc.h
 cleanup_bounce.o: ../../include/nvtable.h
+cleanup_bounce.o: ../../include/pol_stats.h
 cleanup_bounce.o: ../../include/rec_attr_map.h
 cleanup_bounce.o: ../../include/rec_type.h
 cleanup_bounce.o: ../../include/recipient_list.h
@@ -1366,6 +1368,7 @@ cleanup_out_recipient.o: ../../include/msg_stats.h
 cleanup_out_recipient.o: ../../include/myflock.h
 cleanup_out_recipient.o: ../../include/mymalloc.h
 cleanup_out_recipient.o: ../../include/nvtable.h
+cleanup_out_recipient.o: ../../include/pol_stats.h
 cleanup_out_recipient.o: ../../include/rec_type.h
 cleanup_out_recipient.o: ../../include/recipient_list.h
 cleanup_out_recipient.o: ../../include/resolve_clnt.h
index f9861d1f10daec5653ffc4469ad2387102b42b13..7ade5c6c6d99bfdc0f5e1bcb23baec7ddbd01784 100644 (file)
 /* .IP "\fBtls_required_enable (yes)\fR"
 /*     Enable support for the "TLS-Required: no" message header, defined
 /*     in RFC 8689.
+/* .IP "\fBrequiretls_esmtp_header (yes)\fR"
+/*     Record the ESMTP REQUIRETLS request in a "Require-TLS-ESMTP:
+/*     yes" message header.
 /* MISCELLANEOUS CONTROLS
 /* .ad
 /* .fi
@@ -680,6 +683,7 @@ int     main(int argc, char **argv)
     single_server_main(argc, argv, cleanup_service,
                       CA_MAIL_SERVER_INT_TABLE(cleanup_int_table),
                       CA_MAIL_SERVER_BOOL_TABLE(cleanup_bool_table),
+                      CA_MAIL_SERVER_NBOOL_TABLE(cleanup_nbool_table),
                       CA_MAIL_SERVER_STR_TABLE(cleanup_str_table),
                       CA_MAIL_SERVER_TIME_TABLE(cleanup_time_table),
                       CA_MAIL_SERVER_PRE_INIT(cleanup_pre_jail),
index 58b747450a8ff36cbe0726f94503feec05970c77..680d87daa6647fbe9c283bb7ab5375876c9ac9c4 100644 (file)
@@ -133,6 +133,7 @@ typedef struct CLEANUP_STATE {
      * Internationalization, RequireTLS, etc.
      */
     int     sendopts;                  /* what support is desired */
+    int     reqtls_esmtp_hdr_seen;     /* valid Require-TLS-ESMTP header */
 } CLEANUP_STATE;
 
  /*
@@ -226,6 +227,7 @@ extern void cleanup_pre_jail(char *, char **);
 extern void cleanup_post_jail(char *, char **);
 extern const CONFIG_INT_TABLE cleanup_int_table[];
 extern const CONFIG_BOOL_TABLE cleanup_bool_table[];
+extern const CONFIG_NBOOL_TABLE cleanup_nbool_table[];
 extern const CONFIG_STR_TABLE cleanup_str_table[];
 extern const CONFIG_TIME_TABLE cleanup_time_table[];
 
index 6a0c6dac0af72b44724c80c82ba70c984b0c96df..87066e54e2beea6f218a39aedd361783599ff7f3 100644 (file)
@@ -80,6 +80,8 @@
 /* .IP CLEANUP_FLAG_AUTOUTF8
 /*     Autodetection: request SMTPUTF8 support if the message
 /*     contains an UTF8 message header, sender, or recipient.
+/* .IP CLEANUP_FLAG_REQTLS
+/*     The sender requested REQUIRETLS (RFC 8689) enforcement.
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8)
 /*     or \fBpostlogd\fR(8).
@@ -208,9 +210,15 @@ void    cleanup_control(CLEANUP_STATE *state, int flags)
     } else {
        state->err_mask = ~0;
     }
+
+    /*
+     * Propagate requests that are specified at the envelope level. This may
+     * be augmented later with information derived from message content.
+     */
     if (state->flags & CLEANUP_FLAG_SMTPUTF8)
        state->sendopts |= SMTPUTF8_FLAG_REQUESTED;
-    /* TODO(wietse) REQUIRETLS. */
+    if (state->flags & CLEANUP_FLAG_REQTLS)
+       state->sendopts |= SOPT_REQUIRETLS_ESMTP;
     if (msg_verbose)
        msg_info("server flags = %s", cleanup_strflags(state->flags));
 }
index 8358d122785b0a976e6b8ea27f32a628255c441c..87de4cc62b4bbb21bd6efce38b260856a031de40 100644 (file)
@@ -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;
     }
 }
index f7872d881c7a20c07158966b7dd1b47a91b812da..f613944b1c613d2d6828f494e073fe3bd2680368 100644 (file)
@@ -10,6 +10,8 @@
 /*
 /*     CONFIG_BOOL_TABLE cleanup_bool_table[];
 /*
+/*     CONFIG_NBOOL_TABLE cleanup_nbool_table[];
+/*
 /*     CONFIG_STR_TABLE cleanup_str_table[];
 /*
 /*     CONFIG_TIME_TABLE cleanup_time_table[];
@@ -181,6 +183,7 @@ char   *var_full_name_encoding_charset;     /* in =?charset?encoding?gibberish=? */
 int     var_force_mime_iconv;          /* force mime downgrade on input */
 int     var_cleanup_mask_stray_cr_lf;  /* replace stray CR or LF with space */
 char   *var_non_empty_eoh_action;      /* handle non-empty header terminator */
+bool    var_reqtls_esmtp_hdr;
 
 const CONFIG_INT_TABLE cleanup_int_table[] = {
     VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
@@ -202,6 +205,11 @@ const CONFIG_BOOL_TABLE cleanup_bool_table[] = {
     0,
 };
 
+const CONFIG_NBOOL_TABLE cleanup_nbool_table[] = {
+    VAR_REQTLS_ESMTP_HDR, DEF_REQTLS_ESMTP_HDR, &var_reqtls_esmtp_hdr,
+    0,
+};
+
 const CONFIG_TIME_TABLE cleanup_time_table[] = {
     VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
     VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, &var_milt_conn_time, 1, 0,
index 9f84bdcf3587fb9cf5b6394f34620042950fd663..91a7e225a33e5e414ee9b009086882c33434624a 100644 (file)
@@ -660,6 +660,13 @@ static void cleanup_header_callback(void *context, int header_class,
                msg_warn("ignoring malformed header: '%.100s'",
                         vstring_str(header_buf));
        }
+       if (hdr_opts->type == HDR_REQTLS_ESMTP && var_reqtls_esmtp_hdr
+           && var_reqtls_enable) {
+           if (strcasecmp(hdrval, "yes") == 0) {
+               state->sendopts |= SOPT_REQUIRETLS_ESMTP;
+               state->reqtls_esmtp_hdr_seen += 1;
+           }
+       }
        if (CLEANUP_OUT_OK(state)) {
            if (hdr_opts->flags & HDR_OPT_RR)
                state->resent = "Resent-";
@@ -894,6 +901,15 @@ static void cleanup_header_done_callback(void *context)
        }
     }
 
+    /*
+     * Add a "Require-TLS-ESMTP: yes" header to help propagate the REQUIRETLS
+     * ESMTP extension through a post-queue content filter.
+     */
+    if (var_reqtls_enable && var_reqtls_esmtp_hdr
+       && (state->sendopts & SOPT_REQUIRETLS_ESMTP)
+       && state->reqtls_esmtp_hdr_seen == 0)
+       cleanup_out_format(state, REC_TYPE_NORM, "Require-TLS-ESMTP: yes");
+
     /*
      * Place a dummy PTR record right after the last header so that we can
      * append headers without having to worry about clobbering the
index 003983b71920008699df2bd07d12557b092201e8..e4b059116d6724a8c1af77216754028bd5a65c8b 100644 (file)
@@ -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;
     }
index 51f7786e34c0fc8bc6b6c9cc66f837d49d7e15c4..d6eca2f66fe88ced68602ac41bb1b2c2b6052226 100644 (file)
@@ -141,6 +141,7 @@ CLEANUP_STATE *cleanup_state_alloc(VSTREAM *src)
     state->milter_dsn_buf = 0;
     state->free_regions = state->body_regions = state->curr_body_region = 0;
     state->sendopts = 0;
+    state->reqtls_esmtp_hdr_seen = 0;
     return (state);
 }
 
index 8ac5d242c9a454725e422401e5b829a5c2700888..585cf214e007ec5ee239300baced2f7e470f008f 100644 (file)
@@ -66,6 +66,7 @@ discard.o: ../../include/msg.h
 discard.o: ../../include/msg_stats.h
 discard.o: ../../include/mymalloc.h
 discard.o: ../../include/nvtable.h
+discard.o: ../../include/pol_stats.h
 discard.o: ../../include/recipient_list.h
 discard.o: ../../include/sent.h
 discard.o: ../../include/sys_defs.h
index f21b95cbc36d2f0c7b938c214adbf392ef40d3c7..43b728a9c077b1737c1ac0eef66ca98ed60cc85d 100644 (file)
@@ -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;
index 94d239cf33546d0f859cad579c0324621ae27cab..7a5c0d0f21af7d6aeacc55f8dad2076ec548c365 100644 (file)
@@ -69,6 +69,7 @@ error.o: ../../include/msg.h
 error.o: ../../include/msg_stats.h
 error.o: ../../include/mymalloc.h
 error.o: ../../include/nvtable.h
+error.o: ../../include/pol_stats.h
 error.o: ../../include/recipient_list.h
 error.o: ../../include/sys_defs.h
 error.o: ../../include/sys_exits.h
index e1ff1cb7ccafc1be4279d6add1f86988f9db59aa..cece6c64c6d3dfd0b90d6720170c84a00ad94456 100644 (file)
 
 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 POL_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;
index dfc28b6319097f62023a506ddec648e7900eeb48..01b9fa29116a00916aec06bc90e36a6a98c9dbb5 100644 (file)
@@ -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 \
+       pol_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 \
+       pol_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,8 +115,10 @@ 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
-TESTSRC        = rec2stream.c stream2rec.c recdump.c
+       hfrom_format.h rfc2047_code.h ascii_header_text.h sendopts.h \
+       pol_stats.h
+TESTSRC        = rec2stream.c stream2rec.c recdump.c dict_sqlite_test.c \
+       ehlo_mask_test.c haproxy_srvr_test.c sendopts_test.c pol_stats_test.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
 INCL   =
@@ -124,14 +128,14 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
        off_cvt quote_822_local rec2stream recdump resolve_clnt \
        resolve_local rewrite_clnt stream2rec string_list tok822_parse \
        quote_821_local mail_conf_time mime_state strip_addr \
-       verify_clnt xtext anvil_clnt scache ehlo_mask \
+       verify_clnt xtext anvil_clnt scache ehlo_mask_test \
        valid_mailhost_addr own_inet_addr header_body_checks \
        data_redirect addr_match_list safe_ultostr verify_sender_addr \
        mail_version mail_dict server_acl uxtext mail_parm_split \
        fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
        haproxy_srvr_test map_search delivered_hdr login_sender_match \
        compat_level config_known_tcp_ports hfrom_format rfc2047_code \
-       ascii_header_text sendopts_test dict_sqlite_test
+       ascii_header_text sendopts_test dict_sqlite_test pol_stats_test
 
 LIBS   = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
 LIB_DIR        = ../../lib
@@ -336,7 +340,7 @@ anvil_clnt: $(LIB) $(LIBS)
 scache: scache.c $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
-ehlo_mask: ehlo_mask.c $(LIB) $(LIBS)
+ehlo_mask_test: ehlo_mask_test.c $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
 valid_mailhost_addr: valid_mailhost_addr.c $(LIB) $(LIBS)
@@ -412,11 +416,15 @@ dict_sqlite_test: dict_sqlite_test.c dict_sqlite.o $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c dict_sqlite.o $(LIB) $(LIBS) \
            $(SYSLIBS) $(AUXLIBS_SQLITE)
 
+pol_stats_test: pol_stats_test.c pol_stats.o $(LIB) $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c pol_stats.o $(LIB) $(LIBS) \
+           $(SYSLIBS)
+
 config_known_tcp_ports: config_known_tcp_ports.c $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
 tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
-       xtext_test scache_multi_test ehlo_mask_test \
+       xtext_test scache_multi_test test_ehlo_mask \
        namadr_list_test mail_conf_time_test header_body_checks_tests \
        mail_version_test server_acl_test resolve_local_test maps_test \
        safe_ultostr_test mail_parm_split_test fold_addr_test \
@@ -425,7 +433,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
        normalize_mailhost_addr_test test_haproxy_srvr map_search_test \
        delivered_hdr_test login_sender_match_test compat_level_test \
        config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
-       ascii_header_text_test test_sendopts test_dict_sqlite
+       ascii_header_text_test test_sendopts test_dict_sqlite test_pol_stats
 
 mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
        mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
@@ -616,10 +624,8 @@ scache_multi_test: scache scache_multi.in scache_multi.ref
        diff scache_multi.ref scache_multi.tmp
        rm -f scache_multi.tmp
 
-ehlo_mask_test: ehlo_mask ehlo_mask.in ehlo_mask.ref
-       $(SHLIB_ENV) $(VALGRIND) ./ehlo_mask <ehlo_mask.in >ehlo_mask.tmp
-       diff ehlo_mask.ref ehlo_mask.tmp
-       rm -f ehlo_mask.tmp
+test_ehlo_mask: ehlo_mask_test
+       $(SHLIB_ENV) $(VALGRIND) ./ehlo_mask_test
 
 namadr_list_test: namadr_list namadr_list.in namadr_list.ref
        -$(SHLIB_ENV) sh namadr_list.in >namadr_list.tmp 2>&1
@@ -801,6 +807,9 @@ test_sendopts: update sendopts_test
 test_dict_sqlite: update dict_sqlite_test
        $(SHLIB_ENV) $(VALGRIND) ./dict_sqlite_test
 
+test_pol_stats: update pol_stats_test
+       $(SHLIB_ENV) $(VALGRIND) ./pol_stats_test
+
 clean:
        rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAPS)
 
@@ -808,7 +817,7 @@ tidy:       clean
 
 depend: $(MAKES)
        (sed '1,/^# do not edit/!d' Makefile.in; \
-       set -e; for i in [a-z][a-z0-9]*.c; do \
+       set -e; for i in [a-z][a-z0-9_]*.c; do \
            $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
            -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \
            -e 's/o: \.\//o: /' -e p -e '}' ; \
@@ -837,6 +846,7 @@ abounce.o: dsn_buf.h
 abounce.o: mail_params.h
 abounce.o: mail_proto.h
 abounce.o: msg_stats.h
+abounce.o: pol_stats.h
 abounce.o: recipient_list.h
 addr_match_list.o: ../../include/argv.h
 addr_match_list.o: ../../include/check_arg.h
@@ -919,6 +929,7 @@ bounce.o: log_adhoc.h
 bounce.o: mail_params.h
 bounce.o: mail_proto.h
 bounce.o: msg_stats.h
+bounce.o: pol_stats.h
 bounce.o: rcpt_print.h
 bounce.o: recipient_list.h
 bounce.o: trace.h
@@ -1109,6 +1120,7 @@ defer.o: mail_params.h
 defer.o: mail_proto.h
 defer.o: mail_queue.h
 defer.o: msg_stats.h
+defer.o: pol_stats.h
 defer.o: rcpt_print.h
 defer.o: recipient_list.h
 defer.o: trace.h
@@ -1156,6 +1168,7 @@ deliver_pass.o: info_log_addr_form.h
 deliver_pass.o: mail_params.h
 deliver_pass.o: mail_proto.h
 deliver_pass.o: msg_stats.h
+deliver_pass.o: pol_stats.h
 deliver_pass.o: rcpt_print.h
 deliver_pass.o: recipient_list.h
 deliver_request.o: ../../include/attr.h
@@ -1481,6 +1494,16 @@ ehlo_mask.o: ../../include/vbuf.h
 ehlo_mask.o: ../../include/vstring.h
 ehlo_mask.o: ehlo_mask.c
 ehlo_mask.o: ehlo_mask.h
+ehlo_mask_test.o: ../../include/check_arg.h
+ehlo_mask_test.o: ../../include/msg.h
+ehlo_mask_test.o: ../../include/msg_vstream.h
+ehlo_mask_test.o: ../../include/stringops.h
+ehlo_mask_test.o: ../../include/sys_defs.h
+ehlo_mask_test.o: ../../include/vbuf.h
+ehlo_mask_test.o: ../../include/vstream.h
+ehlo_mask_test.o: ../../include/vstring.h
+ehlo_mask_test.o: ehlo_mask.h
+ehlo_mask_test.o: ehlo_mask_test.c
 ext_prop.o: ../../include/check_arg.h
 ext_prop.o: ../../include/name_mask.h
 ext_prop.o: ../../include/sys_defs.h
@@ -1659,6 +1682,7 @@ log_adhoc.o: log_adhoc.c
 log_adhoc.o: log_adhoc.h
 log_adhoc.o: mail_params.h
 log_adhoc.o: msg_stats.h
+log_adhoc.o: pol_stats.h
 log_adhoc.o: recipient_list.h
 login_sender_match.o: ../../include/argv.h
 login_sender_match.o: ../../include/check_arg.h
@@ -1989,6 +2013,7 @@ mail_params.o: ../../include/inet_addr_list.h
 mail_params.o: ../../include/inet_proto.h
 mail_params.o: ../../include/iostuff.h
 mail_params.o: ../../include/logwriter.h
+mail_params.o: ../../include/mac_midna.h
 mail_params.o: ../../include/midna_domain.h
 mail_params.o: ../../include/mkmap.h
 mail_params.o: ../../include/msg.h
@@ -2417,6 +2442,24 @@ pipe_command.o: mail_params.h
 pipe_command.o: pipe_command.c
 pipe_command.o: pipe_command.h
 pipe_command.o: sys_exits.h
+pol_stats.o: ../../include/check_arg.h
+pol_stats.o: ../../include/msg.h
+pol_stats.o: ../../include/mymalloc.h
+pol_stats.o: ../../include/sys_defs.h
+pol_stats.o: ../../include/vbuf.h
+pol_stats.o: ../../include/vstring.h
+pol_stats.o: pol_stats.c
+pol_stats.o: pol_stats.h
+pol_stats_test.o: ../../include/check_arg.h
+pol_stats_test.o: ../../include/msg.h
+pol_stats_test.o: ../../include/msg_vstream.h
+pol_stats_test.o: ../../include/stringops.h
+pol_stats_test.o: ../../include/sys_defs.h
+pol_stats_test.o: ../../include/vbuf.h
+pol_stats_test.o: ../../include/vstream.h
+pol_stats_test.o: ../../include/vstring.h
+pol_stats_test.o: pol_stats.h
+pol_stats_test.o: pol_stats_test.c
 post_mail.o: ../../include/attr.h
 post_mail.o: ../../include/check_arg.h
 post_mail.o: ../../include/events.h
@@ -2567,6 +2610,7 @@ reject_deliver_request.o: deliver_request.h
 reject_deliver_request.o: dsn.h
 reject_deliver_request.o: dsn_buf.h
 reject_deliver_request.o: msg_stats.h
+reject_deliver_request.o: pol_stats.h
 reject_deliver_request.o: recipient_list.h
 reject_deliver_request.o: reject_deliver_request.c
 remove.o: ../../include/check_arg.h
@@ -2751,6 +2795,7 @@ sent.o: dsn_util.h
 sent.o: log_adhoc.h
 sent.o: mail_params.h
 sent.o: msg_stats.h
+sent.o: pol_stats.h
 sent.o: recipient_list.h
 sent.o: sent.c
 sent.o: sent.h
@@ -2958,6 +3003,7 @@ trace.o: log_adhoc.h
 trace.o: mail_params.h
 trace.o: mail_proto.h
 trace.o: msg_stats.h
+trace.o: pol_stats.h
 trace.o: rcpt_print.h
 trace.o: recipient_list.h
 trace.o: trace.c
@@ -3007,6 +3053,7 @@ verify.o: log_adhoc.h
 verify.o: mail_params.h
 verify.o: mail_proto.h
 verify.o: msg_stats.h
+verify.o: pol_stats.h
 verify.o: recipient_list.h
 verify.o: verify.c
 verify.o: verify.h
index a11ee7da86d39d52541c86b160d64c631ca2ad4f..3b2b49dc8b77d4ef2a53c0eaeed8dfaa5626efee 100644 (file)
@@ -6,12 +6,13 @@
 /* SYNOPSIS
 /*     #include <bounce.h>
 /*
-/*     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 POL_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 POL_STATS *tstats;
 /*     DSN     *dsn;
 /*
 /*     void    bounce_client_init(title, maps)
 /* 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 POL_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 POL_STATS *tstats;
 /*     DSN     *dsn;
 /* DESCRIPTION
 /*     This module implements the client interface to the message
 /*     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 POL_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 POL_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 POL_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 POL_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;
index e2d67bd3a86c2a44c9830fe8063efedb935065e9..b68810414647884363e902321294fe0ea14bcb8d 100644 (file)
   */
 #include <deliver_request.h>
 #include <dsn_buf.h>
+#include <pol_stats.h>
 
  /*
   * Client interface.
   */
 extern int bounce_append(int, const char *, MSG_STATS *, RECIPIENT *,
-                                const char *, DSN *);
+                                const char *, const POL_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 POL_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 POL_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 POL_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
index d281c446245cb9b0e0c524ae286e362515287eca..afcf47317033bbed49f602641422854d0ef07d4f 100644 (file)
@@ -55,6 +55,7 @@ static struct cleanup_flag_map cleanup_flag_map[] = {
     CLEANUP_FLAG_SMTP_REPLY, "enable_smtp_reply",
     CLEANUP_FLAG_SMTPUTF8, "smtputf8_requested",
     CLEANUP_FLAG_AUTOUTF8, "smtputf8_autodetect",
+    CLEANUP_FLAG_REQTLS, "requiretls_requested",
 };
 
 /* cleanup_strflags - map flags code to printable string */
index 0b1985b7076704bec30ccf82c15963bfc9323b10..6d7c03ada36a5316bc4768dff8bbaea21b321fa1 100644 (file)
@@ -25,6 +25,7 @@
 #define CLEANUP_FLAG_SMTP_REPLY        (1<<7)  /* Enable SMTP reply */
 #define CLEANUP_FLAG_SMTPUTF8  (1<<8)  /* SMTPUTF8 requested */
 #define CLEANUP_FLAG_AUTOUTF8  (1<<9)  /* Autodetect SMTPUTF8 */
+#define CLEANUP_FLAG_REQTLS    (1<<10) /* REQUIRETLS requested */
 
 #define CLEANUP_FLAG_FILTER_ALL        (CLEANUP_FLAG_FILTER | CLEANUP_FLAG_MILTER)
 
index 919484d63d6db60d4c0d588c97075d0a947d04c5..3f2144dfe5dc5efa1e37e4b0118b3d88a9f4d407 100644 (file)
@@ -6,12 +6,13 @@
 /* SYNOPSIS
 /*     #include <defer.h>
 /*
-/*     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 POL_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;
 /*     MSG_STATS *stats;
 /*     RECIPIENT *rcpt;
 /*     const char *relay;
+/*     const POL_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 POL_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
 /*     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
 
 int     defer_append(int flags, const char *id, MSG_STATS *stats,
                             RECIPIENT *rcpt, const char *relay,
-                            DSN *dsn)
+                            const POL_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 POL_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 POL_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));
 }
index a015052f5da1ca073eee5556053d4a7d52d572e1..ac0b7c205e3d96fa1c369ca039cda1210ee82b8b 100644 (file)
@@ -20,7 +20,7 @@
   * External interface.
   */
 extern int defer_append(int, const char *, MSG_STATS *, RECIPIENT *,
-                               const char *, DSN *);
+                               const char *, const POL_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 POL_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 POL_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
index 5452829459886094dc2b08fc62764ac792b22971..111e51865e208a7e6ab7b762643a2155aa53311b 100644 (file)
@@ -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);
     }
 
     /*
index df905e13f01de72c9c970944e74e530e3d4446da..adf8f0ca7962a0db4b24d9edef3a6f814a51aaaf 100644 (file)
@@ -20,6 +20,7 @@
 /*     #define EHLO_MASK_DSN           (1<<11)
 /*     #define EHLO_MASK_SMTPUTF8      (1<<12)
 /*     #define EHLO_MASK_CHUNKING      (1<<13)
+/*     #define EHLO_MASK_REQTLS        (1<<14)
 /*     #define EHLO_MASK_SILENT        (1<<15)
 /*
 /*     int     ehlo_mask(keyword_list)
   * The lookup table.
   */
 static const NAME_MASK ehlo_mask_table[] = {
-    "8BITMIME", EHLO_MASK_8BITMIME,
-    "AUTH", EHLO_MASK_AUTH,
-    "ETRN", EHLO_MASK_ETRN,
-    "PIPELINING", EHLO_MASK_PIPELINING,
-    "SIZE", EHLO_MASK_SIZE,
-    "VERP", EHLO_MASK_VERP,
-    "VRFY", EHLO_MASK_VRFY,
-    "XCLIENT", EHLO_MASK_XCLIENT,
-    "XFORWARD", EHLO_MASK_XFORWARD,
-    "STARTTLS", EHLO_MASK_STARTTLS,
-    "ENHANCEDSTATUSCODES", EHLO_MASK_ENHANCEDSTATUSCODES,
-    "DSN", EHLO_MASK_DSN,
-    "EHLO_MASK_SMTPUTF8", EHLO_MASK_SMTPUTF8,
-    "SMTPUTF8", EHLO_MASK_SMTPUTF8,
-    "CHUNKING", EHLO_MASK_CHUNKING,
-    "SILENT-DISCARD", EHLO_MASK_SILENT,        /* XXX In-band signaling */
+    EHLO_VERB_8BITMIME, EHLO_MASK_8BITMIME,
+    EHLO_VERB_AUTH, EHLO_MASK_AUTH,
+    EHLO_VERB_ETRN, EHLO_MASK_ETRN,
+    EHLO_VERB_PIPELINING, EHLO_MASK_PIPELINING,
+    EHLO_VERB_SIZE, EHLO_MASK_SIZE,
+    EHLO_VERB_VERP, EHLO_MASK_VERP,
+    EHLO_VERB_VRFY, EHLO_MASK_VRFY,
+    EHLO_VERB_XCLIENT, EHLO_MASK_XCLIENT,
+    EHLO_VERB_XFORWARD, EHLO_MASK_XFORWARD,
+    EHLO_VERB_STARTTLS, EHLO_MASK_STARTTLS,
+    EHLO_VERB_ENHANCEDSTATUSCODES, EHLO_MASK_ENHANCEDSTATUSCODES,
+    EHLO_VERB_DSN, EHLO_MASK_DSN,
+    EHLO_VERB_SMTPUTF8, EHLO_MASK_SMTPUTF8,
+    EHLO_VERB_CHUNKING, EHLO_MASK_CHUNKING,
+    EHLO_VERB_REQTLS, EHLO_MASK_REQTLS,
+    EHLO_VERB_SILENT, EHLO_MASK_SILENT,
     0,
 };
 
@@ -114,32 +115,3 @@ const char *str_ehlo_mask(int mask_bits)
      */
     return (str_name_mask("ehlo bitmask", ehlo_mask_table, mask_bits));
 }
-
-#ifdef TEST
-
- /*
-  * Stand-alone test program.
-  */
-#include <stdlib.h>
-#include <vstream.h>
-#include <vstring.h>
-#include <vstring_vstream.h>
-
-int     main(int unused_argc, char **unused_argv)
-{
-    int     mask_bits;
-    VSTRING *buf = vstring_alloc(1);
-    const char *mask_string;
-
-    while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
-       mask_bits = ehlo_mask(vstring_str(buf));
-       mask_string = str_ehlo_mask(mask_bits);
-       vstream_printf("%s -> 0x%x -> %s\n", vstring_str(buf), mask_bits,
-                      mask_string);
-       vstream_fflush(VSTREAM_OUT);
-    }
-    vstring_free(buf);
-    exit(0);
-}
-
-#endif
index 9a318970d11c8ae2116caea9422b5c8f297caa06..c8d1fae19b039bd4afd0a2f6fe55ce3bbb2af93a 100644 (file)
@@ -12,7 +12,7 @@
 /* .nf
 
  /*
-  * External interface.
+  * Bit flags.
   */
 #define EHLO_MASK_8BITMIME     (1<<0)  /* start of first byte */
 #define EHLO_MASK_PIPELINING   (1<<1)
 #define EHLO_MASK_DSN          (1<<11)
 #define EHLO_MASK_SMTPUTF8     (1<<12)
 #define EHLO_MASK_CHUNKING     (1<<13)
-#define EHLO_MASK_SILENT       (1<<15)
+#define EHLO_MASK_REQTLS       (1<<14)
+#define EHLO_MASK_SILENT       (1<<15) /* in-band signaling */
 
+ /*
+  * ESMTP verbs.
+  */
+#define EHLO_VERB_8BITMIME     "8BITMIME"
+#define EHLO_VERB_PIPELINING   "PIPELINING"
+#define EHLO_VERB_SIZE                 "SIZE"
+#define EHLO_VERB_VRFY                 "VRFY"
+#define EHLO_VERB_ETRN                 "ETRN"
+#define EHLO_VERB_AUTH                 "AUTH"
+#define EHLO_VERB_VERP                 "VERP"
+#define EHLO_VERB_STARTTLS     "STARTTLS"
+#define EHLO_VERB_XCLIENT      "XCLIENT"
+#define EHLO_VERB_XFORWARD     "XFORWARD"
+#define EHLO_VERB_ENHANCEDSTATUSCODES  "ENHANCEDSTATUSCODES"
+#define EHLO_VERB_DSN          "DSN"
+#define EHLO_VERB_SMTPUTF8     "SMTPUTF8"
+#define EHLO_VERB_CHUNKING     "CHUNKING"
+#define EHLO_VERB_REQTLS       "REQUIRETLS"
+#define EHLO_VERB_SILENT       "SILENT-DISCARD"        /* in-band signaling */
+
+ /*
+  * Functions.
+  */
 extern int ehlo_mask(const char *);
 extern const char *str_ehlo_mask(int);
 
diff --git a/postfix/src/global/ehlo_mask.in b/postfix/src/global/ehlo_mask.in
deleted file mode 100644 (file)
index 50fc248..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-starttls, 8bitmime, verp, etrn, etrn
-foobar, auth, pipelining, size, vrfy
-xclient, xforward
diff --git a/postfix/src/global/ehlo_mask.ref b/postfix/src/global/ehlo_mask.ref
deleted file mode 100644 (file)
index e865ab6..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-starttls, 8bitmime, verp, etrn, etrn -> 0xd1 -> 8BITMIME ETRN VERP STARTTLS
-foobar, auth, pipelining, size, vrfy -> 0x2e -> AUTH PIPELINING SIZE VRFY
-xclient, xforward -> 0x300 -> XCLIENT XFORWARD
diff --git a/postfix/src/global/ehlo_mask_test.c b/postfix/src/global/ehlo_mask_test.c
new file mode 100644 (file)
index 0000000..fd0c2c8
--- /dev/null
@@ -0,0 +1,150 @@
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringops.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+  * Global library.
+  */
+#include <ehlo_mask.h>
+
+ /*
+  * Tests and test cases.
+  */
+typedef struct TEST_CASE {
+    const char *label;                 /* identifies test case */
+    const char *raw;
+    int     mask;
+    const char *text;
+} TEST_CASE;
+
+ /*
+  * Verify that each verb has its unique bit mask, and vice versa.
+  */
+static const TEST_CASE test_cases[] = {
+    {"8BITMIME",
+       EHLO_VERB_8BITMIME,
+       EHLO_MASK_8BITMIME,
+       "8BITMIME"
+    },
+    {"8bitmime",
+       "8bitmime",
+       EHLO_MASK_8BITMIME,
+       "8BITMIME"
+    },
+    {"PIPELINING",
+       EHLO_VERB_PIPELINING,
+       EHLO_MASK_PIPELINING,
+       "PIPELINING"
+    },
+    {"SIZE",
+       EHLO_VERB_SIZE,
+       EHLO_MASK_SIZE,
+       "SIZE"
+    },
+    {"VRFY",
+       EHLO_VERB_VRFY,
+       EHLO_MASK_VRFY,
+       "VRFY"
+    },
+    {"ETRN",
+       EHLO_VERB_ETRN,
+       EHLO_MASK_ETRN,
+       "ETRN"
+    },
+    {"AUTH",
+       EHLO_VERB_AUTH,
+       EHLO_MASK_AUTH,
+       "AUTH"
+    },
+    {"VERP",
+       EHLO_VERB_VERP,
+       EHLO_MASK_VERP,
+       "VERP"
+    },
+    {"STARTTLS",
+       EHLO_VERB_STARTTLS,
+       EHLO_MASK_STARTTLS,
+       "STARTTLS"
+    },
+    {"XCLIENT",
+       EHLO_VERB_XCLIENT,
+       EHLO_MASK_XCLIENT,
+       "XCLIENT"
+    },
+    {"ENHANCEDSTATUSCODES",
+       EHLO_VERB_ENHANCEDSTATUSCODES,
+       EHLO_MASK_ENHANCEDSTATUSCODES,
+       "ENHANCEDSTATUSCODES"
+    },
+    {"DSN",
+       EHLO_VERB_DSN,
+       EHLO_MASK_DSN,
+       "DSN"
+    },
+    {"SMTPUTF8",
+       EHLO_VERB_SMTPUTF8,
+       EHLO_MASK_SMTPUTF8,
+       "SMTPUTF8"
+    },
+    {"CHUNKING",
+       EHLO_VERB_CHUNKING,
+       EHLO_MASK_CHUNKING,
+       "CHUNKING"
+    },
+    {"REQUIRETLS",
+       EHLO_VERB_REQTLS,
+       EHLO_MASK_REQTLS,
+       "REQUIRETLS"
+    },
+    {"SILENT",
+       EHLO_VERB_SILENT,
+       EHLO_MASK_SILENT,
+       "SILENT-DISCARD"
+    },
+    {0},
+};
+
+int     main(int argc, char **argv)
+{
+    const TEST_CASE *tp;
+    int     pass = 0;
+    int     fail = 0;
+    const char *got_text;
+    int     got_mask;
+
+    msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+
+    /*
+     * Verify the mapping in both directions.
+     */
+    for (tp = test_cases; tp->label != 0; tp++) {
+       msg_info("RUN  %s", tp->label);
+       if ((got_mask = ehlo_mask(tp->raw)) != tp->mask) {
+           msg_warn("got mask '0x%x', want: '0x%x'", got_mask, tp->mask);
+           fail++;
+           msg_info("FAIL %s", tp->label);
+       } else if ((got_text = str_ehlo_mask(tp->mask)),
+                  strcmp(got_text, tp->text) != 0) {
+           msg_warn("got text '%s', want: '%s'", got_text, tp->text);
+           fail++;
+           msg_info("FAIL %s", tp->label);
+       } else {
+           msg_info("PASS %s", tp->label);
+           pass++;
+       }
+    }
+    msg_info("PASS=%d FAIL=%d", pass, fail);
+    exit(fail != 0);
+}
index 1be71a4eda3b1aa19e4c007bc3670f6e9a7ad4ae..5a984df5fd18f0771794ffacf22af2b825f526b6 100644 (file)
@@ -91,6 +91,7 @@ static HEADER_OPTS header_opts[] = {
     "Sender", HDR_SENDER, HDR_OPT_SENDER,
     "To", HDR_TO, HDR_OPT_XRECIP,
     "TLS-Required", HDR_TLS_REQUIRED, 0,
+    "Require-TLS-ESMTP", HDR_REQTLS_ESMTP, 0,
 };
 
 #define HEADER_OPTS_SIZE (sizeof(header_opts) / sizeof(header_opts[0]))
index 1af23b22ca2c14d43d2f446abc2cb832b77280df..1e8bd5b434a52e1c9c0d6dfa75ca6580d6eac5d0 100644 (file)
@@ -55,6 +55,7 @@ typedef struct {
 #define HDR_MIME_VERSION               30
 #define HDR_DISP_NOTIFICATION          31
 #define HDR_TLS_REQUIRED               32      /* RFC 8689 */
+#define HDR_REQTLS_ESMTP               33      /* non-standard */
 
  /*
   * Header flags.
index e00e93054e48a948f75baea2737cc4f481ef6276..4a0255ad207202059b4e78f6dd9a8b181014393d 100644 (file)
@@ -6,11 +6,12 @@
 /* SYNOPSIS
 /*     #include <log_adhoc.h>
 /*
-/*     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 POL_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 <log_adhoc.h>
 #include <mail_params.h>
 #include <info_log_addr_form.h>
+#include <pol_stats.h>
 
  /*
   * 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 POL_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,16 @@ 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.
+     */
+#ifdef USE_TLS
+    if (tstats && pol_stats_used(tstats)) {
+       vstring_sprintf_append(buf, ", tls=");
+       pol_stats_format(buf, tstats);
+    }
+#endif
+
     /*
      * Finally, the delivery status.
      */
index 583cb6122b2b976355fc1e9add6341dc6639e63a..059d506a17082a767ac8c982d4477fdb6f299dde 100644 (file)
 #include <recipient_list.h>
 #include <dsn.h>
 #include <msg_stats.h>
+#include <pol_stats.h>
 
  /*
   * Client interface.
   */
 extern void log_adhoc(const char *, MSG_STATS *, RECIPIENT *, const char *,
-                             DSN *, const char *);
+                             const POL_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
index a0da78d1aadab5bed58586c76f3afaf3fa72345b..607ab3a9091cf1270a8a143af31106b8eb50fda5 100644 (file)
 /*     int     var_smtputf8_enable;
 /*     int     var_strict_smtputf8;
 /*     char    *var_smtputf8_autoclass;
+/*     int     var_reqtls_enable;
 /*     int     var_tls_required_enable;
 /*     int     var_idna2003_compat;
 /*     char    *var_compatibility_level;
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
 #include <iostuff.h>
 #include <midna_domain.h>
 #include <logwriter.h>
+#include <mac_midna.h>
 
 /* Global library. */
 
@@ -370,6 +375,7 @@ char   *var_dsn_filter;
 int     var_smtputf8_enable;
 int     var_strict_smtputf8;
 char   *var_smtputf8_autoclass;
+int     var_reqtls_enable;
 int     var_tls_required_enable;
 int     var_idna2003_compat;
 char   *var_compatibility_level;
@@ -480,6 +486,7 @@ static const char *check_mydomainname(void)
        /* DO NOT CALL GETHOSTBYNAME OR GETNAMEINFO HERE - EDIT MAIN.CF */
        return (DEF_MYDOMAIN);
     /* DO NOT CALL GETHOSTBYNAME OR GETNAMEINFO HERE - EDIT MAIN.CF */
+    /* TODO(wietse) handle Unicode variants for 'dot'. */
     return (dot + 1);
 }
 
@@ -793,6 +800,7 @@ void    mail_params_init()
        VAR_SMTPUTF8_ENABLE, DEF_SMTPUTF8_ENABLE, &var_smtputf8_enable,
        VAR_IDNA2003_COMPAT, DEF_IDNA2003_COMPAT, &var_idna2003_compat,
        VAR_RESPECTFUL_LOGGING, DEF_RESPECTFUL_LOGGING, &var_respectful_logging,
+       VAR_REQTLS_ENABLE, DEF_REQTLS_ENABLE, &var_reqtls_enable,
        VAR_TLSREQUIRED_ENABLE, DEF_TLSREQUIRED_ENABLE, &var_tls_required_enable,
        0,
     };
@@ -922,6 +930,15 @@ void    mail_params_init()
        0,
     };
     const char *cp;
+    static int first = 1;
+
+    /*
+     * Register named functions.
+     */
+    if (first) {
+       mac_midna_register();
+       first = 0;
+    }
 
     /*
      * Extract compatibility level first, so that we can determine what
index 563e4c625927d7765811e89f825ba71fdd3eaf46..471c4a558cde548fffc1a17cc772fa13b816c62d 100644 (file)
@@ -4431,6 +4431,34 @@ extern int var_idna2003_compat;
 #define DEF_TLSREQUIRED_ENABLE         "yes"
 extern int var_tls_required_enable;
 
+#define VAR_REQTLS_ENABLE              "requiretls_enable"
+#define DEF_REQTLS_ENABLE              "yes"
+extern int var_reqtls_enable;
+
+#define VAR_SMTP_REQTLS_POLICY         "smtp_requiretls_policy"
+#define DEF_SMTP_REQTLS_POLICY         "inline:{{${domain_to_ascii{$mydomain}}=opportunistic}, {.${domain_to_ascii{$mydomain}}=opportunistic}, {localhost=opportunistic}}, cidr:{{0.0.0.0/0 opportunistic}, {::/0 opportunistic}}, enforce"
+extern char *var_smtp_reqtls_policy;
+
+#define VAR_LMTP_REQTLS_POLICY         "lmtp_requiretls_policy"
+#define DEF_LMTP_REQTLS_POLICY         "opportunistic"
+
+#define VAR_REQTLS_REDACT_DSN          "requiretls_redact_dsn"
+#define DEF_REQTLS_REDACT_DSN          "yes"
+extern int var_reqtls_redact_dsn;
+
+#define VAR_REQTLS_ESMTP_HDR           "requiretls_esmtp_header"
+#define DEF_REQTLS_ESMTP_HDR           "yes"
+extern bool var_reqtls_esmtp_hdr;
+
+ /*
+  * 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
index c51f2806761b31a6830f57c088fdb44e3d8f9bcf..1d62039934b41972f51599f1bdd8a3516aafc424 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20251105"
+#define MAIL_RELEASE_DATE      "20251114"
 #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/pol_stats.c b/postfix/src/global/pol_stats.c
new file mode 100644 (file)
index 0000000..83cbedb
--- /dev/null
@@ -0,0 +1,227 @@
+/*++
+/* NAME
+/*     pol_stats 3
+/* SUMMARY
+/*     manage per-feature policy compliance status
+/* SYNOPSIS
+/*     #include <pol_stats.h>
+/*
+/*     POL_STATS *pol_stats_create(void)
+/*
+/*     void    pol_stats_revert(POL_STATS *pstats)
+/*
+/*     void    pol_stats_free(POL_STATS *pstats)
+/*
+/*     void    pol_stat_activate(
+/*     POL_STATS *pstats,
+/*     int     idx,
+/*     const char *init_name)
+/*
+/*     void    pol_stat_decide(
+/*     POL_STATS *pstats,
+/*     int     idx,
+/*     const char *final_name,
+/*     int     status)
+/*
+/*     int     pol_stats_used(POL_STATS *pstats)
+/*
+/*     pol_stats_format(
+/*     VSTRING *buf,
+/*     const POL_STATS *pstats)
+/* DESCRIPTION
+/*     This module records for each activated feature whether the
+/*     current program state satisfies the policy requirements for that
+/*     feature. For example, whether a TLS handshake result matches
+/*     DANE or STS requirements. The combined feature state can
+/*     concisely be formatted with pol_stats_format() and exposed
+/*     with logging.
+/*
+/*     Each feature has an initial name with the desired policy state,
+/*     and a final name that corresponds to the policy state that was
+/*     actually achieved. If the two names differ, then both names will
+/*     be logged as initial:final.
+/*
+/*     pol_stats_create() creates one POL_STATS instance with all status
+/*     information set to POL_STAT_INACTIVE.
+/*
+/*     pol_stats_revert() reverts all changes after pol_stats_create().
+/*
+/*     pol_stats_free() recycles storage for a POL_STATS instance.
+/*
+/*     Specific status information is accessed with an index. Valid
+/*     indices are 0 or 1 (the caller decides their purpose).
+/*
+/*     pol_stat_activate() changes the status in pstats at index idx
+/*     from POL_STAT_INACTIVE to POL_STAT_UNDECIDED, and updates the
+/*     feature's initial name (pointer copy). Calls with an invalid
+/*     index result in a panic(), and calls with an already active
+/*     index result in a warning.
+/*
+/*     pol_stat_decide() updates the status in pstats at index idx from
+/*     POL_STAT_UNDECIDED to POL_STAT_COMPLIANT or POL_STAT_VIOLATION,
+/*     and records its final name or NULL (pointer copy). 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.
+/*
+/*     pol_stats_used() returns the number of activated categories for
+/*     its argument.
+/*
+/*     pol_stats_format() formats TLS feature status information as
+/*     feature/feature/etc., where:
+/* .IP \(bu
+/*     Each feature name is the initial name given to
+/*     pol_stat_activate().
+/* .IP \(bu
+/*     When ':final-name' is appended to a feature name, the feature
+/*     was downgraded to the final name given to pol_stats_decide().
+/*     This does not necessarily imply policy compliance or
+/*     non-compliance for that feature.
+/* .IP \(bu
+/*     When "!" is prepended to the feature, the policy for that feature
+/*     was not satisfied and the operation was blocked.
+/* .IP \(bu
+/*     When "?" is appended to the feature, the policy for that feature
+/*     was left undecided. This typically caused by a lost connection.
+/* 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 <sys_defs.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <mymalloc.h>
+
+ /*
+  * Global library.
+  */
+#include <pol_stats.h>
+
+/* pol_stats_create - create an all-inactive POL_STATS instance */
+
+POL_STATS *pol_stats_create(void)
+{
+    POL_STATS *pstats;
+    POL_STAT *tp;
+
+#define POL_STAT_INIT(tp) do { \
+       (tp)->init_name = 0; \
+       (tp)->final_name = 0; \
+       (tp)->status = POL_STAT_INACTIVE; \
+    } while (0);
+
+    pstats = (POL_STATS *) mymalloc(sizeof(*pstats));
+    pstats->used = 0;
+    for (tp = pstats->st; tp < pstats->st + POL_STATS_SIZE; tp++)
+       POL_STAT_INIT(tp);
+    return pstats;
+}
+
+/* pol_stats_revert - revert changes after pol_stats_create() */
+
+void    pol_stats_revert(POL_STATS *pstats)
+{
+    POL_STAT *tp;
+
+    pstats->used = 0;
+    for (tp = pstats->st; tp < pstats->st + POL_STATS_SIZE; tp++)
+       if (tp->status != POL_STAT_INACTIVE)
+           POL_STAT_INIT(tp);
+}
+
+/* pol_stats_free - POL_STATS destructor */
+
+void    pol_stats_free(POL_STATS *pstats)
+{
+    myfree(pstats);
+}
+
+/* pol_stat_activate - activate status at index */
+
+void    pol_stat_activate(POL_STATS *pstats, int idx, const char *init_name)
+{
+    POL_STAT *pol_stat;
+
+    if (idx < 0 || idx >= POL_STATS_SIZE)
+       msg_panic("%s: bad index: %d", __func__, idx);
+    pol_stat = pstats->st + idx;
+    if (pol_stat->status != POL_STAT_INACTIVE)
+       msg_warn("%s: already active POL_STAT at index %d", __func__, idx);
+    pol_stat->init_name = init_name;
+    pol_stat->final_name = 0;
+    pol_stat->status = POL_STAT_UNDECIDED;
+    pstats->used += 1;
+}
+
+/* pol_stat_decide - update undecided status at index */
+
+extern void pol_stat_decide(POL_STATS *pstats, int idx, const char *final_name,
+                                   int status)
+{
+    POL_STAT *pol_stat;
+
+    if (status != POL_STAT_VIOLATION && status != POL_STAT_COMPLIANT)
+       msg_panic("%s: bad new status: %d", __func__, status);
+    if (idx < 0 || idx >= POL_STATS_SIZE)
+       msg_panic("%s: bad index: %d", __func__, idx);
+    pol_stat = pstats->st + idx;
+    if (pol_stat->status != POL_STAT_UNDECIDED)
+       msg_warn("%s: unexpected status %d at index %d",
+                __func__, pol_stat->status, idx);
+    pol_stat->final_name = final_name;
+    pol_stat->status = status;
+}
+
+/* pol_stat_access - peek at specific POL_STAT instance. */
+
+static const POL_STAT *pol_stat_access(const POL_STATS *pstats, int idx)
+{
+    const POL_STAT *pol_stat;
+
+    if (idx < 0 || idx >= POL_STATS_SIZE)
+       msg_panic("%s: bad index: %d", __func__, idx);
+    pol_stat = pstats->st + idx;
+    return (pol_stat);
+}
+
+/* pol_stats_format - render in external representation */
+
+void    pol_stats_format(VSTRING *buf, const POL_STATS *pstats)
+{
+    int     idx;
+    const POL_STAT *pstat;
+    int     field_count = 0;
+
+    for (idx = 0; idx < POL_STATS_SIZE; idx++) {
+       pstat = pol_stat_access(pstats, idx);
+       if (pstat->status == POL_STAT_INACTIVE)
+           continue;
+       if (field_count > 0)
+           vstring_strcat(buf, "/");
+       if (pstat->status == POL_STAT_VIOLATION)
+           vstring_strcat(buf, "!");
+       vstring_strcat(buf, pstat->init_name);
+       if (pstat->final_name != 0
+           && strcmp(pstat->init_name, pstat->final_name) != 0)
+           vstring_sprintf_append(buf, ":%s", (pstat)->final_name);
+       if (pstat->status == POL_STAT_UNDECIDED)
+           vstring_strcat(buf, "?");
+       field_count += 1;
+    }
+}
+
+#endif                                 /* USE_TLS */
diff --git a/postfix/src/global/pol_stats.h b/postfix/src/global/pol_stats.h
new file mode 100644 (file)
index 0000000..e842cd6
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef _POL_STATS_H_INCLUDED_
+#define _POL_STATS_H_INCLUDED_
+
+/*++
+/* NAME
+/*     tls_stats 3h
+/* SUMMARY
+/*     manage TLS per-feature policy compliance status
+/* SYNOPSIS
+/*     #include <pol_stats.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+typedef struct POL_STAT {
+    const char *init_name;             /* Human-readable feature name */
+    const char *final_name;            /* Human-readable feature name */
+    int     status;                    /* See below */
+} POL_STAT;
+
+#define POL_STAT_INACTIVE      0       /* No data */
+#define POL_STAT_UNDECIDED     1       /* Pending decision */
+#define POL_STAT_VIOLATION     2       /* Definitely did not meet policy */
+#define POL_STAT_COMPLIANT     3       /* Definitely did meet policy */
+
+ /*
+  * Wrap it in a structure for sanity-checked access.
+  */
+#define POL_STATS_SIZE 2               /* TLS level and REQUIRETLS */
+
+typedef struct POL_STATS {
+    int     used;
+    POL_STAT st[POL_STATS_SIZE];
+} POL_STATS;
+
+#ifdef USE_TLS
+
+extern POL_STATS *pol_stats_create(void);
+extern void pol_stats_revert(POL_STATS *);
+extern void pol_stats_free(POL_STATS *);
+
+#define pol_stats_used(t) ((t)->used)
+extern void pol_stat_activate(POL_STATS *, int, const char *);
+extern void pol_stat_decide(POL_STATS *, int, const char *, int);
+extern void pol_stats_format(VSTRING *, const POL_STATS *);
+#endif                                 /* USE_TLS */
+
+#define NO_TLS_STATS   ((POL_STATS *) 0)
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+#endif                                 /* _POL_STATS_H_INCLUDED_ */
diff --git a/postfix/src/global/pol_stats_test.c b/postfix/src/global/pol_stats_test.c
new file mode 100644 (file)
index 0000000..8db8fd7
--- /dev/null
@@ -0,0 +1,182 @@
+/*++
+/* NAME
+/*      pol_stats_test 1t
+/* SUMMARY
+/*      pol_stats unit tests
+/* SYNOPSIS
+/*      ./pol_stats_test
+/* DESCRIPTION
+/*      pol_stats_test runs and logs each configured test, reports if a
+/*      test is a PASS or FAIL, and returns an exit status of zero
+/*      if all tests are a PASS.
+/* LICENSE
+/* .ad
+/* .fi
+/*      The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*      Wietse Venema
+/*      porcupine.org
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <stringops.h>
+#include <vstring.h>
+
+ /*
+  * Application-specific.
+  */
+#include <pol_stats.h>
+
+ /*
+  * Convenience wrappers for feature tests at index 0.
+  */
+#define pol_stat_activate_first(stats, name) \
+        pol_stat_activate((stats), 0, (name))
+#define pol_stat_decide_first(stats, name, status) \
+        pol_stat_decide((stats), 0, (name), (status))
+
+struct first_test_data {
+    const char *target_name;
+    const char *final_name;
+    int     final_status;
+};
+
+ /*
+  * Ditto, at index 1. Originally these were different from the definitions
+  * for the first field. Keep the definitions separate so that tests can be
+  * differentiated again if needed.
+  */
+#define pol_stat_activate_second(stats, name) \
+        pol_stat_activate((stats), 1, (name))
+#define pol_stat_decide_second(stats, name, status) \
+        pol_stat_decide((stats), 1, (name), (status))
+
+struct second_test_data {
+    const char *target_name;
+    const char *final_name;
+    int     final_status;
+};
+
+ /*
+  * Tests cases and test data.
+  */
+typedef struct TEST_CASE {
+    const char *label;
+    const char *want;
+    int     (*action) (const struct TEST_CASE *);
+    struct first_test_data first_data;
+    struct second_test_data second_data;
+} TEST_CASE;
+
+static POL_STATS *pstats;
+static VSTRING *buf;
+
+static int test_pol_stats(const TEST_CASE *tp)
+{
+    char   *got;
+    int     errors = 0;
+
+    pol_stats_revert(pstats);
+    VSTRING_RESET(buf);
+
+    if (tp->first_data.target_name) {
+       pol_stat_activate_first(pstats, tp->first_data.target_name);
+       if (tp->first_data.final_name)
+           pol_stat_decide_first(pstats, tp->first_data.final_name,
+                                 tp->first_data.final_status);
+    }
+    if (tp->second_data.target_name) {
+       pol_stat_activate_second(pstats, tp->second_data.target_name);
+       if (tp->second_data.final_name)
+           pol_stat_decide_second(pstats, tp->second_data.final_name,
+                                  tp->second_data.final_status);
+    }
+    pol_stats_format(buf, pstats);
+    got = vstring_str(buf);
+    if (strcmp(got, tp->want) != 0) {
+       msg_warn("got '%s', want '%s", got, tp->want);
+       errors++;
+    }
+    return (errors == 0);
+}
+
+static TEST_CASE test_cases[] = {
+    /* Tests for single feature at index 0 */
+    {"first_compliant", "first", test_pol_stats,
+       {"first", "first", POL_STAT_COMPLIANT}, {},
+    },
+    {"first_undecided", "first?", test_pol_stats,
+       {"first", 0, POL_STAT_COMPLIANT}, {},
+    },
+    {"first_unspecified_violation", "!first", test_pol_stats,
+       {"first", "first", POL_STAT_VIOLATION}, {},
+    },
+    {"first_downgraded_violation", "!first:low", test_pol_stats,
+       {"first", "low", POL_STAT_VIOLATION}, {},
+    },
+    {"first_downgraded_compliant", "first:none", test_pol_stats,
+       {"first", "none", POL_STAT_COMPLIANT}, {},
+    },
+    /* Tests for single feature at index 1 */
+    {"second_compliant", "second", test_pol_stats,
+       {}, {"second", "second", POL_STAT_COMPLIANT},
+    },
+    {"second_explicit_downgraded_compliant", "second:other", test_pol_stats,
+       {}, {"second", "other", POL_STAT_COMPLIANT},
+    },
+    {"second_explicit_downgraded_violation", "!second:other", test_pol_stats,
+       {}, {"second", "other", POL_STAT_VIOLATION},
+    },
+    /* Combined policy status tests. */
+    {"multi_feature_compliant", "first/second:none", test_pol_stats,
+       {"first", "first", POL_STAT_COMPLIANT},
+       {"second", "none", POL_STAT_COMPLIANT},
+    },
+    {"multi_feature_violation", "!first/second:none", test_pol_stats,
+       {"first", "first", POL_STAT_VIOLATION},
+       {"second", "none", POL_STAT_COMPLIANT},
+    },
+    {"multi_feature_violation", "first/!second:none", test_pol_stats,
+       {"first", "first", POL_STAT_COMPLIANT},
+       {"second", "none", POL_STAT_VIOLATION},
+    },
+    0,
+};
+
+int     main(int argc, char **argv)
+{
+    const TEST_CASE *tp;
+    int     pass = 0;
+    int     fail = 0;
+
+    msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+    pstats = pol_stats_create();
+    buf = vstring_alloc(100);
+
+    for (tp = test_cases; tp->label != 0; tp++) {
+       msg_info("RUN  %s", tp->label);
+       if (tp->action(tp) == 0) {
+           fail++;
+           msg_info("FAIL %s", tp->label);
+       } else {
+           msg_info("PASS %s", tp->label);
+           pass++;
+       }
+    }
+    pol_stats_free(pstats);
+    vstring_free(buf);
+
+    msg_info("PASS=%d FAIL=%d", pass, fail);
+    exit(fail != 0);
+}
index 1a41dfd6cfbdd63ad1d8dfc7e86e01c00a02fabf..f787acc8f4ca24ecc74ab5cb05a721a3c75f4369 100644 (file)
 /* .IP trace_flags
 /*     Message tracing flags as specified in \fB<deliver_request.h>\fR.
 /* .IP sendopts
-/*     Flags defined in <sendopts.h>. This ignores flags based on
-/*     message header content, or envelope email addresses.
+/*     Flags defined in <sendopts.h>. This ignores SMTPUTF8 flags for
+/*     UTF8 detected in message headers or envelope email addresses,
+/*     and the flag for detected "TLS-Required: no".
 /* .IP queue_id
 /*     Null pointer, or pointer to buffer that receives the queue
 /*     ID of the new message.
@@ -225,8 +226,8 @@ static void post_mail_init(VSTREAM *stream, const char *sender,
     int     cleanup_flags =
     int_filt_flags(source_class) | CLEANUP_FLAG_MASK_INTERNAL
     | smtputf8_autodetect(source_class)
+    | ((sendopts & SOPT_REQUIRETLS_ESMTP) ? CLEANUP_FLAG_REQTLS : 0)
     | ((sendopts & SMTPUTF8_FLAG_REQUESTED) ? CLEANUP_FLAG_SMTPUTF8 : 0);
-    /* TODO(wietse) REQUIRETLS. */
 
     GETTIMEOFDAY(&now);
     date = mail_date(now.tv_sec);
index 32d939b9b7b52c280fa45d4e532f70679baa1369..9452c003fdccdc393bb32c16bc969369bcc8d1f6 100644 (file)
  /*
   * The subset of inputs that the postdrop command allows.
   */
-#define REC_TYPE_POST_ENVELOPE "MFSRVAin"
+#define REC_TYPE_POST_ENVELOPE "MCFSRVAin"
 #define REC_TYPE_POST_CONTENT  "XLN"
 #define REC_TYPE_POST_EXTRACT  "EAR"
 
index 1b1b2a57a7904a47cf471a8da3ca6191024c8c5e..8a7bcb4fc8e3f6ae1c27ef9c03959721c7f23dad 100644 (file)
@@ -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;
index c81ccebdcc1bf7f2a196f175960528ef6e5735c2..22eb3722eb9f99fc52f939a4e6313827c3ad108e 100644 (file)
@@ -6,12 +6,13 @@
 /* SYNOPSIS
 /*     #include <sent.h>
 /*
-/*     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 POL_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. */
 
 int     sent(int flags, const char *id, MSG_STATS *stats,
                     RECIPIENT *recipient, const char *relay,
-                    DSN *dsn)
+                    const POL_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);
index 2ed3856a583d3f1a50ac14fe658a1b5498464d14..0087d2b7b3da75cde27cdc3ac3bb8fa116a83948 100644 (file)
@@ -29,7 +29,7 @@
 #define SENT_FLAG_NONE (0)
 
 extern int sent(int, const char *, MSG_STATS *, RECIPIENT *, const char *,
-                       DSN *);
+                       const POL_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
index d826a649426fe672c4988c998380271d59076444..286d8f936b2b6314ad75597a3ab823275fc16a70 100644 (file)
@@ -6,12 +6,13 @@
 /* SYNOPSIS
 /*     #include <trace.h>
 /*
-/*     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 POL_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. */
 
 int     trace_append(int flags, const char *id, MSG_STATS *stats,
                             RECIPIENT *rcpt, const char *relay,
-                            DSN *dsn)
+                            const POL_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);
index 201360198e995a93556c7a74d35522decc9faae5..55664456fc02d8b654e6f6ff0f4da69c1aa7a787 100644 (file)
@@ -20,7 +20,7 @@
   * External interface.
   */
 extern int trace_append(int, const char *, MSG_STATS *, RECIPIENT *,
-                               const char *, DSN *);
+                               const char *, const POL_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
index 2ce091a5d5758a775ed5322bd4ad181b804ce259..df774a5904edfa7c1f105668b29ead6ff170dc93 100644 (file)
@@ -6,12 +6,13 @@
 /* SYNOPSIS
 /*     #include <verify.h>
 /*
-/*     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 POL_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 POL_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);
index 250eb6d65e9520bb05dcdc972635add49daca067..daf2c6cdcc3034885a13e2de1335ef75e979f2ff 100644 (file)
   * Global library.
   */
 #include <deliver_request.h>
+#include <pol_stats.h>
 
  /*
   * External interface.
   */
 extern int verify_append(const char *, MSG_STATS *, RECIPIENT *,
-                                const char *, DSN *, int);
+                                const char *, const POL_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
index a600a267ef3732f0e38ffeaf0c03e1457eb0404b..b1b7012b6d7a79c60219a824aa44e50582694552 100644 (file)
@@ -78,6 +78,7 @@ alias.o: ../../include/myflock.h
 alias.o: ../../include/mymalloc.h
 alias.o: ../../include/mypwd.h
 alias.o: ../../include/nvtable.h
+alias.o: ../../include/pol_stats.h
 alias.o: ../../include/recipient_list.h
 alias.o: ../../include/resolve_clnt.h
 alias.o: ../../include/sent.h
@@ -117,6 +118,7 @@ bounce_workaround.o: ../../include/msg_stats.h
 bounce_workaround.o: ../../include/myflock.h
 bounce_workaround.o: ../../include/mymalloc.h
 bounce_workaround.o: ../../include/nvtable.h
+bounce_workaround.o: ../../include/pol_stats.h
 bounce_workaround.o: ../../include/recipient_list.h
 bounce_workaround.o: ../../include/resolve_clnt.h
 bounce_workaround.o: ../../include/split_addr.h
@@ -156,6 +158,7 @@ command.o: ../../include/myflock.h
 command.o: ../../include/mymalloc.h
 command.o: ../../include/nvtable.h
 command.o: ../../include/pipe_command.h
+command.o: ../../include/pol_stats.h
 command.o: ../../include/recipient_list.h
 command.o: ../../include/resolve_clnt.h
 command.o: ../../include/sent.h
@@ -223,6 +226,7 @@ dotforward.o: ../../include/mymalloc.h
 dotforward.o: ../../include/mypwd.h
 dotforward.o: ../../include/nvtable.h
 dotforward.o: ../../include/open_as.h
+dotforward.o: ../../include/pol_stats.h
 dotforward.o: ../../include/recipient_list.h
 dotforward.o: ../../include/resolve_clnt.h
 dotforward.o: ../../include/sent.h
@@ -260,6 +264,7 @@ file.o: ../../include/msg_stats.h
 file.o: ../../include/myflock.h
 file.o: ../../include/mymalloc.h
 file.o: ../../include/nvtable.h
+file.o: ../../include/pol_stats.h
 file.o: ../../include/recipient_list.h
 file.o: ../../include/resolve_clnt.h
 file.o: ../../include/safe_open.h
@@ -298,6 +303,7 @@ forward.o: ../../include/msg_stats.h
 forward.o: ../../include/myflock.h
 forward.o: ../../include/mymalloc.h
 forward.o: ../../include/nvtable.h
+forward.o: ../../include/pol_stats.h
 forward.o: ../../include/rec_type.h
 forward.o: ../../include/recipient_list.h
 forward.o: ../../include/record.h
@@ -339,6 +345,7 @@ include.o: ../../include/mymalloc.h
 include.o: ../../include/mypwd.h
 include.o: ../../include/nvtable.h
 include.o: ../../include/open_as.h
+include.o: ../../include/pol_stats.h
 include.o: ../../include/recipient_list.h
 include.o: ../../include/resolve_clnt.h
 include.o: ../../include/sent.h
@@ -371,6 +378,7 @@ indirect.o: ../../include/msg_stats.h
 indirect.o: ../../include/myflock.h
 indirect.o: ../../include/mymalloc.h
 indirect.o: ../../include/nvtable.h
+indirect.o: ../../include/pol_stats.h
 indirect.o: ../../include/recipient_list.h
 indirect.o: ../../include/resolve_clnt.h
 indirect.o: ../../include/sent.h
@@ -476,6 +484,7 @@ mailbox.o: ../../include/myflock.h
 mailbox.o: ../../include/mymalloc.h
 mailbox.o: ../../include/mypwd.h
 mailbox.o: ../../include/nvtable.h
+mailbox.o: ../../include/pol_stats.h
 mailbox.o: ../../include/recipient_list.h
 mailbox.o: ../../include/resolve_clnt.h
 mailbox.o: ../../include/safe_open.h
@@ -517,6 +526,7 @@ maildir.o: ../../include/msg_stats.h
 maildir.o: ../../include/myflock.h
 maildir.o: ../../include/mymalloc.h
 maildir.o: ../../include/nvtable.h
+maildir.o: ../../include/pol_stats.h
 maildir.o: ../../include/recipient_list.h
 maildir.o: ../../include/resolve_clnt.h
 maildir.o: ../../include/safe_open.h
@@ -556,6 +566,7 @@ recipient.o: ../../include/myflock.h
 recipient.o: ../../include/mymalloc.h
 recipient.o: ../../include/mypwd.h
 recipient.o: ../../include/nvtable.h
+recipient.o: ../../include/pol_stats.h
 recipient.o: ../../include/recipient_list.h
 recipient.o: ../../include/resolve_clnt.h
 recipient.o: ../../include/split_addr.h
@@ -593,6 +604,7 @@ resolve.o: ../../include/msg_stats.h
 resolve.o: ../../include/myflock.h
 resolve.o: ../../include/mymalloc.h
 resolve.o: ../../include/nvtable.h
+resolve.o: ../../include/pol_stats.h
 resolve.o: ../../include/recipient_list.h
 resolve.o: ../../include/resolve_clnt.h
 resolve.o: ../../include/rewrite_clnt.h
@@ -624,6 +636,7 @@ token.o: ../../include/msg_stats.h
 token.o: ../../include/myflock.h
 token.o: ../../include/mymalloc.h
 token.o: ../../include/nvtable.h
+token.o: ../../include/pol_stats.h
 token.o: ../../include/readlline.h
 token.o: ../../include/recipient_list.h
 token.o: ../../include/resolve_clnt.h
@@ -662,6 +675,7 @@ unknown.o: ../../include/msg_stats.h
 unknown.o: ../../include/myflock.h
 unknown.o: ../../include/mymalloc.h
 unknown.o: ../../include/nvtable.h
+unknown.o: ../../include/pol_stats.h
 unknown.o: ../../include/recipient_list.h
 unknown.o: ../../include/resolve_clnt.h
 unknown.o: ../../include/sent.h
index 7e553ffa33f9380e71985f77dfc9ea3149f808b5..8e7b1807da6db910fa63d94abfd5e3697569979c 100644 (file)
@@ -163,9 +163,10 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
 #define FORWARD_CLEANUP_FLAGS \
        (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL \
        | smtputf8_autodetect(MAIL_SRC_MASK_FORWARD) \
+       | ((request->sendopts & SOPT_REQUIRETLS_ESMTP) ? \
+       CLEANUP_FLAG_REQTLS : 0) \
        | ((request->sendopts & SMTPUTF8_FLAG_REQUESTED) ? \
        CLEANUP_FLAG_SMTPUTF8 : 0))
-       /* TODO(wietse) REQUIRETLS. */
 
     attr_print(cleanup, ATTR_FLAG_NONE,
               SEND_ATTR_INT(MAIL_ATTR_FLAGS, FORWARD_CLEANUP_FLAGS),
index 13d0dc79e2e00e9a4c0b6b4b4d5a2caa216e1c5a..0f024e45f09a4aca9ad687710678227a84682256 100644 (file)
@@ -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) \
index 302456614f07e7eb9c470a857dd59b89d8a37c5e..7c6ed881519670688f46bd6c509e53c66e1e62b7 100644 (file)
@@ -101,6 +101,7 @@ dgram_server.o: ../../include/msg_vstream.h
 dgram_server.o: ../../include/myflock.h
 dgram_server.o: ../../include/mymalloc.h
 dgram_server.o: ../../include/nvtable.h
+dgram_server.o: ../../include/pol_stats.h
 dgram_server.o: ../../include/recipient_list.h
 dgram_server.o: ../../include/resolve_local.h
 dgram_server.o: ../../include/safe_open.h
@@ -142,6 +143,7 @@ event_server.o: ../../include/msg_vstream.h
 event_server.o: ../../include/myflock.h
 event_server.o: ../../include/mymalloc.h
 event_server.o: ../../include/nvtable.h
+event_server.o: ../../include/pol_stats.h
 event_server.o: ../../include/recipient_list.h
 event_server.o: ../../include/resolve_local.h
 event_server.o: ../../include/safe_open.h
@@ -364,6 +366,7 @@ multi_server.o: ../../include/msg_vstream.h
 multi_server.o: ../../include/myflock.h
 multi_server.o: ../../include/mymalloc.h
 multi_server.o: ../../include/nvtable.h
+multi_server.o: ../../include/pol_stats.h
 multi_server.o: ../../include/recipient_list.h
 multi_server.o: ../../include/resolve_local.h
 multi_server.o: ../../include/safe_open.h
@@ -406,6 +409,7 @@ single_server.o: ../../include/msg_vstream.h
 single_server.o: ../../include/myflock.h
 single_server.o: ../../include/mymalloc.h
 single_server.o: ../../include/nvtable.h
+single_server.o: ../../include/pol_stats.h
 single_server.o: ../../include/recipient_list.h
 single_server.o: ../../include/resolve_local.h
 single_server.o: ../../include/safe_open.h
@@ -448,6 +452,7 @@ trigger_server.o: ../../include/msg_vstream.h
 trigger_server.o: ../../include/myflock.h
 trigger_server.o: ../../include/mymalloc.h
 trigger_server.o: ../../include/nvtable.h
+trigger_server.o: ../../include/pol_stats.h
 trigger_server.o: ../../include/recipient_list.h
 trigger_server.o: ../../include/resolve_local.h
 trigger_server.o: ../../include/safe_open.h
index fc48077d7b4b06e6a552432283c077cd2006d261..bd2ef8f585f2dbc05315450bacda2f2a63da4326 100644 (file)
@@ -102,6 +102,7 @@ qmgr_active.o: ../../include/msg.h
 qmgr_active.o: ../../include/msg_stats.h
 qmgr_active.o: ../../include/mymalloc.h
 qmgr_active.o: ../../include/nvtable.h
+qmgr_active.o: ../../include/pol_stats.h
 qmgr_active.o: ../../include/qmgr_user.h
 qmgr_active.o: ../../include/rec_type.h
 qmgr_active.o: ../../include/recipient_list.h
@@ -125,6 +126,7 @@ qmgr_bounce.o: ../../include/htable.h
 qmgr_bounce.o: ../../include/msg_stats.h
 qmgr_bounce.o: ../../include/mymalloc.h
 qmgr_bounce.o: ../../include/nvtable.h
+qmgr_bounce.o: ../../include/pol_stats.h
 qmgr_bounce.o: ../../include/recipient_list.h
 qmgr_bounce.o: ../../include/scan_dir.h
 qmgr_bounce.o: ../../include/sys_defs.h
@@ -147,6 +149,7 @@ qmgr_defer.o: ../../include/msg.h
 qmgr_defer.o: ../../include/msg_stats.h
 qmgr_defer.o: ../../include/mymalloc.h
 qmgr_defer.o: ../../include/nvtable.h
+qmgr_defer.o: ../../include/pol_stats.h
 qmgr_defer.o: ../../include/recipient_list.h
 qmgr_defer.o: ../../include/scan_dir.h
 qmgr_defer.o: ../../include/sys_defs.h
@@ -265,6 +268,7 @@ qmgr_message.o: ../../include/myflock.h
 qmgr_message.o: ../../include/mymalloc.h
 qmgr_message.o: ../../include/nvtable.h
 qmgr_message.o: ../../include/opened.h
+qmgr_message.o: ../../include/pol_stats.h
 qmgr_message.o: ../../include/qmgr_user.h
 qmgr_message.o: ../../include/rec_attr_map.h
 qmgr_message.o: ../../include/rec_type.h
index 00ba885bf98fb7aea993eed765feb1668fd3edf2..ee9f568634fba999d469a5995fdc50856dc4fc8d 100644 (file)
@@ -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);
index dc0319e773a6b3a14961c841248d6a50e88c4501..e87fe0b6e2f685f22f636448c14a2acf7a781e01 100644 (file)
@@ -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);
 }
index a88c8e5e7c6ab4ade7fd5983b776cebcf42e45ea..85b0d714156d6296b91fa609bb7843c9fb278b0a 100644 (file)
@@ -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);
index f2a6822473f591b3e8f0027d77f7be86ba7fdac8..bf6840edd960013e8db1f9b60f37bed50ebdbd1d 100644 (file)
@@ -80,6 +80,7 @@ pipe.o: ../../include/mymalloc.h
 pipe.o: ../../include/nvtable.h
 pipe.o: ../../include/off_cvt.h
 pipe.o: ../../include/pipe_command.h
+pipe.o: ../../include/pol_stats.h
 pipe.o: ../../include/quote_822_local.h
 pipe.o: ../../include/quote_flags.h
 pipe.o: ../../include/recipient_list.h
index 58ff5e0e76494df96bfec8f958bbdb0301837c27..a5ff2204d68f0a16693e0de9fd85280212adb9b5 100644 (file)
@@ -1079,7 +1079,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;
@@ -1096,7 +1096,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;
@@ -1208,7 +1208,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;
index 44c0ce6baafa095391a82e61ad8adcbde4f5a3cc..91cdec1e13e1554f721af38811619e5ef76bb414 100644 (file)
@@ -348,6 +348,10 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
            /* Optional output (here before we update the state machine). */
            if (do_print)
                PRINT_RECORD(flags, offset, rec_type, STR(buffer));
+           /* Postfix 3.11 maildrop files may have preliminary SIZE record. */
+           if (strncmp(VSTREAM_PATH(fp), MAIL_QUEUE_MAILDROP "/",
+                       sizeof(MAIL_QUEUE_MAILDROP)) == 0)
+               continue;
            /* Read the message size/offset for the state machine optimizer. */
            if (data_size >= 0 || data_offset >= 0) {
                msg_warn("file contains multiple size records");
index 5139e1c0b2d2f261d313040b08f5ba8de475fc1a..63a0e5a96b4a067658ac74c1d8ebac167776806d 100644 (file)
@@ -1148,6 +1148,7 @@ postconf.o: ../../include/check_arg.h
 postconf.o: ../../include/compat_level.h
 postconf.o: ../../include/dict.h
 postconf.o: ../../include/htable.h
+postconf.o: ../../include/mac_midna.h
 postconf.o: ../../include/mail_conf.h
 postconf.o: ../../include/mail_dict.h
 postconf.o: ../../include/mail_params.h
@@ -1180,6 +1181,7 @@ postconf_builtin.o: ../../include/mail_conf.h
 postconf_builtin.o: ../../include/mail_params.h
 postconf_builtin.o: ../../include/mail_proto.h
 postconf_builtin.o: ../../include/mail_version.h
+postconf_builtin.o: ../../include/midna_domain.h
 postconf_builtin.o: ../../include/msg.h
 postconf_builtin.o: ../../include/myflock.h
 postconf_builtin.o: ../../include/mymalloc.h
index c68a07f19aa6b9c63d2f8a25ae7ffe584f2096b0..d9e96831dd4d4c8eede1522c29aa280ba8c9c1e2 100644 (file)
 
 /* Utility library. */
 
+#include <mac_midna.h>
 #include <msg.h>
 #include <msg_vstream.h>
 #include <dict.h>
@@ -968,6 +969,7 @@ int     main(int argc, char **argv)
      * For consistency with mail_params_init().
      */
     compat_level_relop_register();
+    mac_midna_register();
 
     /*
      * We don't enforce import_environment consistency in this program.
index 1fc337c5f9a98573503b2b8a2d9da996682774fc..e0ecc6efba931d6ecdac86bb9aa0a5be35bbcc09 100644 (file)
@@ -35,6 +35,9 @@
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
@@ -54,6 +57,7 @@
 #include <vstring.h>
 #include <get_hostname.h>
 #include <stringops.h>
+#include <midna_domain.h>
 
 /* Global library. */
 
@@ -203,6 +207,8 @@ static const char *pcf_check_myhostname(void)
     /*
      * If the local machine name is not in FQDN form, try to append the
      * contents of $mydomain.
+     * 
+     * TODO(wietse) handle alternative 'dot' in U-label form.
      */
     name = get_hostname();
     if ((dot = strchr(name, '.')) == 0) {
@@ -239,6 +245,8 @@ static const char *pcf_check_mydomainname(void)
 
     /*
      * Use a default domain when the hostname is not a FQDN ("foo").
+     * 
+     * TODO(wietse) handle alternative 'dot' in U-label form.
      */
     if (var_myhostname == 0)
        pcf_get_myhostname();
index 5f16459ae11e40b182f3bf58863ae78ac1dc6eba..d8422c67dd8ea326446db190528cd83bdd4dc043 100644 (file)
@@ -1005,6 +1005,27 @@ static void pre_accept(char *unused_name, char **unused_argv)
     }
 }
 
+/* contains_only_parameter - string contains $parameter or ${parameter} */
+
+static int contains_only_parameter(const char *str)
+{
+    const char *last;
+    char   *tmp;
+    int     ret;
+
+    if (*str != '$')
+       return (0);
+    if (*++str != '{')
+       return (mail_conf_lookup(str) != 0);
+    if (*(last = str + strlen(str) - 1) == '}') {
+       tmp = mystrndup(str + 1, last - str - 1);
+       ret = (mail_conf_lookup(tmp) != 0);
+       myfree(tmp);
+       return (ret);
+    }
+    return (0);
+}
+
 /* post_jail_init - post-jail initialization */
 
 static void post_jail_init(char *unused_name, char **unused_argv)
@@ -1029,16 +1050,14 @@ static void post_jail_init(char *unused_name, char **unused_argv)
     /*
      * Workaround for parameters whose values may contain "$", and that have
      * a default of "$parametername". Not sure if it would be a good idea to
-     * always to this in the mail_conf_raw(3) module.
+     * always do this in the mail_conf_raw(3) module.
      */
-    if (*var_psc_rej_footer == '$'
-       && mail_conf_lookup(var_psc_rej_footer + 1)) {
+    if (contains_only_parameter(var_psc_rej_footer)) {
        tmp = mail_conf_eval_once(var_psc_rej_footer);
        myfree(var_psc_rej_footer);
        var_psc_rej_footer = mystrdup(tmp);
     }
-    if (*var_psc_exp_filter == '$'
-       && mail_conf_lookup(var_psc_exp_filter + 1)) {
+    if (contains_only_parameter(var_psc_exp_filter)) {
        tmp = mail_conf_eval_once(var_psc_exp_filter);
        myfree(var_psc_exp_filter);
        var_psc_exp_filter = mystrdup(tmp);
index 25f420bf28cab9f8629a62ad2fa46e2baf6b680e..a1971009a9f025cf9381013420282a097bc59020 100644 (file)
@@ -785,7 +785,7 @@ static int starttls(STATE *state)
 
     cipher_exclusions = vstring_alloc(10);
     ADD_EXCLUDE(cipher_exclusions, DEF_SMTP_TLS_EXCL_CIPH);
-    if (TLS_REQUIRED(state->level))
+    if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->level))
        ADD_EXCLUDE(cipher_exclusions, DEF_SMTP_TLS_MAND_EXCL);
 
     /*
index 573b5c7f42db40797a1d896eae5de6bd89abea60..83445f432da416767a329c7c987248611ab5f3d4 100644 (file)
@@ -104,6 +104,7 @@ qmgr_active.o: ../../include/msg.h
 qmgr_active.o: ../../include/msg_stats.h
 qmgr_active.o: ../../include/mymalloc.h
 qmgr_active.o: ../../include/nvtable.h
+qmgr_active.o: ../../include/pol_stats.h
 qmgr_active.o: ../../include/qmgr_user.h
 qmgr_active.o: ../../include/rec_type.h
 qmgr_active.o: ../../include/recipient_list.h
@@ -127,6 +128,7 @@ qmgr_bounce.o: ../../include/htable.h
 qmgr_bounce.o: ../../include/msg_stats.h
 qmgr_bounce.o: ../../include/mymalloc.h
 qmgr_bounce.o: ../../include/nvtable.h
+qmgr_bounce.o: ../../include/pol_stats.h
 qmgr_bounce.o: ../../include/recipient_list.h
 qmgr_bounce.o: ../../include/scan_dir.h
 qmgr_bounce.o: ../../include/sys_defs.h
@@ -149,6 +151,7 @@ qmgr_defer.o: ../../include/msg.h
 qmgr_defer.o: ../../include/msg_stats.h
 qmgr_defer.o: ../../include/mymalloc.h
 qmgr_defer.o: ../../include/nvtable.h
+qmgr_defer.o: ../../include/pol_stats.h
 qmgr_defer.o: ../../include/recipient_list.h
 qmgr_defer.o: ../../include/scan_dir.h
 qmgr_defer.o: ../../include/sys_defs.h
@@ -280,6 +283,7 @@ qmgr_message.o: ../../include/myflock.h
 qmgr_message.o: ../../include/mymalloc.h
 qmgr_message.o: ../../include/nvtable.h
 qmgr_message.o: ../../include/opened.h
+qmgr_message.o: ../../include/pol_stats.h
 qmgr_message.o: ../../include/qmgr_user.h
 qmgr_message.o: ../../include/rec_attr_map.h
 qmgr_message.o: ../../include/rec_type.h
index 00ba885bf98fb7aea993eed765feb1668fd3edf2..ee9f568634fba999d469a5995fdc50856dc4fc8d 100644 (file)
@@ -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);
index 79615cc0f760c34cedb8aa856978391e2de6013b..1cce65ca5785e58887ebcd9668fe169824263887 100644 (file)
@@ -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);
 }
index 2e44c1d2205dbe4751eb37fdb5f54515f0732693..56533b2a20f1b60d628ae0153400f3438abffe21 100644 (file)
@@ -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);
index 192faf82e274539109f575fd082b6bf709b737ee..d8e0f05609473ab89a3b7ccc17a793673c71294a 100644 (file)
@@ -86,6 +86,7 @@ sendmail.o: ../../include/recipient_list.h
 sendmail.o: ../../include/record.h
 sendmail.o: ../../include/resolve_clnt.h
 sendmail.o: ../../include/safe.h
+sendmail.o: ../../include/sendopts.h
 sendmail.o: ../../include/set_ugid.h
 sendmail.o: ../../include/split_at.h
 sendmail.o: ../../include/stringops.h
index df052c5b0e701e41fc15460d78409b610433314b..83ccc149633484ea90ce66d10198801fc2ae2289 100644 (file)
 /*     This feature is available in Postfix 2.3 and later.
 /* .IP "\fB-n\fR (ignored)"
 /*     Backwards compatibility.
-/* .IP "\fB-oA\fIalias_database\fR"
-/*     Non-default alias database. Specify \fIpathname\fR or
-/*     \fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for
-/*     details.
+/* .IP "\fB-O requiretls=yes\fR"
+/* .IP "\fB-O requiretls=no\fR"
+/*     When delivering a message to an SMTP or LMTP server, the
+/*     connection must use TLS with a verified server certificate,
+/*     and that server must support REQUIRETLS. The "requiretls" name
+/*     and option value are case-insensitive. REQUIRETLS enforcement
+/*     is controlled with the configuration parameters requiretls_enable,
+/*     smtp_requiretls_policy, and lmtp_requiretls_policy.
+/*
+/*     This feature is available in Postfix 3.11 and later.
+/* .IP "\fB-O smtputf8=yes\fR"
+/* .IP "\fB-O smtputf8=no\fR"
+/*     When delivering a message to an SMTP or LMTP server, and an
+/*     envelope address or message header contains UTF8 text, that server
+/*     must support SMTPUTF8. The "smtputf8" option name and value
+/*     are case-insensitive.
+/*
+/*     This feature is available in Postfix 3.11 and later.
 /* .IP "\fB-O \fIoption=value\fR (ignored)"
 /*     Set the named \fIoption\fR to \fIvalue\fR. Use the equivalent
 /*     configuration parameter in \fBmain.cf\fR instead.
+/* .IP "\fB-oA\fIalias_database\fR"
+/*     Non-default alias database. Specify \fIpathname\fR or
+/*     \fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for details.
 /* .IP "\fB-o7\fR (ignored)"
 /* .IP "\fB-o8\fR (ignored)"
 /*     To send 8-bit or binary content, use an appropriate MIME encapsulation
 /*     the Postfix executable files and documentation with the default
 /*     Postfix instance, and that are started, stopped, etc., together
 /*     with the default Postfix instance.
+/* .PP
+/*     Postfix 3.11 and later:
+/* .IP "\fBrequiretls_enable (yes)\fR"
+/*     Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+/*     FROM" command.
 /* FILES
 /*     /var/spool/postfix, mail queue
 /*     /etc/postfix, configuration files
 #include <user_acl.h>
 #include <dsn_mask.h>
 #include <mail_parm_split.h>
+#include <sendopts.h>
 
 /* Application-specific. */
 
@@ -589,6 +612,11 @@ static const CONFIG_STR_TABLE str_table[] = {
     0,
 };
 
+ /*
+  * Sender options.
+  */
+static int sm_sendopts;
+
  /*
   * Silly little macros (SLMs).
   */
@@ -788,6 +816,14 @@ static void enqueue(const int flags, const char *encoding,
      * With "sendmail -N", instead of a per-message NOTIFY record we store one
      * per recipient so that we can simplify the implementation somewhat.
      */
+    if (sm_sendopts)
+       rec_fprintf(dst, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
+                   (REC_TYPE_SIZE_CAST1) ~ 0,  /* message segment size */
+                   (REC_TYPE_SIZE_CAST2) ~ 0,  /* content offset */
+                   (REC_TYPE_SIZE_CAST3) ~ 0,  /* recipient count */
+                   (REC_TYPE_SIZE_CAST4) ~ 0,  /* qmgr options */
+                   (REC_TYPE_SIZE_CAST5) ~ 0,  /* content length */
+                   (REC_TYPE_SIZE_CAST6) sm_sendopts);
     if (dsn_envid)
        rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
                    MAIL_ATTR_DSN_ENVID, dsn_envid);
@@ -1054,6 +1090,7 @@ int     main(int argc, char **argv)
     int     saved_optind;
     ARGV   *import_env;
     char   *alias_map_from_args = 0;
+    const char *oval;
 
     /*
      * Fingerprint executables and core dumps.
@@ -1251,7 +1288,40 @@ int     main(int argc, char **argv)
            break;
        case 'N':
            if ((dsn_notify = dsn_notify_mask(optarg)) == 0)
-               msg_warn("bad -N option value -- ignored");
+               msg_warn("bad -N option value: '%s' -- ignored", optarg);
+           break;
+       case 'O':
+           if ((oval = optarg + strcspn(optarg, "="))[0] != 0)
+               oval += 1;
+           if (strncasecmp(optarg, "REQUIRETLS=", oval - optarg) == 0) {
+               if (var_reqtls_enable == 0) {
+                   msg_warn("Ignoring option '-O %s, because the "
+                            "configuration is '%s = %s'", optarg,
+                            VAR_REQTLS_ENABLE, CONFIG_BOOL_NO);
+                   continue;
+               } else if (strcasecmp(oval, CONFIG_BOOL_YES) == 0) {
+                   sm_sendopts |= SOPT_REQUIRETLS_ESMTP;
+                   continue;
+               } else if (strcasecmp(oval, CONFIG_BOOL_NO) == 0) {
+                   sm_sendopts &= ~SOPT_REQUIRETLS_ESMTP;
+                   continue;
+               }
+               msg_warn("bad -O option: '%s' -- ignored", optarg);
+           } else if (strncasecmp(optarg, "SMTPUTF8=", oval - optarg) == 0) {
+               if (var_smtputf8_enable == 0) {
+                   msg_warn("'-O %s' was requested, but the "
+                            "configuration is '%s = %s'", optarg,
+                            VAR_SMTPUTF8_ENABLE, CONFIG_BOOL_NO);
+                   continue;
+               } else if (strcasecmp(oval, CONFIG_BOOL_YES) == 0) {
+                   sm_sendopts |= SOPT_SMTPUTF8_REQUESTED;
+                   continue;
+               } else if (strcasecmp(oval, CONFIG_BOOL_NO) == 0) {
+                   sm_sendopts &= ~SOPT_SMTPUTF8_REQUESTED;
+                   continue;
+               }
+               msg_warn("bad -O option: '%s' -- ignored", optarg);
+           }
            break;
        case 'R':
            if ((dsn_ret = dsn_ret_code(optarg)) == 0)
index c54a3247671f72cb46c66984efd0961c22c6507c..bb9cefdf5a43fd549fa33953aa05f318ab3695e4 100644 (file)
@@ -216,7 +216,8 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
                arrival_time = atol(start);
            break;
        case REC_TYPE_SIZE:
-           if (msg_size_ok == 0) {
+           /* Maildrop files may have a preliminary SIZE record. */
+           if (msg_size_ok == 0 && strcmp(queue, MAIL_QUEUE_MAILDROP) != 0) {
                msg_size_ok = (start[strspn(start, "0123456789 ")] == 0
                               && (msg_size = atol(start)) >= 0);
                if (msg_size_ok == 0) {
index 9b812f50ea39b26354b8b961ac6d9a5bb1a21b2f..1159a1100b5488332afac84c977ab71b6e735a73 100644 (file)
@@ -2,16 +2,19 @@ SHELL = /bin/sh
 SRCS   = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
        smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c smtp_tls_policy.c \
        smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
-       smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c smtp_tlsrpt.c
+       smtp_sasl_auth_cache.c smtp_key.c smtp_misc.c smtp_tlsrpt.c \
+       smtp_reqtls_policy.c
 OBJS   = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
        smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o smtp_tls_policy.o \
        smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
-       smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o smtp_tlsrpt.o
-HDRS   = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h
-TESTSRC        = smtp_policy_test.c
+       smtp_sasl_auth_cache.o smtp_key.o smtp_misc.o smtp_tlsrpt.o \
+       smtp_reqtls_policy.o
+HDRS   = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h smtp_sasl_auth_cache.h \
+       smtp_reqtls_policy.h
+TESTSRC        = smtp_tls_policy_test.c smtp_reqtls_policy_test.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
-TESTPROG= smtp_unalias smtp_map11 smtp_tls_policy_test
+TESTPROG= smtp_unalias smtp_map11 smtp_tls_policy_test smtp_reqtls_policy_test
 PROG   = smtp
 INC_DIR        = ../../include
 LIBS   = ../../lib/lib$(LIB_PREFIX)master$(LIB_SUFFIX) \
@@ -33,7 +36,7 @@ Makefile: Makefile.in
 
 test:  $(TESTPROG)
 
-tests: smtp_map11_test test_smtp_tls_policy
+tests: smtp_map11_test test_smtp_tls_policy test_smtp_reqtls_policy
 
 root_tests:
 
@@ -64,6 +67,12 @@ smtp_map11_test: smtp_map11 smtp_map11.ref
        diff smtp_map11.ref smtp_map11.tmp
        rm -f smtp_map11.tmp
 
+smtp_reqtls_policy_test: smtp_reqtls_policy_test.c smtp_reqtls_policy.o
+       $(CC) $(CFLAGS) -o $@ $@.c smtp_reqtls_policy.o $(LIBS) $(SYSLIBS)
+
+test_smtp_reqtls_policy: smtp_reqtls_policy_test
+       $(SHLIB_ENV) $(VALGRIND) ./smtp_reqtls_policy_test
+
 SMTP_POLICY_TEST_OBJ = smtp_tls_policy_test.o smtp_tls_policy.o smtp_state.o smtp_key.o
 
 smtp_tls_policy_test: $(SMTP_POLICY_TEST_OBJ) $(LIBS)
@@ -118,6 +127,7 @@ smtp.o: ../../include/mymalloc.h
 smtp.o: ../../include/name_code.h
 smtp.o: ../../include/name_mask.h
 smtp.o: ../../include/nvtable.h
+smtp.o: ../../include/pol_stats.h
 smtp.o: ../../include/recipient_list.h
 smtp.o: ../../include/resolve_clnt.h
 smtp.o: ../../include/scache.h
@@ -136,6 +146,7 @@ smtp.o: lmtp_params.c
 smtp.o: smtp.c
 smtp.o: smtp.h
 smtp.o: smtp_params.c
+smtp.o: smtp_reqtls_policy.h
 smtp.o: smtp_sasl.h
 smtp_addr.o: ../../include/argv.h
 smtp_addr.o: ../../include/attr.h
@@ -164,6 +175,7 @@ smtp_addr.o: ../../include/name_code.h
 smtp_addr.o: ../../include/name_mask.h
 smtp_addr.o: ../../include/nvtable.h
 smtp_addr.o: ../../include/own_inet_addr.h
+smtp_addr.o: ../../include/pol_stats.h
 smtp_addr.o: ../../include/recipient_list.h
 smtp_addr.o: ../../include/resolve_clnt.h
 smtp_addr.o: ../../include/scache.h
@@ -181,6 +193,7 @@ smtp_addr.o: ../../include/vstring.h
 smtp_addr.o: smtp.h
 smtp_addr.o: smtp_addr.c
 smtp_addr.o: smtp_addr.h
+smtp_addr.o: smtp_reqtls_policy.h
 smtp_chat.o: ../../include/argv.h
 smtp_chat.o: ../../include/attr.h
 smtp_chat.o: ../../include/check_arg.h
@@ -213,6 +226,7 @@ smtp_chat.o: ../../include/mymalloc.h
 smtp_chat.o: ../../include/name_code.h
 smtp_chat.o: ../../include/name_mask.h
 smtp_chat.o: ../../include/nvtable.h
+smtp_chat.o: ../../include/pol_stats.h
 smtp_chat.o: ../../include/post_mail.h
 smtp_chat.o: ../../include/recipient_list.h
 smtp_chat.o: ../../include/resolve_clnt.h
@@ -232,6 +246,7 @@ smtp_chat.o: ../../include/vstream.h
 smtp_chat.o: ../../include/vstring.h
 smtp_chat.o: smtp.h
 smtp_chat.o: smtp_chat.c
+smtp_chat.o: smtp_reqtls_policy.h
 smtp_connect.o: ../../include/argv.h
 smtp_connect.o: ../../include/attr.h
 smtp_connect.o: ../../include/check_arg.h
@@ -265,6 +280,7 @@ smtp_connect.o: ../../include/name_code.h
 smtp_connect.o: ../../include/name_mask.h
 smtp_connect.o: ../../include/nvtable.h
 smtp_connect.o: ../../include/own_inet_addr.h
+smtp_connect.o: ../../include/pol_stats.h
 smtp_connect.o: ../../include/recipient_list.h
 smtp_connect.o: ../../include/resolve_clnt.h
 smtp_connect.o: ../../include/sane_connect.h
@@ -286,6 +302,7 @@ smtp_connect.o: ../../include/vstring.h
 smtp_connect.o: smtp.h
 smtp_connect.o: smtp_addr.h
 smtp_connect.o: smtp_connect.c
+smtp_connect.o: smtp_reqtls_policy.h
 smtp_connect.o: smtp_reuse.h
 smtp_key.o: ../../include/argv.h
 smtp_key.o: ../../include/attr.h
@@ -311,6 +328,7 @@ smtp_key.o: ../../include/mymalloc.h
 smtp_key.o: ../../include/name_code.h
 smtp_key.o: ../../include/name_mask.h
 smtp_key.o: ../../include/nvtable.h
+smtp_key.o: ../../include/pol_stats.h
 smtp_key.o: ../../include/recipient_list.h
 smtp_key.o: ../../include/resolve_clnt.h
 smtp_key.o: ../../include/scache.h
@@ -327,6 +345,7 @@ smtp_key.o: ../../include/vstream.h
 smtp_key.o: ../../include/vstring.h
 smtp_key.o: smtp.h
 smtp_key.o: smtp_key.c
+smtp_key.o: smtp_reqtls_policy.h
 smtp_map11.o: ../../include/argv.h
 smtp_map11.o: ../../include/attr.h
 smtp_map11.o: ../../include/check_arg.h
@@ -351,6 +370,7 @@ smtp_map11.o: ../../include/mymalloc.h
 smtp_map11.o: ../../include/name_code.h
 smtp_map11.o: ../../include/name_mask.h
 smtp_map11.o: ../../include/nvtable.h
+smtp_map11.o: ../../include/pol_stats.h
 smtp_map11.o: ../../include/quote_822_local.h
 smtp_map11.o: ../../include/quote_flags.h
 smtp_map11.o: ../../include/recipient_list.h
@@ -368,6 +388,7 @@ smtp_map11.o: ../../include/vstream.h
 smtp_map11.o: ../../include/vstring.h
 smtp_map11.o: smtp.h
 smtp_map11.o: smtp_map11.c
+smtp_map11.o: smtp_reqtls_policy.h
 smtp_misc.o: ../../include/argv.h
 smtp_misc.o: ../../include/attr.h
 smtp_misc.o: ../../include/check_arg.h
@@ -391,6 +412,7 @@ smtp_misc.o: ../../include/mymalloc.h
 smtp_misc.o: ../../include/name_code.h
 smtp_misc.o: ../../include/name_mask.h
 smtp_misc.o: ../../include/nvtable.h
+smtp_misc.o: ../../include/pol_stats.h
 smtp_misc.o: ../../include/quote_821_local.h
 smtp_misc.o: ../../include/quote_822_local.h
 smtp_misc.o: ../../include/quote_flags.h
@@ -409,6 +431,7 @@ smtp_misc.o: ../../include/vstream.h
 smtp_misc.o: ../../include/vstring.h
 smtp_misc.o: smtp.h
 smtp_misc.o: smtp_misc.c
+smtp_misc.o: smtp_reqtls_policy.h
 smtp_params.o: smtp_params.c
 smtp_proto.o: ../../include/argv.h
 smtp_proto.o: ../../include/attr.h
@@ -436,7 +459,6 @@ smtp_proto.o: ../../include/mail_queue.h
 smtp_proto.o: ../../include/maps.h
 smtp_proto.o: ../../include/mark_corrupt.h
 smtp_proto.o: ../../include/match_list.h
-smtp_proto.o: ../../include/match_parent_style.h
 smtp_proto.o: ../../include/mime_state.h
 smtp_proto.o: ../../include/msg.h
 smtp_proto.o: ../../include/msg_stats.h
@@ -448,6 +470,7 @@ smtp_proto.o: ../../include/name_code.h
 smtp_proto.o: ../../include/name_mask.h
 smtp_proto.o: ../../include/nvtable.h
 smtp_proto.o: ../../include/off_cvt.h
+smtp_proto.o: ../../include/pol_stats.h
 smtp_proto.o: ../../include/quote_822_local.h
 smtp_proto.o: ../../include/quote_flags.h
 smtp_proto.o: ../../include/rec_type.h
@@ -475,6 +498,7 @@ smtp_proto.o: ../../include/vstring_vstream.h
 smtp_proto.o: ../../include/xtext.h
 smtp_proto.o: smtp.h
 smtp_proto.o: smtp_proto.c
+smtp_proto.o: smtp_reqtls_policy.h
 smtp_proto.o: smtp_sasl.h
 smtp_rcpt.o: ../../include/argv.h
 smtp_rcpt.o: ../../include/attr.h
@@ -502,6 +526,7 @@ smtp_rcpt.o: ../../include/mymalloc.h
 smtp_rcpt.o: ../../include/name_code.h
 smtp_rcpt.o: ../../include/name_mask.h
 smtp_rcpt.o: ../../include/nvtable.h
+smtp_rcpt.o: ../../include/pol_stats.h
 smtp_rcpt.o: ../../include/recipient_list.h
 smtp_rcpt.o: ../../include/resolve_clnt.h
 smtp_rcpt.o: ../../include/scache.h
@@ -519,6 +544,33 @@ smtp_rcpt.o: ../../include/vstream.h
 smtp_rcpt.o: ../../include/vstring.h
 smtp_rcpt.o: smtp.h
 smtp_rcpt.o: smtp_rcpt.c
+smtp_rcpt.o: smtp_reqtls_policy.h
+smtp_reqtls_policy.o: ../../include/argv.h
+smtp_reqtls_policy.o: ../../include/check_arg.h
+smtp_reqtls_policy.o: ../../include/dict.h
+smtp_reqtls_policy.o: ../../include/mail_params.h
+smtp_reqtls_policy.o: ../../include/midna_domain.h
+smtp_reqtls_policy.o: ../../include/msg.h
+smtp_reqtls_policy.o: ../../include/myflock.h
+smtp_reqtls_policy.o: ../../include/mymalloc.h
+smtp_reqtls_policy.o: ../../include/stringops.h
+smtp_reqtls_policy.o: ../../include/sys_defs.h
+smtp_reqtls_policy.o: ../../include/valid_hostname.h
+smtp_reqtls_policy.o: ../../include/vbuf.h
+smtp_reqtls_policy.o: ../../include/vstream.h
+smtp_reqtls_policy.o: ../../include/vstring.h
+smtp_reqtls_policy.o: smtp_reqtls_policy.c
+smtp_reqtls_policy.o: smtp_reqtls_policy.h
+smtp_reqtls_policy_test.o: ../../include/check_arg.h
+smtp_reqtls_policy_test.o: ../../include/msg.h
+smtp_reqtls_policy_test.o: ../../include/msg_vstream.h
+smtp_reqtls_policy_test.o: ../../include/stringops.h
+smtp_reqtls_policy_test.o: ../../include/sys_defs.h
+smtp_reqtls_policy_test.o: ../../include/vbuf.h
+smtp_reqtls_policy_test.o: ../../include/vstream.h
+smtp_reqtls_policy_test.o: ../../include/vstring.h
+smtp_reqtls_policy_test.o: smtp_reqtls_policy.h
+smtp_reqtls_policy_test.o: smtp_reqtls_policy_test.c
 smtp_reuse.o: ../../include/argv.h
 smtp_reuse.o: ../../include/attr.h
 smtp_reuse.o: ../../include/check_arg.h
@@ -542,6 +594,7 @@ smtp_reuse.o: ../../include/mymalloc.h
 smtp_reuse.o: ../../include/name_code.h
 smtp_reuse.o: ../../include/name_mask.h
 smtp_reuse.o: ../../include/nvtable.h
+smtp_reuse.o: ../../include/pol_stats.h
 smtp_reuse.o: ../../include/recipient_list.h
 smtp_reuse.o: ../../include/resolve_clnt.h
 smtp_reuse.o: ../../include/scache.h
@@ -557,6 +610,7 @@ smtp_reuse.o: ../../include/vbuf.h
 smtp_reuse.o: ../../include/vstream.h
 smtp_reuse.o: ../../include/vstring.h
 smtp_reuse.o: smtp.h
+smtp_reuse.o: smtp_reqtls_policy.h
 smtp_reuse.o: smtp_reuse.c
 smtp_reuse.o: smtp_reuse.h
 smtp_sasl_auth_cache.o: ../../include/argv.h
@@ -585,6 +639,7 @@ smtp_sasl_auth_cache.o: ../../include/mymalloc.h
 smtp_sasl_auth_cache.o: ../../include/name_code.h
 smtp_sasl_auth_cache.o: ../../include/name_mask.h
 smtp_sasl_auth_cache.o: ../../include/nvtable.h
+smtp_sasl_auth_cache.o: ../../include/pol_stats.h
 smtp_sasl_auth_cache.o: ../../include/recipient_list.h
 smtp_sasl_auth_cache.o: ../../include/resolve_clnt.h
 smtp_sasl_auth_cache.o: ../../include/scache.h
@@ -600,6 +655,7 @@ smtp_sasl_auth_cache.o: ../../include/vbuf.h
 smtp_sasl_auth_cache.o: ../../include/vstream.h
 smtp_sasl_auth_cache.o: ../../include/vstring.h
 smtp_sasl_auth_cache.o: smtp.h
+smtp_sasl_auth_cache.o: smtp_reqtls_policy.h
 smtp_sasl_auth_cache.o: smtp_sasl_auth_cache.c
 smtp_sasl_auth_cache.o: smtp_sasl_auth_cache.h
 smtp_sasl_glue.o: ../../include/argv.h
@@ -627,6 +683,7 @@ smtp_sasl_glue.o: ../../include/mymalloc.h
 smtp_sasl_glue.o: ../../include/name_code.h
 smtp_sasl_glue.o: ../../include/name_mask.h
 smtp_sasl_glue.o: ../../include/nvtable.h
+smtp_sasl_glue.o: ../../include/pol_stats.h
 smtp_sasl_glue.o: ../../include/recipient_list.h
 smtp_sasl_glue.o: ../../include/resolve_clnt.h
 smtp_sasl_glue.o: ../../include/scache.h
@@ -645,6 +702,7 @@ smtp_sasl_glue.o: ../../include/vstream.h
 smtp_sasl_glue.o: ../../include/vstring.h
 smtp_sasl_glue.o: ../../include/xsasl.h
 smtp_sasl_glue.o: smtp.h
+smtp_sasl_glue.o: smtp_reqtls_policy.h
 smtp_sasl_glue.o: smtp_sasl.h
 smtp_sasl_glue.o: smtp_sasl_auth_cache.h
 smtp_sasl_glue.o: smtp_sasl_glue.c
@@ -671,6 +729,7 @@ smtp_sasl_proto.o: ../../include/mymalloc.h
 smtp_sasl_proto.o: ../../include/name_code.h
 smtp_sasl_proto.o: ../../include/name_mask.h
 smtp_sasl_proto.o: ../../include/nvtable.h
+smtp_sasl_proto.o: ../../include/pol_stats.h
 smtp_sasl_proto.o: ../../include/recipient_list.h
 smtp_sasl_proto.o: ../../include/resolve_clnt.h
 smtp_sasl_proto.o: ../../include/sasl_mech_filter.h
@@ -687,6 +746,7 @@ smtp_sasl_proto.o: ../../include/vbuf.h
 smtp_sasl_proto.o: ../../include/vstream.h
 smtp_sasl_proto.o: ../../include/vstring.h
 smtp_sasl_proto.o: smtp.h
+smtp_sasl_proto.o: smtp_reqtls_policy.h
 smtp_sasl_proto.o: smtp_sasl.h
 smtp_sasl_proto.o: smtp_sasl_proto.c
 smtp_session.o: ../../include/argv.h
@@ -713,6 +773,7 @@ smtp_session.o: ../../include/mymalloc.h
 smtp_session.o: ../../include/name_code.h
 smtp_session.o: ../../include/name_mask.h
 smtp_session.o: ../../include/nvtable.h
+smtp_session.o: ../../include/pol_stats.h
 smtp_session.o: ../../include/recipient_list.h
 smtp_session.o: ../../include/resolve_clnt.h
 smtp_session.o: ../../include/scache.h
@@ -728,6 +789,7 @@ smtp_session.o: ../../include/vbuf.h
 smtp_session.o: ../../include/vstream.h
 smtp_session.o: ../../include/vstring.h
 smtp_session.o: smtp.h
+smtp_session.o: smtp_reqtls_policy.h
 smtp_session.o: smtp_sasl.h
 smtp_session.o: smtp_session.c
 smtp_state.o: ../../include/argv.h
@@ -754,6 +816,7 @@ smtp_state.o: ../../include/mymalloc.h
 smtp_state.o: ../../include/name_code.h
 smtp_state.o: ../../include/name_mask.h
 smtp_state.o: ../../include/nvtable.h
+smtp_state.o: ../../include/pol_stats.h
 smtp_state.o: ../../include/recipient_list.h
 smtp_state.o: ../../include/resolve_clnt.h
 smtp_state.o: ../../include/scache.h
@@ -769,6 +832,7 @@ smtp_state.o: ../../include/vbuf.h
 smtp_state.o: ../../include/vstream.h
 smtp_state.o: ../../include/vstring.h
 smtp_state.o: smtp.h
+smtp_state.o: smtp_reqtls_policy.h
 smtp_state.o: smtp_sasl.h
 smtp_state.o: smtp_state.c
 smtp_tls_policy.o: ../../include/argv.h
@@ -796,6 +860,7 @@ smtp_tls_policy.o: ../../include/mymalloc.h
 smtp_tls_policy.o: ../../include/name_code.h
 smtp_tls_policy.o: ../../include/name_mask.h
 smtp_tls_policy.o: ../../include/nvtable.h
+smtp_tls_policy.o: ../../include/pol_stats.h
 smtp_tls_policy.o: ../../include/recipient_list.h
 smtp_tls_policy.o: ../../include/resolve_clnt.h
 smtp_tls_policy.o: ../../include/sane_strtol.h
@@ -815,6 +880,7 @@ smtp_tls_policy.o: ../../include/vbuf.h
 smtp_tls_policy.o: ../../include/vstream.h
 smtp_tls_policy.o: ../../include/vstring.h
 smtp_tls_policy.o: smtp.h
+smtp_tls_policy.o: smtp_reqtls_policy.h
 smtp_tls_policy.o: smtp_tls_policy.c
 smtp_tls_policy_test.o: ../../include/argv.h
 smtp_tls_policy_test.o: ../../include/attr.h
@@ -840,6 +906,7 @@ smtp_tls_policy_test.o: ../../include/mymalloc.h
 smtp_tls_policy_test.o: ../../include/name_code.h
 smtp_tls_policy_test.o: ../../include/name_mask.h
 smtp_tls_policy_test.o: ../../include/nvtable.h
+smtp_tls_policy_test.o: ../../include/pol_stats.h
 smtp_tls_policy_test.o: ../../include/recipient_list.h
 smtp_tls_policy_test.o: ../../include/resolve_clnt.h
 smtp_tls_policy_test.o: ../../include/scache.h
@@ -855,6 +922,7 @@ smtp_tls_policy_test.o: ../../include/vbuf.h
 smtp_tls_policy_test.o: ../../include/vstream.h
 smtp_tls_policy_test.o: ../../include/vstring.h
 smtp_tls_policy_test.o: smtp.h
+smtp_tls_policy_test.o: smtp_reqtls_policy.h
 smtp_tls_policy_test.o: smtp_tls_policy_test.c
 smtp_tlsrpt.o: ../../include/argv.h
 smtp_tlsrpt.o: ../../include/attr.h
@@ -881,6 +949,7 @@ smtp_tlsrpt.o: ../../include/mymalloc.h
 smtp_tlsrpt.o: ../../include/name_code.h
 smtp_tlsrpt.o: ../../include/name_mask.h
 smtp_tlsrpt.o: ../../include/nvtable.h
+smtp_tlsrpt.o: ../../include/pol_stats.h
 smtp_tlsrpt.o: ../../include/recipient_list.h
 smtp_tlsrpt.o: ../../include/resolve_clnt.h
 smtp_tlsrpt.o: ../../include/scache.h
@@ -897,6 +966,7 @@ smtp_tlsrpt.o: ../../include/vbuf.h
 smtp_tlsrpt.o: ../../include/vstream.h
 smtp_tlsrpt.o: ../../include/vstring.h
 smtp_tlsrpt.o: smtp.h
+smtp_tlsrpt.o: smtp_reqtls_policy.h
 smtp_tlsrpt.o: smtp_tlsrpt.c
 smtp_trouble.o: ../../include/argv.h
 smtp_trouble.o: ../../include/attr.h
@@ -925,6 +995,7 @@ smtp_trouble.o: ../../include/mymalloc.h
 smtp_trouble.o: ../../include/name_code.h
 smtp_trouble.o: ../../include/name_mask.h
 smtp_trouble.o: ../../include/nvtable.h
+smtp_trouble.o: ../../include/pol_stats.h
 smtp_trouble.o: ../../include/recipient_list.h
 smtp_trouble.o: ../../include/resolve_clnt.h
 smtp_trouble.o: ../../include/scache.h
@@ -941,6 +1012,7 @@ smtp_trouble.o: ../../include/vbuf.h
 smtp_trouble.o: ../../include/vstream.h
 smtp_trouble.o: ../../include/vstring.h
 smtp_trouble.o: smtp.h
+smtp_trouble.o: smtp_reqtls_policy.h
 smtp_trouble.o: smtp_sasl.h
 smtp_trouble.o: smtp_trouble.c
 smtp_unalias.o: ../../include/argv.h
@@ -965,6 +1037,7 @@ smtp_unalias.o: ../../include/mymalloc.h
 smtp_unalias.o: ../../include/name_code.h
 smtp_unalias.o: ../../include/name_mask.h
 smtp_unalias.o: ../../include/nvtable.h
+smtp_unalias.o: ../../include/pol_stats.h
 smtp_unalias.o: ../../include/recipient_list.h
 smtp_unalias.o: ../../include/resolve_clnt.h
 smtp_unalias.o: ../../include/scache.h
@@ -979,4 +1052,5 @@ smtp_unalias.o: ../../include/vbuf.h
 smtp_unalias.o: ../../include/vstream.h
 smtp_unalias.o: ../../include/vstring.h
 smtp_unalias.o: smtp.h
+smtp_unalias.o: smtp_reqtls_policy.h
 smtp_unalias.o: smtp_unalias.c
index b3d456a923f0df03d827850f187cdc852669b011..876decf0be0a0d545d85ad78d88812764116a87b 100644 (file)
@@ -68,6 +68,7 @@
        VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
        VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
        VAR_LMTP_TLSRPT_SOCKNAME, DEF_LMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
+       VAR_LMTP_REQTLS_POLICY, DEF_LMTP_REQTLS_POLICY, &var_smtp_reqtls_policy, 0, 0,
        0,
     };
     static const CONFIG_TIME_TABLE lmtp_time_table[] = {
 #ifdef USE_TLS
        VAR_LMTP_TLS_ENF_STS_MX_PAT, DEF_LMTP_TLS_ENF_STS_MX_PAT, &var_smtp_tls_enf_sts_mx_pat,
 #endif
+       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,
     };
index 873ef2ebbae4f64f24035fbb38d85a1066e9332f..46e49d9573ab45ed89644b326764bd89ee90eafa 100644 (file)
 /*     RFC 6531 (Internationalized SMTP)
 /*     RFC 6533 (Internationalized Delivery Status Notifications)
 /*     RFC 7672 (SMTP security via opportunistic DANE TLS)
-/*     RFC 8689 (TLS-Required message header)
+/*     RFC 8689 (SMTP REQUIRETLS extension, TLS-Required header)
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8)
 /*     or \fBpostlogd\fR(8).
 /* .IP "\fBsmtp_sasl_password_result_delimiter (:)\fR"
 /*     The delimiter between username and password in sasl_passwd_maps lookup
 /*     results.
-/* STARTTLS SUPPORT CONTROLS
+/* TLS SUPPORT CONTROLS
 /* .ad
 /* .fi
 /*     Detailed information about STARTTLS configuration may be found
 /*     Transform the TLS policy from an STS policy plugin: connect to
 /*     an MX host only if its name matches any STS policy MX host pattern,
 /*     and match the server certificate against the MX hostname.
-/* OBSOLETE STARTTLS CONTROLS
+/* .PP
+/*     Available in Postfix version 3.11 and later:
+/* .IP "\fBrequiretls_enable (yes)\fR"
+/*     Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+/*     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.
+/* OBSOLETE TLS CONTROLS
 /* .ad
 /* .fi
 /*     The following configuration parameters exist for compatibility
@@ -1171,6 +1182,8 @@ bool    var_allow_srv_fallback;
 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;
@@ -1198,6 +1211,7 @@ HBC_CHECKS *smtp_body_checks;             /* limited body checks */
 SMTP_CLI_ATTR smtp_cli_attr;           /* parsed command-line */
 int     smtp_hfrom_format;             /* postmaster notifications */
 STRING_LIST *smtp_use_srv_lookup;
+SMTP_REQTLS_POLICY *smtp_reqtls_policy;
 
 #ifdef USE_TLS
 
@@ -1705,6 +1719,16 @@ static void pre_init(char *unused_name, char **unused_argv)
     if (*var_smtp_dns_re_filter)
        dns_rr_filter_compile(VAR_LMTP_SMTP(DNS_RE_FILTER),
                              var_smtp_dns_re_filter);
+
+    /*
+     * REQUIRETLS enforcement policy. The parser appends a default action: DO
+     * NOT skip the code below if the policy string is empty. When
+     * var_reqtls_enable != 0, smtp_reqtls_policy must also be != 0.
+     */
+    if (var_reqtls_enable)
+       smtp_reqtls_policy =
+           smtp_reqtls_policy_parse(VAR_LMTP_SMTP(REQTLS_POLICY),
+                                    var_smtp_reqtls_policy);
 }
 
 /* pre_accept - see if tables have changed */
index 984387a857457419423d818d74755071fced53e5..df83a5fe8f204896224edb295c8fe7f04d4b9167 100644 (file)
@@ -33,6 +33,7 @@
 #include <dsn_buf.h>
 #include <header_body_checks.h>
 #include <sendopts.h>
+#include <pol_stats.h>
 
  /*
   * Postfix TLS library.
   */
 #include <tls_proxy.h>
 
+ /*
+  * This application.
+  */
+#include <smtp_reqtls_policy.h>
+
  /*
   * Global iterator support. This is updated by the connection-management
   * loop, and contains dynamic context that appears in lookup keys for SASL
@@ -195,13 +201,15 @@ 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 */
 #ifdef USE_TLSRPT
     struct TLSRPT_WRAPPER *tlsrpt;
 #endif
+    int     reqtls_level;              /* from smtp_reqtls_policy */
+    POL_STATS *tls_stats;              /* TLS feature policy compliance */
 #endif
 
     /*
@@ -240,10 +248,43 @@ 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_NAME_NOCMATCH    "nocertmatch"
+#define SMTP_TLS_STAT_NAME_NOSTTLS     "nostarttls"
+#define SMTP_TLS_STAT_NAME_NOTLS       "noencryption"
+#define SMTP_TLS_STAT_NAME_NONE                "none"
+#define SMTP_TLS_STAT_NAME_UNKNOWN     "unknown"
+
+#define smtp_tls_stat_activate_sec_level(tstats, level) \
+       pol_stat_activate((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \
+           str_tls_level(level))
+
+#define smtp_tls_stat_activate_sec_unknown(tstats) \
+       pol_stat_activate((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \
+           SMTP_TLS_STAT_NAME_UNKNOWN)
+
+#define smtp_tls_stat_decide_sec_level(tstats, level, status) \
+       pol_stat_decide((tstats), SMTP_TLS_STAT_IDX_SEC_LEVEL, \
+           str_tls_level(level), (status))
+
+#define smtp_tls_stat_activate_reqtls(tstats, name) \
+       pol_stat_activate((tstats), SMTP_TLS_STAT_IDX_REQTLS, \
+       (name))
+
+#define smtp_tls_stat_decide_reqtls(tstats, name, status) \
+       pol_stat_decide((tstats), SMTP_TLS_STAT_IDX_REQTLS, \
+       (name), (status))
+
 #ifdef USE_TLS
 #define STATE_TLS_NOT_REQUIRED(state) \
-       (var_tls_required_enable && \
-           ((state)->request->sendopts & SOPT_REQUIRETLS_HEADER))
+       (var_tls_required_enable \
+           && (var_reqtls_enable == 0 \
+               || ((state)->request->sendopts & SOPT_REQUIRETLS_ESMTP) == 0) \
+           && ((state)->request->sendopts & SOPT_REQUIRETLS_HEADER))
 #endif
 
  /*
@@ -294,6 +335,7 @@ typedef struct SMTP_STATE {
 #define SMTP_FEATURE_XFORWARD_IDENT    (1<<20)
 #define SMTP_FEATURE_SMTPUTF8          (1<<21) /* RFC 6531 */
 #define SMTP_FEATURE_FROM_PROXY                (1<<22) /* proxied connection */
+#define SMTP_FEATURE_REQTLS            (1<<23) /* RFC 8689 */
 
  /*
   * Features that passivate under the endpoint. Be sure to passivate all
@@ -368,6 +410,7 @@ extern STRING_LIST *smtp_use_srv_lookup;/* services with SRV record lookup */
 
 extern TLS_APPL_STATE *smtp_tls_ctx;   /* client-side TLS engine */
 extern int smtp_tls_insecure_mx_policy;        /* DANE post insecure MX? */
+extern SMTP_REQTLS_POLICY *smtp_reqtls_policy; /* parsed list */
 
 #endif
 
@@ -550,6 +593,7 @@ extern HBC_CALL_BACKS smtp_hbc_callbacks[];
 #define PLAINTEXT_FALLBACK_OK_AFTER_STARTTLS_FAILURE \
        (session->tls_context == 0 \
            && state->tls->level == TLS_LEV_MAY \
+           && !TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level) \
            && (TRACE_REQ_ONLY || PREACTIVE_DELAY >= var_min_backoff_time) \
            && !HAVE_SASL_CREDENTIALS)
 
@@ -557,6 +601,7 @@ extern HBC_CALL_BACKS smtp_hbc_callbacks[];
        (session->tls_context != 0 \
            && SMTP_RCPT_LEFT(state) > SMTP_RCPT_MARK_COUNT(state) \
            && state->tls->level == TLS_LEV_MAY \
+           && !TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level) \
            && (TRACE_REQ_ONLY || PREACTIVE_DELAY >= var_min_backoff_time) \
            && !HAVE_SASL_CREDENTIALS)
 
@@ -647,8 +692,10 @@ extern void smtp_rcpt_done(SMTP_STATE *, SMTP_RESP *, RECIPIENT *);
  /*
   * smtp_trouble.c
   */
-#define SMTP_THROTTLE  1
-#define SMTP_NOTHROTTLE        0
+#define SMTP_MISC_FAIL_NONE            0
+#define SMTP_MISC_FAIL_THROTTLE                (1<<0)
+#define SMTP_MISC_FAIL_SOFT_NON_FINAL  (1<<1)
+#define SMTP_MISC_FAIL_DONT_CACHE      (1<<2)
 extern int smtp_sess_fail(SMTP_STATE *);
 extern int PRINTFLIKE(5, 6) smtp_misc_fail(SMTP_STATE *, int, const char *,
                                             SMTP_RESP *, const char *,...);
@@ -658,9 +705,9 @@ extern void PRINTFLIKE(5, 6) smtp_rcpt_fail(SMTP_STATE *, RECIPIENT *,
 extern int smtp_stream_except(SMTP_STATE *, int, const char *);
 
 #define smtp_site_fail(state, mta, resp, ...) \
-       smtp_misc_fail((state), SMTP_THROTTLE, (mta), (resp), __VA_ARGS__)
+    smtp_misc_fail((state), SMTP_MISC_FAIL_THROTTLE, (mta), (resp), __VA_ARGS__)
 #define smtp_mesg_fail(state, mta, resp, ...) \
-       smtp_misc_fail((state), SMTP_NOTHROTTLE, (mta), (resp), __VA_ARGS__)
+    smtp_misc_fail((state), SMTP_MISC_FAIL_NONE, (mta), (resp), __VA_ARGS__)
 
  /*
   * smtp_unalias.c
@@ -695,13 +742,14 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
 #define SMTP_KEY_FLAG_PORT             (1<<6)  /* remote port */
 #define SMTP_KEY_FLAG_TLS_LEVEL                (1<<7)  /* requested TLS level */
 #define SMTP_KEY_FLAG_REQ_SMTPUTF8     (1<<8)  /* SMTPUTF8 is required */
+#define SMTP_KEY_FLAG_REQTLS_LEVEL     (1<<9)  /* REQUIRETLS enforcement */
 
 #define SMTP_KEY_MASK_ALL \
        (SMTP_KEY_FLAG_SERVICE | SMTP_KEY_FLAG_SENDER | \
        SMTP_KEY_FLAG_REQ_NEXTHOP | \
        SMTP_KEY_FLAG_CUR_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | \
        SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL | \
-       SMTP_KEY_FLAG_REQ_SMTPUTF8)
+       SMTP_KEY_FLAG_REQ_SMTPUTF8 | SMTP_KEY_FLAG_REQTLS_LEVEL)
 
  /*
   * Conditional lookup-key flags for cached connections that may be
@@ -745,7 +793,7 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
 #define SMTP_KEY_MASK_SCACHE_DEST_LABEL \
        (SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
        | SMTP_KEY_FLAG_REQ_NEXTHOP | SMTP_KEY_FLAG_TLS_LEVEL \
-       | SMTP_KEY_FLAG_REQ_SMTPUTF8)
+       | SMTP_KEY_FLAG_REQ_SMTPUTF8 | SMTP_KEY_FLAG_REQTLS_LEVEL)
 
  /*
   * Connection-cache endpoint lookup key. The SENDER, CUR_NEXTHOP, HOSTNAME,
@@ -765,7 +813,7 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
        | COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
        | COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP | SMTP_KEY_FLAG_ADDR \
        | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL \
-       | SMTP_KEY_FLAG_REQ_SMTPUTF8)
+       | SMTP_KEY_FLAG_REQ_SMTPUTF8 | SMTP_KEY_FLAG_REQTLS_LEVEL)
 
  /*
   * Silly little macros.
index 6354a857853279f851888163920fa9dc5c3ea8be..5b2eb1d52447ae726dd9bd8bfa229f0ab640f62c 100644 (file)
@@ -507,27 +507,81 @@ static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state)
     SMTP_ITERATOR *iter = state->iterator;
     SMTP_TLS_POLICY *tls = state->tls;
 
+    /*
+     * Prepare TLS feature status logging.
+     */
+    if (state->tls_stats) {
+       pol_stats_revert(state->tls_stats);
+       if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE)
+           smtp_tls_stat_activate_reqtls(state->tls_stats,
+                                         SMTP_TLS_STAT_NAME_REQTLS);
+    }
+
     /*
      * Determine the TLS level for this destination.
      */
     if (!smtp_tls_policy_cache_query(why, tls, iter)) {
+       if (state->tls_stats)
+           smtp_tls_stat_activate_sec_unknown(state->tls_stats);
        return (0);
     }
+    if (state->tls_stats)
+       smtp_tls_stat_activate_sec_level(state->tls_stats,
+                                        state->tls->level);
 
     /*
-     * If the sender requires verified TLS, the TLS level must enforce a
-     * server certificate match.
+     * Skip this destination if its TLS policy cannot satisfy the REQUIRETLS
+     * policy for this destination (REQUIRETLS Failure).
+     * 
+     * Otherwise, log what would fail if REQUIRETLS was fully enforced
+     * (REQUIRETLS Debug).
+     * 
+     * Finally, skip this destination if its REQUIRETLS policy is bad.
      */
-#if 0
-    else if ((state->request->sendopts & SOPT_REQUIRETLS_ESMTP)) {
+    switch (state->reqtls_level) {
+    case SMTP_REQTLS_POLICY_ACT_ENFORCE:
        if (TLS_MUST_MATCH(tls->level) == 0) {
-           dsb_simple(why, "5.7.10", "Sender requires verified TLS, "
-                      " but my configured TLS security level is '%s %s'",
-                      var_mail_name, str_tls_level(tls->level));
+           if (state->tls_stats)
+               smtp_tls_stat_decide_reqtls(state->tls_stats,
+                                           SMTP_TLS_STAT_NAME_NOCMATCH,
+                                           POL_STAT_VIOLATION);
+           dsb_simple(why, "5.7.10", "Sender requested REQUIRETLS, "
+                      "but my configured TLS security level '%s' "
+                      "disables certificate matching. The last "
+                      "attempted server was %s", str_tls_level(tls->level),
+                      STR(iter->host));
            return (0);
        }
+       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,
+                                           SMTP_TLS_STAT_NAME_NOTLS,
+                                           POL_STAT_VIOLATION);
+           dsb_simple(why, "5.7.10", "Sender requested REQUIRETLS, "
+                      "but my configured TLS security level '%s' "
+                      "disables encryption. The last attempted "
+                      "server was %s", str_tls_level(tls->level),
+                      STR(iter->host));
+           return (0);
+       } else if (TLS_MUST_MATCH(tls->level) == 0) {
+           msg_info("%s: Sender requested REQUIRETLS, but my "
+                    "configured TLS security level '%s' disables "
+                    "certificate matching. The last attempted server "
+                    "was %s", state->request->queue_id,
+                    str_tls_level(tls->level), STR(iter->host));
+       }
+       break;
+    case SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC:
+    case SMTP_REQTLS_POLICY_ACT_DISABLE:
+       break;
+    default:
+       dsb_simple(why, "4.7.10", "REQUIRETLS policy configuration "
+                  "error. The last attempted server was %s",
+                  STR(iter->host));
+       return (0);
     }
-#endif
 
     /*
      * Success.
@@ -564,6 +618,19 @@ static void smtp_connect_local(SMTP_STATE *state, const char *path)
     if (state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE_MASK)
        SET_SCACHE_REQUEST_NEXTHOP(state, path);
 
+    /*
+     * REQUIRETLS policy selection is based on the same TLS net-hop name as
+     * with certificate matching. When var_reqtls_enable != 0,
+     * smtp_reqtls_policy must also be != 0.
+     */
+#ifdef USE_TLS
+    if (STATE_REQTLS_IS_REQUESTED(var_reqtls_enable, state))
+       state->reqtls_level =
+           smtp_reqtls_policy_eval(smtp_reqtls_policy, var_myhostname);
+    else
+       state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE;
+#endif
+
     /*
      * Here we ensure that the iter->addr member refers to a copy of the
      * UNIX-domain pathname, so that smtp_save_session() will cache the
@@ -974,6 +1041,19 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
            state->tlsrpt = 0;
 #endif                                         /* USE_TLSRPT */
 
+       /*
+        * REQUIRETLS policy selection is based on the same TLS net-hop name
+        * as with certificate matching. When var_reqtls_enable != 0,
+        * smtp_reqtls_policy must also be != 0.
+        */
+#ifdef USE_TLS
+       if (STATE_REQTLS_IS_REQUESTED(var_reqtls_enable, state))
+           state->reqtls_level =
+               smtp_reqtls_policy_eval(smtp_reqtls_policy, domain);
+       else
+           state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE;
+#endif
+
        /*
         * Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a
         * quoted domain is specified or when DNS lookups are disabled.
@@ -1129,7 +1209,16 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop,
                continue;
                /* XXX Assume there is no code at the end of this loop. */
            }
-           /* Disable TLS when retrying after a handshake failure */
+
+           /*
+            * Disable TLS when retrying after a handshake failure. This must
+            * never happen when TLS is required. See PLAINTEXT_FALLBACK_OK
+            * macros.
+            * 
+            * By dropping the TLS level after smtp_get_effective_tls_level()
+            * and smtp_tls_stat_activate_*(), we will properly record the
+            * fallback for the TLS level etc. in TLS status logging.
+            */
            if (retry_plain) {
                state->tls->level = TLS_LEV_NONE;
                retry_plain = 0;
index 7f52f8298d45a9917c47868ffe1dcfd1a45c27e8..9c716bf85119b30984db1ea0f7bb0f2a3ffda90f 100644 (file)
@@ -69,6 +69,8 @@
 /*     The requested TLS security level.
 /* .IP SMTP_KEY_FLAG_REQ_SMTPUTF8
 /*     Whether SMTPUTF8 support is required.
+/* .IP SMTP_KEY_FLAG_REQTLS_LEVEL
+/*     The REQUIRETLS enforcement level.
 /* .RE
 /* DIAGNOSTICS
 /*     Panic: undefined flag or zero flags. Fatal: out of memory.
@@ -214,6 +216,18 @@ char   *smtp_key_prefix(VSTRING *buffer, const char *delim_na,
        smtp_key_append_na(buffer, delim_na);
 #endif
 
+    /*
+     * REQUIRETLS enforcement level, if applicable. TODO(tlsproxy) should the
+     * lookup engine also try the requested TLS level and 'stronger', in case
+     * a server hosts multiple domains with different TLS requirements?
+     */
+    if (flags & SMTP_KEY_FLAG_REQTLS_LEVEL)
+#ifdef USE_TLS
+       smtp_key_append_uint(buffer, state->reqtls_level, delim_na);
+#else
+       smtp_key_append_na(buffer, delim_na);
+#endif
+
     /*
      * Require SMTPUTF8 support, if applicable. TODO(wietse) if a delivery
      * request does not need SMTPUTF8, should we also search the connection
index 64128b72855c2097ca4057f8321ca40472b42a73..b6ddac53430292bca006ff3605ebe9dfbc50090e 100644 (file)
@@ -69,6 +69,7 @@
        VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0,
        VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0,
        VAR_SMTP_TLSRPT_SOCKNAME, DEF_SMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0,
+       VAR_SMTP_REQTLS_POLICY, DEF_SMTP_REQTLS_POLICY, &var_smtp_reqtls_policy, 0, 0,
        0,
     };
     static const CONFIG_TIME_TABLE smtp_time_table[] = {
 #ifdef USE_TLS
        VAR_SMTP_TLS_ENF_STS_MX_PAT, DEF_SMTP_TLS_ENF_STS_MX_PAT, &var_smtp_tls_enf_sts_mx_pat,
 #endif
+       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,
     };
index f93d57b5bcde714ab85520cfe484592b5dc5c760..9e42386395d20fb00554c0a0f25a24a32cc17822 100644 (file)
 #include <mail_addr_map.h>
 #include <ext_prop.h>
 #include <namadr_list.h>
-#include <match_parent_style.h>
 #include <lex_822.h>
 #include <dsn_mask.h>
 #include <xtext.h>
@@ -604,6 +603,10 @@ int     smtp_helo(SMTP_STATE *state)
                    /* Ignored later if we already sent STARTTLS. */
                    if ((discard_mask & EHLO_MASK_STARTTLS) == 0)
                        session->features |= SMTP_FEATURE_STARTTLS;
+               } else if (strcasecmp(word, "REQUIRETLS") == 0) {
+                   if ((discard_mask & EHLO_MASK_REQTLS) == 0
+                       && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS))
+                       session->features |= SMTP_FEATURE_REQTLS;
 #endif
 #ifdef USE_SASL_AUTH
                } else if (var_smtp_sasl_enable
@@ -632,18 +635,21 @@ int     smtp_helo(SMTP_STATE *state)
      * 
      * Fix 20140706: moved this before negotiating TLS, AUTH, and so on.
      * 
+     * Fix 20250824: try multiple servers before giving up.
+     * 
      * Fix 20250911: do not cache this session because it does not satisfy the
      * requirement expressed in the cache storage key.
      */
     if ((session->features & SMTP_FEATURE_SMTPUTF8) == 0
-       && DELIVERY_REQUIRES_SMTPUTF8(request)) {
-       DONT_CACHE_THIS_SESSION;
-       return (smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
+       && DELIVERY_REQUIRES_SMTPUTF8(request))
+       return (smtp_misc_fail(state, SMTP_MISC_FAIL_DONT_CACHE
+                              | SMTP_MISC_FAIL_SOFT_NON_FINAL,
+                              DSN_BY_LOCAL_MTA,
                               SMTP_RESP_FAKE(&fake, "5.6.7"),
-                              "SMTPUTF8 is required, "
-                              "but was not offered by host %s",
-                              session->namaddr));
-    }
+                              "message requires SMTPUTF8, but no "
+                              "server was found that supports "
+                              "SMTPUTF8. The last attempted server "
+                              "was %s", session->namaddr));
 
     /*
      * Fix 20140706: don't do silly things when the remote server announces
@@ -657,6 +663,53 @@ int     smtp_helo(SMTP_STATE *state)
        session->features |= SMTP_FEATURE_8BITMIME;
     }
 
+    /*
+     * Require that the server announces REQUIRETLS when the sender requested
+     * REQUIRETLS. Return the message as undeliverable only when there are no
+     * more alternative MX hosts. With opportunistic REQUIRETLS, only log
+     * that the server does not offer REQUIRETLS.
+     */
+#ifdef USE_TLS
+    if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE
+       && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0) {
+       if ((session->features & SMTP_FEATURE_REQTLS) != 0) {
+           if (state->tls_stats)
+               smtp_tls_stat_decide_reqtls(state->tls_stats,
+                                TLS_CERT_IS_MATCHED(session->tls_context) ?
+                                           SMTP_TLS_STAT_NAME_REQTLS :
+                                           SMTP_TLS_STAT_NAME_NOCMATCH,
+                                           POL_STAT_COMPLIANT);
+       } else if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) {
+           if (state->tls_stats)
+               smtp_tls_stat_decide_reqtls(state->tls_stats,
+                                           SMTP_TLS_STAT_NAME_NONE,
+                                           POL_STAT_VIOLATION);
+           return (smtp_misc_fail(state, SMTP_MISC_FAIL_DONT_CACHE
+                                  | SMTP_MISC_FAIL_SOFT_NON_FINAL,
+                                  DSN_BY_LOCAL_MTA,
+                                  SMTP_RESP_FAKE(&fake, "5.7.30"),
+                                  "Sender requested REQUIRETLS, "
+                                  "but no server was found that "
+                                  "supports REQUIRETLS. The last "
+                                  "attempted server was %s",
+                                  session->namaddr));
+       } else {
+           if (state->tls_stats)
+               smtp_tls_stat_decide_reqtls(state->tls_stats,
+                                           SMTP_TLS_STAT_NAME_NONE,
+                                           POL_STAT_COMPLIANT);
+           msg_info("%s: 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
+
     /*
      * We use SMTP command pipelining if the server said it supported it.
      * Since we use blocking I/O, RFC 2197 says that we should inspect the
@@ -785,20 +838,50 @@ 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
             * although support for it was announced in the EHLO response.
+            * 
+            * When the sender requested REQUIRETLS, and the REQUIRETLS policy
+            * requires TLS, return the message as undeliverable only when
+            * there are no more alternative MX hosts.
             */
            session->features &= ~SMTP_FEATURE_STARTTLS;
-           if (TLS_REQUIRED(state->tls->level)) {
-#ifdef USE_TLSRPT
-               if (state->tlsrpt)
-                   trw_report_failure(state->tlsrpt,
-                                      TLSRPT_STARTTLS_NOT_SUPPORTED,
-                                       /* additional_info= */ (char *) 0,
-                                       /* failure_reason= */ (char *) 0);
-#endif
+           if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level)
+               || 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,
+                                                SMTP_TLS_STAT_NAME_NOSTTLS,
+                                                   POL_STAT_VIOLATION);
+               }
+               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,
+                                                      POL_STAT_VIOLATION);
+               }
+               /* 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_DONT_CACHE
+                                          | SMTP_MISC_FAIL_SOFT_NON_FINAL,
+                                          DSN_BY_LOCAL_MTA,
+                                          SMTP_RESP_FAKE(&fake, "5.7.10"),
+                                          "Sender requested REQUIRETLS, "
+                                          "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",
                                       session->namaddr,
@@ -813,8 +896,13 @@ int     smtp_helo(SMTP_STATE *state)
         * 200412 Be sure to provide the default clause at the bottom of this
         * block. When TLS is required we must never, ever, end up in
         * plain-text mode.
+        * 
+        * When the sender requested REQUIRETLS, and the REQUIRETLS policy
+        * requires TLS, return the message as undeliverable only when there
+        * are no more alternative MX hosts.
         */
-       if (TLS_REQUIRED(state->tls->level)) {
+       if (TLS_REQUIRED_BY_SECURITY_LEVEL(state->tls->level)
+           || TLS_REQUIRED_BY_REQTLS_POLICY(state->reqtls_level)) {
            if (!(session->features & SMTP_FEATURE_STARTTLS)) {
 #ifdef USE_TLSRPT
                if (state->tlsrpt)
@@ -823,6 +911,29 @@ 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,
+                                                SMTP_TLS_STAT_NAME_NOSTTLS,
+                                                   POL_STAT_VIOLATION);
+               }
+               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,
+                                                      POL_STAT_VIOLATION);
+               /* 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_DONT_CACHE
+                                          | SMTP_MISC_FAIL_SOFT_NON_FINAL,
+                                          DSN_BY_LOCAL_MTA,
+                                          SMTP_RESP_FAKE(&fake, "5.7.30"),
+                                          "Sender requested REQUIRETLS, "
+                                          "but TLS service was not "
+                                          "offered by host %s",
+                                          session->namaddr));
+               /* TLS_REQUIRED_BY_SECURITY_LEVEL */
                return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                       SMTP_RESP_FAKE(&fake, "4.7.4"),
                          "TLS is required, but was not offered by host %s",
@@ -839,6 +950,15 @@ int     smtp_helo(SMTP_STATE *state)
                                       "TLS is required, but unavailable"));
            }
        }
+       /* Continue in plain-text mode. */
+       if (state->tls_stats) {
+           smtp_tls_stat_decide_sec_level(state->tls_stats, TLS_LEV_NONE,
+                                          POL_STAT_COMPLIANT);
+           if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE)
+               smtp_tls_stat_decide_reqtls(state->tls_stats,
+                                           SMTP_TLS_STAT_NAME_NONE,
+                                           POL_STAT_COMPLIANT);
+       }
     }
 #endif
 #ifdef USE_SASL_AUTH
@@ -1123,8 +1243,9 @@ 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_NOTHROTTLE : SMTP_THROTTLE,
+                              SMTP_MISC_FAIL_NONE : SMTP_MISC_FAIL_THROTTLE,
                               DSN_BY_LOCAL_MTA,
                               SMTP_RESP_FAKE(&fake, "4.7.5"),
                               "Cannot start TLS: handshake failure"));
@@ -1147,8 +1268,10 @@ static int smtp_start_tls(SMTP_STATE *state)
      * fall back to "encrypt", updating the tls_context level accordingly, so
      * we must check that here, and not state->tls->level.
      */
-    if (TLS_MUST_MATCH(session->tls_context->level))
+    if (TLS_MUST_MATCH(session->tls_context->level)) {
        if (!TLS_CERT_IS_MATCHED(session->tls_context)) {
+           int     trusted = TLS_CERT_IS_TRUSTED(session->tls_context);
+
 #ifdef USE_TLSRPT
 
            /*
@@ -1156,27 +1279,54 @@ static int smtp_start_tls(SMTP_STATE *state)
             * already reported a more specific reason.
             */
            if (state->tlsrpt && session->tls_context->rpt_reported == 0) {
-               if (!TLS_CERT_IS_TRUSTED(session->tls_context)) {
-                   (void) trw_report_failure(state->tlsrpt,
-                                             TLSRPT_CERTIFICATE_NOT_TRUSTED,
-                                         /* additional_info= */ (char *) 0,
-                                         /* failure_reason= */ (char *) 0);
-               } else {
-                   (void) trw_report_failure(state->tlsrpt,
-                                          TLSRPT_CERTIFICATE_HOST_MISMATCH,
-                                         /* additional_info= */ (char *) 0,
-                                         /* failure_reason= */ (char *) 0);
-               }
+               (void) trw_report_failure(state->tlsrpt, trusted ?
+                                         TLSRPT_CERTIFICATE_HOST_MISMATCH :
+                                         TLSRPT_CERTIFICATE_NOT_TRUSTED,
+                                          /* additional_info= */ (char *) 0,
+                                          /* 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,
+                                              POL_STAT_VIOLATION);
+
+           /*
+            * When the sender requested REQUIRETLS, and REQUIRETLS is
+            * enforced, return the message as undeliverable only when there
+            * are no more alternative MX hosts.
+            */
+           if (state->reqtls_level == SMTP_REQTLS_POLICY_ACT_ENFORCE) {
+               if (state->tls_stats)
+                   smtp_tls_stat_decide_reqtls(state->tls_stats,
+                                               SMTP_TLS_STAT_NAME_NOCMATCH,
+                                               POL_STAT_VIOLATION);
+               return (smtp_misc_fail(state, SMTP_MISC_FAIL_DONT_CACHE
+                                      | SMTP_MISC_FAIL_SOFT_NON_FINAL,
+                                      DSN_BY_LOCAL_MTA,
+                                      SMTP_RESP_FAKE(&fake, "5.7.10"),
+                                      "Sender requested REQUIRETLS, "
+                                      "but no %s server certificate "
+                                      "was found. The last attempted "
+                                      "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,
+                                               SMTP_TLS_STAT_NAME_NOCMATCH,
+                                               POL_STAT_COMPLIANT);
+           }
            return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
                                   SMTP_RESP_FAKE(&fake, "4.7.5"),
                                   "Server certificate not verified"));
        }
+    }
 
     /*
      * Create a TLSRPT 'success' event only if the TLS engine has not created
-     * TLSRPT event. For example, The TLS engine will create a TLSRPT
+     * TLSRPT event. For example, The TLS engine will create a TLSRPT
      * 'failure' event when the TLS handshake was be successful, but the
      * security level was downgraded from opportunistic "dane" to
      * unauthenticated "encrypt".
@@ -1186,6 +1336,14 @@ static int smtp_start_tls(SMTP_STATE *state)
        (void) trw_report_success(state->tlsrpt);
 #endif
 
+    /*
+     * Report relaxed enforcement if the initial TLS level was degraded.
+     */
+    if (state->tls_stats)
+       smtp_tls_stat_decide_sec_level(state->tls_stats,
+                                      session->tls_context->level,
+                                      POL_STAT_COMPLIANT);
+
     /*
      * At this point we have to re-negotiate the "EHLO" to reget the
      * feature-list.
@@ -1647,7 +1805,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
             * and some as null; historically, pickup(8) does not send any of
             * these, and the queue manager presets absent fields to "not
             * available" except for the rewrite context which is preset to
-            * local by way of migration aid.  These definitions need to be
+            * local by way of migration aid. These definitions need to be
             * centralized for maintainability.
             */
 #ifndef CAN_FORWARD_CLIENT_NAME
@@ -1742,7 +1900,12 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                    vstring_sprintf_append(next_command, " ENVID=");
                    xtext_quote_append(next_command, request->dsn_envid, "+=");
                }
-               if (request->dsn_ret)
+               /* Fix 20250825: limit content exposure in bounce. */
+               if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE
+                   && (session->features & SMTP_FEATURE_REQTLS) == 0)
+                   vstring_sprintf_append(next_command, " RET=%s",
+                                          dsn_ret_str(DSN_RET_HDRS));
+               else if (request->dsn_ret)
                    vstring_sprintf_append(next_command, " RET=%s",
                                           dsn_ret_str(request->dsn_ret));
            }
@@ -1759,7 +1922,23 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
            if ((session->features & SMTP_FEATURE_SMTPUTF8) != 0
                && (request->sendopts & SMTPUTF8_FLAG_REQUESTED) != 0)
                vstring_strcat(next_command, " SMTPUTF8");
-           /* TODO(wietse) REQUIRETLS. */
+
+           /*
+            * Request REQUIRETLS when the remote SMTP server supports
+            * REQUIRETLS and the sender requested REQUIRETLS.
+            */
+#ifdef USE_TLS
+           if (state->reqtls_level > SMTP_REQTLS_POLICY_ACT_DISABLE) {
+               if ((session->features & SMTP_FEATURE_REQTLS) != 0) {
+                   vstring_strcat(next_command, " REQUIRETLS");
+               } else if (state->reqtls_level
+                          == SMTP_REQTLS_POLICY_ACT_ENFORCE) {
+                   msg_panic("Can't happen: must enforce REQUIRETLS, but "
+                             "host %s did not announce REQUIRETLS support",
+                             session->namaddr);
+               }
+           }
+#endif
 
            /*
             * We authenticate the local MTA only, but not the sender.
index 6608ea8804a7af6859729493b3a694fa8fae91df..66b9a41ace161b6244ea6f1b53b4e3e715d435fa 100644 (file)
@@ -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_reqtls_policy.c b/postfix/src/smtp/smtp_reqtls_policy.c
new file mode 100644 (file)
index 0000000..410597d
--- /dev/null
@@ -0,0 +1,260 @@
+/*++
+/* NAME
+/*     smtp_reqtls_policy 3
+/* SUMMARY
+/*     requiretls next-hop policy
+/* SYNOPSIS
+/*     #include <smtp_reqtls_policy.h>
+/*
+/*     SMTP_REQTLS_POLICY *smtp_reqtls_policy_parse(
+/*     const char *origin,
+/*     const char *extern_policy)
+/*
+/*     int     smtp_reqtls_policy_eval(
+/*     SMTP_REQTLS_POLICY *intern_policy,
+/*     const char *nexthop_name)
+/*
+/*     void    smtp_reqtls_policy_free(
+/*     SMTP_REQTLS_POLICY *intern_policy)
+/* DESCRIPTION
+/*     This module determines the REQUIRETLS enforcement level for
+/*     connections to a next-hop destination.
+/*
+/*     smtp_reqtls_policy_parse() converts a policy from human-readable
+/*     form to internal form. It should be called as part of
+/*     before-chroot initialization. A policy is a list of elements that
+/*     will be matched in the specified order. A policy element must be
+/*     an atom ("enforce", "opportunistic+starttls", "opportunistic",
+/*     "disable", "error") or a type:table. A table lookup result must
+/*     be an atom, not a type:table. To match a parent domain name
+/*     with a table that wants and exact match, specify an explicit
+/*     ASCII '.' before the parent domain name. In a policy lookup
+/*     table, an internationalized domain name must be specified in
+/*     A-label form.
+/*
+/*     smtp_reqtls_policy_eval() evaluates an internal-form
+/*     policy for the specified next-hop destination. An
+/*     internationalized next-hop name is converted to A-label form.
+/*     The result is SMTP_REQTLS_POLICY_ACT_ENFORCE (enforce),
+/*     SMTP_REQTLS_POLICY_ACT_OPP_TLS (opportunistic+starttls),
+/*     SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC (opportunistic),
+/*     SMTP_REQTLS_POLICY_ACT_DISABLE, or SMTP_REQTLS_POLICY_ACT_ERROR
+/*     (unknown command or database access error).
+/*
+/*     smtp_reqtls_policy_free() destroys an internal-form policy.
+/*
+/*     Arguments:
+/* .IP origin
+/*     The configuration parameter name for the policy from main.cf.
+/*     This is used for error reporting only.
+/* .IP extern_policy
+/*     External policy representation.
+/* .IP intern_policy
+/*     Internal policy representation.
+/* .IP nexthop_name
+/*     Next-hop destination without [ ], :service,  or :port.
+/* HISTORY
+/*     This as derived from the Postfix server_policy(3) module.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <midna_domain.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict.h>
+#include <valid_hostname.h>
+
+ /*
+  * Global library.
+  */
+#include <mail_params.h>
+
+ /*
+  * Application-specific.
+  */
+#include <smtp_reqtls_policy.h>
+
+struct SMTP_REQTLS_POLICY {
+    char   *origin;                    /* parameter name or lookup table */
+    ARGV   *items;                     /* parsed policy */
+};
+
+/* smtp_reqtls_policy_parse - parse policy */
+
+SMTP_REQTLS_POLICY *smtp_reqtls_policy_parse(const char *origin,
+                                                 const char *extern_policy)
+{
+    char   *saved_policy = mystrdup(extern_policy);
+    SMTP_REQTLS_POLICY *intern_policy;
+    char   *bp = saved_policy;
+    char   *item;
+
+    intern_policy = (SMTP_REQTLS_POLICY *) mymalloc(sizeof(*intern_policy));
+    intern_policy->origin = mystrdup(origin);
+    intern_policy->items = argv_alloc(1);
+
+    while ((item = mystrtokq(&bp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
+       if (strchr(item, ':') != 0) {
+           item = dict_open(item, O_RDONLY, DICT_FLAG_LOCK
+                            | DICT_FLAG_FOLD_FIX
+                            | DICT_FLAG_UTF8_REQUEST)->reg_name;
+       }
+       argv_add(intern_policy->items, item, (char *) 0);
+    }
+    argv_terminate(intern_policy->items);
+
+    /*
+     * Cleanup.
+     */
+    myfree(saved_policy);
+    return (intern_policy);
+}
+
+/* smtp_reqtls_policy_eval - evaluate policy */
+
+int     smtp_reqtls_policy_eval(SMTP_REQTLS_POLICY *intern_policy,
+                                       const char *nexthop_name)
+{
+    char  **cpp;
+    DICT   *dict;
+    char   *item;
+    const char *dict_val;
+    int     ret;
+    const char *name;
+    const char *next;
+    const char *origin;
+    const char *aname;
+
+#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
+
+    origin = intern_policy->origin;
+    for (cpp = intern_policy->items->argv; (item = *cpp) != 0; cpp++) {
+       if (msg_verbose)
+           msg_info("origin=%s name=%s item=%s", origin, nexthop_name, item);
+       if (STREQ(item, SMTP_REQTLS_POLICY_NAME_OPP_TLS)) {
+           return (SMTP_REQTLS_POLICY_ACT_OPP_TLS);
+       } else if (STREQ(item, SMTP_REQTLS_POLICY_NAME_ENFORCE)) {
+           return (SMTP_REQTLS_POLICY_ACT_ENFORCE);
+       } else if (STREQ(item, SMTP_REQTLS_POLICY_NAME_OPPORTUNISTIC)) {
+           return (SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC);
+       } else if (STREQ(item, SMTP_REQTLS_POLICY_NAME_DISABLE)) {
+           return (SMTP_REQTLS_POLICY_ACT_DISABLE);
+       } else if (strchr(item, ':') != 0) {
+
+           /*
+            * To avoid ambiguity (insecurity!) with unnormalized U-label
+            * forms and unnormalized label separators, the policy contains
+            * A-label forms, and the evaluator converts queries from U-label
+            * form to A-label form. Determine the conversion result once, so
+            * that it can be reused when a policy contains more than one
+            * lookup table. The alternative requires additional logic that
+            * normalizes domain names before updating or matching a policy.
+            * For consistency across Postfix, such logic would also be
+            * needed for all other configuration and policy mechanisms.
+            */
+#ifndef NO_EAI
+           if (!valid_hostaddr(nexthop_name, DONT_GRIPE)
+               && !allascii(nexthop_name)) {
+               if ((aname = midna_domain_to_ascii(nexthop_name)) == 0) {
+                   msg_warn("%s: malformed next-hop destination: '%s' -- "
+                            "using default policy '%s'",
+                            VAR_SMTP_REQTLS_POLICY, nexthop_name,
+                            SMTP_REQTLS_POLICY_NAME_DEFAULT);
+                   return (SMTP_REQTLS_POLICY_ACT_DEFAULT);
+               }
+           } else
+#endif
+               aname = nexthop_name;
+           if ((dict = dict_handle(item)) == 0)
+               msg_panic("%s: unexpected dictionary: %s", __func__, item);
+           for (name = aname; name != 0 && name[0] != 0; name = next) {
+               if ((dict_val = dict_get(dict, name)) != 0) {
+                   /* Disallow nested table. */
+                   if (dict_val[strcspn(dict_val, ":")]) {
+                       msg_warn("table %s: nested lookup result \"%s\" "
+                          "is not allowed -- ignoring remainder of policy",
+                                item, dict_val);
+                       return (SMTP_REQTLS_POLICY_ACT_ERROR);
+                   }
+                   /* Disallow composite lookup result. */
+                   else if (dict_val[strcspn(dict_val, CHARS_COMMA_SP)]) {
+                       msg_warn("table %s: composite lookup result \"%s\" "
+                          "is not allowed -- ignoring remainder of policy",
+                                item, dict_val);
+                       return (SMTP_REQTLS_POLICY_ACT_ERROR);
+                   }
+                   /* Simple atom. Avoid six malloc() and free() calls. */
+                   else {
+                       SMTP_REQTLS_POLICY fake_policy;
+
+                       fake_policy.origin = item;
+                       ARGV_FAKE_BEGIN(fake_argv, dict_val);
+                       fake_policy.items = &fake_argv;
+                       ret = smtp_reqtls_policy_eval(&fake_policy, name);
+                       ARGV_FAKE_END;
+                       return (ret);
+                   }
+               } else if (dict->error != 0) {
+                   msg_warn("%s: %s:%s: table lookup error -- ignoring "
+                            "the remainder of this policy", origin,
+                            dict->type, dict->name);
+                   return (SMTP_REQTLS_POLICY_ACT_ERROR);
+               }
+               /* Look up .parent if the table wants an exact match. */
+               if ((dict->flags & DICT_FLAG_FIXED) == 0
+                   || valid_hostaddr(name, DONT_GRIPE))
+                   break;
+               next = strchr(name + 1, '.');
+           }
+       } else if (STREQ(item, SMTP_REQTLS_POLICY_NAME_ERROR)) {
+           return (SMTP_REQTLS_POLICY_ACT_ERROR);
+       } else {
+           msg_warn("%s: unknown policy action: '%s' -- ignoring the "
+                    "remainder of this policy", origin, item);
+           return (SMTP_REQTLS_POLICY_ACT_ERROR);
+       }
+    }
+    if (msg_verbose)
+       msg_info("origin=%s name=%s - no match", origin, nexthop_name);
+    return (SMTP_REQTLS_POLICY_ACT_ENFORCE);
+}
+
+/* smtp_reqtls_policy_free - release storage for policy and lookup tables */
+
+void    smtp_reqtls_policy_free(SMTP_REQTLS_POLICY *intern_policy)
+{
+    char  **cpp;
+    DICT   *dict;
+    const char *item;
+
+    myfree(intern_policy->origin);
+    for (cpp = intern_policy->items->argv; (item = *cpp) != 0; cpp++) {
+       if (strchr(item, ':') != 0) {
+           if ((dict = dict_handle(item)) == 0)
+               msg_panic("%s: unexpected dictionary: %s", __func__, item);
+           dict_unregister(item);
+       }
+    }
+    argv_free(intern_policy->items);
+    myfree((void *) intern_policy);
+}
diff --git a/postfix/src/smtp/smtp_reqtls_policy.h b/postfix/src/smtp/smtp_reqtls_policy.h
new file mode 100644 (file)
index 0000000..a7e1a19
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _SMTP_REQTLS_POLICY_INCLUDED_
+#define _SMTP_REQTLS_POLICY_INCLUDED_
+
+/*++
+/* NAME
+/*     smtp_reqtls_policy 3h
+/* SUMMARY
+/*     requiretls per-mx policy
+/* SYNOPSIS
+/*     #include <smtp_reqtls_policy.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * External interface.
+  */
+typedef struct SMTP_REQTLS_POLICY SMTP_REQTLS_POLICY;
+
+extern SMTP_REQTLS_POLICY *smtp_reqtls_policy_parse(const char *, const char *);
+extern int smtp_reqtls_policy_eval(SMTP_REQTLS_POLICY *, const char *);
+extern void smtp_reqtls_policy_free(SMTP_REQTLS_POLICY *);
+
+#define SMTP_REQTLS_POLICY_NAME_ENFORCE                "enforce"
+#define SMTP_REQTLS_POLICY_NAME_OPP_TLS                "opportunistic+starttls"
+#define SMTP_REQTLS_POLICY_NAME_OPPORTUNISTIC  "opportunistic"
+#define SMTP_REQTLS_POLICY_NAME_DISABLE                "disable"
+#define SMTP_REQTLS_POLICY_NAME_ERROR          "error"
+
+#define SMTP_REQTLS_POLICY_NAME_DEFAULT                SMTP_REQTLS_POLICY_NAME_ENFORCE
+
+#define SMTP_REQTLS_POLICY_ACT_ENFORCE         3
+#define SMTP_REQTLS_POLICY_ACT_OPP_TLS         2
+#define SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC   1
+#define SMTP_REQTLS_POLICY_ACT_DISABLE         0
+#define SMTP_REQTLS_POLICY_ACT_ERROR           (-1)
+
+#define SMTP_REQTLS_POLICY_ACT_DEFAULT         SMTP_REQTLS_POLICY_ACT_ENFORCE
+
+#define STATE_REQTLS_IS_REQUESTED(var, state) \
+       SENDOPTS_REQTLS_IS_REQUESTED((var), (state)->request->sendopts)
+
+#define SENDOPTS_REQTLS_IS_REQUESTED(var, sendopts) \
+       ((var) && (sendopts) & SOPT_REQUIRETLS_ESMTP)
+
+#define TLS_REQUIRED_BY_REQTLS_POLICY(reqtls_level) \
+       ((reqtls_level) >= SMTP_REQTLS_POLICY_ACT_OPP_TLS)
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+#endif
diff --git a/postfix/src/smtp/smtp_reqtls_policy_test.c b/postfix/src/smtp/smtp_reqtls_policy_test.c
new file mode 100644 (file)
index 0000000..2e6d88b
--- /dev/null
@@ -0,0 +1,351 @@
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdlib.h>
+
+ /*
+  * Utility library.
+  */
+#include <msg.h>
+#include <msg_vstream.h>
+#include <stringops.h>
+
+ /*
+  * Application-specific.
+  */
+#include <smtp_reqtls_policy.h>
+
+ /*
+  * Tests and test cases.
+  */
+struct QUERY_REPLY {
+    const char *query;
+    int     reply;
+};
+
+typedef struct TEST_CASE {
+    const char *label;
+    int     (*action) (const struct TEST_CASE *);
+} TEST_CASE;
+
+static int non_regexp_policy_no_error(const TEST_CASE *tp)
+{
+    const char *ext_policy = "inline:{"
+    "{.example = disable},"
+    "{.foo.example = enforce},"
+    "{bar.foo.example = opportunistic+starttls}},"
+    "opportunistic";
+    const struct QUERY_REPLY qr[] = {
+       {"foo.example", SMTP_REQTLS_POLICY_ACT_DISABLE},
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {"bar.foo.example", SMTP_REQTLS_POLICY_ACT_OPP_TLS},
+       {"other", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int non_regexp_policy_error_at_end(const TEST_CASE *tp)
+{
+    const char *ext_policy = "inline:{"
+    "{.foo.example = enforce},"
+    "{bar.foo.example = opportunistic+starttls}},"
+    "nonsense";
+    const struct QUERY_REPLY qr[] = {
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {"bar.foo.example", SMTP_REQTLS_POLICY_ACT_OPP_TLS},
+       {"other", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int non_regexp_policy_error_at_start(const TEST_CASE *tp)
+{
+    const char *ext_policy = "nonsense,inline:{"
+    "{.foo.example = enforce},"
+    "{bar.foo.example = debug}},"
+    "nonsense";
+    const struct QUERY_REPLY qr[] = {
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {"bar.foo.example", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {"other", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int policy_table_lookup_error(const TEST_CASE *tp)
+{
+    const char *ext_policy = "fail:whatever enforce";
+    const struct QUERY_REPLY qr[] = {
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {"other", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int regexp_table_no_error(const TEST_CASE *tp)
+{
+    const char *ext_policy = "regexp:{{/^foo\\.example$/ enforce}}, opportunistic";
+    const struct QUERY_REPLY qr[] = {
+       {"foo.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+       {"bar.example", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+       {"other", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int default_is_enforce(const TEST_CASE *tp)
+{
+    const char *ext_policy = "regexp:{{/^foo\\.example$/ opportunistic+starttls}}";
+    const struct QUERY_REPLY qr[] = {
+       {"foo.example", SMTP_REQTLS_POLICY_ACT_OPP_TLS},
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {"bar.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {"other", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int disallows_nested_table(const TEST_CASE *tp)
+{
+    const char *ext_policy = "inline:{{foo.example = hash:/no/where}}";
+    const struct QUERY_REPLY qr[] = {
+       {"foo.example", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int disallows_composite_lookup(const TEST_CASE *tp)
+{
+    const char *ext_policy = "inline:{{foo.example = foo bar}}";
+    const struct QUERY_REPLY qr[] = {
+       {"foo.example", SMTP_REQTLS_POLICY_ACT_ERROR},
+       {"x.foo.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {"example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+       {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+       got = smtp_reqtls_policy_eval(int_policy, qp->query);
+       if (got != qp->reply) {
+           msg_warn("got result '%d', want: '%d'", got, qp->reply);
+           errors++;
+       }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int converts_good_u_label_query(const TEST_CASE *tp)
+{
+    const char *ext_policy = "inline:{{foo.xn--1xa.example = opportunistic}} enforce";
+    const struct QUERY_REPLY qr[] = {
+        {"foo.xn--1xa.example", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+        {"foo.Ï€.example", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+        {"x.foo.xn--1xa.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+        {"x.foo.Ï€.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+        {"example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+        {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+        got = smtp_reqtls_policy_eval(int_policy, qp->query);
+        if (got != qp->reply) {
+            msg_warn("got result '%d', want: '%d'", got, qp->reply);
+            errors++;
+        }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+static int fails_bad_u_label_query(const TEST_CASE *tp)
+{
+    const char *ext_policy = "inline:{{foo.xn--1xa.example = opportunistic}} disable";
+    const struct QUERY_REPLY qr[] = {
+        {"foo.Ï€.example", SMTP_REQTLS_POLICY_ACT_OPPORTUNISTIC},
+        {"foo.-Ï€.example", SMTP_REQTLS_POLICY_ACT_ENFORCE},
+        {"example", SMTP_REQTLS_POLICY_ACT_DISABLE},
+        {0},
+    };
+    SMTP_REQTLS_POLICY *int_policy;
+    const struct QUERY_REPLY *qp;
+    const char test_origin[] = "test";
+    int     got;
+    int     errors = 0;
+
+    int_policy = smtp_reqtls_policy_parse(test_origin, ext_policy);
+    for (qp = qr; qp->query; qp++) {
+        got = smtp_reqtls_policy_eval(int_policy, qp->query);
+        if (got != qp->reply) {
+            msg_warn("got result '%d', want: '%d'", got, qp->reply);
+            errors++;
+        }
+    }
+    smtp_reqtls_policy_free(int_policy);
+    return (errors == 0);
+}
+
+
+static TEST_CASE test_cases[] = {
+    "non_regexp_policy_no_error", non_regexp_policy_no_error,
+    "non_regexp_policy_error_at_end", non_regexp_policy_error_at_end,
+    "non_regexp_policy_error_at_start", non_regexp_policy_error_at_start,
+    "policy_table_lookup_error", policy_table_lookup_error,
+    "regexp_table_no_error", regexp_table_no_error,
+    "default_is_enforce", default_is_enforce,
+    "disallows_nested_table", disallows_nested_table,
+    "disallows_composite_lookup", disallows_composite_lookup,
+    "converts_good_u_label_query", converts_good_u_label_query,
+    "fails_bad_u_label_query", fails_bad_u_label_query,
+    0,
+};
+
+int     main(int argc, char **argv)
+{
+    const TEST_CASE *tp;
+    int     pass = 0;
+    int     fail = 0;
+
+    msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
+    util_utf8_enable = 1;
+
+    for (tp = test_cases; tp->label != 0; tp++) {
+       msg_info("RUN  %s", tp->label);
+       if (tp->action(tp) == 0) {
+           fail++;
+           msg_info("FAIL %s", tp->label);
+       } else {
+           msg_info("PASS %s", tp->label);
+           pass++;
+       }
+    }
+    msg_info("PASS=%d FAIL=%d", pass, fail);
+    exit(fail != 0);
+}
index ec8cc2df39bddcf5206f317f68904b61101cf4e7..bb1e8f6a98be9076979481326a40fcae6887e975 100644 (file)
@@ -82,6 +82,13 @@ SMTP_STATE *smtp_state_alloc(void)
     state->iterator->saved_dest = vstring_alloc(100);
 #ifdef USE_TLSRPT
     state->tlsrpt = 0;
+#endif
+#ifdef USE_TLS
+    state->reqtls_level = SMTP_REQTLS_POLICY_ACT_DISABLE;
+    if (var_log_tls_feature_status)
+       state->tls_stats = pol_stats_create();
+    else
+       state->tls_stats = 0;
 #endif
     if (var_smtp_cache_conn) {
        state->dest_label = vstring_alloc(10);
@@ -109,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)
+       pol_stats_free(state->tls_stats);
 #endif
     vstring_free(state->iterator->request_nexthop);
     vstring_free(state->iterator->dest);
index dd9ac7b4e2846622c3c495c88f36afff3cca22cd..59284f94832205b46c0da8512d6e52d4bc0f9b84 100644 (file)
@@ -85,6 +85,7 @@ bool    var_smtp_use_tls;
 char   *var_smtp_tls_excl_ciph;
 bool    var_smtp_tls_enf_sts_mx_pat;
 bool    var_smtp_tls_wrappermode;
+bool    var_log_tls_feature_status;
 
  /*
   * Other globals.
@@ -128,6 +129,7 @@ static void test_setup(void)
     var_smtp_tls_enf_sts_mx_pat = 1;
     var_smtp_tls_wrappermode = 0;
     var_tls_required_enable = 0;
+    var_log_tls_feature_status = 1;
 
     smtp_mode = 1;
 
@@ -479,7 +481,9 @@ static int test_tls_reqd_no_sans_header(const struct TEST_CASE *tp)
     var_smtp_tls_policy = "static:none";
 
     /* Test-dependent. */
-    state->request = &(DELIVER_REQUEST) {.sendopts = 0};
+    state->request = &(DELIVER_REQUEST) {
+       .sendopts = 0
+    };
     var_smtp_tls_wrappermode = 1;
     var_tls_required_enable = 1;
     want_level = TLS_LEV_NONE;
@@ -515,7 +519,9 @@ static int test_tls_reqd_no_with_wrappermode(const struct TEST_CASE *tp)
     var_smtp_tls_policy = "static:none";
 
     /* Test-dependent. */
-    state->request = &(DELIVER_REQUEST) {.sendopts = SOPT_REQUIRETLS_HEADER};
+    state->request = &(DELIVER_REQUEST) {
+       .sendopts = SOPT_REQUIRETLS_HEADER
+    };
     var_smtp_tls_wrappermode = 1;
     var_tls_required_enable = 1;
     want_level = TLS_LEV_ENCRYPT;
@@ -551,7 +557,9 @@ static int test_tls_reqd_no_sans_wrappermode(const struct TEST_CASE *tp)
     var_smtp_tls_policy = "static:none";
 
     /* Test-dependent. */
-    state->request = &(DELIVER_REQUEST) {.sendopts = SOPT_REQUIRETLS_HEADER};
+    state->request = &(DELIVER_REQUEST) {
+       .sendopts = SOPT_REQUIRETLS_HEADER
+    };
     var_smtp_tls_wrappermode = 0;
     var_tls_required_enable = 1;
     want_level = TLS_LEV_MAY;
index 60880df6a657a2dd18bac23c2ed14ca8e5733d3c..8d0f07c01f990282621d17e9f86f724402daee5c 100644 (file)
@@ -33,9 +33,9 @@
 /*     int     exception;
 /*     const char *description;
 /* AUXILIARY FUNCTIONS
-/*     int     smtp_misc_fail(state, throttle, mta_name, resp, format, ...)
+/*     int     smtp_misc_fail(state, flags, mta_name, resp, format, ...)
 /*     SMTP_STATE *state;
-/*     int     throttle;
+/*     int     flags;
 /*     const char *mta_name;
 /*     SMTP_RESP *resp;
 /*     const char *format;
 /*
 /*     smtp_misc_fail() provides a more detailed interface than
 /*     smtp_site_fail() and smtp_mesg_fail(), which are convenience
-/*     wrappers around smtp_misc_fail(). The throttle argument
-/*     is either SMTP_THROTTLE or SMTP_NOTHROTTLE; it is used only
-/*     in the "soft error, final server" policy, and determines
-/*     whether a destination will be marked as problematic.
+/*     wrappers around smtp_misc_fail(). See the flags argument below.
 /*
 /*     smtp_rcpt_fail() handles the case where a recipient is not
 /*     accepted by the server for reasons other than that the server
 /*     Arguments:
 /* .IP state
 /*     SMTP client state per delivery request.
+/* .IP flags
+/*     Either SMTP_MISC_FAIL_NONE or the bitwise OR of
+/* .RS
+/* .IP SMTP_MISC_FAIL_THROTTLE
+/*     Mark the destination as problematic.
+/* .IP SMTP_MISC_FAIL_SOFT_NON_FINAL
+/*     If the server was not the last one to try, treat a hard error
+/*     as a soft error.
+/* .IP SMTP_MISC_FAIL_DONT_CACHE
+/*     Do not save the connection to the cache. This flag is ignored
+/*     when SMTP_MISC_FAIL_THROTTLE is in effect.
 /* .IP resp
 /*     Server response including reply code and text.
 /* .IP recipient
@@ -210,7 +218,7 @@ static void smtp_check_code(SMTP_SESSION *session, int code)
 
 /* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */
 
-static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
+static int smtp_bulk_fail(SMTP_STATE *state, int flags)
 {
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
@@ -220,8 +228,22 @@ static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
     int     aggregate_status;
     int     soft_error = (STR(why->status)[0] == '4');
     int     soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce);
+    int     throttle_queue = (flags & SMTP_MISC_FAIL_THROTTLE);
+    int     dont_cache = (flags & SMTP_MISC_FAIL_DONT_CACHE);
     int     nrcpt;
 
+    /*
+     * Sanity check.
+     */
+    if ((flags & SMTP_MISC_FAIL_SOFT_NON_FINAL) != 0) {
+       if (soft_error) {
+           msg_warn("smtp_bulk_fail: ignoring SMTP_MISC_FAIL_SOFT_NON_FINAL "
+                    "for a soft error");
+       } else {
+           soft_error = (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0;
+       }
+    }
+
     /*
      * Don't defer the recipients just yet when this error qualifies them for
      * delivery to a backup server. Just log something informative to show
@@ -270,7 +292,8 @@ static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
            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);
@@ -288,6 +311,8 @@ static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue)
      */
     if (throttle_queue && session)
        DONT_CACHE_THROTTLED_SESSION;
+    else if (dont_cache && session)
+       DONT_CACHE_THIS_SESSION;
 
     return (-1);
 }
@@ -302,7 +327,7 @@ int     smtp_sess_fail(SMTP_STATE *state)
      * because this error information is collected by a routine that
      * terminates BEFORE the error is reported.
      */
-    return (smtp_bulk_fail(state, SMTP_THROTTLE));
+    return (smtp_bulk_fail(state, SMTP_MISC_FAIL_THROTTLE));
 }
 
 /* vsmtp_fill_dsn - fill in temporary DSN structure */
@@ -342,7 +367,7 @@ static void vsmtp_fill_dsn(SMTP_STATE *state, const char *mta_name,
 
 /* smtp_misc_fail - maybe throttle queue; skip/defer/bounce all recipients */
 
-int     smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name,
+int     smtp_misc_fail(SMTP_STATE *state, int flags, const char *mta_name,
                               SMTP_RESP *resp, const char *format,...)
 {
     va_list ap;
@@ -360,7 +385,7 @@ int     smtp_misc_fail(SMTP_STATE *state, int throttle, const char *mta_name,
     /*
      * Skip, defer or bounce recipients, and throttle this queue.
      */
-    return (smtp_bulk_fail(state, throttle));
+    return (smtp_bulk_fail(state, flags));
 }
 
 /* smtp_rcpt_fail - skip, defer, or bounce recipient */
@@ -418,7 +443,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);
@@ -472,5 +498,5 @@ int     smtp_stream_except(SMTP_STATE *state, int code, const char *description)
      * falling back to plaintext, because RETRY_AS_PLAINTEXT clears the
      * FINAL_SERVER flag.
      */
-    return (smtp_bulk_fail(state, SMTP_THROTTLE));
+    return (smtp_bulk_fail(state, SMTP_MISC_FAIL_THROTTLE));
 }
index 5bd1a78f6c29404478fcd0b4abac6530a1e03cb4..9ca64dc5ead783da27767e27d43195fe67f7ecf1 100644 (file)
@@ -548,8 +548,10 @@ smtpd_proxy.o: ../../include/check_arg.h
 smtpd_proxy.o: ../../include/cleanup_user.h
 smtpd_proxy.o: ../../include/connect.h
 smtpd_proxy.o: ../../include/dns.h
+smtpd_proxy.o: ../../include/header_opts.h
 smtpd_proxy.o: ../../include/htable.h
 smtpd_proxy.o: ../../include/iostuff.h
+smtpd_proxy.o: ../../include/is_header.h
 smtpd_proxy.o: ../../include/mail_error.h
 smtpd_proxy.o: ../../include/mail_params.h
 smtpd_proxy.o: ../../include/mail_proto.h
index 66a032eee184fa50cb7d0d9a05cdc070127f888f..4199819c1db3d42d15d464497aa3ab12d0acad26 100644 (file)
@@ -57,6 +57,7 @@
 /*     RFC 6531 (Internationalized SMTP)
 /*     RFC 6533 (Internationalized Delivery Status Notifications)
 /*     RFC 7505 ("Null MX" No Service Resource Record)
+/*     RFC 8689 (SMTP REQUIRETLS extension)
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8)
 /*     or \fBpostlogd\fR(8).
 /* .IP "\fBsmtpd_sasl_mechanism_filter (!external, static:rest)\fR"
 /*     If non-empty, a filter for the SASL mechanism names that the
 /*     Postfix SMTP server will announce in the EHLO response.
-/* STARTTLS SUPPORT CONTROLS
+/* TLS SUPPORT CONTROLS
 /* .ad
 /* .fi
 /*     Detailed information about STARTTLS configuration may be
 /*     Request that remote SMTP clients send an RFC7250 raw public key
 /*     instead of an X.509 certificate, when asking for or requiring client
 /*     authentication.
-/* OBSOLETE STARTTLS CONTROLS
+/* .PP
+/*     Available in Postfix version 3.11 and later:
+/* .IP "\fBrequiretls_enable (yes)\fR"
+/*     Enable support for the ESMTP verb "REQUIRETLS" in the "MAIL
+/*     FROM" command.
+/* .IP "\fBrequiretls_esmtp_header (yes)\fR"
+/*     Record the ESMTP REQUIRETLS request in a "Require-TLS-ESMTP:
+/*     yes" message header.
+/* OBSOLETE TLS CONTROLS
 /* .ad
 /* .fi
 /*     The following configuration parameters exist for compatibility
@@ -1571,6 +1580,7 @@ int     var_smtpd_forbid_bare_lf_code;
 static int bare_lf_mask;
 static NAMADR_LIST *bare_lf_excl;
 bool    var_smtpd_hide_client_session;
+bool    var_reqtls_esmtp_hdr;
 
  /*
   * Silly little macros.
@@ -2112,6 +2122,11 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        EHLO_APPEND(state, "SMTPUTF8");
     if ((discard_mask & EHLO_MASK_CHUNKING) == 0)
        EHLO_APPEND(state, "CHUNKING");
+#ifdef USE_TLS
+    if (var_reqtls_enable && (discard_mask & EHLO_MASK_REQTLS) == 0
+       && state->tls_context != 0)
+       EHLO_APPEND(state, "REQUIRETLS");
+#endif
 
     /*
      * Send the reply.
@@ -2188,16 +2203,25 @@ static int mail_open_stream(SMTPD_STATE *state)
     /*
      * Connect to the before-queue filter when one is configured. The MAIL
      * FROM and RCPT TO commands are forwarded as received (including DSN
-     * attributes), with the exception that the before-filter smtpd process
-     * handles all authentication, encryption, access control and relay
-     * control, and that the before-filter smtpd process does not forward
-     * blocked commands. If the after-filter smtp server does not support
-     * some of Postfix's ESMTP features, then they must be turned off in the
-     * before-filter smtpd process with the smtpd_discard_ehlo_keywords
-     * feature.
+     * attributes), with the following exceptions:
+     * 
+     * - No forwarding of the REQUIRETLS VERB in MAIL FROM.
+     * 
+     * - The before-filter smtpd process handles all authentication, encryption,
+     * access control and relay control.
+     * 
+     * - The before-filter smtpd process does not forward blocked commands.
+     * 
+     * If the after-filter smtp server does not support some of Postfix's ESMTP
+     * features, then they must be turned off in the before-filter smtpd
+     * process with the smtpd_discard_ehlo_keywords feature.
      */
     if (state->proxy_mail) {
-       if (smtpd_proxy_create(state, smtpd_proxy_opts, var_smtpd_proxy_filt,
+       int     message_proxy_opts = smtpd_proxy_opts;
+
+       if ((state->flags & SMTPD_FLAG_REQTLS) && var_reqtls_esmtp_hdr)
+           message_proxy_opts |= SMTPD_PROXY_FLAG_REQTLS_HDR;
+       if (smtpd_proxy_create(state, message_proxy_opts, var_smtpd_proxy_filt,
                               var_smtpd_proxy_tmout, var_smtpd_proxy_ehlo,
                               state->proxy_mail) != 0) {
            smtpd_chat_reply(state, "%s", STR(state->proxy->reply));
@@ -2226,7 +2250,8 @@ static int mail_open_stream(SMTPD_STATE *state)
            cleanup_flags |= CLEANUP_FLAG_SMTPUTF8;
        else
            cleanup_flags |= smtputf8_autodetect(MAIL_SRC_MASK_SMTPD);
-       /* TODO(wietse) REQUIRETLS. */
+       if (state->flags & SMTPD_FLAG_REQTLS)
+           cleanup_flags |= CLEANUP_FLAG_REQTLS;
        state->dest = mail_stream_service(MAIL_CLASS_PUBLIC,
                                          var_cleanup_service);
        if (state->dest == 0
@@ -2579,7 +2604,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     int     rate;
     int     dsn_envid = 0;
 
-    state->flags &= ~SMTPD_FLAG_SMTPUTF8;
+    state->flags &= ~SMTPD_FLAGS_PER_MESSAGE;
     state->encoding = 0;
     state->dsn_ret = 0;
 
@@ -2686,6 +2711,13 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
                   && (state->ehlo_discard_mask & EHLO_MASK_SMTPUTF8) == 0
                   && strcasecmp(arg, "SMTPUTF8") == 0) {       /* RFC 6531 */
             /* Already processed early. */ ;
+#ifdef USE_TLS
+       } else if (var_reqtls_enable
+                  && state->tls_context != 0
+                  && (state->ehlo_discard_mask & EHLO_MASK_REQTLS) == 0
+                  && strcasecmp(arg, "REQUIRETLS") == 0) {     /* RFC 8689 */
+           state->flags |= SMTPD_FLAG_REQTLS;
+#endif
 #ifdef USE_SASL_AUTH
        } else if (strncasecmp(arg, "AUTH=", 5) == 0) {
            if ((err = smtpd_sasl_mail_opt(state, arg + 5)) != 0) {
@@ -2862,8 +2894,19 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        state->verp_delims = mystrdup(verp_delims);
     if (dsn_envid)
        state->dsn_envid = mystrdup(STR(state->dsn_buf));
-    if (USE_SMTPD_PROXY(state))
+    if (USE_SMTPD_PROXY(state)) {
+       if (state->flags & SMTPD_FLAG_REQTLS) {
+           vstring_sprintf(state->buffer, "%s %s%s", argv[0].strval,
+                           argv[1].strval, argv[2].strval);
+           for (narg = 3; narg < argc; narg++) {
+               arg = argv[narg].strval;
+               if (strcasecmp(arg, "REQUIRETLS") == 0)
+                   continue;
+               vstring_sprintf_append(state->buffer, " %s", arg);
+           }
+       }
        state->proxy_mail = mystrdup(STR(state->buffer));
+    }
     if (var_smtpd_delay_open == 0 && mail_open_stream(state) < 0) {
        /* XXX Reset access map side effects. */
        mail_reset(state);
@@ -6670,7 +6713,7 @@ static void post_jail_init(char *unused_name, char **unused_argv)
      * recipient checks, address mapping, header_body_checks?.
      */
     smtpd_input_transp_mask =
-    input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
+       input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
 
     /*
      * Initialize before-queue filter options: do we want speed-matching
@@ -6820,6 +6863,7 @@ int     main(int argc, char **argv)
        VAR_RELAY_BEFORE_RCPT_CHECKS, DEF_RELAY_BEFORE_RCPT_CHECKS, &var_relay_before_rcpt_checks,
        VAR_SMTPD_REQ_DEADLINE, DEF_SMTPD_REQ_DEADLINE, &var_smtpd_req_deadline,
        VAR_SMTPD_HIDE_CLIENT_SESSION, DEF_SMTPD_HIDE_CLIENT_SESSION, &var_smtpd_hide_client_session,
+       VAR_REQTLS_ESMTP_HDR, DEF_REQTLS_ESMTP_HDR, &var_reqtls_esmtp_hdr,
        0,
     };
     static const CONFIG_STR_TABLE str_table[] = {
index c049194e2c31d99647b071a98107e7dae602664d..eb84155526a8a1da9c8f776a9e8394a403582311 100644 (file)
@@ -209,9 +209,13 @@ typedef struct {
 #define SMTPD_FLAG_AUTH_USED      (1<<2)       /* don't reuse SASL state */
 #define SMTPD_FLAG_SMTPUTF8       (1<<3)       /* RFC 6531/2 transaction */
 #define SMTPD_FLAG_NEED_MILTER_ABORT (1<<4)    /* undo milter_mail_event() */
+#define SMTPD_FLAG_REQTLS         (1<<5)       /* RFC 8689 */
 
 #define SMTPD_NOTE_BARE_LF        (1<<0)       /* saw at least one bare LF */
 
+/* Flags that apply to only one MAIL transaction. */
+#define SMTPD_FLAGS_PER_MESSAGE        (SMTPD_FLAG_SMTPUTF8 | SMTPD_FLAG_REQTLS)
+
  /* Security: don't reset SMTPD_FLAG_AUTH_USED. */
 #define SMTPD_MASK_MAIL_KEEP \
            ~(SMTPD_FLAG_SMTPUTF8)      /* Fix 20140706 */
index 1c1f9fc497de317b23a413c08ea3ed699a175e6a..b7f63e1971f794074ddc941804ecc7d830dd92b5 100644 (file)
 /*
 /*     Arguments:
 /* .IP flags
-/*     Zero, or SMTPD_PROXY_FLAG_SPEED_ADJUST to buffer up the entire
-/*     message before contacting a before-queue content filter.
+/*     Zero or more of the following:
+/* .RS
+/* .IP SMTPD_PROXY_FLAG_SPEED_ADJUST
+/*     Buffer up the entire message before contacting a before-queue
+/*     content filter.
 /*     Note: when this feature is requested, the before-queue
 /*     filter MUST use the same 2xx, 4xx or 5xx reply code for all
 /*     recipients of a multi-recipient message.
+/* .IP SMTPD_PROXY_FLAG_REQTLS_HDR
+/*     Add a "Require-TLS-ESMTP: yes" header if one is not already
+/*     present.
+/* .RE
 /* .IP server
 /*     The SMTP proxy server host:port. The host or host: part is optional.
 /*     This argument is not duplicated.
 #include <xtext.h>
 #include <record.h>
 #include <mail_queue.h>
+#include <is_header.h>
+#include <header_opts.h>
 
 /* Application-specific. */
 
@@ -849,6 +858,31 @@ static int smtpd_proxy_save_rec_put(VSTREAM *stream, int rec_type,
     return (rec_type);
 }
 
+/* smtpd_proxy_handle_reqtls - propagate or add REQUIRETLS header */
+
+static void smtpd_proxy_handle_reqtls(SMTPD_PROXY *proxy, VSTREAM *stream,
+                                             const char *data, ssize_t len)
+{
+    const HEADER_OPTS *hdr_opts;
+    const char *cp;
+
+    if (is_header_buf(data, len)) {
+       if ((hdr_opts = header_opts_find(data)) != 0
+           && hdr_opts->type == HDR_REQTLS_ESMTP) {
+           cp = data + strlen(hdr_opts->name) + 1;
+           while (cp < data + len && ISSPACE(*cp))
+               cp++;
+           if (data + len == cp + 3 && strncasecmp(cp, "YES", 3) == 0)
+               proxy->reqtls_esmtp_hdr_seen += 1;
+       }
+    } else if (len == 0 || !ISSPACE(data[0])) {
+       if (proxy->reqtls_esmtp_hdr_seen == 0)
+           smtp_fputs("Require-TLS-ESMTP: yes",
+                      sizeof("Require-TLS-ESMTP: yes") - 1, stream);
+       proxy->flags &= ~SMTPD_PROXY_FLAG_REQTLS_HDR;
+    }
+}
+
 /* smtpd_proxy_rec_put - send message content, rec_put() clone */
 
 static int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
@@ -869,9 +903,13 @@ static int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
     /*
      * Send one content record. Errors and results must be as with rec_put().
      */
-    if (rec_type == REC_TYPE_NORM)
+    if (rec_type == REC_TYPE_NORM) {
+       SMTPD_PROXY *proxy = VSTREAM_TO_SMTPD_STATE(stream)->proxy;
+
+       if (proxy->flags & SMTPD_PROXY_FLAG_REQTLS_HDR)
+           smtpd_proxy_handle_reqtls(proxy, stream, data, len);
        smtp_fputs(data, len, stream);
-    else if (rec_type == REC_TYPE_CONT)
+    else if (rec_type == REC_TYPE_CONT)
        smtp_fwrite(data, len, stream);
     else
        msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
@@ -1014,10 +1052,11 @@ int     smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service,
      * When an operation has many arguments it is safer to use named
      * parameters, and have the compiler enforce the argument count.
      */
-#define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) \
+#define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
+       a12, a13) \
        ((p) = (SMTPD_PROXY *) mymalloc(sizeof(*(p))), (p)->a1, (p)->a2, \
         (p)->a3, (p)->a4, (p)->a5, (p)->a6, (p)->a7, (p)->a8, (p)->a9, \
-        (p)->a10, (p)->a11, (p)->a12, (p))
+        (p)->a10, (p)->a11, (p)->a12, (p)->a13, (p))
 
     /*
      * Sanity check.
@@ -1037,7 +1076,8 @@ int     smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service,
                              rec_put = smtpd_proxy_rec_put,
                              flags = flags, service_stream = 0,
                              service_name = service, timeout = timeout,
-                             ehlo_name = ehlo_name, mail_from = mail_from);
+                             ehlo_name = ehlo_name, mail_from = mail_from,
+                             reqtls_esmtp_hdr_seen = 0);
        if (smtpd_proxy_connect(state) < 0) {
            /* NOT: smtpd_proxy_free(state); we still need proxy->reply. */
            return (-1);
@@ -1067,7 +1107,8 @@ int     smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service,
                              rec_put = smtpd_proxy_save_rec_put,
                              flags = flags, service_stream = 0,
                              service_name = service, timeout = timeout,
-                             ehlo_name = ehlo_name, mail_from = mail_from);
+                             ehlo_name = ehlo_name, mail_from = mail_from,
+                             reqtls_esmtp_hdr_seen = 0);
        return (0);
 #endif
     }
index 3d35d07c4a7382496fa951db379d97bec405794e..d0a6c134aedf47ffff090ce8572330826047a012 100644 (file)
@@ -37,9 +37,11 @@ typedef struct SMTPD_PROXY {
     int     timeout;
     const char *ehlo_name;
     const char *mail_from;
+    int     reqtls_esmtp_hdr_seen;
 } SMTPD_PROXY;
 
 #define SMTPD_PROXY_FLAG_SPEED_ADJUST  (1<<0)
+#define SMTPD_PROXY_FLAG_REQTLS_HDR    (1<<1)
 
 #define SMTPD_PROXY_NAME_SPEED_ADJUST  "speed_adjust"
 
@@ -63,4 +65,7 @@ extern int smtpd_proxy_parse_opts(const char *, const char *);
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
index c54f39656051c49153455e706c45424fe8099287..8162bdc2977fdfdbe1897b21d4791938d3079b31 100644 (file)
@@ -50,7 +50,8 @@
 #define TLS_LEV_VERIFY         7       /* certificate verified */
 #define TLS_LEV_SECURE         8       /* "secure" verification */
 
-#define TLS_REQUIRED(l)                ((l) > TLS_LEV_MAY)
+#define TLS_REQUIRED_BY_SECURITY_LEVEL(l) \
+                               ((l) > TLS_LEV_MAY)
 #define TLS_MUST_MATCH(l)      ((l) > TLS_LEV_ENCRYPT)
 #define TLS_MUST_PKIX(l)       ((l) >= TLS_LEV_VERIFY)
 #define TLS_OPPORTUNISTIC(l)   ((l) == TLS_LEV_MAY || (l) == TLS_LEV_DANE)
index 33b5a21d693e4bfcd0298d9dc228e2b233155fd8..ffeb87fc7af4595589d042f95af0e2c1984d6147 100644 (file)
@@ -1373,7 +1373,7 @@ int     main(int argc, char *argv[])
            peername = argv[7];
        msg_info("Verified %s", peername);
     } else {
-       int r = SSL_get_verify_result(tctx->con);
+       int     r = SSL_get_verify_result(tctx->con);
 
        msg_info("certificate verification failed for %s:%s: num=%d:%s",
                 argv[6], argv[7], r, X509_verify_cert_error_string(r));
index b021648113db47abc359fb34b1cc06660d113eb6..e296d7972a81a36de063c2251e973c0d46ef47a7 100644 (file)
@@ -47,7 +47,8 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
        inet_addr_sizes.c quote_for_json.c mystrerror.c \
        sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.c \
-       clean_ascii_cntrl_space.c normalize_v4mapped_addr.c ossl_digest.c
+       clean_ascii_cntrl_space.c normalize_v4mapped_addr.c ossl_digest.c \
+       mac_midna.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -96,7 +97,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
        quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
        normalize_ws.o valid_uri_scheme.o clean_ascii_cntrl_space.o \
-       normalize_v4mapped_addr.o ossl_digest.o
+       normalize_v4mapped_addr.o ossl_digest.o mac_midna.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -125,12 +126,13 @@ HDRS      = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
        vstring_vstream.h watchdog.h format_tv.h load_file.h killme_after.h \
        edit_file.h dict_cache.h dict_thash.h ip_match.h nbbio.h base32_code.h \
        dict_fail.h warn_stat.h dict_sockmap.h line_number.h timecmp.h \
-       slmdb.h compat_va_copy.h dict_pipe.h dict_random.h \
+       slmdb.h compat_va_copy.h dict_pipe.h dict_random.h parse_utf8_char.h \
        valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \
        check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \
        known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \
        inet_prefix_top.h inet_addr_sizes.h valid_uri_scheme.h \
-       clean_ascii_cntrl_space.h normalize_v4mapped_addr.h ossl_digest.h
+       clean_ascii_cntrl_space.h normalize_v4mapped_addr.h ossl_digest.h \
+       mac_midna.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 DEFS   = -I. -D$(SYSTYPE)
@@ -227,7 +229,7 @@ plugin_map_obj_update: $(LIB_MAP_OBJ)
        done
 
 clean:
-       rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp
+       rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp testdb.db
 
 tidy:  clean
 
@@ -2299,6 +2301,17 @@ mac_expand.o: stringops.h
 mac_expand.o: sys_defs.h
 mac_expand.o: vbuf.h
 mac_expand.o: vstring.h
+mac_midna.o: check_arg.h
+mac_midna.o: mac_expand.h
+mac_midna.o: mac_midna.c
+mac_midna.o: mac_midna.h
+mac_midna.o: mac_parse.h
+mac_midna.o: midna_domain.h
+mac_midna.o: msg.h
+mac_midna.o: stringops.h
+mac_midna.o: sys_defs.h
+mac_midna.o: vbuf.h
+mac_midna.o: vstring.h
 mac_parse.o: check_arg.h
 mac_parse.o: mac_parse.c
 mac_parse.o: mac_parse.h
index dea766ab5d237e66dc0636c3940fbb4a609cd3a0..f0616c956b8c83bc46733492f05807142a6624a9 100644 (file)
 /*
 /*     int     allalnum(string)
 /*     const char *string;
+/*
+/*     int     allalnumus(string)
+/*     const char *string;
 /* DESCRIPTION
 /*     alldig() determines if its argument is an all-numerical string.
 /*
 /*     allalnum() determines if its argument is an all-alphanumerical
 /*     string.
+/*
+/*     allalnumus() determines if its argument is an all-(alphanumerical
+/*     or underscore) string.
 /* SEE ALSO
 /*     An alldig() routine appears in Brian W. Kernighan, P.J. Plauger:
 /*     "Software Tools", Addison-Wesley 1976.
@@ -33,6 +39,9 @@
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
@@ -71,3 +80,18 @@ int     allalnum(const char *string)
            return (0);
     return (1);
 }
+
+/* allalnumus - return true if string is all (alphanum or underscore) */
+
+int     allalnumus(const char *string)
+{
+    const char *cp;
+    int     ch;
+
+    if (*string == 0)
+       return (0);
+    for (cp = string; (ch = *cp) != 0; cp++)
+       if (!ISALNUM(ch) && ch != '_')
+           return (0);
+    return (1);
+}
index 555a35ef9e3ae5bc17e0d03d9cf0c8c793bf397a..a91b33bfb9371b375676e56bf088300d639468b3 100644 (file)
 /*     void    ARGV_FAKE_BEGIN(argv, arg)
 /*     const char *arg;
 /*
+/*     void    ARGV_FAKE2_BEGIN(argv, arg1, arg2)
+/*     const char *arg1;
+/*     const char *arg2;
+/*
 /*     void    ARGV_FAKE_END
 /* DESCRIPTION
 /*     The functions in this module manipulate arrays of string
 /*     implementation allocates no heap memory and creates no copy
 /*     of the second argument.  ARGV_FAKE_END closes the statement
 /*     block and thereby releases storage.
+/*
+/*     ARGV_FAKE2_BEGIN/END provide the same functionality for a pair
+/*     of strings.
 /* SEE ALSO
 /*     msg(3) diagnostics interface
 /* DIAGNOSTICS
index 1c479069644e827e227925a5962addca10307095..7331d398056f0787c59c7a4a3db77ee31a605279 100644 (file)
@@ -58,6 +58,15 @@ extern ARGV *argv_split_at_append(ARGV *, const char *, int);
        fake_argv.argv = __fake_argv_args__; \
        fake_argv.argc = fake_argv.len = 1;
 
+#define ARGV_FAKE2_BEGIN(fake_argv, arg1, arg2) { \
+       ARGV fake_argv; \
+       char *__fake_argv_args__[3]; \
+       __fake_argv_args__[0] = (char *) (arg1); \
+       __fake_argv_args__[1] = (char *) (arg2); \
+       __fake_argv_args__[2] = 0; \
+       fake_argv.argv = __fake_argv_args__; \
+       fake_argv.argc = fake_argv.len = 2;
+
 #define ARGV_FAKE_END  }
 
 #define ARGV_END       ((char *) 0)
old mode 100755 (executable)
new mode 100644 (file)
index e579b1760c148db5dcf8ca5b12f9250cd3ae0f8d..d5f55634366fe5d28786510105731cc094724af1 100644 (file)
@@ -167,9 +167,8 @@ static void inet_addr_list_print(INET_ADDR_LIST *list)
 int     main(int argc, char **argv)
 {
     INET_ADDR_LIST list;
-    INET_PROTO_INFO *proto_info;
 
-    proto_info = inet_proto_init(argv[0], INET_PROTO_NAME_ALL);
+    inet_proto_init(argv[0], INET_PROTO_NAME_ALL);
     inet_addr_list_init(&list);
     while (--argc && *++argv)
        if (inet_addr_host(&list, *argv) == 0)
index f35d5f01dbc1a13d407b82cf46833e767e7de24d..0b6fc35604c6c353bbaa810a369fa031c9ac5e74 100644 (file)
@@ -106,6 +106,7 @@ char   *inet_prefix_top(int af, const void *src, int prefix_len)
 #include <stdlib.h>
 #include <msg_vstream.h>
 #include <name_code.h>
+#include <mymalloc.h>
 
  /*
   * TODO: add test cases for fatal and panic errors, intercept msg_fatal()
index f40819eb5d133214af965b85c331e869edd40e61..c3afed605e0f24cbb6a8e9d83e57b3b108b54738 100644 (file)
 /*     MAC_EXPAND_RELOP_FN relop_eval)
 /*
 /*     MAC_EXP_OP_RES mac_exp_op_res_bool[2];
+/
+/*     typedef int (*MAC_EXPAND_NAMED_FN) (
+/*     VSTRING *out,
+/*     const char *arg)
+/*
+/*     void    mac_expand_add_named_fn(
+/*     const char *name,
+/*     MAC_EXPAND_NAMED_FN action)
 /* DESCRIPTION
 /*     This module implements parameter-less named attribute
 /*     expansions, both conditional and unconditional. As of Postfix
 /*     named attribute expansion and relational expression evaluation.
 /*     Otherwise, the result is empty.  Whitespace before or after
 /*     {text} is ignored.
+/* .IP "${name{text}}"
+/*     Apply the registered function \fIname\fR to the specified text
+/*     after named attribute expansion and expression evaluation,
+/*     and replace this input with the function result. Functions are
+/*     registered with mac_expand_add_named_fn().
 /* .IP "${name:text}, ${name:{text}}"
 /*     Conditional attribute-based substitution. If the attribute
 /*     value is empty or undefined, the expansion is the given
 /*     mac_exp_op_res_bool provides an array that converts a boolean
 /*     value (0 or 1) to the corresponding MAX_EXP_OP_RES_TRUE or
 /*     MAX_EXP_OP_RES_FALSE value.
+/*
+/*     mac_expand_add_named_fn() registers a C function that may be
+/*     called as ${name{text}}. The function input is the text after
+/*     attribute expansion and expression evaluation. The function
+/*     should append its output to the specified buffer, and it
+/*     should return either MAC_PARSE_OK or MAC_PARSE_ERROR.
 /* DIAGNOSTICS
 /*     Fatal errors: out of memory.  Warnings: syntax errors, unreasonable
 /*     recursion depth.
 /*
-/*     The result value is the binary OR of zero or more of the following:
+/*     The mac_expand() result value is the binary OR of zero or more
+/*     of the following:
 /* .IP MAC_PARSE_ERROR
 /*     A syntax error was found in \fBpattern\fR, or some attribute had
 /*     an unreasonable nesting depth.
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 /* System library. */
@@ -263,6 +286,14 @@ static const NAME_CODE mac_exp_op_table[] =
 static HTABLE *mac_exp_ext_table;
 static VSTRING *mac_exp_ext_key;
 
+ /*
+  * Support for named functions.
+  */
+static HTABLE *mac_exp_named_fn_table;
+struct mac_exp_named_fn_entry {
+    MAC_EXPAND_NAMED_FN action;
+};
+
  /*
   * SLMs.
   */
@@ -360,18 +391,27 @@ static int PRINTFLIKE(2, 3) mac_exp_parse_error(MAC_EXP_CONTEXT *mc,
 
 /* mac_exp_extract_curly_payload - balance {}, skip whitespace, return payload */
 
-static char *mac_exp_extract_curly_payload(MAC_EXP_CONTEXT *mc, char **bp)
+static char *mac_exp_extract_curly_payload(MAC_EXP_CONTEXT *mc, char **bp,
+                                                  int strip_space)
 {
     char   *payload;
     char   *cp;
     int     level;
     int     ch;
 
+#define DO_STRIP_SPACE 1
+#define NO_STRIP_SPACE 0
+
     /*
      * Extract the payload and balance the {}. The caller is expected to skip
      * leading whitespace before the {. See MAC_EXP_FIND_LEFT_CURLY().
+     * TODO(wietse) this code pre-dates extpar() and mainly differs in how an
+     * error is reported.
      */
-    for (level = 1, cp = *bp, payload = ++cp; /* see below */ ; cp++) {
+    payload = *bp + 1;
+    if (strip_space)
+       payload += strspn(payload, MAC_EXP_WHITESPACE);
+    for (level = 1, cp = payload; /* see below */ ; cp++) {
        if ((ch = *cp) == 0) {
            mac_exp_parse_error(mc, "unbalanced {} in attribute expression: "
                                "\"%s\"",
@@ -384,6 +424,8 @@ static char *mac_exp_extract_curly_payload(MAC_EXP_CONTEXT *mc, char **bp)
                break;
        }
     }
+    if (strip_space)
+       trimblanks(payload, cp - payload)[0] = 0;
     *cp++ = 0;
 
     /*
@@ -417,7 +459,8 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
      * Left operand. The caller is expected to skip leading whitespace before
      * the {. See MAC_EXP_FIND_LEFT_CURLY().
      */
-    if ((left_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+    if ((left_op_strval = mac_exp_extract_curly_payload(mc, &cp,
+                                                     NO_STRIP_SPACE)) == 0)
        return (mc->status);
 
     /*
@@ -458,7 +501,8 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
        MAC_EXP_ERR_RETURN(mc, "\"{expression}\" expected at: "
                           "\"...{%s} %.*s>>>%.20s\"",
                           left_op_strval, (int) op_len, op_pos, cp);
-    if ((rite_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+    if ((rite_op_strval = mac_exp_extract_curly_payload(mc, &cp,
+                                                     NO_STRIP_SPACE)) == 0)
        return (mc->status);
 
     /*
@@ -535,6 +579,70 @@ void    mac_expand_add_relop(int *tok_list, const char *suffix,
     }
 }
 
+/* mac_expand_add_named_fn - register named-function callback */
+
+void    mac_expand_add_named_fn(const char *name, MAC_EXPAND_NAMED_FN action)
+{
+    struct mac_exp_named_fn_entry *xp;
+
+    /*
+     * Sanity checks.
+     */
+    if (!allalnumus(name))
+       msg_panic("%s: bad function name: \"%s\"", __func__, name);
+
+    /*
+     * One-time initialization.
+     */
+    if (mac_exp_named_fn_table == 0)
+       mac_exp_named_fn_table = htable_create(10);
+
+    /*
+     * The C language spec allows sizeof(void *) < sizeof(function pointer).
+     */
+    if (htable_locate(mac_exp_named_fn_table, name) != 0)
+       msg_panic("%s: duplicate key: %s", __func__, name);
+    xp = (struct mac_exp_named_fn_entry *) mymalloc(sizeof(*xp));
+    xp->action = action;
+    (void) htable_enter(mac_exp_named_fn_table, name, (void *) xp);
+}
+
+/* mac_exp_parse_function - interpolate result from caller-defined function */
+
+static int mac_exp_parse_function(char *name_start, char *name_end,
+                                         char *cp, MAC_EXP_CONTEXT *mc)
+{
+    char   *fn_arg;
+    struct mac_exp_named_fn_entry *xp;
+    VSTRING *buf = 0;
+
+    /* cp is positioned at the '{', zero or more spaces after name_end. */
+    if ((fn_arg = mac_exp_extract_curly_payload(mc, &cp, DO_STRIP_SPACE)) == 0)
+       return (mc->status);
+    if (*cp != 0)                              /* garbage */
+       MAC_EXP_ERR_RETURN(mc, "unexpected input at: "
+                          "\"...%s}>>>%.20s\"", fn_arg, cp);
+    *name_end = 0;
+    /* Look up the function and evaluate. */
+    xp = (struct mac_exp_named_fn_entry *)
+       htable_find(mac_exp_named_fn_table, name_start);
+    if (xp == 0)
+       MAC_EXP_ERR_RETURN(mc, "unknown function \"%s\" at"
+                       "\"...>>>%s{%s}\"", name_start, name_start, fn_arg);
+    if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0)
+       buf = vstring_alloc(100);
+    mc->status |=
+       mac_expand(buf, fn_arg, mc->flags, mc->filter, mc->lookup,
+                  mc->context);
+    if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0) {
+       if ((mc->status & MAC_PARSE_ERROR) == 0
+           && xp->action(mc->result, vstring_str(buf)) == MAC_PARSE_ERROR)
+           mc->status |= MAC_PARSE_ERROR;
+       vstring_free(buf);
+    }
+    return (mc->status);
+}
+
 /* mac_expand_callback - callback for mac_parse */
 
 static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
@@ -560,8 +668,9 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
        return (mc->status);
 
     /*
-     * Named parameter or relational expression. In case of a syntax error,
-     * return without doing damage, and issue a warning instead.
+     * Named parameter, function call, or relational expression. In case of a
+     * syntax error, return without doing damage, and issue a warning
+     * instead.
      */
     if (type == MAC_PARSE_EXPR) {
 
@@ -587,14 +696,16 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
        }
 
        /*
-        * Named parameter.
+        * Named parameter or function call.
         */
        else {
            char   *start;
 
            /*
-            * Look for the ? or : operator. In case of a syntax error,
-            * return without doing damage, and issue a warning instead.
+            * Collect the name of the parameter or function. Look for the ?
+            * or : operator, or the '{' to indicate a function call. In case
+            * of a syntax error, return without doing damage, and issue a
+            * warning instead.
             */
            start = (cp += strspn(cp, MAC_EXP_WHITESPACE));
            for ( /* void */ ; /* void */ ; cp++) {
@@ -609,6 +720,8 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
                    lookup_mode = MAC_EXP_MODE_TEST;
                    break;
                }
+               if (ch == '{')
+                   return (mac_exp_parse_function(start, cp, cp + tmp_len, mc));
                ch = *cp;
                if (!ISALNUM(ch) && ch != '_') {
                    MAC_EXP_ERR_RETURN(mc, "attribute name syntax error at: "
@@ -638,7 +751,8 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
        switch (ch) {
        case '?':
            if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
-               if ((res_iftrue = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+               if ((res_iftrue = mac_exp_extract_curly_payload(mc, &cp,
+                                                     NO_STRIP_SPACE)) == 0)
                    return (mc->status);
            } else {
                res_iftrue = cp;
@@ -656,7 +770,8 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
            /* FALLTHROUGH: do not remove, see comment above. */
        case ':':
            if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
-               if ((res_iffalse = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+               if ((res_iffalse = mac_exp_extract_curly_payload(mc, &cp,
+                                                     NO_STRIP_SPACE)) == 0)
                    return (mc->status);
            } else {
                res_iffalse = cp;
@@ -776,6 +891,12 @@ static MAC_EXP_OP_RES length_relop_eval(const char *left, int relop,
     }
 }
 
+static int named_fn_eval(VSTRING *out, const char *in)
+{
+    vstring_sprintf_append(out, "-=oO%sOo=-", in);
+    return (0);
+}
+
 int     main(int unused_argc, char **argv)
 {
     VSTRING *buf = vstring_alloc(100);
@@ -797,6 +918,11 @@ int     main(int unused_argc, char **argv)
      */
     mac_expand_add_relop(length_relops, "length", length_relop_eval);
 
+    /*
+     * Add a silly named function that decorates its argument.
+     */
+    mac_expand_add_named_fn("test_named_fn", named_fn_eval);
+
     /*
      * Loop over the inputs.
      */
index 714b39dc0ee69b36d427699af820dc57ef6f28fd..70954e602d22e06c6f4afcb2ec78afec480ef761 100644 (file)
@@ -58,9 +58,11 @@ extern MAC_EXP_OP_RES mac_exp_op_res_bool[2];
 
 typedef const char *(*MAC_EXP_LOOKUP_FN) (const char *, int, void *);
 typedef MAC_EXP_OP_RES(*MAC_EXPAND_RELOP_FN) (const char *, int, const char *);
+typedef int (*MAC_EXPAND_NAMED_FN) (VSTRING *, const char *);
 
 extern int mac_expand(VSTRING *, const char *, int, const char *, MAC_EXP_LOOKUP_FN, void *);
-void    mac_expand_add_relop(int *, const char *, MAC_EXPAND_RELOP_FN);
+extern void mac_expand_add_relop(int *, const char *, MAC_EXPAND_RELOP_FN);
+extern void mac_expand_add_named_fn(const char *, MAC_EXPAND_NAMED_FN);
 
 /* LICENSE
 /* .ad
@@ -76,6 +78,9 @@ void    mac_expand_add_relop(int *, const char *, MAC_EXPAND_RELOP_FN);
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 #endif
index 1d6190654106ae8a1e3777f56fb3b2217bdfeddd..ade0ab4627893974a8bcf2c3be40b54093e71aa6 100644 (file)
@@ -103,3 +103,9 @@ ${{aaa} < {bb}}
 ${{aaa} <length {bb}}
 ${{aaa} <= {bb}}
 ${{aaa} <=length {bb}}
+
+name1 = whatever
+
+${ test_named_fn { ABCBA } }
+${test_named_fn{AB%BA}}
+$bogus{AB%BA}
index 854c4f91f3561a7b8b35ad8eff9a54bf48f26ee1..a72c850dddb00b1ad1d17308231f5f289279f124 100644 (file)
@@ -30,7 +30,7 @@ stat=1 result=
 unknown: warning: "==" or "!="" or "<"" or "<="" or ">="" or ">" expected at: "...$name1}>>>? {name 1 defined, |"
 stat=1 result=
 << ${x{$name1} != {}?{name 1 defined, |$name1|$name2|}}
-unknown: warning: attribute name syntax error at: "...x>>>{$name1} != {}?{name"
+unknown: warning: unexpected input at: "...$name1}>>>!= {}?{name 1 define"
 stat=1 result=
 << ${{$name1}x?{name 1 defined, |$name1|$name2|}}
 unknown: warning: "==" or "!="" or "<"" or "<="" or ">="" or ">" expected at: "...$name1}>>>x?{name 1 defined, |"
@@ -220,3 +220,13 @@ stat=0 result=
 stat=0 result=true
 << ${{aaa} <=length {bb}}
 stat=0 result=
+<< 
+
+<< name1 = whatever
+<< 
+<< ${ test_named_fn { ABCBA } }
+stat=0 result=-=oOABCBAOo=-
+<< ${test_named_fn{AB%BA}}
+stat=0 result=-=oOAB%BAOo=-
+<< $bogus{AB%BA}
+stat=2 result={AB%BA}
diff --git a/postfix/src/util/mac_midna.c b/postfix/src/util/mac_midna.c
new file mode 100644 (file)
index 0000000..ed54118
--- /dev/null
@@ -0,0 +1,88 @@
+/*++
+/* NAME
+/*     mac_midna 3h
+/* SUMMARY
+/*     IDNA-based mac_expand() plugin
+/* SYNOPSIS
+/*     #include <mac_midna.h>
+/*
+/*     void    mac_midna_register(void)
+/* DESCRIPTION
+/*     mac_midna_register() registers the domain_to_ascii{} and
+/*     domain_to_utf8{} caller-defined functions for mac_expand().
+/* SEE ALSO
+/*     mac_midna_domain(3)
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+
+ /*
+  * Utility library.
+  */
+#include <mac_expand.h>
+#include <mac_midna.h>
+#include <mac_parse.h>
+#include <midna_domain.h>
+#include <msg.h>
+#include <stringops.h>
+#include <vstring.h>
+
+#define NAME_TO_A_LABEL        "domain_to_ascii"
+#define NAME_TO_U_LABEL        "domain_to_utf8"
+
+static int mac_midna_domain_to_ascii_eval(VSTRING *out, const char *name)
+{
+    const char *aname;
+
+#ifndef NO_EAI
+    if (!allascii(name)) {
+       if ((aname = midna_domain_to_ascii(name)) == 0) {
+           msg_warn("bad domain argument in: '%s{%s}'", NAME_TO_A_LABEL, name);
+           return (MAC_PARSE_ERROR);
+       }
+       if (msg_verbose)
+           msg_info("%s: %s asciified to %s", __func__, name, aname);
+    } else
+#endif
+       aname = name;
+    if (out)
+       vstring_strcat(out, aname);
+    return (MAC_PARSE_OK);
+}
+
+static int mac_midna_to_u_label_eval(VSTRING *out, const char *name)
+{
+    const char *uname;
+
+#ifndef NO_EAI
+    if (allascii(name)) {
+       if ((uname = midna_domain_to_utf8(name)) == 0) {
+           msg_warn("bad domain argument in: '%s{%s}'", NAME_TO_U_LABEL, name);
+           return (MAC_PARSE_ERROR);
+       }
+       if (msg_verbose)
+           msg_info("%s: %s internationalzied to %s", __func__, name, uname);
+    } else
+#endif
+       uname = name;
+    if (out)
+       vstring_strcat(out, uname);
+    return (MAC_PARSE_OK);
+}
+
+/* mac_midna_register - register caller-defined function */
+void    mac_midna_register(void)
+{
+    mac_expand_add_named_fn(NAME_TO_A_LABEL, mac_midna_domain_to_ascii_eval);
+    mac_expand_add_named_fn(NAME_TO_U_LABEL, mac_midna_to_u_label_eval);
+}
diff --git a/postfix/src/util/mac_midna.h b/postfix/src/util/mac_midna.h
new file mode 100644 (file)
index 0000000..6220fe2
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _MAC_MIDHA_H_INCLUDED_
+#define _MAC_MIDHA_H_INCLUDED_
+
+/*++
+/* NAME
+/*     mac_midna 3h
+/* SUMMARY
+/*     IDNA-based mac_expand() plugin
+/* SYNOPSIS
+/*     #include <mac_midna.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+extern void mac_midna_register(void);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     porcupine.org
+/*--*/
+
+#endif
index ddd4af3008ad9e23ee6b9a0039d385623f09c179..5aaa85986814275a852819186307f07cf0b350f1 100644 (file)
 /*     int     mask;
 /*     int     flags;
 /*
+/*     const char *str_name_mask_delim_opt(
+/*     VSTRING *buf,
+/*     const char *context,
+/*     const NAME_MASK *table,
+/*     int     mask,
+/*     const char *delim,
+/*     int     flags)
+/*
 /*     const char *str_long_name_mask_opt(buf, context, table, mask, flags)
 /*     VSTRING *buf;
 /*     const char *context;
 /* .IP mask
 /*     A bit mask.
 /* .IP delim
-/*     Delimiter characters to use instead of whitespace and commas.
+/*     When converting from string to mask, the set of delimiter
+/*     characters to use instead of whitespace and commas. When
+/*     converting from mask to string, the delimiter string to use
+/*     instead what may be specified with flags.
 /* .IP flags
 /*     Bit-wise OR of one or more of the following.  Where features
 /*     would have conflicting results (e.g., FATAL versus IGNORE),
@@ -260,18 +271,17 @@ int     name_mask_delim_opt(const char *context, const NAME_MASK *table,
     return (result);
 }
 
-/* str_name_mask_opt - mask to string */
+/* str_name_mask_delim_opt - mask to string */
 
-const char *str_name_mask_opt(VSTRING *buf, const char *context,
-                                     const NAME_MASK *table,
-                                     int mask, int flags)
+const char *str_name_mask_delim_opt(VSTRING *buf, const char *context,
+                                           const NAME_MASK *table,
+                                           int mask, const char *delim,
+                                           int flags)
 {
-    const char *myname = "name_mask";
+    const char *myname = "str_name_mask";
     const NAME_MASK *np;
     ssize_t len;
     static VSTRING *my_buf = 0;
-    int     delim = (flags & NAME_MASK_COMMA ? ',' :
-                    (flags & NAME_MASK_PIPE ? '|' : ' '));
 
     if ((flags & STR_NAME_MASK_REQUIRED) == 0)
        msg_panic("%s: missing NAME_MASK_NUMBER/FATAL/RETURN/WARN/IGNORE flag",
@@ -287,7 +297,7 @@ const char *str_name_mask_opt(VSTRING *buf, const char *context,
     for (np = table; mask != 0; np++) {
        if (np->name == 0) {
            if (flags & NAME_MASK_NUMBER) {
-               vstring_sprintf_append(buf, "0x%x%c", mask, delim);
+               vstring_sprintf_append(buf, "0x%x%s", mask, delim);
            } else if (flags & NAME_MASK_FATAL) {
                msg_fatal("%s: unknown %s bit in mask: 0x%x",
                          myname, context, mask);
@@ -303,16 +313,28 @@ const char *str_name_mask_opt(VSTRING *buf, const char *context,
        }
        if (mask & np->mask) {
            mask &= ~np->mask;
-           vstring_sprintf_append(buf, "%s%c", np->name, delim);
+           vstring_sprintf_append(buf, "%s%s", np->name, delim);
        }
     }
     if ((len = VSTRING_LEN(buf)) > 0)
-       vstring_truncate(buf, len - 1);
+       vstring_truncate(buf, len - strlen(delim));
     VSTRING_TERMINATE(buf);
 
     return (STR(buf));
 }
 
+/* str_name_mask_opt - mask to string */
+
+const char *str_name_mask_opt(VSTRING *buf, const char *context,
+                                     const NAME_MASK *table,
+                                     int mask, int flags)
+{
+    const char *delim = (flags & NAME_MASK_COMMA ? "," :
+                        (flags & NAME_MASK_PIPE ? "|" : " "));
+
+    return (str_name_mask_delim_opt(buf, context, table, mask, delim, flags));
+}
+
 /* long_name_mask_delim_opt - compute mask corresponding to list of names */
 
 long    long_name_mask_delim_opt(const char *context,
index 05e45ec38ba93fb7830a19b7d3931a56ee7ca468..1e7f393ff5e6c1b855455128eb137b1f6706bf84 100644 (file)
@@ -49,9 +49,10 @@ typedef struct {
 #define name_mask(tag, table, str) \
        name_mask_opt((tag), (table), (str), NAME_MASK_DEFAULT)
 #define str_name_mask(tag, table, mask) \
-       str_name_mask_opt(((VSTRING *) 0), (tag), (table), (mask), NAME_MASK_DEFAULT)
+       str_name_mask_delim_opt(((VSTRING *) 0), (tag), (table), (mask), ", ", NAME_MASK_DEFAULT)
 
 extern int name_mask_delim_opt(const char *, const NAME_MASK *, const char *, const char *, int);
+extern const char *str_name_mask_delim_opt(VSTRING *, const char *, const NAME_MASK *, int, const char *, int);
 extern const char *str_name_mask_opt(VSTRING *, const char *, const NAME_MASK *, int, int);
 
  /*
index 68c1c3091682707191028c01bbcb4f331faaa33c..2d2fa56749a4aac0950371514b379243f52029f0 100644 (file)
@@ -6,9 +6,9 @@ unknown: warning: unknown name value "four" in "four"
 four -> 0x0 -> 
 unknown: warning: unknown name value "four" in "zero one two three four"
 zero one two three four -> 0xf -> zero one two three
-unknown: warning: name_mask: unknown mask bit in mask: 0xf0
+unknown: warning: str_name_mask: unknown mask bit in mask: 0xf0
 0xff -> 0xff -> (null)
-unknown: warning: name_mask: unknown mask bit in mask: 0xfffffff0
+unknown: warning: str_name_mask: unknown mask bit in mask: 0xfffffff0
 0xffffffff -> 0xffffffff -> (null)
 unknown: warning: unknown name value "0xffffffffffffffff" in "0xffffffffffffffff"
 0xffffffffffffffff -> 0x0 -> 
index c86a532d36220b5973dc45b533db0c19a7212f75..759ac4284f0fa8af89cc55c31b1d25be896b69b8 100644 (file)
@@ -6,9 +6,9 @@ unknown: warning: unknown name value "four" in "four"
 four -> 0x0 -> 
 unknown: warning: unknown name value "four" in "zero one two three four"
 zero one two three four -> 0xf -> zero one two three
-unknown: warning: name_mask: unknown mask bit in mask: 0xf0
+unknown: warning: str_name_mask: unknown mask bit in mask: 0xf0
 0xff -> 0xff -> zero one two three
-unknown: warning: name_mask: unknown mask bit in mask: 0xfffffff0
+unknown: warning: str_name_mask: unknown mask bit in mask: 0xfffffff0
 0xffffffff -> 0xffffffff -> zero one two three
 unknown: warning: unknown name value "0xffffffffffffffff" in "0xffffffffffffffff"
 0xffffffffffffffff -> 0x0 -> 
index cc498479a23b0e848bf88bdfe50f8ee1d54ebfe3..6b493151f3a5ed5800025b3b76c8e6e33f73cfe8 100644 (file)
@@ -54,6 +54,7 @@ extern VSTRING *unescape(VSTRING *, const char *);
 extern VSTRING *escape(VSTRING *, const char *, ssize_t);
 extern int alldig(const char *);
 extern int allalnum(const char *);
+extern int allalnumus(const char *);
 extern int allprint(const char *);
 extern int allspace(const char *);
 extern int allascii_len(const char *, ssize_t);
@@ -115,6 +116,9 @@ extern char *normalize_ws(char *);
 /*     Google, Inc.
 /*     111 8th Avenue
 /*     New York, NY 10011, USA
+/*
+/*     Wietse Venema
+/*     porcupine.org
 /*--*/
 
 #endif
index 1b04fb0e955fe17e265031c664aa039a533df85d..2526526f426c9c72daa752c00f591933ae60b980 100644 (file)
@@ -93,6 +93,7 @@ mailbox.o: ../../include/msg_stats.h
 mailbox.o: ../../include/myflock.h
 mailbox.o: ../../include/mymalloc.h
 mailbox.o: ../../include/nvtable.h
+mailbox.o: ../../include/pol_stats.h
 mailbox.o: ../../include/recipient_list.h
 mailbox.o: ../../include/safe_open.h
 mailbox.o: ../../include/sent.h
@@ -127,6 +128,7 @@ maildir.o: ../../include/msg_stats.h
 maildir.o: ../../include/myflock.h
 maildir.o: ../../include/mymalloc.h
 maildir.o: ../../include/nvtable.h
+maildir.o: ../../include/pol_stats.h
 maildir.o: ../../include/recipient_list.h
 maildir.o: ../../include/safe_open.h
 maildir.o: ../../include/sane_fsops.h
@@ -156,6 +158,7 @@ recipient.o: ../../include/msg_stats.h
 recipient.o: ../../include/myflock.h
 recipient.o: ../../include/mymalloc.h
 recipient.o: ../../include/nvtable.h
+recipient.o: ../../include/pol_stats.h
 recipient.o: ../../include/recipient_list.h
 recipient.o: ../../include/stringops.h
 recipient.o: ../../include/sys_defs.h
@@ -180,6 +183,7 @@ unknown.o: ../../include/msg_stats.h
 unknown.o: ../../include/myflock.h
 unknown.o: ../../include/mymalloc.h
 unknown.o: ../../include/nvtable.h
+unknown.o: ../../include/pol_stats.h
 unknown.o: ../../include/recipient_list.h
 unknown.o: ../../include/sys_defs.h
 unknown.o: ../../include/vbuf.h
index 75dd6cd2ede1d41d12022a8b0ef3c5cd39d86735..cde8035d5b2bbd16b46606058f0c7cc7170093b0 100644 (file)
@@ -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