mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
- Save wildcard RRset from answer with original owner for use in aggressive
NSEC. git-svn-id: file:///svn/unbound/trunk@4550 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
cd955fa34d
commit
24fc3242fc
9 changed files with 114 additions and 19 deletions
|
|
@ -1,3 +1,7 @@
|
|||
22 February 2018: Ralph
|
||||
- Save wildcard RRset from answer with original owner for use in
|
||||
aggressive NSEC.
|
||||
|
||||
21 February 2018: Wouter
|
||||
- Fix #3512: unbound incorrectly reports SERVFAIL for CAA query
|
||||
when there is a CNAME loop.
|
||||
|
|
|
|||
3
services/cache/dns.c
vendored
3
services/cache/dns.c
vendored
|
|
@ -787,10 +787,11 @@ dns_cache_lookup(struct module_env* env,
|
|||
(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
||||
LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) {
|
||||
uint8_t* wc = NULL;
|
||||
size_t wl;
|
||||
/* if the rrset is not a wildcard expansion, with wcname */
|
||||
/* because, if we return that CNAME rrset on its own, it is
|
||||
* missing the NSEC or NSEC3 proof */
|
||||
if(!(val_rrset_wildcard(rrset, &wc) && wc != NULL)) {
|
||||
if(!(val_rrset_wildcard(rrset, &wc, &wl) && wc != NULL)) {
|
||||
struct dns_msg* msg = rrset_msg(rrset, region, now, &k);
|
||||
if(msg) {
|
||||
lock_rw_unlock(&rrset->entry.lock);
|
||||
|
|
|
|||
32
services/cache/rrset.c
vendored
32
services/cache/rrset.c
vendored
|
|
@ -47,6 +47,7 @@
|
|||
#include "util/data/msgreply.h"
|
||||
#include "util/regional.h"
|
||||
#include "util/alloc.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
void
|
||||
rrset_markdel(void* key)
|
||||
|
|
@ -237,6 +238,37 @@ rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache,
|
||||
struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len,
|
||||
struct alloc_cache* alloc, time_t timenow)
|
||||
{
|
||||
struct rrset_ref ref;
|
||||
uint8_t wc_dname[LDNS_MAX_DOMAINLEN+3];
|
||||
rrset = packed_rrset_copy_alloc(rrset, alloc, timenow);
|
||||
if(!rrset) {
|
||||
log_err("malloc failure in rrset_cache_update_wildcard");
|
||||
return;
|
||||
}
|
||||
/* ce has at least one label less then qname, we can therefore safely
|
||||
* add the wildcard label. */
|
||||
wc_dname[0] = 1;
|
||||
wc_dname[1] = (uint8_t)'*';
|
||||
memmove(wc_dname+2, ce, ce_len);
|
||||
|
||||
rrset->rk.dname_len = ce_len + 2;
|
||||
rrset->rk.dname = (uint8_t*)memdup(wc_dname, rrset->rk.dname_len);
|
||||
if(!rrset->rk.dname) {
|
||||
log_err("memdup failure in rrset_cache_update_wildcard");
|
||||
return;
|
||||
}
|
||||
|
||||
rrset->entry.hash = rrset_key_hash(&rrset->rk);
|
||||
ref.key = rrset;
|
||||
ref.id = rrset->id;
|
||||
/* ignore ret: if it was in the cache, ref updated */
|
||||
(void)rrset_cache_update(rrset_cache, &ref, alloc, timenow);
|
||||
}
|
||||
|
||||
struct ub_packed_rrset_key*
|
||||
rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen,
|
||||
uint16_t qtype, uint16_t qclass, uint32_t flags, time_t timenow,
|
||||
|
|
|
|||
18
services/cache/rrset.h
vendored
18
services/cache/rrset.h
vendored
|
|
@ -133,6 +133,24 @@ void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
|
|||
int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
|
||||
struct alloc_cache* alloc, time_t timenow);
|
||||
|
||||
/**
|
||||
* Update or add an rrset in the rrset cache using a wildcard dname.
|
||||
* Generates wildcard dname by prepending the wildcard label to the closest
|
||||
* encloser. Will lookup if the rrset is in the cache and perform an update if
|
||||
* necessary.
|
||||
*
|
||||
* @param rrset_cache: the rrset cache.
|
||||
* @param rrset: which rrset to cache as wildcard. This rrset is left
|
||||
* untouched.
|
||||
* @param ce: the closest encloser, will be uses to generate the wildcard dname.
|
||||
* @param ce_len: the closest encloser lenght.
|
||||
* @param alloc: how to allocate (and deallocate) the special rrset key.
|
||||
* @param timenow: current time (to see if ttl in cache is expired).
|
||||
*/
|
||||
void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache,
|
||||
struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len,
|
||||
struct alloc_cache* alloc, time_t timenow);
|
||||
|
||||
/**
|
||||
* Lookup rrset. You obtain read/write lock. You must unlock before lookup
|
||||
* anything of else.
|
||||
|
|
|
|||
|
|
@ -847,34 +847,56 @@ void neg_insert_data(struct val_neg_cache* neg,
|
|||
wipeout(neg, zone, el, nsec);
|
||||
}
|
||||
|
||||
void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep)
|
||||
void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep,
|
||||
uint8_t* qname)
|
||||
{
|
||||
size_t i, need;
|
||||
struct ub_packed_rrset_key* soa;
|
||||
uint8_t* dname = NULL;
|
||||
size_t dname_len;
|
||||
uint16_t rrset_class;
|
||||
struct val_neg_zone* zone;
|
||||
/* see if secure nsecs inside */
|
||||
if(!reply_has_nsec(rep))
|
||||
return;
|
||||
/* find the zone name in message */
|
||||
soa = reply_find_soa(rep);
|
||||
if(!soa)
|
||||
if((soa = reply_find_soa(rep))) {
|
||||
dname = soa->rk.dname;
|
||||
dname_len = soa->rk.dname_len;
|
||||
rrset_class = soa->rk.rrset_class;
|
||||
}
|
||||
else {
|
||||
/* No SOA in positive (wildcard) answer. Use signer from the
|
||||
* validated answer RRsets' signature. */
|
||||
size_t i;
|
||||
for(i=0; i<rep->an_numrrsets; i++) {
|
||||
if(qname && query_dname_compare(qname,
|
||||
rep->rrsets[i]->rk.dname) == 0) {
|
||||
val_find_rrset_signer(rep->rrsets[i],
|
||||
&dname, &dname_len);
|
||||
rrset_class = rep->rrsets[i]->rk.rrset_class;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!dname)
|
||||
return;
|
||||
}
|
||||
|
||||
log_nametypeclass(VERB_ALGO, "negcache insert for zone",
|
||||
soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class));
|
||||
dname, LDNS_RR_TYPE_SOA, ntohs(rrset_class));
|
||||
|
||||
/* ask for enough space to store all of it */
|
||||
need = calc_data_need(rep) +
|
||||
calc_zone_need(soa->rk.dname, soa->rk.dname_len);
|
||||
calc_zone_need(dname, dname_len);
|
||||
lock_basic_lock(&neg->lock);
|
||||
neg_make_space(neg, need);
|
||||
|
||||
/* find or create the zone entry */
|
||||
zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len,
|
||||
ntohs(soa->rk.rrset_class));
|
||||
zone = neg_find_zone(neg, dname, dname_len,
|
||||
ntohs(rrset_class));
|
||||
if(!zone) {
|
||||
if(!(zone = neg_create_zone(neg, soa->rk.dname,
|
||||
soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) {
|
||||
if(!(zone = neg_create_zone(neg, dname,
|
||||
dname_len, ntohs(rrset_class)))) {
|
||||
lock_basic_unlock(&neg->lock);
|
||||
log_err("out of memory adding negative zone");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -198,9 +198,12 @@ int val_neg_zone_compare(const void* a, const void* b);
|
|||
* Insert NSECs from this message into the negative cache for reference.
|
||||
* @param neg: negative cache
|
||||
* @param rep: reply with NSECs.
|
||||
* @param qname: used to find correct signer, needed when rep does not contain
|
||||
* a SOA record.
|
||||
* Errors are ignored, means that storage is omitted.
|
||||
*/
|
||||
void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep);
|
||||
void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep,
|
||||
uint8_t* qname);
|
||||
|
||||
/**
|
||||
* Insert NSECs from this referral into the negative cache for reference.
|
||||
|
|
|
|||
|
|
@ -767,7 +767,8 @@ rrsig_get_labcount(struct packed_rrset_data* d, size_t sig)
|
|||
}
|
||||
|
||||
int
|
||||
val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc)
|
||||
val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc,
|
||||
size_t* wc_len)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
|
||||
entry.data;
|
||||
|
|
@ -800,6 +801,7 @@ val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc)
|
|||
if(labdiff > 0) {
|
||||
*wc = wn;
|
||||
dname_remove_labels(wc, &wl, labdiff);
|
||||
*wc_len = wl;
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset);
|
|||
* @param wc: the wildcard name, if the rrset was synthesized from a wildcard.
|
||||
* unchanged if not. The wildcard name, without "*." in front, is
|
||||
* returned. This is a pointer into the rrset owner name.
|
||||
* @param wc_len: the length of the returned wildcard name.
|
||||
* @return false if the signatures are inconsistent in indicating the
|
||||
* wildcard status; possible spoofing of wildcard response for other
|
||||
* responses is being tried. We lost the status which rrsig was verified
|
||||
|
|
@ -279,7 +280,8 @@ int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset);
|
|||
* of service; but in that you could also have removed the real
|
||||
* signature anyway.
|
||||
*/
|
||||
int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc);
|
||||
int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc,
|
||||
size_t* wc_len);
|
||||
|
||||
/**
|
||||
* Chase the cname to the next query name.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include "validator/val_sigcrypt.h"
|
||||
#include "validator/autotrust.h"
|
||||
#include "services/cache/dns.h"
|
||||
#include "services/cache/rrset.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/module.h"
|
||||
#include "util/log.h"
|
||||
|
|
@ -745,6 +746,8 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
|
|||
struct key_entry_key* kkey)
|
||||
{
|
||||
uint8_t* wc = NULL;
|
||||
size_t wl;
|
||||
int wc_cached = 0;
|
||||
int wc_NSEC_ok = 0;
|
||||
int nsec3s_seen = 0;
|
||||
size_t i;
|
||||
|
|
@ -757,13 +760,19 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
|
|||
/* Check to see if the rrset is the result of a wildcard
|
||||
* expansion. If so, an additional check will need to be
|
||||
* made in the authority section. */
|
||||
if(!val_rrset_wildcard(s, &wc)) {
|
||||
if(!val_rrset_wildcard(s, &wc, &wl)) {
|
||||
log_nametypeclass(VERB_QUERY, "Positive response has "
|
||||
"inconsistent wildcard sigs:", s->rk.dname,
|
||||
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
|
||||
chase_reply->security = sec_status_bogus;
|
||||
return;
|
||||
}
|
||||
if(wc && !wc_cached && env->cfg->aggressive_nsec) {
|
||||
rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl,
|
||||
env->alloc, *env->now);
|
||||
wc_cached = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* validate the AUTHORITY section as well - this will generally be
|
||||
|
|
@ -1081,6 +1090,7 @@ validate_any_response(struct module_env* env, struct val_env* ve,
|
|||
/* but check if a wildcard response is given, then check NSEC/NSEC3
|
||||
* for qname denial to see if wildcard is applicable */
|
||||
uint8_t* wc = NULL;
|
||||
size_t wl;
|
||||
int wc_NSEC_ok = 0;
|
||||
int nsec3s_seen = 0;
|
||||
size_t i;
|
||||
|
|
@ -1099,7 +1109,7 @@ validate_any_response(struct module_env* env, struct val_env* ve,
|
|||
/* Check to see if the rrset is the result of a wildcard
|
||||
* expansion. If so, an additional check will need to be
|
||||
* made in the authority section. */
|
||||
if(!val_rrset_wildcard(s, &wc)) {
|
||||
if(!val_rrset_wildcard(s, &wc, &wl)) {
|
||||
log_nametypeclass(VERB_QUERY, "Positive ANY response"
|
||||
" has inconsistent wildcard sigs:",
|
||||
s->rk.dname, ntohs(s->rk.type),
|
||||
|
|
@ -1188,6 +1198,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
|
|||
struct key_entry_key* kkey)
|
||||
{
|
||||
uint8_t* wc = NULL;
|
||||
size_t wl;
|
||||
int wc_NSEC_ok = 0;
|
||||
int nsec3s_seen = 0;
|
||||
size_t i;
|
||||
|
|
@ -1200,7 +1211,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
|
|||
/* Check to see if the rrset is the result of a wildcard
|
||||
* expansion. If so, an additional check will need to be
|
||||
* made in the authority section. */
|
||||
if(!val_rrset_wildcard(s, &wc)) {
|
||||
if(!val_rrset_wildcard(s, &wc, &wl)) {
|
||||
log_nametypeclass(VERB_QUERY, "Cname response has "
|
||||
"inconsistent wildcard sigs:", s->rk.dname,
|
||||
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
|
||||
|
|
@ -2178,7 +2189,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
|||
&qstate->qinfo);
|
||||
if(!qstate->no_cache_store) {
|
||||
val_neg_addreply(qstate->env->neg_cache,
|
||||
vq->orig_msg->rep);
|
||||
vq->orig_msg->rep, qstate->qinfo.qname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3109,7 +3120,7 @@ process_dlv_response(struct module_qstate* qstate, struct val_qstate* vq,
|
|||
return;
|
||||
}
|
||||
/* store NSECs into negative cache */
|
||||
val_neg_addreply(ve->neg_cache, msg->rep);
|
||||
val_neg_addreply(ve->neg_cache, msg->rep, NULL);
|
||||
|
||||
/* was the lookup a failure?
|
||||
* if we have to go up into the DLV for a higher DLV anchor
|
||||
|
|
|
|||
Loading…
Reference in a new issue