From 188bfacd05de3dc3b51ff2cbc3ef225d194eb11d Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Mon, 6 Aug 2007 12:57:29 +0000 Subject: [PATCH] validator work. git-svn-id: file:///svn/unbound/trunk@493 be551aaa-1e26-0410-a405-d3ace91eadb9 --- validator/val_kentry.c | 7 ++ validator/val_kentry.h | 7 ++ validator/val_utils.c | 174 +++++++++++++++++++++++++++++++++++++++++ validator/val_utils.h | 90 +++++++++++++++++++++ validator/validator.c | 134 ++++++++++++++++++++++++++++++- validator/validator.h | 27 +++++++ 6 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 validator/val_utils.c create mode 100644 validator/val_utils.h diff --git a/validator/val_kentry.c b/validator/val_kentry.c index 7096453a..2d833775 100644 --- a/validator/val_kentry.c +++ b/validator/val_kentry.c @@ -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); +} diff --git a/validator/val_kentry.h b/validator/val_kentry.h index 9c1fb9a5..74bb06f7 100644 --- a/validator/val_kentry.h +++ b/validator/val_kentry.h @@ -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 index 00000000..093ec533 --- /dev/null +++ b/validator/val_utils.c @@ -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; ian_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; ian_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 index 00000000..31f9f721 --- /dev/null +++ b/validator/val_utils.h @@ -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 */ diff --git a/validator/validator.c b/validator/validator.c index ccaad4ee..f14b4933 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -43,7 +43,10 @@ #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" @@ -140,19 +143,141 @@ val_new(struct module_qstate* qstate, int id) } /** - * Process init state for validator. + * 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. - * @return true to continue processing + * @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 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 */ diff --git a/validator/validator.h b/validator/validator.h index e0e3039c..0b6fdcc4 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -44,8 +44,10 @@ #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; }; /**