On Wed, Nov 26, 2025 at 03:58:13PM +0000, Gordon Tetlow wrote: > The branch main has been updated by gordon: > > URL: > https://cgit.FreeBSD.org/src/commit/?id=2a3a6a1771148a709c2d9694c1d66c41ce8dee79 > > commit 2a3a6a1771148a709c2d9694c1d66c41ce8dee79 > Author: Gordon Tetlow <[email protected]> > AuthorDate: 2025-11-21 21:24:58 +0000 > Commit: Gordon Tetlow <[email protected]> > CommitDate: 2025-11-26 15:57:33 +0000 > > Mitigate YXDOMAIN and nodata non-referral answer poisoning. > > Add a fix to apply scrubbing of unsolicited NS RRSets (and their > respective address records) for YXDOMAIN and nodata non-referral > answers. This prevents a malicious actor from exploiting a possible > cache poison attack. > > Obtained from: NLnet Labs > Security: CVE-2025-11411 > --- > contrib/unbound/iterator/iter_scrub.c | 39 > +++++++++++++++++++++++++++++++---- > 1 file changed, 35 insertions(+), 4 deletions(-) > > diff --git a/contrib/unbound/iterator/iter_scrub.c > b/contrib/unbound/iterator/iter_scrub.c > index 553d3655f0e3..8507a3fb65ac 100644 > --- a/contrib/unbound/iterator/iter_scrub.c > +++ b/contrib/unbound/iterator/iter_scrub.c > @@ -418,12 +418,13 @@ shorten_rrset(sldns_buffer* pkt, struct rrset_parse* > rrset, int count) > * @param qinfo: original query. > * @param region: where to allocate synthesized CNAMEs. > * @param env: module env with config options. > + * @param zonename: name of server zone. > * @return 0 on error. > */ > static int > scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, > struct query_info* qinfo, struct regional* region, > - struct module_env* env) > + struct module_env* env, uint8_t* zonename) > { > uint8_t* sname = qinfo->qname; > size_t snamelen = qinfo->qname_len; > @@ -431,7 +432,8 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, > int cname_length = 0; /* number of CNAMEs, or DNAMEs */ > > if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR && > - FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN) > + FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN && > + FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_YXDOMAIN) > return 1; > > /* For the ANSWER section, remove all "irrelevant" records and add > @@ -470,6 +472,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, > &aliaslen, pkt)) { > verbose(VERB_ALGO, "synthesized CNAME " > "too long"); > + if(FLAGS_GET_RCODE(msg->flags) == > LDNS_RCODE_YXDOMAIN) { > + prev = rrset; > + rrset = rrset->rrset_all_next; > + continue; > + } > return 0; > } > cname_length++; > @@ -650,6 +657,29 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, > "RRset:", pkt, msg, prev, &rrset); > continue; > } > + /* Also delete promiscuous NS for other RCODEs */ > + if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR > + && env->cfg->iter_scrub_promiscuous) { > + remove_rrset("normalize: removing promiscuous " > + "RRset:", pkt, msg, prev, &rrset); > + continue; > + } > + /* Also delete promiscuous NS for NOERROR with nodata > + * for authoritative answers, not for delegations. > + * NOERROR with an_rrsets!=0 already handled. > + * Also NOERROR and soa_in_auth already handled. > + * NOERROR with an_rrsets==0, and not a referral. > + * referral is (NS not the zonename, noSOA). > + */ > + if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR > + && msg->an_rrsets == 0 > + && !(dname_pkt_compare(pkt, rrset->dname, > + zonename) != 0 && !soa_in_auth(msg)) > + && env->cfg->iter_scrub_promiscuous) { > + remove_rrset("normalize: removing promiscuous " > + "RRset:", pkt, msg, prev, &rrset); > + continue; > + } > if(nsset == NULL) { > nsset = rrset; > } else { > @@ -1060,7 +1090,8 @@ scrub_message(sldns_buffer* pkt, struct msg_parse* msg, > /* this is not required for basic operation but is a forgery > * resistance (security) feature */ > if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR || > - FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) && > + FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN || > + FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) && > msg->qdcount == 0) > return 0; > > @@ -1074,7 +1105,7 @@ scrub_message(sldns_buffer* pkt, struct msg_parse* msg, > } > > /* normalize the response, this cleans up the additional. */ > - if(!scrub_normalize(pkt, msg, qinfo, region, env)) > + if(!scrub_normalize(pkt, msg, qinfo, region, env, zonename)) > return 0; > /* delete all out-of-zone information */ > if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate)) >
Hey Gordon, Do you know if this fix was the incomplete one from Unbound 1.24.1? Or does this include the additional fix that landed in 1.24.2 earlier today? Thanks, -- Shawn Webb Cofounder / Security Engineer HardenedBSD Signal Username: shawn_webb.74 Tor-ified Signal: +1 303-901-1600 / shawn_webb_opsec.50 https://git.hardenedbsd.org/hardenedbsd/pubkeys/-/raw/master/Shawn_Webb/03A4CBEBB82EA5A67D9F3853FF2E67A277F8E1FA.pub.asc
signature.asc
Description: PGP signature
