Prereq: "3.10.2" diff -ur --new-file /var/tmp/postfix-3.10.2/src/global/mail_version.h ./src/global/mail_version.h --- /var/tmp/postfix-3.10.2/src/global/mail_version.h 2025-04-22 11:14:49.000000000 -0400 +++ ./src/global/mail_version.h 2025-07-09 17:54:38.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 "20250422" -#define MAIL_VERSION_NUMBER "3.10.2" +#define MAIL_RELEASE_DATE "20250710" +#define MAIL_VERSION_NUMBER "3.10.3" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -ur --new-file /var/tmp/postfix-3.10.2/HISTORY ./HISTORY --- /var/tmp/postfix-3.10.2/HISTORY 2025-04-22 08:06:24.000000000 -0400 +++ ./HISTORY 2025-07-09 17:56:05.000000000 -0400 @@ -29029,3 +29029,28 @@ add an extra newline character before appending the new setting, causing information to become garbled. Fix by Michael Tokarev. File: postconf/postconf_edit.c. + +20250619 + + Bugfix (defect introduced: Postfix-3.10, date 20250117): + include the current TLS security level in the SMTP connection + cache lookup key for lookups by next-hop destination. The + idea is that to deliver a message without "TLS-Required: + no" header, the Postfix SMTP client must not reuse a + connection that was created during a burst of deliveries + of messages with "TLS-Required: no" to the same destination. + + Including the TLS security level in the SMTP connection + cache lookup key will also prevent false connection reuse + when any future feature is added that overrides the TLS + security level based on message content or envelope metadata. + Files: smtp/smtp.h. + + Likewise, include the current TLS security level in the TLS + client serverid field. File: smtp/smtp_proto.c. + + Bugfix (defect introduced: Postfix-3.10, date 20250117): the + Postfix SMTP client attempted to look up TLSA records even + with "TLS-Required: no". This could result in unnecessary + failures. Fix by Viktor Dukhovni & Wietse. Files: smtp/smtp.h, + smtp/smtp_policy.c, smtp/smtp_connect.c. diff -ur --new-file /var/tmp/postfix-3.10.2/src/smtp/smtp.h ./src/smtp/smtp.h --- /var/tmp/postfix-3.10.2/src/smtp/smtp.h 2024-10-10 18:15:24.000000000 -0400 +++ ./src/smtp/smtp.h 2025-07-09 17:22:54.000000000 -0400 @@ -59,6 +59,9 @@ VSTRING *host; /* hostname or empty */ VSTRING *addr; /* printable address or empty */ unsigned port; /* network byte order or null */ +#ifdef USE_TLS + int tlsreqno; /* "TLS-Required: no" */ +#endif struct DNS_RR *rr; /* DNS resource record or null */ struct DNS_RR *mx; /* DNS resource record or null */ /* Private members. */ @@ -66,11 +69,18 @@ struct SMTP_STATE *parent; /* parent linkage */ } SMTP_ITERATOR; +#ifdef USE_TLS +#define IF_USE_TLS(...) (__VA_ARGS__) +#else +#define IF_USE_TLS(...) +#endif + #define SMTP_ITER_INIT(iter, _dest, _host, _addr, _port, state) do { \ vstring_strcpy((iter)->dest, (_dest)); \ vstring_strcpy((iter)->host, (_host)); \ vstring_strcpy((iter)->addr, (_addr)); \ (iter)->port = (_port); \ + IF_USE_TLS((iter)->tlsreqno = 0); \ (iter)->mx = (iter)->rr = 0; \ vstring_strcpy((iter)->saved_dest, ""); \ (iter)->parent = (state); \ @@ -728,7 +738,7 @@ */ #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_REQ_NEXTHOP | SMTP_KEY_FLAG_TLS_LEVEL) /* * Connection-cache endpoint lookup key. The SENDER, CUR_NEXTHOP, HOSTNAME, diff -ur --new-file /var/tmp/postfix-3.10.2/src/smtp/smtp_connect.c ./src/smtp/smtp_connect.c --- /var/tmp/postfix-3.10.2/src/smtp/smtp_connect.c 2025-02-01 16:06:57.000000000 -0500 +++ ./src/smtp/smtp_connect.c 2025-07-09 17:22:54.000000000 -0400 @@ -508,6 +508,24 @@ SMTP_TLS_POLICY *tls = state->tls; /* + * If the message contains a "TLS-Required: no" header, update the + * iterator to limit the policy at TLS_LEV_MAY. + * + * We must do this early to avoid possible failure if TLSA record lookups + * fail, or if TLSA records are found, but can't be activated because the + * security level has been reset to "may". + * + * Note that the REQUIRETLS verb in ESMTP overrides the "TLS-Required: no" + * header. + */ +#ifdef USE_TLS + if (var_tls_required_enable + && (state->request->sendopts & SOPT_REQUIRETLS_HEADER)) { + iter->tlsreqno = 1; + } +#endif + + /* * Determine the TLS level for this destination. */ if (!smtp_tls_policy_cache_query(why, tls, iter)) { @@ -530,16 +548,6 @@ #endif /* - * Otherwise, if the TLS level is not TLS_LEV_NONE or some non-level, and - * the message contains a "TLS-Required: no" header, limit the level to - * TLS_LEV_MAY. - */ - else if (var_tls_required_enable && tls->level > TLS_LEV_NONE - && (state->request->sendopts & SOPT_REQUIRETLS_HEADER)) { - tls->level = TLS_LEV_MAY; - } - - /* * Success. */ return (1); diff -ur --new-file /var/tmp/postfix-3.10.2/src/smtp/smtp_proto.c ./src/smtp/smtp_proto.c --- /var/tmp/postfix-3.10.2/src/smtp/smtp_proto.c 2025-01-13 17:40:09.000000000 -0500 +++ ./src/smtp/smtp_proto.c 2025-07-09 17:21:02.000000000 -0400 @@ -926,13 +926,16 @@ * XXX: The TLS library will salt the serverid with further details of the * protocol and cipher requirements including the server ehlo response. * Deferring the helo to the digested suffix results in more predictable - * SSL session lookup key lengths. + * SSL session lookup key lengths. Add the current TLS security level to + * account for TLS level overrides based on message content or envelope + * metadata. */ serverid = vstring_alloc(10); smtp_key_prefix(serverid, "&", state->iterator, SMTP_KEY_FLAG_SERVICE | SMTP_KEY_FLAG_CUR_NEXTHOP /* With port */ | SMTP_KEY_FLAG_HOSTNAME - | SMTP_KEY_FLAG_ADDR); + | SMTP_KEY_FLAG_ADDR + | SMTP_KEY_FLAG_TLS_LEVEL); if (state->tls->conn_reuse) { TLS_CLIENT_PARAMS tls_params; diff -ur --new-file /var/tmp/postfix-3.10.2/src/smtp/smtp_tls_policy.c ./src/smtp/smtp_tls_policy.c --- /var/tmp/postfix-3.10.2/src/smtp/smtp_tls_policy.c 2024-10-24 15:01:42.000000000 -0400 +++ ./src/smtp/smtp_tls_policy.c 2025-07-09 17:22:54.000000000 -0400 @@ -651,7 +651,12 @@ tls->level = global_tls_level(); site_level = TLS_LEV_NOTFOUND; - if (tls_policy) { + if (iter->tlsreqno) { + if (msg_verbose) + msg_info("%s: no tls policy lookup", __func__); + if (tls->level > TLS_LEV_MAY) + tls->level = TLS_LEV_MAY; + } else if (tls_policy) { tls_policy_lookup(tls, &site_level, dest, "next-hop destination"); } else if (tls_per_site) { tls_site_lookup(tls, &site_level, dest, "next-hop destination");