-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
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
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
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
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
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()
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
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
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.
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.
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:
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
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.
* 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
--- /dev/null
+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.
+
$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
$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
--- /dev/null
+<!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:<sender@example.org> 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>
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
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>
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>
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:
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>
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
<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>
<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>.
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.
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 < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 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>
mand, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the MAIL FROM
+ The Postfix SMTP client time limit for sending the MAIL FROM
command, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the SMTP RCPT TO
+ The Postfix SMTP client time limit for sending the SMTP RCPT TO
command, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The Postfix SMTP client time limit for sending the SMTP DATA
+ The Postfix SMTP client time limit for sending the SMTP DATA
command, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The Postfix SMTP client time limit for sending the SMTP message
+ The Postfix SMTP client time limit for sending the SMTP message
content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
- The maximal number of MX (mail exchanger) IP addresses that can
- result from Postfix SMTP client mail exchanger lookups, or zero
+ The maximal number of MX (mail exchanger) IP addresses that can
+ result from Postfix SMTP client mail exchanger lookups, or zero
(no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery request before
- the Postfix SMTP client gives up or delivers to a fall-back
+ The maximal number of SMTP sessions per delivery request before
+ the Postfix SMTP client gives up or delivers to a fall-back
<a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
Available in Postfix version 2.2 and earlier:
<b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
- Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
+ Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
seconds.
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the specified
+ Permanently enable SMTP connection caching for the specified
destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a destination
+ Temporarily enable SMTP connection caching while a destination
has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
<b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or receive opera-
+ Time limit for connection cache connect, send or receive opera-
tions.
Available in Postfix version 2.9 - 3.6:
<b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per read or write system call, to a time limit to
- send or receive a complete record (an SMTP command line, SMTP
- response line, SMTP message content line, or TLS protocol mes-
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per read or write system call, to a time limit to
+ send or receive a complete record (an SMTP command line, SMTP
+ response line, SMTP message content line, or TLS protocol mes-
sage).
Available in Postfix version 2.11 and later:
<b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
- When SMTP connection caching is enabled, the number of times
- that an SMTP session may be reused before it is closed, or zero
+ When SMTP connection caching is enabled, the number of times
+ that an SMTP session may be reused before it is closed, or zero
(no limit).
Available in Postfix version 3.4 and later:
Available in Postfix version 3.7 and later:
<b><a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a> (no)</b>
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per plaintext or TLS read or write call, to a com-
- bined time limit for sending a complete SMTP request and for
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per plaintext or TLS read or write call, to a com-
+ bined time limit for sending a complete SMTP request and for
receiving a complete SMTP response.
<b><a href="postconf.5.html#smtp_min_data_rate">smtp_min_data_rate</a> (500)</b>
- The minimum plaintext data transfer rate in bytes/second for
+ The minimum plaintext data transfer rate in bytes/second for
DATA requests, when deadlines are enabled with
<a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a>.
<b><a href="postconf.5.html#transport_destination_concurrency_limit">transport_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
- A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
+ A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
<a href="postconf.5.html#default_destination_concurrency_limit">currency_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b><a href="postconf.5.html#transport_destination_recipient_limit">transport_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipi</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">ent_limit</a>)</b>
A transport-specific override for the <a href="postconf.5.html#default_destination_recipient_limit">default_destination_recip</a>-
- <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+ <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b><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>
<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>
The process name of a Postfix command or daemon process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
- The remote network interface addresses that this mail system
- receives mail on by way of a proxy or network address transla-
+ The remote network interface addresses that this mail system
+ receives mail on by way of a proxy or network address transla-
tion unit.
<b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
- client will try first, when a destination has IPv6 and IPv4
+ client will try first, when a destination has IPv6 and IPv4
addresses with equal MX preference.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv4 connection.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv6 connection.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available with Postfix 2.2 and earlier:
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay destinations that will be used when an
- SMTP destination is not found, or when delivery fails due to a
+ Optional list of relay destinations that will be used when an
+ SMTP destination is not found, or when delivery fails due to a
non-permanent error.
Available with Postfix 3.0 and later:
<b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
- In the context of email address verification, the SMTP protocol
+ In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Available with Postfix 3.1 and later:
Available in Postfix 3.7 and later:
<b><a href="postconf.5.html#smtp_bind_address_enforce">smtp_bind_address_enforce</a> (no)</b>
- Defer delivery when the Postfix SMTP client cannot apply the
+ Defer delivery when the Postfix SMTP client cannot apply the
<a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> or <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> setting.
<b><a name="see_also">SEE ALSO</a></b>
<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)
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
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
<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)
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
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
<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;
<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>
<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>
digits, otherwise the comparison is lexicographical. These forms
are supported with Postfix versions ≥ 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>
<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>
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 ≥ 3.11. </p>
+
+
</DD>
<DT><b><a name="dont_remove">dont_remove</a>
<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 ≥ 3.11. </p>
+
+
</DD>
<DT><b><a name="lmtp_mail_timeout">lmtp_mail_timeout</a>
<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 ≥ 3.11. </p>
+
+
</DD>
<DT><b><a name="lmtp_rset_timeout">lmtp_rset_timeout</a>
</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 ≥ 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>
</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>
<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 ≥ 3.11. </p>
+
+
</DD>
<DT><b><a name="smtp_rset_timeout">smtp_rset_timeout</a>
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 ≥ 3.10. </p>
<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>
<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)
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
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
<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>.
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.
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 < 2.3 control for the Postfix SMTP client TLS
+ Obsolete Postfix < 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>
mand, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_mail_timeout">smtp_mail_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the MAIL FROM
+ The Postfix SMTP client time limit for sending the MAIL FROM
command, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_rcpt_timeout">smtp_rcpt_timeout</a> (300s)</b>
- The Postfix SMTP client time limit for sending the SMTP RCPT TO
+ The Postfix SMTP client time limit for sending the SMTP RCPT TO
command, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_data_init_timeout">smtp_data_init_timeout</a> (120s)</b>
- The Postfix SMTP client time limit for sending the SMTP DATA
+ The Postfix SMTP client time limit for sending the SMTP DATA
command, and for receiving the remote SMTP server response.
<b><a href="postconf.5.html#smtp_data_xfer_timeout">smtp_data_xfer_timeout</a> (180s)</b>
- The Postfix SMTP client time limit for sending the SMTP message
+ The Postfix SMTP client time limit for sending the SMTP message
content.
<b><a href="postconf.5.html#smtp_data_done_timeout">smtp_data_done_timeout</a> (600s)</b>
Available in Postfix version 2.1 and later:
<b><a href="postconf.5.html#smtp_mx_address_limit">smtp_mx_address_limit</a> (5)</b>
- The maximal number of MX (mail exchanger) IP addresses that can
- result from Postfix SMTP client mail exchanger lookups, or zero
+ The maximal number of MX (mail exchanger) IP addresses that can
+ result from Postfix SMTP client mail exchanger lookups, or zero
(no limit).
<b><a href="postconf.5.html#smtp_mx_session_limit">smtp_mx_session_limit</a> (2)</b>
- The maximal number of SMTP sessions per delivery request before
- the Postfix SMTP client gives up or delivers to a fall-back
+ The maximal number of SMTP sessions per delivery request before
+ the Postfix SMTP client gives up or delivers to a fall-back
<a href="postconf.5.html#relayhost">relay host</a>, or zero (no limit).
<b><a href="postconf.5.html#smtp_rset_timeout">smtp_rset_timeout</a> (20s)</b>
Available in Postfix version 2.2 and earlier:
<b><a href="postconf.5.html#lmtp_cache_connection">lmtp_cache_connection</a> (yes)</b>
- Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
+ Keep Postfix LMTP client connections open for up to $<a href="postconf.5.html#max_idle">max_idle</a>
seconds.
Available in Postfix version 2.2 and later:
<b><a href="postconf.5.html#smtp_connection_cache_destinations">smtp_connection_cache_destinations</a> (empty)</b>
- Permanently enable SMTP connection caching for the specified
+ Permanently enable SMTP connection caching for the specified
destinations.
<b><a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a> (yes)</b>
- Temporarily enable SMTP connection caching while a destination
+ Temporarily enable SMTP connection caching while a destination
has a high volume of mail in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
<b><a href="postconf.5.html#smtp_connection_reuse_time_limit">smtp_connection_reuse_time_limit</a> (300s)</b>
Available in Postfix version 2.3 and later:
<b><a href="postconf.5.html#connection_cache_protocol_timeout">connection_cache_protocol_timeout</a> (5s)</b>
- Time limit for connection cache connect, send or receive opera-
+ Time limit for connection cache connect, send or receive opera-
tions.
Available in Postfix version 2.9 - 3.6:
<b><a href="postconf.5.html#smtp_per_record_deadline">smtp_per_record_deadline</a> (no)</b>
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per read or write system call, to a time limit to
- send or receive a complete record (an SMTP command line, SMTP
- response line, SMTP message content line, or TLS protocol mes-
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per read or write system call, to a time limit to
+ send or receive a complete record (an SMTP command line, SMTP
+ response line, SMTP message content line, or TLS protocol mes-
sage).
Available in Postfix version 2.11 and later:
<b><a href="postconf.5.html#smtp_connection_reuse_count_limit">smtp_connection_reuse_count_limit</a> (0)</b>
- When SMTP connection caching is enabled, the number of times
- that an SMTP session may be reused before it is closed, or zero
+ When SMTP connection caching is enabled, the number of times
+ that an SMTP session may be reused before it is closed, or zero
(no limit).
Available in Postfix version 3.4 and later:
Available in Postfix version 3.7 and later:
<b><a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a> (no)</b>
- Change the behavior of the smtp_*_timeout time limits, from a
- time limit per plaintext or TLS read or write call, to a com-
- bined time limit for sending a complete SMTP request and for
+ Change the behavior of the smtp_*_timeout time limits, from a
+ time limit per plaintext or TLS read or write call, to a com-
+ bined time limit for sending a complete SMTP request and for
receiving a complete SMTP response.
<b><a href="postconf.5.html#smtp_min_data_rate">smtp_min_data_rate</a> (500)</b>
- The minimum plaintext data transfer rate in bytes/second for
+ The minimum plaintext data transfer rate in bytes/second for
DATA requests, when deadlines are enabled with
<a href="postconf.5.html#smtp_per_request_deadline">smtp_per_request_deadline</a>.
<b><a href="postconf.5.html#transport_destination_concurrency_limit">transport_destination_concurrency_limit</a> ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concur</a>-</b>
<b><a href="postconf.5.html#default_destination_concurrency_limit">rency_limit</a>)</b>
- A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
+ A transport-specific override for the <a href="postconf.5.html#default_destination_concurrency_limit">default_destination_con</a>-
<a href="postconf.5.html#default_destination_concurrency_limit">currency_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b><a href="postconf.5.html#transport_destination_recipient_limit">transport_destination_recipient_limit</a> ($<a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipi</a>-</b>
<b><a href="postconf.5.html#default_destination_recipient_limit">ent_limit</a>)</b>
A transport-specific override for the <a href="postconf.5.html#default_destination_recipient_limit">default_destination_recip</a>-
- <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
+ <a href="postconf.5.html#default_destination_recipient_limit">ient_limit</a> parameter value, where <i>transport</i> is the <a href="master.5.html">master.cf</a>
name of the message delivery transport.
<b><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>
<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>
The process name of a Postfix command or daemon process.
<b><a href="postconf.5.html#proxy_interfaces">proxy_interfaces</a> (empty)</b>
- The remote network interface addresses that this mail system
- receives mail on by way of a proxy or network address transla-
+ The remote network interface addresses that this mail system
+ receives mail on by way of a proxy or network address transla-
tion unit.
<b><a href="postconf.5.html#smtp_address_preference">smtp_address_preference</a> (any)</b>
The address type ("ipv6", "ipv4" or "any") that the Postfix SMTP
- client will try first, when a destination has IPv6 and IPv4
+ client will try first, when a destination has IPv6 and IPv4
addresses with equal MX preference.
<b><a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> (empty)</b>
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv4 connection.
<b><a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> (empty)</b>
- An optional numerical network address that the Postfix SMTP
+ An optional numerical network address that the Postfix SMTP
client should bind to when making an IPv6 connection.
<b><a href="postconf.5.html#smtp_helo_name">smtp_helo_name</a> ($<a href="postconf.5.html#myhostname">myhostname</a>)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (see 'postconf -d' output)</b>
- A prefix that is prepended to the process name in syslog
+ A prefix that is prepended to the process name in syslog
records, so that, for example, "smtpd" becomes "prefix/smtpd".
Available with Postfix 2.2 and earlier:
Available with Postfix 2.3 and later:
<b><a href="postconf.5.html#smtp_fallback_relay">smtp_fallback_relay</a> ($<a href="postconf.5.html#fallback_relay">fallback_relay</a>)</b>
- Optional list of relay destinations that will be used when an
- SMTP destination is not found, or when delivery fails due to a
+ Optional list of relay destinations that will be used when an
+ SMTP destination is not found, or when delivery fails due to a
non-permanent error.
Available with Postfix 3.0 and later:
<b><a href="postconf.5.html#smtp_address_verify_target">smtp_address_verify_target</a> (rcpt)</b>
- In the context of email address verification, the SMTP protocol
+ In the context of email address verification, the SMTP protocol
stage that determines whether an email address is deliverable.
Available with Postfix 3.1 and later:
Available in Postfix 3.7 and later:
<b><a href="postconf.5.html#smtp_bind_address_enforce">smtp_bind_address_enforce</a> (no)</b>
- Defer delivery when the Postfix SMTP client cannot apply the
+ Defer delivery when the Postfix SMTP client cannot apply the
<a href="postconf.5.html#smtp_bind_address">smtp_bind_address</a> or <a href="postconf.5.html#smtp_bind_address6">smtp_bind_address6</a> setting.
<b><a name="see_also">SEE ALSO</a></b>
<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>.
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.
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.
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
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.
.SH "FILES"
.na
.nf
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
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
.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
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.
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
.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
.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
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
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.
.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
TLS_README for more information about security levels.
.PP
Example:
+.sp
.nf
.na
# Preferred syntax with Postfix >= 3.6:
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:
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
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:
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
.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
.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
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
.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
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
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
.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
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
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 |
$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;
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;
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;
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;
../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 \
../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 \
../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) >$@
../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) >$@
../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 $@
--- /dev/null
+<!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:<sender@example.org> 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>
<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>
<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>
digits, otherwise the comparison is lexicographical. These forms
are supported with Postfix versions ≥ 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>
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
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
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 ≥ 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 ≥ 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 ≥ 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 ≥ 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 ≥ 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
<p> This feature is available in Postfix ≥ 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 ≥ 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
RequireTLS
requiretls
sendopts
+TODO
+Onoop
+Orequiretls
tz
GID
SIGKILL
rhansen
XDG
crosstalk
+HDRS
+deliverability
+certmatch
+Punycode
+unnormalized
+xa
+ascii
+halfdane
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
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
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
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
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
OSSL
ossl
deduplicates
+crosstalk
intmax
lflag
REPLYCODE
PTEST
finalizer
+REQTLS
+reqtls
+Esmtp
+ENF
+tstats
+pstats
enf
+allalnumus
Christophe
Kalt
stdlib
bitflags
Schulze
tlspol
+TlsRequired
Gueven
Oemer
Kozmenko
PRI
YP
Natalenko
+nocertmatch
+pgnd
Snawoot
Zuplu
tlspol
+nocertmatch
+noencryption
+nomatch
+nostarttls
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
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
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
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
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
/* .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. */
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.
&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,
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.
*/
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.
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.
*/
};
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,
};
}
/*
- * 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)
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
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
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
/* .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
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),
* Internationalization, RequireTLS, etc.
*/
int sendopts; /* what support is desired */
+ int reqtls_esmtp_hdr_seen; /* valid Require-TLS-ESMTP header */
} CLEANUP_STATE;
/*
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[];
/* .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).
} 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));
}
*/
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;
}
}
/*
/* CONFIG_BOOL_TABLE cleanup_bool_table[];
/*
+/* CONFIG_NBOOL_TABLE cleanup_nbool_table[];
+/*
/* CONFIG_STR_TABLE cleanup_str_table[];
/*
/* CONFIG_TIME_TABLE cleanup_time_table[];
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,
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,
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-";
}
}
+ /*
+ * 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
}
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;
}
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;
}
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);
}
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
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;
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
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;
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;
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 \
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.
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 =
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
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)
$(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 \
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
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
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)
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 '}' ; \
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
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
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
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
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
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
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
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
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
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
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
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
/* 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,
/* 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;
/* 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;
/* 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
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;
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;
*/
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);
}
*/
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);
}
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);
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;
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;
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 */
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;
*/
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);
}
*/
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);
}
* 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));
}
/*
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);
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;
*/
#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,
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 *);
/*
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
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
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 */
#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)
/* 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,
/* 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;
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;
*/
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);
}
*/
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);
}
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);
/*
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;
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));
}
* 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,
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.
#ifdef DSN_INTERN
extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *,
- const char *, DSN *);
+ const char *, const POL_STATS *, DSN *);
#endif
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
(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);
}
/*
/* #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,
};
*/
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
/* .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);
+++ /dev/null
-starttls, 8bitmime, verp, etrn, etrn
-foobar, auth, pipelining, size, vrfy
-xclient, xforward
+++ /dev/null
-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
--- /dev/null
+ /*
+ * 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);
+}
"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]))
#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.
/* 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
/* 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
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
#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
/* 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 */
*
* 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 { \
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.
*/
#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
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
/* 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. */
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;
/* 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);
}
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,
};
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
#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
* 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
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
--- /dev/null
+/*++
+/* 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 */
--- /dev/null
+#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_ */
--- /dev/null
+/*++
+/* 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);
+}
/* .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.
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);
/*
* 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"
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/*
(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;
/* 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,
/* 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
/* 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;
*/
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);
}
*/
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);
}
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);
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);
#define SENT_FLAG_NONE (0)
extern int sent(int, const char *, MSG_STATS *, RECIPIENT *, const char *,
- DSN *);
+ const POL_STATS *, DSN *);
/* LICENSE
/* .ad
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
/* 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,
/* 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
/* 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;
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);
* 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);
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
/* 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
/* 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".
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
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;
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);
* 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
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
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
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
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
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
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
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
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
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
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
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
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
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
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
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
#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),
#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) \
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
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
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
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
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
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
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
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
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
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);
*/
message->flags |= defer_append(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", dsn);
+ "none", NO_TLS_STATS, dsn);
}
&& !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);
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
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;
(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;
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;
/* 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");
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
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
/* Utility library. */
+#include <mac_midna.h>
#include <msg.h>
#include <msg_vstream.h>
#include <dict.h>
* For consistency with mail_params_init().
*/
compat_level_relop_register();
+ mac_midna_register();
/*
* We don't enforce import_environment consistency in this program.
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
#include <vstring.h>
#include <get_hostname.h>
#include <stringops.h>
+#include <midna_domain.h>
/* Global library. */
/*
* 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) {
/*
* 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();
}
}
+/* 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)
/*
* 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);
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);
/*
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
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
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
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
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);
*/
message->flags |= defer_append(message->tflags, message->queue_id,
QMGR_MSG_STATS(&stats, message), recipient,
- "none", dsn);
+ "none", NO_TLS_STATS, dsn);
}
&& !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);
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
/* 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. */
0,
};
+ /*
+ * Sender options.
+ */
+static int sm_sendopts;
+
/*
* Silly little macros (SLMs).
*/
* 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);
int saved_optind;
ARGV *import_env;
char *alias_map_from_args = 0;
+ const char *oval;
/*
* Fingerprint executables and core dumps.
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)
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) {
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) \
test: $(TESTPROG)
-tests: smtp_map11_test test_smtp_tls_policy
+tests: smtp_map11_test test_smtp_tls_policy test_smtp_reqtls_policy
root_tests:
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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,
};
/* 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
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;
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
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 */
#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
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
/*
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
/*
#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
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
#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)
(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)
/*
* 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 *,...);
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
#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
#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,
| 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.
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.
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
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.
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;
/* 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.
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
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,
};
#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>
/* 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
*
* 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
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
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,
* 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)
/* 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",
"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
*/
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"));
* 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
/*
* 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
+ * a 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".
(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.
* 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
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));
}
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.
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);
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
--- /dev/null
+ /*
+ * 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);
+}
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);
#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);
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.
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;
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;
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;
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;
/* 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
/* 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;
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
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);
*/
if (throttle_queue && session)
DONT_CACHE_THROTTLED_SESSION;
+ else if (dont_cache && session)
+ DONT_CACHE_THIS_SESSION;
return (-1);
}
* 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 */
/* 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;
/*
* 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 */
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);
* 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));
}
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
/* 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
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.
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.
/*
* 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));
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
int rate;
int dsn_envid = 0;
- state->flags &= ~SMTPD_FLAG_SMTPUTF8;
+ state->flags &= ~SMTPD_FLAGS_PER_MESSAGE;
state->encoding = 0;
state->dsn_ret = 0;
&& (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) {
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);
* 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
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[] = {
#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 */
/*
/* 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. */
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,
/*
* 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);
* 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.
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);
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
}
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"
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#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)
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));
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 \
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.
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)
done
clean:
- rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp
+ rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAKES) *.tmp testdb.db
tidy: clean
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
/*
/* 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.
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
/* System library. */
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);
+}
/* 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
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)
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)
#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()
/* 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. */
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.
*/
/* 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\"",
break;
}
}
+ if (strip_space)
+ trimblanks(payload, cp - payload)[0] = 0;
*cp++ = 0;
/*
* 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);
/*
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);
/*
}
}
+/* 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)
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) {
}
/*
- * 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++) {
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: "
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;
/* 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;
}
}
+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);
*/
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.
*/
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
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
${{aaa} <length {bb}}
${{aaa} <= {bb}}
${{aaa} <=length {bb}}
+
+name1 = whatever
+
+${ test_named_fn { ABCBA } }
+${test_named_fn{AB%BA}}
+$bogus{AB%BA}
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, |"
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}
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
/* 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),
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",
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);
}
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,
#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);
/*
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 ->
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 ->
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);
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
+/* porcupine.org
/*--*/
#endif
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
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
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
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
#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