]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.9.6 v3.9.6
authorWietse Z Venema <wietse@porcupine.org>
Sun, 26 Oct 2025 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Mon, 27 Oct 2025 01:36:53 +0000 (12:36 +1100)
15 files changed:
postfix/HISTORY
postfix/conf/main.cf
postfix/src/global/mail_version.h
postfix/src/postcat/postcat.c
postfix/src/postconf/postconf_edit.c
postfix/src/posttls-finger/posttls-finger.c
postfix/src/showq/showq.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_key.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtpd/smtpd.c
postfix/src/util/dict_db.c
postfix/src/util/dict_dbm.c
postfix/src/util/dict_lmdb.c
postfix/src/util/dict_sdbm.c

index 762955fb2887e2c91e8b752ffc1974df46120bd9..ba062204ba05e212ae9511c3971edccd08adcbed 100644 (file)
@@ -28182,3 +28182,60 @@ Apologies for any names omitted.
        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.
+
+20250816
+
+       Bugfix (defect introduced: Postfix 3.0, date 20140731): the
+       smtpd 'disconnect' command counts did not count malformed
+       commands with "bad syntax" and "bad UTF-8 syntax" errors.
+       File: smtpd/smtpd.c.
+
+20250819
+
+       Bugfix: the 20250717 workaround broke DBM library support
+       which is still needed on Solaris. File: util/dict_dbm.c.
+
+20250823
+
+       Bugfix (defect introduced: Postfix 3.9, date 20230517):
+       posttls-finger logged a zero port number. Viktor Dukhovni.
+       File: posttls-finger/posttls-finger.c.
+
+20250829
+
+       Postfix 3.11 forward compatibility: allow a partial 'size'
+       record in maildrop queue files created with Postfix 3.11
+       or later, instead of logging an ugly warning. Files:
+       showq/showq.c, postcat/postcat.c.
+
+20250911
+
+       Bugfix (defect introduced: Postfix 3.0): the Postfix SMTP
+       client's connection reuse logic did not distinguish between
+       sessions that require SMTPUTF8 support, and sessions that
+       do not. The solution is to store sessions with different
+       SMTPUTF8 requirements under distinct connection cache storage
+       keys, and to preserve the availability of SMTPUTF8 support
+       in the connection cache, so that a reused connection will
+       be stored under the same keys as it was looked up with.
+       Finally, do not cache a connection when SMTPUTF8 is
+       required but the server does not support that feature.
+       Files: smtp/smtp.h, smtp/smtp_key.c, smtp/smtp_proto.c.
+
+20250919
+
+       Bugfix (defect introduced: Postfix 3.8, date 20220128): the
+       'postconf -e' output order for new main.cf entries was no
+       longer deterministic. Problem reported by Oleksandr Natalenko,
+       diagnosis by Eray Aslan. File: postconf/postconf_edit.c.
+
+       Add missing meta_directory and shlib_directory settings to
+       the stock main.cf file. Problem diagnosed by Eray Aslan.
+       File: conf/main.cf.
+
+20251021
+
+       Cleanup: the change at 20250717 could result in warnings
+       with "database X is older than source file Y". Files:
+       util/dict.c, util/dict_db.c, util/dict_dbm.c, util/dict_lmdb.c,
+       util/dict_sdbm.c.
index 2f4eba9ed980f7a77d6ef648cbbe590211e4fe5c..831ed3236bf6443d99e0ebf0fd8598b22d3b12b2 100644 (file)
@@ -683,3 +683,5 @@ sample_directory =
 #
 readme_directory =
 inet_protocols = ipv4
+shlib_directory = /usr/lib/postfix/${mail_version}
+meta_directory = /etc/postfix
index 5e4cd92c6059ce4f4b872fb0494e81614017e996..efe069408cd170f80034df1cacdbb88255abefb7 100644 (file)
@@ -20,8 +20,8 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20250818"
-#define MAIL_VERSION_NUMBER    "3.9.5"
+#define MAIL_RELEASE_DATE      "20251026"
+#define MAIL_VERSION_NUMBER    "3.9.6"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
index 26b46a91bb0ef1d793a85b2affc25622ec7a0615..0389e49baa6e00da772d011ce74ddfc40de0facd 100644 (file)
@@ -339,6 +339,10 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
            /* Optional output (here before we update the state machine). */
            if (do_print)
                PRINT_RECORD(flags, offset, rec_type, STR(buffer));
+           /* Postfix 3.11 maildrop files may have preliminary SIZE record. */
+           if (strncmp(VSTREAM_PATH(fp), MAIL_QUEUE_MAILDROP "/",
+                       sizeof(MAIL_QUEUE_MAILDROP)) == 0)
+               continue;
            /* Read the message size/offset for the state machine optimizer. */
            if (data_size >= 0 || data_offset >= 0) {
                msg_warn("file contains multiple size records");
index 437f2a87013ae379ec90d275fe1e23e5dbf70dde..64296581715a4b9752d479e5c0d25303e307c7a2 100644 (file)
@@ -66,6 +66,7 @@
 /* System library. */
 
 #include <sys_defs.h>
+#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
@@ -148,6 +149,16 @@ static void pcf_gobble_cf_line(VSTRING *full_entry_buf, VSTRING *line_buf,
     }
 }
 
+/* pcf_cmp_ht_key - qsort helper for ht_info pointer array */
+
+static int pcf_cmp_ht_key(const void *a, const void *b)
+{
+    HTABLE_INFO **ap = (HTABLE_INFO **) a;
+    HTABLE_INFO **bp = (HTABLE_INFO **) b;
+
+    return (strcmp(ap[0]->key, bp[0]->key));
+}
+
 /* pcf_edit_main - edit main.cf file */
 
 void    pcf_edit_main(int mode, int argc, char **argv)
@@ -262,7 +273,9 @@ void    pcf_edit_main(int mode, int argc, char **argv)
      * Generate new entries for parameters that were not found.
      */
     if (mode & PCF_EDIT_CONF) {
-       for (ht_info = ht = htable_list(table); *ht; ht++) {
+       ht_info = htable_list(table);
+       qsort((void *) ht_info, table->used, sizeof(*ht_info), pcf_cmp_ht_key);
+       for (ht = ht_info; *ht; ht++) {
            cvalue = (struct cvalue *) ht[0]->value;
            if (cvalue->found == 0)
                vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value);
index 6c7c378b691b2a4a3d51afec5dc363d7d3f49598..47542bcc20c2c754397b2f9a673e5887620efd12 100644 (file)
@@ -1647,7 +1647,7 @@ static void connect_remote(STATE *state, char *dest)
        if (level == TLS_LEV_INVALID
            || (state->stream = connect_addr(state, addr)) == 0) {
            msg_info("Failed to establish session to %s via %s:%u: %s",
-                    dest, HNAME(addr), addr->port,
+                    dest, HNAME(addr), ntohs(state->port),
                     vstring_str(state->why->reason));
            continue;
        }
index 80e1e89e2ee8335bc86c3f971b0548f8e6a3d16d..043c7ea7b81221b02c89d6008158d8e21ba4cfcf 100644 (file)
@@ -213,7 +213,8 @@ static void showq_report(VSTREAM *client, char *queue, char *id,
                arrival_time = atol(start);
            break;
        case REC_TYPE_SIZE:
-           if (msg_size_ok == 0) {
+           /* Postfix 3.11 maildrop files may have preliminary SIZE record. */
+           if (msg_size_ok == 0 && strcmp(queue, MAIL_QUEUE_MAILDROP) != 0) {
                msg_size_ok = (start[strspn(start, "0123456789 ")] == 0
                               && (msg_size = atol(start)) >= 0);
                if (msg_size_ok == 0) {
index 60c68f845f660d9a14c532cb678979c9bd9ca094..e51847b81bae8cf59350a7889cfad559907df228 100644 (file)
@@ -655,12 +655,14 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
 #define SMTP_KEY_FLAG_ADDR             (1<<5)  /* remote address */
 #define SMTP_KEY_FLAG_PORT             (1<<6)  /* remote port */
 #define SMTP_KEY_FLAG_TLS_LEVEL                (1<<7)  /* requested TLS level */
+#define SMTP_KEY_FLAG_REQ_SMTPUTF8     (1<<8)  /* SMTPUTF8 is required */
 
 #define SMTP_KEY_MASK_ALL \
        (SMTP_KEY_FLAG_SERVICE | SMTP_KEY_FLAG_SENDER | \
        SMTP_KEY_FLAG_REQ_NEXTHOP | \
        SMTP_KEY_FLAG_CUR_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | \
-       SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL)
+       SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL | \
+       SMTP_KEY_FLAG_REQ_SMTPUTF8)
 
  /*
   * Conditional lookup-key flags for cached connections that may be
@@ -699,7 +701,8 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
   */
 #define SMTP_KEY_MASK_SCACHE_DEST_LABEL \
        (SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
-       | SMTP_KEY_FLAG_REQ_NEXTHOP)
+       | SMTP_KEY_FLAG_REQ_NEXTHOP | SMTP_KEY_FLAG_TLS_LEVEL \
+       | SMTP_KEY_FLAG_REQ_SMTPUTF8)
 
  /*
   * Connection-cache endpoint lookup key. The SENDER, CUR_NEXTHOP, HOSTNAME,
@@ -714,7 +717,8 @@ char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);
        | COND_SASL_SMTP_KEY_FLAG_CUR_NEXTHOP \
        | COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
        | COND_TLS_SMTP_KEY_FLAG_CUR_NEXTHOP | SMTP_KEY_FLAG_ADDR | \
-       SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL)
+       SMTP_KEY_FLAG_PORT | SMTP_KEY_FLAG_TLS_LEVEL \
+       | SMTP_KEY_FLAG_REQ_SMTPUTF8)
 
  /*
   * Silly little macros.
index 643f4db7e61d978e4c35d1efd453cd12df2b9667..d1d1edb0792328ee02279b3fc768e105c12605e8 100644 (file)
 /*     The current iterator's remote address.
 /* .IP SMTP_KEY_FLAG_PORT
 /*     The current iterator's remote port.
+/* .IP SMTP_KEY_FLAG_TLS_LEVEL
+/*     The requested TLS security level.
+/* .IP SMTP_KEY_FLAG_REQ_SMTPUTF8
+/*     Whether SMTPUTF8 support is required.
 /* .RE
 /* DIAGNOSTICS
 /*     Panic: undefined flag or zero flags. Fatal: out of memory.
   * Global library.
   */
 #include <mail_params.h>
+#include <smtputf8.h>
 
  /*
   * Application-specific.
   */
 #include <smtp.h>
 
+ /* Duplicated to minimze patch footprint. */
+#define DELIVERY_REQUIRES_SMTPUTF8(request) \
+       ((request->smtputf8 & SMTPUTF8_FLAG_REQUESTED) \
+       && (request->smtputf8 & ~SMTPUTF8_FLAG_REQUESTED))
+
  /*
   * We use a configurable field terminator and optional place holder for data
   * that is unavailable or inapplicable. We base64-encode content that
@@ -209,6 +219,20 @@ char   *smtp_key_prefix(VSTRING *buffer, const char *delim_na,
        smtp_key_append_na(buffer, delim_na);
 #endif
 
+    /*
+     * Require SMTPUTF8 support, if applicable. TODO(wietse) if a delivery
+     * request does not need SMTPUTF8, should we also search the connection
+     * cache for a connection that is known to support it? No, because the
+     * connection would be saved back under a key that does not require
+     * SMTPUTF8 support.
+     */
+    if (flags & SMTP_KEY_FLAG_REQ_SMTPUTF8)
+       smtp_key_append_uint(buffer,
+                            DELIVERY_REQUIRES_SMTPUTF8(state->request),
+                            delim_na);
+    else
+       smtp_key_append_na(buffer, delim_na);
+
     VSTRING_TERMINATE(buffer);
 
     return STR(buffer);
index e022bc2cff262e14a2a43e8c4eb9372c90f3a1c9..ff52e8a5a6587c5cc1a25875383337accb0ace2c 100644 (file)
@@ -643,14 +643,19 @@ int     smtp_helo(SMTP_STATE *state)
      * SMTPUTF8.
      * 
      * Fix 20140706: moved this before negotiating TLS, AUTH, and so on.
+     * 
+     * Fix 20250911: do not cache this session because it does not satisfy the
+     * requirement expressed in the cache storage key.
      */
     if ((session->features & SMTP_FEATURE_SMTPUTF8) == 0
-       && DELIVERY_REQUIRES_SMTPUTF8)
+       && DELIVERY_REQUIRES_SMTPUTF8) {
+       DONT_CACHE_THIS_SESSION;
        return (smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
                               SMTP_RESP_FAKE(&fake, "5.6.7"),
                               "SMTPUTF8 is required, "
                               "but was not offered by host %s",
                               session->namaddr));
+    }
 
     /*
      * Fix 20140706: don't do silly things when the remote server announces
index bce0d43f69952a01f1baa53a3df7bacd8cb726b6..fce0e3224e432529a11e4baaa18314116b2eda3c 100644 (file)
@@ -5568,6 +5568,13 @@ static SMTPD_CMD smtpd_cmd_table[] = {
     {0,},
 };
 
+ /*
+  * In addition to counting unknown commands, the last table element also
+  * counts malformed commands (which aren't looked up in the command table).
+  */
+#define LAST_TABLE_PTR(table) ((table) + sizeof(table)/sizeof(*(table)) - 1)
+static SMTPD_CMD *smtpd_cmdp_unknown = LAST_TABLE_PTR(smtpd_cmd_table);
+
 static STRING_LIST *smtpd_noop_cmds;
 static STRING_LIST *smtpd_forbid_cmds;
 
@@ -5936,6 +5943,8 @@ static void smtpd_proto(SMTPD_STATE *state)
                state->error_mask |= MAIL_ERROR_PROTOCOL;
                smtpd_chat_reply(state, "500 5.5.2 Error: bad UTF-8 syntax");
                state->error_count++;
+               state->where = SMTPD_CMD_UNKNOWN;
+               smtpd_cmdp_unknown->total_count += 1;
                continue;
            }
            /* Move into smtpd_chat_query() and update session transcript. */
@@ -5957,6 +5966,8 @@ static void smtpd_proto(SMTPD_STATE *state)
                state->error_mask |= MAIL_ERROR_PROTOCOL;
                smtpd_chat_reply(state, "500 5.5.2 Error: bad syntax");
                state->error_count++;
+               state->where = SMTPD_CMD_UNKNOWN;
+               smtpd_cmdp_unknown->total_count += 1;
                continue;
            }
            /* Ignore smtpd_noop_cmds lookup errors. Non-critical feature. */
@@ -5965,6 +5976,7 @@ static void smtpd_proto(SMTPD_STATE *state)
                smtpd_chat_reply(state, "250 2.0.0 Ok");
                if (state->junk_cmds++ > var_smtpd_junk_cmd_limit)
                    state->error_count++;
+               /* XXX We can't count these. */
                continue;
            }
            for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++)
index 0a760c6b9963cd075dfd4abf38a87c49567a69cc..053db3c1edcc1cedda51131a6c2bf7617da3e342 100644 (file)
@@ -799,6 +799,7 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
      * the source file changed only seconds ago.
      */
     if ((dict_flags & DICT_FLAG_LOCK) != 0
+       && open_flags == O_RDONLY
        && stat(path, &st) == 0
        && st.st_mtime > dict_db->dict.mtime
        && st.st_mtime < time((time_t *) 0) - 100)
index b3bbe8510adbb5111b8cd1bc014f275fd22420e8..45fa71295535e8f64dd7f00a7262113b21ca009a 100644 (file)
@@ -472,7 +472,7 @@ DICT   *dict_dbm_open(const char *path, int open_flags, int dict_flags)
        msg_fatal("open database %s: cannot support GDBM", path);
     if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
        msg_fatal("dict_dbm_open: fstat: %m");
-    if (open_mode == O_RDONLY)
+    if (open_flags == O_RDONLY)
        dict_dbm->dict.mtime = st.st_mtime;
     dict_dbm->dict.owner.uid = st.st_uid;
     dict_dbm->dict.owner.status = (st.st_uid != 0);
@@ -482,6 +482,7 @@ DICT   *dict_dbm_open(const char *path, int open_flags, int dict_flags)
      * the source file changed only seconds ago.
      */
     if ((dict_flags & DICT_FLAG_LOCK) != 0
+       && open_flags == O_RDONLY
        && stat(path, &st) == 0
        && st.st_mtime > dict_dbm->dict.mtime
        && st.st_mtime < time((time_t *) 0) - 100)
index 6f4f276ca529648b2e3bf9b68887e036ed33545a..f672e1c055cdbc2af8d5c7c5f49172a721af3b88 100644 (file)
@@ -666,6 +666,7 @@ DICT   *dict_lmdb_open(const char *path, int open_flags, int dict_flags)
      * the source file changed only seconds ago.
      */
     if ((dict_flags & DICT_FLAG_LOCK) != 0
+       && open_flags == O_RDONLY
        && stat(path, &st) == 0
        && st.st_mtime > dict_lmdb->dict.mtime
        && st.st_mtime < time((time_t *) 0) - 100)
index fcb2556e8c09bc7534afe443e3e6d190968d2fc5..6cba5a2ec2644ad09c250460f8a4b8ab1a331527 100644 (file)
@@ -459,6 +459,7 @@ DICT   *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
      * the source file changed only seconds ago.
      */
     if ((dict_flags & DICT_FLAG_LOCK) != 0
+       && open_flags == O_RDONLY
        && stat(path, &st) == 0
        && st.st_mtime > dict_sdbm->dict.mtime
        && st.st_mtime < time((time_t *) 0) - 100)