[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1111798: trixie-pu: package postfix/3.10.4-1~deb13u1



Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: postfix@packages.debian.org
Control: affects -1 + src:postfix
User: release.debian.org@packages.debian.org
Usertags: pu

[ Reason ]
There's an upstream stable/bugfix release fixing a handful of
issues found in 3.10.3 version of postfix.

Additionally there's a fix for chroot update procedure which
I wanted to do for quite some time, -- see #1100100 & #1110704.
I had to write a simple file copy procedure in perl because no
existing basic tools has what's needed.

[ Tests ]
There's a minimal internal postfix testsuite, which works.
Additionally, I use this release in production already, without
any issues.  The new file copy procedure has been tested in
forky already, also not causing issues.

[ Risks ]
Postfix upstream stable series has always been an excellent example
of stability (actually it's been this way with all postfix releases).
So I don't expect risks from the changes in postfix itself.

However, the new chroot sync procedure might cause effects, - it's
a large-ish change for debian stable series.  I did try to write it
in a most accurate way and verified it from various points of view,
though.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Upstream chages comes in a form of release notes in d/changelog, below,
also in the diff for upstream HISTORY file.

The change in the chroot sync procedure is described in the commit
message and - briefly - in d/syncfiles.pl.  See
https://salsa.debian.org/postfix-team/postfix-dev/-/commit/d4e1171ae6a79d36e7121bb0698eef3bd3f2498b
for the commit in question.

Thanks,

/mjt

diff -Nru postfix-3.10.3/debian/changelog postfix-3.10.4/debian/changelog
--- postfix-3.10.3/debian/changelog	2025-07-11 00:50:43.000000000 +0300
+++ postfix-3.10.4/debian/changelog	2025-08-22 09:51:46.000000000 +0300
@@ -1,3 +1,65 @@
+postfix (3.10.4-1~deb13u1) trixie; urgency=medium
+
+  * New upstream stable/bugfix version 3.10.4, with a handful of fixes.
+    From the upstream release notes:
+    - Fixes for postscreen(8):
+      * Bugfix (defect introduced: Postfix 2.2, date 20050203): after
+        detecting a lookup table change, and after starting a new
+        postscreen process, the old postscreen process logged an ENOTSOCK
+        error while attempting to accept a connection on a socket that
+        it was no longer listening on. This error was introduced first
+        in the multi_server skeleton code, and was five years later
+        duplicated in the event_server skeleton that was created for
+        postscreen. Problem reported by Florian Piekert.
+      * Bugfix (defect introduced: Postfix 2.8, date 20101230):
+        after detecting a cache table change and before starting a new
+        postscreen process, the old postscreen process did not close the
+        postscreen_cache_map, and therefore kept an exclusive lock that
+        could prevent a new postscreen process from starting. Problem
+        reported by Florian Piekert.
+    - Fixes for tlsproxy(8):
+      * Bugfix (defect introduced: Postfix 3.7): incorrect backwards
+        compatible support for the legacy configuration parameters
+        tlsproxy_client_level and tlsproxy_client_policy. This
+        disabled the tlsproxy TLS client role when a legacy parameter
+        was set (instead of the newer tlsproxy_client_security_level
+        or tlsproxy_client_policy_maps). Reported by John Doe,
+        diagnosed by Viktor Dukhovni.
+      * Bugfix (defect introduced: Postfix 3.4): with the TLS client role
+        disabled by configuration, the tlsproxy daemon dereferenced a
+        null pointer while handling a tlsproxy client request. Reported by
+        John Doe.
+    - Reducing process churn: Postfix daemons no longer automatically
+      restart after a btree:, dbm:, hash:, lmdb:, or sdbm: table file
+      modification time change, when they opened that table for writing.
+    - Portability: deleted an <openssl/engine.h> build dependency,
+      because the feature is being removed from OpenSSL, and Postfix
+      no longer needs it.
+    - Cleanup: with "tls_required_enable = yes", the Postfix SMTP client
+      will no longer maintain TLSRPT statistics for messages that contain
+      a "TLS-Required: no" header. This can prevent TLSRPT notifications
+      for TLSRPT notifications.
+    - Bugfix (defect introduced: Postfix 3.6, date 20200710): Postfix TLS
+      client code logged "Untrusted TLS connection" (wrong) instead of
+      "Trusted TLS connection" (right), for a new or resumed TLS session,
+      when a server offered a trusted (valid PKI trust chain) certificate
+      that did not match the expected server name pattern. Fix by Viktor
+      Dukhovni.
+  * d/gbp.conf: debian-branch=debian/trixie
+  * configure-instance.in: fix typo
+  * configure-instance.in: limit maxdepth=1 in /etc/ssl/certs dirs
+  * configure-instance.in: use home-grown file copy procedure to sync chroot
+    There are a few issues with using cp(1) to update files in chroot, -
+    a file should be copied even if the source date is *less* than the
+    target date (eg, if a package has been downgraded), which is not done
+    by `cp -u` (#1110704), a file should be copied atomically (copy+rename,
+    not truncate+copy), and care should be taken with extra attributes
+    (#1100100).  Use a simple perl-based script (using just perl-base)
+    to update files instead, which fixes all this stuff.
+    (Closes: #1100100, #1110704)
+
+ -- Michael Tokarev <mjt@tls.msk.ru>  Fri, 22 Aug 2025 09:51:46 +0300
+
 postfix (3.10.3-2) unstable; urgency=medium
 
   * d/changelog: fix wrongly formatted previous changelog entry (double email)
diff -Nru postfix-3.10.3/debian/configure-instance.in postfix-3.10.4/debian/configure-instance.in
--- postfix-3.10.3/debian/configure-instance.in	2025-04-24 10:52:30.000000000 +0300
+++ postfix-3.10.4/debian/configure-instance.in	2025-08-10 19:31:17.000000000 +0300
@@ -88,7 +88,7 @@
     [ -f /$file ] && cp="$cp /$file" || rm="$rm ./$file"
 done
 [ -n "$rm" ] && rm -f $rm
-[ -n "$cp" ] && cp -pLuf --parents -t . -- $cp
+[ -n "$cp" ] && /usr/lib/postfix/syncfiles.pl $queue_directory $cp
 
 if [ -z "$need_chroot" ]; then
     [ ! -d etc/ssl/certs ] || rm -rf etc/ssl/certs
@@ -135,7 +135,7 @@
 	(/*) [ -d $cadir ] || continue;;
 	(*) continue;;
     esac
-    case "$cadis_copied " in
+    case "$cadirs_copied " in
 	(*" $cadir "*) continue ;;
     esac
     cadirs_copied="$cadirs_copied $cadir"
@@ -158,11 +158,8 @@
 		[ -f "$caddr/$file" ] || rm -f "$file"
 	    done
     ) fi
-    mkdir -p $dest
-    ( cd $cadir
-	find -L . -name '[0-9a-f]*.[0-9]' -type f \
-	    -exec cp -pLuf -t $dest '{}' +
-    )
+    find -L $cadir -maxdepth 1 -name '[0-9a-f]*.[0-9]' -type f \
+	    -exec /usr/lib/postfix/syncfiles.pl $queue_directory '{}' +
 done
 
 mkdir -p usr/lib/sasl2 # https://bugs.debian.org/426338
diff -Nru postfix-3.10.3/debian/gbp.conf postfix-3.10.4/debian/gbp.conf
--- postfix-3.10.3/debian/gbp.conf	2025-04-24 10:52:30.000000000 +0300
+++ postfix-3.10.4/debian/gbp.conf	2025-08-22 09:50:46.000000000 +0300
@@ -2,6 +2,6 @@
 sign-tags = True
 pristine-tar = True
 upstream-branch = stable/v3.10
-debian-branch = debian/master
+debian-branch = debian/trixie
 debian-tag = v%(version)s
 upstream-tag = v%(version)s
diff -Nru postfix-3.10.3/debian/rules postfix-3.10.4/debian/rules
--- postfix-3.10.3/debian/rules	2025-06-18 11:38:50.000000000 +0300
+++ postfix-3.10.4/debian/rules	2025-08-22 09:04:44.000000000 +0300
@@ -239,6 +239,7 @@
 	sed 's/@MULTIARCH@/${DEB_HOST_MULTIARCH}/' debian/configure-instance.in > \
 		${base}${prvlibdir}/configure-instance.sh
 	chmod 0755 ${base}${prvlibdir}/configure-instance.sh
+	install -m0755 debian/syncfiles.pl ${base}${prvlibdir}/
 	echo ${package}:Provides=$(if $(filter ${DEB_VENDOR},Ubuntu),default-mta) \
 		>> debian/${package}.substvars
 	install -m0644 debian/postfix.ufw.profile -DT ${base}/etc/ufw/applications.d/postfix
diff -Nru postfix-3.10.3/debian/syncfiles.pl postfix-3.10.4/debian/syncfiles.pl
--- postfix-3.10.3/debian/syncfiles.pl	1970-01-01 03:00:00.000000000 +0300
+++ postfix-3.10.4/debian/syncfiles.pl	2025-08-10 19:06:15.000000000 +0300
@@ -0,0 +1,67 @@
+#! /usr/bin/perl -W
+#
+# Update a set of files in the given chroot dir.
+# Decision to update is based on file size and date, -
+# if any of these don't match, whole file is copied
+# to a temp file and renamed into place.
+# In the destination, files are copied with full path.
+# Only regular files (or symlinks to regular files)
+# are copied.
+
+use strict;
+use Fcntl qw(O_RDONLY O_WRONLY O_EXCL O_CREAT S_IMODE S_ISREG);
+use Errno qw(EEXIST ENOENT);
+
+my $dest = shift @ARGV;
+
+-d "$dest" or die "not a directory: $dest\n";
+
+foreach my $snm (@ARGV) {
+
+  sysopen SRC, $snm, O_RDONLY
+    and my @sst = stat SRC
+      or die "unable to open $snm: $!\n";
+
+  my $dnm = "$dest/$snm";
+  if (my @dst = stat $dnm) {
+    next if $sst[7] == $dst[7] && $sst[9] == $dst[9];
+  }
+
+  die "$snm: not a regular file\n"
+    unless S_ISREG($sst[2]);
+
+  print "updating $snm => $dnm\n";
+
+  my $dtnm = "$dnm.tmp";
+  unlink "$dtnm";
+
+  while (!sysopen DST, $dtnm, O_WRONLY|O_EXCL|O_CREAT, 0600) {
+    $! == ENOENT or die "unable to create $dtnm: $!\n";
+    my @c = split /\/+/, $snm;
+    pop @c; # all but last component
+    my $dd = $dest;
+    foreach my $c (@c) {
+      $dd .= '/' . $c;
+      unless (mkdir $dd, 0755) {
+        die "unable to mkdir $dd: $!" unless $! == EEXIST;
+      }
+    }
+  }
+
+  my $r;
+  while($r = sysread(SRC,$_,64*1024)) {
+    syswrite(DST,$_) or die "error writing to $dtnm: $!\n";
+  }
+  $r == 0 or die "error reading from $snm: $!\n";
+  close SRC;
+
+  utime $sst[9], $sst[9], \*DST;
+  chmod S_IMODE($sst[2]), \*DST;
+  chown $sst[4], $sst[5], \*DST;
+
+  close DST and rename $dtnm, $dnm
+    or die "error renaming $dtnm to $dnm: $!\n";
+
+}
+
+exit 0;
diff -Nru postfix-3.10.3/HISTORY postfix-3.10.4/HISTORY
--- postfix-3.10.3/HISTORY	2025-07-10 00:56:05.000000000 +0300
+++ postfix-3.10.4/HISTORY	2025-08-19 01:44:50.000000000 +0300
@@ -29054,3 +29054,71 @@
 	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.
+
+20250710
+
+	Bugfix (defect introduced: postfix-2.2, date 20050203):
+	after detecting a lookup table change, and after starting
+	a new postscreen process, the old postscreen process logged
+	an ENOTSOCK error while attempting to accept a connection
+	on a socket that it was no longer listening on. This error
+	was introduced first in the multi_server skeleton code, and
+	was five years later duplicated in the event_server skeleton
+	that was created for postscreen. Problem reported by Florian
+	Piekert. Files: master/multi_server.c, master/event_server.c.
+
+20250714
+
+	Deleted an <openssl/engine.h> dependency, because the feature is
+	being removed from OpenSSL, and Postfix no longer needs it. File:
+	posttls-finger/posttls-finger.c.
+
+20250716
+
+	Bugfix (defect introduced: Postfix 2.8, date 20101230):
+	after detecting a cache table change and before starting a
+	new postscreen process, the old postscreen process did not
+	close the postscreen_cache_map, and therefore kept an
+	exclusive lock that could prevent a new postscreen process
+	from starting. Problem reported by Florian Piekert. File:
+	postscreen/postscreen.c.
+
+20250717
+
+	Workaround: Postfix daemons no longer automatically restart
+	after a btree:, dbm:, hash:, lmdb:, or sdbm: table file
+	modification time change, when they opened that table for
+	writing. Files: util/dict.c, util/dict_db.c, util/dict_dbm.c,
+	util/dict_lmdb.c, util/dict_sdbm.c.
+
+20250730
+
+	Bugfix (defect introduced: Postfix 3.6, date 20200710):
+	Postfix TLS client code logged "Untrusted TLS connection"
+	(wrong) instead of "Trusted TLS connection" (right), for a
+	new or resumed TLS session, when a server offered a trusted
+	(valid PKI trust chain) certificate that did not match the
+	expected server name pattern. Viktor Dukhovni. Files:
+	tls/tls_client.c, tls/tls_verify.c.
+
+20250801
+
+	Bugfix (defect introduced: Postfix 3.7): incorrect backwards
+	compatible support for the legacy configuration parameters
+	tlsproxy_client_level and tlsproxy_client_policy. This
+	disabled the tlsproxy TLS client role when a legacy parameter
+	was set. Reported by John Doe, diagnosed by Viktor Dukhovni.
+	File: global/mail_params.h.
+
+	Bugfix (defect introduced: Postfix 3.4): with the TLS client
+	role disabled by configuration, the tlsproxy daemon
+	dereferenced a null pointer while handling a tlsproxy client
+	request. Reported by John Doe. File: tlsproxy/tlsproxy.c.
+
+20250803
+
+	Cleanup: with "tls_required_enable = yes", the Postfix SMTP
+	client will no longer maintain TLSRPT statistics for
+	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.
diff -Nru postfix-3.10.3/src/global/mail_params.h postfix-3.10.4/src/global/mail_params.h
--- postfix-3.10.3/src/global/mail_params.h	2025-02-15 23:05:04.000000000 +0300
+++ postfix-3.10.4/src/global/mail_params.h	2025-08-18 23:06:41.000000000 +0300
@@ -4171,7 +4171,9 @@
 /* Migrate an incorrect name. */
 #define OBS_TLSP_CLNT_LEVEL		"tlsproxy_client_level"
 #define VAR_TLSP_CLNT_LEVEL		"tlsproxy_client_security_level"
-#define DEF_TLSP_CLNT_LEVEL		"${" OBS_TLSP_CLNT_LEVEL ":$" VAR_SMTP_TLS_LEVEL "}"
+#define DEF_TLSP_CLNT_LEVEL		"${" OBS_TLSP_CLNT_LEVEL "?{$" \
+					OBS_TLSP_CLNT_LEVEL "}:{$" \
+					VAR_SMTP_TLS_LEVEL "}}"
 extern char *var_tlsp_clnt_level;
 
 #define VAR_TLSP_CLNT_PER_SITE		"tlsproxy_client_per_site"
@@ -4181,7 +4183,9 @@
 /* Migrate an incorrect name. */
 #define OBS_TLSP_CLNT_POLICY		"tlsproxy_client_policy"
 #define VAR_TLSP_CLNT_POLICY		"tlsproxy_client_policy_maps"
-#define DEF_TLSP_CLNT_POLICY		"${" OBS_TLSP_CLNT_POLICY ":$" VAR_SMTP_TLS_POLICY "}"
+#define DEF_TLSP_CLNT_POLICY		"${" OBS_TLSP_CLNT_POLICY "?{$" \
+					OBS_TLSP_CLNT_POLICY "}:{$" \
+					VAR_SMTP_TLS_POLICY "}}"
 extern char *var_tlsp_clnt_policy;
 
  /*
diff -Nru postfix-3.10.3/src/global/mail_version.h postfix-3.10.4/src/global/mail_version.h
--- postfix-3.10.3/src/global/mail_version.h	2025-07-10 00:54:38.000000000 +0300
+++ postfix-3.10.4/src/global/mail_version.h	2025-08-18 22:55:36.000000000 +0300
@@ -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	"20250710"
-#define MAIL_VERSION_NUMBER	"3.10.3"
+#define MAIL_RELEASE_DATE	"20250818"
+#define MAIL_VERSION_NUMBER	"3.10.4"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE	"-" MAIL_RELEASE_DATE
diff -Nru postfix-3.10.3/src/master/event_server.c postfix-3.10.4/src/master/event_server.c
--- postfix-3.10.3/src/master/event_server.c	2021-12-19 17:48:14.000000000 +0300
+++ postfix-3.10.4/src/master/event_server.c	2025-08-18 22:51:23.000000000 +0300
@@ -273,6 +273,7 @@
 static void (*event_server_pre_disconn) (VSTREAM *, char *, char **);
 static void (*event_server_slow_exit) (char *, char **);
 static int event_server_watchdog = 1000;
+static int event_server_drain_was_called = 0;
 
 /* event_server_exit - normal termination */
 
@@ -327,6 +328,9 @@
     const char *myname = "event_server_drain";
     int     fd;
 
+    if (event_server_drain_was_called)
+	return (0);
+
     switch (fork()) {
 	/* Try again later. */
     case -1:
@@ -343,6 +347,7 @@
 		msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
 	}
 	var_use_limit = 1;
+	event_server_drain_was_called = 1;
 	return (0);
 	/* Let the master start a new process. */
     default:
@@ -445,6 +450,9 @@
     int     time_left = -1;
     int     fd;
 
+    if (event_server_drain_was_called)
+	return;
+
     /*
      * Be prepared for accept() to fail because some other process already
      * got the connection (the number of processes competing for clients is
@@ -457,6 +465,8 @@
 
     if (event_server_pre_accept)
 	event_server_pre_accept(event_server_name, event_server_argv);
+    if (event_server_drain_was_called)
+	return;
     fd = LOCAL_ACCEPT(listen_fd);
     if (event_server_lock != 0
 	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
@@ -483,6 +493,9 @@
     int     fd;
     HTABLE *attr = 0;
 
+    if (event_server_drain_was_called)
+	return;
+
     /*
      * Be prepared for accept() to fail because some other process already
      * got the connection (the number of processes competing for clients is
@@ -495,6 +508,8 @@
 
     if (event_server_pre_accept)
 	event_server_pre_accept(event_server_name, event_server_argv);
+    if (event_server_drain_was_called)
+	return;
     fd = pass_accept_attr(listen_fd, &attr);
     if (event_server_lock != 0
 	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
@@ -520,6 +535,9 @@
     int     time_left = -1;
     int     fd;
 
+    if (event_server_drain_was_called)
+	return;
+
     /*
      * Be prepared for accept() to fail because some other process already
      * got the connection (the number of processes competing for clients is
@@ -532,6 +550,8 @@
 
     if (event_server_pre_accept)
 	event_server_pre_accept(event_server_name, event_server_argv);
+    if (event_server_drain_was_called)
+	return;
     fd = inet_accept(listen_fd);
     if (event_server_lock != 0
 	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
diff -Nru postfix-3.10.3/src/master/multi_server.c postfix-3.10.4/src/master/multi_server.c
--- postfix-3.10.3/src/master/multi_server.c	2021-12-19 17:48:14.000000000 +0300
+++ postfix-3.10.4/src/master/multi_server.c	2025-08-18 22:51:23.000000000 +0300
@@ -260,6 +260,7 @@
 static int multi_server_in_flow_delay;
 static unsigned multi_server_generation;
 static void (*multi_server_pre_disconn) (VSTREAM *, char *, char **);
+static int multi_server_drain_was_called = 0;
 
 /* multi_server_exit - normal termination */
 
@@ -295,6 +296,9 @@
     const char *myname = "multi_server_drain";
     int     fd;
 
+    if (multi_server_drain_was_called)
+	return (0);
+
     switch (fork()) {
 	/* Try again later. */
     case -1:
@@ -311,6 +315,7 @@
 		msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
 	}
 	var_use_limit = 1;
+	multi_server_drain_was_called = 1;
 	return (0);
 	/* Let the master start a new process. */
     default:
@@ -429,6 +434,9 @@
     int     time_left = -1;
     int     fd;
 
+    if (multi_server_drain_was_called)
+	return;
+
     /*
      * Be prepared for accept() to fail because some other process already
      * got the connection (the number of processes competing for clients is
@@ -441,6 +449,8 @@
 
     if (multi_server_pre_accept)
 	multi_server_pre_accept(multi_server_name, multi_server_argv);
+    if (multi_server_drain_was_called)
+	return;
     fd = LOCAL_ACCEPT(listen_fd);
     if (multi_server_lock != 0
 	&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
@@ -467,6 +477,9 @@
     int     fd;
     HTABLE *attr = 0;
 
+    if (multi_server_drain_was_called)
+	return;
+
     /*
      * Be prepared for accept() to fail because some other process already
      * got the connection (the number of processes competing for clients is
@@ -479,6 +492,8 @@
 
     if (multi_server_pre_accept)
 	multi_server_pre_accept(multi_server_name, multi_server_argv);
+    if (multi_server_drain_was_called)
+	return;
     fd = pass_accept_attr(listen_fd, &attr);
     if (multi_server_lock != 0
 	&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
@@ -504,6 +519,9 @@
     int     time_left = -1;
     int     fd;
 
+    if (multi_server_drain_was_called)
+	return;
+
     /*
      * Be prepared for accept() to fail because some other process already
      * got the connection (the number of processes competing for clients is
@@ -516,6 +534,8 @@
 
     if (multi_server_pre_accept)
 	multi_server_pre_accept(multi_server_name, multi_server_argv);
+    if (multi_server_drain_was_called)
+	return;
     fd = inet_accept(listen_fd);
     if (multi_server_lock != 0
 	&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
diff -Nru postfix-3.10.3/src/postscreen/postscreen.c postfix-3.10.4/src/postscreen/postscreen.c
--- postfix-3.10.3/src/postscreen/postscreen.c	2023-09-15 17:56:57.000000000 +0300
+++ postfix-3.10.4/src/postscreen/postscreen.c	2025-08-18 23:02:01.000000000 +0300
@@ -996,7 +996,7 @@
     if (new_event_time >= last_event_time + 1
 	&& (name = dict_changed_name()) != 0) {
 	msg_info("table %s has changed - finishing in the background", name);
-	event_server_drain();
+	psc_drain(unused_name, unused_argv);
     } else {
 	last_event_time = new_event_time;
     }
diff -Nru postfix-3.10.3/src/posttls-finger/posttls-finger.c postfix-3.10.4/src/posttls-finger/posttls-finger.c
--- postfix-3.10.3/src/posttls-finger/posttls-finger.c	2024-10-11 01:15:23.000000000 +0300
+++ postfix-3.10.4/src/posttls-finger/posttls-finger.c	2025-08-18 22:56:40.000000000 +0300
@@ -414,7 +414,6 @@
 
 #ifdef USE_TLS
 #include <tls_proxy.h>
-#include <openssl/engine.h>
 #endif
 
  /*
diff -Nru postfix-3.10.3/src/smtp/Makefile.in postfix-3.10.4/src/smtp/Makefile.in
--- postfix-3.10.3/src/smtp/Makefile.in	2025-01-16 22:12:41.000000000 +0300
+++ postfix-3.10.4/src/smtp/Makefile.in	2025-08-19 01:01:52.000000000 +0300
@@ -113,6 +113,7 @@
 smtp.o: ../../include/recipient_list.h
 smtp.o: ../../include/resolve_clnt.h
 smtp.o: ../../include/scache.h
+smtp.o: ../../include/sendopts.h
 smtp.o: ../../include/sock_addr.h
 smtp.o: ../../include/string_list.h
 smtp.o: ../../include/stringops.h
@@ -158,6 +159,7 @@
 smtp_addr.o: ../../include/recipient_list.h
 smtp_addr.o: ../../include/resolve_clnt.h
 smtp_addr.o: ../../include/scache.h
+smtp_addr.o: ../../include/sendopts.h
 smtp_addr.o: ../../include/sock_addr.h
 smtp_addr.o: ../../include/string_list.h
 smtp_addr.o: ../../include/stringops.h
@@ -304,6 +306,7 @@
 smtp_key.o: ../../include/recipient_list.h
 smtp_key.o: ../../include/resolve_clnt.h
 smtp_key.o: ../../include/scache.h
+smtp_key.o: ../../include/sendopts.h
 smtp_key.o: ../../include/sock_addr.h
 smtp_key.o: ../../include/string_list.h
 smtp_key.o: ../../include/sys_defs.h
@@ -344,6 +347,7 @@
 smtp_map11.o: ../../include/recipient_list.h
 smtp_map11.o: ../../include/resolve_clnt.h
 smtp_map11.o: ../../include/scache.h
+smtp_map11.o: ../../include/sendopts.h
 smtp_map11.o: ../../include/sock_addr.h
 smtp_map11.o: ../../include/string_list.h
 smtp_map11.o: ../../include/sys_defs.h
@@ -384,6 +388,7 @@
 smtp_misc.o: ../../include/recipient_list.h
 smtp_misc.o: ../../include/resolve_clnt.h
 smtp_misc.o: ../../include/scache.h
+smtp_misc.o: ../../include/sendopts.h
 smtp_misc.o: ../../include/sock_addr.h
 smtp_misc.o: ../../include/string_list.h
 smtp_misc.o: ../../include/sys_defs.h
@@ -491,6 +496,7 @@
 smtp_rcpt.o: ../../include/recipient_list.h
 smtp_rcpt.o: ../../include/resolve_clnt.h
 smtp_rcpt.o: ../../include/scache.h
+smtp_rcpt.o: ../../include/sendopts.h
 smtp_rcpt.o: ../../include/sent.h
 smtp_rcpt.o: ../../include/sock_addr.h
 smtp_rcpt.o: ../../include/string_list.h
@@ -530,6 +536,7 @@
 smtp_reuse.o: ../../include/recipient_list.h
 smtp_reuse.o: ../../include/resolve_clnt.h
 smtp_reuse.o: ../../include/scache.h
+smtp_reuse.o: ../../include/sendopts.h
 smtp_reuse.o: ../../include/sock_addr.h
 smtp_reuse.o: ../../include/string_list.h
 smtp_reuse.o: ../../include/stringops.h
@@ -572,6 +579,7 @@
 smtp_sasl_auth_cache.o: ../../include/recipient_list.h
 smtp_sasl_auth_cache.o: ../../include/resolve_clnt.h
 smtp_sasl_auth_cache.o: ../../include/scache.h
+smtp_sasl_auth_cache.o: ../../include/sendopts.h
 smtp_sasl_auth_cache.o: ../../include/sock_addr.h
 smtp_sasl_auth_cache.o: ../../include/string_list.h
 smtp_sasl_auth_cache.o: ../../include/stringops.h
@@ -613,6 +621,7 @@
 smtp_sasl_glue.o: ../../include/recipient_list.h
 smtp_sasl_glue.o: ../../include/resolve_clnt.h
 smtp_sasl_glue.o: ../../include/scache.h
+smtp_sasl_glue.o: ../../include/sendopts.h
 smtp_sasl_glue.o: ../../include/smtp_stream.h
 smtp_sasl_glue.o: ../../include/sock_addr.h
 smtp_sasl_glue.o: ../../include/split_at.h
@@ -657,6 +666,7 @@
 smtp_sasl_proto.o: ../../include/resolve_clnt.h
 smtp_sasl_proto.o: ../../include/sasl_mech_filter.h
 smtp_sasl_proto.o: ../../include/scache.h
+smtp_sasl_proto.o: ../../include/sendopts.h
 smtp_sasl_proto.o: ../../include/sock_addr.h
 smtp_sasl_proto.o: ../../include/string_list.h
 smtp_sasl_proto.o: ../../include/stringops.h
@@ -697,6 +707,7 @@
 smtp_session.o: ../../include/recipient_list.h
 smtp_session.o: ../../include/resolve_clnt.h
 smtp_session.o: ../../include/scache.h
+smtp_session.o: ../../include/sendopts.h
 smtp_session.o: ../../include/sock_addr.h
 smtp_session.o: ../../include/string_list.h
 smtp_session.o: ../../include/stringops.h
@@ -737,6 +748,7 @@
 smtp_state.o: ../../include/recipient_list.h
 smtp_state.o: ../../include/resolve_clnt.h
 smtp_state.o: ../../include/scache.h
+smtp_state.o: ../../include/sendopts.h
 smtp_state.o: ../../include/sock_addr.h
 smtp_state.o: ../../include/string_list.h
 smtp_state.o: ../../include/sys_defs.h
@@ -778,6 +790,7 @@
 smtp_tls_policy.o: ../../include/resolve_clnt.h
 smtp_tls_policy.o: ../../include/sane_strtol.h
 smtp_tls_policy.o: ../../include/scache.h
+smtp_tls_policy.o: ../../include/sendopts.h
 smtp_tls_policy.o: ../../include/sock_addr.h
 smtp_tls_policy.o: ../../include/string_list.h
 smtp_tls_policy.o: ../../include/stringops.h
@@ -821,6 +834,7 @@
 smtp_tlsrpt.o: ../../include/recipient_list.h
 smtp_tlsrpt.o: ../../include/resolve_clnt.h
 smtp_tlsrpt.o: ../../include/scache.h
+smtp_tlsrpt.o: ../../include/sendopts.h
 smtp_tlsrpt.o: ../../include/sock_addr.h
 smtp_tlsrpt.o: ../../include/string_list.h
 smtp_tlsrpt.o: ../../include/stringops.h
@@ -864,6 +878,7 @@
 smtp_trouble.o: ../../include/recipient_list.h
 smtp_trouble.o: ../../include/resolve_clnt.h
 smtp_trouble.o: ../../include/scache.h
+smtp_trouble.o: ../../include/sendopts.h
 smtp_trouble.o: ../../include/smtp_stream.h
 smtp_trouble.o: ../../include/sock_addr.h
 smtp_trouble.o: ../../include/string_list.h
@@ -903,6 +918,7 @@
 smtp_unalias.o: ../../include/recipient_list.h
 smtp_unalias.o: ../../include/resolve_clnt.h
 smtp_unalias.o: ../../include/scache.h
+smtp_unalias.o: ../../include/sendopts.h
 smtp_unalias.o: ../../include/sock_addr.h
 smtp_unalias.o: ../../include/string_list.h
 smtp_unalias.o: ../../include/sys_defs.h
diff -Nru postfix-3.10.3/src/smtp/smtp.h postfix-3.10.4/src/smtp/smtp.h
--- postfix-3.10.3/src/smtp/smtp.h	2025-07-10 00:22:54.000000000 +0300
+++ postfix-3.10.4/src/smtp/smtp.h	2025-08-19 01:01:52.000000000 +0300
@@ -32,6 +32,7 @@
 #include <tok822.h>
 #include <dsn_buf.h>
 #include <header_body_checks.h>
+#include <sendopts.h>
 
  /*
   * Postfix TLS library.
@@ -59,9 +60,6 @@
     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. */
@@ -69,18 +67,11 @@
     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); \
@@ -248,6 +239,12 @@
     unsigned logged_line_length_limit:1;
 } SMTP_STATE;
 
+#ifdef USE_TLS
+#define STATE_TLS_NOT_REQUIRED(state) \
+	(var_tls_required_enable && \
+	    ((state)->request->sendopts & SOPT_REQUIRETLS_HEADER))
+#endif
+
  /*
   * Primitives to enable/disable/test connection caching and reuse based on
   * the delivery request next-hop destination (i.e. not smtp_fallback_relay).
diff -Nru postfix-3.10.3/src/smtp/smtp_connect.c postfix-3.10.4/src/smtp/smtp_connect.c
--- postfix-3.10.3/src/smtp/smtp_connect.c	2025-07-10 00:22:54.000000000 +0300
+++ postfix-3.10.4/src/smtp/smtp_connect.c	2025-08-19 01:01:52.000000000 +0300
@@ -508,24 +508,6 @@
     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)) {
@@ -976,9 +958,15 @@
 	 * level of "may" to "encrypt"? This would disable falling back to
 	 * plaintext, and could break interoperability with receivers that
 	 * crank up security up to 11.
+	 * 
+	 * With "TLS-Required: no" in effect, the SMTP client ignores the
+	 * recipient-side policy mechanism TLSRPT, in addition to the already
+	 * ignored DANE and MTA-STS mechanisms. This prevents TLSRPT
+	 * notifications for all SMTP deliveries that do not require TLS.
 	 */
 #ifdef USE_TLSRPT
 	if (smtp_mode && var_smtp_tlsrpt_enable
+	    && STATE_TLS_NOT_REQUIRED(state) == 0
 	    && tls_level_lookup(var_smtp_tls_level) > TLS_LEV_NONE
 	    && !valid_hostaddr(domain, DONT_GRIPE))
 	    smtp_tlsrpt_create_wrapper(state, domain);
diff -Nru postfix-3.10.3/src/smtp/smtp_tls_policy.c postfix-3.10.4/src/smtp/smtp_tls_policy.c
--- postfix-3.10.3/src/smtp/smtp_tls_policy.c	2025-07-10 00:22:54.000000000 +0300
+++ postfix-3.10.4/src/smtp/smtp_tls_policy.c	2025-08-19 01:01:52.000000000 +0300
@@ -647,11 +647,18 @@
      * Compute the per-site TLS enforcement level. For compatibility with the
      * original TLS patch, this algorithm is gives equal precedence to host
      * and next-hop policies.
+     * 
+     * When "TLS-Required: no" is in effect, skip TLS policy lookup and limit
+     * the security level to "may". Do not reset the security level after
+     * policy lookup, as that would result in errors. For example, when TLSA
+     * records are looked up for security level "dane", and then the security
+     * level is reset to "may", the activation of those TLSA records will
+     * fail.
      */
     tls->level = global_tls_level();
     site_level = TLS_LEV_NOTFOUND;
 
-    if (iter->tlsreqno) {
+    if (STATE_TLS_NOT_REQUIRED(iter->parent)) {
 	if (msg_verbose)
 	    msg_info("%s: no tls policy lookup", __func__);
 	if (tls->level > TLS_LEV_MAY)
diff -Nru postfix-3.10.3/src/tls/tls_client.c postfix-3.10.4/src/tls/tls_client.c
--- postfix-3.10.3/src/tls/tls_client.c	2024-10-11 01:15:24.000000000 +0300
+++ postfix-3.10.4/src/tls/tls_client.c	2025-08-19 00:59:45.000000000 +0300
@@ -313,6 +313,7 @@
 static void verify_x509(TLS_SESS_STATE *TLScontext, X509 *peercert,
 			        const TLS_CLIENT_START_PROPS *props)
 {
+    int     x509_err = SSL_get_verify_result(TLScontext->con);
 
     /*
      * On exit both peer_CN and issuer_CN should be set.
@@ -324,7 +325,7 @@
      * Is the certificate trust chain trusted and matched?  Any required name
      * checks are now performed internally in OpenSSL.
      */
-    if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) {
+    if (x509_err == X509_V_OK) {
 	TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
 	if (TLScontext->must_fail) {
 	    msg_panic("%s: cert valid despite trust init failure",
@@ -356,6 +357,13 @@
 		tls_dane_log(TLScontext);
 	    }
 	}
+    } else if (TLS_MUST_MATCH(TLScontext->level) &&
+	       x509_err == X509_V_ERR_HOSTNAME_MISMATCH) {
+	/*
+	 * If the only error is a hostname mismatch, the certificate must have
+	 * been trusted.
+	 */
+	TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;
     }
 
     /*
diff -Nru postfix-3.10.3/src/tls/tls_verify.c postfix-3.10.4/src/tls/tls_verify.c
--- postfix-3.10.3/src/tls/tls_verify.c	2025-02-17 19:53:16.000000000 +0300
+++ postfix-3.10.4/src/tls/tls_verify.c	2025-08-19 00:59:45.000000000 +0300
@@ -120,12 +120,26 @@
 
 /* update_error_state - safely stash away error state */
 
-static void update_error_state(TLS_SESS_STATE *TLScontext, int depth,
-			               X509 *errorcert, int errorcode)
+static void update_error_state(X509_STORE_CTX *ctx, TLS_SESS_STATE *TLScontext,
+			          int depth, X509 *errorcert, int errorcode)
 {
-    /* No news is good news */
-    if (TLScontext->errordepth >= 0 && TLScontext->errordepth <= depth)
-	return;
+
+    /*
+     * Report the error that is closest to the leaf certificate, any errors
+     * higher up the chain are immaterial until the "inner" errors are fixed.
+     * 
+     * We special-case "X509_V_ERR_HOSTNAME_MISMATCH" (at depth 0) in order to
+     * distinguish between untrusted certificates and trusted certificates
+     * with a hostname mismatch.  Any other error has a higher priority.
+     */
+    if (TLScontext->errordepth >= 0) {
+	if ((TLScontext->errordepth <= depth &&
+	     TLScontext->errorcode != X509_V_ERR_HOSTNAME_MISMATCH) ||
+	    errorcode == X509_V_ERR_HOSTNAME_MISMATCH) {
+	    X509_STORE_CTX_set_error(ctx, TLScontext->errorcode);
+	    return;
+	}
+    }
 
     /*
      * The certificate pointer is stable during the verification callback,
@@ -179,12 +193,12 @@
     if (TLScontext->must_fail) {
 	if (depth == 0) {
 	    X509_STORE_CTX_set_error(ctx, err = X509_V_ERR_UNSPECIFIED);
-	    update_error_state(TLScontext, depth, cert, err);
+	    update_error_state(ctx, TLScontext, depth, cert, err);
 	}
 	return (1);
     }
     if (ok == 0)
-	update_error_state(TLScontext, depth, cert, err);
+	update_error_state(ctx, TLScontext, depth, cert, err);
 
     if (TLScontext->log_mask & TLS_LOG_VERBOSE) {
 	if (cert) {
diff -Nru postfix-3.10.3/src/tlsproxy/tlsproxy.c postfix-3.10.4/src/tlsproxy/tlsproxy.c
--- postfix-3.10.3/src/tlsproxy/tlsproxy.c	2024-11-13 19:22:42.000000000 +0300
+++ postfix-3.10.4/src/tlsproxy/tlsproxy.c	2025-08-18 23:06:41.000000000 +0300
@@ -1267,6 +1267,12 @@
     init_buf = vstring_alloc(100);
     init_key = tls_proxy_client_init_serialize(attr_print_plain, init_buf,
 					       init_props);
+#define TLSP_CLIENT_INIT_RETURN(retval) do { \
+	vstring_free(init_buf); \
+	vstring_free(param_buf); \
+	return (retval); \
+    } while (0)
+
     if (tlsp_pre_jail_done == 0) {
 	if (tlsp_pre_jail_client_param_key == 0
 	    || tlsp_pre_jail_client_init_key == 0) {
@@ -1284,9 +1290,12 @@
      * TLS_APPL_STATE instance; this makes a mismatch of TLS_CLIENT_PARAMS
      * settings problematic.
      */
-    if (tlsp_pre_jail_done
-	&& !been_here_fixed(tlsp_params_mismatch_filter, param_key)
-	&& strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) {
+    else if (tlsp_pre_jail_client_param_key == 0
+	     || tlsp_pre_jail_client_init_key == 0) {
+	msg_warn("TLS client role is disabled by configuration");
+	TLSP_CLIENT_INIT_RETURN(0);
+    } else if (!been_here_fixed(tlsp_params_mismatch_filter, param_key)
+	       && strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) {
 	msg_warn("request from tlsproxy client with unexpected settings");
 	tlsp_log_config_diff(tlsp_pre_jail_client_param_key, param_key);
 	log_hints = 1;
@@ -1361,9 +1370,7 @@
 			 SSL_MODE_ENABLE_PARTIAL_WRITE
 			 | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
     }
-    vstring_free(init_buf);
-    vstring_free(param_buf);
-    return (appl_state);
+    TLSP_CLIENT_INIT_RETURN(appl_state);
 }
 
 /* tlsp_close_event - pre-handshake plaintext-client close event */
@@ -1497,6 +1504,7 @@
 				TLSP_INIT_TIMEOUT, (void *) state);
 	return;
     } else {
+	state->flags |= TLSP_FLAG_DO_HANDSHAKE;
 	tlsp_request_read_event(plaintext_fd, tlsp_get_fd_event,
 				TLSP_INIT_TIMEOUT, (void *) state);
 	return;
diff -Nru postfix-3.10.3/src/tlsproxy/tlsproxy_state.c postfix-3.10.4/src/tlsproxy/tlsproxy_state.c
--- postfix-3.10.3/src/tlsproxy/tlsproxy_state.c	2019-02-09 01:12:13.000000000 +0300
+++ postfix-3.10.4/src/tlsproxy/tlsproxy_state.c	2025-08-18 23:06:41.000000000 +0300
@@ -105,7 +105,7 @@
 {
     TLSP_STATE *state = (TLSP_STATE *) mymalloc(sizeof(*state));
 
-    state->flags = TLSP_FLAG_DO_HANDSHAKE;
+    state->flags = 0;
     state->service = mystrdup(service);
     state->plaintext_stream = plaintext_stream;
     state->plaintext_buf = 0;
diff -Nru postfix-3.10.3/src/util/dict.c postfix-3.10.4/src/util/dict.c
--- postfix-3.10.3/src/util/dict.c	2022-05-01 20:01:35.000000000 +0300
+++ postfix-3.10.4/src/util/dict.c	2025-08-18 23:02:53.000000000 +0300
@@ -147,8 +147,8 @@
 /* .IP "char *context"
 /*	Application context from the caller.
 /* .PP
-/*	dict_changed_name() returns non-zero when any dictionary needs to
-/*	be re-opened because it has changed or because it was unlinked.
+/*	dict_changed_name() returns non-zero when any dictionary is
+/*	opened read-only and has changed, or because it was unlinked.
 /*	A non-zero result is the name of a changed dictionary.
 /*
 /*	dict_load_file_xt() reads name-value entries from the named file.
@@ -601,11 +601,12 @@
 	dict = ((DICT_NODE *) h->value)->dict;
 	if (dict->stat_fd < 0)			/* not file-based */
 	    continue;
-	if (dict->mtime == 0)			/* not bloody likely */
-	    msg_warn("%s: table %s: null time stamp", myname, h->key);
+	if (dict->mtime < 0)			/* not bloody likely */
+	    msg_warn("%s: table %s: negative time stamp", myname, h->key);
 	if (fstat(dict->stat_fd, &st) < 0)
 	    msg_fatal("%s: fstat: %m", myname);
 	if (((dict->flags & DICT_FLAG_MULTI_WRITER) == 0
+	     && dict->mtime > 0
 	     && st.st_mtime != dict->mtime)
 	    || st.st_nlink == 0)
 	    status = h->key;
diff -Nru postfix-3.10.3/src/util/dict_db.c postfix-3.10.4/src/util/dict_db.c
--- postfix-3.10.3/src/util/dict_db.c	2022-02-02 20:43:27.000000000 +0300
+++ postfix-3.10.4/src/util/dict_db.c	2025-08-18 23:02:53.000000000 +0300
@@ -789,7 +789,8 @@
     dict_db->dict.stat_fd = dbfd;
     if (fstat(dict_db->dict.stat_fd, &st) < 0)
 	msg_fatal("dict_db_open: fstat: %m");
-    dict_db->dict.mtime = st.st_mtime;
+    if (open_flags == O_RDONLY)
+	dict_db->dict.mtime = st.st_mtime;
     dict_db->dict.owner.uid = st.st_uid;
     dict_db->dict.owner.status = (st.st_uid != 0);
 
diff -Nru postfix-3.10.3/src/util/dict_dbm.c postfix-3.10.4/src/util/dict_dbm.c
--- postfix-3.10.3/src/util/dict_dbm.c	2014-11-17 21:28:01.000000000 +0300
+++ postfix-3.10.4/src/util/dict_dbm.c	2025-08-18 23:02:53.000000000 +0300
@@ -472,7 +472,8 @@
 	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");
-    dict_dbm->dict.mtime = st.st_mtime;
+    if (open_mode == 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);
 
diff -Nru postfix-3.10.3/src/util/dict_lmdb.c postfix-3.10.4/src/util/dict_lmdb.c
--- postfix-3.10.3/src/util/dict_lmdb.c	2021-06-04 23:42:49.000000000 +0300
+++ postfix-3.10.4/src/util/dict_lmdb.c	2025-08-18 23:02:53.000000000 +0300
@@ -653,7 +653,8 @@
 	msg_fatal("dict_lmdb_open: fstat: %m");
     dict_lmdb->dict.lock_fd = dict_lmdb->dict.stat_fd = db_fd;
     dict_lmdb->dict.lock_type = MYFLOCK_STYLE_FCNTL;
-    dict_lmdb->dict.mtime = st.st_mtime;
+    if (open_flags == O_RDONLY)
+	dict_lmdb->dict.mtime = st.st_mtime;
     dict_lmdb->dict.owner.uid = st.st_uid;
     dict_lmdb->dict.owner.status = (st.st_uid != 0);
 
diff -Nru postfix-3.10.3/src/util/dict_sdbm.c postfix-3.10.4/src/util/dict_sdbm.c
--- postfix-3.10.3/src/util/dict_sdbm.c	2012-01-25 04:41:08.000000000 +0400
+++ postfix-3.10.4/src/util/dict_sdbm.c	2025-08-18 23:02:53.000000000 +0300
@@ -449,7 +449,8 @@
     dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm);
     if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
 	msg_fatal("dict_sdbm_open: fstat: %m");
-    dict_sdbm->dict.mtime = st.st_mtime;
+    if (open_flags == O_RDONLY)
+	dict_sdbm->dict.mtime = st.st_mtime;
     dict_sdbm->dict.owner.uid = st.st_uid;
     dict_sdbm->dict.owner.status = (st.st_uid != 0);
 


Reply to: