]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Faster verification.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 28 Aug 2007 13:54:55 +0000 (13:54 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 28 Aug 2007 13:54:55 +0000 (13:54 +0000)
git-svn-id: file:///svn/unbound/trunk@557 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
validator/val_sigcrypt.c
validator/val_sigcrypt.h
validator/val_utils.c

index c4955dc35d269ad8097bdaf8fdbe13f6575bb576..1e4ce025b798c3b54d4632d3996e43474f9ad231 100644 (file)
@@ -9,6 +9,11 @@
        - adjust TTL downwards if rrset TTL bigger than signature allows.
        - permissive mode feature, sets AD bit for secure, but bogus does
          not give servfail (bogus is changed into indeterminate).
+       - optimization of rrset verification. rr canonical sorting is reused,
+         for the same rrset. canonical rrset image in buffer is reused for
+         the same signature.
+       - if the rrset is too big (64k exactly + large owner name) the
+         canonicalization routine will fail if it does not fit in buffer.
 
 27 August 2007: Wouter
        - do not garble the edns if a cache answer fails.
index 9b0a114d6c9c649b2055035d8769770b8ea5ae30..b28578b974561a6d11cdc1d2d71f5e454e596f37 100644 (file)
@@ -397,6 +397,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
 {
        enum sec_status sec;
        size_t i, num;
+       rbtree_t* sortree = NULL;
        num = rrset_get_sigcount(rrset);
        if(num == 0) {
                verbose(VERB_ALGO, "rrset failed to verify due to a lack of "
@@ -404,7 +405,8 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
                return sec_status_bogus;
        }
        for(i=0; i<num; i++) {
-               sec = dnskeyset_verify_rrset_sig(env, ve, rrset, dnskey, i);
+               sec = dnskeyset_verify_rrset_sig(env, ve, rrset, dnskey, i,
+                       &sortree);
                if(sec == sec_status_secure)
                        return sec;
        }
@@ -415,10 +417,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
 enum sec_status 
 dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
         struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
-               size_t dnskey_idx)
+       size_t dnskey_idx)
 {
        enum sec_status sec;
        size_t i, num;
+       rbtree_t* sortree = NULL;
+       int buf_canon = 0;
+
        num = rrset_get_sigcount(rrset);
        if(num == 0) {
                verbose(VERB_ALGO, "rrset failed to verify due to a lack of "
@@ -426,8 +431,10 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
                return sec_status_bogus;
        }
        for(i=0; i<num; i++) {
-               sec = dnskey_verify_rrset_sig(env, ve, rrset, dnskey, 
-                       dnskey_idx, i);
+               buf_canon = 0;
+               sec = dnskey_verify_rrset_sig(env->scratch, 
+                       env->scratch_buffer, ve, rrset, dnskey, dnskey_idx, i,
+                       &sortree, &buf_canon);
                if(sec == sec_status_secure)
                        return sec;
        }
@@ -438,7 +445,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
 enum sec_status 
 dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
         struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
-               size_t sig_idx)
+       size_t sig_idx, struct rbtree_t** sortree)
 {
        /* find matching keys and check them */
        enum sec_status sec = sec_status_bogus;
@@ -446,6 +453,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
        int algo = rrset_get_sig_algo(rrset, sig_idx);
        size_t i, num = rrset_get_count(dnskey);
        size_t numchecked = 0;
+       int buf_canon = 0;
        verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
        
        for(i=0; i<num; i++) {
@@ -456,8 +464,9 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
 
                numchecked ++;
                /* see if key verifies */
-               sec = dnskey_verify_rrset_sig(env, ve, rrset, dnskey, 
-                       i, sig_idx);
+               sec = dnskey_verify_rrset_sig(env->scratch, 
+                       env->scratch_buffer, ve, rrset, dnskey, i, sig_idx,
+                       sortree, &buf_canon);
                if(sec == sec_status_secure)
                        return sec;
        }
@@ -972,29 +981,47 @@ canonicalize_rdata(ldns_buffer* buf, struct ub_packed_rrset_key* rrset,
  * @param sig: RRSIG rdata to include.
  * @param siglen: RRSIG rdata len excluding signature field, but inclusive
  *     signer name length.
+ * @param sortree: if NULL is passed a new sorted rrset tree is built.
+ *     Otherwise it is reused.
  * @return false on alloc error.
  */
 static int
 rrset_canonical(struct region* region, ldns_buffer* buf, 
-       struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen)
+       struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen,
+       struct rbtree_t** sortree)
 {
        struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data;
        uint8_t* can_owner = NULL;
        size_t can_owner_len = 0;
-       rbtree_t sortree;
        struct canon_rr* walk;
        struct canon_rr* rrs;
-       rrs = region_alloc(region, sizeof(struct canon_rr)*d->count);
-       if(!rrs)
-               return 0;
-       rbtree_init(&sortree, &canonical_tree_compare);
-       canonical_sort(k, d, &sortree, rrs);
+
+       if(!*sortree) {
+               *sortree = (struct rbtree_t*)region_alloc(region, 
+                       sizeof(rbtree_t));
+               if(!*sortree)
+                       return 0;
+               rrs = region_alloc(region, sizeof(struct canon_rr)*d->count);
+               if(!rrs) {
+                       *sortree = NULL;
+                       return 0;
+               }
+               rbtree_init(*sortree, &canonical_tree_compare);
+               canonical_sort(k, d, *sortree, rrs);
+       }
 
        ldns_buffer_clear(buf);
        ldns_buffer_write(buf, sig, siglen);
        /* canonicalize signer name */
        query_dname_tolower(ldns_buffer_begin(buf)+18); 
-       RBTREE_FOR(walk, struct canon_rr*, &sortree) {
+       RBTREE_FOR(walk, struct canon_rr*, (*sortree)) {
+               /* see if there is enough space left in the buffer */
+               if(ldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4
+                       + d->rr_len[walk->rr_idx]) {
+                       log_err("verify: failed to canonicalize, "
+                               "rrset too big");
+                       return 0;
+               }
                /* determine canonical owner name */
                if(can_owner)
                        ldns_buffer_write(buf, can_owner, can_owner_len);
@@ -1092,7 +1119,6 @@ adjust_ttl(struct val_env* ve, struct ub_packed_rrset_key* rrset,
        /* get current date */
        if(ve->date_override) {
                now = ve->date_override;
-               verbose(VERB_ALGO, "date override option %d used", (int)now); 
        } else  now = (int32_t)time(0);
        expittl = expi - now;
 
@@ -1333,9 +1359,11 @@ verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock,
 }
 
 enum sec_status 
-dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve,
+dnskey_verify_rrset_sig(struct region* region, ldns_buffer* buf, 
+       struct val_env* ve,
         struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
-               size_t dnskey_idx, size_t sig_idx)
+        size_t dnskey_idx, size_t sig_idx,
+       struct rbtree_t** sortree, int* buf_canon)
 {
        enum sec_status sec;
        uint8_t* sig;           /* RRSIG rdata */
@@ -1420,11 +1448,15 @@ dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve,
                return sec_status_bogus;
        }
 
-       /* create rrset canonical format in buffer, ready for signature */
-       if(!rrset_canonical(env->scratch, env->scratch_buffer, rrset, sig+2, 
-               18 + signer_len)) {
-               log_err("verify: failed due to alloc error");
-               return sec_status_unchecked;
+       if(!*buf_canon) {
+               /* create rrset canonical format in buffer, ready for 
+                * signature */
+               if(!rrset_canonical(region, buf, rrset, sig+2, 
+                       18 + signer_len, sortree)) {
+                       log_err("verify: failed due to alloc error");
+                       return sec_status_unchecked;
+               }
+               *buf_canon = 1;
        }
 
        /* check that dnskey is available */
@@ -1435,7 +1467,7 @@ dnskey_verify_rrset_sig(struct module_env* env, struct val_env* ve,
        }
 
        /* verify */
-       sec = verify_canonrrset(env->scratch_buffer, (int)sig[2+2],
+       sec = verify_canonrrset(buf, (int)sig[2+2],
                sigblock, sigblock_len, key, keylen);
 
        /* check if TTL is too high - reduce if so */
index cbaff26047cbe36e402eb00794b2b013bc85e2ee..6fe0705443a88169d1808eb007e740baacb2e796 100644 (file)
@@ -47,6 +47,8 @@ struct val_env;
 struct module_env;
 struct ub_packed_rrset_key;
 enum sec_status;
+struct rbtree_t;
+struct region;
 
 /** 
  * Check if dnskey matches a DS digest 
@@ -165,26 +167,38 @@ enum sec_status dnskey_verify_rrset(struct module_env* env,
  * @param rrset: to be validated.
  * @param dnskey: DNSKEY rrset, keyset to try.
  * @param sig_idx: which signature to try to validate.
+ * @param sortree: reused sorted order. Stored in region. Pass NULL at start,
+ *     and for a new rrset.
  * @return secure if any key signs *this* signature. bogus if no key signs it,
  *     or unchecked on error.
  */
 enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, 
        struct val_env* ve, struct ub_packed_rrset_key* rrset, 
-       struct ub_packed_rrset_key* dnskey, size_t sig_idx);
+       struct ub_packed_rrset_key* dnskey, size_t sig_idx, 
+       struct rbtree_t** sortree);
 
 /** 
  * verify rrset, with specific dnskey(from set), for a specific rrsig 
+ * @param region: scratch region used for temporary allocation.
+ * @param buf: scratch buffer used for canonicalized rrset data.
  * @param env: module environment, scratch space is used.
  * @param ve: validator environment, date settings.
  * @param rrset: to be validated.
  * @param dnskey: DNSKEY rrset, keyset.
  * @param dnskey_idx: which key from the rrset to try.
  * @param sig_idx: which signature to try to validate.
+ * @param sortree: pass NULL at start, the sorted rrset order is returned.
+ *     pass it again for the same rrset.
+ * @param buf_canon: if true, the buffer is already canonical.
+ *     pass false at start. pass old value only for same rrset and same
+ *     signature (but perhaps different key) for reuse.
  * @return secure if this key signs this signature. unchecked on error or 
  *     bogus if it did not validate.
  */
-enum sec_status dnskey_verify_rrset_sig(struct module_env* env, 
-       struct val_env* ve, struct ub_packed_rrset_key* rrset, 
-       struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx);
+enum sec_status dnskey_verify_rrset_sig(struct region* region, 
+       ldns_buffer* buf, struct val_env* ve, 
+       struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, 
+       size_t dnskey_idx, size_t sig_idx,
+       struct rbtree_t** sortree, int* buf_canon);
 
 #endif /* VALIDATOR_VAL_SIGCRYPT_H */
index 33b9c13282d1ff8a91e493462d0d385dbd51c50d..239fc02bd307dfd69cf00382a4952ce78459290a 100644 (file)
@@ -50,6 +50,7 @@
 #include "util/data/dname.h"
 #include "util/net_help.h"
 #include "util/module.h"
+#include "util/region-allocator.h"
 
 enum val_classification 
 val_classify_response(uint16_t query_flags, struct query_info* qinf, 
@@ -298,6 +299,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
                ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
        sec = dnskeyset_verify_rrset(env, ve, rrset, keys);
        verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
+       region_free_all(env->scratch);
 
        /* update rrset security status 
         * only improves security status