]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
nsec work, canonical compare routine and tests.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 17 Aug 2007 14:25:42 +0000 (14:25 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 17 Aug 2007 14:25:42 +0000 (14:25 +0000)
git-svn-id: file:///svn/unbound/trunk@530 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
testcode/unitdname.c
util/data/dname.c
util/data/dname.h
validator/val_nsec.c
validator/val_nsec.h
validator/validator.c

index 97a37ffafd637eb99cdc1cc528c26964c6f07e1c..909e04c55f9f20c015ff2a293351d6965b98ba35 100644 (file)
@@ -2,6 +2,7 @@
        - work on DS2KE routine.
        - val_nsec.c for validator NSEC proofs.
        - unit test for NSEC bitmap reading.
+       - dname iswild and canonical_compare with unit tests.
 
 16 August 2007: Wouter
        - DS sig unit test.
index 25e33e707eac57e721f687525b99c7ee319dd2b3..f8662f7e6d7c73000e1610dd973afbbf7ca2aabd 100644 (file)
@@ -482,6 +482,235 @@ dname_test_sigcount()
                (uint8_t*)"\001*\003www\007example\003xom\000") == 3);
 }
 
+/** test dname_is_wild routine */
+static void
+dname_test_iswild()
+{
+       unit_assert( !dname_is_wild((uint8_t*)"\000") );
+       unit_assert( dname_is_wild((uint8_t*)"\001*\000") );
+       unit_assert( !dname_is_wild((uint8_t*)"\003net\000") );
+       unit_assert( dname_is_wild((uint8_t*)"\001*\003net\000") );
+}
+
+/** test dname_canonical_compare */
+static void
+dname_test_canoncmp()
+{
+       /* equality */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\000",
+               (uint8_t*)"\000"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003net\000",
+               (uint8_t*)"\003net\000"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example\003net\000",
+               (uint8_t*)"\007example\003net\000"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\004test\007example\003net\000",
+               (uint8_t*)"\004test\007example\003net\000"
+               ) == 0);
+
+       /* subdomains */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003com",
+               (uint8_t*)"\000"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\000",
+               (uint8_t*)"\003com"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example\003com",
+               (uint8_t*)"\003com"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003com",
+               (uint8_t*)"\007example\003com"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example\003com",
+               (uint8_t*)"\000"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\000",
+               (uint8_t*)"\007example\003com"
+               ) == -1);
+
+       /* compare rightmost label */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003com",
+               (uint8_t*)"\003net"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003net",
+               (uint8_t*)"\003com"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003net",
+               (uint8_t*)"\003org"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example\003net",
+               (uint8_t*)"\003org"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003org",
+               (uint8_t*)"\007example\003net"
+               ) == 1);
+
+       /* label length makes a difference; but only if rest is equal */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\004neta",
+               (uint8_t*)"\003net"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\002ne",
+               (uint8_t*)"\004neta"
+               ) == -1);
+
+       /* label content */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003aag\007example\003net",
+               (uint8_t*)"\003bla\007example\003net"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003bla\007example\003net",
+               (uint8_t*)"\003aag\007example\003net"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003bla\003aag\007example\003net",
+               (uint8_t*)"\003aag\003bla\007example\003net"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\02sn\003opt\003aag\007example\003net",
+               (uint8_t*)"\02sn\003opt\003bla\007example\003net"
+               ) == -1);
+
+       /* lowercase during compare */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\003bLa\007examPLe\003net",
+               (uint8_t*)"\003bla\007eXAmple\003nET"
+               ) == 0);
+
+       /* example from 4034 */
+       /* example a.example yljkjljk.a.example Z.a.example zABC.a.EXAMPLE
+        z.example \001.z.example *.z.example \200.z.example */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"",
+               (uint8_t*)"\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example",
+               (uint8_t*)"\001a\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001a\007example",
+               (uint8_t*)"\010yljkjljk\001a\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\010yljkjljk\001a\007example",
+               (uint8_t*)"\001Z\001a\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001Z\001a\007example",
+               (uint8_t*)"\004zABC\001a\007EXAMPLE"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\004zABC\001a\007EXAMPLE",
+               (uint8_t*)"\001z\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001z\007example",
+               (uint8_t*)"\001\001\001z\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001\001\001z\007example",
+               (uint8_t*)"\001*\001z\007example"
+               ) == -1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001*\001z\007example",
+               (uint8_t*)"\001\200\001z\007example"
+               ) == -1);
+       /* same example in reverse */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example",
+               (uint8_t*)""
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001a\007example",
+               (uint8_t*)"\007example"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\010yljkjljk\001a\007example",
+               (uint8_t*)"\001a\007example"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001Z\001a\007example",
+               (uint8_t*)"\010yljkjljk\001a\007example"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\004zABC\001a\007EXAMPLE",
+               (uint8_t*)"\001Z\001a\007example"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001z\007example",
+               (uint8_t*)"\004zABC\001a\007EXAMPLE"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001\001\001z\007example",
+               (uint8_t*)"\001z\007example"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001*\001z\007example",
+               (uint8_t*)"\001\001\001z\007example"
+               ) == 1);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001\200\001z\007example",
+               (uint8_t*)"\001*\001z\007example"
+               ) == 1);
+       /* same example for equality */
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\007example",
+               (uint8_t*)"\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001a\007example",
+               (uint8_t*)"\001a\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\010yljkjljk\001a\007example",
+               (uint8_t*)"\010yljkjljk\001a\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001Z\001a\007example",
+               (uint8_t*)"\001Z\001a\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\004zABC\001a\007EXAMPLE",
+               (uint8_t*)"\004zABC\001a\007EXAMPLE"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001z\007example",
+               (uint8_t*)"\001z\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001\001\001z\007example",
+               (uint8_t*)"\001\001\001z\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001*\001z\007example",
+               (uint8_t*)"\001*\001z\007example"
+               ) == 0);
+       unit_assert( dname_canonical_compare(
+               (uint8_t*)"\001\200\001z\007example",
+               (uint8_t*)"\001\200\001z\007example"
+               ) == 0);
+}
+
 void dname_test()
 {
        ldns_buffer* buff = ldns_buffer_new(65800);
@@ -498,5 +727,7 @@ void dname_test()
        dname_test_isroot();
        dname_test_removelabel();
        dname_test_sigcount();
+       dname_test_iswild();
+       dname_test_canoncmp();
        ldns_buffer_free(buff);
 }
index 3b42ee40570cbbd6c4fcd666e0d12633c83179fd..2d3289831002e83f03fe8d6049928f73fc604375 100644 (file)
@@ -621,3 +621,107 @@ dname_signame_label_count(uint8_t* dname)
        }
        return count;
 }
+
+int 
+dname_is_wild(uint8_t* dname)
+{
+       return (dname[0] == 1 && dname[1] == '*');
+}
+
+/**
+ * Compare labels in memory, lowercase while comparing.
+ * Returns canonical order for labels. If all is equal, the
+ * shortest is first.
+ *
+ * @param p1: label 1
+ * @param len1: length of label 1.
+ * @param p2: label 2
+ * @param len2: length of label 2.
+ * @return: 0, -1, +1 comparison result.
+ */
+static int
+memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
+{
+       uint8_t min = (len1<len2)?len1:len2;
+       int c = memlowercmp(p1, p2, min);
+       if(c != 0)
+               return c;
+       /* equal, see who is shortest */
+       if(len1 < len2)
+               return -1;
+       if(len1 > len2)
+               return 1;
+       return 0;
+}
+
+
+int 
+dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
+{
+       /* like dname_lab_cmp, but with different label comparison,
+        * empty character sorts before \000.
+        * So   ylyly is before z. */
+       uint8_t len1, len2;
+       int atlabel = labs1;
+       int lastmlabs;
+       int lastdiff = 0;
+       int c;
+       /* first skip so that we compare same label. */
+       if(labs1 > labs2) {
+               while(atlabel > labs2) {
+                       len1 = *d1++;
+                       d1 += len1;
+                       atlabel--;
+               }
+               log_assert(atlabel == labs2);
+       } else if(labs1 < labs2) {
+               atlabel = labs2;
+               while(atlabel > labs1) {
+                       len2 = *d2++;
+                       d2 += len2;
+                       atlabel--;
+               }
+               log_assert(atlabel == labs1);
+       }
+       lastmlabs = atlabel+1;
+       /* now at same label in d1 and d2, atlabel */
+       /* www.example.com.                  */
+       /* 4   3       2  1   atlabel number */
+       /* repeat until at root label (which is always the same) */
+       while(atlabel > 1) {
+               len1 = *d1++;
+               len2 = *d2++;
+
+               if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
+                       if(c<0)
+                               lastdiff = -1;
+                       else    lastdiff = 1;
+                       lastmlabs = atlabel;
+               }
+
+               d1 += len1;
+               d2 += len2;
+               atlabel--;
+       }
+       /* last difference atlabel number, so number of labels matching,
+        * at the right side, is one less. */
+       *mlabs = lastmlabs-1;
+       if(lastdiff == 0) {
+               /* all labels compared were equal, check if one has more
+                * labels, so that example.com. > com. */
+               if(labs1 > labs2)
+                       return 1;
+               else if(labs1 < labs2)
+                       return -1;
+       }
+       return lastdiff;
+}
+
+int
+dname_canonical_compare(uint8_t* d1, uint8_t* d2)
+{
+       int labs1, labs2, m;
+       labs1 = dname_count_labels(d1);
+       labs2 = dname_count_labels(d2);
+       return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
+}
index 26d54bc337f3b062b3f4e75e41f8caf24584f15d..f2b29987744942985fd64bd9c1b6db2fdd6efe9b 100644 (file)
@@ -167,7 +167,7 @@ int dname_count_size_labels(uint8_t* dname, size_t* size);
  * @param labs1: number of labels in first dname.
  * @param d2: second dname. pointer to uncompressed wireformat.
  * @param labs2: number of labels in second dname.
- * @param mlabs: number of labels that matched exactly.
+ * @param mlabs: number of labels that matched exactly (the shared topdomain).
  * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
  */
 int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs);
@@ -249,4 +249,35 @@ void dname_remove_labels(uint8_t** dname, size_t* len, int n);
  */
 int dname_signame_label_count(uint8_t* dname);
 
+/**
+ * Return true if the label is a wildcard, *.example.com.
+ * @param dname: valid uncompressed wireformat.
+ * @return true if wildcard, or false.
+ */
+int dname_is_wild(uint8_t* dname);
+
+/**
+ * Compare dnames, Canonical in rfc4034 sense, but by label.
+ * Such that zone contents follows zone apex.
+ *
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param labs1: number of labels in first dname.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @param labs2: number of labels in second dname.
+ * @param mlabs: number of labels that matched exactly (the shared topdomain).
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, 
+       int* mlabs);
+
+/**
+ * Canonical dname compare. Takes care of counting labels.
+ * Per rfc 4034 canonical order.
+ *
+ * @param d1: first dname. pointer to uncompressed wireformat.
+ * @param d2: second dname. pointer to uncompressed wireformat.
+ * @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
+ */
+int dname_canonical_compare(uint8_t* d1, uint8_t* d2);
+
 #endif /* UTIL_DATA_DNAME_H */
index 6bf997f46cca7e4f4dc63fa0eabbd3ee0a3ed7dc..9eac00bed6f527e5301bd1ae0ef57669fc613ca4 100644 (file)
@@ -71,7 +71,7 @@ nsec_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
                        size_t mybyte = type_low>>3;
                        if(winlen <= mybyte)
                                return 0; /* window too short */
-                       return bitmap[mybyte] & masks[type_low&0x7];
+                       return (int)(bitmap[mybyte] & masks[type_low&0x7]);
                } else {
                        /* not the window we are looking for */
                        bitmap += winlen;
@@ -107,7 +107,36 @@ nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
        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);
+       return nsec_has_type_rdata(d->rr_data[0]+2+len, 
+               d->rr_len[0]-2-len, type);
+}
+
+/**
+ * Get next owner name from nsec record
+ * @param nsec: the nsec RRset.
+ *     If there are multiple RRs, then this will only return one of them.
+ * @param nm: the next name is returned.
+ * @param ln: length of nm is returned.
+ * @return false on a bad NSEC RR (too short, malformed dname).
+ */
+static int 
+nsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln)
+{
+       struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
+               entry.data;
+       if(!d || d->count == 0 || d->rr_len[0] < 2+1) {
+               *nm = 0;
+               *ln = 0;
+               return 0;
+       }
+       *nm = d->rr_data[0]+2;
+       *ln = dname_valid(*nm, d->rr_len[0]-2);
+       if(!*ln) {
+               *nm = 0;
+               *ln = 0;
+               return 0;
+       }
+       return 1;
 }
 
 /**
@@ -119,7 +148,7 @@ nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
  *     insecure if it proves it is not a delegation point.
  *     or bogus if something was wrong.
  */
-enum sec_status 
+static enum sec_status 
 val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, 
        struct query_info* qinfo)
 {
@@ -128,17 +157,34 @@ val_nsec_proves_no_ds(struct ub_packed_rrset_key* 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;
+       if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) || 
+               nsec_has_type(nsec, LDNS_RR_TYPE_DS)) {
+               /* SOA present means that this is the NSEC from the child, 
+                * not the parent (so it is the wrong one).
+                * DS present means that there should have been a positive 
+                * response to the DS query, so there is something wrong. */
+               return sec_status_bogus;
+       }
+
+       if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) {
+               /* If there is no NS at this point at all, then this 
+                * doesn't prove anything one way or the other. */
+               return sec_status_insecure;
+       }
+       /* Otherwise, this proves no DS. */
+       return sec_status_secure;
 }
 
 enum sec_status 
-val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve, 
+val_nsec_prove_nodata_dsreply(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);
+       enum sec_status sec;
+       size_t i;
 
        /* If we have a NSEC at the same name, it must prove one 
         * of two things
@@ -146,8 +192,7 @@ val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
         * 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);
+               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.");
@@ -171,8 +216,91 @@ val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
        /* 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 */
+       /* verify NSEC rrsets in auth section */
+       for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets; 
+               i++) {
+               if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
+                       continue;
+               sec = val_verify_rrset_entry(env, ve, rep->rrsets[i], kkey);
+               if(sec != sec_status_secure) {
+                       verbose(VERB_ALGO, "NSEC for empty non-terminal "
+                               "did not verify.");
+                       return sec_status_bogus;
+               }
+               if(nsec_proves_nodata(rep->rrsets[i], qinfo)) {
+                       verbose(VERB_ALGO, "NSEC for empty non-terminal "
+                               "proved no DS.");
+                       return sec_status_secure;
+               }
+       }
+
+       /* NSEC proof did not conlusively point to DS or no DS */
+       return sec_status_unchecked;
+}
+
+int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 
+       struct query_info* qinfo)
+{
+       if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) {
+               uint8_t* nm;
+               size_t ln;
+               /* wildcard checking. */
+
+               /* If this is a wildcard NSEC, make sure that a) it was 
+                * possible to have generated qname from the wildcard and 
+                * b) the type map does not contain qtype. Note that this 
+                * does NOT prove that this wildcard was the applicable 
+                * wildcard. */
+               if(dname_is_wild(nsec->rk.dname)) {
+                       /* the purported closest encloser. */
+                       uint8_t* ce = nsec->rk.dname;
+                       size_t ce_len = nsec->rk.dname_len;
+                       dname_remove_label(&ce, &ce_len);
+
+                       /* The qname must be a strict subdomain of the 
+                        * closest encloser, and the qtype must be absent 
+                        * from the type map. */
+                       if(!dname_strict_subdomain_c(qinfo->qname, ce) ||
+                               nsec_has_type(nsec, qinfo->qtype)) {
+                               return 0;
+                       }
+                       return 1;
+               }
+
+               /* empty-non-terminal checking. */
+
+               /* If the nsec is proving that qname is an ENT, the nsec owner 
+                * will be less than qname, and the next name will be a child 
+                * domain of the qname. */
+               if(!nsec_get_next(nsec, &nm, &ln))
+                       return 0; /* bad nsec */
+               if(dname_strict_subdomain_c(nm, qinfo->qname) &&
+                       dname_canonical_compare(nsec->rk.dname, 
+                               qinfo->qname) < 0) {
+                       return 1; /* proves ENT */
+               }
+               /* Otherwise, this NSEC does not prove ENT, so it does not 
+                * prove NODATA. */
+               return 0;
+       }
+
+       /* If the qtype exists, then we should have gotten it. */
+       if(nsec_has_type(nsec, qinfo->qtype)) {
+               return 0;
+       }
+
+       /* if the name is a CNAME node, then we should have gotten the CNAME*/
+       if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) {
+               return 0;
+       }
+
+       /* If an NS set exists at this name, and NOT a SOA (so this is a 
+        * zone cut, not a zone apex), then we should have gotten a 
+        * referral (or we just got the wrong NSEC). */
+       if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 
+               !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
+               return 0;
+       }
 
-       return sec_status_bogus;
+       return 1;
 }
index 7e9a1447394266b6fbd99191787bb63e4d412589..aee1483beeb380e30aac146476593c3d2bdfbfb6 100644 (file)
@@ -67,9 +67,9 @@ struct key_entry_key;
  *     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).
+ *     UNCHECKED: there was no way to prove anything (no NSECs, unknown algo).
  */
-enum sec_status val_nsec_prove_nodata_ds(struct module_env* env,
+enum sec_status val_nsec_prove_nodata_dsreply(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);
@@ -77,4 +77,18 @@ enum sec_status val_nsec_prove_nodata_ds(struct module_env* env,
 /** Unit test call to test function for nsec typemap check */
 int unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type);
 
+/**
+ * Determine if a NSEC proves the NOERROR/NODATA conditions. This will also
+ * handle the empty non-terminal (ENT) case and partially handle the
+ * wildcard case. If the ownername of 'nsec' is a wildcard, the validator
+ * must still be provided proof that qname did not directly exist and that
+ * the wildcard is, in fact, *.closest_encloser.
+ *
+ * @param nsec: the nsec record to check against.
+ * @param qinfo: the query info.
+ * @return true if NSEC proves this.
+ */
+int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 
+       struct query_info* qinfo);
+
 #endif /* VALIDATOR_VAL_NSEC_H */
index 7f12cbe799ac55b373872408ae8cd0f1d999dd4b..ae4f2ed144bc90319d747788b2353877b4ba698e 100644 (file)
@@ -690,8 +690,9 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                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);
+               enum sec_status sec = val_nsec_prove_nodata_dsreply(
+                       qstate->env, ve, qinfo, msg->rep, vq->key_entry, 
+                       &proof_ttl);
                switch(sec) {
                        case sec_status_secure:
                                verbose(VERB_ALGO, "NSEC RRset for the "