Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package postfix for openSUSE:Factory checked 
in at 2026-09-10 11:46:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/postfix (Old)
 and      /work/SRC/openSUSE:Factory/.postfix.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "postfix"

Thu Sep 10 11:46:58 2026 rev:278 rq:1376583 version:3.11.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/postfix/postfix-bdb.changes      2026-08-19 
17:55:16.101420459 +0200
+++ /work/SRC/openSUSE:Factory/.postfix.new.1265/postfix-bdb.changes    
2026-09-10 11:47:29.657766908 +0200
@@ -1,0 +2,74 @@
+Tue Sep  8 17:47:46 UTC 2026 - Arjen de Korte <[email protected]>
+
+- update to 3.11.7
+  * Bug (introduced: Postfix 3.9, date: 20240106) SMTP smuggling
+    was still possible with smtpd_proxy_filter (disabled by default)
+    when the after-filter SMTP server used the default policy
+    settings "smtpd_forbid_bare_newline_exclusions = $mynetworks"
+    and "smtpd_forbid_bare_newline = normalize".
+  * Bug (introduced: Postfix 3.11, date: 20250917): SMTP smuggling
+    was possible with smtpd_proxy_filter (disabled by default) when
+    the before-filter SMTP server added a "Require-TLS-ESMTP: yes"
+    message header, due to implementation edge cases. Adding this
+    header is enabled with the "requiretls_esmtp_header = yes"
+    default setting.
+  * Bug (defect introduced: Postfix 3.0, date: 20140707): null
+    pointer read error after receiving MAIL FROM, RCPT TO, and VRFY
+    with an UTF8 address but no SMTPUTF8 parameter. This requires
+    "smtputf8_enable = yes" (the default) and "strict_smtputf8 =
+    yes" (not default). With this, the SMTP server did an unnecessary
+    MAIL FROM reset without RCPT TO reset. A crafted remote SMTP
+    client could then send a DATA command and crash a Postfix SMTP
+    server process with a null pointer read error.
+  * Bug (defect introduced: Postfix 3.4, date: 20180303): the MySQL
+    client setting "tls_verify_cert = yes" had no effect with Oracle
+    MySQL 8 and later.
+  * Bug (defect introduced: Postfix-beta, date: 19990119): the
+    pipe(8) delivery agent deleted a command-line argument if the
+    argument contained $user AND $user expanded to an empty string,
+    breaking the positional order of arguments. This was a workaround
+    for a problem that hopefully no longer exists.
+  * Bug (defect introduced: Postfix 2.3, date: 20050323): the SMTP
+    client enhanced status code parser could process stale data
+    when a remote SMTP server sent a three-digit reply without other
+    text.
+  * Isolation: stamp Postfix SMTP server TLS session tickets with
+    their master.cf service name. With this, an SMTP server defined
+    in master.cf will no longer accept tickets issued by a different
+    SMTP server defined in the same master.cf file.
+  * The postmap and postalias commands now log a warning when
+    creating a root-owned database file in a directory that is not
+    owned by root. They log that the database source file, indexed
+    file(s), and parent directory should have the same owner, to
+    prevent a privilege-escalation attack.
+  * Bug (introduced: Postfix 2.3, date: 20060629): a malicious
+    Milter or attacker-in-the-middle could trigger a null-terminated
+    heap memory overread in the SMTP daemon while formatting a
+    malformed multiline response.
+  * Bug (defect introduced: Postfix 3.0, date: 20141117): in the
+    postqueue command don't free() text before logging a fatal error
+    message.
+  * Code hygiene: in the SMTP client protocol engine, evaluate a
+    RETURN() macro argument before freeing resources.
+  * (Postfix 3.11) Hardening: in the non-BerkeleyDB migration
+    service, delay the decision between running postmap or postalias
+    until after the database file/directory owner/permission checks.
+    The benefit from making the decision early (better error messages)
+    was not worth the risk.
+  * (Postfix-3.11) Hardened the database parent directory permission
+    checks for automatic re-indexing with the non-Berkeley-DB
+    migration service.
+  * Hardening command-line email submission: the postdrop command
+    now disallows null and line-break characters in queue file
+    envelope records (line-break characters in non-envelope queue
+    file records are already neutralized by default with
+    "cleanup_replace_stray_cr_lf = yes").
+  * Shut up nagging from multiple AIs and harden the virtual delivery
+    agent against an evil (LDAP or SQL) database.
+  * Code hygiene: myrealloc(ptr, 0) still resulted in a panic.
+    Also adopt a mystrndup() fix from Postfix 3.12.
+  * Portability: OpenBSD does not define NS_INT16SZ.
+- refreshed patch:
+  % postfix-vda-v14-3.0.3.patch
+
+-------------------------------------------------------------------
postfix.changes: same change

Old:
----
  postfix-3.11.6.tar.gz
  postfix-3.11.6.tar.gz.asc

New:
----
  postfix-3.11.7.tar.gz
  postfix-3.11.7.tar.gz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ postfix-bdb.spec ++++++
--- /var/tmp/diff_new_pack.RVJ8R7/_old  2026-09-10 11:47:31.946862871 +0200
+++ /var/tmp/diff_new_pack.RVJ8R7/_new  2026-09-10 11:47:31.953863165 +0200
@@ -61,7 +61,7 @@
 %endif
 %bcond_without ldap
 Name:           postfix-bdb
-Version:        3.11.6
+Version:        3.11.7
 Release:        0
 Summary:        A fast, secure, and flexible mailer
 License:        EPL-2.0 OR IPL-1.0

++++++ postfix.spec ++++++
--- /var/tmp/diff_new_pack.RVJ8R7/_old  2026-09-10 11:47:32.040866812 +0200
+++ /var/tmp/diff_new_pack.RVJ8R7/_new  2026-09-10 11:47:32.042866896 +0200
@@ -63,7 +63,7 @@
 #
 %bcond_without ldap
 Name:           postfix
-Version:        3.11.6
+Version:        3.11.7
 Release:        0
 Summary:        A fast, secure, and flexible mailer
 License:        EPL-2.0 OR IPL-1.0

++++++ postfix-3.11.6.tar.gz -> postfix-3.11.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/HISTORY new/postfix-3.11.7/HISTORY
--- old/postfix-3.11.6/HISTORY  2026-08-09 22:34:23.000000000 +0200
+++ new/postfix-3.11.7/HISTORY  2026-09-06 16:10:56.000000000 +0200
@@ -31118,3 +31118,123 @@
        Queue hygiene: the postdrop command accepted the null record
        type which the rest of Postfix ingnores. Reported by OpenAI
        Security. File: postdrop.c.
+
+20260903
+
+       Bug (defect introduced: Postfix 2.3, date: 20050323): the
+       SMTP client enhanced status code parser could process stale
+       data. Reported by Qualys, assisted by Claude Mythos Preview.
+       File: smtp_chat.c.
+
+       Bug (defect introduced: Postfix 3.0, date: 20141117): don't
+       free() text before logging a fatal error message. Reported
+       by Qualys, assisted by Claude Mythos Preview. File:
+       postqueue.c.
+
+       Hardening: in the non-BerkeleyDB migration service, delay
+       the decision between running postmap or postalias until
+       after the database file/directory owner/permission checks.
+       The benefit from making the decision early (better error
+       messages) was not worth the risk. Qualys, assisted by Claude
+       Mythos Preview. Files: nbdb_process.c, nbdb_process_test.c.
+
+       Hardened the database parent directory permission checks
+       for automatic re-indexing with the non-Berkeley-DB migration
+       service. Files: nbdb_safe.c, nbdb_safe.h, nbdb_safe_test.c,
+       nbdb_process_test.c.
+
+       Bug (defect introduced: Postfix-beta, date: 19990119): the
+       pipe(8) delivery agent deleted a command-line argument if
+       the argument contained $user AND $user expanded to an empty
+       string, breaking the positional order of arguments. This
+       was a workaround for a problem that hopefully no longer
+       exists. Reported by Qualys, assisted by Claude Mythos
+       Preview. File: pipe/pipe.c.
+
+       Code hygiene: in the SMTP client protocol engine, evaluate
+       the RETURN() macro argument before freeing resources.
+       Reported by Qualys, assisted by Claude Mythos Preview. File:
+       smtp_proto.c.
+
+       Code hygiene: myrealloc(ptr, 0) still resulted in a panic.
+       Reported by Qualys, assisted by Claude Mythos Preview. Also
+       adopt a mystrndup() fix from Postfix 3.12. File: mymalloc.c.
+
+       The postmap and postalias commands now log a warning when
+       creating a root-owned database file in a directory that is
+       not owned by root. They log that the database source file,
+       indexed file(s), and parent directory should have the same
+       owner, to prevent a privilege-escalation attack. Problem
+       reported by OpenAI Security, remediation strategy (don't
+       break PROD) by Wietse. Files: postalias.c, postmap.c.
+
+       Hardening command-line email submission: the postdrop
+       command now disallows null and line-break characters in
+       queue file envelope records (line-break characters in other
+       queue file records are already neutralized by default with
+       "cleanup_replace_stray_cr_lf = yes").
+
+       The new constraint not only eliminates line-break injection
+       into local mailbox files as reported by OpenAI Security,
+       but also prevents other forms of misuse. Later, this
+       constraint may be moved into the Postfix core. Fix by Wietse.
+       File: postdrop.c.
+
+       Shut up nagging from multiple AIs and harden the virtual
+       delivery agent against an evil SQL database. File:
+       virtual/mailbox.c.
+
+       Bug (defect introduced: Postfix 3.4, date: 20180303): the
+       MySQL client setting "tls_verify_cert = yes" had no effect
+       with Oracle MySQL 8 and later. Report and fix by OpenAI
+       Security. File: dict_mysql.c.
+
+       Isolation: stamp Postfix SMTP server TLS session tickets
+       with their master.cf service name. With this, an SMTP server
+       defined in master.cf will no longer accept tickets issued
+       by a different SMTP server defined in the same master.cf
+       file. Fix by OpenAI security. File: tls_server.c.
+
+       Bug (introduced: Postfix 2.3, date: 20060629): a malicious
+       Milter or attacker-in-the-middle could trigger a null-terminated
+       heap memory overread in the SMTP daemon while formatting a
+       malformed multiline response. Fix from OpenAI Security
+       adopted with minor changes. Files: milter8.c, smtpd_chat.c.
+
+       Bug (introduced: Postfix 3.9, date: 20240106) SMTP smuggling
+       was still possible with smtpd_proxy_filter (disabled by
+       default) when the after-filter SMTP server used the default
+       policy settings "smtpd_forbid_bare_newline_exclusions =
+       $mynetworks" and "smtpd_forbid_bare_newline = normalize".
+       Reported by OpenAI Security. Fix by Wietse. Files:
+       smtpd_proxy.[hc].
+
+       Bug (introduced: Postfix 3.11, date: 20250917): SMTP smuggling
+       was possible with smtpd_proxy_filter (disabled by default)
+       when the before-filter SMTP server added a "Require-TLS-ESMTP:
+       yes" message header, due to implementation edge cases.
+       Adding this header is enabled with the "requiretls_esmtp_header
+       = yes" default setting. Reported by OpenAI Security. Fix
+       by Wietse. Files: smtpd_proxy.[hc].
+ 
+       Cleanup: as suggested by OpenAI Security, eliminate stray
+       CR characters from the smtpd_proxy_filter input stream.
+       The before-proxy-filter SMTP server already eliminated stray
+       LF. File: smtpd/smtpd_proxy.[hc].
+
+       Portability: OpenBSD does not define NS_INT16SZ. Brad Smith.
+       File: dns.h.
+
+       Shut up nagging about allowing gid==0 in database responses.
+       File: virtual/mailbox.c.
+
+       Bug (defect introduced: Postfix 3.0, date: 20140707): null
+       pointer read error after receiving MAIL FROM, RCPT TO, and
+       VRFY with an UTF8 address but no SMTPUTF8 parameter. This
+       requires "smtputf8_enable = yes" (the default) and
+       "strict_smtputf8 = yes" (not default). With this, the SMTP
+       server did an unnecessary MAIL FROM reset without RCPT TO
+       reset. A crafted remote SMTP client could then send a DATA
+       command and crash the Postfix SMTP server with a null pointer
+       read error. Reported by Wonyoung Jung (정원영). File:
+       smtpd/smtpd.c.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/dns/dns.h 
new/postfix-3.11.7/src/dns/dns.h
--- old/postfix-3.11.6/src/dns/dns.h    2026-01-27 23:01:58.000000000 +0100
+++ new/postfix-3.11.7/src/dns/dns.h    2026-09-03 21:31:19.000000000 +0200
@@ -54,6 +54,10 @@
 
 #endif
 
+#ifndef NS_INT16SZ
+#define NS_INT16SZ     2
+#endif
+
  /*
   * Provide API compatibility for systems without res_nxxx() API. Also
   * require calling dns_get_h_errno() instead of directly accessing the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/global/dict_mysql.c 
new/postfix-3.11.7/src/global/dict_mysql.c
--- old/postfix-3.11.6/src/global/dict_mysql.c  2025-06-24 23:24:27.000000000 
+0200
+++ new/postfix-3.11.7/src/global/dict_mysql.c  2026-09-03 21:09:22.000000000 
+0200
@@ -118,7 +118,8 @@
 #if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50023
 #define DICT_MYSQL_SSL_VERIFY_SERVER_CERT MYSQL_OPT_SSL_VERIFY_SERVER_CERT
 #elif MYSQL_VERSION_ID >= 80000
-#define DICT_MYSQL_SSL_VERIFY_SERVER_CERT MYSQL_OPT_SSL_MODE
+/* 202607 OpenAI: MYSQL_OPT_SSL_MODE is an enum, not a bool. */
+#define DICT_MYSQL_SSL_MODE MYSQL_OPT_SSL_MODE
 #endif
 
  /*
@@ -171,7 +172,7 @@
     char   *tls_CAfile;
     char   *tls_CApath;
     char   *tls_ciphers;
-#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
+#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT) || defined(DICT_MYSQL_SSL_MODE)
     int     tls_verify_cert;
 #endif
     int     require_result_set;
@@ -601,6 +602,11 @@
  */
 static void plmysql_connect_single(DICT_MYSQL *dict_mysql, HOST *host)
 {
+#if defined(DICT_MYSQL_SSL_MODE)
+    enum mysql_ssl_mode ssl_mode;
+
+#endif
+
     if ((host->db = mysql_init(NULL)) == NULL)
        msg_fatal("dict_mysql: insufficient memory");
     if (dict_mysql->option_file)
@@ -628,7 +634,19 @@
                      dict_mysql->tls_CAfile, dict_mysql->tls_CApath,
                      dict_mysql->tls_ciphers);
 #endif
-#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
+#if defined(DICT_MYSQL_SSL_MODE)
+    if (dict_mysql->tls_verify_cert != -1) {
+       /* MYSQL_OPT_SSL_MODE enum values are not Boolean values. */
+       ssl_mode = dict_mysql->tls_verify_cert ? SSL_MODE_VERIFY_IDENTITY :
+           SSL_MODE_PREFERRED;
+       if (mysql_options(host->db, DICT_MYSQL_SSL_MODE, &ssl_mode) != 0) {
+           msg_warn("dict_mysql: could not set TLS mode for host %s: %s",
+                    host->hostname, mysql_error(host->db));
+           plmysql_down_host(host, dict_mysql->retry_interval);
+           return;
+       }
+    }
+#elif defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
     if (dict_mysql->tls_verify_cert != -1)
        mysql_options(host->db, DICT_MYSQL_SSL_VERIFY_SERVER_CERT,
                      &dict_mysql->tls_verify_cert);
@@ -704,7 +722,7 @@
     dict_mysql->tls_CAfile = cfg_get_str(p, "tls_CAfile", NULL, 0, 0);
     dict_mysql->tls_CApath = cfg_get_str(p, "tls_CApath", NULL, 0, 0);
     dict_mysql->tls_ciphers = cfg_get_str(p, "tls_ciphers", NULL, 0, 0);
-#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT)
+#if defined(DICT_MYSQL_SSL_VERIFY_SERVER_CERT) || defined(DICT_MYSQL_SSL_MODE)
     dict_mysql->tls_verify_cert = cfg_get_bool(p, "tls_verify_cert", -1);
 #endif
     dict_mysql->require_result_set = cfg_get_bool(p, "require_result_set", 1);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/global/mail_version.h 
new/postfix-3.11.7/src/global/mail_version.h
--- old/postfix-3.11.6/src/global/mail_version.h        2026-08-09 
22:36:19.000000000 +0200
+++ new/postfix-3.11.7/src/global/mail_version.h        2026-09-07 
18:24:23.000000000 +0200
@@ -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      "20260809"
-#define MAIL_VERSION_NUMBER    "3.11.6"
+#define MAIL_RELEASE_DATE      "20260907"
+#define MAIL_VERSION_NUMBER    "3.11.7"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/milter/milter8.c 
new/postfix-3.11.7/src/milter/milter8.c
--- old/postfix-3.11.6/src/milter/milter8.c     2026-07-06 16:33:50.000000000 
+0200
+++ new/postfix-3.11.7/src/milter/milter8.c     2026-09-03 21:12:17.000000000 
+0200
@@ -955,6 +955,64 @@
     return (err);
 }
 
+/* milter8_valid_smtp_reply - validate SMTP reply (after 202607 OpenAI) */
+
+static int milter8_valid_smtp_reply(VSTRING *buf)
+{
+    const unsigned char *cp = (unsigned char *) STR(buf);
+    const unsigned char *buf_end = cp + LEN(buf);
+
+    /* Exclude one terminal Milter protocol null from the reply text. */
+    if (buf_end > cp && buf_end[-1] == 0)
+       buf_end -= 1;
+
+    if (memchr(cp, 0, buf_end - cp) != 0)
+       return (0);
+
+    while (cp < buf_end) {
+       const unsigned char *line_end;
+       ssize_t line_len;
+       int     final;
+
+       /* Part of a breaking change, not allowed in stable release. */
+#ifdef SNAPSHOT
+       unsigned char code[3];
+       int     first = 1;
+
+#endif
+
+       /* A final line must not end in a line terminator. */
+       line_len = strcspn((const char *) cp, "\r\n");
+       line_end = cp + line_len;
+       final = (line_end == buf_end);
+
+       /* Each line must match /^ddd(-| )d?/. */
+       if (line_len < 4 || (cp[0] != '4' && cp[0] != '5') || !ISDIGIT(cp[1])
+           || !ISDIGIT(cp[2]) || (cp[3] != (final ? ' ' : '-'))
+           || (line_len > 4 && ISDIGIT(cp[4]) && cp[4] != cp[0]))
+           return (0);
+
+       /* Part of a breaking change, not allowed in stable release. */
+#ifdef SNAPSHOT
+       if (first) {
+           code[0] = cp[0];
+           code[1] = cp[1];
+           code[2] = cp[2];
+           first = 0;
+       } else if (cp[0] != code[0] || cp[1] != code[1] || cp[2] != code[2]) {
+           return (0);
+       }
+#endif
+       /* A non-final line must end in a line terminator. */
+       if (final)
+           return (1);
+       if (line_end > buf_end - 2 || *line_end != '\r' || line_end[1] != '\n')
+           return (0);
+       cp = line_end + 2;
+    }
+    return (0);
+}
+
 /* milter8_event - report event and receive reply */
 
 static const char *milter8_event(MILTER8 *milter, int event,
@@ -1290,13 +1348,7 @@
                                  MILTER8_DATA_BUFFER, milter->buf,
                                  MILTER8_DATA_END) != 0)
                MILTER8_EVENT_BREAK(milter->def_reply);
-           /* XXX Enforce this for each line of a multi-line reply. */
-           if ((STR(milter->buf)[0] != '4' && STR(milter->buf)[0] != '5')
-               || !ISDIGIT(STR(milter->buf)[1])
-               || !ISDIGIT(STR(milter->buf)[2])
-               || (STR(milter->buf)[3] != ' ' && STR(milter->buf)[3] != '-')
-               || (ISDIGIT(STR(milter->buf)[4])
-                   && (STR(milter->buf)[4] != STR(milter->buf)[0]))) {
+           if (!milter8_valid_smtp_reply(milter->buf)) {
                msg_warn("milter %s: malformed reply: %s",
                         milter->m.name, STR(milter->buf));
                milter8_conf_error(milter);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/nbdb_reindexd/Makefile.in 
new/postfix-3.11.7/src/nbdb_reindexd/Makefile.in
--- old/postfix-3.11.6/src/nbdb_reindexd/Makefile.in    2026-02-26 
01:25:02.000000000 +0100
+++ new/postfix-3.11.7/src/nbdb_reindexd/Makefile.in    2026-09-03 
20:36:33.000000000 +0200
@@ -50,7 +50,7 @@
 test_nbdb_sniffer: nbdb_sniffer_test
        $(SHLIB_ENV) $(VALGRIND) ./nbdb_sniffer_test
 
-SAFE_TEST_OBJ = nbdb_safe.o
+SAFE_TEST_OBJ = nbdb_safe.o $(TESTLIB)
 
 nbdb_safe_test: nbdb_safe_test.o $(SAFE_TEST_OBJ) $(LIBS)
        $(CC) $(CFLAGS) -o $@ [email protected] $(SAFE_TEST_OBJ) $(LIBS) $(SYSLIBS)
@@ -206,6 +206,8 @@
 nbdb_safe.o: ../../include/check_arg.h
 nbdb_safe.o: ../../include/mail_conf.h
 nbdb_safe.o: ../../include/mail_params.h
+nbdb_safe.o: ../../include/msg.h
+nbdb_safe.o: ../../include/mymalloc.h
 nbdb_safe.o: ../../include/sys_defs.h
 nbdb_safe.o: ../../include/vbuf.h
 nbdb_safe.o: ../../include/vstring.h
@@ -218,6 +220,7 @@
 nbdb_safe_test.o: ../../include/check_arg.h
 nbdb_safe_test.o: ../../include/mail_conf.h
 nbdb_safe_test.o: ../../include/mail_params.h
+nbdb_safe_test.o: ../../include/mock_stat.h
 nbdb_safe_test.o: ../../include/msg.h
 nbdb_safe_test.o: ../../include/msg_vstream.h
 nbdb_safe_test.o: ../../include/stringops.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/nbdb_reindexd/nbdb_process.c 
new/postfix-3.11.7/src/nbdb_reindexd/nbdb_process.c
--- old/postfix-3.11.6/src/nbdb_reindexd/nbdb_process.c 2026-02-19 
22:58:18.000000000 +0100
+++ new/postfix-3.11.7/src/nbdb_reindexd/nbdb_process.c 2026-09-03 
20:35:08.000000000 +0200
@@ -171,26 +171,24 @@
     }
 
     /*
-     * Should we run postmap or postalias? Open the source file with the same
-     * (uid, gid) as the postmap or postalias commands would, so that we can
-     * detect permission errors quickly.
-     * 
-     * Note: we do this before the file allowlist/owner/permission safety
-     * checks, so that we can log the concrete postmap or postalias command
-     * if a safety check fails.
-     */
-    if ((index_cmd = nbdb_get_index_cmd_as(source_path, leg_idx_st.st_uid,
-                                          leg_idx_st.st_gid, why)) == 0)
-       return (NBDB_STAT_ERROR);
-
-    /*
      * Allow indexing as the legacy indexed file owner if it is considered
      * "safe".
      */
     if (!nbdb_safe_to_index_as_legacy_index_owner(source_path, &source_st,
                                             STR(leg_idx_path), &leg_idx_st,
                                         parent_dir, &parent_dir_st, why)) {
-       status = NBDB_STAT_ERROR;
+       return (NBDB_STAT_ERROR);
+    }
+
+    /*
+     * Should we run postmap or postalias? Open the source file with the same
+     * (uid, gid) as the postmap or postalias commands would, so that we can
+     * detect permission errors quickly. 202607 Qualys+Mythos: move this
+     * after the safety checks.
+     */
+    if ((index_cmd = nbdb_get_index_cmd_as(source_path, leg_idx_st.st_uid,
+                                          leg_idx_st.st_gid, why)) == 0) {
+       return (NBDB_STAT_ERROR);
     }
 
     /*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/nbdb_reindexd/nbdb_process_test.c 
new/postfix-3.11.7/src/nbdb_reindexd/nbdb_process_test.c
--- old/postfix-3.11.6/src/nbdb_reindexd/nbdb_process_test.c    2026-02-23 
00:30:58.000000000 +0100
+++ new/postfix-3.11.7/src/nbdb_reindexd/nbdb_process_test.c    2026-09-03 
20:36:33.000000000 +0200
@@ -102,6 +102,8 @@
     MOCK_STAT_REQ mock_stat_source;
     MOCK_STAT_REQ mock_stat_leg_idx;
     MOCK_STAT_REQ mock_stat_parent;
+    MOCK_STAT_REQ mock_stat_slash_path;        /* "/path" */
+    MOCK_STAT_REQ mock_stat_slash;     /* "/" */
     MOCK_STAT_REQ mock_stat_new_idx;
     MOCK_OPEN_AS_REQ mock_open;
     MOCK_SPAWN_CMD_REQ mock_spawn;
@@ -143,6 +145,7 @@
     nbdb_util_init(tp->params.migr_level);
     setup_mock_cdb("{{x = x}}");
     setup_mock_lmdb("{{x = x}}");
+    teardown_mock_stat();
     if (nbdb_level >= NBDB_LEV_CODE_REINDEX) {
        var_nbdb_allow_root_pfxs = (char *) tp->params.allow_root_pfxs;
        var_nbdb_allow_user_pfxs = (char *) tp->params.allow_user_pfxs;
@@ -154,6 +157,8 @@
        setup_mock_stat(&tp->mock_stat_leg_idx);
        setup_mock_stat(&tp->mock_stat_parent);
        setup_mock_stat(&tp->mock_stat_new_idx);
+       setup_mock_stat(&tp->mock_stat_slash_path);
+       setup_mock_stat(&tp->mock_stat_slash);
        setup_mock_vstream_fopen_as(&tp->mock_open);
        setup_mock_spawn_command(&tp->mock_spawn);
     }
@@ -222,6 +227,16 @@
            .out_errno = 0,
            .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
@@ -286,6 +301,16 @@
            .out_errno = 0,
            .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
@@ -338,6 +363,16 @@
            .out_errno = 0,
            .out_st = {.st_mode = S_IWGRP,.st_uid = 0,.st_gid = 0,},
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
@@ -379,6 +414,16 @@
            .out_errno = 0,
            .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
@@ -430,6 +475,16 @@
                .st_mode = S_IRWXU,.st_uid = 1,.st_gid = 0,
            },
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
@@ -450,7 +505,7 @@
            .in_new_type = "hash",
            .in_source_path = "/path/to/file",
            .want_status = NBDB_STAT_ERROR,
-           .want_why = "could not execute command 'postmap cdb:/path/to/file': 
table /path/to/file has an unexpected pathname",
+           .want_why = "table /path/to/file has an unexpected pathname",
        },
     },
 
@@ -471,6 +526,16 @@
            .out_errno = 0,
            .out_st = {.st_mode = S_IRWXU,.st_uid = 1,.st_gid = 0,},
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
@@ -524,13 +589,23 @@
            .out_errno = 0,
            .out_st = {.st_mode = S_IRWXU,.st_uid = 1,.st_gid = 0,},
        },
+       .mock_stat_slash_path = {
+           .want_path = "/path",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
+       .mock_stat_slash = {
+           .want_path = "/",
+           .out_errno = 0,
+           .out_st = {.st_mode = S_IRWXU,.st_uid = 0,.st_gid = 0,},
+       },
        .mock_stat_new_idx = {
            .want_path = "/path/to/file.cdb",
            .out_errno = ENOENT,
        },
        .mock_open = {
            .want_path = "/path/to/file",
-           .want_uid = 0,
+           .want_uid = 1,
            .want_gid = 0,
            .out_errno = 0,
            .out_data = "foo bar\nfoo :bar",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/nbdb_reindexd/nbdb_safe.c 
new/postfix-3.11.7/src/nbdb_reindexd/nbdb_safe.c
--- old/postfix-3.11.6/src/nbdb_reindexd/nbdb_safe.c    2026-02-19 
22:58:18.000000000 +0100
+++ new/postfix-3.11.7/src/nbdb_reindexd/nbdb_safe.c    2026-09-03 
20:36:33.000000000 +0200
@@ -10,6 +10,11 @@
 /*     uid_t   uid,
 /*     const struct stat *st)
 /*
+/*     bool    nbdb_safe_parent_for_uid(
+/*     uid_t   uid,
+/*     const char *parent_dir,
+/*     const struct stat *parent_dir_st)
+/*
 /*     bool    nbdb_safe_to_index_as_legacy_index_owner(
 /*     const char *source_path,
 /*     const struct stat *source_st,
@@ -26,6 +31,11 @@
 /*     the safety of other pathname components. It is a good idea to
 /*     trust only a limited number of pathname prefixes.
 /*
+/*     nbdb_safe_parent_for_uid() implements nbdb_safe_for_uid()
+/*     for the directories in the specified pathname. When a pathname
+/*     element contains a symlink, this function will examine the
+/*     symlink target but not the target's parent directories.
+/*
 /*     nbdb_safe_to_index_as_legacy_index_owner() determines
 /*     if a Berkeley DB source file and parent directory are 'safe'
 /*     for the uid of the Berkeley DB indexed file.
@@ -55,6 +65,8 @@
   */
 #include <sys_defs.h>
 #include <sys/stat.h>
+#include <string.h>
+#include <libgen.h>
 
  /*
   * Utility library.
@@ -67,6 +79,9 @@
 #include <allowed_prefix.h>
 #include <mail_conf.h>
 #include <mail_params.h>
+#include <msg.h>
+#include <mymalloc.h>
+#include <wrap_stat.h>
 
  /*
   * Application-specific.
@@ -74,6 +89,36 @@
 #include <nbdb_safe.h>
 #include <nbdb_reindexd.h>
 
+/* nbdb_safe_parent_for_uid - simple pathname safety */
+
+bool    nbdb_safe_parent_for_uid(uid_t uid, const char *path,
+                                      const struct stat * path_st)
+{
+    bool    ret;
+
+    /*
+     * See also the comment in nbdb_safe_for_uid().
+     */
+    if (!nbdb_safe_for_uid(uid, path_st)) {
+       ret = false;
+    } else if (strcmp(path, "/") == 0) {
+       ret = true;
+    } else {
+       char   *saved_path = mystrdup(path);
+       char   *parent = dirname(saved_path);
+       struct stat parent_st;
+
+       if (stat(parent, &parent_st) < 0) {
+           ret = false;
+           msg_warn("look up status for directory '%s': %m", parent);
+       } else {
+           ret = nbdb_safe_parent_for_uid(uid, parent, &parent_st);
+       }
+       myfree(saved_path);
+    }
+    return (ret);
+}
+
 /* nbdb_safe_for_uid - owned by the user or root, not group or other writable 
*/
 
 bool    nbdb_safe_for_uid(uid_t uid, const struct stat *st)
@@ -147,7 +192,7 @@
                        leg_idx_path, (int) runas_uid, source_path);
        return (false);
     }
-    if (!nbdb_safe_for_uid(runas_uid, parent_dir_st)) {
+    if (!nbdb_safe_parent_for_uid(runas_uid, parent_dir, parent_dir_st)) {
        vstring_sprintf(why, "legacy indexed file '%s' is owned by "
                        "uid '%d', but parent directory '%s' is "
                        "owned or writable by other user; to allow "
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/nbdb_reindexd/nbdb_safe.h 
new/postfix-3.11.7/src/nbdb_reindexd/nbdb_safe.h
--- old/postfix-3.11.6/src/nbdb_reindexd/nbdb_safe.h    2026-02-19 
22:58:18.000000000 +0100
+++ new/postfix-3.11.7/src/nbdb_reindexd/nbdb_safe.h    2026-09-03 
20:36:33.000000000 +0200
@@ -25,6 +25,7 @@
   * Internal API.
   */
 extern bool nbdb_safe_for_uid(uid_t, const struct stat *);
+extern bool nbdb_safe_parent_for_uid(uid_t, const char *, const struct stat *);
 extern bool nbdb_safe_to_index_as_legacy_index_owner(
                                          const char *, const struct stat *,
                                          const char *, const struct stat *,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/nbdb_reindexd/nbdb_safe_test.c 
new/postfix-3.11.7/src/nbdb_reindexd/nbdb_safe_test.c
--- old/postfix-3.11.6/src/nbdb_reindexd/nbdb_safe_test.c       2026-02-19 
22:58:18.000000000 +0100
+++ new/postfix-3.11.7/src/nbdb_reindexd/nbdb_safe_test.c       2026-09-03 
20:36:33.000000000 +0200
@@ -21,6 +21,7 @@
   * System library.
   */
 #include <sys_defs.h>
+#include <sys/stat.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -41,6 +42,11 @@
 #include <mail_params.h>
 
  /*
+  * Test library.
+  */
+#include <mock_stat.h>
+
+ /*
   * Application-specific.
   */
 #include <nbdb_safe.h>
@@ -72,8 +78,11 @@
     struct stat source_st;
     const char *leg_idx_path;
     struct stat leg_idx_st;
-    const char *parent_dir;
+    const char *parent_dir;    /* "/etc/postfix" */
     struct stat parent_dir_st;
+    MOCK_STAT_REQ slash_etc_dir;       /* "/etc" */
+    MOCK_STAT_REQ slash_dir;   /* "/" */
+    struct stat slash_dir_st;
     const char *allow_root_pfxs;
     const char *allow_user_pfxs;
     const char *want_why;
@@ -118,6 +127,9 @@
     var_nbdb_allow_user_pfxs = (char *) tp->allow_user_pfxs;
     parsed_allow_root_pfxs = allowed_prefix_create(var_nbdb_allow_root_pfxs);
     parsed_allow_user_pfxs = allowed_prefix_create(var_nbdb_allow_user_pfxs);
+    teardown_mock_stat();
+    setup_mock_stat(&tp->slash_etc_dir);
+    setup_mock_stat(&tp->slash_dir);
     got_bool = nbdb_safe_to_index_as_legacy_index_owner(
                                            tp->source_path, &tp->source_st,
                                          tp->leg_idx_path, &tp->leg_idx_st,
@@ -231,6 +243,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = true,
@@ -244,6 +258,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/posttfix",
        .allow_user_pfxs = "",
        .want_bool = false,
@@ -257,6 +273,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = false,
@@ -270,6 +288,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = false,
@@ -283,11 +303,43 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = false,
        .want_why = "'/etc/postfix/access' is owned or writable by other user",
     },
+    {.label = "safe_to_index_as_root:bad_root_owner",
+       .action = test_nbdb_safe_to_index_as_legacy_index_owner,
+       .source_path = "/etc/postfix/access",
+       .source_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .leg_idx_path = "/etc/postfix/access.db",
+       .leg_idx_st = {.st_uid = 0,},
+       .parent_dir = "/etc/postfix",
+       .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 1,.st_mode = S_IRWXU,}, 0,},
+       .allow_root_pfxs = "/etc/postfix",
+       .allow_user_pfxs = "",
+       .want_bool = false,
+       .want_why = "",
+    },
+    {.label = "safe_to_index_as_root:bad_root_perms",
+       .action = test_nbdb_safe_to_index_as_legacy_index_owner,
+       .source_path = "/etc/postfix/access",
+       .source_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .leg_idx_path = "/etc/postfix/access.db",
+       .leg_idx_st = {.st_uid = 0,},
+       .parent_dir = "/etc/postfix",
+       .parent_dir_st = {.st_uid = 0,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IWOTH,}, 0,},
+       .allow_root_pfxs = "/etc/postfix",
+       .allow_user_pfxs = "",
+       .want_bool = false,
+       .want_why = "",
+    },
     {.label = "safe_to_index_as_root:bad_parent_owner",
        .action = test_nbdb_safe_to_index_as_legacy_index_owner,
        .source_path = "/etc/postfix/access",
@@ -296,6 +348,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 1,.st_mode = S_IRWXU,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = false,
@@ -309,6 +363,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IWGRP,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = false,
@@ -322,6 +378,8 @@
        .leg_idx_st = {.st_uid = 0,},
        .parent_dir = "/etc/postfix",
        .parent_dir_st = {.st_uid = 0,.st_mode = S_IWOTH,},
+       .slash_etc_dir = {"/etc", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
+       .slash_dir = {"/", {.st_uid = 0,.st_mode = S_IRWXU,}, 0,},
        .allow_root_pfxs = "/etc/postfix",
        .allow_user_pfxs = "",
        .want_bool = false,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/pipe/pipe.c 
new/postfix-3.11.7/src/pipe/pipe.c
--- old/postfix-3.11.6/src/pipe/pipe.c  2025-09-16 23:05:54.000000000 +0200
+++ new/postfix-3.11.7/src/pipe/pipe.c  2026-09-03 20:39:05.000000000 +0200
@@ -765,12 +765,6 @@
                 * Either anything to the left of the extension delimiter or,
                 * in absence of the latter, anything to the left of the
                 * rightmost @.
-                * 
-                * Beware: if the user name is blank (e.g. +user@host), the
-                * argument is suppressed. This is necessary to allow for
-                * cyrus bulletin-board (global mailbox) delivery. XXX But,
-                * skipping empty user parts will also prevent other
-                * expansions of this specific command-line argument.
                 */
                if (state.expand_flag & PIPE_FLAG_USER) {
                    morph_recipient(buf, rcpt_list->info[i].address,
@@ -780,8 +774,7 @@
                                 rcpt_list->info[i].address);
                    if (*var_rcpt_delim)
                        split_addr(STR(buf), var_rcpt_delim);
-                   if (*STR(buf) == 0)
-                       continue;
+                   /* 202607 Qualys+Mythos: don't skip arg if $user is "".*/
                    dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/postalias/postalias.c 
new/postfix-3.11.7/src/postalias/postalias.c
--- old/postfix-3.11.6/src/postalias/postalias.c        2026-02-20 
20:37:30.000000000 +0100
+++ new/postfix-3.11.7/src/postalias/postalias.c        2026-09-03 
21:04:44.000000000 +0200
@@ -349,11 +349,30 @@
     /*
      * If running as root, run as the owner of the source file, so that the
      * result shows proper ownership, and so that a bug in postalias does not
-     * allow privilege escalation.
+     * allow privilege escalation. If the source file is owned by root, but
+     * the parent directory is not, log a warning with suggestions.
      */
-    if ((postalias_flags & POSTALIAS_FLAG_AS_OWNER) && getuid() == 0
-       && (st.st_uid != geteuid() || st.st_gid != getegid()))
-       set_eugid(st.st_uid, st.st_gid);
+    if ((postalias_flags & POSTALIAS_FLAG_AS_OWNER) && getuid() == 0) {
+       if (st.st_uid != 0) {
+           set_eugid(st.st_uid, st.st_gid);
+       } else {
+           char   *parent_dir;
+           struct stat parent_st;
+
+           parent_dir = sane_dirname((VSTRING *) 0, path_name);
+           if (stat(parent_dir, &parent_st) < 0)
+               msg_fatal("stat %s: %m", parent_dir);
+           if (parent_st.st_uid != 0) {
+               msg_warn("Creating root-owned database file(s) %s.* "
+                        "in non-root-owned directory %s. Files "
+                        "%s, %s.*, and directory %s should have "
+                        "the same owner. Please fix ownership "
+                        "to avoid a privilege-escalation attack.",
+                        path_name, parent_dir, VSTREAM_PATH(source_fp),
+                        path_name, parent_dir);
+           }
+       }
+    }
 
     /*
      * Override the default per-table cache size for DB map (re)builds. We
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/postdrop/postdrop.c 
new/postfix-3.11.7/src/postdrop/postdrop.c
--- old/postfix-3.11.6/src/postdrop/postdrop.c  2026-08-09 22:31:40.000000000 
+0200
+++ new/postfix-3.11.7/src/postdrop/postdrop.c  2026-09-03 21:06:43.000000000 
+0200
@@ -515,6 +515,11 @@
            msg_fatal("uid=%ld: malformed input", (long) uid);
        if (rec_type == 0 || strchr(*expected, rec_type) == 0)
            msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, 
rec_type);
+       /* 202607 OpenAI: reject line breaks and nulls in envelope content. */
+       if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT
+           && strcspn(vstring_str(buf), "\r\n") != VSTRING_LEN(buf))
+           msg_fatal("uid=%ld: null or line break in '%s' record type: %.200s",
+                     (long) uid, rec_type_name(rec_type), vstring_str(buf));
        if (rec_type == **expected)
            expected++;
        /* Override time information from the untrusted caller. */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/postmap/postmap.c 
new/postfix-3.11.7/src/postmap/postmap.c
--- old/postfix-3.11.6/src/postmap/postmap.c    2026-02-20 20:37:30.000000000 
+0100
+++ new/postfix-3.11.7/src/postmap/postmap.c    2026-09-03 21:04:44.000000000 
+0200
@@ -442,11 +442,30 @@
     /*
      * If running as root, run as the owner of the source file, so that the
      * result shows proper ownership, and so that a bug in postmap does not
-     * allow privilege escalation.
+     * allow privilege escalation. If the source file is owned by root, but
+     * the parent directory is not, log a warning with suggestions.
      */
-    if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0
-       && (st.st_uid != geteuid() || st.st_gid != getegid()))
-       set_eugid(st.st_uid, st.st_gid);
+    if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0) {
+       if (st.st_uid != 0) {
+           set_eugid(st.st_uid, st.st_gid);
+       } else {
+           char   *parent_dir;
+           struct stat parent_st;
+
+           parent_dir = sane_dirname((VSTRING *) 0, path_name);
+           if (stat(parent_dir, &parent_st) < 0)
+               msg_fatal("stat %s: %m", parent_dir);
+           if (parent_st.st_uid != 0) {
+               msg_warn("Creating root-owned database file(s) %s.* "
+                        "in non-root-owned directory %s. Files "
+                        "%s, %s.*, and directory %s should have "
+                        "the same owner. Please fix ownership "
+                        "to avoid a privilege-escalation attack.",
+                        path_name, parent_dir, VSTREAM_PATH(source_fp),
+                        path_name, parent_dir);
+           }
+       }
+    }
 
     /*
      * Override the default per-table cache size for DB map (re)builds. We
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/postqueue/postqueue.c 
new/postfix-3.11.7/src/postqueue/postqueue.c
--- old/postfix-3.11.6/src/postqueue/postqueue.c        2025-12-19 
18:22:14.000000000 +0100
+++ new/postfix-3.11.7/src/postqueue/postqueue.c        2026-09-03 
20:33:08.000000000 +0200
@@ -431,10 +431,11 @@
            stat = vstream_pclose(showq);
        }
        argv_free(argv);
-       myfree(showq_path);
+       /* 202606 Qualys+Mythos: don't free() text before logging. */
        if (stat != 0)
            msg_fatal_status(stat < 0 ? EX_OSERR : EX_SOFTWARE,
                             "Error running %s", showq_path);
+       myfree(showq_path);
     }
 
     /*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/smtp/smtp_chat.c 
new/postfix-3.11.7/src/smtp/smtp_chat.c
--- old/postfix-3.11.6/src/smtp/smtp_chat.c     2021-10-02 16:46:46.000000000 
+0200
+++ new/postfix-3.11.7/src/smtp/smtp_chat.c     2026-09-03 20:31:04.000000000 
+0200
@@ -413,7 +413,8 @@
     if (three_digs != 0) {
        rdata.code = atoi(STR(session->buffer));
        if (strchr("245", STR(session->buffer)[0]) != 0) {
-           for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
+           /* 202606 Qualys+Mythos: start loop immediately after 'ddd'. */
+           for (cp = STR(session->buffer) + 3; *cp == ' '; cp++)
                 /* void */ ;
            if ((len = dsn_valid(cp)) > 0 && *cp == *STR(session->buffer)) {
                vstring_strncpy(rdata.dsn_buf, cp, len);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/smtp/smtp_proto.c 
new/postfix-3.11.7/src/smtp/smtp_proto.c
--- old/postfix-3.11.6/src/smtp/smtp_proto.c    2026-06-08 18:06:22.000000000 
+0200
+++ new/postfix-3.11.7/src/smtp/smtp_proto.c    2026-09-03 20:43:39.000000000 
+0200
@@ -1704,7 +1704,10 @@
 
     /* Caution: changes to RETURN() also affect code outside the main loop. */
 
+    /* 202607 Qualys+Mythos: evaluate argument before freeing resources. */
+
 #define RETURN(x) do { \
+       int _rv = (x); \
        if (recv_state != SMTP_STATE_LAST) \
            DONT_CACHE_THIS_SESSION; \
        vstring_free(next_command); \
@@ -1712,7 +1715,7 @@
            myfree((void *) survivors); \
        if (session->mime_state) \
            session->mime_state = mime_state_free(session->mime_state); \
-       return (x); \
+       return (_rv); \
     } while (0)
 
 #define SENDER_IS_AHEAD \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/smtpd/smtpd.c 
new/postfix-3.11.7/src/smtpd/smtpd.c
--- old/postfix-3.11.6/src/smtpd/smtpd.c        2026-08-09 18:55:01.000000000 
+0200
+++ new/postfix-3.11.7/src/smtpd/smtpd.c        2026-09-03 21:52:19.000000000 
+0200
@@ -4306,8 +4306,8 @@
                    state->act_size += len + 2;
                    if (*start == '.' && proxy != 0
                        && state->bdat_prev_rec_type != REC_TYPE_CONT)
-                       if (out_record(out_stream, REC_TYPE_CONT, ".", 1) < 0)
-                           state->err = out_error;
+                       /* 202507 OpenAI: more robust dot-stuffing. */
+                       vstring_prepend(state->bdat_get_buffer, ".", 1);
                    if (state->err == CLEANUP_STAT_OK
                        && out_record(out_stream, curr_rec_type,
                                      vstring_str(state->bdat_get_buffer),
@@ -4491,7 +4491,7 @@
     /* Fix 20140707: Check the VRFY command. */
     if (smtputf8 == 0 && var_strict_smtputf8) {
        if (*STR(state->addr_buf) && !allascii(STR(state->addr_buf))) {
-           mail_reset(state);
+           /* Wonyoung.Jung 20260811: don't reset SMTP state. */
            smtpd_chat_reply(state, "553 5.6.7 Must declare SMTPUTF8 to send 
unicode address");
            return (-1);
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/smtpd/smtpd_chat.c 
new/postfix-3.11.7/src/smtpd/smtpd_chat.c
--- old/postfix-3.11.6/src/smtpd/smtpd_chat.c   2025-12-22 23:01:57.000000000 
+0100
+++ new/postfix-3.11.7/src/smtpd/smtpd_chat.c   2026-09-03 21:12:17.000000000 
+0200
@@ -217,6 +217,7 @@
     char   *end;
     const char *alt_reply;
     const char *footer;
+    ssize_t line_len;                  /* 202607 OpenAI line length checks */
 
     /*
      * Slow down clients that make errors. Sleep-on-anything slows down
@@ -258,20 +259,26 @@
 
     /* All 5xx replies must have a 5.xx.xx detail code. */
     for (cp = STR(state->buffer), end = cp + strlen(STR(state->buffer));;) {
+       if ((next = strstr(cp, "\r\n")) != 0)
+           line_len = next - cp;
+       else
+           line_len = end - cp;
        if (var_soft_bounce) {
-           if (cp[0] == '5') {
+           if (line_len > 0 && cp[0] == '5') {
                cp[0] = '4';
-               if (cp[4] == '5')
+               if (line_len > 4 && cp[4] == '5')
                    cp[4] = '4';
            }
        }
        /* This is why we use strlen() above instead of VSTRING_LEN(). */
-       if ((next = strstr(cp, "\r\n")) != 0) {
+       if (next != 0) {
            *next = 0;
-           if (next[2] != 0)
-               cp[3] = '-';                    /* contact footer kludge */
-           else
+           if (next[2] != 0) {
+               if (line_len > 3)
+                   cp[3] = '-';                /* contact footer kludge */
+           } else {
                next = end;                     /* strip trailing \r\n */
+           }
        } else {
            next = end;
        }
@@ -280,7 +287,7 @@
        if (msg_verbose)
            msg_info("> %s: %s", state->namaddr, cp);
 
-       smtp_fputs(cp, next - cp, state->client);
+       smtp_fputs(cp, line_len, state->client);
        if (next < end)
            cp = next + 2;
        else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/smtpd/smtpd_proxy.c 
new/postfix-3.11.7/src/smtpd/smtpd_proxy.c
--- old/postfix-3.11.6/src/smtpd/smtpd_proxy.c  2025-09-17 22:56:39.000000000 
+0200
+++ new/postfix-3.11.7/src/smtpd/smtpd_proxy.c  2026-09-03 21:14:37.000000000 
+0200
@@ -883,12 +883,39 @@
     }
 }
 
+/* smtpd_proxy_fix_line_breaks - replace line-break crud with space */
+
+static const char *smtpd_proxy_fix_line_breaks(SMTPD_PROXY *proxy,
+                                                      const char *data,
+                                                      ssize_t len)
+{
+    char   *cp;
+
+    /*
+     * We still need to eliminate <CR>.
+     * 
+     * <LF> is already eliminated by smtp_get_no_except() (used for BDAT), and
+     * by smtp_get() (used for DATA) which calls smtp_get_no_except().
+     */
+    if (memchr(data, '\r', len) != 0) {
+       if (proxy->data_buf == 0)
+           proxy->data_buf = vstring_alloc(100);
+       vstring_memcpy(proxy->data_buf, data, len);
+       for (cp = STR(proxy->data_buf); cp < vstring_end(proxy->data_buf); cp++)
+           if (*cp == '\r')
+               *cp = ' ';
+       data = STR(proxy->data_buf);
+    }
+    return (data);
+}
+
 /* smtpd_proxy_rec_put - send message content, rec_put() clone */
 
 static int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
                                       const char *data, ssize_t len)
 {
     const char *myname = "smtpd_proxy_rec_put";
+    SMTPD_PROXY *proxy = VSTREAM_TO_SMTPD_STATE(stream)->proxy;
     int     err = 0;
 
     /*
@@ -899,20 +926,31 @@
        (void) smtpd_proxy_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream), err);
        return (REC_TYPE_ERROR);
     }
+    if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM)
+       msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
+
+    /* 202608 OpenAI: fixed guard against header prepend edge cases. */
+    if (proxy->last_text_rec != REC_TYPE_CONT) {
+       if (proxy->flags & SMTPD_PROXY_FLAG_REQTLS_HDR)
+           smtpd_proxy_handle_reqtls(proxy, stream, data, len);
+       /* Wietse: leading '.' may be followed by line-break crud. */
+       if (data[0] == '.' && (len == 1 || data[1] != '.')) {
+           msg_warn("prepending '.' to malformed line: '%.*s'",
+                    len > 10 ? 10 : (int) len, data);
+           smtp_fwrite(".", 1, stream);
+       }
+    }
 
     /*
      * Send one content record. Errors and results must be as with rec_put().
      */
+    /* 202607 OpenAI: replace line-break crud with space. */
+    data = smtpd_proxy_fix_line_breaks(proxy, data, len);
     if (rec_type == REC_TYPE_NORM) {
-       SMTPD_PROXY *proxy = VSTREAM_TO_SMTPD_STATE(stream)->proxy;
-
-       if (proxy->flags & SMTPD_PROXY_FLAG_REQTLS_HDR)
-           smtpd_proxy_handle_reqtls(proxy, stream, data, len);
        smtp_fputs(data, len, stream);
     } else if (rec_type == REC_TYPE_CONT)
        smtp_fwrite(data, len, stream);
-    else
-       msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
+    proxy->last_text_rec = rec_type;
     return (rec_type);
 }
 
@@ -1053,10 +1091,10 @@
      * parameters, and have the compiler enforce the argument count.
      */
 #define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
-       a12, a13) \
+       a12, a13, a14, a15) \
        ((p) = (SMTPD_PROXY *) mymalloc(sizeof(*(p))), (p)->a1, (p)->a2, \
         (p)->a3, (p)->a4, (p)->a5, (p)->a6, (p)->a7, (p)->a8, (p)->a9, \
-        (p)->a10, (p)->a11, (p)->a12, (p)->a13, (p))
+        (p)->a10, (p)->a11, (p)->a12, (p)->a13, (p)->a14, (p)->a15, (p))
 
     /*
      * Sanity check.
@@ -1077,7 +1115,8 @@
                              flags = flags, service_stream = 0,
                              service_name = service, timeout = timeout,
                              ehlo_name = ehlo_name, mail_from = mail_from,
-                             reqtls_esmtp_hdr_seen = 0);
+                             reqtls_esmtp_hdr_seen = 0, last_text_rec = 0,
+                             data_buf = 0);
        if (smtpd_proxy_connect(state) < 0) {
            /* NOT: smtpd_proxy_free(state); we still need proxy->reply. */
            return (-1);
@@ -1108,7 +1147,8 @@
                              flags = flags, service_stream = 0,
                              service_name = service, timeout = timeout,
                              ehlo_name = ehlo_name, mail_from = mail_from,
-                             reqtls_esmtp_hdr_seen = 0);
+                             reqtls_esmtp_hdr_seen = 0, last_text_rec = 0,
+                             data_buf = 0);
        return (0);
 #endif
     }
@@ -1151,6 +1191,8 @@
        vstring_free(proxy->request);
     if (proxy->reply != 0)
        vstring_free(proxy->reply);
+    if (proxy->data_buf != 0)
+       vstring_free(proxy->data_buf);
     myfree((void *) proxy);
     state->proxy = 0;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/smtpd/smtpd_proxy.h 
new/postfix-3.11.7/src/smtpd/smtpd_proxy.h
--- old/postfix-3.11.6/src/smtpd/smtpd_proxy.h  2025-09-17 22:56:39.000000000 
+0200
+++ new/postfix-3.11.7/src/smtpd/smtpd_proxy.h  2026-09-03 21:14:37.000000000 
+0200
@@ -38,6 +38,8 @@
     const char *ehlo_name;
     const char *mail_from;
     int     reqtls_esmtp_hdr_seen;
+    int     last_text_rec;
+    VSTRING *data_buf;
 } SMTPD_PROXY;
 
 #define SMTPD_PROXY_FLAG_SPEED_ADJUST  (1<<0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/tls/tls_server.c 
new/postfix-3.11.7/src/tls/tls_server.c
--- old/postfix-3.11.6/src/tls/tls_server.c     2025-07-30 00:14:02.000000000 
+0200
+++ new/postfix-3.11.7/src/tls/tls_server.c     2026-09-03 21:11:03.000000000 
+0200
@@ -827,6 +827,11 @@
     const char *cipher_list;
     TLS_APPL_STATE *app_ctx = props->ctx;
     int     log_mask = app_ctx->log_mask;
+    /* 202607 OpenAI: isolate session tickets by master.cf service name. */ 
+    EVP_MD_CTX *sessid_ctx_md = 0;
+    unsigned char sessid_ctx[EVP_MAX_MD_SIZE];
+    unsigned int sessid_ctx_len;
+    static const char sessid_ctx_label[] = "Postfix/TLS serverid";
 
     /*
      * Implicitly enable logging of trust chain errors when verified certs
@@ -868,6 +873,29 @@
     TLScontext->stream = props->stream;
     TLScontext->mdalg = props->mdalg;
 
+    /*
+     * The serverid already partitions stateful sessions in the tlsmgr
+     * session cache. Apply the same partition to stateless sessions; all
+     * services in one Postfix instance share the tlsmgr ticket keys.
+     */
+    if (tls_digest_byname(LN_sha256, &sessid_ctx_md) == 0
+       || EVP_DigestUpdate(sessid_ctx_md, sessid_ctx_label,
+                           sizeof(sessid_ctx_label)) != 1
+       || EVP_DigestUpdate(sessid_ctx_md, props->serverid,
+                           strlen(props->serverid) + 1) != 1
+       || EVP_DigestFinal_ex(sessid_ctx_md, sessid_ctx, &sessid_ctx_len) != 1
+       || sessid_ctx_len > SSL_MAX_SID_CTX_LENGTH
+       || SSL_set_session_id_context(TLScontext->con, sessid_ctx,
+                                     sessid_ctx_len) != 1) {
+       msg_warn("Could not set TLS session ID context");
+       tls_print_errors();
+       if (sessid_ctx_md)
+           EVP_MD_CTX_destroy(sessid_ctx_md);
+       tls_free_context(TLScontext);
+       return (0);
+    }
+    EVP_MD_CTX_destroy(sessid_ctx_md);
+
     if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
        msg_warn("Could not set application data for 'TLScontext->con'");
        tls_print_errors();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/util/mymalloc.c 
new/postfix-3.11.7/src/util/mymalloc.c
--- old/postfix-3.11.6/src/util/mymalloc.c      2026-06-28 22:10:45.000000000 
+0200
+++ new/postfix-3.11.7/src/util/mymalloc.c      2026-09-03 20:45:36.000000000 
+0200
@@ -197,6 +197,10 @@
 #ifndef NO_SHARED_EMPTY_STRINGS
     if (ptr == empty_string)
        return (mymalloc(len));
+    if (len == 0) {
+       myfree(ptr);
+       return (mymalloc(0));
+    }
 #endif
 
     /*
@@ -204,7 +208,7 @@
      * allows us to catch integer overflow problems that weren't already
      * caught up-stream.
      */
-    if (len < 1)
+    if (len < 0)
        msg_panic("myrealloc: requested length %ld", (long) len);
 #ifdef MYMALLOC_FUZZ
     len += MYMALLOC_FUZZ;
@@ -266,7 +270,7 @@
     if (len < 0)
        msg_panic("mystrndup: requested length %ld", (long) len);
 #ifndef NO_SHARED_EMPTY_STRINGS
-    if (*str == 0)
+    if (*str == 0 || /* fix 20220615 */ len == 0)
        return ((char *) empty_string);
 #endif
     if ((cp = memchr(str, 0, len)) != 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/virtual/Makefile.in 
new/postfix-3.11.7/src/virtual/Makefile.in
--- old/postfix-3.11.6/src/virtual/Makefile.in  2025-09-30 00:35:01.000000000 
+0200
+++ new/postfix-3.11.7/src/virtual/Makefile.in  2026-09-03 22:04:41.000000000 
+0200
@@ -96,6 +96,7 @@
 mailbox.o: ../../include/pol_stats.h
 mailbox.o: ../../include/recipient_list.h
 mailbox.o: ../../include/safe_open.h
+mailbox.o: ../../include/sane_strtol.h
 mailbox.o: ../../include/sent.h
 mailbox.o: ../../include/set_eugid.h
 mailbox.o: ../../include/stringops.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/postfix-3.11.6/src/virtual/mailbox.c 
new/postfix-3.11.7/src/virtual/mailbox.c
--- old/postfix-3.11.6/src/virtual/mailbox.c    2018-01-06 16:43:44.000000000 
+0100
+++ new/postfix-3.11.7/src/virtual/mailbox.c    2026-09-03 21:50:05.000000000 
+0200
@@ -57,6 +57,7 @@
 #include <mymalloc.h>
 #include <stringops.h>
 #include <set_eugid.h>
+#include <sane_strtol.h>
 
 /* Global library. */
 
@@ -176,7 +177,8 @@
     const char *uid_res;
     const char *gid_res;
     DSN_BUF *why = state.msg_attr.why;
-    long    n;
+    char   *end;
+    unsigned long n;
 
     /*
      * Make verbose logging easier to understand.
@@ -215,6 +217,15 @@
 
 #define RETURN(res) { myfree(usr_attr.mailbox); return (res); }
 
+    if (strstr(usr_attr.mailbox + strlen(var_virt_mailbox_base), "/../")) {
+       msg_warn("recipient %s: bad mailbox path %s in %s",
+            state.msg_attr.user, mailbox_res, virtual_mailbox_maps->title);
+       dsb_simple(why, "4.3.5", "mail system configuration error");
+       *statusp = defer_append(BOUNCE_FLAGS(state.request),
+                               BOUNCE_ATTR(state.msg_attr));
+       RETURN(YES);
+    }
+
     /*
      * Look up the mailbox owner rights. Defer in case of trouble.
      */
@@ -228,7 +239,10 @@
                                BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
-    if ((n = atol(uid_res)) < var_virt_minimum_uid) {
+    usr_attr.uid = (uid_t) (n = sane_strtoul(uid_res, &end, 10));
+    if (*end != 0 || errno != 0 || usr_attr.uid != n
+       || usr_attr.uid == (uid_t) - 1          /* Special for safe_open()) */
+       || usr_attr.uid < var_virt_minimum_uid) {
        msg_warn("recipient %s: bad uid %s in %s",
                 state.msg_attr.user, uid_res, virtual_uid_maps->title);
        dsb_simple(why, "4.3.5", "mail system configuration error");
@@ -236,7 +250,6 @@
                                BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
-    usr_attr.uid = (uid_t) n;
 
     /*
      * Look up the mailbox group rights. Defer in case of trouble.
@@ -251,7 +264,10 @@
                                BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
-    if ((n = atol(gid_res)) <= 0) {
+    usr_attr.gid = (gid_t) (n = sane_strtoul(gid_res, &end, 10));
+    if (*end != 0 || errno != 0 || usr_attr.gid !=n
+       || usr_attr.gid == 0                    /* Backwards compatibility */
+       || usr_attr.gid == (gid_t) - 1) {       /* Special for safe_open()) */
        msg_warn("recipient %s: bad gid %s in %s",
                 state.msg_attr.user, gid_res, virtual_gid_maps->title);
        dsb_simple(why, "4.3.5", "mail system configuration error");
@@ -259,12 +275,10 @@
                                BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
-    usr_attr.gid = (gid_t) n;
-
     if (msg_verbose)
-       msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
+       msg_info("%s[%d]: set user_attr: %s, uid = %lu, gid = %lu",
                 myname, state.level, usr_attr.mailbox,
-                (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
+                (unsigned long) usr_attr.uid, (unsigned long) usr_attr.gid);
 
     /*
      * Deliver to mailbox or to maildir.

++++++ postfix-vda-v14-3.0.3.patch ++++++
--- /var/tmp/diff_new_pack.RVJ8R7/_old  2026-09-10 11:47:34.201957410 +0200
+++ /var/tmp/diff_new_pack.RVJ8R7/_new  2026-09-10 11:47:34.207957661 +0200
@@ -19,7 +19,7 @@
 ===================================================================
 --- src/global/mail_params.h.orig
 +++ src/global/mail_params.h
-@@ -2742,6 +2742,54 @@ extern char *var_virt_uid_maps;
+@@ -2620,6 +2620,54 @@ extern char *var_virt_uid_maps;
  #define DEF_VIRT_GID_MAPS             ""
  extern char *var_virt_gid_maps;
  
@@ -95,15 +95,15 @@
 ===================================================================
 --- src/virtual/mailbox.c.orig
 +++ src/virtual/mailbox.c
-@@ -57,6 +57,7 @@
- #include <mymalloc.h>
+@@ -58,6 +58,7 @@
  #include <stringops.h>
  #include <set_eugid.h>
+ #include <sane_strtol.h>
 +#include <iostuff.h>
  
  /* Global library. */
  
-@@ -75,6 +76,70 @@
+@@ -76,6 +77,70 @@
  #define YES   1
  #define NO    0
  
@@ -174,7 +174,7 @@
  /* deliver_mailbox_file - deliver to recipient mailbox */
  
  static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
-@@ -219,62 +284,72 @@ int     deliver_mailbox(LOCAL_STATE stat
+@@ -230,51 +295,52 @@ int     deliver_mailbox(LOCAL_STATE stat
       * Look up the mailbox owner rights. Defer in case of trouble.
       */
      uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user,
@@ -197,7 +197,10 @@
 +        }
      }
 +
-     if ((n = atol(uid_res)) < var_virt_minimum_uid) {
+     usr_attr.uid = (uid_t) (n = sane_strtoul(uid_res, &end, 10));
+     if (*end != 0 || errno != 0 || usr_attr.uid != n
+       || usr_attr.uid == (uid_t) - 1          /* Special for safe_open()) */
+       || usr_attr.uid < var_virt_minimum_uid) {
 -      msg_warn("recipient %s: bad uid %s in %s",
 -               state.msg_attr.user, uid_res, virtual_uid_maps->title);
 -      dsb_simple(why, "4.3.5", "mail system configuration error");
@@ -209,8 +212,6 @@
 +              *statusp = defer_append(BOUNCE_FLAGS(state.request), 
BOUNCE_ATTR(state.msg_attr));
 +              RETURN(YES);
      }
-+
-     usr_attr.uid = (uid_t) n;
  
      /*
       * Look up the mailbox group rights. Defer in case of trouble.
@@ -235,7 +236,10 @@
 +        }
      }
 +
-     if ((n = atol(gid_res)) <= 0) {
+     usr_attr.gid = (gid_t) (n = sane_strtoul(gid_res, &end, 10));
+     if (*end != 0 || errno != 0 || usr_attr.gid !=n
+       || usr_attr.gid == 0                    /* Backwards compatibility */
+       || usr_attr.gid == (gid_t) - 1) {       /* Special for safe_open()) */
 -      msg_warn("recipient %s: bad gid %s in %s",
 -               state.msg_attr.user, gid_res, virtual_gid_maps->title);
 -      dsb_simple(why, "4.3.5", "mail system configuration error");
@@ -248,18 +252,10 @@
 +              RETURN(YES);
      }
 +
-     usr_attr.gid = (gid_t) n;
- 
      if (msg_verbose)
--      msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
--               myname, state.level, usr_attr.mailbox,
--               (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
-+        msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
-+                myname, state.level, usr_attr.mailbox,
-+                (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
- 
-     /*
-      * Deliver to mailbox or to maildir.
+       msg_info("%s[%d]: set user_attr: %s, uid = %lu, gid = %lu",
+                myname, state.level, usr_attr.mailbox,
+@@ -285,10 +349,18 @@ int     deliver_mailbox(LOCAL_STATE stat
       */
  #define LAST_CHAR(s) (s[strlen(s) - 1])
  

Reply via email to