]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.9.5 v3.9.5
authorWietse Z Venema <wietse@porcupine.org>
Mon, 18 Aug 2025 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Tue, 19 Aug 2025 14:37:53 +0000 (00:37 +1000)
14 files changed:
postfix/HISTORY
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/master/event_server.c
postfix/src/master/multi_server.c
postfix/src/postscreen/postscreen.c
postfix/src/posttls-finger/posttls-finger.c
postfix/src/tlsproxy/tlsproxy.c
postfix/src/tlsproxy/tlsproxy_state.c
postfix/src/util/dict.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 20bcfe8e8ff0c37eacf2a34f8e896a828f7dc17f..762955fb2887e2c91e8b752ffc1974df46120bd9 100644 (file)
@@ -28132,3 +28132,53 @@ Apologies for any names omitted.
        add an extra newline character before appending the new
        setting, causing information to become garbled. Fix by
        Michael Tokarev. File: postconf/postconf_edit.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.
+
+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.
index e91f4c57df8a5105073d7efb3ea141ebbfafa920..7a3ce797fafd5a57cf60dc626d64c846f3cae1a1 100644 (file)
@@ -4167,7 +4167,9 @@ extern bool var_tlsp_clnt_enforce_tls;
 /* 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"
@@ -4177,7 +4179,9 @@ extern char *var_tlsp_clnt_per_site;
 /* 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;
 
  /*
index 6d605ff229ff85ccc0872d9b83d5416182269f46..5e4cd92c6059ce4f4b872fb0494e81614017e996 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      "20250422"
-#define MAIL_VERSION_NUMBER    "3.9.4"
+#define MAIL_RELEASE_DATE      "20250818"
+#define MAIL_VERSION_NUMBER    "3.9.5"
 
 #ifdef SNAPSHOT
 #define MAIL_VERSION_DATE      "-" MAIL_RELEASE_DATE
index 9802bdf2f02d368b5112b70a515f854d8ea82b0d..ff280db571c598edb9ad56a39a726e934730a4b6 100644 (file)
@@ -273,6 +273,7 @@ static unsigned event_server_generation;
 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 @@ int     event_server_drain(void)
     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 @@ int     event_server_drain(void)
                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 @@ static void event_server_accept_local(int unused_event, void *context)
     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 @@ static void event_server_accept_local(int unused_event, void *context)
 
     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 @@ static void event_server_accept_pass(int unused_event, void *context)
     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 @@ static void event_server_accept_pass(int unused_event, void *context)
 
     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 @@ static void event_server_accept_inet(int unused_event, void *context)
     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 @@ static void event_server_accept_inet(int unused_event, void *context)
 
     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,
index 6150f229aeb537442d50fba8ea6970aa41b4b580..4e744d4d116bd348b7b571fbdb92e5c7c2060571 100644 (file)
@@ -260,6 +260,7 @@ static VSTREAM *multi_server_lock;
 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 @@ int     multi_server_drain(void)
     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 @@ int     multi_server_drain(void)
                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 @@ static void multi_server_accept_local(int unused_event, void *context)
     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 @@ static void multi_server_accept_local(int unused_event, void *context)
 
     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 @@ static void multi_server_accept_pass(int unused_event, void *context)
     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 @@ static void multi_server_accept_pass(int unused_event, void *context)
 
     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 @@ static void multi_server_accept_inet(int unused_event, void *context)
     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 @@ static void multi_server_accept_inet(int unused_event, void *context)
 
     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,
index ebb680c06805588f38c248eb9c8aa0840d178613..2744dd15f2f0619502415befb92ad7acac117009 100644 (file)
@@ -996,7 +996,7 @@ static void pre_accept(char *unused_name, char **unused_argv)
     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;
     }
index b474a40066e1521fcd805cf369b73533b5e39f6a..6c7c378b691b2a4a3d51afec5dc363d7d3f49598 100644 (file)
 
 #ifdef USE_TLS
 #include <tls_proxy.h>
-#include <openssl/engine.h>
 #endif
 
  /*
index 0ebf52c68ec81444690f8813a58504b551fb5835..d33dcf04fd2d813a304db1596608f91866bdbc95 100644 (file)
@@ -1252,6 +1252,12 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
     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) {
@@ -1269,9 +1275,12 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
      * 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;
@@ -1346,9 +1355,7 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
                         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 */
@@ -1482,6 +1489,7 @@ static void tlsp_get_request_event(int event, void *context)
                                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;
index df6cbda1de0dd4b4c367cc55df665f87813c8db9..4a08d17fedd2016be9d5d59f3760c44709611e9a 100644 (file)
@@ -105,7 +105,7 @@ TLSP_STATE *tlsp_state_create(const char *service,
 {
     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;
index 35c02db050195afa4bc8aa53d4bbef36aa998a67..3a553f8ceb9735c04c2201c7dc175ba96c5b4e98 100644 (file)
 /* .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 @@ const char *dict_changed_name(void)
        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;
index b2d0c336f3bc441a88d86a9efa3404363b6d094b..0a760c6b9963cd075dfd4abf38a87c49567a69cc 100644 (file)
@@ -789,7 +789,8 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
     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);
 
index e47b7eedff978df23c35ba622343df59d084c2c6..b3bbe8510adbb5111b8cd1bc014f275fd22420e8 100644 (file)
@@ -472,7 +472,8 @@ 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");
-    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);
 
index bed20e0c450c1571b4fd33a6d389ffb753774fc8..6f4f276ca529648b2e3bf9b68887e036ed33545a 100644 (file)
@@ -653,7 +653,8 @@ DICT   *dict_lmdb_open(const char *path, int open_flags, int dict_flags)
        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);
 
index 23371dc7c72efd46df8d0da70d6eafa981af7e9b..fcb2556e8c09bc7534afe443e3e6d190968d2fc5 100644 (file)
@@ -449,7 +449,8 @@ DICT   *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
     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);