]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix CVE-2026-46582, A wildcard replay, as another piece of data,
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 22 Jul 2026 08:07:52 +0000 (10:07 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 22 Jul 2026 08:07:52 +0000 (10:07 +0200)
  triggers poisoning in the serve expired reply path. Thanks to
  Qifan Zhang, Palo Alto Networks, for the report.

validator/val_utils.c
validator/validator.c

index 8e4c9190043d08fe10f50bd3ab464c949a51fc83..405cf897f4c6cf2bcb591477c48220db391325b0 100644 (file)
@@ -439,10 +439,15 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
         * only improves security status 
         * and bogus is set only once, even if we rechecked the status */
        if(sec > d->security) {
+               int wc_expanded = 0;
                d->security = sec;
-               if(sec == sec_status_secure)
+               if(sec == sec_status_secure) {
+                       uint8_t* wc = NULL;
+                       size_t wclen = 0;
                        d->trust = rrset_trust_validated;
-               else if(sec == sec_status_bogus) {
+                       if(val_rrset_wildcard(rrset, &wc, &wclen) && wc)
+                               wc_expanded = 1;
+               } else if(sec == sec_status_bogus) {
                        size_t i;
                        /* update ttl for rrset to fixed value. */
                        d->ttl = ve->bogus_ttl;
@@ -455,7 +460,11 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
                        lock_basic_unlock(&ve->bogus_lock);
                }
                /* if status updated - store in cache for reuse */
-               rrset_update_sec_status(env->rrset_cache, rrset, *env->now);
+               /* For a wildcard rrset, that is secure, do not store this
+                * into the cache, because it changes proofs around the
+                * item. */
+               if(!wc_expanded)
+                       rrset_update_sec_status(env->rrset_cache, rrset, *env->now);
        }
 
        return sec;
index e7992b6e3711f50a5e4d480ceefaf7d01f27c5df..8fc9ffc949cdfeababfc254352ec4f5783f38a72 100644 (file)
@@ -1044,6 +1044,9 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
        size_t wl;
        int wc_cached = 0;
        int wc_NSEC_ok = 0;
+       /* This is used to update the RRset cache, with the combination
+        * of the dname expansion and this wildcard, for security status. */
+       struct ub_packed_rrset_key* wc_rrset = NULL;
        int nsec3s_seen = 0;
        size_t i;
        struct ub_packed_rrset_key* s;
@@ -1062,6 +1065,9 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
                                ntohs(s->rk.type), ntohs(s->rk.rrset_class));
                        chase_reply->security = sec_status_bogus;
                        update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+                       if(wc_rrset)
+                               ((struct packed_rrset_data*)wc_rrset->
+                               entry.data)->security = sec_status_bogus;
                        return;
                }
                if(wc && !wc_cached && env->cfg->aggressive_nsec) {
@@ -1069,7 +1075,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
                                env->alloc, *env->now);
                        wc_cached = 1;
                }
-
+               if(wc) wc_rrset = s;
        }
 
        /* validate the AUTHORITY section as well - this will generally be 
@@ -1126,6 +1132,9 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
                        "did not exist");
                chase_reply->security = sec_status_bogus;
                update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+               if(wc_rrset)
+                       ((struct packed_rrset_data*)wc_rrset->
+                       entry.data)->security = sec_status_bogus;
                return;
        }
 
@@ -1527,6 +1536,16 @@ validate_any_response(struct module_env* env, struct val_env* ve,
                        "did not exist");
                chase_reply->security = sec_status_bogus;
                update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+               /* Make the expanded name and wildcard RRSIG rrsets bogus */
+               for(i=0; i<chase_reply->an_numrrsets; i++) {
+                       uint8_t* cwc = NULL;
+                       size_t cwl = 0;
+                       s = chase_reply->rrsets[i];
+                       if(val_rrset_wildcard(s, &cwc, &cwl) && cwc) {
+                               ((struct packed_rrset_data*)s->
+                               entry.data)->security = sec_status_bogus;
+                       }
+               }
                return;
        }
 
@@ -1564,6 +1583,9 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
        uint8_t* wc = NULL;
        size_t wl;
        int wc_NSEC_ok = 0;
+       /* This is used to update the RRset cache, with the combination
+        * of the dname expansion and this wildcard, for security status. */
+       struct ub_packed_rrset_key* wc_rrset = NULL;
        int nsec3s_seen = 0;
        size_t i;
        struct ub_packed_rrset_key* s;
@@ -1584,6 +1606,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
                        update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
                        return;
                }
+               if(wc) wc_rrset = s;
                
                /* Refuse wildcarded DNAMEs rfc 4597. 
                 * Do not follow a wildcarded DNAME because 
@@ -1595,6 +1618,9 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
                                ntohs(s->rk.type), ntohs(s->rk.rrset_class));
                        chase_reply->security = sec_status_bogus;
                        update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+                       if(wc_rrset)
+                               ((struct packed_rrset_data*)wc_rrset->
+                               entry.data)->security = sec_status_bogus;
                        return;
                }
 
@@ -1659,6 +1685,9 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
                        "did not exist");
                chase_reply->security = sec_status_bogus;
                update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+               if(wc_rrset)
+                       ((struct packed_rrset_data*)wc_rrset->
+                       entry.data)->security = sec_status_bogus;
                return;
        }