validator work.

git-svn-id: file:///svn/unbound/trunk@493 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-08-06 12:57:29 +00:00
parent 4eaa855db9
commit 188bfacd05
6 changed files with 436 additions and 3 deletions

View file

@ -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);
}

View file

@ -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 */

174
validator/val_utils.c Normal file
View file

@ -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; i<rep->an_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; i<rep->an_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;
}
}

90
validator/val_utils.h Normal file
View file

@ -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 */

View file

@ -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)
{
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 */

View file

@ -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;
};
/**