unbound: patch CVE-2026-46582

Details:
https://nvd.nist.gov/vuln/detail/cve-2026-46582

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Ankur Tyagi
2026-09-15 10:13:56 +05:30
committed by Anuj Mittal
parent 9af75c4481
commit e7aac6e2ec
2 changed files with 152 additions and 0 deletions
@@ -0,0 +1,151 @@
From 97245ce2852162fbf19cc23b016ee73fe2aec63c Mon Sep 17 00:00:00 2001
From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
Date: Wed, 22 Jul 2026 10:07:52 +0200
Subject: [PATCH] - Fix CVE-2026-46582, A wildcard replay, as another piece of
data, triggers poisoning in the serve expired reply path. Thanks to Qifan
Zhang, Palo Alto Networks, for the report.
(cherry picked from commit fea0ff550bb6193417c9b17ffff409eb6736f90d)
CVE: CVE-2026-46582
Upstream-Status: Backport [https://github.com/NLnetLabs/unbound/commit/fea0ff550bb6193417c9b17ffff409eb6736f90d]
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
validator/val_utils.c | 15 ++++++++++++---
validator/validator.c | 31 ++++++++++++++++++++++++++++++-
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/validator/val_utils.c b/validator/val_utils.c
index 4495695ac..87c5a034a 100644
--- a/validator/val_utils.c
+++ b/validator/val_utils.c
@@ -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;
diff --git a/validator/validator.c b/validator/validator.c
index 5817fc808..68c4bf643 100644
--- a/validator/validator.c
+++ b/validator/validator.c
@@ -1013,6 +1013,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;
@@ -1031,6 +1034,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) {
@@ -1038,7 +1044,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
@@ -1095,6 +1101,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;
}
@@ -1496,6 +1505,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;
}
@@ -1533,6 +1552,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;
@@ -1553,6 +1575,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
@@ -1564,6 +1587,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;
}
@@ -1628,6 +1654,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;
}
@@ -23,6 +23,7 @@ SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;branch=master;t
file://CVE-2026-42960.patch \
file://CVE-2026-44390.patch \
file://CVE-2026-44608.patch \
file://CVE-2026-46582.patch \
"
SRCREV = "f6269baa605d31859f28770e01a24e3677e5f82c"