]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.12-20260624 master
authorWietse Z Venema <wietse@porcupine.org>
Wed, 24 Jun 2026 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <ietf-dane@dukhovni.org>
Fri, 26 Jun 2026 18:17:41 +0000 (04:17 +1000)
12 files changed:
postfix/HISTORY
postfix/src/bounce/bounce_templates.c
postfix/src/dns/dns_lookup.c
postfix/src/global/dict_ldap.c
postfix/src/global/mail_addr_map.c
postfix/src/global/mail_version.h
postfix/src/global/rec_streamlf.c
postfix/src/global/record.c
postfix/src/global/scache_single.c
postfix/src/local/include.c
postfix/src/local/recipient.c
postfix/src/util/mymalloc_test.c

index 7e7684653c425226bccec779f4c86680d9646431..354af58b7eb0154cbd992674a8373b02251c69c1 100644 (file)
@@ -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.
index 358530b947955ac62b342c4d8c04d399e30607cb..7392314f8823c5e748093dbc650730c9d3c40277 100644 (file)
@@ -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);
index b18b7b2b4dc62cc589cb0249e95c35c684592470..8c52027144d5eb74cf4bb9424b7a51f8e013bfcc 100644 (file)
@@ -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);
index 9236639a1deff9e295e0656fa53d25d37a55018d..1e65a5179a806d37de2213bcd91f096d0d5630f5 100644 (file)
@@ -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;
index d9f2c01d7d69099beac793523f78228f6dec8f2d..0222250bb19ecc5837950df0efc19a86bf2a9ba0 100644 (file)
@@ -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);
     }
 
     /*
index cc6d52cfb7c2fb77d1cae5114c0f5aac929783f0..4897081bd0751c52b110882ed959863055f3cecd 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      "20260623"
+#define MAIL_RELEASE_DATE      "20260624"
 #define MAIL_VERSION_NUMBER    "3.12"
 
 #ifdef SNAPSHOT
index 76bf95ab01a3ade574f4984e3997cbe7d66e126d..08caecdd8219015b25a20c07d86e05d3a46ce5d8 100644 (file)
@@ -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);
index 80cb1ac3b3c7e3cde7c56ceb720bd09e11832d21..c8b72302b3bf731c201a524e239b243e38d4330e 100644 (file)
@@ -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);
index 2a3864b9bd52fdfc79747340f608045b2faf9c79..8e2bff3b9594510da921594c6cad814cd1b93812 100644 (file)
@@ -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)
index a213d3c0888b5d5ca96b3e0b5c8c01163ee9a2ac..da5b6a8d08d0151906233f854ea77f32e47a4d32 100644 (file)
@@ -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
      * 
index e3f4d1ceb93e462f8465180471d4700c2e6ed585..a1900d6d03bfed030b7f522fadf39497f224ee74 100644 (file)
@@ -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.
      */
index f4651a51e99ef894e4d815326e8d5d7c220de4cb..9e52067d37fcf1261bb76643088f2803b922b763 100644 (file)
@@ -21,7 +21,7 @@
 #include <ptest.h>
 
  /*
-  * Scafffolding.
+  * Scaffolding.
   */
 static bool memset_was_called;