- 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:
Ralph Dolmans 2018-02-22 15:12:31 +00:00
parent cd955fa34d
commit 24fc3242fc
9 changed files with 114 additions and 19 deletions

View file

@ -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.

View file

@ -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);

View file

@ -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,

View file

@ -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.

View file

@ -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)
return;
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;

View file

@ -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.

View file

@ -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;

View file

@ -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.

View file

@ -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