]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.12-20260601
authorWietse Z Venema <wietse@porcupine.org>
Mon, 1 Jun 2026 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Fri, 5 Jun 2026 10:34:17 +0000 (20:34 +1000)
postfix/HISTORY
postfix/proto/stop
postfix/proto/stop.double-history
postfix/proto/stop.spell-history
postfix/src/dns/dns_strrecord.c
postfix/src/global/mail_version.h
postfix/src/global/smtp_stream.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd_chat.h
postfix/src/tls/tls_dane.c

index 4a30c1525e3291805b3098851f9ee9e4f148d96e..1906d84fd4faf6d711286bae0852c556716245d1 100644 (file)
@@ -31172,6 +31172,76 @@ Apologies for any names omitted.
        Re-indented the source code after deleting 'stat' and adding
        missing types to .indent.pro.
 
+20260524
+
+       Bugfix (defect introduced: Postfix 3.1, date 20150607):
+       null pointer dereference and heap data overread in the
+       Postfix SMTP client's smtp_dns_reply_filter (this is disabled
+       by default), when the Postfix SMTP client is configured to
+       use opportunistic or mandatory DANE authentication (this
+       is disabled by default); and the destination domain publishes
+       a TLSA record that is empty or shorter than 20 bytes; and
+       the OS is configured to use a resolver that passes such a
+       TLSA record. For example, a zero-length TLSA record is
+       blocked by BIND, Google DNS, OpenDNS, and by configurations
+       that use systemd-resolved (the default on many LINUX systems);
+       it is passed by Cloudflare, Quad9 DNS, and unbound, if these
+       resolvers are used without systemd-resolved.
+
+       The root cause is a missing 'break' statement after the
+       code that converts a TLSA record to string, resulting in a
+       null pointer crash when the record length is zero; or a
+       data overread (or rarely, a segfault) with 0 < record length
+       < 20 bytes. The overread content is not disclosed.
+
+       The impact of crashes is easily overstated. That said,
+       crashes must be eliminated regardless of their impact.
+
+       On systems that deliver fewer than one message per minute,
+       a null pointer or other segfault crash can result in a delay
+       of up to one minute for email delivery to other destination
+       domains.
+
+       On systems with a larger traffic volume, the impact of a
+       null pointer or other segfault crash on deliveries to other
+       destination domains is minor because Postfix reuses SMTP
+       client processes and replaces a failed process within seconds
+       (self-healing); the practical impact is believed to be no
+       worse than that of an uncooperative receiver that tarpits
+       SMTP connections from Postfix to one or more destination
+       domains under their control (by replying within Postfix
+       SMTP client read time limits which are several minutes by
+       default).
+
+       Problem reported by TristanInSec, found with ASAN. Also
+       reported by other people. Reproduction and real-world impact
+       researched by Wietse. File: dns/dns_strrecord.c.
+
+20260529
+
+       Robustness: Postfix SMTP server will no longer receive (and
+       discard) an unlimited amount of text while receiving a long
+       SMTP command line. Problem introduced: Postfix 2.9, date:
+       20110205; reported by Michael Wollner. Under high load
+       conditions, the amount of text was already limited by a
+       10-second deadline to receive an SMTP command. Files:
+       global/smtp_stream.c, smtpd/smtpd.c, smtpd/smtpd_chat.h.
+
+       Robustness: with the above change the Postfix SMTP client
+       will no longer receive (and discard) an unlimited amount
+       of text while receiving a long SMTP response line.
+
+       Robustness: do not receive (and discard) unlimited amounts
+       of data with BDAT commands. Problem introduced: Postfix
+       3.4, date: 20180825; found during code maintenance. File:
+       smtpd/smtpd.c.
+
+20260531
+
+       Bugfix: (defect introduced: Postfix 3.6, date: 20200710):
+       panic() while parsing a TLSA record with length 3. Found
+       during code maintenance. File: tls/tls_dane.c.
+
 TODO
 
        Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
index e7e6efe2f69524a90774825d7284341993393bbb..937f37ec6a20af256d5828f1d74c35527c18ed01 100644 (file)
@@ -1720,3 +1720,5 @@ substrings
 Substring
 tlstrace
 yyyymmddhhmmss
+ASAN
+TristanInSec
index 2a32e3bee1af562bd1dae4705486c5b18c9161f3..2dde19d542d3ec7f8432bd8190c395383ef13f66 100644 (file)
@@ -261,3 +261,4 @@ proto  proto stop proto stop double cc
  tlsproxy tlsproxy c tlsproxy tlsproxy_client c 
  postscreen postscreen c postscreen postscreen_tls_conf c 
  anvil anvil c global anvil_clnt hc proto postconf proto 
+ global smtp_stream c smtpd smtpd c smtpd smtpd_chat h 
index f1f938112cf9fe8bf7bb818e0dd7d9b982fbad0a..816e252d84699add8d1c85b188e2f12dc34a4c1c 100644 (file)
@@ -140,3 +140,9 @@ sunos
 WebPKI
 rf
 musl
+Cloudflare
+OpenDNS
+overread
+resolvers
+tarpits
+Wollner
index 1e3b74389353f9540c22485a198d1f19610cf00c..b6dbe69bdcbba5c0f7c841d469408501e438bbf5 100644 (file)
@@ -99,6 +99,8 @@ char   *dns_strrecord(VSTRING *buf, DNS_RR *rr)
        } else {
            vstring_sprintf_append(buf, "[truncated record]");
        }
+       /* 202605 Missing break found by TristanInSec using ASAN. */
+       break;
 
        /*
         * We use the SOA record TTL to determine the negative reply TTL. We
index 047d98af7ca5842e891f352925f377e075f46b28..e619ac82eed643b00d3d15687a573f35ddff25b7 100644 (file)
@@ -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      "20260516"
+#define MAIL_RELEASE_DATE      "20260601"
 #define MAIL_VERSION_NUMBER    "3.12"
 
 #ifdef SNAPSHOT
index 662950803ac280337e2973b8e7231260872fa76f..7ac6e0181875dd5d488a93fe4ff23d75997d3d54 100644 (file)
@@ -457,8 +457,12 @@ int     smtp_get_noexcept(VSTRING *vp, VSTREAM *stream, ssize_t bound, int flags
        && vstream_feof(stream) == 0 && vstream_ferror(stream) == 0)
        while ((next_char = VSTREAM_GETC(stream)) != VSTREAM_EOF
               && next_char != '\n')
-            /* void */ ;
-
+           if (--bound <= 0) {
+               msg_warn("disabling input from %s", VSTREAM_PATH(stream));
+               vstream_fpurge(stream, VSTREAM_PURGE_READ);
+               shutdown(vstream_fileno(stream), SHUT_RD);
+               break;
+           }
     return (last_char);
 }
 
index 8e0bc714f8f2063acc3c250cf774e208e4aedbe1..cf9da72a4ba806bbefc301037137285114e8a9ce 100644 (file)
@@ -4034,6 +4034,21 @@ static int skip_bdat(SMTPD_STATE *state, off_t chunk_size,
     off_t   done;
     off_t   len;
 
+    /*
+     * Skip inputs below 1.5 times the message size limit, staying in sync
+     * with the remote SMTP client. Otherwise, force a negative chunk_size
+     * value to disable reading and discarding input here, and to force a
+     * "lost connection" condition upon a later read operation.
+     */
+    if (ENFORCING_SIZE_LIMIT(var_message_limit)
+       && state->act_size / 1.5 > var_message_limit - chunk_size / 1.5) {
+       chunk_size = -1;
+    } else if (state->act_size > OFF_T_MAX - chunk_size) {
+       state->act_size = OFF_T_MAX;
+    } else {
+       state->act_size += chunk_size;
+    }
+
     /*
      * Read and discard content from the remote SMTP client. TODO: drop the
      * connection in case of overload.
@@ -4051,6 +4066,16 @@ static int skip_bdat(SMTPD_STATE *state, off_t chunk_size,
     vsmtpd_chat_reply(state, format, ap);
     va_end(ap);
 
+    /*
+     * Force a "lost connection" condition upon the next read operation.
+     */
+    if (chunk_size < 0) {
+       msg_warn("%s: too much BDAT content -- disabling further input from %s",
+                state->queue_id ? state->queue_id : "NOQUEUE",
+                state->namaddr);
+       shutdown(vstream_fileno(state->client), SHUT_RD);
+    }
+
     /*
      * Reset state, or drop subsequent BDAT payloads until BDAT LAST or RSET.
      */
@@ -6098,7 +6123,12 @@ static void smtpd_proto(SMTPD_STATE *state)
                break;
            }
            watchdog_pat();
-           smtpd_chat_query(state);
+           if (!smtpd_chat_query(state)) {
+                state->error_mask |= MAIL_ERROR_PROTOCOL;
+                smtpd_chat_reply(state, "500 5.5.2 %s Error: command too long",
+                                 var_myhostname);
+               continue;
+           }
            if (IS_BARE_LF_REPLY_REJECT(smtp_got_bare_lf)) {
                log_whatsup(state, "reject", "bare <LF> received");
                state->error_mask |= MAIL_ERROR_PROTOCOL;
index 9fbe178ff3ae53d0f9c2e083ae533e68c22da41b..b3e1a83ae7fdbdb64abfd2860397e23d89f4dd0a 100644 (file)
@@ -26,7 +26,7 @@ extern void vsmtpd_chat_reply(SMTPD_STATE *, const char *, va_list);
 extern void smtpd_chat_notify(SMTPD_STATE *);
 
 #define smtpd_chat_query(state) \
-       ((void) smtpd_chat_query_limit((state), var_line_limit))
+       smtpd_chat_query_limit((state), var_line_limit)
 
 /* LICENSE
 /* .ad
index 5533b7b712eb138b1e95f67ba2f153406a1b224e..b5d48ba2907bc62364f56da00d35681d887623c1 100644 (file)
@@ -57,9 +57,6 @@
 /*     void    tls_dane_log(TLScontext)
 /*     TLS_SESS_STATE *TLScontext;
 /*
-/*     int     tls_dane_unusable(dane)
-/*     const TLS_DANE *dane;
-/*
 /*     int     tls_dane_notfound(dane)
 /*     const TLS_DANE *dane;
 /* DESCRIPTION
 /*     tls_dane_log() logs successful verification via DNS-based or
 /*     synthetic DANE TLSA RRs (fingerprint or "tafile").
 /*
-/*     tls_dane_unusable() checks whether a cached TLS_DANE record is
-/*     the result of a validated RRset, with no usable elements.  In
-/*     this case, TLS is mandatory, but certificate verification is
-/*     not DANE-based.
-/*
 /*     tls_dane_notfound() checks whether a cached TLS_DANE record is
 /*     the result of a validated DNS lookup returning NODATA. In
 /*     this case, TLS is not required by RFC, though users may elect
@@ -518,7 +510,7 @@ static int parse_tlsa_rr(TLS_DANE *dane, DNS_RR *rr)
                  q, a, r, rr->type);
 
     /* Drop truncated records */
-    if ((dlen = rr->data_len - 3) < 0) {
+    if ((dlen = rr->data_len - 3) <= 0) {
        msg_warn("%s%s%s: truncated TLSA RR length == %u",
                 q, a, r, (unsigned) rr->data_len);
        return (0);