]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
val_util work.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 7 Aug 2007 14:30:01 +0000 (14:30 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 7 Aug 2007 14:30:01 +0000 (14:30 +0000)
git-svn-id: file:///svn/unbound/trunk@497 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
util/data/msgreply.c
util/data/msgreply.h
validator/val_kentry.c
validator/val_kentry.h
validator/val_utils.c
validator/val_utils.h
validator/validator.c
validator/validator.h

index 2a70cf592568438c24e7bdd428c5eca4c0ed6207..c271cff0451b383d8eca80bd1a9c1ea50164fa32 100644 (file)
@@ -3,6 +3,7 @@
        - security status is copied when rdata is equal for rrsets.
        - rrset id is updated to invalidate all the message cache entries
          that refer to NSEC, NSEC3, DNAME rrsets that have changed.
+       - val_util work
 
 6 August 2007: Wouter
        - key cache for validator.
index 46d943bc31957cf51498eb6c2788932fc7b65f28..d890fa9f1f1d94e7ecd8bea16e3cb96e8e1364ee 100644 (file)
@@ -670,6 +670,22 @@ reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep)
        return NULL;
 }
 
+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)
+{
+       size_t i;
+       for(i=0; i<rep->an_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 f7a30cc3c9ae082b7569c7786c4d58ffe6f06c62..1f45e561a046aea1c56302fd75480c5cf7d994d4 100644 (file)
@@ -321,6 +321,18 @@ int parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg,
 struct ub_packed_rrset_key* reply_find_answer_rrset(struct query_info* qinfo,
        struct reply_info* rep);
 
+/**
+ * Find rrset in reply, inside the answer section. Does not follow CNAMEs.
+ * @param rep: looks in answer 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_an(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 2d833775827d4db08449bb4f2c632875ec9151f4..350221e63ed06a950782de2c9df48fac3ee714d7 100644 (file)
@@ -184,3 +184,93 @@ 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);
 }
+
+int 
+key_entry_isgood(struct key_entry_key* kkey)
+{
+       struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+       return (!d->isbad && d->rrset_data != NULL);
+}
+
+int 
+key_entry_isbad(struct key_entry_key* kkey)
+{
+       struct key_entry_data* d = (struct key_entry_data*)kkey->entry.data;
+       return (int)(d->isbad);
+}
+
+/** setup key entry in region */
+static int
+key_entry_setup(struct region* region,
+       uint8_t* name, size_t namelen, uint16_t dclass, 
+       struct key_entry_key** k, struct key_entry_data** d)
+{
+       *k = region_alloc(region, sizeof(**k));
+       if(!*k)
+               return 0;
+       memset(*k, 0, sizeof(**k));
+       (*k)->entry.key = *k;
+       (*k)->name = region_alloc_init(region, name, namelen);
+       if(!(*k)->name)
+               return 0;
+       (*k)->namelen = namelen;
+       (*k)->key_class = dclass;
+       *d = region_alloc(region, sizeof(**d));
+       if(!*d)
+               return 0;
+       (*k)->entry.data = d;
+       return 1;
+}
+
+struct key_entry_key* 
+key_entry_create_null(struct region* region,
+       uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl)
+{
+       struct key_entry_key* k;
+       struct key_entry_data* d;
+       if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
+               return NULL;
+       d->ttl = ttl;
+       d->isbad = 0;
+       d->rrset_type = LDNS_RR_TYPE_DNSKEY;
+       d->rrset_data = NULL;
+       return k;
+}
+
+struct key_entry_key* 
+key_entry_create_rrset(struct region* region,
+       uint8_t* name, size_t namelen, uint16_t dclass,
+       struct ub_packed_rrset_key* rrset)
+{
+       struct key_entry_key* k;
+       struct key_entry_data* d;
+       struct packed_rrset_data* rd = (struct packed_rrset_data*)
+               rrset->entry.data;
+       if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
+               return NULL;
+       d->ttl = rd->ttl;
+       log_info("New key entry TTL is %d", (int)d->ttl);
+       d->isbad = 0;
+       d->rrset_type = ntohs(rrset->rk.type);
+       d->rrset_data = (struct packed_rrset_data*)region_alloc_init(region,
+               rd, packed_rrset_sizeof(rd));
+       if(!d->rrset_data)
+               return NULL;
+       packed_rrset_ptr_fixup(d->rrset_data);
+       return k;
+}
+
+struct key_entry_key* 
+key_entry_create_bad(struct region* region,
+       uint8_t* name, size_t namelen, uint16_t dclass)
+{
+       struct key_entry_key* k;
+       struct key_entry_data* d;
+       if(!key_entry_setup(region, name, namelen, dclass, &k, &d))
+               return NULL;
+       d->ttl = 0;
+       d->isbad = 1;
+       d->rrset_type = LDNS_RR_TYPE_DNSKEY;
+       d->rrset_data = NULL;
+       return k;
+}
index 74bb06f7cb7a23adcceb7696084eeae05c3ba03f..d18d20e423cf5ea3e182b520db8e2cf7a5812c44 100644 (file)
@@ -43,6 +43,7 @@
 #define VALIDATOR_VAL_KENTRY_H
 struct packed_rrset_data;
 struct region;
+struct ub_packed_rrset_key;
 #include "util/storage/lruhash.h"
 
 /**
@@ -123,4 +124,54 @@ struct key_entry_key* key_entry_copy(struct key_entry_key* kkey);
  */
 int key_entry_isnull(struct key_entry_key* kkey);
 
+/**
+ * See if this entry is good. Does not do locking.
+ * @param kkey: must have data pointer set correctly
+ * @return true if it is good.
+ */
+int key_entry_isgood(struct key_entry_key* kkey);
+
+/**
+ * See if this entry is bad. Does not do locking.
+ * @param kkey: must have data pointer set correctly
+ * @return true if it is bad.
+ */
+int key_entry_isbad(struct key_entry_key* kkey);
+
+/**
+ * Create a null entry, in the given region.
+ * @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.
+ * @return new key entry or NULL on alloc failure
+ */
+struct key_entry_key* key_entry_create_null(struct region* region,
+       uint8_t* name, size_t namelen, uint16_t dclass, uint32_t ttl);
+
+/**
+ * Create a key entry from an rrset, in the given region.
+ * @param region: where to allocate.
+ * @param name: the key name
+ * @param namelen: length of name
+ * @param dclass: class of key entry.
+ * @param rrset: data for key entry. This is copied to the region.
+ * @return new key entry or NULL on alloc failure
+ */
+struct key_entry_key* key_entry_create_rrset(struct region* region,
+        uint8_t* name, size_t namelen, uint16_t dclass, 
+       struct ub_packed_rrset_key* rrset);
+
+/**
+ * Create a bad entry, in the given region.
+ * @param region: where to allocate
+ * @param name: the key name
+ * @param namelen: length of name
+ * @param dclass: class of key entry.
+ * @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);
+
 #endif /* VALIDATOR_VAL_KENTRY_H */
index 093ec5331a4224840bdcb0f93bc44b4f97aa14ac..27f4d50647f75d45272f121ea3cb205ddbfe52ae 100644 (file)
@@ -40,6 +40,7 @@
  */
 #include "config.h"
 #include "validator/val_utils.h"
+#include "validator/val_kentry.h"
 #include "util/data/msgreply.h"
 #include "util/data/packed_rrset.h"
 #include "util/data/dname.h"
@@ -172,3 +173,135 @@ val_find_signer(struct query_info* qinf, struct reply_info* rep,
                *signer_len = 0;
        }
 }
+
+/** return number of rrs in an rrset */
+static size_t
+rrset_get_count(struct ub_packed_rrset_key* rrset)
+{
+       struct packed_rrset_data* d = (struct packed_rrset_data*)
+               rrset->entry.data;
+       if(!d) return 0;
+       return d->count;
+}
+
+/** return TTL of rrset */
+static uint32_t
+rrset_get_ttl(struct ub_packed_rrset_key* rrset)
+{
+       struct packed_rrset_data* d = (struct packed_rrset_data*)
+               rrset->entry.data;
+       if(!d) return 0;
+       return d->ttl;
+}
+
+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)
+{
+
+       return sec_status_bogus;
+}
+
+/** 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, 
+       struct ub_packed_rrset_key* dnskey_rrset, 
+        struct ub_packed_rrset_key* ds_rrset, size_t ds_idx)
+{
+       enum sec_status sec;
+       size_t i, num;
+       num = rrset_get_count(dnskey_rrset);
+       for(i=0; i<num; i++) {
+               /* Skip DNSKEYs that don't match the basic criteria. */
+               /* if (ds.getFootprint() != dnskey.getFootprint()
+                *             || ds.getAlgorithm() != dnskey.getAlgorithm())
+                *                     {
+                *                               continue;
+                *                                       }
+                */
+
+               /* Convert the candidate DNSKEY into a hash using the 
+                * same DS hash algorithm. */
+               /* byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
+                *         byte[] ds_hash = ds.getDigest() */
+
+               /* if length or contents of the hash mismatch; continue */
+
+               /* Otherwise, we have a match! Make sure that the DNSKEY 
+                * verifies *with this key*  */
+               /*
+               sec = verify_rrset_key(env, ve, dnskey_rrset, dnskey_rrset, i);
+               */
+               if(sec == sec_status_secure) {
+                       return sec;
+               }
+               /* If it didn't validate with the DNSKEY, try the next one! */
+       }
+       return sec_status_bogus;
+}
+
+struct key_entry_key* 
+val_verify_new_DNSKEYs(struct region* region, struct module_env* env, 
+       struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, 
+       struct ub_packed_rrset_key* ds_rrset)
+{
+       /* as long as this is false, we can consider this DS rrset to be
+        * equivalent to no DS rrset. */
+       int has_useful_ds = 0;
+       size_t i, num;
+       enum sec_status sec;
+
+       if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len ||
+               query_dname_compare(dnskey_rrset->rk.dname, ds_rrset->rk.dname)
+               != 0) {
+               verbose(VERB_ALGO, "DNSKEY RRset did not match DS RRset "
+                       "by name");
+               return key_entry_create_bad(region, ds_rrset->rk.dname,
+                       ds_rrset->rk.dname_len, 
+                       ntohs(ds_rrset->rk.rrset_class));
+       }
+
+       num = rrset_get_count(ds_rrset);
+       for(i=0; i<num; i++) {
+
+               /* Check to see if we can understand this DS. */
+               /* if (!supportsDigestID(ds.getDigestID())
+                *           || !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
+                *                 {
+                *                         continue;
+                *                               }
+                */
+
+               /* Once we see a single DS with a known digestID and 
+                * algorithm, we cannot return INSECURE (with a 
+                * "null" KeyEntry). */
+               has_useful_ds = true;
+
+               sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
+                       ds_rrset, i);
+               if(sec == sec_status_secure) {
+                       verbose(VERB_ALGO, "DS matched DNSKEY.");
+                       /* TODO -- cannot, wrong region for prime */
+                       /* update dnskey RRset status as secure */
+                       return key_entry_create_rrset(region, 
+                               ds_rrset->rk.dname, ds_rrset->rk.dname_len,
+                               ntohs(ds_rrset->rk.rrset_class), dnskey_rrset);
+               }
+       }
+
+       /* None of the DS's worked out. */
+
+       /* If no DSs were understandable, then this is OK. */
+       if(!has_useful_ds) {
+               verbose(VERB_ALGO, "No usable DS records were found -- "
+                       "treating as insecure.");
+               return key_entry_create_null(region, ds_rrset->rk.dname,
+                       ds_rrset->rk.dname_len, 
+                       ntohs(ds_rrset->rk.rrset_class),
+                       rrset_get_ttl(ds_rrset));
+       }
+       /* If any were understandable, then it is bad. */
+       verbose(VERB_ALGO, "Failed to match any usable DS to a DNSKEY.");
+       return key_entry_create_bad(region, ds_rrset->rk.dname,
+               ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class));
+}
index 31f9f721ac9cbf3823ccf539d9ca150c191a203b..65f7646870f9c34c5d2c545fdca09e7827116e87 100644 (file)
 #define VALIDATOR_VAL_UTILS_H
 struct query_info;
 struct reply_info;
+struct val_env;
+struct module_env;
+struct ub_packed_rrset_key;
+struct region;
+enum sec_status;
 
 /**
  * Response classifications for the validator. The different types of proofs.
@@ -87,4 +92,39 @@ enum val_classification val_classify_response(struct query_info* qinf,
 void val_find_signer(struct query_info* qinf, struct reply_info* rep,
        uint8_t** signer_name, size_t* signer_len);
 
+/**
+ * Verify RRset with keys
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param rrset: what to verify
+ * @param keys: dnskey rrset to verify with.
+ * @return security status of verification.
+ */
+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 new DNSKEYs with DS rrset. The DS contains hash values that should
+ * match the DNSKEY keys.
+ * match the DS to a DNSKEY and verify the DNSKEY rrset with that key.
+ *
+ * @param region: where to allocate key entry result.
+ * @param env: module environment (scratch buffer)
+ * @param ve: validator environment (verification settings)
+ * @param dnskey_rrset: DNSKEY rrset to verify
+ * @param ds_rrset: DS rrset to verify with.
+ * @return a KeyEntry. This will either contain the now trusted
+ *         dnskey_rrset, a "null" key entry indicating that this DS
+ *         rrset/DNSKEY pair indicate an secure end to the island of trust
+ *         (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey
+ *         rrset fails to verify. Note that the "null" response should
+ *         generally only occur in a private algorithm scenario: normally
+ *         this sort of thing is checked before fetching the matching DNSKEY
+ *         rrset.
+ */
+struct key_entry_key* val_verify_new_DNSKEYs(struct region* region, 
+       struct module_env* env, struct val_env* ve, 
+       struct ub_packed_rrset_key* dnskey_rrset, 
+       struct ub_packed_rrset_key* ds_rrset);
+
 #endif /* VALIDATOR_VAL_UTILS_H */
index 4d077133d903ad8eea1808907ee539bda41fec9c..28ed54bf36f334b92e8f72d4f82f733865102b85 100644 (file)
@@ -298,9 +298,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
                /* 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->chase_reply->security = sec_status_insecure;
                vq->state = vq->final_state;
                return 1;
        }
@@ -414,17 +412,75 @@ static struct key_entry_key*
 primeResponseToKE(int rcode, struct dns_msg* msg, struct trust_anchor* ta,
        struct module_qstate* qstate, int id)
 {
+       struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
        struct ub_packed_rrset_key* dnskey_rrset = NULL;
+       struct key_entry_key* kkey = NULL;
+       enum sec_status sec = sec_status_unchecked;
+
        if(rcode == LDNS_RCODE_NOERROR) {
-               dnskey_rrset = 0/*find answer */;
+               dnskey_rrset = reply_find_rrset_section_an(msg->rep,
+                       ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY,
+                       ta->dclass);
        }
        if(!dnskey_rrset) {
                log_query_info(VERB_ALGO, "failed to prime trust anchor -- "
                        "could not fetch DNSKEY rrset", &msg->qinfo);
-               /* create NULL key with NULL_KEY_TTL, store in cache. */
-               return NULL;
+               kkey = key_entry_create_null(qstate->region, ta->name,
+                       ta->namelen, ta->dclass, time(0)+NULL_KEY_TTL);
+               if(!kkey) {
+                       log_err("out of memory: allocate null prime key");
+                       return NULL;
+               }
+               key_cache_insert(ve->kcache, kkey);
+               return kkey;
+       }
+       /* attempt to verify with trust anchor DS and DNSKEY */
+       if(ta->ds_rrset) {
+               kkey = val_verify_new_DNSKEYs(qstate->region, qstate->env, ve, 
+                       dnskey_rrset, ta->ds_rrset);
+               if(!kkey) {
+                       log_err("out of memory: verifying prime DS");
+                       return NULL;
+               }
+               if(key_entry_isgood(kkey))
+                       sec = sec_status_secure;
+               else
+                       sec = sec_status_bogus;
+       }
+       if(sec != sec_status_secure && ta->dnskey_rrset) {
+               sec = val_verify_rrset(qstate->env, ve, dnskey_rrset,
+                       ta->dnskey_rrset);
+               if(sec == sec_status_secure) {
+                       kkey = key_entry_create_rrset(qstate->region, 
+                               ta->name, ta->namelen, ta->dclass, 
+                               dnskey_rrset);
+                       if(!kkey) {
+                               log_err("out of memory: allocate primed key");
+                               return NULL;
+                       }
+               }
        }
-       return NULL;
+
+       if(sec != sec_status_secure) {
+               log_query_info(VERB_ALGO, "failed to prime trust anchor -- "
+                       "could not fetch DNSKEY rrset", &msg->qinfo);
+               /* 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);
+               if(!kkey) {
+                       log_err("out of memory: allocate null prime key");
+                       return NULL;
+               }
+               key_cache_insert(ve->kcache, kkey);
+               return kkey;
+       }
+
+       log_query_info(VERB_ALGO, "Successfully primed trust anchor", 
+               &msg->qinfo);
+       /* store the freshly primed entry in the cache */
+       key_cache_insert(ve->kcache, kkey);
+       return kkey;
 }
 
 /**
index 881f1292bf28e922e150d04f2b0e7553cdacab8d..bfa3a42c976fae5478f631e02f02edaf05d9e37f 100644 (file)
@@ -49,6 +49,12 @@ struct val_anchors;
 struct key_cache;
 struct key_entry_key;
 
+/**
+ * This is the TTL to use when a trust anchor fails to prime. A trust anchor
+ * will be primed no more often than this interval.
+ */
+#define NULL_KEY_TTL   900 /* seconds */
+
 /**
  * Global state for the validator. 
  */