2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-3.7.16

This commit is contained in:
Wietse Z Venema 2025-08-18 00:00:00 -05:00 committed by Viktor Dukhovni
parent 0d826f21a7
commit 60c874bf8b
14 changed files with 127 additions and 21 deletions

View File

@ -26912,3 +26912,53 @@ Apologies for any names omitted.
add an extra newline character before appending the new add an extra newline character before appending the new
setting, causing information to become garbled. Fix by setting, causing information to become garbled. Fix by
Michael Tokarev. File: postconf/postconf_edit.c. 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.

View File

@ -4108,7 +4108,9 @@ extern bool var_tlsp_clnt_enforce_tls;
/* Migrate an incorrect name. */ /* Migrate an incorrect name. */
#define OBS_TLSP_CLNT_LEVEL "tlsproxy_client_level" #define OBS_TLSP_CLNT_LEVEL "tlsproxy_client_level"
#define VAR_TLSP_CLNT_LEVEL "tlsproxy_client_security_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; extern char *var_tlsp_clnt_level;
#define VAR_TLSP_CLNT_PER_SITE "tlsproxy_client_per_site" #define VAR_TLSP_CLNT_PER_SITE "tlsproxy_client_per_site"
@ -4118,7 +4120,9 @@ extern char *var_tlsp_clnt_per_site;
/* Migrate an incorrect name. */ /* Migrate an incorrect name. */
#define OBS_TLSP_CLNT_POLICY "tlsproxy_client_policy" #define OBS_TLSP_CLNT_POLICY "tlsproxy_client_policy"
#define VAR_TLSP_CLNT_POLICY "tlsproxy_client_policy_maps" #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; extern char *var_tlsp_clnt_policy;
/* /*

View File

@ -20,8 +20,8 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20250422" #define MAIL_RELEASE_DATE "20250818"
#define MAIL_VERSION_NUMBER "3.7.15" #define MAIL_VERSION_NUMBER "3.7.16"
#ifdef SNAPSHOT #ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE

View File

@ -273,6 +273,7 @@ static unsigned event_server_generation;
static void (*event_server_pre_disconn) (VSTREAM *, char *, char **); static void (*event_server_pre_disconn) (VSTREAM *, char *, char **);
static void (*event_server_slow_exit) (char *, char **); static void (*event_server_slow_exit) (char *, char **);
static int event_server_watchdog = 1000; static int event_server_watchdog = 1000;
static int event_server_drain_was_called = 0;
/* event_server_exit - normal termination */ /* event_server_exit - normal termination */
@ -327,6 +328,9 @@ int event_server_drain(void)
const char *myname = "event_server_drain"; const char *myname = "event_server_drain";
int fd; int fd;
if (event_server_drain_was_called)
return (0);
switch (fork()) { switch (fork()) {
/* Try again later. */ /* Try again later. */
case -1: case -1:
@ -343,6 +347,7 @@ int event_server_drain(void)
msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd); msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
} }
var_use_limit = 1; var_use_limit = 1;
event_server_drain_was_called = 1;
return (0); return (0);
/* Let the master start a new process. */ /* Let the master start a new process. */
default: default:
@ -445,6 +450,9 @@ static void event_server_accept_local(int unused_event, void *context)
int time_left = -1; int time_left = -1;
int fd; int fd;
if (event_server_drain_was_called)
return;
/* /*
* Be prepared for accept() to fail because some other process already * Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is * 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) if (event_server_pre_accept)
event_server_pre_accept(event_server_name, event_server_argv); event_server_pre_accept(event_server_name, event_server_argv);
if (event_server_drain_was_called)
return;
fd = LOCAL_ACCEPT(listen_fd); fd = LOCAL_ACCEPT(listen_fd);
if (event_server_lock != 0 if (event_server_lock != 0
&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, && 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; int fd;
HTABLE *attr = 0; HTABLE *attr = 0;
if (event_server_drain_was_called)
return;
/* /*
* Be prepared for accept() to fail because some other process already * Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is * 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) if (event_server_pre_accept)
event_server_pre_accept(event_server_name, event_server_argv); event_server_pre_accept(event_server_name, event_server_argv);
if (event_server_drain_was_called)
return;
fd = pass_accept_attr(listen_fd, &attr); fd = pass_accept_attr(listen_fd, &attr);
if (event_server_lock != 0 if (event_server_lock != 0
&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, && 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 time_left = -1;
int fd; int fd;
if (event_server_drain_was_called)
return;
/* /*
* Be prepared for accept() to fail because some other process already * Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is * 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) if (event_server_pre_accept)
event_server_pre_accept(event_server_name, event_server_argv); event_server_pre_accept(event_server_name, event_server_argv);
if (event_server_drain_was_called)
return;
fd = inet_accept(listen_fd); fd = inet_accept(listen_fd);
if (event_server_lock != 0 if (event_server_lock != 0
&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK, && myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,

View File

@ -260,6 +260,7 @@ static VSTREAM *multi_server_lock;
static int multi_server_in_flow_delay; static int multi_server_in_flow_delay;
static unsigned multi_server_generation; static unsigned multi_server_generation;
static void (*multi_server_pre_disconn) (VSTREAM *, char *, char **); static void (*multi_server_pre_disconn) (VSTREAM *, char *, char **);
static int multi_server_drain_was_called = 0;
/* multi_server_exit - normal termination */ /* multi_server_exit - normal termination */
@ -295,6 +296,9 @@ int multi_server_drain(void)
const char *myname = "multi_server_drain"; const char *myname = "multi_server_drain";
int fd; int fd;
if (multi_server_drain_was_called)
return (0);
switch (fork()) { switch (fork()) {
/* Try again later. */ /* Try again later. */
case -1: case -1:
@ -311,6 +315,7 @@ int multi_server_drain(void)
msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd); msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
} }
var_use_limit = 1; var_use_limit = 1;
multi_server_drain_was_called = 1;
return (0); return (0);
/* Let the master start a new process. */ /* Let the master start a new process. */
default: default:
@ -429,6 +434,9 @@ static void multi_server_accept_local(int unused_event, void *context)
int time_left = -1; int time_left = -1;
int fd; int fd;
if (multi_server_drain_was_called)
return;
/* /*
* Be prepared for accept() to fail because some other process already * Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is * 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) if (multi_server_pre_accept)
multi_server_pre_accept(multi_server_name, multi_server_argv); multi_server_pre_accept(multi_server_name, multi_server_argv);
if (multi_server_drain_was_called)
return;
fd = LOCAL_ACCEPT(listen_fd); fd = LOCAL_ACCEPT(listen_fd);
if (multi_server_lock != 0 if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, && 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; int fd;
HTABLE *attr = 0; HTABLE *attr = 0;
if (multi_server_drain_was_called)
return;
/* /*
* Be prepared for accept() to fail because some other process already * Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is * 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) if (multi_server_pre_accept)
multi_server_pre_accept(multi_server_name, multi_server_argv); multi_server_pre_accept(multi_server_name, multi_server_argv);
if (multi_server_drain_was_called)
return;
fd = pass_accept_attr(listen_fd, &attr); fd = pass_accept_attr(listen_fd, &attr);
if (multi_server_lock != 0 if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, && 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 time_left = -1;
int fd; int fd;
if (multi_server_drain_was_called)
return;
/* /*
* Be prepared for accept() to fail because some other process already * Be prepared for accept() to fail because some other process already
* got the connection (the number of processes competing for clients is * 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) if (multi_server_pre_accept)
multi_server_pre_accept(multi_server_name, multi_server_argv); multi_server_pre_accept(multi_server_name, multi_server_argv);
if (multi_server_drain_was_called)
return;
fd = inet_accept(listen_fd); fd = inet_accept(listen_fd);
if (multi_server_lock != 0 if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,

View File

@ -989,7 +989,7 @@ static void pre_accept(char *unused_name, char **unused_argv)
if (new_event_time >= last_event_time + 1 if (new_event_time >= last_event_time + 1
&& (name = dict_changed_name()) != 0) { && (name = dict_changed_name()) != 0) {
msg_info("table %s has changed - finishing in the background", name); msg_info("table %s has changed - finishing in the background", name);
event_server_drain(); psc_drain(unused_name, unused_argv);
} else { } else {
last_event_time = new_event_time; last_event_time = new_event_time;
} }

View File

@ -402,7 +402,6 @@
#ifdef USE_TLS #ifdef USE_TLS
#include <tls_proxy.h> #include <tls_proxy.h>
#include <openssl/engine.h>
#endif #endif
/* /*

View File

@ -1235,6 +1235,12 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
init_buf = vstring_alloc(100); init_buf = vstring_alloc(100);
init_key = tls_proxy_client_init_serialize(attr_print_plain, init_buf, init_key = tls_proxy_client_init_serialize(attr_print_plain, init_buf,
init_props); 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_done == 0) {
if (tlsp_pre_jail_client_param_key == 0 if (tlsp_pre_jail_client_param_key == 0
|| tlsp_pre_jail_client_init_key == 0) { || tlsp_pre_jail_client_init_key == 0) {
@ -1252,8 +1258,11 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
* TLS_APPL_STATE instance; this makes a mismatch of TLS_CLIENT_PARAMS * TLS_APPL_STATE instance; this makes a mismatch of TLS_CLIENT_PARAMS
* settings problematic. * settings problematic.
*/ */
if (tlsp_pre_jail_done else if (tlsp_pre_jail_client_param_key == 0
&& !been_here_fixed(tlsp_params_mismatch_filter, param_key) || 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) { && strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) {
msg_warn("request from tlsproxy client with unexpected settings"); msg_warn("request from tlsproxy client with unexpected settings");
tlsp_log_config_diff(tlsp_pre_jail_client_param_key, param_key); tlsp_log_config_diff(tlsp_pre_jail_client_param_key, param_key);
@ -1329,9 +1338,7 @@ static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params,
SSL_MODE_ENABLE_PARTIAL_WRITE SSL_MODE_ENABLE_PARTIAL_WRITE
| SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
} }
vstring_free(init_buf); TLSP_CLIENT_INIT_RETURN(appl_state);
vstring_free(param_buf);
return (appl_state);
} }
/* tlsp_close_event - pre-handshake plaintext-client close event */ /* tlsp_close_event - pre-handshake plaintext-client close event */
@ -1465,6 +1472,7 @@ static void tlsp_get_request_event(int event, void *context)
TLSP_INIT_TIMEOUT, (void *) state); TLSP_INIT_TIMEOUT, (void *) state);
return; return;
} else { } else {
state->flags |= TLSP_FLAG_DO_HANDSHAKE;
tlsp_request_read_event(plaintext_fd, tlsp_get_fd_event, tlsp_request_read_event(plaintext_fd, tlsp_get_fd_event,
TLSP_INIT_TIMEOUT, (void *) state); TLSP_INIT_TIMEOUT, (void *) state);
return; return;

View File

@ -105,7 +105,7 @@ TLSP_STATE *tlsp_state_create(const char *service,
{ {
TLSP_STATE *state = (TLSP_STATE *) mymalloc(sizeof(*state)); TLSP_STATE *state = (TLSP_STATE *) mymalloc(sizeof(*state));
state->flags = TLSP_FLAG_DO_HANDSHAKE; state->flags = 0;
state->service = mystrdup(service); state->service = mystrdup(service);
state->plaintext_stream = plaintext_stream; state->plaintext_stream = plaintext_stream;
state->plaintext_buf = 0; state->plaintext_buf = 0;

View File

@ -147,8 +147,8 @@
/* .IP "char *context" /* .IP "char *context"
/* Application context from the caller. /* Application context from the caller.
/* .PP /* .PP
/* dict_changed_name() returns non-zero when any dictionary needs to /* dict_changed_name() returns non-zero when any dictionary is
/* be re-opened because it has changed or because it was unlinked. /* opened read-only and has changed, or because it was unlinked.
/* A non-zero result is the name of a changed dictionary. /* A non-zero result is the name of a changed dictionary.
/* /*
/* dict_load_file_xt() reads name-value entries from the named file. /* dict_load_file_xt() reads name-value entries from the named file.
@ -595,11 +595,12 @@ const char *dict_changed_name(void)
dict = ((DICT_NODE *) h->value)->dict; dict = ((DICT_NODE *) h->value)->dict;
if (dict->stat_fd < 0) /* not file-based */ if (dict->stat_fd < 0) /* not file-based */
continue; continue;
if (dict->mtime == 0) /* not bloody likely */ if (dict->mtime < 0) /* not bloody likely */
msg_warn("%s: table %s: null time stamp", myname, h->key); msg_warn("%s: table %s: negative time stamp", myname, h->key);
if (fstat(dict->stat_fd, &st) < 0) if (fstat(dict->stat_fd, &st) < 0)
msg_fatal("%s: fstat: %m", myname); msg_fatal("%s: fstat: %m", myname);
if (((dict->flags & DICT_FLAG_MULTI_WRITER) == 0 if (((dict->flags & DICT_FLAG_MULTI_WRITER) == 0
&& dict->mtime > 0
&& st.st_mtime != dict->mtime) && st.st_mtime != dict->mtime)
|| st.st_nlink == 0) || st.st_nlink == 0)
status = h->key; status = h->key;

View File

@ -789,6 +789,7 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
dict_db->dict.stat_fd = dbfd; dict_db->dict.stat_fd = dbfd;
if (fstat(dict_db->dict.stat_fd, &st) < 0) if (fstat(dict_db->dict.stat_fd, &st) < 0)
msg_fatal("dict_db_open: fstat: %m"); msg_fatal("dict_db_open: fstat: %m");
if (open_flags == O_RDONLY)
dict_db->dict.mtime = st.st_mtime; dict_db->dict.mtime = st.st_mtime;
dict_db->dict.owner.uid = st.st_uid; dict_db->dict.owner.uid = st.st_uid;
dict_db->dict.owner.status = (st.st_uid != 0); dict_db->dict.owner.status = (st.st_uid != 0);

View File

@ -472,6 +472,7 @@ DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
msg_fatal("open database %s: cannot support GDBM", path); msg_fatal("open database %s: cannot support GDBM", path);
if (fstat(dict_dbm->dict.stat_fd, &st) < 0) if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_dbm_open: fstat: %m"); msg_fatal("dict_dbm_open: fstat: %m");
if (open_mode == O_RDONLY)
dict_dbm->dict.mtime = st.st_mtime; dict_dbm->dict.mtime = st.st_mtime;
dict_dbm->dict.owner.uid = st.st_uid; dict_dbm->dict.owner.uid = st.st_uid;
dict_dbm->dict.owner.status = (st.st_uid != 0); dict_dbm->dict.owner.status = (st.st_uid != 0);

View File

@ -653,6 +653,7 @@ DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags)
msg_fatal("dict_lmdb_open: fstat: %m"); msg_fatal("dict_lmdb_open: fstat: %m");
dict_lmdb->dict.lock_fd = dict_lmdb->dict.stat_fd = db_fd; dict_lmdb->dict.lock_fd = dict_lmdb->dict.stat_fd = db_fd;
dict_lmdb->dict.lock_type = MYFLOCK_STYLE_FCNTL; dict_lmdb->dict.lock_type = MYFLOCK_STYLE_FCNTL;
if (open_flags == O_RDONLY)
dict_lmdb->dict.mtime = st.st_mtime; dict_lmdb->dict.mtime = st.st_mtime;
dict_lmdb->dict.owner.uid = st.st_uid; dict_lmdb->dict.owner.uid = st.st_uid;
dict_lmdb->dict.owner.status = (st.st_uid != 0); dict_lmdb->dict.owner.status = (st.st_uid != 0);

View File

@ -449,6 +449,7 @@ DICT *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm); dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm);
if (fstat(dict_sdbm->dict.stat_fd, &st) < 0) if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
msg_fatal("dict_sdbm_open: fstat: %m"); msg_fatal("dict_sdbm_open: fstat: %m");
if (open_flags == O_RDONLY)
dict_sdbm->dict.mtime = st.st_mtime; dict_sdbm->dict.mtime = st.st_mtime;
dict_sdbm->dict.owner.uid = st.st_uid; dict_sdbm->dict.owner.uid = st.st_uid;
dict_sdbm->dict.owner.status = (st.st_uid != 0); dict_sdbm->dict.owner.status = (st.st_uid != 0);