mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
- Fix that the aggressive negative cache does not insert NSEC
records with overreaching next owner name. Also the result is not above the trust anchor's bailiwick. Also RRSIGS are not considered valid when an NSEC next owner name is not under the signer zone name. Thanks to Qifan Zhang, Palo Alto Networks, for the report.
This commit is contained in:
+18
-3
@@ -938,6 +938,10 @@ void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep)
|
||||
continue;
|
||||
if(!dname_subdomain_c(rep->rrsets[i]->rk.dname,
|
||||
zone->name)) continue;
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC &&
|
||||
!nsec_nextowner_subdomain(rep->rrsets[i], zone->name)) {
|
||||
continue; /* nextowner not in zone */
|
||||
}
|
||||
/* insert NSEC into this zone's tree */
|
||||
neg_insert_data(neg, zone, rep->rrsets[i]);
|
||||
}
|
||||
@@ -1022,6 +1026,10 @@ void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
|
||||
continue;
|
||||
if(!dname_subdomain_c(rep->rrsets[i]->rk.dname,
|
||||
zone->name)) continue;
|
||||
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC &&
|
||||
!nsec_nextowner_subdomain(rep->rrsets[i], zone->name)) {
|
||||
continue; /* nextowner not in zone */
|
||||
}
|
||||
/* insert NSEC into this zone's tree */
|
||||
neg_insert_data(neg, zone, rep->rrsets[i]);
|
||||
}
|
||||
@@ -1110,12 +1118,14 @@ grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
|
||||
* @param rrset_cache: rrset cache
|
||||
* @param now: to check ttl against
|
||||
* @param region: where to alloc result
|
||||
* @param topname: do not look higher than this name, so that the
|
||||
* result cannot be taken from a zone above the current trust anchor.
|
||||
* @return rrset or NULL
|
||||
*/
|
||||
static struct ub_packed_rrset_key*
|
||||
neg_find_nsec(struct val_neg_cache* neg_cache, uint8_t* qname, size_t qname_len,
|
||||
uint16_t qclass, struct rrset_cache* rrset_cache, time_t now,
|
||||
struct regional* region)
|
||||
struct regional* region, uint8_t* topname)
|
||||
{
|
||||
int labs;
|
||||
uint32_t flags;
|
||||
@@ -1133,6 +1143,11 @@ neg_find_nsec(struct val_neg_cache* neg_cache, uint8_t* qname, size_t qname_len,
|
||||
lock_basic_unlock(&neg_cache->lock);
|
||||
return NULL;
|
||||
}
|
||||
if(topname && !dname_subdomain_c(zone->name, topname)) {
|
||||
/* Reject NSEC not within trust anchor's bailiwick */
|
||||
lock_basic_unlock(&neg_cache->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* NSEC only for now */
|
||||
if(zone->nsec3_hash) {
|
||||
@@ -1430,7 +1445,7 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
|
||||
|
||||
/* Get best available NSEC for qname */
|
||||
nsec = neg_find_nsec(neg, qinfo->qname, qinfo->qname_len, qinfo->qclass,
|
||||
rrset_cache, now, region);
|
||||
rrset_cache, now, region, topname);
|
||||
|
||||
/* Matching NSEC, use to generate No Data answer. Not creating answers
|
||||
* yet for No Data proven using wildcard. */
|
||||
@@ -1510,7 +1525,7 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
|
||||
* proof */
|
||||
if(!(wcrr = neg_find_nsec(neg, wc_qinfo.qname,
|
||||
wc_qinfo.qname_len, qinfo->qclass,
|
||||
rrset_cache, now, region)))
|
||||
rrset_cache, now, region, topname)))
|
||||
return NULL;
|
||||
|
||||
nodata_wc = NULL;
|
||||
|
||||
@@ -1641,6 +1641,16 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
|
||||
return sec_status_bogus; /* NSEC3 owner not b32.signer */
|
||||
}
|
||||
}
|
||||
/* NSEC, a next owner that is not under the signer is not allowed.*/
|
||||
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NSEC &&
|
||||
!nsec_nextowner_subdomain(rrset, signer)) {
|
||||
verbose(VERB_QUERY, "verify: NSEC next owner overreaches signer name");
|
||||
*reason = "NSEC next owner overreaches signer name";
|
||||
if(reason_bogus)
|
||||
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
|
||||
return sec_status_bogus; /* nextowner overreaching */
|
||||
}
|
||||
|
||||
sigblock = (unsigned char*)signer+signer_len;
|
||||
if(siglen < 2+18+signer_len+1) {
|
||||
verbose(VERB_QUERY, "verify: too short, no signature data");
|
||||
|
||||
@@ -1384,3 +1384,20 @@ int derive_cname_from_dname(struct ub_packed_rrset_key* cname,
|
||||
memmove(out+prefix_len, dname_target, dname_target_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nsec_nextowner_subdomain(struct ub_packed_rrset_key* rrset, uint8_t* name)
|
||||
{
|
||||
struct packed_rrset_data* d;
|
||||
uint8_t* next;
|
||||
size_t nextlen;
|
||||
if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_NSEC)
|
||||
return 0;
|
||||
d = (struct packed_rrset_data*)rrset->entry.data;
|
||||
if(!d || d->count == 0)
|
||||
return 0;
|
||||
next = d->rr_data[0]+2;
|
||||
nextlen = dname_valid(next, d->rr_len[0]-2);
|
||||
if(nextlen == 0)
|
||||
return 0; /* malformed */
|
||||
return dname_subdomain_c(next, name);
|
||||
}
|
||||
|
||||
@@ -452,4 +452,7 @@ int derive_cname_from_dname(struct ub_packed_rrset_key* cname,
|
||||
void rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname,
|
||||
size_t* slen);
|
||||
|
||||
/** See if the NSEC nextowner name is a subdomain of the name. */
|
||||
int nsec_nextowner_subdomain(struct ub_packed_rrset_key* rrset, uint8_t* name);
|
||||
|
||||
#endif /* VALIDATOR_VAL_UTILS_H */
|
||||
|
||||
Reference in New Issue
Block a user