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)) {
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,
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.
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);
}
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.
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
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;
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;
+}
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.
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,
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,
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;
/* 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;
* @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.
/**
* 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. */
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];
};
#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 */
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;
}
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);
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;
* (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;
}
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;
}
* 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;
}
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
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);
*/
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;