ds2ke and nsec work.

git-svn-id: file:///svn/unbound/trunk@529 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-08-17 11:41:49 +00:00
parent 9ddbb430ef
commit cedeaa8316
13 changed files with 580 additions and 14 deletions

View File

@ -1,3 +1,8 @@
17 August 2007: Wouter
- work on DS2KE routine.
- val_nsec.c for validator NSEC proofs.
- unit test for NSEC bitmap reading.
16 August 2007: Wouter
- DS sig unit test.
- latest release libevent 1.3c and 1.3d have threading fixed.

View File

@ -42,6 +42,7 @@
#include "util/log.h"
#include "testcode/unitmain.h"
#include "validator/val_sigcrypt.h"
#include "validator/val_nsec.h"
#include "validator/validator.h"
#include "testcode/ldns-testpkts.h"
#include "util/data/msgreply.h"
@ -312,10 +313,53 @@ dstest_file(const char* fname)
ldns_buffer_free(buf);
}
/** Test NSEC type bitmap routine */
static void
nsectest()
{
/* bitmap starts at type bitmap rdata field */
/* from rfc 4034 example */
char* bitmap = "\000\006\100\001\000\000\000\003"
"\004\033\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000"
"\000\000\000\000\040";
size_t len = 37;
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 0));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_A));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 3));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 4));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 5));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 6));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 7));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 8));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 9));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 10));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 11));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 12));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 13));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 14));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_MX));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_RRSIG));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_NSEC));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, 1234));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1233));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1235));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1236));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1237));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1238));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1239));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1240));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2230));
}
void
verify_test()
{
printf("verify test\n");
verifytest_file("testdata/test_signatures.1", "20070818005004");
dstest_file("testdata/test_ds_sig.1");
nsectest();
}

View File

@ -686,6 +686,22 @@ struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
return NULL;
}
struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep,
uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass)
{
size_t i;
for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
struct ub_packed_rrset_key* s = rep->rrsets[i];
if(ntohs(s->rk.type) == type &&
ntohs(s->rk.rrset_class) == dclass &&
namelen == s->rk.dname_len &&
query_dname_compare(name, s->rk.dname) == 0) {
return s;
}
}
return NULL;
}
void
log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep)
{

View File

@ -333,6 +333,18 @@ struct ub_packed_rrset_key* reply_find_answer_rrset(struct query_info* qinfo,
struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep,
uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
/**
* Find rrset in reply, inside the authority section. Does not follow CNAMEs.
* @param rep: looks in authority section of this message.
* @param name: what to look for.
* @param namelen: length of name.
* @param type: looks for (host order).
* @param dclass: looks for (host order).
* @return: pointer to rrset, or NULL if not found.
*/
struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep,
uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass);
/**
* Debug send the query info and reply info to the log in readable form.
* @param str: descriptive string printed with packet content.

View File

@ -257,3 +257,11 @@ sec_status_to_string(enum sec_status s)
}
return "unknown_sec_status_value";
}
uint32_t
ub_packed_rrset_ttl(struct ub_packed_rrset_key* key)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)key->
entry.data;
return d->ttl;
}

View File

@ -265,6 +265,13 @@ void ub_packed_rrset_parsedelete(struct ub_packed_rrset_key* pkey,
*/
size_t packed_rrset_sizeof(struct packed_rrset_data* data);
/**
* Get TTL of rrset. RRset data must be filled in correctly.
* @param key: rrset key, with data to examine.
* @return ttl value.
*/
uint32_t ub_packed_rrset_ttl(struct ub_packed_rrset_key* key);
/**
* Calculate memory size of rrset entry. For hash table usage.
* @param key: struct ub_packed_rrset_key*.

View File

@ -230,7 +230,7 @@ key_entry_create_null(struct region* region,
struct key_entry_data* d;
if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
return NULL;
d->ttl = ttl;
d->ttl = time(0) + ttl;
d->isbad = 0;
d->rrset_type = LDNS_RR_TYPE_DNSKEY;
d->rrset_data = NULL;
@ -274,3 +274,31 @@ key_entry_create_bad(struct region* region,
d->rrset_data = NULL;
return k;
}
struct ub_packed_rrset_key*
key_entry_get_rrset(struct key_entry_key* kkey, struct region* region)
{
struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
struct ub_packed_rrset_key* rrk;
struct packed_rrset_data* rrd;
if(!d || !d->rrset_data)
return NULL;
rrk = region_alloc(region, sizeof(*rrk));
if(!rrk)
return NULL;
memset(rrk, 0, sizeof(*rrk));
rrk->rk.dname = region_alloc_init(region, kkey->name, kkey->namelen);
if(!rrk->rk.dname)
return NULL;
rrk->rk.dname_len = kkey->namelen;
rrk->rk.type = htons(d->rrset_type);
rrk->rk.rrset_class = htons(kkey->key_class);
rrk->entry.key = rrk;
rrd = region_alloc_init(region, d->rrset_data,
packed_rrset_sizeof(d->rrset_data));
if(!rrd)
return NULL;
rrk->entry.data = rrd;
packed_rrset_ptr_fixup(rrd);
return rrk;
}

View File

@ -78,7 +78,7 @@ struct key_entry_data {
uint32_t ttl;
/** the key rrdata. can be NULL to signal keyless name. */
struct packed_rrset_data* rrset_data;
/** DNS RR type of the rrset data */
/** DNS RR type of the rrset data (host order) */
uint16_t rrset_type;
/** if the key is bad: Bogus or malformed */
uint8_t isbad;
@ -143,8 +143,8 @@ int key_entry_isbad(struct key_entry_key* kkey);
* @param region: where to allocate
* @param name: the key name
* @param namelen: length of name
* @param dclass: class of key entry.
* @param ttl: what ttl should the key have.
* @param dclass: class of key entry. (host order);
* @param ttl: what ttl should the key have. relative.
* @return new key entry or NULL on alloc failure
*/
struct key_entry_key* key_entry_create_null(struct region* region,
@ -155,7 +155,7 @@ struct key_entry_key* key_entry_create_null(struct region* region,
* @param region: where to allocate.
* @param name: the key name
* @param namelen: length of name
* @param dclass: class of key entry.
* @param dclass: class of key entry. (host order);
* @param rrset: data for key entry. This is copied to the region.
* @return new key entry or NULL on alloc failure
*/
@ -168,10 +168,19 @@ struct key_entry_key* key_entry_create_rrset(struct region* region,
* @param region: where to allocate
* @param name: the key name
* @param namelen: length of name
* @param dclass: class of key entry.
* @param dclass: class of key entry. (host order);
* @return new key entry or NULL on alloc failure
*/
struct key_entry_key* key_entry_create_bad(struct region* region,
uint8_t* name, size_t namelen, uint16_t dclass);
/**
* Obtain rrset from a key entry, allocated in region.
* @param kkey: key entry to convert to a rrset.
* @param region: where to allocate rrset
* @return rrset copy; if no rrset or alloc error returns NULL.
*/
struct ub_packed_rrset_key* key_entry_get_rrset(struct key_entry_key* kkey,
struct region* region);
#endif /* VALIDATOR_VAL_KENTRY_H */

178
validator/val_nsec.c Normal file
View File

@ -0,0 +1,178 @@
/*
* validator/val_nsec.c - validator NSEC denial of existance functions.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains helper functions for the validator module.
* The functions help with NSEC checking, the different NSEC proofs
* for denial of existance, and proofs for presence of types.
*/
#include "config.h"
#include "validator/val_nsec.h"
#include "validator/val_utils.h"
#include "util/data/msgreply.h"
#include "util/data/dname.h"
/** Check type present in NSEC typemap with bitmap arg */
static int
nsec_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
{
/* bitmasks for determining type-lowerbits presence */
uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
uint8_t type_window = type>>8;
uint8_t type_low = type&0xff;
uint8_t win, winlen;
/* read each of the type bitmap windows and see if the searched
* type is amongst it */
while(len > 0) {
if(len < 3) /* bad window, at least window# winlen bitmap */
return 0;
win = *bitmap++;
winlen = *bitmap++;
len -= 2;
if(len < winlen || winlen < 1 || winlen > 32)
return 0; /* bad window length */
if(win == type_window) {
/* search window bitmap for the correct byte */
/* mybyte is 0 if we need the first byte */
size_t mybyte = type_low>>3;
if(winlen <= mybyte)
return 0; /* window too short */
return bitmap[mybyte] & masks[type_low&0x7];
} else {
/* not the window we are looking for */
bitmap += winlen;
len -= winlen;
}
}
/* end of bitmap reached, no type found */
return 0;
}
int
unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type)
{
return nsec_has_type_rdata((uint8_t*)bitmap, len, type);
}
/**
* Check if type is present in the NSEC typemap
* @param nsec: the nsec RRset.
* If there are multiple RRs, then each must have the same typemap,
* since the typemap represents the types at this domain node.
* @param type: type to check for, host order.
* @return true if present
*/
static int
nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
entry.data;
size_t len;
if(!d || d->count == 0 || d->rr_len[0] < 2+1)
return 0;
len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
if(!len)
return 0;
nsec_has_type_rdata(d->rr_data[0]+2+len, d->rr_len[0]-2-len, type);
}
/**
* For an NSEC that matches the DS queried for, check absence of DS type.
*
* @param nsec: NSEC for proof, must be trusted.
* @param qinfo: what is queried for.
* @return if secure the nsec proves that no DS is present, or
* insecure if it proves it is not a delegation point.
* or bogus if something was wrong.
*/
enum sec_status
val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
struct query_info* qinfo)
{
log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC);
/* this proof may also work if qname is a subdomain */
log_assert(query_dname_compare(nsec->rk.dname, qinfo->qname) == 0);
return sec_status_bogus;
}
enum sec_status
val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
struct query_info* qinfo, struct reply_info* rep,
struct key_entry_key* kkey, uint32_t* proof_ttl)
{
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
qinfo->qclass);
/* If we have a NSEC at the same name, it must prove one
* of two things
* --
* 1) this is a delegation point and there is no DS
* 2) this is not a delegation point */
if(nsec) {
enum sec_status sec = val_verify_rrset_entry(env, ve, nsec,
kkey);
if(sec != sec_status_secure) {
verbose(VERB_ALGO, "NSEC RRset for the "
"referral did not verify.");
return sec_status_bogus;
}
sec = val_nsec_proves_no_ds(nsec, qinfo);
if(sec == sec_status_bogus) {
/* something was wrong. */
return sec;
} else if(sec == sec_status_insecure) {
/* this wasn't a delegation point. */
return sec;
} else if(sec == sec_status_secure) {
/* this proved no DS. */
*proof_ttl = ub_packed_rrset_ttl(nsec);
return sec;
}
/* if unchecked, fall through to next proof */
}
/* Otherwise, there is no NSEC at qname. This could be an ENT.
* (ENT=empty non terminal). If not, this is broken. */
/* verify NSEC rrsets in auth section, call */
/* ValUtils.nsecProvesNodata, if so: NULL entry */
return sec_status_bogus;
}

80
validator/val_nsec.h Normal file
View File

@ -0,0 +1,80 @@
/*
* validator/val_nsec.h - validator NSEC denial of existance functions.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains helper functions for the validator module.
* The functions help with NSEC checking, the different NSEC proofs
* for denial of existance, and proofs for presence of types.
*/
#ifndef VALIDATOR_VAL_NSEC_H
#define VALIDATOR_VAL_NSEC_H
struct val_env;
struct module_env;
struct ub_packed_rrset_key;
enum sec_status;
struct reply_info;
struct query_info;
struct key_entry_key;
/**
* Check DS absence.
* There is a NODATA reply to a DS that needs checking.
* NSECs can prove this is not a delegation point, or sucessfully prove
* that there is no DS. Or this fails.
*
* @param env: module env for rrsig verification routines.
* @param ve: validator env for rrsig verification routines.
* @param qinfo: the DS queried for.
* @param rep: reply received.
* @param kkey: key entry to use for verification of signatures.
* @param proof_ttl: if secure, the TTL of how long this proof lasts.
* @return security status.
* SECURE: proved absence of DS.
* INSECURE: proved that this was not a delegation point.
* BOGUS: crypto bad, or no absence of DS proven.
* UNCHECKED: there was no way to prove anything (no nsecs, unknown algo).
*/
enum sec_status val_nsec_prove_nodata_ds(struct module_env* env,
struct val_env* ve, struct query_info* qinfo,
struct reply_info* rep, struct key_entry_key* kkey,
uint32_t* proof_ttl);
/** Unit test call to test function for nsec typemap check */
int unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type);
#endif /* VALIDATOR_VAL_NSEC_H */

View File

@ -210,6 +210,25 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
return sec;
}
enum sec_status
val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey)
{
/* temporary dnskey rrset-key */
struct ub_packed_rrset_key dnskey;
struct key_entry_data* kd = (struct key_entry_data*)kkey->entry.data;
enum sec_status sec;
dnskey.rk.type = htons(kd->rrset_type);
dnskey.rk.rrset_class = htons(kkey->key_class);
dnskey.rk.flags = 0;
dnskey.rk.dname = kkey->name;
dnskey.rk.dname_len = kkey->namelen;
dnskey.entry.key = &dnskey;
dnskey.entry.data = kd->rrset_data;
sec = val_verify_rrset(env, ve, rrset, &dnskey);
return sec;
}
/** verify that a DS RR hashes to a key and that key signs the set */
static enum sec_status
verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
@ -312,3 +331,15 @@ val_verify_new_DNSKEYs(struct region* region, struct module_env* env,
return key_entry_create_bad(region, ds_rrset->rk.dname,
ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class));
}
int
val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
{
size_t i;
for(i=0; i<rrset_get_count(ds_rrset); i++) {
if(ds_digest_algo_is_supported(ds_rrset, i) &&
ds_key_algo_is_supported(ds_rrset, i))
return 1;
}
return 0;
}

View File

@ -46,6 +46,7 @@ struct reply_info;
struct val_env;
struct module_env;
struct ub_packed_rrset_key;
struct key_entry_key;
struct region;
enum sec_status;
@ -103,6 +104,18 @@ void val_find_signer(struct query_info* qinf, struct reply_info* rep,
enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys);
/**
* Verify RRset with keys from a keyset.
* @param env: module environment (scratch buffer)
* @param ve: validator environment (verification settings)
* @param rrset: what to verify
* @param kkey: key_entry to verify with.
* @return security status of verification.
*/
enum sec_status val_verify_rrset_entry(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct key_entry_key* kkey);
/**
* Verify new DNSKEYs with DS rrset. The DS contains hash values that should
* match the DNSKEY keys.
@ -127,4 +140,14 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct region* region,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset);
/**
* Determine if DS rrset is usable for validator or not.
* Returns true if the algorithms for key and DShash are supported,
* for at least one RR.
*
* @param ds_rrset: the newly received DS rrset.
* @return true or false if not usable.
*/
int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset);
#endif /* VALIDATOR_VAL_UTILS_H */

View File

@ -45,6 +45,7 @@
#include "validator/val_kcache.h"
#include "validator/val_kentry.h"
#include "validator/val_utils.h"
#include "validator/val_nsec.h"
#include "services/cache/dns.h"
#include "util/data/dname.h"
#include "util/module.h"
@ -557,7 +558,7 @@ primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta,
log_query_info(VERB_ALGO, "failed to prime trust anchor -- "
"could not fetch DNSKEY rrset", &msg->qinfo);
kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, time(0)+NULL_KEY_TTL);
ta->namelen, ta->dclass, NULL_KEY_TTL);
if(!kkey) {
log_err("out of memory: allocate null prime key");
return NULL;
@ -599,7 +600,7 @@ primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta,
/* NOTE: in this case, we should probably reject the trust
* anchor for longer, perhaps forever. */
kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, time(0)+NULL_KEY_TTL);
ta->namelen, ta->dclass, NULL_KEY_TTL);
if(!kkey) {
log_err("out of memory: allocate null prime key");
return NULL;
@ -615,8 +616,132 @@ primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta,
return kkey;
}
/**
* In inform supers, with the resulting message and rcode and the current
* keyset in the super state, validate the DS response, returning a KeyEntry.
*
* @param qstate: query state that is validating and asked for a DS.
* @param vq: validator query state
* @param id: module id.
* @param rcode: rcode result value.
* @param msg: result message (if rcode is OK).
* @param qinfo: from the sub query state, query info.
* @param ke: the key entry to return. It returns
* bad if the DS response fails to validate, null if the
* DS response indicated an end to secure space, good if the DS
* validated. It returns null if the DS response indicated that the
* request wasn't a delegation point.
* @return 0 on servfail error (malloc failure).
*/
static int
ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct key_entry_key** ke)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
enum val_classification subtype;
if(rcode != LDNS_RCODE_NOERROR) {
/* errors here pretty much break validation */
verbose(VERB_ALGO, "DS response was error, thus bogus");
goto return_bogus;
}
subtype = val_classify_response(qinfo, msg->rep);
if(subtype == VAL_CLASS_POSITIVE) {
struct ub_packed_rrset_key* ds;
enum sec_status sec;
ds = reply_find_answer_rrset(qinfo, msg->rep);
/* If there was no DS rrset, then we have mis-classified
* this message. */
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
goto return_bogus;
}
/* Verify only returns BOGUS or SECURE. If the rrset is
* bogus, then we are done. */
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry);
if(sec != sec_status_secure) {
verbose(VERB_ALGO, "DS rrset in DS response did "
"not verify");
goto return_bogus;
}
/* If the DS rrset validates, we still have to make sure
* that they are usable. */
if(!val_dsset_isusable(ds)) {
/* If they aren't usable, then we treat it like
* there was no DS. */
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
ub_packed_rrset_ttl(ds));
return (*ke) != NULL;
}
/* Otherwise, we return the positive response. */
log_query_info(VERB_ALGO, "DS rrset was good.", qinfo);
*ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds);
return (*ke) != NULL;
} else if(subtype == VAL_CLASS_NODATA) {
/* NODATA means that the qname exists, but that there was
* no DS. This is a pretty normal case. */
uint32_t proof_ttl = 0;
/* Try to prove absence of the DS with NSEC */
enum sec_status sec = val_nsec_prove_nodata_ds(qstate->env, ve,
qinfo, msg->rep, vq->key_entry, &proof_ttl);
switch(sec) {
case sec_status_secure:
verbose(VERB_ALGO, "NSEC RRset for the "
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl);
return (*ke) != NULL;
case sec_status_insecure:
verbose(VERB_ALGO, "NSEC RRset for the "
"referral proved not a delegation point");
*ke = NULL;
return 1;
case sec_status_bogus:
verbose(VERB_ALGO, "NSEC RRset for the "
"referral did not prove no DS.");
goto return_bogus;
case sec_status_unchecked:
default:
/* NSEC proof did not work, try next */
break;
}
/* Or it could be using NSEC3. TODO */
/* Apparently, no available NSEC/NSEC3 proved NODATA, so
* this is BOGUS. */
verbose(VERB_ALGO, "DS ran out of options, so return bogus");
goto return_bogus;
} else if(subtype == VAL_CLASS_NAMEERROR) {
verbose(VERB_ALGO, "DS response was NAMEERROR, thus bogus.");
goto return_bogus;
} else {
verbose(VERB_ALGO, "Encountered an unhandled type of "
"DS response, thus bogus.");
return_bogus:
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
qinfo->qname_len, qinfo->qclass);
return (*ke) != NULL;
}
/* unreachable */
log_assert(0);
}
/**
* Process DS response. Called from inform_supers.
* Because it is in inform_supers, the mesh itself is busy doing callbacks
* for a state that is to be deleted soon; don't touch the mesh; instead
* set a state in the super, as the super will be reactivated soon.
* Perform processing to determine what state to set in the super.
*
* @param qstate: query state that is validating and asked for a DS.
* @param vq: validator query state
@ -630,9 +755,7 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo)
{
struct key_entry_key* dske = NULL;
/* TODO
if(!ds_response_to_ke(qstate, vq, id, rcode, msg, &dske)) {
@@@ */ if(0) {
if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) {
log_err("malloc failure in DStoKE");
vq->key_entry = NULL; /* make it error */
vq->state = VAL_VALIDATE_STATE;
@ -644,9 +767,7 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
/* ds response indicated that we aren't on a delegation point.
* Keep the forState.state on FINDKEY. */
} else if(key_entry_isgood(dske)) {
/* TODO
vq->ds_rrset = key_entry_getrrset(dske);
*/
vq->ds_rrset = key_entry_get_rrset(dske, qstate->region);
if(!vq->ds_rrset) {
log_err("malloc failure in process DS");
vq->key_entry = NULL; /* make it error */
@ -667,6 +788,10 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
/**
* Process DNSKEY response. Called from inform_supers.
* Sets the key entry in the state.
* Because it is in inform_supers, the mesh itself is busy doing callbacks
* for a state that is to be deleted soon; don't touch the mesh; instead
* set a state in the super, as the super will be reactivated soon.
* Perform processing to determine what state to set in the super.
*
* @param qstate: query state that is validating and asked for a DNSKEY.
* @param vq: validator query state