From: Wietse Z Venema Date: Wed, 24 Jun 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260624 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6d91fdbb63fe7c54baeeefa97bce4d13e705a3e0;p=thirdparty%2Fpostfix.git postfix-3.12-20260624 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 7e7684653..354af58b7 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -31446,6 +31446,57 @@ Apologies for any names omitted. that allow this, therefore it should not trigger a panic in Postfix. Files: mymalloc.c, mymalloc_test.c. +20260623 + + Bug (defect introduced: before Postfix alpha, date 19970424): + the DNS client could read up to two bytes past the end of + an MX record, before discovering that the record was too + short. This behavior was later copied with SRV records, + potentially over-reading up to six bytes. Problem reported + by Qualys, assisted by Claude Mythos Preview. File: + dns_lookup.c. + + Non-bug: tighten a bound on the size of a TXT record fragment. + Suggested by Qualys, assisted by Claude Mythos Preview. File: + dns_lookup.c. + + Non-bug: timer leak in a short-lived test program. Reported + by Qualys, assisted by Claude Mythos Preview. File: + scache_single.c. + + Future proofing: destroy objects after logging. Suggested + 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: before Postfix alpha, date: 19971106): + 'int' over-shift, in the queue file record-length parser. + Postfix programs do not generate such records, but an + attacker could cause postdrop to reject input or panic(). + Reported by Qualys, assisted by Claude Mythos Preview. File: + record.c. + + Contract violation (defect introduced: Postfix 1.1, date: + 20020518): rec_streamlf_get() did not null-terminate all + results. No existing code was affected by this. Found by + Qualys, assisted by Claude Mythos Preview. File: rec_streamlf.c. + +20260624: + + 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. + + Safety: added a global nesting guard. File: local/recipient.c. + TODO Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc. diff --git a/postfix/src/bounce/bounce_templates.c b/postfix/src/bounce/bounce_templates.c index 358530b94..7392314f8 100644 --- a/postfix/src/bounce/bounce_templates.c +++ b/postfix/src/bounce/bounce_templates.c @@ -306,6 +306,7 @@ void bounce_templates_load(VSTREAM *fp, BOUNCE_TEMPLATES *ts) vstring_strcat(multi_line_buf, STR(line_buf)); } if (vstream_feof(fp)) + /* 202606 Qualys+Mythos: log the saved end marker. */ msg_warn("%s, line %d: missing \"%s\" end marker", VSTREAM_PATH(fp), lineno, STR(saved_end_marker)); member_name = STR(saved_member_name); diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index b18b7b2b4..8c5202714 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -761,6 +761,8 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, int frag_len; int ch; + /* At this point, (pos + fixed.length <= reply->end) */ + #define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b)) *list = 0; @@ -783,6 +785,8 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, data_len = strlen(temp) + 1; break; case T_SRV: + if (fixed->length < 3 * NS_INT16SZ) + return (DNS_RETRY); GETSHORT(pref, pos); GETSHORT(weight, pos); GETSHORT(port, pos); @@ -795,6 +799,8 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, data_len = strlen(temp) + 1; break; case T_MX: + if (fixed->length < NS_INT16SZ) + return (DNS_RETRY); GETSHORT(pref, pos); if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0) return (DNS_RETRY); @@ -838,7 +844,8 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, src < pos + fixed->length; /* */ ) { frag_len = *src++; /* 202604 Claude: move debug logging after the frag_len check. */ - if (frag_len > reply->end - src + /* 202606 Qualys+Mythos: tighter bound for fragment size. */ + if (frag_len > pos + fixed->length - src || frag_len >= ((unsigned char *) ltemp + sizeof(ltemp)) - dst) { msg_warn("extract_answer: bad TXT string length: %d", frag_len); return (DNS_RETRY); diff --git a/postfix/src/global/dict_ldap.c b/postfix/src/global/dict_ldap.c index 9236639a1..1e65a5179 100644 --- a/postfix/src/global/dict_ldap.c +++ b/postfix/src/global/dict_ldap.c @@ -1183,8 +1183,10 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage *res, break; } - if (resloop != 0) + if (resloop != 0) { ldap_msgfree(resloop); + resloop = 0; + } if (dict_ldap->dict.error != 0) break; diff --git a/postfix/src/global/mail_addr_map.c b/postfix/src/global/mail_addr_map.c index d9f2c01d7..0222250bb 100644 --- a/postfix/src/global/mail_addr_map.c +++ b/postfix/src/global/mail_addr_map.c @@ -166,10 +166,6 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate, */ argv = mail_addr_crunch_opt(string, propagate ? extension : 0, MA_FORM_EXTERNAL, out_form); - if (buffer) - vstring_free(buffer); - if (ext_address) - vstring_free(ext_address); if (msg_verbose) for (i = 0; i < argv->argc; i++) msg_info("%s: %s -> %d: %s", myname, address, i, argv->argv[i]); @@ -179,6 +175,12 @@ ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate, argv = argv_free(argv); path->error = DICT_ERR_RETRY; } + + /* 202606 Qualys+Mythos: future proofing: destroy objects last. */ + if (buffer) + vstring_free(buffer); + if (ext_address) + vstring_free(ext_address); } /* diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index cc6d52cfb..4897081bd 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 "20260623" +#define MAIL_RELEASE_DATE "20260624" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/global/rec_streamlf.c b/postfix/src/global/rec_streamlf.c index 76bf95ab0..08caecdd8 100644 --- a/postfix/src/global/rec_streamlf.c +++ b/postfix/src/global/rec_streamlf.c @@ -85,8 +85,10 @@ int rec_streamlf_get(VSTREAM *stream, VSTRING *buf, int maxlen) */ VSTRING_RESET(buf); while (n-- > 0) { - if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) + if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) { + VSTRING_TERMINATE(buf); return (VSTRING_LEN(buf) > 0 ? REC_TYPE_CONT : REC_TYPE_EOF); + } if (ch == '\n') { VSTRING_TERMINATE(buf); return (REC_TYPE_NORM); diff --git a/postfix/src/global/record.c b/postfix/src/global/record.c index 80cb1ac3b..c8b72302b 100644 --- a/postfix/src/global/record.c +++ b/postfix/src/global/record.c @@ -238,7 +238,7 @@ int rec_get_raw(VSTREAM *stream, VSTRING *buf, ssize_t maxsize, int flags) const char *myname = "rec_get"; int type; ssize_t len; - int len_byte; + ssize_t len_byte; unsigned shift; /* @@ -261,7 +261,7 @@ int rec_get_raw(VSTREAM *stream, VSTRING *buf, ssize_t maxsize, int flags) * limit. */ for (len = 0, shift = 0; /* void */ ; shift += 7) { - if (shift >= (int) (NBBY * sizeof(int))) { + if (shift +7 >= (int) (NBBY * sizeof(int))) { msg_warn("%s: too many length bits, record type %d", VSTREAM_PATH(stream), type); return (REC_TYPE_ERROR); diff --git a/postfix/src/global/scache_single.c b/postfix/src/global/scache_single.c index 2a3864b9b..8e2bff3b9 100644 --- a/postfix/src/global/scache_single.c +++ b/postfix/src/global/scache_single.c @@ -275,6 +275,11 @@ static void scache_single_free(SCACHE *scache) { SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache; + if (SCACHE_SINGLE_ENDP_BUSY(sp)) + scache_single_free_endp(sp); + if (SCACHE_SINGLE_DEST_BUSY(sp)) + scache_single_free_dest(sp); + vstring_free(sp->endp.endp_label); vstring_free(sp->endp.endp_prop); if (sp->endp.fd >= 0) diff --git a/postfix/src/local/include.c b/postfix/src/local/include.c index a213d3c08..da5b6a8d0 100644 --- a/postfix/src/local/include.c +++ b/postfix/src/local/include.c @@ -93,6 +93,15 @@ int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path) if (msg_verbose) MSG_LOG_STATE(myname, state); + /* 202606 Qualys+Mythos: add missing nesting limit. */ + if (state.level > 100) { + msg_warn(":include: nesting limit exceeded for %s", path); + dsb_simple(state.msg_attr.why, "5.4.6", + ":include: nesting limit exceeded"); + return (bounce_append(BOUNCE_FLAGS(state.request), + BOUNCE_ATTR(state.msg_attr))); + } + /* * DUPLICATE ELIMINATION * diff --git a/postfix/src/local/recipient.c b/postfix/src/local/recipient.c index e3f4d1ceb..a1900d6d0 100644 --- a/postfix/src/local/recipient.c +++ b/postfix/src/local/recipient.c @@ -216,6 +216,20 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) if (msg_verbose) MSG_LOG_STATE(myname, state); + /* + * Global recursion safety check for loops that are not already broken + * locally, such as :include: files that directly :include: another + * file). + */ + if (state.level > 100) { + msg_warn("recipient nesting limit exceeded for %s", + state.msg_attr.rcpt.address); + dsb_simple(state.msg_attr.why, "5.4.6", + "recipient nesting limit exceeded"); + return (bounce_append(BOUNCE_FLAGS(state.request), + BOUNCE_ATTR(state.msg_attr))); + } + /* * Duplicate filter. */ diff --git a/postfix/src/util/mymalloc_test.c b/postfix/src/util/mymalloc_test.c index f4651a51e..9e52067d3 100644 --- a/postfix/src/util/mymalloc_test.c +++ b/postfix/src/util/mymalloc_test.c @@ -21,7 +21,7 @@ #include /* - * Scafffolding. + * Scaffolding. */ static bool memset_was_called;