]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
nxdomain validation.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Aug 2007 14:27:11 +0000 (14:27 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 20 Aug 2007 14:27:11 +0000 (14:27 +0000)
git-svn-id: file:///svn/unbound/trunk@534 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
validator/val_nsec.c
validator/val_nsec.h
validator/validator.c

index 44fa414ccbafdea6776ef66cd2300ed9c59fbbd5..3f2db5acd16762fc688dc97e8755ba1e525f83f9 100644 (file)
@@ -1,7 +1,7 @@
 18 August 2007: Wouter
        - process DNSKEY response in FINDKEY state.
        - validate and positive validation, positive wildcard NSEC validation.
-       - nodata validation.
+       - nodata validation, nxdomain validation.
 
 17 August 2007: Wouter
        - work on DS2KE routine.
index 29a55ecbe35885a97f553ae324f44f339dd77dc6..8a45599c10958ad56d8a9930bcf66f8e91ca631f 100644 (file)
@@ -330,19 +330,21 @@ val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname)
                return 0;
        }
 
-       /* see if this nsec is the only nsec */
        if(query_dname_compare(owner, next) == 0) {
-               /* only zone.name NSEC zone.name, disproves everything else */
-               return 1;
+               /* this nsec is the only nsec */
+               /* zone.name NSEC zone.name, disproves everything else */
+               /* but only for subdomains of that zone */
+               if(dname_strict_subdomain_c(qname, next))
+                       return 1;
        }
-       /* see if this nsec is the last nsec */
-       if(dname_canonical_compare(owner, next) > 0) {
+       else if(dname_canonical_compare(owner, next) > 0) {
                /* this is the last nsec, ....(bigger) NSEC zonename(smaller) */
-               /* the names after the last (owner) name do not exist */
-               if(dname_canonical_compare(owner, qname) < 0)
+               /* the names after the last (owner) name do not exist 
+                * there are no names before the zone name in the zone 
+                * but the qname must be a subdomain of the zone name(next). */
+               if(dname_canonical_compare(owner, qname) < 0 &&
+                       dname_strict_subdomain_c(qname, next))
                        return 1;
-               /* there are no names before the zone name in the zone */
-               /* if(dname_canonical_compare(qname, next) < 0) return 1; */
        } else {
                /* regular NSEC, (smaller) NSEC (larger) */
                if(dname_canonical_compare(owner, qname) < 0 &&
@@ -387,3 +389,38 @@ int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec,
        }
        return 1;
 }
+
+int 
+val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 
+       size_t qnamelen)
+{
+       /* Determine if a NSEC record proves the non-existence of a 
+        * wildcard that could have produced qname. */
+       int labs;
+       int i;
+       uint8_t* ce = nsec_closest_encloser(qname, nsec);
+       uint8_t* strip;
+       size_t striplen;
+       uint8_t buf[LDNS_MAX_DOMAINLEN+3];
+       if(!ce)
+               return 0;
+       /* we can subtract the closest encloser count - since that is the
+        * largest shared topdomain with owner and next NSEC name,
+        * because the NSEC is no proof for names shorter than the owner 
+        * and next names. */
+       labs = dname_count_labels(qname) - dname_count_labels(ce);
+
+       for(i=labs; i>0; i--) {
+               /* i is number of labels to strip off qname, prepend * wild */
+               strip = qname;
+               striplen = qnamelen;
+               dname_remove_labels(&strip, &striplen, i);
+               buf[0] = 1;
+               buf[1] = (uint8_t)'*';
+               memmove(buf+2, strip, striplen);
+               if(val_nsec_proves_name_error(nsec, buf)) {
+                       return 1;
+               }
+       }
+       return 0;
+}
index 296f9356c566529b70369e2d8089d1fa53530926..d511f59f4f3e923aea2eb0ef53a6cbc54c5fc0b0 100644 (file)
@@ -123,4 +123,15 @@ int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec,
 uint8_t* nsec_closest_encloser(uint8_t* qname, 
        struct ub_packed_rrset_key* nsec);
 
+/**
+ * Determine if the given NSEC proves that a wildcard match does not exist.
+ *
+ * @param nsec: the nsec RRset.
+ * @param qname: the name queried for.
+ * @param qnamelen: length of qname.
+ * @return true if proven.
+ */
+int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 
+       size_t qnamelen);
+
 #endif /* VALIDATOR_VAL_NSEC_H */
index dcfc38e3cb4e5ef3ae1a68a57cabda1246ad7d7a..d260969386c316f4345543e355eb89c05bdd8fa4 100644 (file)
@@ -451,9 +451,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
                        if(val_nsec_proves_name_error(s, qchase->qname)) {
                                ce = nsec_closest_encloser(qchase->qname, s);
                        }
-               }
-
-               if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
+               } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
                        nsec3s_seen = 1;
                }
        }
@@ -491,12 +489,81 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
        chase_reply->security = sec_status_secure;
 }
 
-/** validate NAME ERROR (nxdomain) response */
+/** 
+ * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN
+ * Rcode. This consists of verifying the authority section rrsets and making
+ * certain that the authority section NSEC proves that the qname doesn't
+ * exist and the covering wildcard also doesn't exist..
+ * 
+ * Note that by the time this method is called, the process of finding the
+ * trusted DNSKEY rrset that signs this response must already have been
+ * completed.
+ *
+ * @param env: module env for verify.
+ * @param ve: validator env for verify.
+ * @param qchase: query that was made.
+ * @param chase_reply: answer to that query to validate.
+ * @param key_entry: the key entry, which is trusted, and which matches
+ *     the signer of the answer. The key entry isgood().
+ */
 static void
 validate_nameerror_response(struct module_env* env, struct val_env* ve, 
        struct query_info* qchase, struct reply_info* chase_reply, 
        struct key_entry_key* key_entry)
 {
+       /* FIXME: should we check to see if there is anything in the answer
+        * section? if so, what should the result be? */
+
+       int has_valid_nsec = 0;
+       int has_valid_wnsec = 0;
+       int nsec3s_seen = 0;
+       struct ub_packed_rrset_key* s; 
+       enum sec_status sec;
+       size_t i;
+
+       for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
+               chase_reply->ns_numrrsets; i++) {
+               s = chase_reply->rrsets[i];
+               sec = val_verify_rrset_entry(env, ve, s, key_entry);
+               if(sec != sec_status_secure) {
+                       log_nametypeclass(VERB_ALGO, "NameError response has "
+                               "failed AUTHORITY rrset: ", s->rk.dname,
+                               ntohs(s->rk.type), ntohs(s->rk.rrset_class));
+                       chase_reply->security = sec_status_bogus;
+                       return;
+               }
+               if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
+                       if(val_nsec_proves_name_error(s, qchase->qname))
+                               has_valid_nsec = 1;
+                       if(val_nsec_proves_no_wc(s, qchase->qname, 
+                               qchase->qname_len))
+                               has_valid_wnsec = 1;
+               } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3)
+                       nsec3s_seen = 1;
+       }
+
+       if(!has_valid_nsec || !has_valid_wnsec) {
+               /* TODO: use NSEC3 proof */
+       }
+
+       /* If the message fails to prove either condition, it is bogus. */
+       if(!has_valid_nsec) {
+               verbose(VERB_ALGO, "NameError response has failed to prove: "
+                         "qname does not exist");
+               chase_reply->security = sec_status_bogus;
+               return;
+       }
+
+       if(!has_valid_wnsec) {
+               verbose(VERB_ALGO, "NameError response has failed to prove: "
+                         "covering wildcard does not exist");
+               chase_reply->security = sec_status_bogus;
+               return;
+       }
+
+       /* Otherwise, we consider the message secure. */
+       verbose(VERB_ALGO, "successfully validated NAME ERROR response.");
+       chase_reply->security = sec_status_secure;
 }
 
 /** validate positive ANY response */