}
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);
+}
*/
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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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 */
#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"
return vq;
}
+/**
+ * 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.
+ * @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 to continue processing
+ * @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;
}
/**
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 */
#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.
* 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;
};
/**