Prereq: "3.7.15" diff -ur --new-file /var/tmp/postfix-3.7.15/src/global/mail_version.h ./src/global/mail_version.h --- /var/tmp/postfix-3.7.15/src/global/mail_version.h 2025-04-22 11:12:31.000000000 -0400 +++ ./src/global/mail_version.h 2025-08-18 16:52:22.000000000 -0400 @@ -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.7.15" +#define MAIL_RELEASE_DATE "20250818" +#define MAIL_VERSION_NUMBER "3.7.16" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -ur --new-file /var/tmp/postfix-3.7.15/HISTORY ./HISTORY --- /var/tmp/postfix-3.7.15/HISTORY 2025-04-22 11:00:40.000000000 -0400 +++ ./HISTORY 2025-08-18 16:52:49.000000000 -0400 @@ -26912,3 +26912,53 @@ 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 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. diff -ur --new-file /var/tmp/postfix-3.7.15/src/global/mail_params.h ./src/global/mail_params.h --- /var/tmp/postfix-3.7.15/src/global/mail_params.h 2025-02-12 17:14:23.000000000 -0500 +++ ./src/global/mail_params.h 2025-08-18 16:51:30.000000000 -0400 @@ -4108,7 +4108,9 @@ /* Migrate an incorrect name. */ #define OBS_TLSP_CLNT_LEVEL "tlsproxy_client_level" #define VAR_TLSP_CLNT_LEVEL "tlsproxy_client_security_level" -#define DEF_TLSP_CLNT_LEVEL "${" OBS_TLSP_CLNT_LEVEL ":$" VAR_SMTP_TLS_LEVEL "}" +#define DEF_TLSP_CLNT_LEVEL "${" OBS_TLSP_CLNT_LEVEL "?{$" \ + OBS_TLSP_CLNT_LEVEL "}:{$" \ + VAR_SMTP_TLS_LEVEL "}}" extern char *var_tlsp_clnt_level; #define VAR_TLSP_CLNT_PER_SITE "tlsproxy_client_per_site" @@ -4118,7 +4120,9 @@ /* Migrate an incorrect name. */ #define OBS_TLSP_CLNT_POLICY "tlsproxy_client_policy" #define VAR_TLSP_CLNT_POLICY "tlsproxy_client_policy_maps" -#define DEF_TLSP_CLNT_POLICY "${" OBS_TLSP_CLNT_POLICY ":$" VAR_SMTP_TLS_POLICY "}" +#define DEF_TLSP_CLNT_POLICY "${" OBS_TLSP_CLNT_POLICY "?{$" \ + OBS_TLSP_CLNT_POLICY "}:{$" \ + VAR_SMTP_TLS_POLICY "}}" extern char *var_tlsp_clnt_policy; /* diff -ur --new-file /var/tmp/postfix-3.7.15/src/master/event_server.c ./src/master/event_server.c --- /var/tmp/postfix-3.7.15/src/master/event_server.c 2021-12-19 09:48:14.000000000 -0500 +++ ./src/master/event_server.c 2025-08-18 16:51:30.000000000 -0400 @@ -273,6 +273,7 @@ static void (*event_server_pre_disconn) (VSTREAM *, char *, char **); static void (*event_server_slow_exit) (char *, char **); static int event_server_watchdog = 1000; +static int event_server_drain_was_called = 0; /* event_server_exit - normal termination */ @@ -327,6 +328,9 @@ const char *myname = "event_server_drain"; int fd; + if (event_server_drain_was_called) + return (0); + switch (fork()) { /* Try again later. */ case -1: @@ -343,6 +347,7 @@ msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd); } var_use_limit = 1; + event_server_drain_was_called = 1; return (0); /* Let the master start a new process. */ default: @@ -445,6 +450,9 @@ int time_left = -1; int fd; + if (event_server_drain_was_called) + return; + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -457,6 +465,8 @@ if (event_server_pre_accept) event_server_pre_accept(event_server_name, event_server_argv); + if (event_server_drain_was_called) + return; fd = LOCAL_ACCEPT(listen_fd); if (event_server_lock != 0 && myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, @@ -483,6 +493,9 @@ int fd; HTABLE *attr = 0; + if (event_server_drain_was_called) + return; + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -495,6 +508,8 @@ if (event_server_pre_accept) event_server_pre_accept(event_server_name, event_server_argv); + if (event_server_drain_was_called) + return; fd = pass_accept_attr(listen_fd, &attr); if (event_server_lock != 0 && myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, @@ -520,6 +535,9 @@ int time_left = -1; int fd; + if (event_server_drain_was_called) + return; + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -532,6 +550,8 @@ if (event_server_pre_accept) event_server_pre_accept(event_server_name, event_server_argv); + if (event_server_drain_was_called) + return; fd = inet_accept(listen_fd); if (event_server_lock != 0 && myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, diff -ur --new-file /var/tmp/postfix-3.7.15/src/master/multi_server.c ./src/master/multi_server.c --- /var/tmp/postfix-3.7.15/src/master/multi_server.c 2021-12-19 09:48:14.000000000 -0500 +++ ./src/master/multi_server.c 2025-08-18 16:51:30.000000000 -0400 @@ -260,6 +260,7 @@ static int multi_server_in_flow_delay; static unsigned multi_server_generation; static void (*multi_server_pre_disconn) (VSTREAM *, char *, char **); +static int multi_server_drain_was_called = 0; /* multi_server_exit - normal termination */ @@ -295,6 +296,9 @@ const char *myname = "multi_server_drain"; int fd; + if (multi_server_drain_was_called) + return (0); + switch (fork()) { /* Try again later. */ case -1: @@ -311,6 +315,7 @@ msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd); } var_use_limit = 1; + multi_server_drain_was_called = 1; return (0); /* Let the master start a new process. */ default: @@ -429,6 +434,9 @@ int time_left = -1; int fd; + if (multi_server_drain_was_called) + return; + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -441,6 +449,8 @@ if (multi_server_pre_accept) multi_server_pre_accept(multi_server_name, multi_server_argv); + if (multi_server_drain_was_called) + return; fd = LOCAL_ACCEPT(listen_fd); if (multi_server_lock != 0 && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, @@ -467,6 +477,9 @@ int fd; HTABLE *attr = 0; + if (multi_server_drain_was_called) + return; + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -479,6 +492,8 @@ if (multi_server_pre_accept) multi_server_pre_accept(multi_server_name, multi_server_argv); + if (multi_server_drain_was_called) + return; fd = pass_accept_attr(listen_fd, &attr); if (multi_server_lock != 0 && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, @@ -504,6 +519,9 @@ int time_left = -1; int fd; + if (multi_server_drain_was_called) + return; + /* * Be prepared for accept() to fail because some other process already * got the connection (the number of processes competing for clients is @@ -516,6 +534,8 @@ if (multi_server_pre_accept) multi_server_pre_accept(multi_server_name, multi_server_argv); + if (multi_server_drain_was_called) + return; fd = inet_accept(listen_fd); if (multi_server_lock != 0 && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, diff -ur --new-file /var/tmp/postfix-3.7.15/src/postscreen/postscreen.c ./src/postscreen/postscreen.c --- /var/tmp/postfix-3.7.15/src/postscreen/postscreen.c 2023-01-21 16:17:28.000000000 -0500 +++ ./src/postscreen/postscreen.c 2025-08-18 16:51:30.000000000 -0400 @@ -989,7 +989,7 @@ if (new_event_time >= last_event_time + 1 && (name = dict_changed_name()) != 0) { msg_info("table %s has changed - finishing in the background", name); - event_server_drain(); + psc_drain(unused_name, unused_argv); } else { last_event_time = new_event_time; } diff -ur --new-file /var/tmp/postfix-3.7.15/src/posttls-finger/posttls-finger.c ./src/posttls-finger/posttls-finger.c --- /var/tmp/postfix-3.7.15/src/posttls-finger/posttls-finger.c 2024-02-27 12:28:33.000000000 -0500 +++ ./src/posttls-finger/posttls-finger.c 2025-08-18 16:51:30.000000000 -0400 @@ -402,7 +402,6 @@ #ifdef USE_TLS #include -#include #endif /* diff -ur --new-file /var/tmp/postfix-3.7.15/src/tlsproxy/tlsproxy.c ./src/tlsproxy/tlsproxy.c --- /var/tmp/postfix-3.7.15/src/tlsproxy/tlsproxy.c 2023-06-04 17:46:40.000000000 -0400 +++ ./src/tlsproxy/tlsproxy.c 2025-08-18 16:51:30.000000000 -0400 @@ -1235,6 +1235,12 @@ init_buf = vstring_alloc(100); init_key = tls_proxy_client_init_serialize(attr_print_plain, init_buf, init_props); +#define TLSP_CLIENT_INIT_RETURN(retval) do { \ + vstring_free(init_buf); \ + vstring_free(param_buf); \ + return (retval); \ + } while (0) + if (tlsp_pre_jail_done == 0) { if (tlsp_pre_jail_client_param_key == 0 || tlsp_pre_jail_client_init_key == 0) { @@ -1252,9 +1258,12 @@ * TLS_APPL_STATE instance; this makes a mismatch of TLS_CLIENT_PARAMS * settings problematic. */ - if (tlsp_pre_jail_done - && !been_here_fixed(tlsp_params_mismatch_filter, param_key) - && strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) { + else if (tlsp_pre_jail_client_param_key == 0 + || tlsp_pre_jail_client_init_key == 0) { + msg_warn("TLS client role is disabled by configuration"); + TLSP_CLIENT_INIT_RETURN(0); + } else if (!been_here_fixed(tlsp_params_mismatch_filter, param_key) + && strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) { msg_warn("request from tlsproxy client with unexpected settings"); tlsp_log_config_diff(tlsp_pre_jail_client_param_key, param_key); log_hints = 1; @@ -1329,9 +1338,7 @@ SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); } - vstring_free(init_buf); - vstring_free(param_buf); - return (appl_state); + TLSP_CLIENT_INIT_RETURN(appl_state); } /* tlsp_close_event - pre-handshake plaintext-client close event */ @@ -1465,6 +1472,7 @@ TLSP_INIT_TIMEOUT, (void *) state); return; } else { + state->flags |= TLSP_FLAG_DO_HANDSHAKE; tlsp_request_read_event(plaintext_fd, tlsp_get_fd_event, TLSP_INIT_TIMEOUT, (void *) state); return; diff -ur --new-file /var/tmp/postfix-3.7.15/src/tlsproxy/tlsproxy_state.c ./src/tlsproxy/tlsproxy_state.c --- /var/tmp/postfix-3.7.15/src/tlsproxy/tlsproxy_state.c 2019-02-08 17:12:13.000000000 -0500 +++ ./src/tlsproxy/tlsproxy_state.c 2025-08-18 16:51:30.000000000 -0400 @@ -105,7 +105,7 @@ { TLSP_STATE *state = (TLSP_STATE *) mymalloc(sizeof(*state)); - state->flags = TLSP_FLAG_DO_HANDSHAKE; + state->flags = 0; state->service = mystrdup(service); state->plaintext_stream = plaintext_stream; state->plaintext_buf = 0; diff -ur --new-file /var/tmp/postfix-3.7.15/src/util/dict.c ./src/util/dict.c --- /var/tmp/postfix-3.7.15/src/util/dict.c 2018-11-05 19:25:30.000000000 -0500 +++ ./src/util/dict.c 2025-08-18 16:51:30.000000000 -0400 @@ -147,8 +147,8 @@ /* .IP "char *context" /* Application context from the caller. /* .PP -/* dict_changed_name() returns non-zero when any dictionary needs to -/* be re-opened because it has changed or because it was unlinked. +/* dict_changed_name() returns non-zero when any dictionary is +/* opened read-only and has changed, or because it was unlinked. /* A non-zero result is the name of a changed dictionary. /* /* dict_load_file_xt() reads name-value entries from the named file. @@ -595,11 +595,12 @@ dict = ((DICT_NODE *) h->value)->dict; if (dict->stat_fd < 0) /* not file-based */ continue; - if (dict->mtime == 0) /* not bloody likely */ - msg_warn("%s: table %s: null time stamp", myname, h->key); + if (dict->mtime < 0) /* not bloody likely */ + msg_warn("%s: table %s: negative time stamp", myname, h->key); if (fstat(dict->stat_fd, &st) < 0) msg_fatal("%s: fstat: %m", myname); if (((dict->flags & DICT_FLAG_MULTI_WRITER) == 0 + && dict->mtime > 0 && st.st_mtime != dict->mtime) || st.st_nlink == 0) status = h->key; diff -ur --new-file /var/tmp/postfix-3.7.15/src/util/dict_db.c ./src/util/dict_db.c --- /var/tmp/postfix-3.7.15/src/util/dict_db.c 2022-02-02 12:43:27.000000000 -0500 +++ ./src/util/dict_db.c 2025-08-18 16:51:30.000000000 -0400 @@ -789,7 +789,8 @@ dict_db->dict.stat_fd = dbfd; if (fstat(dict_db->dict.stat_fd, &st) < 0) msg_fatal("dict_db_open: fstat: %m"); - dict_db->dict.mtime = st.st_mtime; + if (open_flags == O_RDONLY) + dict_db->dict.mtime = st.st_mtime; dict_db->dict.owner.uid = st.st_uid; dict_db->dict.owner.status = (st.st_uid != 0); diff -ur --new-file /var/tmp/postfix-3.7.15/src/util/dict_dbm.c ./src/util/dict_dbm.c --- /var/tmp/postfix-3.7.15/src/util/dict_dbm.c 2014-11-17 13:28:01.000000000 -0500 +++ ./src/util/dict_dbm.c 2025-08-18 16:51:30.000000000 -0400 @@ -472,7 +472,8 @@ msg_fatal("open database %s: cannot support GDBM", path); if (fstat(dict_dbm->dict.stat_fd, &st) < 0) msg_fatal("dict_dbm_open: fstat: %m"); - dict_dbm->dict.mtime = st.st_mtime; + if (open_mode == O_RDONLY) + dict_dbm->dict.mtime = st.st_mtime; dict_dbm->dict.owner.uid = st.st_uid; dict_dbm->dict.owner.status = (st.st_uid != 0); diff -ur --new-file /var/tmp/postfix-3.7.15/src/util/dict_lmdb.c ./src/util/dict_lmdb.c --- /var/tmp/postfix-3.7.15/src/util/dict_lmdb.c 2021-06-04 16:42:49.000000000 -0400 +++ ./src/util/dict_lmdb.c 2025-08-18 16:51:30.000000000 -0400 @@ -653,7 +653,8 @@ msg_fatal("dict_lmdb_open: fstat: %m"); dict_lmdb->dict.lock_fd = dict_lmdb->dict.stat_fd = db_fd; dict_lmdb->dict.lock_type = MYFLOCK_STYLE_FCNTL; - dict_lmdb->dict.mtime = st.st_mtime; + if (open_flags == O_RDONLY) + dict_lmdb->dict.mtime = st.st_mtime; dict_lmdb->dict.owner.uid = st.st_uid; dict_lmdb->dict.owner.status = (st.st_uid != 0); diff -ur --new-file /var/tmp/postfix-3.7.15/src/util/dict_sdbm.c ./src/util/dict_sdbm.c --- /var/tmp/postfix-3.7.15/src/util/dict_sdbm.c 2012-01-24 19:41:08.000000000 -0500 +++ ./src/util/dict_sdbm.c 2025-08-18 16:51:30.000000000 -0400 @@ -449,7 +449,8 @@ dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm); if (fstat(dict_sdbm->dict.stat_fd, &st) < 0) msg_fatal("dict_sdbm_open: fstat: %m"); - dict_sdbm->dict.mtime = st.st_mtime; + if (open_flags == O_RDONLY) + dict_sdbm->dict.mtime = st.st_mtime; dict_sdbm->dict.owner.uid = st.st_uid; dict_sdbm->dict.owner.status = (st.st_uid != 0);