+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.
#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"
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();
}
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)
{
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.
}
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;
+}
*/
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*.
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;
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;
+}
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;
* @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,
* @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
*/
* @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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
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,
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;
+}
struct val_env;
struct module_env;
struct ub_packed_rrset_key;
+struct key_entry_key;
struct region;
enum sec_status;
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.
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 */
#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"
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;
/* 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;
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
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;
/* 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 */
/**
* 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