mirror of
https://github.com/mollyim/unbound.git
synced 2025-05-20 00:57:55 +01:00
validator work.
git-svn-id: file:///svn/unbound/trunk@493 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
4eaa855db9
commit
188bfacd05
6 changed files with 436 additions and 3 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
174
validator/val_utils.c
Normal 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
90
validator/val_utils.h
Normal 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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue