From: Wietse Z Venema Date: Fri, 10 Jul 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260710 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5913dd9515065edf1939c8bee8639ed288210334;p=thirdparty%2Fpostfix.git postfix-3.12-20260710 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index c22b0b2f7..0445d7a1a 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -31653,6 +31653,41 @@ Apologies for any names omitted. was used in test code. Reported by Qualys, assisted by Claude Mythos Preview. Files: dict.h, dict.c. +20260710 + + Code hygiene: the unsafe macro VSTRING_AT_OFFSET() was + called with an argument containing the unsafe macro + VSTRING_LEN(). This under-estimated the amount of available + buffer space (no harm done). Reported by Qualys (not assisted + by AI). Files: vstream.c, vstring.h. + + Code hygiene: do not reply with stale data after an error + receiving a tlsmgr(8) request. Reported by Qualys, assisted + by Claude Mythos Preview. File: tlsmgr.c. + + Code hygiene: in the SMTP client protocol engine, evaluate + the RETURN() macro argument before freeing resources. + Reported by Qualys, assisted by Claude Mythos Preview. File: + smtp_proto.c. + + Code hygiene: myrealloc(ptr, 0) still resulted in a panic. + Reported by Qualys, assisted by Claude Mythos Preview. + Files: mymalloc.c, mymalloc_test.c. + + Code hygiene: worst-case memory allocation for the + smtp_sasl_auth_cache reader. Suggested by Qualys, assisted + by Claude Mythos Preview. File: smtp_sasl_auth_cache.c. + + Bug (defect introduced: Postfix 2.4, date: 20051222): null + pointer read crash while parsing a malformed Dovecot AUTH + server response. Reported by Qualys, assisted by Claude + Mythos Preview. File: xsasl_dovecot_server.c. + + Code hygiene: in the qmqpd daemon, explicitly null-terminate + message content to prevent strncmp() from reading up to 5 + bytes of unused VSTRING buffer space. Suggested by Qualys, + assisted by Claude Mythos Preview. File: qmqpd.c. + TODO Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc. diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 8dcd30780..b516b12cb 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 "20260706" +#define MAIL_RELEASE_DATE "20260710" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/qmqpd/qmqpd.c b/postfix/src/qmqpd/qmqpd.c index 2214d9855..815d1e4eb 100644 --- a/postfix/src/qmqpd/qmqpd.c +++ b/postfix/src/qmqpd/qmqpd.c @@ -493,7 +493,10 @@ static void qmqpd_write_content(QMQPD_STATE *state) * * XXX Deal with UNIX-style From_ lines at the start of message content just * in case. + * + * 202607 Qualys+Mythos: strncmp() requires null-terminated input. */ + VSTRING_TERMINATE(state->message); for (next = STR(state->message); /* void */ ; /* void */ ) { if ((ch = qmqpd_next_line(state->message, &start, &len, &next)) < 0) break; diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 4499087b6..6ad8f6497 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -1730,7 +1730,10 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, /* Caution: changes to RETURN() also affect code outside the main loop. */ + /* 202607 Qualys+Mythos: evaluate argument before freeing resources. */ + #define RETURN(x) do { \ + int _rv = (x); \ if (recv_state != SMTP_STATE_LAST) \ DONT_CACHE_THIS_SESSION; \ vstring_free(next_command); \ @@ -1738,7 +1741,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state, myfree((void *) survivors); \ if (session->mime_state) \ session->mime_state = mime_state_free(session->mime_state); \ - return (x); \ + return (_rv); \ } while (0) #define SENDER_IS_AHEAD \ diff --git a/postfix/src/smtp/smtp_sasl_auth_cache.c b/postfix/src/smtp/smtp_sasl_auth_cache.c index f3104ca81..6203bbdcb 100644 --- a/postfix/src/smtp/smtp_sasl_auth_cache.c +++ b/postfix/src/smtp/smtp_sasl_auth_cache.c @@ -199,7 +199,8 @@ static int smtp_sasl_auth_cache_valid_value(SMTP_SASL_AUTH_CACHE *auth_cache, const char *entry, const char *password) { - ssize_t len = strlen(entry); + /* 202607 Qualys+Mythos: reserve space for whole entry incl. terminator. */ + ssize_t len = strlen(entry + 1); char *cache_hash = mymalloc(len); char *curr_hash; unsigned long now = (unsigned long) time((time_t *) 0); diff --git a/postfix/src/tlsmgr/tlsmgr.c b/postfix/src/tlsmgr/tlsmgr.c index 59fc95eec..e38c4dffa 100644 --- a/postfix/src/tlsmgr/tlsmgr.c +++ b/postfix/src/tlsmgr/tlsmgr.c @@ -639,6 +639,7 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, * Load session from cache. */ if (STREQ(STR(request), TLS_MGR_REQ_LOOKUP)) { + VSTRING_RESET(buffer); if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), RECV_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id), @@ -649,13 +650,12 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, if (ent->cache_label == 0) { msg_warn("bogus cache type \"%s\" in \"%s\" request", STR(cache_type), TLS_MGR_REQ_LOOKUP); - VSTRING_RESET(buffer); } else if (ent->cache_info == 0) { /* * Cache type valid, but not enabled */ - VSTRING_RESET(buffer); + /* void */ ; } else { status = tls_scache_lookup(ent->cache_info, STR(cache_id), buffer) ? @@ -725,16 +725,15 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, * RFC 5077 TLS session ticket keys */ else if (STREQ(STR(request), TLS_MGR_REQ_TKTKEY)) { + VSTRING_RESET(buffer); if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_DATA(TLS_MGR_ATTR_KEYNAME, buffer), ATTR_TYPE_END) == 1) { if (LEN(buffer) != 0 && LEN(buffer) != TLS_TICKET_NAMELEN) { msg_warn("invalid session ticket key name length: %ld", (long) LEN(buffer)); - VSTRING_RESET(buffer); } else if (*smtpd_cache.cache_timeout <= 0) { status = TLS_MGR_STAT_ERR; - VSTRING_RESET(buffer); } else { status = tlsmgr_key(buffer, *smtpd_cache.cache_timeout); } @@ -750,10 +749,10 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, * Entropy request. */ else if (STREQ(STR(request), TLS_MGR_REQ_SEED)) { + VSTRING_RESET(buffer); if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_INT(TLS_MGR_ATTR_SIZE, &len), ATTR_TYPE_END) == 1) { - VSTRING_RESET(buffer); if (len <= 0 || len > 255) { msg_warn("bogus seed length \"%d\" in \"%s\" request", len, TLS_MGR_REQ_SEED); diff --git a/postfix/src/util/mymalloc.c b/postfix/src/util/mymalloc.c index dbf0e496d..d70affd83 100644 --- a/postfix/src/util/mymalloc.c +++ b/postfix/src/util/mymalloc.c @@ -197,6 +197,10 @@ void *myrealloc(void *ptr, ssize_t len) #ifndef NO_SHARED_EMPTY_STRINGS if (ptr == empty_string) return (mymalloc(len)); + if (len == 0) { + myfree(ptr); + return (mymalloc(0)); + } #endif /* @@ -204,7 +208,7 @@ void *myrealloc(void *ptr, ssize_t len) * allows us to catch integer overflow problems that weren't already * caught up-stream. */ - if (len < 1) + if (len < 0) msg_panic("myrealloc: requested length %ld", (long) len); #ifdef MYMALLOC_FUZZ len += MYMALLOC_FUZZ; diff --git a/postfix/src/util/mymalloc_test.c b/postfix/src/util/mymalloc_test.c index 9e52067d3..3de6866f1 100644 --- a/postfix/src/util/mymalloc_test.c +++ b/postfix/src/util/mymalloc_test.c @@ -130,15 +130,35 @@ static void test_myrealloc_normal(PTEST_CTX *t, const PTEST_CASE *tp) myfree(ptr); } +static void test_myrealloc_zero(PTEST_CTX *t, const PTEST_CASE *tp) +{ + void *ptr; + void *got; + void *want; + + ptr = mymalloc(100); + got = myrealloc(ptr, 0); + want = mymalloc(0); + if (got != want) + ptest_error(t, "myrealloc: zero length result: got %p, want %p", + got, want); + + /* + * myfree() is a NOOP. + */ + myfree(want); + myfree(got); +} + static void test_myrealloc_panic_too_small(PTEST_CTX *t, const PTEST_CASE *tp) { void *ptr; - expect_ptest_log_event(t, "panic: myrealloc: requested length 0"); + expect_ptest_log_event(t, "panic: myrealloc: requested length -1"); ptr = mymalloc(100); ptest_defer(t, myfree, ptr); - (void) myrealloc(ptr, 0); - ptest_fatal(t, "myrealloc(_, 0) returned"); + (void) myrealloc(ptr, -1); + ptest_fatal(t, "myrealloc(_, -1) returned"); } static void test_myrealloc_fatal_out_of_mem(PTEST_CTX *t, const PTEST_CASE *tp) @@ -343,6 +363,8 @@ static const PTEST_CASE ptestcases[] = { }, {"myrealloc + myfree normal case", test_myrealloc_normal, }, + {"myrealloc static result for zero length", test_myrealloc_zero, + }, {"myrealloc panic for too small request", test_myrealloc_panic_too_small, }, {"myrealloc fatal for out of memory", test_myrealloc_fatal_out_of_mem, diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 136db9de0..d380b7514 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -541,8 +541,6 @@ /* Utility library. */ -#define VSTRING_INTERNAL - #include "mymalloc.h" #include "msg.h" #include "vbuf_print.h" @@ -1588,7 +1586,7 @@ ssize_t vstream_fread_buf(VSTREAM *fp, VSTRING *vp, ssize_t len) VSTRING_SPACE(vp, len); ret = vstream_fread(fp, vstring_str(vp), len); if (ret > 0) - VSTRING_AT_OFFSET(vp, ret); + vstring_set_payload_size(vp, ret); return (ret); } @@ -1601,7 +1599,7 @@ ssize_t vstream_fread_app(VSTREAM *fp, VSTRING *vp, ssize_t len) VSTRING_SPACE(vp, len); ret = vstream_fread(fp, vstring_end(vp), len); if (ret > 0) - VSTRING_AT_OFFSET(vp, VSTRING_LEN(vp) + ret); + vstring_set_payload_size(vp, VSTRING_LEN(vp) + ret); return (ret); } diff --git a/postfix/src/util/vstring.h b/postfix/src/util/vstring.h index c478d02af..80045b24c 100644 --- a/postfix/src/util/vstring.h +++ b/postfix/src/util/vstring.h @@ -91,8 +91,10 @@ CHECK_VAL_HELPER_DCL(VSTRING_CTL, ssize_t); */ #ifdef VSTRING_INTERNAL #define VSTRING_AT_OFFSET(vp, offset) do { \ - (vp)->vbuf.ptr = (vp)->vbuf.data + (offset); \ - (vp)->vbuf.cnt = (vp)->vbuf.len - (offset); \ + VSTRING *__vp__ = (vp); \ + ssize_t __offset__ = (offset); \ + (__vp__)->vbuf.ptr = (__vp__)->vbuf.data + (__offset__); \ + (__vp__)->vbuf.cnt = (__vp__)->vbuf.len - (__offset__); \ } while (0) #endif diff --git a/postfix/src/xsasl/xsasl_dovecot_server.c b/postfix/src/xsasl/xsasl_dovecot_server.c index e9c16466c..b93cd1bf9 100644 --- a/postfix/src/xsasl/xsasl_dovecot_server.c +++ b/postfix/src/xsasl/xsasl_dovecot_server.c @@ -313,7 +313,7 @@ static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp) cmd = line; line = split_at(line, '\t'); - if (strcmp(cmd, "VERSION") == 0) { + if (strcmp(cmd, "VERSION") == 0 && line != NULL) { if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) { msg_warn("SASL: Protocol version error"); break;