]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
ds2ke and nsec work.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 17 Aug 2007 11:41:49 +0000 (11:41 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 17 Aug 2007 11:41:49 +0000 (11:41 +0000)
git-svn-id: file:///svn/unbound/trunk@529 be551aaa-1e26-0410-a405-d3ace91eadb9

13 files changed:
doc/Changelog
testcode/unitverify.c
util/data/msgreply.c
util/data/msgreply.h
util/data/packed_rrset.c
util/data/packed_rrset.h
validator/val_kentry.c
validator/val_kentry.h
validator/val_nsec.c [new file with mode: 0644]
validator/val_nsec.h [new file with mode: 0644]
validator/val_utils.c
validator/val_utils.h
validator/validator.c

index 3701a4174c894196fc7a3b8851c64c7609a70612..97a37ffafd637eb99cdc1cc528c26964c6f07e1c 100644 (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.
index 51cca401dff04739ade7d274dea9cd734ac97ac8..f3bd2eec400d6a786dda839ea252ba4ff180f782 100644 (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();
 }
index 8acdbb6e30442c8a22e49e67d84fd25c43f21e20..ef72d5fd9e15ad3c63310924a2a625eac605242f 100644 (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)
 {
index 1f45e561a046aea1c56302fd75480c5cf7d994d4..cacf806bc42fa0a601c49caa8c585477a10f2033 100644 (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.
index a9d1abfdcced37800d78a825031f88c1b247a499..a010fe8955f7923b4ee01504ba3b636036068e9f 100644 (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;
+}
index 482be3a466614b5b8a6203b3d8d654d58e187c36..bff8bec537a93741127b326859a449928d4fdf71 100644 (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*.
index e491fc5fb3180fbd5cc923771a516c81618980f5..ca999b6f397b34a67effd6f77e9086dd4078945d 100644 (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;
+}
index d18d20e423cf5ea3e182b520db8e2cf7a5812c44..06e1bf224886f01ac3f26b5a8c890f9c96a448fd 100644 (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 */
diff --git a/validator/val_nsec.c b/validator/val_nsec.c
new file mode 100644 (file)
index 0000000..6bf997f
--- /dev/null
@@ -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 (file)
index 0000000..7e9a144
--- /dev/null
@@ -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 */
index 45a70f073ad822a1d43846bb58b3831346eadb08..795d1b8575be7a14a847f34a81884ad948540bd3 100644 (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;
+}
index 65f7646870f9c34c5d2c545fdca09e7827116e87..7aa48cefd7998107bed2c387631a325e630ff797 100644 (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 */
index f3570fd280f066edd0d724fc1050fb3cf8d310b0..7f12cbe799ac55b373872408ae8cd0f1d999dd4b 100644 (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