Prereq: "3.10.4" diff -ur --new-file /var/tmp/postfix-3.10.4/src/global/mail_version.h ./src/global/mail_version.h --- /var/tmp/postfix-3.10.4/src/global/mail_version.h 2025-08-18 15:55:36.000000000 -0400 +++ ./src/global/mail_version.h 2025-10-26 18:48:02.000000000 -0400 @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20250818" -#define MAIL_VERSION_NUMBER "3.10.4" +#define MAIL_RELEASE_DATE "20251026" +#define MAIL_VERSION_NUMBER "3.10.5" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -ur --new-file /var/tmp/postfix-3.10.4/HISTORY ./HISTORY --- /var/tmp/postfix-3.10.4/HISTORY 2025-08-18 18:44:50.000000000 -0400 +++ ./HISTORY 2025-10-24 11:06:14.000000000 -0400 @@ -29122,3 +29122,89 @@ messages that contain a "TLS-Required: no" header. This can prevent TLSRPT notifications for TLSRPT notifications. Files: smtp/smtp_connect.c, smtp_tls_policy.c. + +20250816 + + Bugfix (defect introduced: Postfix 3.0, date 20140731): the + smtpd 'disconnect' command counts did not count malformed + commands with "bad syntax" and "bad UTF-8 syntax" errors. + File: smtpd/smtpd.c. + +20250819 + + Bugfix: the 20250717 workaround broke DBM library support + which is still needed on Solaris. File: util/dict_dbm.c. + +20250823 + + Bugfix (defect introduced: Postfix 3.9, date 20230517): + posttls-finger logged a zero port number. Viktor Dukhovni. + File: posttls-finger/posttls-finger.c. + +20250829 + + Postfix 3.11 forward compatibility: allow a partial 'size' + record in maildrop queue files created with Postfix 3.11 + or later, instead of logging an ugly warning. Files: + showq/showq.c, postcat/postcat.c. + +20250906 + + Workaround for an interface mis-match between the Postfix + SMTP client and MTA-STS policy plugins. This introduces a + new parameter "smtp_tls_enforce_sts_mx_patterns" (default: + "yes"). The MTA-STS plugin configuration needs to enable + TLSRPT support, so that it forwards STS policy attributes + to Postfix. This works even if Postfix TLSRPT support is + disabled at build time or at runtime. + + With the above two configurations, the Postfix SMTP client + will connect to an MX host only if its name matches any STS + policy MX host pattern, and will match a server certificate + against the MX hostname. Otherwise, the old behavior stays + in effect: connect to any MX host listed in DNS, and match + a server certificate against any STS policy MX host pattern. + Files: mantools/postlink, proto/postconf.proto, + global/mail_params.h, smtp/lmtp_params.c, smtp/smtp.c, + smtp/smtp.h, smtp/smtp_connect.c, smtp/smtp_params.c, + smtp/smtp_tls_policy.c. + +20250911 + + Bugfix (defect introduced: Postfix 3.0): the Postfix SMTP + client's connection reuse logic did not distinguish between + sessions that require SMTPUTF8 support, and sessions that + do not. The solution is to store sessions with different + SMTPUTF8 requirements under distinct connection cache storage + keys, and to preserve the availability of SMTPUTF8 support + in the connection cache, so that a reused connection will + be stored under the same keys as it was looked up with. + Finally, do not cache a connection when SMTPUTF8 is + required but the server does not support that feature. + Files: smtp/smtp.h, smtp/smtp_key.c, smtp/smtp_proto.c. + +20250919 + + Bugfix (defect introduced: Postfix 3.8, date 20220128): the + 'postconf -e' output order for new main.cf entries was no + longer deterministic. Problem reported by Oleksandr Natalenko, + diagnosis by Eray Aslan. File: postconf/postconf_edit.c. + + Add missing meta_directory and shlib_directory settings to + the stock main.cf file. Problem diagnosed by Eray Aslan. + File: conf/main.cf. + +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. + +20251021 + + Cleanup: the change at 20250717 could result in warnings + with "database X is older than source file Y". Files: + util/dict.c, util/dict_db.c, util/dict_dbm.c, util/dict_lmdb.c, + util/dict_sdbm.c. diff -ur --new-file /var/tmp/postfix-3.10.4/conf/main.cf ./conf/main.cf --- /var/tmp/postfix-3.10.4/conf/main.cf 2024-11-21 12:49:05.000000000 -0500 +++ ./conf/main.cf 2025-10-23 15:51:44.000000000 -0400 @@ -683,3 +683,5 @@ # readme_directory = inet_protocols = ipv4 +shlib_directory = /usr/lib/postfix/${mail_version} +meta_directory = /etc/postfix diff -ur --new-file /var/tmp/postfix-3.10.4/html/lmtp.8.html ./html/lmtp.8.html --- /var/tmp/postfix-3.10.4/html/lmtp.8.html 2025-02-15 15:21:59.000000000 -0500 +++ ./html/lmtp.8.html 2025-10-26 18:56:06.000000000 -0400 @@ -766,6 +766,13 @@ Enable support for the "TLS-Required: no" message header, defined in RFC 8689. + Available in Postfix version 3.10.5 and later: + + smtp_tls_enforce_sts_mx_patterns (yes) + Transform the TLS policy from an STS policy plugin: connect to + an MX host only if its name matches any STS policy MX host pat- + tern, and match the server certificate against the MX hostname. + OBSOLETE STARTTLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff -ur --new-file /var/tmp/postfix-3.10.4/html/postconf.5.html ./html/postconf.5.html --- /var/tmp/postfix-3.10.4/html/postconf.5.html 2025-02-17 12:57:52.000000000 -0500 +++ ./html/postconf.5.html 2025-10-26 18:52:23.000000000 -0400 @@ -13502,6 +13502,27 @@ +
smtp_tls_enforce_sts_mx_patterns +(default: yes)
+ +

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. This +setting takes effect only when an STS policy plugin has TLSRPT +support enabled, so that it forwards STS policy attributes to +Postfix. This works even if Postfix TLSRPT support is disabled at +build time or at runtime.

+ +

Without the above configuration settings for Postfix and STS +plugins, the old behavior stays in effect: connect to any MX host +listed in DNS, and match a server certificate against any STS policy +MX host pattern.

+ +

This feature is available in Postfix ≥ 3.10.5.

+ + +
+
smtp_tls_exclude_ciphers (default: empty)
diff -ur --new-file /var/tmp/postfix-3.10.4/html/smtp.8.html ./html/smtp.8.html --- /var/tmp/postfix-3.10.4/html/smtp.8.html 2025-02-15 15:21:59.000000000 -0500 +++ ./html/smtp.8.html 2025-10-26 18:56:06.000000000 -0400 @@ -766,6 +766,13 @@ Enable support for the "TLS-Required: no" message header, defined in RFC 8689. + Available in Postfix version 3.10.5 and later: + + smtp_tls_enforce_sts_mx_patterns (yes) + Transform the TLS policy from an STS policy plugin: connect to + an MX host only if its name matches any STS policy MX host pat- + tern, and match the server certificate against the MX hostname. + OBSOLETE STARTTLS CONTROLS The following configuration parameters exist for compatibility with Postfix versions before 2.3. Support for these will be removed in a diff -ur --new-file /var/tmp/postfix-3.10.4/man/man5/postconf.5 ./man/man5/postconf.5 --- /var/tmp/postfix-3.10.4/man/man5/postconf.5 2025-02-17 12:57:52.000000000 -0500 +++ ./man/man5/postconf.5 2025-10-26 18:52:23.000000000 -0400 @@ -8664,6 +8664,21 @@ .PP This feature is available in Postfix 2.2 and later. With Postfix 2.3 and later use smtp_tls_security_level instead. +.SH smtp_tls_enforce_sts_mx_patterns (default: yes) +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. This +setting takes effect only when an STS policy plugin has TLSRPT +support enabled, so that it forwards STS policy attributes to +Postfix. This works even if Postfix TLSRPT support is disabled at +build time or at runtime. +.PP +Without the above configuration settings for Postfix and STS +plugins, the old behavior stays in effect: connect to any MX host +listed in DNS, and match a server certificate against any STS policy +MX host pattern. +.PP +This feature is available in Postfix >= 3.10.5. .SH smtp_tls_exclude_ciphers (default: empty) List of ciphers or cipher types to exclude from the Postfix SMTP client cipher diff -ur --new-file /var/tmp/postfix-3.10.4/man/man8/smtp.8 ./man/man8/smtp.8 --- /var/tmp/postfix-3.10.4/man/man8/smtp.8 2025-02-15 15:21:58.000000000 -0500 +++ ./man/man8/smtp.8 2025-10-26 18:52:23.000000000 -0400 @@ -688,6 +688,13 @@ .IP "\fBtls_required_enable (yes)\fR" Enable support for the "TLS\-Required: no" message header, defined in RFC 8689. +.PP +Available in Postfix version 3.10.5 and later: +.IP "\fBsmtp_tls_enforce_sts_mx_patterns (yes)\fR" +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. +.PP .SH "OBSOLETE STARTTLS CONTROLS" .na .nf diff -ur --new-file /var/tmp/postfix-3.10.4/mantools/postlink ./mantools/postlink --- /var/tmp/postfix-3.10.4/mantools/postlink 2025-02-05 17:04:14.000000000 -0500 +++ ./mantools/postlink 2025-10-24 10:41:15.000000000 -0400 @@ -728,6 +728,7 @@ s;\blmtp_tlsrpt_enable\b;$&;g; s;\blmtp_tlsrpt_socket_name\b;$&;g; s;\blmtp_tlsrpt_skip_reused_handshakes\b;$&;g; + s;\bsmtp_tls_enforce_sts_mx_patterns\b;$&;g; s;\bsmtpd_enforce_tls\b;$&;g; s;\bsmtpd_sasl_tls_security_options\b;$&;g; s;\bsmtpd_sasl_type\b;$&;g; diff -ur --new-file /var/tmp/postfix-3.10.4/proto/postconf.proto ./proto/postconf.proto --- /var/tmp/postfix-3.10.4/proto/postconf.proto 2025-02-17 11:55:50.000000000 -0500 +++ ./proto/postconf.proto 2025-10-24 10:41:15.000000000 -0400 @@ -19496,6 +19496,23 @@

This feature is available in Postfix ≥ 3.10.

+%PARAM smtp_tls_enforce_sts_mx_patterns yes + +

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. This +setting takes effect only when an STS policy plugin has TLSRPT +support enabled, so that it forwards STS policy attributes to +Postfix. This works even if Postfix TLSRPT support is disabled at +build time or at runtime.

+ +

Without the above configuration settings for Postfix and STS +plugins, the old behavior stays in effect: connect to any MX host +listed in DNS, and match a server certificate against any STS policy +MX host pattern.

+ +

This feature is available in Postfix ≥ 3.10.5.

+ %PARAM full_name_encoding_charset utf-8

The character set name (also called "charset") that Postfix diff -ur --new-file /var/tmp/postfix-3.10.4/src/global/mail_params.h ./src/global/mail_params.h --- /var/tmp/postfix-3.10.4/src/global/mail_params.h 2025-08-18 16:06:41.000000000 -0400 +++ ./src/global/mail_params.h 2025-10-24 10:41:15.000000000 -0400 @@ -4492,6 +4492,12 @@ #define DEF_LMTP_TLSRPT_SKIP_REUSED_HS DEF_SMTP_TLSRPT_SKIP_REUSED_HS extern int var_smtp_tlsrpt_skip_reused_hs; +#define VAR_SMTP_TLS_ENF_STS_MX_PAT "smtp_tls_enforce_sts_mx_patterns" +#define DEF_SMTP_TLS_ENF_STS_MX_PAT "yes" +#define VAR_LMTP_TLS_ENF_STS_MX_PAT "lmtp_tls_enforce_sts_mx_patterns" +#define DEF_LMTP_TLS_ENF_STS_MX_PAT "yes" +extern bool var_smtp_tls_enf_sts_mx_pat; + /* * RFC 2047 encoding of full name info. */ diff -ur --new-file /var/tmp/postfix-3.10.4/src/postcat/postcat.c ./src/postcat/postcat.c --- /var/tmp/postfix-3.10.4/src/postcat/postcat.c 2024-09-25 10:27:30.000000000 -0400 +++ ./src/postcat/postcat.c 2025-10-23 15:58:17.000000000 -0400 @@ -348,6 +348,10 @@ /* 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"); diff -ur --new-file /var/tmp/postfix-3.10.4/src/postconf/postconf_edit.c ./src/postconf/postconf_edit.c --- /var/tmp/postfix-3.10.4/src/postconf/postconf_edit.c 2025-04-21 16:54:07.000000000 -0400 +++ ./src/postconf/postconf_edit.c 2025-10-23 16:03:49.000000000 -0400 @@ -66,6 +66,7 @@ /* System library. */ #include +#include #include #include @@ -148,6 +149,16 @@ } } +/* pcf_cmp_ht_key - qsort helper for ht_info pointer array */ + +static int pcf_cmp_ht_key(const void *a, const void *b) +{ + HTABLE_INFO **ap = (HTABLE_INFO **) a; + HTABLE_INFO **bp = (HTABLE_INFO **) b; + + return (strcmp(ap[0]->key, bp[0]->key)); +} + /* pcf_edit_main - edit main.cf file */ void pcf_edit_main(int mode, int argc, char **argv) @@ -262,7 +273,9 @@ * Generate new entries for parameters that were not found. */ if (mode & PCF_EDIT_CONF) { - for (ht_info = ht = htable_list(table); *ht; ht++) { + ht_info = htable_list(table); + qsort((void *) ht_info, table->used, sizeof(*ht_info), pcf_cmp_ht_key); + for (ht = ht_info; *ht; ht++) { cvalue = (struct cvalue *) ht[0]->value; if (cvalue->found == 0) vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value); diff -ur --new-file /var/tmp/postfix-3.10.4/src/posttls-finger/posttls-finger.c ./src/posttls-finger/posttls-finger.c --- /var/tmp/postfix-3.10.4/src/posttls-finger/posttls-finger.c 2025-08-18 15:56:40.000000000 -0400 +++ ./src/posttls-finger/posttls-finger.c 2025-10-23 12:47:49.000000000 -0400 @@ -1651,7 +1651,7 @@ if (level == TLS_LEV_INVALID || (state->stream = connect_addr(state, addr)) == 0) { msg_info("Failed to establish session to %s via %s:%u: %s", - dest, HNAME(addr), addr->port, + dest, HNAME(addr), ntohs(state->port), vstring_str(state->why->reason)); continue; } diff -ur --new-file /var/tmp/postfix-3.10.4/src/showq/showq.c ./src/showq/showq.c --- /var/tmp/postfix-3.10.4/src/showq/showq.c 2022-02-20 17:27:31.000000000 -0500 +++ ./src/showq/showq.c 2025-10-23 15:58:17.000000000 -0400 @@ -213,7 +213,8 @@ arrival_time = atol(start); break; case REC_TYPE_SIZE: - if (msg_size_ok == 0) { + /* Postfix 3.11 maildrop files may have 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) { diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/Makefile.in ./src/smtp/Makefile.in --- /var/tmp/postfix-3.10.4/src/smtp/Makefile.in 2025-08-18 18:01:52.000000000 -0400 +++ ./src/smtp/Makefile.in 2025-10-24 10:41:15.000000000 -0400 @@ -307,6 +307,7 @@ smtp_key.o: ../../include/resolve_clnt.h smtp_key.o: ../../include/scache.h smtp_key.o: ../../include/sendopts.h +smtp_key.o: ../../include/smtputf8.h smtp_key.o: ../../include/sock_addr.h smtp_key.o: ../../include/string_list.h smtp_key.o: ../../include/sys_defs.h @@ -777,6 +778,7 @@ smtp_tls_policy.o: ../../include/mail_params.h smtp_tls_policy.o: ../../include/maps.h smtp_tls_policy.o: ../../include/match_list.h +smtp_tls_policy.o: ../../include/midna_domain.h smtp_tls_policy.o: ../../include/mime_state.h smtp_tls_policy.o: ../../include/msg.h smtp_tls_policy.o: ../../include/msg_stats.h diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/lmtp_params.c ./src/smtp/lmtp_params.c --- /var/tmp/postfix-3.10.4/src/smtp/lmtp_params.c 2024-09-27 12:18:47.000000000 -0400 +++ ./src/smtp/lmtp_params.c 2025-10-24 10:48:29.000000000 -0400 @@ -140,5 +140,8 @@ VAR_LMTP_REQ_DEADLINE, DEF_LMTP_REQ_DEADLINE, &var_smtp_req_deadline, VAR_LMTP_TLSRPT_ENABLE, DEF_LMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable, VAR_LMTP_TLSRPT_SKIP_REUSED_HS, DEF_LMTP_TLSRPT_SKIP_REUSED_HS, &var_smtp_tlsrpt_skip_reused_hs, +#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 0, }; diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp.c ./src/smtp/smtp.c --- /var/tmp/postfix-3.10.4/src/smtp/smtp.c 2025-02-15 15:21:58.000000000 -0500 +++ ./src/smtp/smtp.c 2025-10-24 10:41:15.000000000 -0400 @@ -654,6 +654,13 @@ /* .IP "\fBtls_required_enable (yes)\fR" /* Enable support for the "TLS-Required: no" message header, defined /* in RFC 8689. +/* .PP +/* Available in Postfix version 3.10.5 and later: +/* .IP "\fBsmtp_tls_enforce_sts_mx_patterns (yes)\fR" +/* 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. +/* .PP /* OBSOLETE STARTTLS CONTROLS /* .ad /* .fi @@ -1133,6 +1140,7 @@ bool var_smtp_tls_force_tlsa; char *var_smtp_tls_insecure_mx_policy; bool var_smtp_tls_enable_rpk; +bool var_smtp_tls_enf_sts_mx_pat; #endif diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp.h ./src/smtp/smtp.h --- /var/tmp/postfix-3.10.4/src/smtp/smtp.h 2025-08-18 18:01:52.000000000 -0400 +++ ./src/smtp/smtp.h 2025-10-24 10:41:15.000000000 -0400 @@ -137,6 +137,7 @@ extern void smtp_tls_list_init(void); extern int smtp_tls_policy_cache_query(DSN_BUF *, SMTP_TLS_POLICY *, SMTP_ITERATOR *); extern void smtp_tls_policy_cache_flush(void); +extern int smtp_tls_authorize_mx_hostname(SMTP_TLS_POLICY *, const char *); /* * Macros must use distinct names for local temporary variables, otherwise @@ -691,12 +692,14 @@ #define SMTP_KEY_FLAG_ADDR (1<<5) /* remote address */ #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_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_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL | \ + SMTP_KEY_FLAG_REQ_SMTPUTF8) /* * Conditional lookup-key flags for cached connections that may be @@ -735,7 +738,8 @@ */ #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_NEXTHOP | SMTP_KEY_FLAG_TLS_LEVEL \ + | SMTP_KEY_FLAG_REQ_SMTPUTF8) /* * Connection-cache endpoint lookup key. The SENDER, CUR_NEXTHOP, HOSTNAME, @@ -750,7 +754,8 @@ | COND_SASL_SMTP_KEY_FLAG_CUR_NEXTHOP \ | 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_PORT | SMTP_KEY_FLAG_TLS_LEVEL \ + | SMTP_KEY_FLAG_REQ_SMTPUTF8) /* * Silly little macros. diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp_connect.c ./src/smtp/smtp_connect.c --- /var/tmp/postfix-3.10.4/src/smtp/smtp_connect.c 2025-08-18 18:01:52.000000000 -0400 +++ ./src/smtp/smtp_connect.c 2025-10-24 10:41:15.000000000 -0400 @@ -1124,6 +1124,11 @@ continue; /* XXX Assume there is no code at the end of this loop. */ } + /* Skip MX hosts that lack authorization. */ + if (!smtp_tls_authorize_mx_hostname(state->tls, SMTP_HNAME(addr))) { + continue; + /* XXX Assume there is no code at the end of this loop. */ + } /* Disable TLS when retrying after a handshake failure */ if (retry_plain) { state->tls->level = TLS_LEV_NONE; diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp_key.c ./src/smtp/smtp_key.c --- /var/tmp/postfix-3.10.4/src/smtp/smtp_key.c 2018-11-20 07:25:40.000000000 -0500 +++ ./src/smtp/smtp_key.c 2025-10-23 14:17:45.000000000 -0400 @@ -65,6 +65,10 @@ /* The current iterator's remote address. /* .IP SMTP_KEY_FLAG_PORT /* The current iterator's remote port. +/* .IP SMTP_KEY_FLAG_TLS_LEVEL +/* The requested TLS security level. +/* .IP SMTP_KEY_FLAG_REQ_SMTPUTF8 +/* Whether SMTPUTF8 support is required. /* .RE /* DIAGNOSTICS /* Panic: undefined flag or zero flags. Fatal: out of memory. @@ -103,12 +107,18 @@ * Global library. */ #include +#include /* * Application-specific. */ #include + /* Duplicated to minimze patch footprint. */ +#define DELIVERY_REQUIRES_SMTPUTF8(request) \ + (((request)->sendopts & SMTPUTF8_FLAG_REQUESTED) \ + && ((request)->sendopts & SMTPUTF8_FLAG_DERIVED)) + /* * We use a configurable field terminator and optional place holder for data * that is unavailable or inapplicable. We base64-encode content that @@ -209,6 +219,20 @@ 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 + * cache for a connection that is known to support it? No, because the + * connection would be saved back under a key that does not require + * SMTPUTF8 support. + */ + if (flags & SMTP_KEY_FLAG_REQ_SMTPUTF8) + smtp_key_append_uint(buffer, + DELIVERY_REQUIRES_SMTPUTF8(state->request), + delim_na); + else + smtp_key_append_na(buffer, delim_na); + VSTRING_TERMINATE(buffer); return STR(buffer); diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp_params.c ./src/smtp/smtp_params.c --- /var/tmp/postfix-3.10.4/src/smtp/smtp_params.c 2024-09-27 12:18:46.000000000 -0400 +++ ./src/smtp/smtp_params.c 2025-10-24 10:48:03.000000000 -0400 @@ -144,5 +144,8 @@ VAR_SMTP_REQ_DEADLINE, DEF_SMTP_REQ_DEADLINE, &var_smtp_req_deadline, VAR_SMTP_TLSRPT_ENABLE, DEF_SMTP_TLSRPT_ENABLE, &var_smtp_tlsrpt_enable, VAR_SMTP_TLSRPT_SKIP_REUSED_HS, DEF_SMTP_TLSRPT_SKIP_REUSED_HS, &var_smtp_tlsrpt_skip_reused_hs, +#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 0, }; diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp_proto.c ./src/smtp/smtp_proto.c --- /var/tmp/postfix-3.10.4/src/smtp/smtp_proto.c 2025-07-09 17:21:02.000000000 -0400 +++ ./src/smtp/smtp_proto.c 2025-10-23 14:17:45.000000000 -0400 @@ -658,14 +658,19 @@ * SMTPUTF8. * * Fix 20140706: moved this before negotiating TLS, AUTH, and so on. + * + * 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) + && DELIVERY_REQUIRES_SMTPUTF8) { + DONT_CACHE_THIS_SESSION; return (smtp_mesg_fail(state, DSN_BY_LOCAL_MTA, SMTP_RESP_FAKE(&fake, "5.6.7"), "SMTPUTF8 is required, " "but was not offered by host %s", session->namaddr)); + } /* * Fix 20140706: don't do silly things when the remote server announces diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtp/smtp_tls_policy.c ./src/smtp/smtp_tls_policy.c --- /var/tmp/postfix-3.10.4/src/smtp/smtp_tls_policy.c 2025-08-18 18:01:52.000000000 -0400 +++ ./src/smtp/smtp_tls_policy.c 2025-10-24 10:41:15.000000000 -0400 @@ -17,6 +17,10 @@ /* SMTP_TLS_POLICY *tls; /* /* void smtp_tls_policy_cache_flush() +/* +/* int smtp_tls_authorize_mx_hostname(tls, qname) +/* SMTP_TLS_POLICY *tls; +/* const char *qname; /* DESCRIPTION /* smtp_tls_list_init() initializes lookup tables used by the TLS /* policy engine. @@ -31,6 +35,11 @@ /* When any required table or DNS lookups fail, the TLS level /* is set to TLS_LEV_INVALID, the "why" argument is updated /* with the error reason and the result value is zero (false). +/* When var_smtp_tls_enf_sts_mx_pat is not null, and a policy plugin +/* specifies a policy_type "sts" plus one or more mx_host_pattern +/* instances, transform the policy as follows: allow only MX hosts +/* that an match mx_host_pattern instance, and match a server +/* certificate against the server hostname. /* /* smtp_tls_policy_dummy() initializes a trivial, non-cached, /* policy with TLS disabled. @@ -38,6 +47,11 @@ /* smtp_tls_policy_cache_flush() destroys the TLS policy cache /* and contents. /* +/* smtp_tls_authorize_mx_hostname() authorizes an MX host if the +/* name used for host lookup satisfies a TLS policy MX name +/* constraint (for example, an STS policy MX pattern), or if the +/* TLS policy has no name constraint. +/* /* Arguments: /* .IP why /* A pointer to a DSN_BUF which holds error status information when @@ -107,6 +121,7 @@ #include #include #include +#include /* Global library. */ @@ -136,6 +151,51 @@ static MAPS *tls_policy; /* lookup table(s) */ static MAPS *tls_per_site; /* lookup table(s) */ +/* match_sts_mx_host_pattern - match hostname against STS policy MX pattern */ + +static int match_sts_mx_host_pattern(const char *pattern, const char *qname) +{ + const char *first_dot_in_qname; + + /* Caller guarantees that inputs are in ASCII form. */ + return (strcasecmp(qname, pattern) == 0 + || (pattern[0] == '*' && pattern[1] == '.' && pattern[2] != 0 + && (first_dot_in_qname = strchr(qname, '.')) != 0 + && first_dot_in_qname > qname + && strcasecmp(first_dot_in_qname + 1, pattern + 2) == 0)); +} + +/* smtp_tls_authorize_mx_hostname - enforce applicable MX hostname policy */ + +int smtp_tls_authorize_mx_hostname(SMTP_TLS_POLICY *tls, const char *name) +{ + +#define SAFE_FOR_SMTP_TLS_ENF_STS_MX_PAT(tls) (var_smtp_tls_enf_sts_mx_pat \ + && (tls)->ext_policy_type != 0 \ + && strcasecmp((tls)->ext_policy_type, "sts") == 0 \ + && (tls)->matchargv != 0 && (tls)->ext_mx_host_patterns != 0) + + /* Enforce STS policy MX patterns. */ + if (SAFE_FOR_SMTP_TLS_ENF_STS_MX_PAT(tls)) { + const char *aname; + char **pattp; + +#ifndef NO_EAI + if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) { + if (msg_verbose) + msg_info("%s asciified to %s", name, aname); + } else +#endif + aname = name; + for (pattp = tls->ext_mx_host_patterns->argv; *pattp; pattp++) + if (match_sts_mx_host_pattern(*pattp, aname)) + return (1); + return (0); + } + /* No applicable policy name patterns. */ + return (1); +} + /* smtp_tls_list_init - initialize per-site policy lists */ void smtp_tls_list_init(void) @@ -517,6 +577,10 @@ INVALID_RETURN(tls->why, site_level); } } + if (SAFE_FOR_SMTP_TLS_ENF_STS_MX_PAT(tls)) { + argv_truncate(tls->matchargv, 0); + argv_add(tls->matchargv, "hostname", (char *) 0); + } FREE_RETURN; } diff -ur --new-file /var/tmp/postfix-3.10.4/src/smtpd/smtpd.c ./src/smtpd/smtpd.c --- /var/tmp/postfix-3.10.4/src/smtpd/smtpd.c 2025-02-05 17:04:14.000000000 -0500 +++ ./src/smtpd/smtpd.c 2025-10-23 12:45:51.000000000 -0400 @@ -5632,6 +5632,13 @@ {0,}, }; + /* + * In addition to counting unknown commands, the last table element also + * counts malformed commands (which aren't looked up in the command table). + */ +#define LAST_TABLE_PTR(table) ((table) + sizeof(table)/sizeof(*(table)) - 1) +static SMTPD_CMD *smtpd_cmdp_unknown = LAST_TABLE_PTR(smtpd_cmd_table); + static STRING_LIST *smtpd_noop_cmds; static STRING_LIST *smtpd_forbid_cmds; @@ -6000,6 +6007,8 @@ state->error_mask |= MAIL_ERROR_PROTOCOL; smtpd_chat_reply(state, "500 5.5.2 Error: bad UTF-8 syntax"); state->error_count++; + state->where = SMTPD_CMD_UNKNOWN; + smtpd_cmdp_unknown->total_count += 1; continue; } /* Move into smtpd_chat_query() and update session transcript. */ @@ -6021,6 +6030,8 @@ state->error_mask |= MAIL_ERROR_PROTOCOL; smtpd_chat_reply(state, "500 5.5.2 Error: bad syntax"); state->error_count++; + state->where = SMTPD_CMD_UNKNOWN; + smtpd_cmdp_unknown->total_count += 1; continue; } /* Ignore smtpd_noop_cmds lookup errors. Non-critical feature. */ @@ -6029,6 +6040,7 @@ smtpd_chat_reply(state, "250 2.0.0 Ok"); if (state->junk_cmds++ > var_smtpd_junk_cmd_limit) state->error_count++; + /* XXX We can't count these. */ continue; } for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++) diff -ur --new-file /var/tmp/postfix-3.10.4/src/tls/tlsrpt_wrapper.c ./src/tls/tlsrpt_wrapper.c --- /var/tmp/postfix-3.10.4/src/tls/tlsrpt_wrapper.c 2025-02-17 11:49:23.000000000 -0500 +++ ./src/tls/tlsrpt_wrapper.c 2025-10-24 11:05:35.000000000 -0400 @@ -161,7 +161,8 @@ /* policies[].policy.policy-string[]. Ignored if the tls_policy_type /* value is TLSRPT_NO_POLICY_FOUND. /* .IP tls_policy_domain (may be null) -/* policies[].policy.policy-domain. +/* policies[].policy.policy-domain. If null, this defaults to the +/* TLSRPT policy domain. /* .IP mx_host_patterns (may be null) /* policies[].policy.mx-host[]. Ignored if the tls_policy_type /* value is TLSRPT_NO_POLICY_FOUND. @@ -403,10 +404,12 @@ PSTR_OR_NULL(mx_host_patterns)); trw->tls_policy_type = tls_policy_type; + if (tls_policy_domain == 0) + tls_policy_domain = trw->rpt_policy_domain; MYFREE_IF_SET_AND_COPY(trw->tls_policy_domain, tls_policy_domain); if (tls_policy_type == TLSRPT_NO_POLICY_FOUND) { ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings); - ARGV_FREE_IF_SET_AND_CLEAR(trw->tls_policy_strings); + ARGV_FREE_IF_SET_AND_CLEAR(trw->mx_host_patterns); } else { ARGV_FREE_IF_SET_AND_COPY(trw->tls_policy_strings, tls_policy_strings); ARGV_FREE_IF_SET_AND_COPY(trw->mx_host_patterns, mx_host_patterns); diff -ur --new-file /var/tmp/postfix-3.10.4/src/util/dict_db.c ./src/util/dict_db.c --- /var/tmp/postfix-3.10.4/src/util/dict_db.c 2025-08-18 16:02:53.000000000 -0400 +++ ./src/util/dict_db.c 2025-10-23 16:00:35.000000000 -0400 @@ -799,6 +799,7 @@ * the source file changed only seconds ago. */ if ((dict_flags & DICT_FLAG_LOCK) != 0 + && open_flags == O_RDONLY && stat(path, &st) == 0 && st.st_mtime > dict_db->dict.mtime && st.st_mtime < time((time_t *) 0) - 100) diff -ur --new-file /var/tmp/postfix-3.10.4/src/util/dict_dbm.c ./src/util/dict_dbm.c --- /var/tmp/postfix-3.10.4/src/util/dict_dbm.c 2025-08-18 16:02:53.000000000 -0400 +++ ./src/util/dict_dbm.c 2025-10-23 16:00:35.000000000 -0400 @@ -472,7 +472,7 @@ msg_fatal("open database %s: cannot support GDBM", path); if (fstat(dict_dbm->dict.stat_fd, &st) < 0) msg_fatal("dict_dbm_open: fstat: %m"); - if (open_mode == O_RDONLY) + if (open_flags == O_RDONLY) dict_dbm->dict.mtime = st.st_mtime; dict_dbm->dict.owner.uid = st.st_uid; dict_dbm->dict.owner.status = (st.st_uid != 0); @@ -482,6 +482,7 @@ * the source file changed only seconds ago. */ if ((dict_flags & DICT_FLAG_LOCK) != 0 + && open_flags == O_RDONLY && stat(path, &st) == 0 && st.st_mtime > dict_dbm->dict.mtime && st.st_mtime < time((time_t *) 0) - 100) diff -ur --new-file /var/tmp/postfix-3.10.4/src/util/dict_lmdb.c ./src/util/dict_lmdb.c --- /var/tmp/postfix-3.10.4/src/util/dict_lmdb.c 2025-08-18 16:02:53.000000000 -0400 +++ ./src/util/dict_lmdb.c 2025-10-23 16:00:35.000000000 -0400 @@ -666,6 +666,7 @@ * the source file changed only seconds ago. */ if ((dict_flags & DICT_FLAG_LOCK) != 0 + && open_flags == O_RDONLY && stat(path, &st) == 0 && st.st_mtime > dict_lmdb->dict.mtime && st.st_mtime < time((time_t *) 0) - 100) diff -ur --new-file /var/tmp/postfix-3.10.4/src/util/dict_sdbm.c ./src/util/dict_sdbm.c --- /var/tmp/postfix-3.10.4/src/util/dict_sdbm.c 2025-08-18 16:02:53.000000000 -0400 +++ ./src/util/dict_sdbm.c 2025-10-23 16:00:35.000000000 -0400 @@ -459,6 +459,7 @@ * the source file changed only seconds ago. */ if ((dict_flags & DICT_FLAG_LOCK) != 0 + && open_flags == O_RDONLY && stat(path, &st) == 0 && st.st_mtime > dict_sdbm->dict.mtime && st.st_mtime < time((time_t *) 0) - 100)