From: Wietse Z Venema Date: Mon, 6 Jul 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260706 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f5c30a02485cf583a6d607854b67d4a07a2886d8;p=thirdparty%2Fpostfix.git postfix-3.12-20260706 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 354af58b7..fc597d994 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -31394,10 +31394,11 @@ Apologies for any names omitted. Bug (defect introduced: Postfix 2.8, date: 20100914): read-after-free in the PSC_CALL_BACK_NOTIFY() macro. This had no effect on program execution, because myfree() wiped - memory. Problem reported by Qualys, assisted by Claude - Mythos Preview. File: postscreen_dnsbl.c. + memory, and that memory was not yet reused. Problem reported + by Qualys, assisted by Claude Mythos Preview. File: + postscreen_dnsbl.c. - Bug (defect introduced: Postfix 3.11, date: 20251025): a + Bug (defect introduced: Postfix 3.1, date: 20151129): a missing return statement in the SHOWQ_CLEANUP_AND_RETURN() macro. A local user could submit a crafted message that triggered a read-after-free and panic() in the unprivileged @@ -31468,11 +31469,12 @@ Apologies for any names omitted. by Qualys, assisted by Claude Mythos Preview. File: mail_addr_map.c. - Bug (defect introduced: Postfix 1.0, date: 20001028): double - ldap_msgfree(resloop) call when special_result_attribute - is configured, and an attacker controls LDAP server or can - play attacker-in-the-middle. Reported by Qualys, assisted - by Claude Mythos Preview. File: dict_ldap.c. + Bug (defect introduced: Postfix 2.3, date: 20060611): double + ldap_msgfree(resloop) call during error handling when + special_result_attribute is configured. An attacker who + controls the LDAP server or can play attacker-in-the-middle + could corrupt heap memory. Reported by Qualys, assisted by + Claude Mythos Preview. File: dict_ldap.c. Bug (defect introduced: before Postfix alpha, date: 19971106): 'int' over-shift, in the queue file record-length parser. @@ -31486,16 +31488,116 @@ Apologies for any names omitted. results. No existing code was affected by this. Found by Qualys, assisted by Claude Mythos Preview. File: rec_streamlf.c. -20260624: +20260624 + + Bug (defect introduced: Postfix < alpha, date: 1997): missing + recursion guard while processing :include: files that + directly include other :include: files in local(8) aliases + or .forward files. This could result in exhausting stack + space (segfault) or file handles (fatal error). This is not + a global DOS; it affected at most two parallel delivery + processes for the local recipient who created the condition. + Reported by Qualys, assisted by Claude Mythos Preview. File: + local/include.c. + + Safety: added a global nesting guard. File: local/recipient.c. + +20260625 + + Hardening: make the handling of the '-v' command-line option + consistent with postqueue(1) and postdrop(1). Suggested by + Qualys, assisted by Claude Mythos Preview. File: postlog.c. + + Bug (defect introduced: Postfix 2.7, date: 20090617): + out-of-memory condition with remote input in the postscreen + dummy SMTP engine. This dummy engine is used after PREGREET + or DNSBL checks fail, or when "after 220" protocol checks + are enabled. Reported by Qualys, assisted by Claude Mythos + Preview. File: postscreen_smtpd.c. + + Bug (defect introduced: Postfix 2.10, date: 20120617): + uninitialized memory read in postscreen HaProxy client after + remote I/O exception, causing garbage to be logged. Reported + by Qualys, assisted by Claude Mythos Preview. File: + postscreen_haproxy.c. + + Latent bug (defect introduced: Postfix 2.7, date: 20090618): + uninitialized memory read after dnsblog(8) returns a string + that is not an IPv4 address. Reported by Qualys, assisted + by Claude Mythos Preview. File: postscreen_dnsbl.c. + + Bug (defect introduced: Postfix 2.0, date: 20030619): file + system DOS: with smtpd_proxy_filter enabled, the before-filter + SMTP server did not enforce the message size limit for + mailbox From_ lines at the beginning of a message. With + smtpd_proxy_filter disabled, the file size limit was still + enforced by the cleanup daemon. Reported by Qualys, assisted + by Claude Mythos Preview. File: smtpd.c. + + Bug (defect introduced: Postfix 2.1, date: 20030619): SMTP + server panic() in smtpd_proxy_filter when handling long + mailbox From_ lines at the beginning of a message. Reported + by Qualys, assisted by Claude Mythos Preview. File: smtpd.c. + + Bug: (defect introduced: Postfix 3.10, date: 20240925): + NULL pointer read in the TLSRPT client, caused by missing + STR_OR_NULL() wrappers. Reported by Qualys, assisted by + Claude Mythos Preview. File: tlsrpt_wrapper.c. + + Bug (defect introduced: Postfix 3.11, date: 20260219): In + the non-BerkeleyDB re-indexing server, vstream_fopen_as() + ignored the uid and gid arguments and opened a database + source file read-only as the 'postfix' user instead of the + file owner. Reported by Qualys, assisted by Claude Mythos + Preview. File: open_as.c. + +20260627 + + Future proofing: use the correct device name in DEV_PATH() + macro. Reported by Qualys, assisted by Claude Mythos Preview. + File: tlsmgr.c. + + Bug (defect introduced: Postfix 2.2. date: 20040829): after + a RAND_bytes() call failure, do not rely on stack-based + pseudo-randomness for tlsmgr seed generation, and for timing + jitter of tlsmgr seed refresh intervals. Reported by Qualys, + assisted by Claude Mythos Preview. File: tlsmgr.c. + + Logging: the qmqpd server logging sometimes swapped the + client address and the client name. Reported by Qualys, + assisted by Claude Mythos Preview. File: qmqpd.c. + + Bug (defect introduced: Postfix 2.2, date: 20050117): + non-transitive comparison of IPv4 addresses. Reported by + Qualys, assisted by Claude Mythos Preview. File: sock_addr.c. + + Low (defect introduced: Postfix 3.11, date: 20260219): the + non-BerkeleyDB re-indexing server logged only the first + line of error output from a failed postmap or postalias + process. Reported by Qualys, assisted by Claude Mythos + Preview. File: nbdb_index_as.c. + + Bug (defect introduced: Postfix 2.3, date: 20060711): In + the Milter client, null-terminate the SMFIR_REPLYCODE + response data to exclude stale data when processing the + result as a C string. Reported by Qualys, assisted by Claude + Mythos Preview. File: milter8.c. + + Bug (defect introduced: Postfix 2.3, date: 20060711): + one-byte heap over-write in the Milter client with + soft_bounce=yes while processing a malformed SMFIR_REPLYCODE + Milter response. An attacker who controls the Milter or who + can play attacker-in-the-middle could corrupt heap memory. + Reported by Qualys, assisted by Claude Mythos Preview. File: + milter8.c. - Bug (defect introduced: Postfix < alpha, date: < 1998): - missing recursion guard while processing :include: files - that directly :include: other files in local(8) aliases or - .forward files. This could result in exhausting stack space - (segfault) or file handles (fatal error). Reported by Qualys, - assisted by Claude Mythos Preview. File: local/include.c. +20260628 - Safety: added a global nesting guard. File: local/recipient.c. + Bug (defect introduced: Postfix < alpha, date: 19971221): + a signal handler in the postdrop command could call unlink() + with a pathname that was already wiped and free()d, but not + yet reused. Reported by Qualys, assisted by Claude Mythos + Preview. File: postdrop.c. TODO diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 4897081bd..8dcd30780 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20260624" +#define MAIL_RELEASE_DATE "20260706" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/milter/milter8.c b/postfix/src/milter/milter8.c index 02e3ca3cd..1abd04505 100644 --- a/postfix/src/milter/milter8.c +++ b/postfix/src/milter/milter8.c @@ -491,6 +491,7 @@ static const NAME_CODE milter8_versions[] = { #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) +#define END(x) vstring_end(x) /* milter8_def_reply - set persistent response */ @@ -683,6 +684,8 @@ static int vmilter8_read_data(MILTER8 *milter, ssize_t *data_len, va_list ap) return (milter8_comm_error(milter)); } *data_len = 0; + /* Qualys+Mythos: terminate string data before stale data. */ + VSTRING_TERMINATE(buf); break; /* @@ -1309,10 +1312,11 @@ static const char *milter8_event(MILTER8 *milter, int event, } } if (var_soft_bounce) { - for (cp = STR(milter->buf); /* void */ ; cp = next) { + for (cp = STR(milter->buf); cp < END(milter->buf) ; cp = next) { if (cp[0] == '5') { cp[0] = '4'; - if (cp[4] == '5') + /* Qualys+Mythos: add missing guard. */ + if (cp + 4 < END(milter->buf) && cp[4] == '5') cp[4] = '4'; } if ((next = strstr(cp, "\r\n")) == 0) diff --git a/postfix/src/milter/milter_macros.c b/postfix/src/milter/milter_macros.c index 27f5509f2..a216f765d 100644 --- a/postfix/src/milter/milter_macros.c +++ b/postfix/src/milter/milter_macros.c @@ -242,6 +242,9 @@ MILTER_MACROS *milter_macros_alloc(int mode) /* * This macro was originally in milter.h, but no-one else needed it. + * + * Note: expr is evaluated once. This is OK only because zero and + * mystrdup("") are constants. */ #define milter_macros_init(mp, expr) do { \ MILTER_MACROS *__mp = (mp); \ diff --git a/postfix/src/nbdb_reindexd/nbdb_index_as.c b/postfix/src/nbdb_reindexd/nbdb_index_as.c index e86d8252f..0c9da0e49 100644 --- a/postfix/src/nbdb_reindexd/nbdb_index_as.c +++ b/postfix/src/nbdb_reindexd/nbdb_index_as.c @@ -160,7 +160,7 @@ int nbdb_index_as(const char *command, const char *new_type, /* Apply some basic hygiene. */ vstring_set_payload_size(why, count); VSTRING_TERMINATE(why); - (void) translit(STR(why), "\b\r\n", "__"); + (void) translit(STR(why), "\b\r\n", "___"); } } close(cmd_out[0]); /* us */ diff --git a/postfix/src/postdrop/postdrop.c b/postfix/src/postdrop/postdrop.c index f7771ea79..28c89748c 100644 --- a/postfix/src/postdrop/postdrop.c +++ b/postfix/src/postdrop/postdrop.c @@ -504,8 +504,10 @@ int main(int argc, char **argv) msg_warn("uid=%ld: remove %s: %m", (long) uid, postdrop_path); else if (msg_verbose) msg_info("remove %s", postdrop_path); - myfree(postdrop_path); + /* Qualys+Mythos: avoid read-after-free in signal handler. */ + junk = postdrop_path; postdrop_path = 0; + myfree(junk); exit(0); } if (rec_type == REC_TYPE_ERROR) diff --git a/postfix/src/postlog/postlog.c b/postfix/src/postlog/postlog.c index 4a40c7af8..38510d562 100644 --- a/postfix/src/postlog/postlog.c +++ b/postfix/src/postlog/postlog.c @@ -317,7 +317,9 @@ int main(int argc, char **argv) unsanitized_tag = optarg; break; case 'v': - msg_verbose++; + /* Qualys+Mythos: add guard as in postdrop/postqueue. */ + if (geteuid() == 0) + msg_verbose++; break; } } diff --git a/postfix/src/postscreen/postscreen_dnsbl.c b/postfix/src/postscreen/postscreen_dnsbl.c index 1f6c7052b..c89b0a6dc 100644 --- a/postfix/src/postscreen/postscreen_dnsbl.c +++ b/postfix/src/postscreen/postscreen_dnsbl.c @@ -317,7 +317,8 @@ static int psc_dnsbl_match(const char *filter, ARGV *reply) if (inet_pton(AF_INET, *cpp, addr_buf) != 1) msg_warn("address conversion error for %s -- ignoring this reply", *cpp); - if (ip_match_execute(filter, addr_buf)) + /* Qualys+Mythos: skip ip_match_execute() after inet_pton() failure. */ + else if (ip_match_execute(filter, addr_buf)) return (1); } return (0); diff --git a/postfix/src/postscreen/postscreen_haproxy.c b/postfix/src/postscreen/postscreen_haproxy.c index 45c6b9a90..59d953677 100644 --- a/postfix/src/postscreen/postscreen_haproxy.c +++ b/postfix/src/postscreen/postscreen_haproxy.c @@ -87,6 +87,11 @@ static void psc_endpt_haproxy_event(int event, void *context) int non_proxy = 0; switch (event) { + /* Qualys+Mythos: handle I/O exception as error. */ + default: + msg_warn("haproxy read: unexpected event %d", event); + status = -1; + break; case EVENT_TIME: msg_warn("haproxy read: time limit exceeded"); status = -1; diff --git a/postfix/src/postscreen/postscreen_smtpd.c b/postfix/src/postscreen/postscreen_smtpd.c index 6449a9abf..d46243682 100644 --- a/postfix/src/postscreen/postscreen_smtpd.c +++ b/postfix/src/postscreen/postscreen_smtpd.c @@ -844,9 +844,10 @@ static void psc_smtpd_read_event(int event, void *context) /* * Sanity check. We don't want to store infinitely long commands. + * + * Qualys+Mythos: make the VSTRING_LEN test unconditional. */ - if (state->read_state == PSC_SMTPD_CMD_ST_ANY - && VSTRING_LEN(state->cmd_buffer) >= var_line_limit) { + if (VSTRING_LEN(state->cmd_buffer) >= var_line_limit) { msg_info("COMMAND LENGTH LIMIT from [%s]:%s after %s", PSC_CLIENT_ADDR_PORT(state), state->where); PSC_CLEAR_EVENT_DROP_SESSION_STATE(state, psc_smtpd_time_event, diff --git a/postfix/src/qmqpd/qmqpd_peer.c b/postfix/src/qmqpd/qmqpd_peer.c index d086d06e1..4555a7f0f 100644 --- a/postfix/src/qmqpd/qmqpd_peer.c +++ b/postfix/src/qmqpd/qmqpd_peer.c @@ -259,7 +259,7 @@ void qmqpd_peer_init(QMQPD_STATE *state) for (res = res0; /* void */ ; res = res->ai_next) { if (res == 0) { msg_warn("hostname %s does not resolve to address %s", - state->addr, state->name); + state->name, state->addr); REJECT_PEER_NAME(state); break; } diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 7499cd8b6..57e893fda 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -3778,10 +3778,11 @@ static void receive_data_message(SMTPD_STATE *state, start = vstring_str(state->buffer); len = VSTRING_LEN(state->buffer); if (first) { + /* Qualys+Mythos: DOS in mbox line reading loop. */ if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) { - out_fprintf(out_stream, curr_rec_type, - "X-Mailbox-Line: %s", start); - continue; + /* Qualys+Mythos: panic in smtpd_proxy*rec_fprintf(). */ + out_record(out_stream, REC_TYPE_CONT, "X-Mailbox-Line: ", 16); + state->act_size += 16; } first = 0; if (len > 0 && IS_SPACE_TAB(start[0])) diff --git a/postfix/src/tls/tlsrpt_wrapper.c b/postfix/src/tls/tlsrpt_wrapper.c index 3feca68bf..194609f66 100644 --- a/postfix/src/tls/tlsrpt_wrapper.c +++ b/postfix/src/tls/tlsrpt_wrapper.c @@ -582,7 +582,8 @@ int trw_report_failure(TLSRPT_WRAPPER *trw, /* Give the local admin a clue. */ msg_info("TLSRPT: status=failure, domain=%s, receiving_mx=%s[%s]," " failure_type=%s%s%s", - trw->rpt_policy_domain, trw->rcv_mta_name, trw->rcv_mta_addr, + trw->rpt_policy_domain, STR_OR_NULL(trw->rcv_mta_name), + STR_OR_NULL(trw->rcv_mta_addr), trw_failure_type_to_string(failure_type), failure_reason ? ", failure_reason=" : "", failure_reason ? failure_reason : ""); diff --git a/postfix/src/tlsmgr/tlsmgr.c b/postfix/src/tlsmgr/tlsmgr.c index d32e63d4b..59fc95eec 100644 --- a/postfix/src/tlsmgr/tlsmgr.c +++ b/postfix/src/tlsmgr/tlsmgr.c @@ -283,7 +283,7 @@ static TLS_PRNG_SRC *rand_source_file; */ #define DEV_PREF "dev:" #define DEV_PREF_LEN (sizeof((DEV_PREF)) - 1) -#define DEV_PATH(dev) ((dev) + EGD_PREF_LEN) +#define DEV_PATH(dev) ((dev) + DEV_PREF_LEN) #define EGD_PREF "egd:" #define EGD_PREF_LEN (sizeof((EGD_PREF)) - 1) @@ -353,7 +353,8 @@ static void tlsmgr_prng_exch_event(int unused_event, void *dummy) * Make prediction difficult for outsiders and calculate the time for the * next execution randomly. */ - RAND_bytes(&randbyte, 1); + if (RAND_bytes(&randbyte, 1) < 0) + randbyte = getpid() & 0xff; next_period = (var_tls_prng_exch_period * randbyte) / UCHAR_MAX; event_request_timer(tlsmgr_prng_exch_event, dummy, next_period); } @@ -758,9 +759,12 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, len, TLS_MGR_REQ_SEED); } else { VSTRING_SPACE(buffer, len); - RAND_bytes((unsigned char *) STR(buffer), len); - vstring_set_payload_size(buffer, len); - status = TLS_MGR_STAT_OK; + if (RAND_bytes((unsigned char *) STR(buffer), len) < 0) { + status = TLS_MGR_STAT_ERR; + } else { + vstring_set_payload_size(buffer, len); + status = TLS_MGR_STAT_OK; + } } } attr_print(client_stream, ATTR_FLAG_NONE, diff --git a/postfix/src/util/open_as.c b/postfix/src/util/open_as.c index 744bb8161..8adecd819 100644 --- a/postfix/src/util/open_as.c +++ b/postfix/src/util/open_as.c @@ -89,7 +89,7 @@ VSTREAM *vstream_fopen_as(const char *path, int flags, mode_t mode, { int fd; - if ((fd = open(path, flags, mode)) < 0) + if ((fd = open_as(path, flags, mode, euid, egid)) < 0) return (0); return (vstream_fdopen(fd, flags)); } diff --git a/postfix/src/util/sock_addr.c b/postfix/src/util/sock_addr.c index 52a7e3e82..c9dfc1dc2 100644 --- a/postfix/src/util/sock_addr.c +++ b/postfix/src/util/sock_addr.c @@ -120,7 +120,10 @@ int sock_addr_cmp_addr(const struct sockaddr *sa, * sequence would invalidate the use of memcmp(). */ if (sa->sa_family == AF_INET) { - return (SOCK_ADDR_IN_ADDR(sa).s_addr - SOCK_ADDR_IN_ADDR(sb).s_addr); + /* Qualys+Mythos 202606: (int=unsigned-unsigned) is non-transitive. */ + return (memcmp((void *) &SOCK_ADDR_IN_ADDR(sa).s_addr, + (void *) &SOCK_ADDR_IN_ADDR(sb).s_addr, + sizeof(SOCK_ADDR_IN_ADDR(sa).s_addr))); #ifdef HAS_IPV6 } else if (sa->sa_family == AF_INET6) { return (memcmp((void *) &(SOCK_ADDR_IN6_ADDR(sa)),