Version in base suite: 3.10.5-1~deb13u1 Base version: postfix_3.10.5-1~deb13u1 Target version: postfix_3.10.10-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/postfix/postfix_3.10.5-1~deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/postfix/postfix_3.10.10-0+deb13u1.dsc HISTORY | 148 +++++++++++++++++++++++++ auxiliary/collate/collate.pl | 3 debian/NOTES | 2 debian/README.Debian | 57 ++++----- debian/changelog | 89 ++++++++++++++- debian/configure-instance.in | 8 - debian/patches/debian-postfix-chroot-cmd.patch | 2 debian/patches/linux7.patch | 21 +++ debian/patches/series | 1 debian/postfix.postinst | 2 debian/postfix.service | 8 - debian/rules | 17 -- html/postconf.5.html | 15 +- makedefs | 64 ++++++++++ man/man5/postconf.5 | 16 +- proto/postconf.proto | 15 +- src/dns/dns_lookup.c | 7 - src/global/dict_pgsql.c | 4 src/global/dsn_util.c | 2 src/global/mail_params.h | 6 - src/global/mail_version.h | 4 src/global/pipe_command.c | 5 src/global/rfc2047_code.c | 6 + src/milter/milter8.c | 4 src/postconf/postconf_edit.c | 6 - src/smtp/smtp_tls_policy.c | 20 ++- src/smtp/smtp_tlsrpt.c | 6 - src/tls/tls_prng_file.c | 3 src/util/argv.c | 11 + src/util/dict_cdb.c | 3 src/util/dict_cidr.c | 13 +- src/util/dict_db.c | 6 - src/util/dict_pcre.c | 3 src/util/dict_sockmap.c | 3 src/util/midna_domain.c | 28 ++-- src/util/msg_vstream.c | 1 src/util/netstring.c | 11 + src/util/sys_defs.h | 19 +-- src/util/vbuf_print.c | 29 +++- src/util/vstream.c | 20 +++ src/util/vstream.h | 5 src/util/vstring.h | 2 42 files changed, 551 insertions(+), 144 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp50ro4_fe/postfix_3.10.5-1~deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp50ro4_fe/postfix_3.10.10-0+deb13u1.dsc: no acceptable signature found diff -Nru postfix-3.10.5/HISTORY postfix-3.10.10/HISTORY --- postfix-3.10.5/HISTORY 2025-10-24 15:06:14.000000000 +0000 +++ postfix-3.10.10/HISTORY 2026-05-15 19:54:47.000000000 +0000 @@ -29208,3 +29208,151 @@ 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. + +20251024 + + Logging: with "smtp_tls_enforce_sts_mx_patterns=yes" and + TLSRPT support enabled in a TLS policy plugin, the Postfix + SMTP client logs a warning when an MX hostname does not + match STS policy MX patterns; it logs a successful match + when verbose logging is enabled. File: smtp/smtp_tls_policy.c. + +20251027 + + Bugfix (defect introduced: Postfix 3.10, date: 20240902): + SMTP client null pointer crash when an STS policy plugin + sends no policy_string or no mx_pattern attributes. This + can happen only during tests with a fake STS plugin. File: + smtp/smtp_tlsrpt.c. + +20251028 + + Documentation: removed incorrect text from the parameter + description for smtp_cname_overrides_servername. File: + proto/postconf.proto. + +20251031 + + Bugfix (defect introduced: Postfix 3.10, date 20250117): + support for "TLS-Required: no" broke client-side TLS wrappermode + support, by downgrading a connection to TLS security level 'may'. + The solution is to change the downgrade level for wrappermode + connections to 'encrypt'. Rationale: by design, TLS can be + optional only for connections that use STARTTLS. The downgrade + to unauthenticated 'encrypt' allows a sender to avoid an email + delivery problem. Problem reported by Joshua Tyler Cochran. + File: smtp/smtp_tls_policy.c. + +20251120 + + Bugfix (defect introduced: Postfix 2.9, date: 20120307): + segfault with duplicate parameter name in "postconf -X" or + "postconf -#'. File: postconf/postconf_edit.c. + +20251205 + + Compatibility: recent Linux distributions have C compilers + with a built-in 'bool' type (size=1) that conflicts with + Postfix's bool (an alias for int, typically size=4). Postfix + 3.11 adopts the built-in bool type, but that change is too + large for the stable releases. Instead, the command "make + makefiles" will figure out how to make the compiler + backwards-compatible. File: makedefs. + +20251208 + + Improved Milter error handling for messages that arrive + over a long-lived SMTP connection, by changing the default + milter_default_action from "tempfail" to the new "shutdown" + action (i.e. disconnect the remote SMTP client). + + The problem was that after a single Milter error, Postfix + could tempfail all messages that the client sends over a + long-lived connection, even if the Milter error was only + temporary. This problem was reported by Ankit Kulkarni. + + Files: proto/postconf.proto global/mail_params.h milter/milter8.c. + +20260217 + + Bugfix: (defect introduced: Postfix 2.11): panic() after + recursive logging loop with "posttls-finger -v -v -v". + Reported by Geert Hendrickx, diagnosed by Viktor Dukhovni, + and fixed by Wietse. Files: util/vstream.[hc], util/msg_vstream.c. + +20260310 + + Bugfix (defect introduced: Postfix 3.0): buffer over-read + when Postfix is configured with an enhanced status code not + followed by other text. For example, "5.7.2" without text + after the three-number code in a remote server response, + in an access(5) table, header or body checks, or with + "$rbl_code $rbl_text" in rbl_reply_maps or default_rbl_reply. + Problem reported by Kamil Frankowicz. File: global/dsn_split.c. + +20260426 + + Portability: support for recent FreeBSD, NetBSD, and OpenBSD + versions. Brad Smith. Files: makedefs, util/sys_defs.h. + +20260501 + + Bugfix (defect introduced: Postfix 3.10): The RFC 2047 + encoder for the sender "full name" could loop when a very + long full_name_encoding_charset value was configured in + main.cf. Found by Claude Opus 4.6. File: global/rfc2047_code.c. + + Bugfix (defect introduced: Postfix 2.2, date 20041207): + When truncating a database file, the CDB client looked at + the file size from before requesting an exclusive lock on + a database file, instead of the file size after the exclusive + lock was granted. Found by Claude Opus 4.6. File: + util/dict_cdb.c. + + Bugfix (defect introduced: Postfix alpha, date 19980309): + file descriptor leak after fork() failure. Found by Claude + Opus 4.6. File: global/pipe_command.c. + + Mistakes in debug logging. Found by Claude Opus 4.6. Files: + dns/dns_lookup.c, util/dict_cidr.c, tls/tls_prng_file.c. + + Unchecked null pointer results after an out-of-memory + condition in a library dependency. Found by Claude Opus + 4.6. Files: util/dict_pcre.c, util/midna_domain.c, + global/dict_pgsql.c. + + Missing or incomplete guards for ssize_t or int overflow, + found by Claude Opus 4.6. Files: util/argv.c, util/netstring.c, + util/vbuf_print.c. + + Cleanup: log a fatal error instead of dereferencing a null + pointer after a first/next cursor initialization failure. + Fedor Vorobev. File: util/dict_db.c. + +20260507 + + Fix for 'uninitialized value' error. Viktor Dukhovni. File: + auxiliary/collate/collate.pl. + +20260508 + + Claude AI finding: signed integer overshift (util/vstring.h). + Brought to our attention by Robert Sayre. + +20260513 + + Bitrot: builds with musl libc were using the obsolete + NO_SNPRINTF code path in vbuf_print.c. File: util/sys_defs.h. + +20260514 + + Bitrot: the obsolete NO_SNPRINTF code path in vbuf_print.c + wasn't updated for Claude Code findings. File: util/vbuf_print.c. + +20250515 + + Portability: The __MAXINT__(T) macro, to determine the + maximal signed value for objects of type T, was using + implementation-defined behavior (shift one bit into the + sign position). This works today but may break later. + Reported by Kamil Frankowicz. File: util/sys_defs.h. diff -Nru postfix-3.10.5/auxiliary/collate/collate.pl postfix-3.10.10/auxiliary/collate/collate.pl --- postfix-3.10.5/auxiliary/collate/collate.pl 2022-02-11 21:56:54.000000000 +0000 +++ postfix-3.10.10/auxiliary/collate/collate.pl 2026-05-15 18:37:50.000000000 +0000 @@ -122,8 +122,7 @@ if (defined($transaction{$qid})) { $transaction{$qid} .= $_; } - $transaction{$newid} = - $_ . $transaction{$newid}; + $transaction{$newid} = $_ . ($transaction{$newid} // ""); $seqno{$newid} = ++$i if (! exists $seqno{$newid}); } next; diff -Nru postfix-3.10.5/debian/NOTES postfix-3.10.10/debian/NOTES --- postfix-3.10.5/debian/NOTES 2025-04-24 07:52:30.000000000 +0000 +++ postfix-3.10.10/debian/NOTES 2026-03-01 06:31:58.000000000 +0000 @@ -52,7 +52,7 @@ There might be another case when we may want to run newaliases during upgrade: when file format has changed for some database. But this should be handled for all maps, not just aliases, and for all instances, -and it is dependent on other packages, not on postifx. +and it is dependent on other packages, not on postfix. See https://bugs.debian.org/847242 https://bugs.debian.org/865005 https://bugs.debian.org/864609 diff -Nru postfix-3.10.5/debian/README.Debian postfix-3.10.10/debian/README.Debian --- postfix-3.10.5/debian/README.Debian 2025-04-24 07:52:30.000000000 +0000 +++ postfix-3.10.10/debian/README.Debian 2026-03-01 06:31:58.000000000 +0000 @@ -12,7 +12,7 @@ pgsql, sqlite, are shipped in Debian in separate packages. For example, to use cdb: map, please install postfix-cdb package. -When you run Postfix chrooted and use certain network-based map types, notable +When you run Postfix chrooted and use certain network-based map types, notably ldap, mysql, pgsql, especially when also using TLS, please consider proxying map access through proxymap(8) service which is designed for this very purpose. So instead of "ldap:users.cf", use "proxy:ldap:users.cf", - this avoids issues @@ -22,10 +22,11 @@ Controlling chroot ~~~~~~~~~~~~~~~~~~ -Postfix has an excellent security track. However it additionally allows one to -run most its components confined in a chroot jail in its queue directory. This -is an advanced security measure to lessen possible damage in case any component -is ever compromised. However this security measure comes with its own costs. +Postfix has an excellent security track record. However it additionally allows +one to run most of its components confined in a chroot jail in its queue +directory. This is an advanced security measure to lessen possible damage in +case any component is ever compromised. However this security measure comes with +its own costs. Postfix in Debian is installed chrooted by default, which means extra actions might be required to implement some configurations. However, there's a simple @@ -43,7 +44,7 @@ The Postfix chroot feature is supposed to help only in cases where other aspects of system security are already addressed. In order to do this, one -have to understand security by heart, just installing a Debian system with +has to understand security by heart, just installing a Debian system with default settings is not enough. Due to this, running Postfix services chrooted is usually overkill. It is strongly recommended that you turn off chroot feature in Postfix as shipped in Debian, if you can not solve a problem which @@ -60,13 +61,13 @@ service dependencies and so on. Postfix upstream documentation states to run `postfix start' from command line to start postfix. However this way, in upstream postfix, runs the mail system within user service context, it -is basically hidden from the service manager, it is completely unaware of -an instance started this way. Postfix in Debian comes with simple change -modification which redirects manual `postfix start' command to systemd +is basically hidden from the service manager, which is completely unaware of +an instance started this way. Postfix in Debian comes with a simple +modification which redirects the manual `postfix start' command to systemd service startup, so that the service manager is aware of the service startup -and keeps track of it. So on Debian system, starting postfix using +and keeps track of it. So on a Debian system, starting postfix using `systemctl start postfix' or traditional `postfix start' does the same, - -both starts a service within systemd context. +both start postfix within systemd context. Multiple Postfix instances under systemd @@ -99,27 +100,27 @@ Instance names specified within main.cf in the config directory (using multi_instance_name) are not used, only path-based naming is in effect. -On previous Debian releases, this worked differently: default Postfix instance -were named postfix@-.service (which is gone in postfix 3.9.1-7), and other -non-default instances were named after multi_instance_name parameter in their -main.cf. This does not work easily with systemd, because there is no good way -to map from instance name to postfix config directory and back. This scheme -is also gone in 3.9.1-7 version of the package. Using path-based instance -naming allows to identify instance immediately instead of using guessing by -searching in multiple main.cf files, and it is easier to find for the human -too. So if you had non-default instances running with debian postfix package -before 3.9.1-7, you might want to review and rearrange your configuration. +In previous Debian releases, this worked differently: the default Postfix +instance was named postfix@-.service (which is gone in postfix 3.9.1-7), and +other non-default instances were named after the multi_instance_name parameter +in their main.cf. This does not work easily with systemd, because there is no +good way to map from instance name to postfix config directory and back. This +scheme is also gone in postfix 3.9.1-7. Using path-based instance naming allows +identifying instances immediately instead of searching in multiple main.cf +files, and it is easier to find for the human too. So if you had non-default +instances running with the Debian postfix package before 3.9.1-7, you might need +to review and rearrange your configuration. Also, no instances besides the default one are started automatically as before, please enable using `systemctl enable postfix@INST.service' as needed. -If your system used non-default instances with configuration directories -not having /etc/postfix- prefix, these will not be managed by the system +If your system used non-default instances with configuration directories not +having the /etc/postfix- prefix, these will not be managed by the default system startup anymore and must be managed using some other means, or the config -directories has to be moved to have such prefix. The same is true if you -had non-default postfix instances within /etc/postfix/ subdirectory, -like /etc/potfix/out. This naming is on-par with the upstream recommendation, -but unlike `postfix start', which can start instance with any configuration +directories have to be moved to have such prefix. The same is true if you had +non-default postfix instances within /etc/postfix/ subdirectories, like +/etc/postfix/out. This naming is on-par with the upstream recommendation, but +unlike `postfix start', which can start an instance with any configuration directory, Postfix integration with systemd in Debian does not support this. @@ -167,7 +168,7 @@ a. SASL configuration goes in /etc/postfix/sasl. Starting in Debian Trixie (13), the patch that previously hard coded this path is replaced by setting cyrus_sasl_config_path = /etc/postfix/sasl in Debian's defaults. - Setting this value to a different patch is now supported. + Setting this value to a different path is now supported. b. myhostname=/path/to/file is supported (and used) in main.cf 4. IPV6 support is enabled: postfix listens on ipv6/ipv4 by default, (see: inet_protocols) diff -Nru postfix-3.10.5/debian/changelog postfix-3.10.10/debian/changelog --- postfix-3.10.5/debian/changelog 2025-10-28 10:24:35.000000000 +0000 +++ postfix-3.10.10/debian/changelog 2026-06-11 06:55:11.000000000 +0000 @@ -1,3 +1,86 @@ +postfix (3.10.10-0+deb13u1) trixie; urgency=medium + + [ Michael Tokarev ] + * keep postfix running during upgrades (Closes: #1120869) + * linux7.patch: support building of the source on 7.x kernels + + * new upstream stable/bugfix release 3.10.10: + - Bitrot: builds with musl libc broke, because they were using an obsolete + NO_SNPRINTF code path. + - Two fixes for a signed integer overshift condition (a left shift into the + sign bit). This "works" on contemporary CPUs, but may break in the future. + - Fix an 'uninitialized value' error in the 'collate.pl' script. + + * new upstream stable/bugfix release 3.10.9: + - Bugfix: The RFC 2047 encoder for the sender "full name" could + loop when a very long full_name_encoding_charset value was + configured in main.cf. + - Bugfix buffer over-read when Postfix an enhanced status code is not + followed by other text. For example, "5.7.2" without text after the + three-number code. This CANNOT be triggered with an SMTP or LMTP server + response; is confirmed with an access(5) table and likely with a policy + server response; can possibly be triggered with pipe-to-command output, + header_checks(5), body_checks(5), an error(8) transport in transport_maps, + or a milter response; and is confirmed with a DNSBL server TXT response + while Postfix is configured with "$rbl_code $rbl_text" in rbl_reply_maps + or default_rbl_reply. This could result in process termination. + (Closes: #1135718, CVE-2026-43964) + - Code cleanup: log a fatal error instead of dereferencing a null pointer + after a first/next cursor initialization failure. + - Portability: support for recent FreeBSD, NetBSD, and OpenBSD versions. + - Bugfix: When truncating a database file, the cdb: database client looked + at the file size from before requesting an exclusive lock on a database + file, instead of the file size after the exclusive lock was granted. + - Bugfix: file descriptor leak after fork() failure. + - Mistakes in debug logging. + - Unchecked null pointer results after an out-of-memory condition in a + library dependency. Found by Claude Opus 4.6. The fix is to return an + error status or to log a fatal error. + - Missing or incomplete guards for ssize_t or int overflow. These limits + are unlikely to be exceeded because the size of in-memory objects is + limited by design (the number of in-memory objects is also limited). + + * new upstream stable/bugfix release 3.10.8: + - Improved Milter error handling for messages that arrive over a + long-lived SMTP connection. + - Fix "posttls-finger -v -v -v" panic and recursive panic. + + * new upstream stable/bugfix release 3.10.7: + - build fix for modern compilers and standard bool types + (already included in debian) + + * new upstream stable/bugfix release 3.10.6: + - Bugfix: warning messages that smtp_tls_wrappermode requires + "smtp_tls_security_level = encrypt". + Root cause: support for "TLS-Required: no" broke client-side + TLS wrappermode support, by downgrading a connection to TLS + security level 'may'. + The fix changes the downgrade level for wrappermode connections + to 'encrypt'. + Rationale: by design, TLS can be optional only for connections + that use STARTTLS. The downgrade to unauthenticated 'encrypt' + allows a sender to avoid an email delivery problem. + - New logging: the Postfix SMTP client will log a warning when + an MX hostname does not match STS policy MX patterns, with + "smtp_tls_enforce_sts_mx_patterns = yes" in Postfix, and with + TLSRPT support enabled in a TLS policy plugin. It will log a + successful match only when verbose logging is enabled. + - Bugfix: SMTP client null pointer crash when an STS policy plugin + sends no policy_string or no mx_pattern attributes. + This can happen only during tests with a fake STS plugin. + - Bugfix: segfault when a duplicate parameter name is given to + "postconf -X" or "postconf -#'. + - Documentation: removed incorrect text from the parameter description for + smtp_cname_overrides_servername + + [ Aaron Thompson ] + * debian-postfix-chroot-cmd.patch: Fix non-ASCII whitespace typo + * configure-instance.in: fix typo + * d/README.Debian: minor copyediting + * Fix some cosmetic typos + + -- Michael Tokarev Thu, 11 Jun 2026 09:55:11 +0300 + postfix (3.10.5-1~deb13u1) trixie; urgency=medium * new upstream stable/bugfix 3.10.5 release, with multiple fixes. @@ -383,7 +466,7 @@ - copy just one directory deep - more advanced master.cf reading; skip simple services: chroot wont be updated if only postfix internal services are chrooted - - do not ship chroot files in /var/spool/postix/ (dev, etc, lib), + - do not ship chroot files in /var/spool/postfix/ (dev, etc, lib), create them in configure-instance.sh for every instance instead - remove /etc/passwd copying to chroot (was a hack for #65473) - exit early if !SYNC_CHROOT or !NEED_CHROOT @@ -984,9 +1067,9 @@ [Scott Kitterman] * Add postfix-mta-sts-resolver to suggests. Closes: #968516 - * Include compatibility_level in addition to postifx version when + * Include compatibility_level in addition to postfix version when determining default value for chroot in master.cf. Closes: #995129 - * Fixup errors in postifx-add-* man pages. Closes: #995031 + * Fixup errors in postfix-add-* man pages. Closes: #995031 * Set compatibility level to 3.6 for fresh installs * Update main/master.cf.proto on upgrade if not modified. Closes: #991513 * Decruft debconf template: diff -Nru postfix-3.10.5/debian/configure-instance.in postfix-3.10.10/debian/configure-instance.in --- postfix-3.10.5/debian/configure-instance.in 2025-09-17 09:42:58.000000000 +0000 +++ postfix-3.10.10/debian/configure-instance.in 2026-06-11 06:49:12.000000000 +0000 @@ -71,10 +71,10 @@ then rm -f lib/libnss_*.so.2 fi -if [ -f etc/ssl/cert/GlobalSign_Root_CA.pem ] # arbitrary, <<3.9.1-5 +if [ -f etc/ssl/certs/GlobalSign_Root_CA.pem ] # arbitrary, <<3.9.1-5 then # we re-created everything each run before 3.9.1-5 # remove just the most common dir - rm -rf etc/ssl/cert + rm -rf etc/ssl/certs fi # always copy/update small stuff so simple services works too @@ -118,7 +118,7 @@ # Copy certificate dirs # There are 2 types of certs storage: ca-certificates.crt file (smtp_tls_CAfile) -# and a directory with xxxxxxxx.N files (smtp_tls_CAdir) +# and a directory with xxxxxxxx.N files (smtp_tls_CApath) # It is best to use just CAfile (/etc/ssl/certs/ca-certificates.crt) which is read # before chrooting, - usually it isn't large, and requires no processing. # Can show a warning here suggesting to switch to smtp_tls_CAfile. @@ -142,7 +142,7 @@ # only copy already hashed names. This means we only trust # what's trusted on the system, not everything present - # Before 3.9.1-5 we truested everything + # Before 3.9.1-5 we trusted everything dest=$queue_directory$cadir if [ -d $dest ]; then ( cd $dest diff -Nru postfix-3.10.5/debian/patches/debian-postfix-chroot-cmd.patch postfix-3.10.10/debian/patches/debian-postfix-chroot-cmd.patch --- postfix-3.10.5/debian/patches/debian-postfix-chroot-cmd.patch 2025-04-24 07:52:30.000000000 +0000 +++ postfix-3.10.10/debian/patches/debian-postfix-chroot-cmd.patch 2026-06-11 06:49:12.000000000 +0000 @@ -47,7 +47,7 @@ + shift `expr ${OPTIND} - 1` + case "$1" in + on | y) verify="$yes" set=y ;; -+ off | n) verify="$no" set=n ;; ++ off | n) verify="$no" set=n ;; + query | q | "") verify="$yes" set= ;; + *) echo "E: chroot: unknown arg $1 (expected [-n][-S][-X][-c] on|off|query)" >&2 + exit 1 diff -Nru postfix-3.10.5/debian/patches/linux7.patch postfix-3.10.10/debian/patches/linux7.patch --- postfix-3.10.5/debian/patches/linux7.patch 1970-01-01 00:00:00.000000000 +0000 +++ postfix-3.10.10/debian/patches/linux7.patch 2026-06-11 06:49:12.000000000 +0000 @@ -0,0 +1,21 @@ +Subject: allow building on linux kernel version 7.x +From: Michael Tokarev +Date: Thu, 04 Jun 2026 08:44:31 +0300 +Forwarded: not-needed (already fixed in a next version) + +diff --git a/src/util/sys_defs.h b/src/util/sys_defs.h +--- a/src/util/sys_defs.h ++++ b/src/util/sys_defs.h +@@ -766,3 +766,3 @@ extern int initgroups(const char *, int); + #if defined(LINUX2) || defined(LINUX3) || defined(LINUX4) || defined(LINUX5) \ +- || defined(LINUX6) ++ || defined(LINUX6) || defined(LINUX7) + #define SUPPORTED +diff --git a/makedefs b/makedefs +--- a/makedefs ++++ b/makedefs +@@ -684,3 +684,3 @@ EOF + ;; +- Linux.[3456].*) ++ Linux.[34567].*) + SYSTYPE=LINUX$RELEASE_MAJOR diff -Nru postfix-3.10.5/debian/patches/series postfix-3.10.10/debian/patches/series --- postfix-3.10.5/debian/patches/series 2025-09-17 09:55:58.000000000 +0000 +++ postfix-3.10.10/debian/patches/series 2026-06-11 06:49:12.000000000 +0000 @@ -11,3 +11,4 @@ debian-systemd-start.patch debian-postfix-chroot-cmd.patch hurd.patch +linux7.patch diff -Nru postfix-3.10.5/debian/postfix.postinst postfix-3.10.10/debian/postfix.postinst --- postfix-3.10.5/debian/postfix.postinst 2025-04-24 07:52:30.000000000 +0000 +++ postfix-3.10.10/debian/postfix.postinst 2026-06-11 06:49:12.000000000 +0000 @@ -10,7 +10,7 @@ [ -z "$DPKG_ROOT" ] || exit 0 echo "Restarting postfix" if [ -d /run/systemd/system ]; then - deb-systemd-invoke start postfix.service >/dev/null || : + deb-systemd-invoke restart postfix.service >/dev/null || : else invoke-rc.d postfix restart || : fi diff -Nru postfix-3.10.5/debian/postfix.service postfix-3.10.10/debian/postfix.service --- postfix-3.10.5/debian/postfix.service 2025-04-24 07:52:30.000000000 +0000 +++ postfix-3.10.10/debian/postfix.service 2026-06-11 06:49:12.000000000 +0000 @@ -28,7 +28,7 @@ # runs things as user, and a user needs to be able to run suid/sgid programs # (if not only to be able to deliver mail to /var/spool/postfix/postdrop). # Individual Postfix daemons are started as root, optionally perform chroot -# into the queue directory, and drop privileges voluntary +# into the queue directory, and drop privileges voluntarily. # listen(2) on privileged ports (smtp) CapabilityBoundingSet=CAP_NET_BIND_SERVICE @@ -40,7 +40,7 @@ CapabilityBoundingSet=CAP_DAC_OVERRIDE # https://bugs.debian.org/1099891 : CapabilityBoundingSet=CAP_DAC_READ_SEARCH -# chown(2) is needed for procmal &Co to create /var/mail/$USER +# chown(2) is needed for procmail &Co to create /var/mail/$USER CapabilityBoundingSet=CAP_CHOWN # users might run suid/sgid programs from ~/.forward: @@ -54,8 +54,8 @@ # or using virtual(8), you can also set #RestrictSUIDSGID=yes #NoNewPrivileges=yes -# Also, CAP_DAC_OVERRIDE can be eliminated by adding root user to ACL to -# postfix-owned dis in spool: public, private; and whatever maps in protected +# Also, CAP_DAC_OVERRIDE can be eliminated by adding root user to ACLs of +# postfix-owned dirs in spool: public, private; and whatever maps in protected # subdirs you use, relying on cap_dac_override LockPersonality=yes diff -Nru postfix-3.10.5/debian/rules postfix-3.10.10/debian/rules --- postfix-3.10.5/debian/rules 2025-10-28 10:09:33.000000000 +0000 +++ postfix-3.10.10/debian/rules 2026-06-11 06:49:12.000000000 +0000 @@ -128,7 +128,7 @@ # do nothing override_dh_install-indep: -# note: actual docs goes to postfix package doc dir instead of postix-doc +# note: actual docs go to postfix package doc dir instead of postfix-doc # (dh_installdocs & dh_installexamples etc does magic on this) cp -p AAAREADME meta/README # rename dh_installdocs -p ${package}-doc \ @@ -267,20 +267,11 @@ dh_installman -p ${package} debian/postfix-add-filter.8 debian/postfix-add-policy.8 override_dh_installsystemd-arch: -# postfix is (re)started by a dpkg trigger, so --no-start -# (but needs manual stop in preinst & prerm due to dh_installsystemd limitation) +# postfix is (re)started by a dpkg trigger, so --no-start --no-stop-on-upgrade patch -i debian/postfix@.service.diff -o debian/postfix@.service debian/postfix.service - dh_installsystemd -p ${package} --no-start postfix.service + dh_installsystemd -p ${package} --no-start --no-stop-on-upgrade postfix.service rm debian/postfix@.service - { echo '[ -z "$${DPKG_ROOT:-}" ] && [ upgrade = "$$1" ] && [ -d /run/systemd/system ] &&' ; \ - echo ' deb-systemd-invoke stop postfix.service >/dev/null || :' ; } >> ${base}.preinst.debhelper - { echo '[ -z "$${DPKG_ROOT:-}" ] && [ remove = "$$1" ] && [ -d /run/systemd/system ] &&' ; \ - echo ' deb-systemd-invoke stop postfix.service >/dev/null || :'; } >> ${base}.prerm.debhelper - dh_installinit -p ${package} --no-start - { echo '[ -z "$${DPKG_ROOT:-}" ] && [ upgrade = "$$1" ] && [ -x /etc/init.d/postfix ] &&' ; \ - echo ' invoke-rc.d --skip-systemd-native postfix stop || :' ; } >> ${base}.preinst.debhelper - { echo '[ -z "$${DPKG_ROOT:-}" ] && [ remove = "$$1" ] && [ -x /etc/init.d/postfix ] &&' ; \ - echo ' invoke-rc.d --skip-systemd-native postfix stop || :' ; } >> ${base}.prerm.debhelper + dh_installinit -p ${package} --no-start --no-stop-on-upgrade dh_installsystemd -p ${package} --no-enable --no-start --name postfix-resolvconf override_dh_installinit: # done in installsystemd diff -Nru postfix-3.10.5/html/postconf.5.html postfix-3.10.10/html/postconf.5.html --- postfix-3.10.5/html/postconf.5.html 2025-10-26 22:52:23.000000000 +0000 +++ postfix-3.10.10/html/postconf.5.html 2026-02-18 19:20:01.000000000 +0000 @@ -7363,7 +7363,7 @@
milter_default_action -(default: tempfail)
+(default: see 'postconf -d milter_default_action' output)

The default action when a Milter (mail filter) response is unavailable (for example, bad Postfix configuration or Milter @@ -7380,11 +7380,20 @@

tempfail
Reject all further commands in this session with a temporary status code.
+
shutdown
Close the SMTP connection after sending a 421 +SMTP reply. Available in Postfix 3.11, 3.10.8, 3.9.9, 3.8.15, 3.7.20, +and later.
+
quarantine
Like "accept", but freeze the message in the "hold" queue. Available with Postfix 2.6 and later.
+

The current default action is "shutdown", i.e. disconnect the +SMTP client. With the old "tempfail" default, Postfix could tempfail +all messages that the client sends over a long-lived connection, +even if a Milter failure is only temporary.

+

This feature is available in Postfix 2.3 and later.

@@ -11373,10 +11382,6 @@ password file lookups more predictable. This is the default setting as of Postfix 2.3.

-

When DNS CNAME records are validated with secure DNS lookups -(smtp_dns_support_level = dnssec), they are always allowed to -override the above servername (Postfix 2.11 and later).

-

This feature is available in Postfix 2.2.9 and later.

diff -Nru postfix-3.10.5/makedefs postfix-3.10.10/makedefs --- postfix-3.10.5/makedefs 2025-02-17 17:41:56.000000000 +0000 +++ postfix-3.10.10/makedefs 2026-05-01 20:09:28.000000000 +0000 @@ -351,6 +351,24 @@ : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} : ${PLUGIN_LD="${CC} -shared"} ;; + FreeBSD.15*) SYSTYPE=FREEBSD15 + : ${CC=cc} + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC} -shared"} + ;; + FreeBSD.16*) SYSTYPE=FREEBSD16 + : ${CC=cc} + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC} -shared"} + ;; DragonFly.*) SYSTYPE=DRAGONFLY ;; OpenBSD.2*) SYSTYPE=OPENBSD2 @@ -386,9 +404,18 @@ : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} : ${PLUGIN_LD="${CC} -shared"} ;; + OpenBSD.8*) SYSTYPE=OPENBSD8 + : ${CC=cc} + : ${SHLIB_SUFFIX=.so.1.0} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC} -shared"} + ;; ekkoBSD.1*) SYSTYPE=EKKOBSD1 ;; - NetBSD.1*) SYSTYPE=NETBSD1 + NetBSD.1.*) SYSTYPE=NETBSD1 ;; NetBSD.2*) SYSTYPE=NETBSD2 ;; @@ -438,6 +465,22 @@ : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} : ${PLUGIN_LD="${CC-gcc} -shared"} ;; + NetBSD.11*) SYSTYPE=NETBSD11 + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC-gcc} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC-gcc} -shared"} + ;; + NetBSD.12*) SYSTYPE=NETBSD12 + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC-gcc} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC-gcc} -shared"} + ;; BSD/OS.2*) SYSTYPE=BSDI2 ;; BSD/OS.3*) SYSTYPE=BSDI3 @@ -1235,6 +1278,25 @@ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ -Wunused -Wno-missing-braces -fno-common'} +# C23 introduces a built-in type 'bool' (size 1) which breaks past Postfix +# practice where 'bool' is an alias for 'int' (typically, size 4). +# Fortunately, -std=gnu17 works with both gcc and clang. +cat >makedefs.test.c <<'EOF' +/* This appears in mail_params.h */ +typedef int bool; +EOF + +if eval ${CC} ${CCARGS} -c makedefs.test.c >/dev/null 2>&1 +then + : No further action needed. +elif eval ${CC} -std=gnu17 ${CCARGS} -c makedefs.test.c >/dev/null 2>&1 +then + CCARGS="-std=gnu17 $CCARGS" +else + error 'Please specify "CC=..." or "CCARGS=..." option to disable C23 bool' +fi +rm -f makedefs.test.[co] + # Extract map type names from -DHAS_XXX compiler options. We avoid # problems with tr(1) range syntax by using enumerations instead, # and we don't try to figure out which awk versions have tolower(). diff -Nru postfix-3.10.5/man/man5/postconf.5 postfix-3.10.10/man/man5/postconf.5 --- postfix-3.10.5/man/man5/postconf.5 2025-10-26 22:52:23.000000000 +0000 +++ postfix-3.10.10/man/man5/postconf.5 2026-02-18 19:20:01.000000000 +0000 @@ -4496,7 +4496,7 @@ for a list of available macro names and their meanings. .PP This feature is available in Postfix 2.3 and later. -.SH milter_default_action (default: tempfail) +.SH milter_default_action (default: see 'postconf \-d milter_default_action' output) The default action when a Milter (mail filter) response is unavailable (for example, bad Postfix configuration or Milter failure). Specify one of the following: @@ -4511,12 +4511,22 @@ Reject all further commands in this session with a temporary status code. .br +.IP "shutdown" +Close the SMTP connection after sending a 421 +SMTP reply. Available in Postfix 3.11, 3.10.8, 3.9.9, 3.8.15, 3.7.20, +and later. +.br .IP "quarantine" Like "accept", but freeze the message in the "hold" queue. Available with Postfix 2.6 and later. .br .br .PP +The current default action is "shutdown", i.e. disconnect the +SMTP client. With the old "tempfail" default, Postfix could tempfail +all messages that the client sends over a long\-lived connection, +even if a Milter failure is only temporary. +.PP This feature is available in Postfix 2.3 and later. .SH milter_end_of_data_macros (default: see "postconf \-d" output) The macros that are sent to Milter (mail filter) applications @@ -7136,10 +7146,6 @@ password file lookups more predictable. This is the default setting as of Postfix 2.3. .PP -When DNS CNAME records are validated with secure DNS lookups -(smtp_dns_support_level = dnssec), they are always allowed to -override the above servername (Postfix 2.11 and later). -.PP This feature is available in Postfix 2.2.9 and later. .SH smtp_connect_timeout (default: 30s) The Postfix SMTP client time limit for completing a TCP connection, or diff -Nru postfix-3.10.5/proto/postconf.proto postfix-3.10.10/proto/postconf.proto --- postfix-3.10.5/proto/postconf.proto 2025-10-24 14:41:15.000000000 +0000 +++ postfix-3.10.10/proto/postconf.proto 2026-02-18 16:51:40.000000000 +0000 @@ -11398,10 +11398,6 @@ password file lookups more predictable. This is the default setting as of Postfix 2.3.

-

When DNS CNAME records are validated with secure DNS lookups -(smtp_dns_support_level = dnssec), they are always allowed to -override the above servername (Postfix 2.11 and later).

-

This feature is available in Postfix 2.2.9 and later.

%PARAM lmtp_cname_overrides_servername yes @@ -12278,7 +12274,7 @@

This feature is available in Postfix 2.3 and later.

-%PARAM milter_default_action tempfail +%PARAM milter_default_action see 'postconf -d milter_default_action' output

The default action when a Milter (mail filter) response is unavailable (for example, bad Postfix configuration or Milter @@ -12295,11 +12291,20 @@

tempfail
Reject all further commands in this session with a temporary status code.
+
shutdown
Close the SMTP connection after sending a 421 +SMTP reply. Available in Postfix 3.11, 3.10.8, 3.9.9, 3.8.15, 3.7.20, +and later.
+
quarantine
Like "accept", but freeze the message in the "hold" queue. Available with Postfix 2.6 and later.
+

The current default action is "shutdown", i.e. disconnect the +SMTP client. With the old "tempfail" default, Postfix could tempfail +all messages that the client sends over a long-lived connection, +even if a Milter failure is only temporary.

+

This feature is available in Postfix 2.3 and later.

%PARAM milter_connect_timeout 30s diff -Nru postfix-3.10.5/src/dns/dns_lookup.c postfix-3.10.10/src/dns/dns_lookup.c --- postfix-3.10.5/src/dns/dns_lookup.c 2024-10-10 22:15:18.000000000 +0000 +++ postfix-3.10.10/src/dns/dns_lookup.c 2026-05-01 20:02:52.000000000 +0000 @@ -834,14 +834,15 @@ for (src = pos, dst = (unsigned char *) ltemp; src < pos + fixed->length; /* */ ) { frag_len = *src++; - if (msg_verbose) - msg_info("frag_len=%d text=\"%.*s\"", - (int) frag_len, (int) frag_len, (char *) src); + /* 202604 Claude: move debug logging after the frag_len check. */ if (frag_len > reply->end - src || frag_len >= ((unsigned char *) ltemp + sizeof(ltemp)) - dst) { msg_warn("extract_answer: bad TXT string length: %d", frag_len); return (DNS_RETRY); } + if (msg_verbose) + msg_info("frag_len=%d text=\"%.*s\"", + (int) frag_len, (int) frag_len, (char *) src); while (frag_len-- > 0) { ch = *src++; *dst++ = (ISPRINT(ch) ? ch : ' '); diff -Nru postfix-3.10.5/src/global/dict_pgsql.c postfix-3.10.10/src/global/dict_pgsql.c --- postfix-3.10.5/src/global/dict_pgsql.c 2025-02-07 15:32:55.000000000 +0000 +++ postfix-3.10.10/src/global/dict_pgsql.c 2026-05-01 20:02:52.000000000 +0000 @@ -574,8 +574,10 @@ dict_pgsql->password); } if (host->db == NULL || PQstatus(host->db) != CONNECTION_OK) { + /* 202604 Claude: don't call PQerrorMessage(NULL). */ msg_warn("connect to pgsql server %s: %s", - host->hostname, PQerrorMessage(host->db)); + host->hostname, host->db ? PQerrorMessage(host->db) : + "PQconnectdb or PQsetdbLogin failed"); plpgsql_down_host(host, dict_pgsql->retry_interval); return; } diff -Nru postfix-3.10.5/src/global/dsn_util.c postfix-3.10.10/src/global/dsn_util.c --- postfix-3.10.5/src/global/dsn_util.c 2006-01-08 01:28:37.000000000 +0000 +++ postfix-3.10.10/src/global/dsn_util.c 2026-05-01 20:06:13.000000000 +0000 @@ -154,7 +154,7 @@ if ((len = dsn_valid(cp)) > 0) { strncpy(dp->dsn.data, cp, len); dp->dsn.data[len] = 0; - cp += len + 1; + cp += len; } else if ((len = dsn_valid(def_dsn)) > 0) { strncpy(dp->dsn.data, def_dsn, len); dp->dsn.data[len] = 0; diff -Nru postfix-3.10.5/src/global/mail_params.h postfix-3.10.10/src/global/mail_params.h --- postfix-3.10.5/src/global/mail_params.h 2025-10-24 14:41:15.000000000 +0000 +++ postfix-3.10.10/src/global/mail_params.h 2026-02-18 16:40:51.000000000 +0000 @@ -3516,7 +3516,7 @@ extern char *var_cleanup_milters; #define VAR_MILT_DEF_ACTION "milter_default_action" -#define DEF_MILT_DEF_ACTION "tempfail" +#define DEF_MILT_DEF_ACTION "shutdown" extern char *var_milt_def_action; #define VAR_MILT_CONN_MACROS "milter_connect_macros" @@ -3571,10 +3571,6 @@ #define DEF_MILT_PROTOCOL "6" extern char *var_milt_protocol; -#define VAR_MILT_DEF_ACTION "milter_default_action" -#define DEF_MILT_DEF_ACTION "tempfail" -extern char *var_milt_def_action; - #define VAR_MILT_DAEMON_NAME "milter_macro_daemon_name" #define DEF_MILT_DAEMON_NAME "$" VAR_MYHOSTNAME extern char *var_milt_daemon_name; diff -Nru postfix-3.10.5/src/global/mail_version.h postfix-3.10.10/src/global/mail_version.h --- postfix-3.10.5/src/global/mail_version.h 2025-10-26 22:48:02.000000000 +0000 +++ postfix-3.10.10/src/global/mail_version.h 2026-05-15 18:38:35.000000000 +0000 @@ -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 "20251026" -#define MAIL_VERSION_NUMBER "3.10.5" +#define MAIL_RELEASE_DATE "20260515" +#define MAIL_VERSION_NUMBER "3.10.10" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -Nru postfix-3.10.5/src/global/pipe_command.c postfix-3.10.10/src/global/pipe_command.c --- postfix-3.10.5/src/global/pipe_command.c 2014-12-25 16:47:18.000000000 +0000 +++ postfix-3.10.10/src/global/pipe_command.c 2026-05-01 20:02:52.000000000 +0000 @@ -460,6 +460,11 @@ msg_warn("fork: %m"); dsb_unix(why, "4.3.0", sys_exits_detail(EX_OSERR)->text, "Delivery failed: %m"); + /* 202604 Claude: close pipes for the child and parent paths. */ + close(cmd_in_pipe[0]); + close(cmd_in_pipe[1]); + close(cmd_out_pipe[0]); + close(cmd_out_pipe[1]); return (PIPE_STAT_DEFER); /* diff -Nru postfix-3.10.5/src/global/rfc2047_code.c postfix-3.10.10/src/global/rfc2047_code.c --- postfix-3.10.5/src/global/rfc2047_code.c 2025-01-05 17:35:43.000000000 +0000 +++ postfix-3.10.10/src/global/rfc2047_code.c 2026-05-01 20:02:52.000000000 +0000 @@ -274,6 +274,12 @@ msg_warn("%s: encoder called with empty charset name", myname); return (0); } + /* 202604 Claude: avoid 'space_left' underflow. */ + if (strlen(charset) > ENC_WORD_MAX_LEN / 2) { + msg_warn("%s: unreasonable charset name: '%.100s'", + myname, charset); + return (0); + } for (cp = (const unsigned char *) charset; (ch = *cp) != 0; cp++) { if (!RFC2047_ALLOWED_TOKEN_CHAR(ch)) { msg_warn("%s: invalid character: 0x%x in charset name: '%s'", diff -Nru postfix-3.10.5/src/milter/milter8.c postfix-3.10.10/src/milter/milter8.c --- postfix-3.10.5/src/milter/milter8.c 2025-01-07 22:25:43.000000000 +0000 +++ postfix-3.10.10/src/milter/milter8.c 2026-02-18 16:40:51.000000000 +0000 @@ -523,6 +523,8 @@ } if (strcasecmp(milter->def_action, "accept") == 0) { reply = 0; + } else if (strcasecmp(milter->def_action, "shutdown") == 0) { + reply = "421 4.3.5 Server configuration problem - try again later"; } else if (strcasecmp(milter->def_action, "quarantine") == 0) { reply = "Hdefault_action"; } else { @@ -557,6 +559,8 @@ reply = "550 5.5.0 Service unavailable"; } else if (strcasecmp(milter->def_action, "tempfail") == 0) { reply = "451 4.7.1 Service unavailable - try again later"; + } else if (strcasecmp(milter->def_action, "shutdown") == 0) { + reply = "421 4.7.1 Service unavailable - try again later"; } else if (strcasecmp(milter->def_action, "quarantine") == 0) { reply = "Hdefault_action"; } else { diff -Nru postfix-3.10.5/src/postconf/postconf_edit.c postfix-3.10.10/src/postconf/postconf_edit.c --- postfix-3.10.5/src/postconf/postconf_edit.c 2025-10-23 20:03:49.000000000 +0000 +++ postfix-3.10.10/src/postconf/postconf_edit.c 2025-11-25 17:20:32.000000000 +0000 @@ -209,8 +209,10 @@ msg_panic("pcf_edit_main: unknown mode %d", mode); } if ((cvalue = htable_find(table, pattern)) != 0) { - msg_warn("ignoring earlier request: '%s = %s'", - pattern, cvalue->value); + if (edit_value && cvalue->value + && strcmp(edit_value, cvalue->value) != 0) + msg_warn("ignoring earlier request: '%s = %s'", + pattern, cvalue->value); htable_delete(table, pattern, myfree); } cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue)); diff -Nru postfix-3.10.5/src/smtp/smtp_tls_policy.c postfix-3.10.10/src/smtp/smtp_tls_policy.c --- postfix-3.10.5/src/smtp/smtp_tls_policy.c 2025-10-24 14:41:15.000000000 +0000 +++ postfix-3.10.10/src/smtp/smtp_tls_policy.c 2025-11-25 19:31:07.000000000 +0000 @@ -187,9 +187,16 @@ } else #endif aname = name; - for (pattp = tls->ext_mx_host_patterns->argv; *pattp; pattp++) - if (match_sts_mx_host_pattern(*pattp, aname)) + for (pattp = tls->ext_mx_host_patterns->argv; *pattp; pattp++) { + if (match_sts_mx_host_pattern(*pattp, aname)) { + if (msg_verbose) + msg_info("MX name '%s' matches STS MX pattern for '%s'", + aname, tls->ext_policy_domain ? tls->ext_policy_domain : ""); return (1); + } + } + msg_warn("MX name '%s' does not match STS MX pattern for '%s'", + aname, tls->ext_policy_domain ? tls->ext_policy_domain : ""); return (0); } /* No applicable policy name patterns. */ @@ -725,8 +732,13 @@ if (STATE_TLS_NOT_REQUIRED(iter->parent)) { if (msg_verbose) msg_info("%s: no tls policy lookup", __func__); - if (tls->level > TLS_LEV_MAY) - tls->level = TLS_LEV_MAY; + if (var_smtp_tls_wrappermode) { + if (tls->level > TLS_LEV_ENCRYPT) + tls->level = TLS_LEV_ENCRYPT; + } else { + 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) { diff -Nru postfix-3.10.5/src/smtp/smtp_tlsrpt.c postfix-3.10.10/src/smtp/smtp_tlsrpt.c --- postfix-3.10.5/src/smtp/smtp_tlsrpt.c 2024-10-10 22:15:24.000000000 +0000 +++ postfix-3.10.10/src/smtp/smtp_tlsrpt.c 2025-11-25 19:23:32.000000000 +0000 @@ -306,13 +306,15 @@ if (tls->ext_policy_type == 0) msg_panic("smtp_tlsrpt_set_ext_policy: no policy type"); +#define ARGV_OR_NULL(ap) ((ap) ? (ap)->argv : 0) + switch (policy_type_val = convert_tlsrpt_policy_type(tls->ext_policy_type)) { case TLSRPT_POLICY_STS: trw_set_tls_policy(state->tlsrpt, policy_type_val, - (const char *const *) tls->ext_policy_strings->argv, + (const char *const *) ARGV_OR_NULL(tls->ext_policy_strings), tls->ext_policy_domain, - (const char *const *) tls->ext_mx_host_patterns->argv); + (const char *const *) ARGV_OR_NULL(tls->ext_mx_host_patterns)); break; case TLSRPT_NO_POLICY_FOUND: smtp_tlsrpt_set_no_policy(state); diff -Nru postfix-3.10.5/src/tls/tls_prng_file.c postfix-3.10.10/src/tls/tls_prng_file.c --- postfix-3.10.5/src/tls/tls_prng_file.c 2014-12-07 01:35:33.000000000 +0000 +++ postfix-3.10.10/src/tls/tls_prng_file.c 2026-05-01 20:02:52.000000000 +0000 @@ -132,7 +132,8 @@ RAND_seed(buffer, count); } if (msg_verbose) - msg_info("read %ld bytes from entropy file %s: %m", + /* 202604 Claude: remove '%m' from non-error logging. */ + msg_info("read %ld bytes from entropy file %s", (long) (len - to_read), fh->name); return (len - to_read); } diff -Nru postfix-3.10.5/src/util/argv.c postfix-3.10.10/src/util/argv.c --- postfix-3.10.5/src/util/argv.c 2025-01-17 21:21:02.000000000 +0000 +++ postfix-3.10.10/src/util/argv.c 2026-05-01 20:02:52.000000000 +0000 @@ -203,6 +203,9 @@ argvp = (ARGV *) mymalloc(sizeof(*argvp)); argvp->len = 0; sane_len = (len < 2 ? 2 : len); + /* 202604 Claude: avoid overflowing sane_len + 1 */ + if (sane_len > SSIZE_MAX - 1) + msg_panic("argv_alloc: array length overflow"); argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *)); argvp->len = sane_len; argvp->argc = 0; @@ -261,6 +264,9 @@ { ssize_t new_len; + /* 202604 Claude: avoid overflowing (new_len + 1) * sizeof(char *). */ + if (argvp->len > SSIZE_MAX / (2 * sizeof(char *)) - 1) + msg_panic("argv_extend: array length overflow"); new_len = argvp->len * 2; argvp->argv = (char **) myrealloc((void *) argvp->argv, (new_len + 1) * sizeof(char *)); @@ -404,9 +410,10 @@ ssize_t pos; /* - * Sanity check. + * Sanity check. 202604 Claude: avoid expression 'first + how_many'. */ - if (first < 0 || how_many < 0 || first + how_many > argvp->argc) + if (first < 0 || how_many < 0 || first > argvp->argc + || how_many > argvp->argc - first) msg_panic("argv_delete bad range: (start=%ld count=%ld)", (long) first, (long) how_many); diff -Nru postfix-3.10.5/src/util/dict_cdb.c postfix-3.10.10/src/util/dict_cdb.c --- postfix-3.10.5/src/util/dict_cdb.c 2025-01-09 21:32:35.000000000 +0000 +++ postfix-3.10.10/src/util/dict_cdb.c 2026-05-01 20:02:52.000000000 +0000 @@ -456,7 +456,8 @@ } #ifndef NO_FTRUNCATE - if (st0.st_size) + /* 202604 Claude: use the post-myflock() fstat result. */ + if (st1.st_size) ftruncate(fd, 0); #endif diff -Nru postfix-3.10.5/src/util/dict_cidr.c postfix-3.10.10/src/util/dict_cidr.c --- postfix-3.10.5/src/util/dict_cidr.c 2023-04-16 20:41:47.000000000 +0000 +++ postfix-3.10.10/src/util/dict_cidr.c 2026-05-01 20:02:52.000000000 +0000 @@ -237,11 +237,14 @@ rule->lineno = lineno; if (msg_verbose) { - if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, - hostaddr.buf, sizeof(hostaddr.buf)) == 0) - msg_fatal("inet_ntop: %m"); - msg_info("dict_cidr_open: add %s/%d %s", - hostaddr.buf, cidr_info.mask_shift, rule->value); + /* 202604 Claude: ENDIF has no address pattern. */ + if (cidr_info.op != CIDR_MATCH_OP_ENDIF) { + if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, + hostaddr.buf, sizeof(hostaddr.buf)) == 0) + msg_fatal("inet_ntop: %m"); + msg_info("dict_cidr_open: add %s/%d %s", + hostaddr.buf, cidr_info.mask_shift, rule->value); + } } return (rule); } diff -Nru postfix-3.10.5/src/util/dict_db.c postfix-3.10.10/src/util/dict_db.c --- postfix-3.10.5/src/util/dict_db.c 2025-10-23 20:00:35.000000000 +0000 +++ postfix-3.10.10/src/util/dict_db.c 2026-05-01 20:03:50.000000000 +0000 @@ -446,8 +446,10 @@ */ switch (function) { case DICT_SEQ_FUN_FIRST: - if (dict_db->cursor == 0) - DICT_DB_CURSOR(db, &(dict_db->cursor)); + if (dict_db->cursor == 0 + && (status = DICT_DB_CURSOR(db, &(dict_db->cursor))) != 0) + msg_fatal("error [%d] initializing cursor for %s: %m", + status, dict_db->dict.name); db_function = DB_FIRST; break; case DICT_SEQ_FUN_NEXT: diff -Nru postfix-3.10.5/src/util/dict_pcre.c postfix-3.10.10/src/util/dict_pcre.c --- postfix-3.10.5/src/util/dict_pcre.c 2023-04-16 20:42:29.000000000 +0000 +++ postfix-3.10.10/src/util/dict_pcre.c 2026-05-01 20:02:52.000000000 +0000 @@ -728,6 +728,9 @@ } engine->match_data = pcre2_match_data_create_from_pattern( engine->pattern, (void *) 0); + /* 202604 Claude: handle error result. */ + if (engine->match_data == 0) + msg_fatal("out of memory in pcre2_match_data_create_from_pattern()"); #endif return (1); } diff -Nru postfix-3.10.5/src/util/dict_sockmap.c postfix-3.10.10/src/util/dict_sockmap.c --- postfix-3.10.5/src/util/dict_sockmap.c 2025-01-07 22:30:28.000000000 +0000 +++ postfix-3.10.10/src/util/dict_sockmap.c 2026-05-01 20:02:52.000000000 +0000 @@ -254,7 +254,8 @@ reply_payload = split_at(STR(dp->rdwr_buf), ' '); if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_OK) == 0) { dict->error = 0; - return (reply_payload); + /* 202604 Claude: don't return NULL with dict->error==0. */ + return (reply_payload ? reply_payload : ""); } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_NOTFOUND) == 0) { dict->error = 0; return (0); diff -Nru postfix-3.10.5/src/util/midna_domain.c postfix-3.10.10/src/util/midna_domain.c --- postfix-3.10.5/src/util/midna_domain.c 2024-11-21 17:49:05.000000000 +0000 +++ postfix-3.10.10/src/util/midna_domain.c 2026-05-01 20:02:52.000000000 +0000 @@ -189,11 +189,13 @@ */ idna = uidna_openUTS46(midna_domain_transitional ? UIDNA_DEFAULT : UIDNA_NONTRANSITIONAL_TO_ASCII, &error); - anl = uidna_nameToASCII_UTF8(idna, - name, strlen(name), - buf, sizeof(buf) - 1, - &info, - &error); + /* 202604 Claude: avoid null deref after uidna_openUTS46() failure. */ + if (idna && U_SUCCESS(error)) + anl = uidna_nameToASCII_UTF8(idna, + name, strlen(name), + buf, sizeof(buf) - 1, + &info, + &error); uidna_close(idna); /* @@ -203,7 +205,7 @@ * "fake" A-labels, as required by UTS 46 section 4.1, but we rely on * valid_hostname() on the output side just to be sure. */ - if (U_SUCCESS(error) && info.errors == 0 && anl > 0) { + if (idna && U_SUCCESS(error) && info.errors == 0 && anl > 0) { buf[anl] = 0; /* XXX */ if (!valid_hostname(buf, DONT_GRIPE)) { msg_warn("%s: Problem translating domain \"%.100s\" to ASCII form: %s", @@ -243,11 +245,13 @@ */ idna = uidna_openUTS46(midna_domain_transitional ? UIDNA_DEFAULT : UIDNA_NONTRANSITIONAL_TO_UNICODE, &error); - anl = uidna_nameToUnicodeUTF8(idna, - name, strlen(name), - buf, sizeof(buf) - 1, - &info, - &error); + /* 202604 Claude: avoid null deref after uidna_openUTS46() failure. */ + if (idna && U_SUCCESS(error)) + anl = uidna_nameToUnicodeUTF8(idna, + name, strlen(name), + buf, sizeof(buf) - 1, + &info, + &error); uidna_close(idna); /* @@ -256,7 +260,7 @@ * other invalid forms that are not covered in UTS 46, section 4.1). We * rely on midna_domain_to_ascii() to validate the output. */ - if (U_SUCCESS(error) && info.errors == 0 && anl > 0) { + if (idna && U_SUCCESS(error) && info.errors == 0 && anl > 0) { buf[anl] = 0; /* XXX */ if (midna_domain_to_ascii(buf) == 0) return (0); diff -Nru postfix-3.10.5/src/util/msg_vstream.c postfix-3.10.10/src/util/msg_vstream.c --- postfix-3.10.5/src/util/msg_vstream.c 2006-06-15 18:07:16.000000000 +0000 +++ postfix-3.10.10/src/util/msg_vstream.c 2026-02-18 16:52:29.000000000 +0000 @@ -80,6 +80,7 @@ msg_tag = name; msg_stream = vp; + vstream_no_debug(vp); if (first_call) { first_call = 0; msg_output(msg_vstream_print); diff -Nru postfix-3.10.5/src/util/netstring.c postfix-3.10.10/src/util/netstring.c --- postfix-3.10.5/src/util/netstring.c 2025-01-07 22:30:28.000000000 +0000 +++ postfix-3.10.10/src/util/netstring.c 2026-05-01 20:02:52.000000000 +0000 @@ -305,14 +305,17 @@ VA_COPY(ap2, ap); /* - * Figure out the total result size. + * Figure out the total result size. 202604 Claude: move the wrap-around + * guard inside the loop. */ - for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len) + for (total = 0; (data = va_arg(ap, char *)) != 0; /* see below */ ) { if ((data_len = va_arg(ap, ssize_t)) < 0) msg_panic("%s: bad data length %ld", myname, (long) data_len); + if (data_len > SSIZE_T_MAX - total) + msg_panic("%s: total length overflow", myname); + total += data_len; + } va_end(ap); - if (total < 0) - msg_panic("%s: bad total length %ld", myname, (long) total); if (msg_verbose > 1) msg_info("%s: write total length %ld", myname, (long) total); diff -Nru postfix-3.10.5/src/util/sys_defs.h postfix-3.10.10/src/util/sys_defs.h --- postfix-3.10.5/src/util/sys_defs.h 2025-01-03 18:19:26.000000000 +0000 +++ postfix-3.10.10/src/util/sys_defs.h 2026-05-15 19:34:03.000000000 +0000 @@ -31,14 +31,15 @@ || defined(FREEBSD5) || defined(FREEBSD6) || defined(FREEBSD7) \ || defined(FREEBSD8) || defined(FREEBSD9) || defined(FREEBSD10) \ || defined(FREEBSD11) || defined(FREEBSD12) || defined(FREEBSD13) \ - || defined(FREEBSD14) \ + || defined(FREEBSD14) || defined(FREEBSD15) || defined(FREEBSD16) \ || defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \ || defined(OPENBSD2) || defined(OPENBSD3) || defined(OPENBSD4) \ || defined(OPENBSD5) || defined(OPENBSD6) || defined(OPENBSD7) \ + || defined(OPENBSD8) \ || defined(NETBSD1) || defined(NETBSD2) || defined(NETBSD3) \ || defined(NETBSD4) || defined(NETBSD5) || defined(NETBSD6) \ || defined(NETBSD7) | defined(NETBSD8) || defined(NETBSD9) \ - || defined(NETBSD10) \ + || defined(NETBSD10) || defined(NETBSD11) || defined(NETBSD12) \ || defined(EKKOBSD1) || defined(DRAGONFLY) #define SUPPORTED #include @@ -796,8 +797,6 @@ #if HAVE_GLIBC_API_VERSION_SUPPORT(2, 1) #define SOCKADDR_SIZE socklen_t #define SOCKOPT_SIZE socklen_t -#else -#define NO_SNPRINTF #endif #ifndef NO_IPV6 #define HAS_IPV6 @@ -1700,13 +1699,15 @@ /* * Bit banging!! There is no official constant that defines the INT_MAX - * equivalent for off_t, ssize_t, etc. Wietse came up with the following - * macro that works as long as off_t, ssize_t, etc. use one's or two's - * complement logic (that is, the maximum value is binary 01...1). Don't use - * right-shift for signed types: the result is implementation-defined. + * equivalent for off_t, ssize_t, etc. Decades ago, Wietse came up with a + * macro that worked on one's or two's complement logic (that is, the + * maximum value is binary 01...1). As Kamil Frankowicz pointed out, that + * code relied on shifting into the sign bit, which is not defined in the + * language standard. The current version still works on one's and two's + * complement logic, but avoids the undefined behavior. */ #include -#define __MAXINT__(T) ((T) ~(((T) 1) << ((sizeof(T) * CHAR_BIT) - 1))) +#define __MAXINT__(T) ((((T) 1 << (sizeof(T) * CHAR_BIT - 2)) - 1) * 2 + 1) #ifndef OFF_T_MAX #define OFF_T_MAX __MAXINT__(off_t) #endif diff -Nru postfix-3.10.5/src/util/vbuf_print.c postfix-3.10.10/src/util/vbuf_print.c --- postfix-3.10.5/src/util/vbuf_print.c 2024-09-17 21:40:42.000000000 +0000 +++ postfix-3.10.10/src/util/vbuf_print.c 2026-05-15 18:37:50.000000000 +0000 @@ -103,12 +103,16 @@ /* * Helper macros... Note that there is no need to check the result from - * VSTRING_SPACE() because that always succeeds or never returns. + * VSTRING_SPACE() because that always succeeds or never returns. 202406 + * Claude: avoid integer overflow in field width computations. */ #ifndef NO_SNPRINTF -#define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \ +#define VBUF_SNPRINTF(bp, width_or_prec, type_space, fmt, arg) do { \ ssize_t _ret; \ - if (VBUF_SPACE((bp), (sz)) != 0) \ + if ((width_or_prec) > INT_MAX - (type_space)) \ + msg_panic("vbuf_print: field width (%d + %lu) > INT_MAX", \ + (width_or_prec), (unsigned long) (type_space)); \ + if (VBUF_SPACE((bp), (width_or_prec) + (type_space)) != 0) \ return (bp); \ _ret = snprintf((char *) (bp)->ptr, (bp)->cnt, (fmt), (arg)); \ if (_ret < 0) \ @@ -119,8 +123,11 @@ VBUF_SKIP(bp); \ } while (0) #else -#define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \ - if (VBUF_SPACE((bp), (sz)) != 0) \ +#define VBUF_SNPRINTF(bp, width_or_prec, type_space, fmt, arg) do { \ + if ((width_or_prec) > INT_MAX - (type_space)) \ + msg_panic("vbuf_print: field width (%d + %lu) > INT_MAX", \ + (width_or_prec), (unsigned long) (type_space)); \ + if (VBUF_SPACE((bp), (width_or_prec) + (type_space)) != 0) \ return (bp); \ sprintf((char *) (bp)->ptr, (fmt), (arg)); \ VBUF_SKIP(bp); \ @@ -133,7 +140,7 @@ } while (0) #define VSTRING_ADDNUM(vp, n) do { \ - VBUF_SNPRINTF(&(vp)->vbuf, INT_SPACE, "%d", n); \ + VBUF_SNPRINTF(&(vp)->vbuf, 0, INT_SPACE, "%d", n); \ } while (0) #define VBUF_STRCAT(bp, s) do { \ @@ -260,7 +267,7 @@ msg_panic("%s: %%l%c is not supported", myname, *cp); s = va_arg(ap, char *); if (prec >= 0 || (width > 0 && width > strlen(s))) { - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), INT_SPACE, vstring_str(fmt), s); } else { VBUF_STRCAT(bp, s); @@ -276,17 +283,17 @@ case 'x': case 'X': if (long_flag) - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), INT_SPACE, vstring_str(fmt), va_arg(ap, long)); else - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), INT_SPACE, vstring_str(fmt), va_arg(ap, int)); break; case 'e': /* float-valued argument */ case 'f': case 'g': /* C99 *printf ignore the 'l' modifier. */ - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + DBL_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), DBL_SPACE, vstring_str(fmt), va_arg(ap, double)); break; case 'm': @@ -296,7 +303,7 @@ case 'p': if (long_flag) msg_panic("%s: %%l%c is not supported", myname, *cp); - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + PTR_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), PTR_SPACE, vstring_str(fmt), va_arg(ap, char *)); break; default: /* anything else is bad */ diff -Nru postfix-3.10.5/src/util/vstream.c postfix-3.10.10/src/util/vstream.c --- postfix-3.10.5/src/util/vstream.c 2023-05-16 14:12:59.000000000 +0000 +++ postfix-3.10.10/src/util/vstream.c 2026-02-18 16:52:29.000000000 +0000 @@ -166,6 +166,9 @@ /* int vstream_fstat(stream, flags) /* VSTREAM *stream; /* int flags; +/* +/* void vstream_no_debug(stream) +/* VSTREAM *stream; /* DESCRIPTION /* The \fIvstream\fR module implements light-weight buffered I/O /* similar to the standard I/O routines. @@ -494,6 +497,10 @@ /* .IP VSTREAM_FLAG_OWN_VSTRING /* The stream 'owns' the VSTRING buffer, and is responsible /* for cleaning up when the stream is closed. +/* +/* vstream_no_debug() disables 'spontaneous' logging of output +/* activity on the last specified VSTREAM, to prevent recursive +/* logging. /* DIAGNOSTICS /* Panics: interface violations. Fatal errors: out of memory. /* SEE ALSO @@ -674,6 +681,8 @@ } \ } while (0) +static VSTREAM *vstream_log_veto; + /* vstream_buf_init - initialize buffer */ static void vstream_buf_init(VBUF *bp, int flags) @@ -771,7 +780,7 @@ used = bp->len - bp->cnt; left_over = used - to_flush; - if (msg_verbose > 2 && stream != VSTREAM_ERR) + if (msg_verbose > 2 && stream != vstream_log_veto) msg_info("%s: fd %d flush %ld", myname, stream->fd, (long) to_flush); if (to_flush < 0 || left_over < 0) msg_panic("%s: bad to_flush %ld", myname, (long) to_flush); @@ -834,7 +843,7 @@ } } } - if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush) + if (msg_verbose > 2 && stream != vstream_log_veto && n != to_flush) msg_info("%s: %d flushed %ld/%ld", myname, stream->fd, (long) n, (long) to_flush); } @@ -1890,6 +1899,13 @@ return (stream); } +/* vstream_no_debug - debug logging lockout */ + +void vstream_no_debug(VSTREAM *stream) +{ + vstream_log_veto = stream; +} + #ifdef TEST static void copy_line(ssize_t bufsize) diff -Nru postfix-3.10.5/src/util/vstream.h postfix-3.10.10/src/util/vstream.h --- postfix-3.10.5/src/util/vstream.h 2021-08-08 12:25:14.000000000 +0000 +++ postfix-3.10.10/src/util/vstream.h 2026-02-18 16:52:29.000000000 +0000 @@ -274,6 +274,11 @@ vstream_memreopen((VSTREAM *) 0, (string), (flags)) VSTREAM *vstream_memreopen(VSTREAM *, struct VSTRING *, int); + /* + * Debug logging lockout. + */ +extern void vstream_no_debug(VSTREAM *); + /* LICENSE /* .ad /* .fi diff -Nru postfix-3.10.5/src/util/vstring.h postfix-3.10.10/src/util/vstring.h --- postfix-3.10.5/src/util/vstring.h 2020-09-25 20:41:00.000000000 +0000 +++ postfix-3.10.10/src/util/vstring.h 2026-05-15 18:37:50.000000000 +0000 @@ -62,7 +62,7 @@ /* Flags 24..31 are reserved for VSTRING. */ #define VSTRING_FLAG_EXACT (1<<24) /* exact allocation for tests */ -#define VSTRING_FLAG_MASK (255 << 24) +#define VSTRING_FLAG_MASK (255U << 24) /* * Macros. Unsafe macros have UPPERCASE names.