mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 14:47:09 +00:00
ds2ke and nsec work.
git-svn-id: file:///svn/unbound/trunk@529 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
9ddbb430ef
commit
cedeaa8316
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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*.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
178
validator/val_nsec.c
Normal 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
80
validator/val_nsec.h
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user