]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Returns and caches validated replies.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 21 Aug 2007 13:12:10 +0000 (13:12 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 21 Aug 2007 13:12:10 +0000 (13:12 +0000)
git-svn-id: file:///svn/unbound/trunk@536 be551aaa-1e26-0410-a405-d3ace91eadb9

15 files changed:
daemon/daemon.c
daemon/worker.c
doc/Changelog
iterator/iter_utils.c
services/cache/dns.c
services/cache/dns.h
services/mesh.c
util/data/msgencode.c
util/data/msgencode.h
util/data/packed_rrset.h
util/module.h
util/net_help.h
validator/val_utils.c
validator/validator.c
validator/validator.h

index 3ed1f78a165d887eca4cbf39b15781c1f3830095..944bd7da5f29ea940d71ee290dbdf373cf2db8d3 100644 (file)
@@ -271,6 +271,7 @@ static void daemon_setup_modules(struct daemon* daemon)
        daemon->env->worker = NULL;
        daemon->env->send_packet = &worker_send_packet;
        daemon->env->send_query = &worker_send_query;
+       daemon->env->need_to_validate = 0; /* set by module init below */
        for(i=0; i<daemon->num_modules; i++) {
                log_info("init module %d: %s", i, daemon->modfunc[i]->name);
                if(!(*daemon->modfunc[i]->init)(daemon->env, i)) {
index 209311c825d87351f8d53ab80eb5658b2228c3e5..d3cd0582651213b9c04122bcb56de9f7a91ac009 100644 (file)
@@ -327,6 +327,18 @@ check_cache_chain(struct reply_info* rep) {
        return 1;
 }
 
+/** check security status in cache reply */
+static int
+all_rrsets_secure(struct reply_info* rep) {
+       size_t i;
+       for(i=0; i<rep->rrset_count; i++) {
+               if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
+                       ->security != sec_status_secure )
+                       return 0;
+       }
+       return 1;
+}
+
 /** answer query from the cache */
 static int
 answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
@@ -336,6 +348,8 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
        struct reply_info* rep = (struct reply_info*)e->data;
        uint32_t timenow = time(0);
        uint16_t udpsize = edns->udp_size;
+       int secure;
+       int must_validate = !(flags&BIT_CD) && worker->env.need_to_validate;
        /* see if it is possible */
        if(rep->ttl <= timenow) {
                /* the rrsets may have been updated in the meantime.
@@ -356,15 +370,44 @@ answer_from_cache(struct worker* worker, struct lruhash_entry* e, uint16_t id,
                htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type == 
                htons(LDNS_RR_TYPE_DNAME))) {
                if(!check_cache_chain(rep)) {
+                       /* cname chain invalid, redo iterator steps */
+                       verbose(VERB_ALGO, "Cache reply: cname chain broken");
+               bail_out:
                        rrset_array_unlock_touch(worker->env.rrset_cache, 
                                worker->scratchpad, rep->ref, rep->rrset_count);
                        region_free_all(worker->scratchpad);
                        return 0;
                }
        }
+       /* check security status of the cached answer */
+       if( rep->security == sec_status_bogus && must_validate) {
+               /* BAD cached */
+               error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, 
+                       &mrentry->key, id, flags, edns);
+               rrset_array_unlock_touch(worker->env.rrset_cache, 
+                       worker->scratchpad, rep->ref, rep->rrset_count);
+               region_free_all(worker->scratchpad);
+               return 1;
+       } else if( rep->security == sec_status_unchecked && must_validate) {
+               verbose(VERB_ALGO, "Cache reply: unchecked entry needs "
+                       "validation");
+               goto bail_out; /* need to validate cache entry first */
+       } else if(rep->security == sec_status_secure) {
+               if(all_rrsets_secure(rep))
+                       secure = 1;
+               else    {
+                       if(must_validate) {
+                               verbose(VERB_ALGO, "Cache reply: secure entry"
+                                       " changed status");
+                               goto bail_out; /* rrset changed, re-verify */
+                       }
+                       secure = 0;
+               }
+       } else  secure = 0;
+
        if(!reply_info_answer_encode(&mrentry->key, rep, id, flags, 
                repinfo->c->buffer, timenow, 1, worker->scratchpad,
-               udpsize, edns, (int)(edns->bits & EDNS_DO) )) {
+               udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
                error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, 
                        &mrentry->key, id, flags, edns);
        }
index 325927ee1cf12537ec85e58bee651690beb4b2ec..7a23907b685d32f27f33d9ed6887c2ef9c801ef8 100644 (file)
@@ -1,5 +1,9 @@
 21 August 2007: Wouter
        - ANY response validation.
+       - store security status in cache.
+       - check cache security status and either send the query to be
+         validated, return the query to client, or send servfail to client.
+         Sets AD bit on validated replies.
 
 20 August 2007: Wouter
        - validate and positive validation, positive wildcard NSEC validation.
index 38cc92b969373ca7185b3e6095eb3e46d497d439..a77bed4b15afd94897ad233217d2c3e23f49938b 100644 (file)
@@ -296,51 +296,7 @@ int
 iter_dns_store(struct module_env* env, struct query_info* msgqinf,
        struct reply_info* msgrep, int is_referral)
 {
-       struct reply_info* rep = NULL;
-       /* alloc, malloc properly (not in region, like msg is) */
-       rep = reply_info_copy(msgrep, env->alloc, NULL);
-       if(!rep)
-               return 0;
-
-       if(is_referral) {
-               /* store rrsets */
-               struct rrset_ref ref;
-               uint32_t now = time(NULL);
-               size_t i;
-               for(i=0; i<rep->rrset_count; i++) {
-                       packed_rrset_ttl_add((struct packed_rrset_data*)
-                               rep->rrsets[i]->entry.data, now);
-                       ref.key = rep->rrsets[i];
-                       ref.id = rep->rrsets[i]->id;
-                       /*ignore ret: it was in the cache, ref updated */
-                       (void)rrset_cache_update(env->rrset_cache, &ref, 
-                               env->alloc, now);
-               }
-               free(rep);
-               return 1;
-       } else {
-               /* store msg, and rrsets */
-               struct query_info qinf;
-               hashvalue_t h;
-
-               qinf = *msgqinf;
-               qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
-               if(!qinf.qname) {
-                       reply_info_parsedelete(rep, env->alloc);
-                       return 0;
-               }
-               /* fixup flags to be sensible for a reply based on the cache */
-               /* this module means that RA is available. It is an answer QR. 
-                * Not AA from cache. Not CD in cache (depends on client bit). */
-               rep->flags |= (BIT_RA | BIT_QR);
-               rep->flags &= ~(BIT_AA | BIT_CD);
-               h = query_info_hash(&qinf);
-               dns_cache_store_msg(env, &qinf, h, rep);
-               /* qname is used inside query_info_entrysetup, and set to 
-                * NULL. If it has not been used, free it. free(0) is safe. */
-               free(qinf.qname);
-       }
-       return 1;
+       return dns_cache_store(env, msgqinf, msgrep, is_referral);
 }
 
 int 
index 00da8e0ee51e17eae4e646649612a4ea7c6de9d7..cacc33ddde29418c9b894ad52532c0be7992d775 100644 (file)
@@ -399,6 +399,7 @@ tomsg(struct module_env* env, struct msgreply_entry* e, struct reply_info* r,
        msg->rep->flags = r->flags;
        msg->rep->qdcount = r->qdcount;
        msg->rep->ttl = r->ttl;
+       msg->rep->security = r->security;
        msg->rep->an_numrrsets = r->an_numrrsets;
        msg->rep->ns_numrrsets = r->ns_numrrsets;
        msg->rep->ar_numrrsets = r->ar_numrrsets;
@@ -585,3 +586,54 @@ dns_cache_lookup(struct module_env* env,
 
        return NULL;
 }
+
+int 
+dns_cache_store(struct module_env* env, struct query_info* msgqinf,
+        struct reply_info* msgrep, int is_referral)
+{
+       struct reply_info* rep = NULL;
+       /* alloc, malloc properly (not in region, like msg is) */
+       rep = reply_info_copy(msgrep, env->alloc, NULL);
+       if(!rep)
+               return 0;
+
+       if(is_referral) {
+               /* store rrsets */
+               struct rrset_ref ref;
+               uint32_t now = time(NULL);
+               size_t i;
+               for(i=0; i<rep->rrset_count; i++) {
+                       packed_rrset_ttl_add((struct packed_rrset_data*)
+                               rep->rrsets[i]->entry.data, now);
+                       ref.key = rep->rrsets[i];
+                       ref.id = rep->rrsets[i]->id;
+                       /*ignore ret: it was in the cache, ref updated */
+                       (void)rrset_cache_update(env->rrset_cache, &ref, 
+                               env->alloc, now);
+               }
+               free(rep);
+               return 1;
+       } else {
+               /* store msg, and rrsets */
+               struct query_info qinf;
+               hashvalue_t h;
+
+               qinf = *msgqinf;
+               qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len);
+               if(!qinf.qname) {
+                       reply_info_parsedelete(rep, env->alloc);
+                       return 0;
+               }
+               /* fixup flags to be sensible for a reply based on the cache */
+               /* this module means that RA is available. It is an answer QR. 
+                * Not AA from cache. Not CD in cache (depends on client bit). */
+               rep->flags |= (BIT_RA | BIT_QR);
+               rep->flags &= ~(BIT_AA | BIT_CD);
+               h = query_info_hash(&qinf);
+               dns_cache_store_msg(env, &qinf, h, rep);
+               /* qname is used inside query_info_entrysetup, and set to 
+                * NULL. If it has not been used, free it. free(0) is safe. */
+               free(qinf.qname);
+       }
+       return 1;
+}
index a9849d954ae9b432008a7297fd3f553edee0a67d..d694a86635fbaf7375c03b40aa8ed50618894cae 100644 (file)
@@ -59,6 +59,24 @@ struct dns_msg {
        struct reply_info *rep;
 };
 
+/**
+ * Allocate a dns_msg with malloc/alloc structure and store in dns cache.
+ *
+ * @param env: environment, with alloc structure and dns cache.
+ * @param qinf: query info, the query for which answer is stored.
+ *     this is allocated in a region, and will be copied to malloc area
+ *     before insertion.
+ * @param rep: reply in dns_msg from dns_alloc_msg for example.
+ *     this is allocated in a region, and will be copied to malloc area
+ *     before insertion.
+ * @param is_referral: If true, then the given message to be stored is a
+ *      referral. The cache implementation may use this as a hint.
+ *      It will store only the RRsets, not the message.
+ * @return 0 on alloc error (out of memory).
+ */
+int dns_cache_store(struct module_env* env, struct query_info* qinf,
+        struct reply_info* rep, int is_referral); 
+
 /**
  * Store message in the cache. Stores in message cache and rrset cache.
  * Both qinfo and rep should be malloced and are put in the cache.
index 6c085207ac9b76abf6dec83941071c05f15b3490..2adc732c1350c83c4d141614a30c5fd2ecd738a0 100644 (file)
@@ -425,6 +425,15 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
        struct mesh_reply* r)
 {
        struct timeval end_time;
+       int secure;
+       /* examine security status */
+       if(m->s.env->need_to_validate && !(r->qflags&BIT_CD) &&
+               rep->security <= sec_status_bogus) {
+               rcode = LDNS_RCODE_SERVFAIL;
+       }
+       if(rep->security == sec_status_secure)
+               secure = 1;
+       else    secure = 0;
        /* send the reply */
        if(rcode) {
                error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo,
@@ -439,7 +448,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
                if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, 
                        r->qflags, r->query_reply.c->buffer, 0, 1, 
                        m->s.env->scratch, udp_size, &r->edns, 
-                       (int)(r->edns.bits & EDNS_DO))) 
+                       (int)(r->edns.bits & EDNS_DO), secure)) 
                {
                        error_encode(r->query_reply.c->buffer, 
                                LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, 
index 6db5619ce8fac687cf275a75e7c4047594a9a189..a17653d733f1751416ef6560478efdadff8886b1 100644 (file)
@@ -706,7 +706,7 @@ int
 reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, 
        uint16_t id, uint16_t qflags, ldns_buffer* pkt, uint32_t timenow,
        int cached, struct region* region, uint16_t udpsize, 
-       struct edns_data* edns, int dnssec)
+       struct edns_data* edns, int dnssec, int secure)
 {
        uint16_t flags;
        int attach_edns = 1;
@@ -718,6 +718,8 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
                /* remove AA bit, copy RD and CD bits from query. */
                flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD)); 
        }
+       if(secure)
+               flags |= BIT_AD;
        log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
        if(udpsize < LDNS_HEADER_SIZE)
                return 0;
index 11fae6be11abfeea3f5604f0b28638741aaeaacd..3040c235c3fca152e80b16d5f1ec3d5c62c81bb7 100644 (file)
@@ -62,12 +62,13 @@ struct edns_data;
  * @param edns: EDNS data included in the answer, NULL for none.
  *     or if edns_present = 0, it is not included.
  * @param dnssec: if 0 DNSSEC records are omitted from the answer.
+ * @param secure: if 1, the AD bit is set in the reply.
  * @return: 0 on error (server failure).
  */
 int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, 
        uint16_t id, uint16_t qflags, ldns_buffer* dest, uint32_t timenow,
        int cached, struct region* region, uint16_t udpsize, 
-       struct edns_data* edns, int dnssec);
+       struct edns_data* edns, int dnssec, int secure);
 
 /**
  * Regenerate the wireformat from the stored msg reply.
index bff8bec537a93741127b326859a449928d4fdf71..f0005277959d4ba96807707937d66b97606fd620 100644 (file)
@@ -151,6 +151,7 @@ enum rrset_trust {
 
 /**
  * Security status from validation for data.
+ * The order is significant; more secure, more proven later.
  */
 enum sec_status {
        /** UNCHECKED means that object has yet to be validated. */
index 2d378ead1474980a021b75a387abc7fc74f4068c..1115d36df063b59cbc5aea9589ca0385275e9453 100644 (file)
@@ -191,6 +191,9 @@ struct module_env {
        struct alloc_cache* alloc;
        /** random table to generate random numbers */
        struct ub_randstate* rnd;
+       /** is validation required for messages, controls client-facing
+        * validation status (AD bits) and servfails */
+       int need_to_validate;
        /** module specific data. indexed by module id. */
        void* modinfo[MAX_MODULE];
 };
index 6c7eff6ef6aea749fe264e8fd42229e5885bcdce..c2584450267d6c54f0ae3ae24f3a33d39a781e88 100644 (file)
 #define NET_HELP_H
 #include "util/log.h"
 
-/** DNS constants for uint16_t style flag manipulation. host byteorder. */
-/** AA flag */
-#define BIT_AA 0x0400
-/** RD flag */
-#define BIT_RD 0x0100
-/** RA flag */
-#define BIT_RA 0x0080
+/** DNS constants for uint16_t style flag manipulation. host byteorder. 
+ *                                1  1  1  1  1  1
+ *  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * |QR|   Opcode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ */ 
 /** CD flag */
 #define BIT_CD 0x0010
+/** AD flag */
+#define BIT_AD 0x0020
+/** RA flag */
+#define BIT_RA 0x0080
+/** RD flag */
+#define BIT_RD 0x0100
+/** TC flag */
+#define BIT_TC 0x0200
+/** AA flag */
+#define BIT_AA 0x0400
 /** QR flag */
 #define BIT_QR 0x8000
 /** get RCODE bits from uint16 flags */
index a080d359ea0aabf8f9434c100442f4a7ed3877eb..9a7d6439f02365453dd3a42adecbca3a9f22ae4e 100644 (file)
@@ -200,12 +200,27 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
         struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys)
 {
        enum sec_status sec;
+       struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
+               entry.data;
+       if(d->security == sec_status_secure) {
+               /* re-verify all other statuses, because keyset may change*/
+               log_nametypeclass(VERB_ALGO, "verify rrset cached", 
+                       rrset->rk.dname, ntohs(rrset->rk.type), 
+                       ntohs(rrset->rk.rrset_class));
+               return d->security;
+       }
        log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
                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));
 
-       /* TODO: update rrset security status */
+       /* update rrset security status 
+        * only improves security status */
+       if(sec > d->security) {
+               d->security = sec;
+               if(sec == sec_status_secure)
+                       d->trust = rrset_trust_validated;
+       }
 
        return sec;
 }
@@ -307,8 +322,6 @@ val_verify_new_DNSKEYs(struct region* region, struct module_env* env,
                        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);
index 60de3298fd135388dd36fcabe5e37c88c50a90e3..c662eea466c0f56bfa81ca61fa5bfca0d03800e9 100644 (file)
@@ -89,6 +89,7 @@ val_init(struct module_env* env, int id)
                return 0;
        }
        env->modinfo[id] = (void*)val_env;
+       env->need_to_validate = 1;
        if(!val_apply_cfg(val_env, env->cfg)) {
                log_err("validator: could not apply configuration settings.");
                return 0;
@@ -300,6 +301,12 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
                 * (unless qtype=DNAME). */
                if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
                        dname_seen = 0;
+                       /* CNAME was synthesized by our own iterator */
+                       /* since the DNAME verified, mark the CNAME as secure */
+                       ((struct packed_rrset_data*)s->entry.data)->security =
+                               sec_status_secure;
+                       ((struct packed_rrset_data*)s->entry.data)->trust =
+                               rrset_trust_validated;
                        continue;
                }
 
@@ -665,14 +672,18 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
        uint8_t* lookup_name;
        size_t lookup_len;
        if(!needs_validation(qstate, vq)) {
-               vq->state = vq->final_state;
-               return 1;
+               qstate->return_rcode = LDNS_RCODE_NOERROR;
+               qstate->return_msg = vq->orig_msg;
+               qstate->ext_state[id] = module_finished;
+               return 0;
        }
        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;
+               vq->chase_reply->security = sec_status_indeterminate;
+               /* go to finished state to cache this result */
+               vq->state = VAL_FINISHED_STATE;
                return 1;
        }
 
@@ -707,7 +718,8 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
                 * However, we do set the status to INSECURE, since it is 
                 * essentially proven insecure. */
                vq->chase_reply->security = sec_status_insecure;
-               vq->state = vq->final_state;
+               /* go to finished state to cache this result */
+               vq->state = VAL_FINISHED_STATE;
                return 1;
        }
 
@@ -911,6 +923,31 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
        return 1;
 }
 
+/**
+ * The Finished state. The validation status (good or bad) has been determined.
+ *
+ * @param qstate: query state.
+ * @param vq: validator query state.
+ * @param id: module id.
+ * @return true if the event should be processed further on return, false if
+ *         not.
+ */
+static int
+processFinished(struct module_qstate* qstate, struct val_qstate* vq, int id)
+{
+       /* TODO CNAME query restarts */
+
+       /* store results in cache */
+       if(!dns_cache_store(qstate->env, &vq->qchase, vq->chase_reply, 0)) {
+               log_err("out of memory caching validator results");
+       }
+       qstate->return_rcode = LDNS_RCODE_NOERROR;
+       qstate->return_msg = vq->orig_msg;
+       qstate->return_msg->rep = vq->chase_reply;
+       qstate->ext_state[id] = module_finished;
+       return 0;
+}
+
 /** 
  * Handle validator state.
  * If a method returns true, the next state is started. If false, then
@@ -939,6 +976,8 @@ val_handle(struct module_qstate* qstate, struct val_qstate* vq,
                                cont = processValidate(qstate, vq, ve, id);
                                break;
                        case VAL_FINISHED_STATE: 
+                               cont = processFinished(qstate, vq, id);
+                               break;
                        default:
                                log_warn("validator: invalid state %d",
                                        vq->state);
index 726293a03a06f6a6b719340422f098c563afd215..d191188847a922c38dd1cdc87a5657c9161cce86 100644 (file)
@@ -112,9 +112,6 @@ struct val_qstate {
         */
        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;