From cedeaa831676533b35abb33da322f9e773cc1e47 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 17 Aug 2007 11:41:49 +0000 Subject: [PATCH] ds2ke and nsec work. git-svn-id: file:///svn/unbound/trunk@529 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 5 ++ testcode/unitverify.c | 44 ++++++++++ util/data/msgreply.c | 16 ++++ util/data/msgreply.h | 12 +++ util/data/packed_rrset.c | 8 ++ util/data/packed_rrset.h | 7 ++ validator/val_kentry.c | 30 ++++++- validator/val_kentry.h | 19 +++-- validator/val_nsec.c | 178 +++++++++++++++++++++++++++++++++++++++ validator/val_nsec.h | 80 ++++++++++++++++++ validator/val_utils.c | 31 +++++++ validator/val_utils.h | 23 +++++ validator/validator.c | 141 +++++++++++++++++++++++++++++-- 13 files changed, 580 insertions(+), 14 deletions(-) create mode 100644 validator/val_nsec.c create mode 100644 validator/val_nsec.h diff --git a/doc/Changelog b/doc/Changelog index 3701a4174..97a37ffaf 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/testcode/unitverify.c b/testcode/unitverify.c index 51cca401d..f3bd2eec4 100644 --- a/testcode/unitverify.c +++ b/testcode/unitverify.c @@ -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(); } diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 8acdbb6e3..ef72d5fd9 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -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; ian_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) { diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 1f45e561a..cacf806bc 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -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. diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c index a9d1abfdc..a010fe895 100644 --- a/util/data/packed_rrset.c +++ b/util/data/packed_rrset.c @@ -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; +} diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h index 482be3a46..bff8bec53 100644 --- a/util/data/packed_rrset.h +++ b/util/data/packed_rrset.h @@ -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*. diff --git a/validator/val_kentry.c b/validator/val_kentry.c index e491fc5fb..ca999b6f3 100644 --- a/validator/val_kentry.c +++ b/validator/val_kentry.c @@ -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; +} diff --git a/validator/val_kentry.h b/validator/val_kentry.h index d18d20e42..06e1bf224 100644 --- a/validator/val_kentry.h +++ b/validator/val_kentry.h @@ -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 */ diff --git a/validator/val_nsec.c b/validator/val_nsec.c new file mode 100644 index 000000000..6bf997f46 --- /dev/null +++ b/validator/val_nsec.c @@ -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; +} diff --git a/validator/val_nsec.h b/validator/val_nsec.h new file mode 100644 index 000000000..7e9a14473 --- /dev/null +++ b/validator/val_nsec.h @@ -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 */ diff --git a/validator/val_utils.c b/validator/val_utils.c index 45a70f073..795d1b857 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -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; iqinfo); 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 -- 2.47.2