]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
validator work.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 6 Aug 2007 12:57:29 +0000 (12:57 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 6 Aug 2007 12:57:29 +0000 (12:57 +0000)
git-svn-id: file:///svn/unbound/trunk@493 be551aaa-1e26-0410-a405-d3ace91eadb9

validator/val_kentry.c
validator/val_kentry.h
validator/val_utils.c [new file with mode: 0644]
validator/val_utils.h [new file with mode: 0644]
validator/validator.c
validator/validator.h

index 7096453aae0f771d9c1f4488394553136a53115d..2d833775827d4db08449bb4f2c632875ec9151f4 100644 (file)
@@ -177,3 +177,10 @@ key_entry_copy(struct key_entry_key* kkey)
        }
        return newk;
 }
+
+int 
+key_entry_isnull(struct key_entry_key* kkey)
+{
+       struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+       return (!d->isbad && d->rrset_data == NULL);
+}
index 9c1fb9a56a6c1ae85d9163aadc755581b2a4319d..74bb06f7cb7a23adcceb7696084eeae05c3ba03f 100644 (file)
@@ -116,4 +116,11 @@ struct key_entry_key* key_entry_copy_toregion(struct key_entry_key* kkey,
  */
 struct key_entry_key* key_entry_copy(struct key_entry_key* kkey);
 
+/**
+ * See if this is a null entry. Does not do locking.
+ * @param kkey: must have data pointer set correctly
+ * @return true if it is a NULL rrset entry.
+ */
+int key_entry_isnull(struct key_entry_key* kkey);
+
 #endif /* VALIDATOR_VAL_KENTRY_H */
diff --git a/validator/val_utils.c b/validator/val_utils.c
new file mode 100644 (file)
index 0000000..093ec53
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * validator/val_utils.c - validator utility 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.
+ */
+#include "config.h"
+#include "validator/val_utils.h"
+#include "util/data/msgreply.h"
+#include "util/data/packed_rrset.h"
+#include "util/data/dname.h"
+#include "util/net_help.h"
+
+enum val_classification 
+val_classify_response(struct query_info* qinf, struct reply_info* rep)
+{
+       int rcode = (int)FLAGS_GET_RCODE(rep->flags);
+       size_t i;
+
+       /* Normal Name Error's are easy to detect -- but don't mistake a CNAME
+        * chain ending in NXDOMAIN. */
+       if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0)
+               return VAL_CLASS_NAMEERROR;
+
+       log_assert(rcode == LDNS_RCODE_NOERROR);
+       /* Next is NODATA */
+       if(rep->an_numrrsets == 0)
+               return VAL_CLASS_NODATA;
+       
+       /* We distinguish between CNAME response and other positive/negative
+        * responses because CNAME answers require extra processing. */
+
+       /* We distinguish between ANY and CNAME or POSITIVE because 
+        * ANY responses are validated differently. */
+       if(qinf->qtype == LDNS_RR_TYPE_ANY)
+               return VAL_CLASS_ANY;
+       
+       /* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless
+        * qtype=CNAME, this will yield a CNAME response. */
+       for(i=0; i<rep->an_numrrsets; i++) {
+               if(ntohs(rep->rrsets[i]->rk.type) == qinf->qtype)
+                       return VAL_CLASS_POSITIVE;
+               if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME)
+                       return VAL_CLASS_CNAME;
+       }
+       log_dns_msg("validator: failed to classify response message: ",
+               qinf, rep);
+       return VAL_CLASS_UNKNOWN;
+}
+
+/** Get signer name from RRSIG */
+static void
+rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname, size_t* slen)
+{
+       /* RRSIG rdata is not allowed to be compressed, it is stored
+        * uncompressed in memory as well, so return a ptr to the name */
+       if(len < 21) {
+               /* too short RRSig:
+                * short, byte, byte, long, long, long, short, "." is
+                * 2    1       1       4       4  4    2       1 = 19
+                *                      and a skip of 18 bytes to the name.
+                * +2 for the rdatalen is 21 bytes len for root label */
+               *sname = NULL;
+               *slen = 0;
+               return;
+       }
+       data += 20; /* skip the fixed size bits */
+       len -= 20;
+       *slen = dname_valid(data, len);
+       if(!*slen) {
+               /* bad dname in this rrsig. */
+               *sname = NULL;
+               return;
+       }
+       *sname = data;
+}
+
+/**
+ * Find the signer name for an RRset.
+ * @param rrset: the rrset.
+ * @param sname: signer name is returned or NULL if not signed.
+ * @param slen: length of sname (or 0).
+ */
+static void 
+val_find_rrset_signer(struct ub_packed_rrset_key* rrset, uint8_t** sname,
+       size_t* slen)
+{
+       struct packed_rrset_data* d = (struct packed_rrset_data*)
+               rrset->entry.data;
+       /* return signer for first signature, or NULL */
+       if(d->rrsig_count == 0) {
+               *sname = NULL;
+               *slen = 0;
+               return;
+       }
+       /* get rrsig signer name out of the signature */
+       rrsig_get_signer(d->rr_data[d->count], d->rr_len[d->count], 
+               sname, slen);
+}
+
+void 
+val_find_signer(struct query_info* qinf, struct reply_info* rep,
+       uint8_t** signer_name, size_t* signer_len)
+{
+       enum val_classification subtype = val_classify_response(qinf, rep);
+       size_t i;
+       
+       if(subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_CNAME 
+               || subtype == VAL_CLASS_ANY) {
+               /* check for the answer rrset */
+               for(i=0; i<rep->an_numrrsets; i++) {
+                       if(query_dname_compare(qinf->qname, 
+                               rep->rrsets[i]->rk.dname) == 0) {
+                               val_find_rrset_signer(rep->rrsets[i], 
+                                       signer_name, signer_len);
+                               return;
+                       }
+               }
+               *signer_name = NULL;
+               *signer_len = 0;
+       } else if(subtype == VAL_CLASS_NAMEERROR 
+               || subtype == VAL_CLASS_NODATA) {
+               /*Check to see if the AUTH section NSEC record(s) have rrsigs*/
+               for(i=rep->an_numrrsets; i<
+                       rep->an_numrrsets+rep->ns_numrrsets; i++) {
+                       if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC
+                               || ntohs(rep->rrsets[i]->rk.type) ==
+                               LDNS_RR_TYPE_NSEC3) {
+                               val_find_rrset_signer(rep->rrsets[i], 
+                                       signer_name, signer_len);
+                               return;
+                       }
+               }
+       } else {
+               verbose(VERB_ALGO, "find_signer: could not find signer name"
+                       " for unknown type response");
+               *signer_name = NULL;
+               *signer_len = 0;
+       }
+}
diff --git a/validator/val_utils.h b/validator/val_utils.h
new file mode 100644 (file)
index 0000000..31f9f72
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * validator/val_utils.h - validator utility 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.
+ */
+
+#ifndef VALIDATOR_VAL_UTILS_H
+#define VALIDATOR_VAL_UTILS_H
+struct query_info;
+struct reply_info;
+
+/**
+ * Response classifications for the validator. The different types of proofs.
+ */
+enum val_classification {
+       /** Not subtyped yet. */
+       VAL_CLASS_UNTYPED = 0,
+       /** Not a recognized subtype. */
+       VAL_CLASS_UNKNOWN,
+       /** A positive, direct, response */
+       VAL_CLASS_POSITIVE,
+       /** A positive response, with a CNAME/DNAME chain. */
+       VAL_CLASS_CNAME,
+       /** A NOERROR/NODATA response. */
+       VAL_CLASS_NODATA,
+       /** A NXDOMAIN response. */
+       VAL_CLASS_NAMEERROR,
+       /** A response to a qtype=ANY query. */
+       VAL_CLASS_ANY
+};
+
+/**
+ * Given a response, classify ANSWER responses into a subtype.
+ * @param qinf: query info
+ * @param rep: response
+ * @return A subtype ranging from UNKNOWN to NAMEERROR.
+ */
+enum val_classification val_classify_response(struct query_info* qinf,
+       struct reply_info* rep);
+
+/**
+ * Given a response, determine the name of the "signer". This is primarily
+ * to determine if the response is, in fact, signed at all, and, if so, what
+ * is the name of the most pertinent keyset.
+ *
+ * @param qinf: query
+ * @param rep: response to that
+ * @param signer_name:  signer name, if the response is signed 
+ *     (even partially), or null if the response isn't signed.
+ * @param signer_len: length of signer_name of 0 if signer_name is NULL.
+ */
+void val_find_signer(struct query_info* qinf, struct reply_info* rep,
+       uint8_t** signer_name, size_t* signer_len);
+
+#endif /* VALIDATOR_VAL_UTILS_H */
index ccaad4eeac568cd370ef603544bf92116f6ff142..f14b49337b32eb68c7592c6e3aaecbc249545af3 100644 (file)
 #include "validator/validator.h"
 #include "validator/val_anchor.h"
 #include "validator/val_kcache.h"
+#include "validator/val_kentry.h"
+#include "validator/val_utils.h"
 #include "services/cache/dns.h"
+#include "util/data/dname.h"
 #include "util/module.h"
 #include "util/log.h"
 #include "util/net_help.h"
@@ -139,20 +142,142 @@ val_new(struct module_qstate* qstate, int id)
        return vq;
 }
 
+/** 
+ * Check to see if a given response needs to go through the validation
+ * process. Typical reasons for this routine to return false are: CD bit was
+ * on in the original request, the response was already validated, or the
+ * response is a kind of message that is unvalidatable (i.e., SERVFAIL,
+ * REFUSED, etc.)
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @return true if the response could use validation (although this does not
+ *         mean we can actually validate this response).
+ */
+static int
+needs_validation(struct module_qstate* qstate, struct val_qstate* vq)
+{
+       int rcode;
+
+       /* If the CD bit is on in the original request, then we don't bother to
+        * validate anything.*/
+       if(qstate->query_flags | BIT_CD) {
+               verbose(VERB_ALGO, "not validating response due to CD bit");
+               return 0;
+       }
+
+       /* TODO: check if already validated */
+       /*
+        *     if (response.getStatus() > SecurityStatus.BOGUS)
+        *         {
+        *               log.debug("response has already been validated");
+        *                     return false;
+        *                         }
+        */
+
+       rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags);
+       if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
+               verbose(VERB_ALGO, "cannot validate non-answer, rcode %s",
+                       ldns_lookup_by_id(ldns_rcodes, rcode)?
+                       ldns_lookup_by_id(ldns_rcodes, rcode)->name:"??");
+               return 0;
+       }
+       return 1;
+}
+
+/**
+ * Prime trust anchor for use.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param ve: validator shared global environment.
+ * @param id: module id.
+ * @param toprime: what to prime.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static void
+prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
+        struct val_env* ve, int id, struct trust_anchor* toprime)
+{
+
+}
+
 /** 
  * Process init state for validator.
+ * Process the INIT state. First tier responses start in the INIT state.
+ * This is where they are vetted for validation suitability, and the initial
+ * key search is done.
+ * 
+ * Currently, events the come through this routine will be either promoted
+ * to FINISHED/CNAME_RESP (no validation needed), FINDKEY (next step to
+ * validation), or will be (temporarily) retired and a new priming request
+ * event will be generated.
  *
  * @param qstate: query state.
  * @param vq: validator query state.
  * @param ve: validator shared global environment.
  * @param id: module id.
- * @return true to continue processing
+ * @return true if the event should be processed further on return, false if
+ *         not.
  */
 static int
 processInit(struct module_qstate* qstate, struct val_qstate* vq, 
        struct val_env* ve, int id)
 {
-       return 0;
+       uint8_t* lookup_name;
+       size_t lookup_len;
+       if(!needs_validation(qstate, vq)) {
+               vq->state = vq->final_state;
+               return 1;
+       }
+       vq->trust_anchor = anchors_lookup(ve->anchors, vq->qchase.qname,
+               vq->qchase.qname_len, vq->qchase.qclass);
+       if(vq->trust_anchor == NULL) {
+               /*response isn't under a trust anchor, so we cannot validate.*/
+               vq->state = vq->final_state;
+               return 1;
+       }
+
+       /* Determine the signer/lookup name */
+       val_find_signer(&vq->qchase, vq->chase_reply, 
+               &vq->signer_name, &vq->signer_len);
+       if(vq->signer_name == NULL) {
+               lookup_name = vq->qchase.qname;
+               lookup_len = vq->qchase.qname_len;
+       } else {
+               lookup_name = vq->signer_name;
+               lookup_len = vq->signer_len;
+       }
+
+       vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
+               vq->qchase.qclass, qstate->region);
+       
+       /* if not key, or if keyentry is *above* the trustanchor, i.e.
+        * the keyentry is based on another (higher) trustanchor */
+       if(vq->key_entry == NULL || dname_strict_subdomain_c(
+               vq->trust_anchor->name, vq->key_entry->name)) {
+               /* fire off a trust anchor priming query. */
+               prime_trust_anchor(qstate, vq, ve, id, vq->trust_anchor);
+               /* and otherwise, don't continue processing this event.
+                * (it will be reactivated when the priming query returns). */
+               vq->state = VAL_FINDKEY_STATE;
+               return 0;
+       } else if(key_entry_isnull(vq->key_entry)) {
+               /* response is under a null key, so we cannot validate
+                * However, we do set the status to INSECURE, since it is 
+                * essentially proven insecure. */
+               /* TODO
+               vq->security_state = SEC_INSECURE;
+               */
+               vq->state = vq->final_state;
+               return 1;
+       }
+
+       /* otherwise, we have our "closest" cached key -- continue 
+        * processing in the next state. */
+       vq->state = VAL_FINDKEY_STATE;
+       return 1;
 }
 
 /** 
@@ -197,6 +322,9 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
                strmodulevent(event));
        log_query_info(VERB_DETAIL, "validator operate: query",
                &qstate->qinfo);
+       if(vq && qstate->qinfo.qname != vq->qchase.qname) 
+               log_query_info(VERB_DETAIL, "validator operate: chased to",
+               &vq->qchase);
        (void)outbound;
        if(event == module_event_new || event == module_event_pass) {
                /* pass request to next module, to get it */
index e0e3039c9077d4e6e5f240643e67c544445cd2cf..0b6fdcc4c4b1f64d63ad8e8f20ed00f39b30ee19 100644 (file)
 #define VALIDATOR_VALIDATOR_H
 struct module_func_block;
 #include "util/data/msgreply.h"
+#include "validator/val_utils.h"
 struct val_anchors;
 struct key_cache;
+struct key_entry_key;
 
 /**
  * Global state for the validator. 
@@ -105,6 +107,31 @@ struct val_qstate {
         *      o answer plus authority, additional (nsecs).
         */
        struct reply_info* chase_reply;
+
+       /** This is the "final" state for the event. */
+       enum val_state final_state;
+
+       /** the trust anchor rrset */
+       struct trust_anchor* trust_anchor;
+
+       /** the DS rrset */
+       struct ub_packed_rrset_key* ds_rrset;
+
+       /** domain name for empty nonterminal detection */
+       uint8_t* empty_DS_name;
+       /** length of empty_DS_name */
+       size_t empty_DS_len;
+
+       /** the current key entry */
+       struct key_entry_key* key_entry;
+
+       /** subtype */
+       enum val_classification subtype;
+
+       /** signer name */
+       uint8_t* signer_name;
+       /** length of signer_name */
+       size_t signer_len;
 };
 
 /**