]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.12-20260706
authorWietse Z Venema <wietse@porcupine.org>
Mon, 6 Jul 2026 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Tue, 7 Jul 2026 02:13:00 +0000 (12:13 +1000)
16 files changed:
postfix/HISTORY
postfix/src/global/mail_version.h
postfix/src/milter/milter8.c
postfix/src/milter/milter_macros.c
postfix/src/nbdb_reindexd/nbdb_index_as.c
postfix/src/postdrop/postdrop.c
postfix/src/postlog/postlog.c
postfix/src/postscreen/postscreen_dnsbl.c
postfix/src/postscreen/postscreen_haproxy.c
postfix/src/postscreen/postscreen_smtpd.c
postfix/src/qmqpd/qmqpd_peer.c
postfix/src/smtpd/smtpd.c
postfix/src/tls/tlsrpt_wrapper.c
postfix/src/tlsmgr/tlsmgr.c
postfix/src/util/open_as.c
postfix/src/util/sock_addr.c

index 354af58b7eb0154cbd992674a8373b02251c69c1..fc597d9940ad80fa5aadd7389a9c9242aa654dae 100644 (file)
@@ -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
 
index 4897081bd0751c52b110882ed959863055f3cecd..8dcd307805b6d3cbfe0210fb6ba9356ae81a6ad8 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      "20260624"
+#define MAIL_RELEASE_DATE      "20260706"
 #define MAIL_VERSION_NUMBER    "3.12"
 
 #ifdef SNAPSHOT
index 02e3ca3cd00c7a3fcb9246a1f01025c24923ea9f..1abd04505d28f1661ac301e966b19d03534d5338 100644 (file)
@@ -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)
index 27f5509f250237a2b0f975957b42446f741a7984..a216f765d945ca6e26b66f6495515cc9af552d3a 100644 (file)
@@ -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); \
index e86d8252f71ccc8f4e2bdb91131be2db5891c581..0c9da0e4903f5d7c135b60a31e2ec39a0833ecd7 100644 (file)
@@ -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 */
index f7771ea79a23732cd2cbdf08a419f27b4111dd26..28c89748c5f7106a3b09f271c6ad23c700d707a7 100644 (file)
@@ -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)
index 4a40c7af8d1e2ccbe7329f06dc7da142f35b44c0..38510d5626932b62aec557325b406b3b85fa1843 100644 (file)
@@ -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;
        }
     }
index 1f6c7052bf2d1e3a59c7d9439c3a97b339398e67..c89b0a6dc652a26bea58c55fc540dd4b2f25a279 100644 (file)
@@ -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);
index 45c6b9a90bca163a27a9c729b43b9f609111da9a..59d9536775f7124de72d92d659b0d1ac5d57e74b 100644 (file)
@@ -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;
index 6449a9abf245d0c1acfe0d6451324af8d5ed28cd..d462436827f4d569569a2d5e3976dc79bf44a63b 100644 (file)
@@ -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,
index d086d06e1539fa9c915ebc35a043a07e84cff778..4555a7f0fe656f411b54af63d4fd3e9be03b4c75 100644 (file)
@@ -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;
                    }
index 7499cd8b69dfc4eb98965a97b1bf9ecc416ff29b..57e893fda2cdb40a8713d46c5849b878f3e45203 100644 (file)
@@ -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]))
index 3feca68bf21274ef20ff0eb9c98c185e89806ea5..194609f669130c903f6a8437a506c1aca8b785de 100644 (file)
@@ -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 : "");
index d32e63d4b9d3f42423403457e28213f18b178d17..59fc95eec9179daf7f414cf818ed89a80fc19ff9 100644 (file)
@@ -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,
index 744bb816186537fb9858863862920900c3be43b7..8adecd81960609ed7067232ed84455b87f054597 100644 (file)
@@ -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));
 }
index 52a7e3e828be2d3658b89e09960a8fd3e5875a7a..c9dfc1dc27e1e0dd2c409001248885e79b2c6941 100644 (file)
@@ -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)),