diff --git a/postfix/HISTORY b/postfix/HISTORY index 19895a78c..913da047d 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -28959,20 +28959,29 @@ Apologies for any names omitted. Completed: simplified cleanup_envelope_test implementation. -TODO: +20250127 - Add a log-only mode for REQUIRETLS, so that one can find out - how much would break. Maybe make it a map, so that different - sites can have different enforcement levels. + Cleanup: broken non-TLS builds because of a missing #ifdef + USE_TLS/#endif around a new function get_effective_tls_level(). + File: smtp/smtp_connect.c. - If we support log-only REQUIRETLS, then don't panic ("can't - happen") when the code reaches a point that can't be reached - when REQUIRETLS is actually enforced. + Cleanup: a few remaining pre-ANSI C function definitions + in the lowest-level Postfix code. Files: util/binhash.c, + util/close_on_exec.c, util/non_blocking.c, util/ring.c. + +20250129 + + Completed: smtp_enforce_requiretls list of next-hop domains + (or UNIX-domain pathnames) that are ready for REQUIRETLS + enforcement. This may help with gradual adoption. + +TODO Encapsulate the sendopts-to-cleanup-flags mapping. How do we make it work with multi-instance SMTP-based content - filters? How is this different from the single-instance case? + filters? How is this different from the single-instance + case? What REQUIRETLS expectations can we enforce when delivering over a UNIX-domain channel? The SMTP/LMTP client currently diff --git a/postfix/html/lmtp.8.html b/postfix/html/lmtp.8.html index 054c592fd..715a34ee9 100644 --- a/postfix/html/lmtp.8.html +++ b/postfix/html/lmtp.8.html @@ -765,6 +765,13 @@ SMTP(8) SMTP(8) Enable support for the ESMTP verb "REQUIRETLS", defined in RFC 8689. + smtp_enforce_requiretls (empty) + An optional list of next-hop destinations that the Postfix + SMTP/LMTP client will enforce REQUIRETLS for, when a message was + received with the REQUIRETLS option: the next-hop server must + offer a matching TLS server certificate, and the server must + announce REQUIRETLS support). + OBSOLETE TLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 1150d0316..c755aaabf 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -5041,6 +5041,17 @@ configuration parameter. See there for details.

This feature is available in Postfix 2.11 and later.

+ + +
lmtp_enforce_requiretls +(default: empty)
+ +

The LMTP-specific version of the smtp_enforce_requiretls +configuration parameter. See there for details.

+ +

This feature is available in Postfix ≥ 3.10.

+ +
lmtp_enforce_tls @@ -10585,7 +10596,16 @@ the smtp_mx_address_limit pa satisfies requirements. Otherwise, Postfix returns the message as undeliverable.

-

Note: REQUIRETLS overrides "TLS-Required: no".

+

Notes:

+ +

This feature is available in Postfix ≥ 3.10.

@@ -11921,6 +11941,86 @@ RES_USE_DNSSEC and RES_USE_EDNS0 resolver options.

This feature is available in Postfix 2.11 and later.

+ + +
smtp_enforce_requiretls +(default: empty)
+ +

An optional list of next-hop destinations that the Postfix +SMTP/LMTP client will enforce REQUIRETLS for, when a message was +received with the REQUIRETLS option: the next-hop server must offer +a matching TLS server certificate, and the server must announce +REQUIRETLS support). Such a message will be returned to the sender +if some REQUIRETLS requirement cannot be satisfied. These "hard" +REQUIRETLS failures are logged as "REQUIRETLS failure".

+ +

Other messages that were received with the REQUIRETLS option +will be delivered with REQUIRETLS if possible. If not, a message +will be delivered as if it was received without the REQUIRETLS +option. This allows a mail sending site to discover when REQUIRETLS +can be enforced, without disrupting email deliveries. These "soft" +REQUIRETLS failures are logged with "REQUIRETLS Debug".

+ +

On a perimeter MTA, it can make sense to turn off REQUIRETLS +enforcement, or even to turn off REQUIRETLS support, when delivering +a message to an internal destination. The internal servers may not +support REQUIRETLS, and their connections may be secured with means +other than DANE, STS, and the like.

+ +

The supported syntax differs with SMTP and LMTP:

+ + + +

By default, specify the form ".domain" to match any name ending +in ".domain". This behavior is controlled by the presence or absence +of "smtp_enforce_requiretls" or "lmtp_enforce_requiretls" in the +parent_domain_matches_subdomains parameter value (by default they +are absent).

+ +

+SMTP Examples: +

+ +
+# Enforce REQUIRETLS for SMTP with selected next-hop domains.
+smtp_enforce_requiretls = example.com, foo.example
+
+# Enforce REQUIRETLS for SMTP with all but a few next-hop domains. +smtp_enforce_requiretls = !foo.example, static:all +
+ +

+LMTP examples: +

+ +
+# Don't enforce REQUIRETLS
+lmtp_enforce_requiretls =
+
+# Enforce REQUIRETLS for specific destinations. +lmtp_enforce_requiretls = /path/to/socket, message-store.example +
+ +

This feature is available in Postfix ≥ 3.10.

+ +
smtp_enforce_tls diff --git a/postfix/html/smtp.8.html b/postfix/html/smtp.8.html index 054c592fd..715a34ee9 100644 --- a/postfix/html/smtp.8.html +++ b/postfix/html/smtp.8.html @@ -765,6 +765,13 @@ SMTP(8) SMTP(8) Enable support for the ESMTP verb "REQUIRETLS", defined in RFC 8689. + smtp_enforce_requiretls (empty) + An optional list of next-hop destinations that the Postfix + SMTP/LMTP client will enforce REQUIRETLS for, when a message was + received with the REQUIRETLS option: the next-hop server must + offer a matching TLS server certificate, and the server must + announce REQUIRETLS support). + OBSOLETE TLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index 502baddc6..3c66468c7 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -3136,6 +3136,11 @@ The LMTP\-specific version of the smtp_dns_support_level configuration parameter. See there for details. .PP This feature is available in Postfix 2.11 and later. +.SH lmtp_enforce_requiretls (default: empty) +The LMTP\-specific version of the smtp_enforce_requiretls +configuration parameter. See there for details. +.PP +This feature is available in Postfix >= 3.10. .SH lmtp_enforce_tls (default: no) The LMTP\-specific version of the smtp_enforce_tls configuration parameter. See there for details. @@ -6591,7 +6596,13 @@ the smtp_mx_address_limit parameter, until it finds a server that satisfies requirements. Otherwise, Postfix returns the message as undeliverable. .PP -Note: REQUIRETLS overrides "TLS\-Required: no". +Notes: +.IP \(bu +REQUIRETLS enforcement is controlled with smtp_enforce_requiretls +or lmtp_enforce_requiretls. +.IP \(bu +REQUIRETLS overrides "TLS\-Required: no". +.br .PP This feature is available in Postfix >= 3.10. .SH reset_owner_alias (default: no) @@ -7528,6 +7539,77 @@ reasonably\-modern DNS \fBresolver\fR(3) library that implements the RES_USE_DNSSEC and RES_USE_EDNS0 resolver options. .PP This feature is available in Postfix 2.11 and later. +.SH smtp_enforce_requiretls (default: empty) +An optional list of next\-hop destinations that the Postfix +SMTP/LMTP client will enforce REQUIRETLS for, when a message was +received with the REQUIRETLS option: the next\-hop server must offer +a matching TLS server certificate, and the server must announce +REQUIRETLS support). Such a message will be returned to the sender +if some REQUIRETLS requirement cannot be satisfied. These "hard" +REQUIRETLS failures are logged as "REQUIRETLS failure". +.PP +Other messages that were received with the REQUIRETLS option +will be delivered with REQUIRETLS if possible. If not, a message +will be delivered as if it was received without the REQUIRETLS +option. This allows a mail sending site to discover when REQUIRETLS +can be enforced, without disrupting email deliveries. These "soft" +REQUIRETLS failures are logged with "REQUIRETLS Debug". +.PP +On a perimeter MTA, it can make sense to turn off REQUIRETLS +enforcement, or even to turn off REQUIRETLS support, when delivering +a message to an internal destination. The internal servers may not +support REQUIRETLS, and their connections may be secured with means +other than DANE, STS, and the like. +.PP +The supported syntax differs with SMTP and LMTP: +.IP \(bu +With SMTP, specify a list of next\-hop domain names (without +the ":port" or ":service" suffix), "/file/name" patterns or +"type:table" lookup tables, separated by commas and/or whitespace. +Continue long lines by starting the next line with whitespace. A +"/file/name" pattern is replaced by its contents; a "type:table" +lookup table is matched when a domain appears as lookup key. Specify +"!pattern" to exclude a domain from the list. +.IP \(bu +With LMTP, specify list of domain names (without the "inet": +prefix, or ":port" or ":service" suffix), and/or UNIX\-domain socket +"/path/name" (without the "unix:" prefix), separated by commas +and/or whitespace. Continue long lines by starting the next line +with whitespace. Specify "!pattern" to exclude a pattern from the +list. +.br +.PP +By default, specify the form ".domain" to match any name ending +in ".domain". This behavior is controlled by the presence or absence +of "smtp_enforce_requiretls" or "lmtp_enforce_requiretls" in the +parent_domain_matches_subdomains parameter value (by default they +are absent). +.PP +SMTP Examples: +.PP +.nf +.na +# Enforce REQUIRETLS for SMTP with selected next\-hop domains. +smtp_enforce_requiretls = example.com, foo.example +.br +# Enforce REQUIRETLS for SMTP with all but a few next\-hop domains. +smtp_enforce_requiretls = !foo.example, static:all +.fi +.ad +.PP +LMTP examples: +.PP +.nf +.na +# Don't enforce REQUIRETLS +lmtp_enforce_requiretls = +.br +# Enforce REQUIRETLS for specific destinations. +lmtp_enforce_requiretls = /path/to/socket, message\-store.example +.fi +.ad +.PP +This feature is available in Postfix >= 3.10. .SH smtp_enforce_tls (default: no) Enforcement mode: require that remote SMTP servers use TLS encryption, and never send mail in the clear. This also requires diff --git a/postfix/man/man8/smtp.8 b/postfix/man/man8/smtp.8 index a4a343a2f..9f5f726ca 100644 --- a/postfix/man/man8/smtp.8 +++ b/postfix/man/man8/smtp.8 @@ -687,6 +687,12 @@ information to report). .IP "\fBrequiretls_enable (yes)\fR" Enable support for the ESMTP verb "REQUIRETLS", defined in RFC 8689. +.IP "\fBsmtp_enforce_requiretls (empty)\fR" +An optional list of next\-hop destinations that the Postfix +SMTP/LMTP client will enforce REQUIRETLS for, when a message was +received with the REQUIRETLS option: the next\-hop server must offer +a matching TLS server certificate, and the server must announce +REQUIRETLS support). .SH "OBSOLETE TLS CONTROLS" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index c2fb9b476..8cd3fe4d4 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -1188,6 +1188,8 @@ while (<>) { s;\btls_required_enable\b;$&;g; s;\brequiretls_enable\b;$&;g; + s;\bsmtp_enforce_requiretls\b;$&;g; + s;\blmtp_enforce_requiretls\b;$&;g; s;\bfull_name_encoding_charset\b;$&;g; diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index d9b191878..674c6dde9 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -19501,6 +19501,98 @@ the smtp_mx_address_limit parameter, until it finds a server that satisfies requirements. Otherwise, Postfix returns the message as undeliverable.

-

Note: REQUIRETLS overrides "TLS-Required: no".

+

Notes:

+ +
    + +
  • REQUIRETLS enforcement is controlled with smtp_enforce_requiretls +or lmtp_enforce_requiretls.

    + +
  • REQUIRETLS overrides "TLS-Required: no".

    + +
+ +

This feature is available in Postfix ≥ 3.10.

+ +%PARAM smtp_enforce_requiretls empty + +

An optional list of next-hop destinations that the Postfix +SMTP/LMTP client will enforce REQUIRETLS for, when a message was +received with the REQUIRETLS option: the next-hop server must offer +a matching TLS server certificate, and the server must announce +REQUIRETLS support). Such a message will be returned to the sender +if some REQUIRETLS requirement cannot be satisfied. These "hard" +REQUIRETLS failures are logged as "REQUIRETLS failure".

+ +

Other messages that were received with the REQUIRETLS option +will be delivered with REQUIRETLS if possible. If not, a message +will be delivered as if it was received without the REQUIRETLS +option. This allows a mail sending site to discover when REQUIRETLS +can be enforced, without disrupting email deliveries. These "soft" +REQUIRETLS failures are logged with "REQUIRETLS Debug".

+ +

On a perimeter MTA, it can make sense to turn off REQUIRETLS +enforcement, or even to turn off REQUIRETLS support, when delivering +a message to an internal destination. The internal servers may not +support REQUIRETLS, and their connections may be secured with means +other than DANE, STS, and the like.

+ +

The supported syntax differs with SMTP and LMTP:

+ +
    + +
  • With SMTP, specify a list of next-hop domain names (without +the ":port" or ":service" suffix), "/file/name" patterns or +"type:table" lookup tables, separated by commas and/or whitespace. +Continue long lines by starting the next line with whitespace. A +"/file/name" pattern is replaced by its contents; a "type:table" +lookup table is matched when a domain appears as lookup key. Specify +"!pattern" to exclude a domain from the list.

    + +
  • With LMTP, specify list of domain names (without the "inet": +prefix, or ":port" or ":service" suffix), and/or UNIX-domain socket +"/path/name" (without the "unix:" prefix), separated by commas +and/or whitespace. Continue long lines by starting the next line +with whitespace. Specify "!pattern" to exclude a pattern from the +list.

    + +
+ +

By default, specify the form ".domain" to match any name ending +in ".domain". This behavior is controlled by the presence or absence +of "smtp_enforce_requiretls" or "lmtp_enforce_requiretls" in the +parent_domain_matches_subdomains parameter value (by default they +are absent).

+ +

+SMTP Examples: +

+ +
+# Enforce REQUIRETLS for SMTP with selected next-hop domains.
+smtp_enforce_requiretls = example.com, foo.example
+
+# Enforce REQUIRETLS for SMTP with all but a few next-hop domains. +smtp_enforce_requiretls = !foo.example, static:all +
+ +

+LMTP examples: +

+ +
+# Don't enforce REQUIRETLS 
+lmtp_enforce_requiretls =
+
+# Enforce REQUIRETLS for specific destinations. +lmtp_enforce_requiretls = /path/to/socket, message-store.example +
+ +

This feature is available in Postfix ≥ 3.10.

+ +%PARAM lmtp_enforce_requiretls empty + +

The LMTP-specific version of the smtp_enforce_requiretls +configuration parameter. See there for details.

This feature is available in Postfix ≥ 3.10.

diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index 34e439196..c8fffdf36 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1858,3 +1858,4 @@ TINYCDB getdata XXXSENDOPTS xtra +NODICT diff --git a/postfix/src/global/addr_match_list.c b/postfix/src/global/addr_match_list.c index 8008df203..136ce24b5 100644 --- a/postfix/src/global/addr_match_list.c +++ b/postfix/src/global/addr_match_list.c @@ -42,6 +42,10 @@ /* Request that addr_match_list_match() logs a warning and /* returns zero with list->error set to a non-zero dictionary /* error code, instead of raising a fatal error. +/* .IP MATCH_FLAG_NOFILE +/* Disable special handling for /file/name. +/* .IP MATCH_FLAG_NODICT +/* Disable special handling for type:name. /* .PP /* Specify MATCH_FLAG_NONE to request none of the above. /* The last argument is a list of patterns, or the absolute @@ -67,6 +71,9 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ diff --git a/postfix/src/global/domain_list.c b/postfix/src/global/domain_list.c index d79beafd3..e3fe1caec 100644 --- a/postfix/src/global/domain_list.c +++ b/postfix/src/global/domain_list.c @@ -45,6 +45,10 @@ /* Request that domain_list_match() logs a warning and returns /* zero, with list->error set to a non-zero dictionary error /* code, instead of raising a fatal error. +/* .IP MATCH_FLAG_NOFILE +/* Disable special handling for /file/name. +/* .IP MATCH_FLAG_NODICT +/* Disable special handling for type:name. /* .PP /* Specify MATCH_FLAG_NONE to request none of the above. /* The last argument is a list of domain patterns, or the name of @@ -69,6 +73,9 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index fc0b7a5f6..6a0417169 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -4387,6 +4387,13 @@ extern int var_requiretls_enable; #define DEF_TLSREQUIRED_ENABLE "yes" extern int var_tls_required_enable; +#define VAR_SMTP_ENFORCE_REQUIRETLS "smtp_enforce_requiretls" +#define DEF_SMTP_ENFORCE_REQUIRETLS "" +extern char *var_smtp_enforce_requiretls; + +#define VAR_LMTP_ENFORCE_REQUIRETLS "lmtp_enforce_requiretls" +#define DEF_LMTP_ENFORCE_REQUIRETLS "" + /* * Workaround for future incompatibility. Our implementation of RFC 2308 * negative reply caching relies on the promise that res_query() and diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index f2675f1fd..41df81ba5 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20250126" +#define MAIL_RELEASE_DATE "20250129" #define MAIL_VERSION_NUMBER "3.10" #ifdef SNAPSHOT diff --git a/postfix/src/global/namadr_list.c b/postfix/src/global/namadr_list.c index 071a73341..0a9a9054f 100644 --- a/postfix/src/global/namadr_list.c +++ b/postfix/src/global/namadr_list.c @@ -51,6 +51,10 @@ /* Request that namadr_list_match() logs a warning and returns /* zero with list->error set to a non-zero dictionary error /* code, instead of raising a fatal error. +/* .IP MATCH_FLAG_NOFILE +/* Disable special handling for /file/name. +/* .IP MATCH_FLAG_NODICT +/* Disable special handling for type:name. /* .PP /* Specify MATCH_FLAG_NONE to request none of the above. /* The last argument is a list of patterns, or the absolute @@ -75,6 +79,9 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ diff --git a/postfix/src/global/string_list.c b/postfix/src/global/string_list.c index ddd950a00..88a43fb1b 100644 --- a/postfix/src/global/string_list.c +++ b/postfix/src/global/string_list.c @@ -39,6 +39,10 @@ /* Request that string_list_match() logs a warning and returns /* zero with list->error set to a non-zero dictionary error /* code, instead of raising a fatal error. +/* .IP MATCH_FLAG_NOFILE +/* Disable special handling for /file/name. +/* .IP MATCH_FLAG_NODICT +/* Disable special handling for type:name. /* .PP /* Specify MATCH_FLAG_NONE to request none of the above. /* The last argument specifies a list of string patterns. @@ -61,6 +65,9 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ diff --git a/postfix/src/smtp/Makefile.in b/postfix/src/smtp/Makefile.in index 44add46b2..801e7ee0e 100644 --- a/postfix/src/smtp/Makefile.in +++ b/postfix/src/smtp/Makefile.in @@ -84,6 +84,7 @@ smtp.o: ../../include/deliver_request.h smtp.o: ../../include/delivered_hdr.h smtp.o: ../../include/dict.h smtp.o: ../../include/dns.h +smtp.o: ../../include/domain_list.h smtp.o: ../../include/dsn.h smtp.o: ../../include/dsn_buf.h smtp.o: ../../include/ext_prop.h @@ -134,6 +135,7 @@ smtp_addr.o: ../../include/check_arg.h smtp_addr.o: ../../include/deliver_request.h smtp_addr.o: ../../include/dict.h smtp_addr.o: ../../include/dns.h +smtp_addr.o: ../../include/domain_list.h smtp_addr.o: ../../include/dsn.h smtp_addr.o: ../../include/dsn_buf.h smtp_addr.o: ../../include/header_body_checks.h @@ -178,6 +180,7 @@ smtp_chat.o: ../../include/cleanup_user.h smtp_chat.o: ../../include/deliver_request.h smtp_chat.o: ../../include/dict.h smtp_chat.o: ../../include/dns.h +smtp_chat.o: ../../include/domain_list.h smtp_chat.o: ../../include/dsn.h smtp_chat.o: ../../include/dsn_buf.h smtp_chat.o: ../../include/dsn_util.h @@ -229,6 +232,7 @@ smtp_connect.o: ../../include/deliver_pass.h smtp_connect.o: ../../include/deliver_request.h smtp_connect.o: ../../include/dict.h smtp_connect.o: ../../include/dns.h +smtp_connect.o: ../../include/domain_list.h smtp_connect.o: ../../include/dsn.h smtp_connect.o: ../../include/dsn_buf.h smtp_connect.o: ../../include/header_body_checks.h @@ -284,6 +288,7 @@ smtp_key.o: ../../include/check_arg.h smtp_key.o: ../../include/deliver_request.h smtp_key.o: ../../include/dict.h smtp_key.o: ../../include/dns.h +smtp_key.o: ../../include/domain_list.h smtp_key.o: ../../include/dsn.h smtp_key.o: ../../include/dsn_buf.h smtp_key.o: ../../include/header_body_checks.h @@ -321,6 +326,7 @@ smtp_map11.o: ../../include/check_arg.h smtp_map11.o: ../../include/deliver_request.h smtp_map11.o: ../../include/dict.h smtp_map11.o: ../../include/dns.h +smtp_map11.o: ../../include/domain_list.h smtp_map11.o: ../../include/dsn.h smtp_map11.o: ../../include/dsn_buf.h smtp_map11.o: ../../include/header_body_checks.h @@ -361,6 +367,7 @@ smtp_misc.o: ../../include/check_arg.h smtp_misc.o: ../../include/deliver_request.h smtp_misc.o: ../../include/dict.h smtp_misc.o: ../../include/dns.h +smtp_misc.o: ../../include/domain_list.h smtp_misc.o: ../../include/dsn.h smtp_misc.o: ../../include/dsn_buf.h smtp_misc.o: ../../include/ext_prop.h @@ -404,6 +411,7 @@ smtp_proto.o: ../../include/defer.h smtp_proto.o: ../../include/deliver_request.h smtp_proto.o: ../../include/dict.h smtp_proto.o: ../../include/dns.h +smtp_proto.o: ../../include/domain_list.h smtp_proto.o: ../../include/dsn.h smtp_proto.o: ../../include/dsn_buf.h smtp_proto.o: ../../include/dsn_mask.h @@ -470,6 +478,7 @@ smtp_rcpt.o: ../../include/deliver_completed.h smtp_rcpt.o: ../../include/deliver_request.h smtp_rcpt.o: ../../include/dict.h smtp_rcpt.o: ../../include/dns.h +smtp_rcpt.o: ../../include/domain_list.h smtp_rcpt.o: ../../include/dsn.h smtp_rcpt.o: ../../include/dsn_buf.h smtp_rcpt.o: ../../include/dsn_mask.h @@ -510,6 +519,7 @@ smtp_reuse.o: ../../include/check_arg.h smtp_reuse.o: ../../include/deliver_request.h smtp_reuse.o: ../../include/dict.h smtp_reuse.o: ../../include/dns.h +smtp_reuse.o: ../../include/domain_list.h smtp_reuse.o: ../../include/dsn.h smtp_reuse.o: ../../include/dsn_buf.h smtp_reuse.o: ../../include/header_body_checks.h @@ -551,6 +561,7 @@ smtp_sasl_auth_cache.o: ../../include/deliver_request.h smtp_sasl_auth_cache.o: ../../include/dict.h smtp_sasl_auth_cache.o: ../../include/dict_proxy.h smtp_sasl_auth_cache.o: ../../include/dns.h +smtp_sasl_auth_cache.o: ../../include/domain_list.h smtp_sasl_auth_cache.o: ../../include/dsn.h smtp_sasl_auth_cache.o: ../../include/dsn_buf.h smtp_sasl_auth_cache.o: ../../include/dsn_util.h @@ -591,6 +602,7 @@ smtp_sasl_glue.o: ../../include/check_arg.h smtp_sasl_glue.o: ../../include/deliver_request.h smtp_sasl_glue.o: ../../include/dict.h smtp_sasl_glue.o: ../../include/dns.h +smtp_sasl_glue.o: ../../include/domain_list.h smtp_sasl_glue.o: ../../include/dsn.h smtp_sasl_glue.o: ../../include/dsn_buf.h smtp_sasl_glue.o: ../../include/header_body_checks.h @@ -636,6 +648,7 @@ smtp_sasl_proto.o: ../../include/check_arg.h smtp_sasl_proto.o: ../../include/deliver_request.h smtp_sasl_proto.o: ../../include/dict.h smtp_sasl_proto.o: ../../include/dns.h +smtp_sasl_proto.o: ../../include/domain_list.h smtp_sasl_proto.o: ../../include/dsn.h smtp_sasl_proto.o: ../../include/dsn_buf.h smtp_sasl_proto.o: ../../include/header_body_checks.h @@ -677,6 +690,7 @@ smtp_session.o: ../../include/debug_peer.h smtp_session.o: ../../include/deliver_request.h smtp_session.o: ../../include/dict.h smtp_session.o: ../../include/dns.h +smtp_session.o: ../../include/domain_list.h smtp_session.o: ../../include/dsn.h smtp_session.o: ../../include/dsn_buf.h smtp_session.o: ../../include/header_body_checks.h @@ -717,6 +731,7 @@ smtp_state.o: ../../include/debug_peer.h smtp_state.o: ../../include/deliver_request.h smtp_state.o: ../../include/dict.h smtp_state.o: ../../include/dns.h +smtp_state.o: ../../include/domain_list.h smtp_state.o: ../../include/dsn.h smtp_state.o: ../../include/dsn_buf.h smtp_state.o: ../../include/header_body_checks.h @@ -757,6 +772,7 @@ smtp_tls_policy.o: ../../include/ctable.h smtp_tls_policy.o: ../../include/deliver_request.h smtp_tls_policy.o: ../../include/dict.h smtp_tls_policy.o: ../../include/dns.h +smtp_tls_policy.o: ../../include/domain_list.h smtp_tls_policy.o: ../../include/dsn.h smtp_tls_policy.o: ../../include/dsn_buf.h smtp_tls_policy.o: ../../include/header_body_checks.h @@ -799,6 +815,7 @@ smtp_tlsrpt.o: ../../include/check_arg.h smtp_tlsrpt.o: ../../include/deliver_request.h smtp_tlsrpt.o: ../../include/dict.h smtp_tlsrpt.o: ../../include/dns.h +smtp_tlsrpt.o: ../../include/domain_list.h smtp_tlsrpt.o: ../../include/dsn.h smtp_tlsrpt.o: ../../include/dsn_buf.h smtp_tlsrpt.o: ../../include/header_body_checks.h @@ -843,6 +860,7 @@ smtp_trouble.o: ../../include/deliver_completed.h smtp_trouble.o: ../../include/deliver_request.h smtp_trouble.o: ../../include/dict.h smtp_trouble.o: ../../include/dns.h +smtp_trouble.o: ../../include/domain_list.h smtp_trouble.o: ../../include/dsn.h smtp_trouble.o: ../../include/dsn_buf.h smtp_trouble.o: ../../include/header_body_checks.h @@ -884,6 +902,7 @@ smtp_unalias.o: ../../include/check_arg.h smtp_unalias.o: ../../include/deliver_request.h smtp_unalias.o: ../../include/dict.h smtp_unalias.o: ../../include/dns.h +smtp_unalias.o: ../../include/domain_list.h smtp_unalias.o: ../../include/dsn.h smtp_unalias.o: ../../include/dsn_buf.h smtp_unalias.o: ../../include/header_body_checks.h diff --git a/postfix/src/smtp/lmtp_params.c b/postfix/src/smtp/lmtp_params.c index b77500326..8dfc5ace9 100644 --- a/postfix/src/smtp/lmtp_params.c +++ b/postfix/src/smtp/lmtp_params.c @@ -68,6 +68,7 @@ VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0, VAR_LMTP_TLSRPT_SOCKNAME, DEF_LMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0, + VAR_LMTP_ENFORCE_REQUIRETLS, DEF_LMTP_ENFORCE_REQUIRETLS, &var_smtp_enforce_requiretls, 0, 0, 0, }; static const CONFIG_TIME_TABLE lmtp_time_table[] = { diff --git a/postfix/src/smtp/smtp.c b/postfix/src/smtp/smtp.c index 597cc105a..fbaa452a4 100644 --- a/postfix/src/smtp/smtp.c +++ b/postfix/src/smtp/smtp.c @@ -653,6 +653,12 @@ /* .IP "\fBrequiretls_enable (yes)\fR" /* Enable support for the ESMTP verb "REQUIRETLS", defined in RFC /* 8689. +/* .IP "\fBsmtp_enforce_requiretls (empty)\fR" +/* An optional list of next-hop destinations that the Postfix +/* SMTP/LMTP client will enforce REQUIRETLS for, when a message was +/* received with the REQUIRETLS option: the next-hop server must offer +/* a matching TLS server certificate, and the server must announce +/* REQUIRETLS support). /* OBSOLETE TLS CONTROLS /* .ad /* .fi @@ -1020,6 +1026,8 @@ #include #include #include +#include +#include /* DNS library. */ @@ -1164,6 +1172,7 @@ bool var_allow_srv_fallback; bool var_smtp_tlsrpt_enable; char *var_smtp_tlsrpt_sockname; bool var_smtp_tlsrpt_skip_reused_hs; +char *var_smtp_enforce_requiretls; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; @@ -1191,6 +1200,7 @@ HBC_CHECKS *smtp_body_checks; /* limited body checks */ SMTP_CLI_ATTR smtp_cli_attr; /* parsed command-line */ int smtp_hfrom_format; /* postmaster notifications */ STRING_LIST *smtp_use_srv_lookup; +DOMAIN_LIST *smtp_enforce_requiretls; #ifdef USE_TLS @@ -1692,6 +1702,23 @@ static void pre_init(char *unused_name, char **unused_argv) if (*var_smtp_dns_re_filter) dns_rr_filter_compile(VAR_LMTP_SMTP(DNS_RE_FILTER), var_smtp_dns_re_filter); + + /* + * REQUIRETLS enforcement uses a match list. No MATCH_FLAG_RETURN after + * table lookup error, because fail-open is not a good option. We would + * have to defer all delivery requests anyway. Disable magic syntax for + * LMTP, because the syntax would interfere with UNIX-domain socket + * pathnames. + */ + if (var_requiretls_enable && *var_smtp_enforce_requiretls) { + int flags = smtp_mode ? 0 : (MATCH_FLAG_NOFILE | MATCH_FLAG_NODICT); + const char *param_name = VAR_LMTP_SMTP(ENFORCE_REQUIRETLS); + + smtp_enforce_requiretls = + string_list_init(param_name, + match_parent_style(param_name) | flags, + var_smtp_enforce_requiretls); + } } /* pre_accept - see if tables have changed */ diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 9dc8c70cf..9f36daeec 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -32,6 +32,7 @@ #include #include #include +#include /* * Postfix TLS library. @@ -200,6 +201,7 @@ typedef struct SMTP_STATE { #ifdef USE_TLSRPT struct TLSRPT_WRAPPER *tlsrpt; #endif + int enforce_requiretls; /* from smtp_enforce_requiretls */ #endif /* @@ -359,6 +361,7 @@ extern STRING_LIST *smtp_use_srv_lookup;/* services with SRV record lookup */ extern TLS_APPL_STATE *smtp_tls_ctx; /* client-side TLS engine */ extern int smtp_tls_insecure_mx_policy; /* DANE post insecure MX? */ +extern DOMAIN_LIST *smtp_enforce_requiretls; /* parsed list */ #endif diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c index 72abf67c8..c7524a69a 100644 --- a/postfix/src/smtp/smtp_connect.c +++ b/postfix/src/smtp/smtp_connect.c @@ -106,6 +106,7 @@ #include #include #include +#include /* DNS library. */ @@ -521,13 +522,24 @@ static int smtp_get_effective_tls_level(DSN_BUF *why, SMTP_STATE *state) else if (var_requiretls_enable && (state->request->sendopts & SOPT_REQUIRETLS_ESMTP)) { if (TLS_MUST_MATCH(tls->level) == 0) { - dsb_simple(why, "5.7.10", "Sender requires a TLS server " - "certificate match, but the configured %s TLS " - "security level '%s' does not support that. " - "The last attempted server was %s", - var_mail_name, str_tls_level(tls->level), - STR(iter->host)); - return (0); + if (state->enforce_requiretls) { + dsb_simple(why, "5.7.10", "REQUIRETLS Failure: Sender " + "requires a TLS server certificate match, " + "but the configured %s TLS security level '%s' " + "does not support that. The last attempted " + "server was %s", + var_mail_name, str_tls_level(tls->level), + STR(iter->host)); + return (0); + } else { + msg_info("REQUIRETLS Debug: Sender requires a TLS server " + "certificate match, but the configured %s TLS " + "security level '%s' does not support that. " + "The last attempted server was %s", + var_mail_name, str_tls_level(tls->level), + STR(iter->host)); + return (0); + } } } @@ -576,6 +588,18 @@ static void smtp_connect_local(SMTP_STATE *state, const char *path) if (state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE_MASK) SET_SCACHE_REQUEST_NEXTHOP(state, path); + /* + * REQUIRETLS enforcement is based on the UNIX-domain pathname, without + * the "unix:" prefix. + */ +#ifdef USE_TLS + state->enforce_requiretls = + (var_requiretls_enable + && smtp_enforce_requiretls + && (state->request->sendopts & SOPT_REQUIRETLS_ESMTP) != 0 + && domain_list_match(smtp_enforce_requiretls, path)); +#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 @@ -980,6 +1004,18 @@ static void smtp_connect_inet(SMTP_STATE *state, const char *nexthop, state->tlsrpt = 0; #endif /* USE_TLSRPT */ + /* + * REQUIRETLS enforcement is based on the next-hop domain name + * without the service or port. + */ +#ifdef USE_TLS + state->enforce_requiretls = + (var_requiretls_enable + && smtp_enforce_requiretls + && (state->request->sendopts & SOPT_REQUIRETLS_ESMTP) != 0 + && domain_list_match(smtp_enforce_requiretls, domain)); +#endif + /* * Resolve an SMTP or LMTP server. Skip MX or SRV lookups when a * quoted domain is specified or when DNS lookups are disabled. diff --git a/postfix/src/smtp/smtp_params.c b/postfix/src/smtp/smtp_params.c index 6d80ef105..65940a61a 100644 --- a/postfix/src/smtp/smtp_params.c +++ b/postfix/src/smtp/smtp_params.c @@ -69,6 +69,7 @@ VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, VAR_USE_SRV_LOOKUP, DEF_USE_SRV_LOOKUP, &var_use_srv_lookup, 0, 0, VAR_SMTP_TLSRPT_SOCKNAME, DEF_SMTP_TLSRPT_SOCKNAME, &var_smtp_tlsrpt_sockname, 0, 0, + VAR_SMTP_ENFORCE_REQUIRETLS, DEF_SMTP_ENFORCE_REQUIRETLS, &var_smtp_enforce_requiretls, 0, 0, 0, }; static const CONFIG_TIME_TABLE smtp_time_table[] = { diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 1c7ca195b..2f71a926a 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -693,15 +693,24 @@ int smtp_helo(SMTP_STATE *state) if (var_requiretls_enable && (request->sendopts & SOPT_REQUIRETLS_ESMTP) != 0 && (state->misc_flags & SMTP_MISC_FLAG_IN_STARTTLS) != 0 - && (session->features & SMTP_FEATURE_REQUIRETLS) == 0) - return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, - DSN_BY_LOCAL_MTA, - SMTP_RESP_FAKE(&fake, "5.7.30"), - "Sender requested delivery wth " - "REQUIRETLS, but no mail server was " - "found with REQUIRETLS support. The " - "last attempted server was %s", - session->namaddr)); + && (session->features & SMTP_FEATURE_REQUIRETLS) == 0) { + if (state->enforce_requiretls) { + return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, + DSN_BY_LOCAL_MTA, + SMTP_RESP_FAKE(&fake, "5.7.30"), + "REQUIRETLS Failure: Sender requested " + "delivery wth REQUIRETLS, but no mail " + "server was found with REQUIRETLS " + "support. The last attempted server " + "was %s", session->namaddr)); + } else { + msg_info("REQUIRETLS Debug: Sender requested delivery wth " + "REQUIRETLS, but no mail server was found with " + "REQUIRETLS support. The last attempted server was " + "%s", session->namaddr); + } + + } #endif /* @@ -1221,14 +1230,22 @@ static int smtp_start_tls(SMTP_STATE *state) */ if (var_requiretls_enable && (state->request->sendopts & SOPT_REQUIRETLS_ESMTP)) { - return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, - DSN_BY_LOCAL_MTA, - SMTP_RESP_FAKE(&fake, "5.7.10"), - "Sender requires a TLS server " - "certificate match, but no matching " - "mail server was found. The last " - "attempted server was %s", - session->namaddr)); + if (state->enforce_requiretls) { + return (smtp_misc_fail(state, SMTP_MISC_FAIL_SOFT_NON_FINAL, + DSN_BY_LOCAL_MTA, + SMTP_RESP_FAKE(&fake, "5.7.10"), + "REQUIRETLS Failure: Sender " + "requested a TLS server " + "certificate match, but no " + "match was found. The last " + "attempted server was %s", + session->namaddr)); + } else { + msg_info("REQUIRETLS Debug: Sender requested a TLS " + "server certificate match, but no match was " + "found. The last attempted server was %s", + session->namaddr); + } } return (smtp_site_fail(state, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "4.7.5"), @@ -1830,8 +1847,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, && (request->sendopts & SOPT_REQUIRETLS_ESMTP) != 0) { if ((session->features & SMTP_FEATURE_REQUIRETLS) != 0) vstring_strcat(next_command, " REQUIRETLS"); - else if ((request->sendopts & SOPT_REQUIRETLS_ESMTP) != 0) - msg_panic("Can't happen: message requires REQUIRETLS, but " + else if (state->enforce_requiretls) + msg_panic("Can't happen: must enforce REQUIRETLS, but " "host %s did not announce REQUIRETLS support", session->namaddr); } diff --git a/postfix/src/smtp/smtp_state.c b/postfix/src/smtp/smtp_state.c index ec8cc2df3..42f7dc7ba 100644 --- a/postfix/src/smtp/smtp_state.c +++ b/postfix/src/smtp/smtp_state.c @@ -82,6 +82,9 @@ SMTP_STATE *smtp_state_alloc(void) state->iterator->saved_dest = vstring_alloc(100); #ifdef USE_TLSRPT state->tlsrpt = 0; +#endif +#ifdef USE_TLS + state->enforce_requiretls = 0; #endif if (var_smtp_cache_conn) { state->dest_label = vstring_alloc(10); diff --git a/postfix/src/util/binhash.c b/postfix/src/util/binhash.c index fd9bc8779..0383a899a 100644 --- a/postfix/src/util/binhash.c +++ b/postfix/src/util/binhash.c @@ -346,8 +346,7 @@ void binhash_walk(BINHASH *table, void (*action) (BINHASH_INFO *, void *), /* binhash_list - list all table members */ -BINHASH_INFO **binhash_list(table) -BINHASH *table; +BINHASH_INFO **binhash_list(BINHASH *table) { BINHASH_INFO **list; BINHASH_INFO *member; diff --git a/postfix/src/util/close_on_exec.c b/postfix/src/util/close_on_exec.c index efa341538..afa49b873 100644 --- a/postfix/src/util/close_on_exec.c +++ b/postfix/src/util/close_on_exec.c @@ -46,9 +46,7 @@ /* close_on_exec - set/clear close-on-exec flag */ -int close_on_exec(fd, on) -int fd; -int on; +int close_on_exec(int fd, int on) { int flags; diff --git a/postfix/src/util/match_list.c b/postfix/src/util/match_list.c index fa533ba65..01dd37df2 100644 --- a/postfix/src/util/match_list.c +++ b/postfix/src/util/match_list.c @@ -60,6 +60,10 @@ /* Request that match_list_match() logs a warning and returns /* zero (with list->error set to a non-zero dictionary error /* code) instead of raising a fatal run-time error. +/* .IP MATCH_FLAG_NOFILE +/* Disable special handling for /file/name. +/* .IP MATCH_FLAG_NODICT +/* Disable special handling for type:name. /* .RE /* Specify MATCH_FLAG_NONE to request none of the above. /* .IP pattern_list @@ -84,6 +88,9 @@ /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* System library. */ @@ -152,7 +159,8 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list, if (*item == 0) /* No graceful degradation for this... */ msg_fatal("%s: no pattern after '!'", match_list->pname); - if (*item == '/') { /* /file/name */ + if (*item == '/' /* /file/name */ + && (match_list->flags & MATCH_FLAG_NOFILE) == 0) { if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) { /* Replace unusable pattern with pseudo table. */ vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item); @@ -169,7 +177,8 @@ static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list, if (vstream_fclose(fp)) msg_fatal("%s: read file %s: %m", myname, item); } - } else if (MATCH_DICTIONARY(item)) { /* type:table */ + } else if (MATCH_DICTIONARY(item) /* type:table */ + &&(match_list->flags & MATCH_FLAG_NODICT) == 0) { vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!", item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS)); map_type_name_flags = STR(buf) + (match == 0); diff --git a/postfix/src/util/match_list.h b/postfix/src/util/match_list.h index d8b779430..e07d273eb 100644 --- a/postfix/src/util/match_list.h +++ b/postfix/src/util/match_list.h @@ -38,8 +38,10 @@ struct MATCH_LIST { #define MATCH_FLAG_NONE 0 #define MATCH_FLAG_PARENT (1<<0) #define MATCH_FLAG_RETURN (1<<1) -#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT | MATCH_FLAG_RETURN) - +#define MATCH_FLAG_NOFILE (1<<2) +#define MATCH_FLAG_NODICT (1<<3) +#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT | MATCH_FLAG_RETURN | \ + MATCH_FLAG_NOFILE | MATCH_FLAG_NODICT) extern MATCH_LIST *match_list_init(const char *, int, const char *, int,...); extern int match_list_match(MATCH_LIST *,...); extern void match_list_free(MATCH_LIST *); diff --git a/postfix/src/util/non_blocking.c b/postfix/src/util/non_blocking.c index 6427cd80f..2343814eb 100644 --- a/postfix/src/util/non_blocking.c +++ b/postfix/src/util/non_blocking.c @@ -52,9 +52,7 @@ /* non_blocking - set/clear non-blocking flag */ -int non_blocking(fd, on) -int fd; -int on; +int non_blocking(int fd, int on) { int flags; diff --git a/postfix/src/util/ring.c b/postfix/src/util/ring.c index d4c5f82ae..9933c4cc0 100644 --- a/postfix/src/util/ring.c +++ b/postfix/src/util/ring.c @@ -76,17 +76,14 @@ /* ring_init - initialize ring head */ -void ring_init(ring) -RING *ring; +void ring_init(RING *ring) { ring->pred = ring->succ = ring; } /* ring_append - insert entry after ring head */ -void ring_append(ring, entry) -RING *ring; -RING *entry; +void ring_append(RING *ring, RING *entry) { entry->succ = ring->succ; entry->pred = ring; @@ -96,9 +93,7 @@ RING *entry; /* ring_prepend - insert new entry before ring head */ -void ring_prepend(ring, entry) -RING *ring; -RING *entry; +void ring_prepend(RING *ring, RING *entry) { entry->pred = ring->pred; entry->succ = ring; @@ -108,8 +103,7 @@ RING *entry; /* ring_detach - remove entry from ring */ -void ring_detach(entry) -RING *entry; +void ring_detach(RING *entry) { RING *succ = entry->succ; RING *pred = entry->pred;