mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
DLV work
git-svn-id: file:///svn/unbound/trunk@1190 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
44f5800da9
commit
7549bddfda
6 changed files with 443 additions and 12 deletions
|
|
@ -1,3 +1,6 @@
|
||||||
|
14 August 2008: Wouter
|
||||||
|
- synthesize DLV messages from the rrset cache, like done for DS.
|
||||||
|
|
||||||
13 August 2008: Wouter
|
13 August 2008: Wouter
|
||||||
- bug #203: nicer do-auto log message when user sets incompatible
|
- bug #203: nicer do-auto log message when user sets incompatible
|
||||||
options.
|
options.
|
||||||
|
|
|
||||||
5
services/cache/dns.c
vendored
5
services/cache/dns.c
vendored
|
|
@ -595,8 +595,9 @@ dns_cache_lookup(struct module_env* env,
|
||||||
lock_rw_unlock(&rrset->entry.lock);
|
lock_rw_unlock(&rrset->entry.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* construct DS, DNSKEY messages from rrset cache. */
|
/* construct DS, DNSKEY, DLV messages from rrset cache. */
|
||||||
if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY) &&
|
if((qtype == LDNS_RR_TYPE_DS || qtype == LDNS_RR_TYPE_DNSKEY ||
|
||||||
|
qtype == LDNS_RR_TYPE_DLV) &&
|
||||||
(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
(rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen,
|
||||||
qtype, qclass, 0, now, 0))) {
|
qtype, qclass, 0, now, 0))) {
|
||||||
/* if the rrset is from the additional section, and the
|
/* if the rrset is from the additional section, and the
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
#include "validator/val_utils.h"
|
#include "validator/val_utils.h"
|
||||||
#include "util/data/msgreply.h"
|
#include "util/data/msgreply.h"
|
||||||
#include "util/data/dname.h"
|
#include "util/data/dname.h"
|
||||||
|
#include "util/net_help.h"
|
||||||
|
|
||||||
/** get ttl of rrset */
|
/** get ttl of rrset */
|
||||||
static uint32_t
|
static uint32_t
|
||||||
|
|
@ -479,3 +480,79 @@ val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closest NONEMPTY encloser.
|
||||||
|
* Thus, no empty nonterminals are returned.
|
||||||
|
* @param qname: query name
|
||||||
|
* @param nsec: nsec record.
|
||||||
|
* @return the name (part of qname).
|
||||||
|
*/
|
||||||
|
static uint8_t*
|
||||||
|
nsec_closest_nonempty(uint8_t* qname, struct ub_packed_rrset_key* nsec)
|
||||||
|
{
|
||||||
|
uint8_t* next;
|
||||||
|
size_t nlen;
|
||||||
|
uint8_t* common1, *common2;
|
||||||
|
if(!nsec_get_next(nsec, &next, &nlen))
|
||||||
|
return NULL;
|
||||||
|
/* shortest common with owner or next name */
|
||||||
|
common1 = dname_get_shared_topdomain(qname, nsec->rk.dname);
|
||||||
|
common2 = dname_get_shared_topdomain(qname, next);
|
||||||
|
if(dname_count_labels(common1) < dname_count_labels(common2))
|
||||||
|
return common1;
|
||||||
|
return common2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val_nsec_check_dlv(struct query_info* qinfo,
|
||||||
|
struct reply_info* rep, uint8_t** nm, size_t* nm_len)
|
||||||
|
{
|
||||||
|
uint8_t* next;
|
||||||
|
size_t i, nlen;
|
||||||
|
int c;
|
||||||
|
/* we should now have a NOERROR/NODATA or NXDOMAIN message */
|
||||||
|
if(rep->an_numrrsets != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* is this NOERROR ? */
|
||||||
|
if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
|
||||||
|
/* it can be a plain NSEC match - go up one more level. */
|
||||||
|
/* or its an empty nonterminal - go up to nonempty level */
|
||||||
|
for(i=0; i<rep->ns_numrrsets; i++) {
|
||||||
|
if(!nsec_get_next(rep->rrsets[i], &next, &nlen))
|
||||||
|
continue;
|
||||||
|
c = dname_canonical_compare(
|
||||||
|
rep->rrsets[i]->rk.dname, qinfo->qname);
|
||||||
|
if(c == 0) {
|
||||||
|
/* plain match */
|
||||||
|
dname_remove_label(nm, nm_len);
|
||||||
|
return 1;
|
||||||
|
} else if(c < 0 &&
|
||||||
|
dname_strict_subdomain_c(next, qinfo->qname)) {
|
||||||
|
/* ENT */
|
||||||
|
*nm = nsec_closest_nonempty(
|
||||||
|
*nm, rep->rrsets[i]);
|
||||||
|
if(!*nm) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is this NXDOMAIN ? */
|
||||||
|
if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN) {
|
||||||
|
/* find the qname denial NSEC record. It can tell us
|
||||||
|
* a closest encloser name; or that we not need bother */
|
||||||
|
for(i=0; i<rep->ns_numrrsets; i++) {
|
||||||
|
if(val_nsec_proves_name_error(rep->rrsets[i],
|
||||||
|
qinfo->qname)) {
|
||||||
|
*nm = nsec_closest_nonempty(
|
||||||
|
*nm, rep->rrsets[i]);
|
||||||
|
if(!*nm) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,4 +145,17 @@ uint8_t* nsec_closest_encloser(uint8_t* qname,
|
||||||
int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
|
int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
|
||||||
size_t qnamelen);
|
size_t qnamelen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the DLV result, what to do with NSEC DLV reply.
|
||||||
|
* @param qinfo: what was queried for.
|
||||||
|
* @param rep: the nonpositive reply.
|
||||||
|
* @param nm: dlv lookup name, to adjust for new lookup name (if needed).
|
||||||
|
* @param nm_len: length of lookup name.
|
||||||
|
* @return 0 on error, 1 if a higher point is found.
|
||||||
|
* If the higher point is above the dlv repo anchor, the qname does
|
||||||
|
* not exist.
|
||||||
|
*/
|
||||||
|
int val_nsec_check_dlv(struct query_info* qinfo,
|
||||||
|
struct reply_info* rep, uint8_t** nm, size_t* nm_len);
|
||||||
|
|
||||||
#endif /* VALIDATOR_VAL_NSEC_H */
|
#endif /* VALIDATOR_VAL_NSEC_H */
|
||||||
|
|
|
||||||
|
|
@ -287,11 +287,12 @@ needs_validation(struct module_qstate* qstate, int ret_rc,
|
||||||
* @param namelen: length of name.
|
* @param namelen: length of name.
|
||||||
* @param qtype: query type.
|
* @param qtype: query type.
|
||||||
* @param qclass: query class.
|
* @param qclass: query class.
|
||||||
|
* @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
|
||||||
* @return false on alloc failure.
|
* @return false on alloc failure.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
generate_request(struct module_qstate* qstate, int id, uint8_t* name,
|
generate_request(struct module_qstate* qstate, int id, uint8_t* name,
|
||||||
size_t namelen, uint16_t qtype, uint16_t qclass)
|
size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
|
||||||
{
|
{
|
||||||
struct module_qstate* newq;
|
struct module_qstate* newq;
|
||||||
struct query_info ask;
|
struct query_info ask;
|
||||||
|
|
@ -302,7 +303,7 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name,
|
||||||
log_query_info(VERB_ALGO, "generate request", &ask);
|
log_query_info(VERB_ALGO, "generate request", &ask);
|
||||||
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
|
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
|
||||||
if(!(*qstate->env->attach_sub)(qstate, &ask,
|
if(!(*qstate->env->attach_sub)(qstate, &ask,
|
||||||
(uint16_t)(BIT_RD|BIT_CD), 0, &newq)){
|
(uint16_t)(BIT_RD|flags), 0, &newq)){
|
||||||
log_err("Could not generate request: out of memory");
|
log_err("Could not generate request: out of memory");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -328,7 +329,7 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
int id, struct trust_anchor* toprime)
|
int id, struct trust_anchor* toprime)
|
||||||
{
|
{
|
||||||
int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
|
int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
|
||||||
LDNS_RR_TYPE_DNSKEY, toprime->dclass);
|
LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD);
|
||||||
if(!ret) {
|
if(!ret) {
|
||||||
log_err("Could not prime trust anchor: out of memory");
|
log_err("Could not prime trust anchor: out of memory");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1303,7 +1304,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
|
||||||
vq->key_entry->name) != 0) {
|
vq->key_entry->name) != 0) {
|
||||||
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
|
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
|
||||||
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
|
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
|
||||||
vq->qchase.qclass)) {
|
vq->qchase.qclass, BIT_CD)) {
|
||||||
log_err("mem error generating DNSKEY request");
|
log_err("mem error generating DNSKEY request");
|
||||||
return val_error(qstate, id);
|
return val_error(qstate, id);
|
||||||
}
|
}
|
||||||
|
|
@ -1313,7 +1314,8 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
|
||||||
if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
|
if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
|
||||||
target_key_name) != 0) {
|
target_key_name) != 0) {
|
||||||
if(!generate_request(qstate, id, target_key_name,
|
if(!generate_request(qstate, id, target_key_name,
|
||||||
target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass)) {
|
target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
|
||||||
|
BIT_CD)) {
|
||||||
log_err("mem error generating DS request");
|
log_err("mem error generating DS request");
|
||||||
return val_error(qstate, id);
|
return val_error(qstate, id);
|
||||||
}
|
}
|
||||||
|
|
@ -1323,7 +1325,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
|
||||||
/* Otherwise, it is time to query for the DNSKEY */
|
/* Otherwise, it is time to query for the DNSKEY */
|
||||||
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
|
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
|
||||||
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
|
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
|
||||||
vq->qchase.qclass)) {
|
vq->qchase.qclass, BIT_CD)) {
|
||||||
log_err("mem error generating DNSKEY request");
|
log_err("mem error generating DNSKEY request");
|
||||||
return val_error(qstate, id);
|
return val_error(qstate, id);
|
||||||
}
|
}
|
||||||
|
|
@ -1472,6 +1474,130 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init DLV check.
|
||||||
|
* Called when a query is determined by other trust anchors to be insecure
|
||||||
|
* (or indeterminate). Then we look if there is a key in the DLV.
|
||||||
|
* Performs aggressive negative cache check to see if there is no key.
|
||||||
|
* Otherwise, spawns a DLV query, and changes to the DLV wait state.
|
||||||
|
*
|
||||||
|
* @param qstate: query state.
|
||||||
|
* @param vq: validator query state.
|
||||||
|
* @param ve: validator shared global environment.
|
||||||
|
* @param id: module id.
|
||||||
|
* @return true if there is no DLV.
|
||||||
|
* false: processing is finished for the validator operate().
|
||||||
|
* This function may exit in three ways:
|
||||||
|
* o no DLV (agressive cache), so insecure. (true)
|
||||||
|
* o error - stop processing (false)
|
||||||
|
* o DLV lookup was started, stop processing (false)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
val_dlv_init(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
|
struct val_env* ve, int id)
|
||||||
|
{
|
||||||
|
uint8_t* nm;
|
||||||
|
size_t nm_len;
|
||||||
|
/* there must be a DLV configured */
|
||||||
|
log_assert(qstate->env->anchors->dlv_anchor);
|
||||||
|
/* this bool is true to avoid looping in the DLV checks */
|
||||||
|
log_assert(vq->dlv_checked);
|
||||||
|
|
||||||
|
/* init the DLV lookup variables */
|
||||||
|
vq->dlv_lookup_name = NULL;
|
||||||
|
vq->dlv_lookup_name_len = 0;
|
||||||
|
vq->dlv_insecure_at = NULL;
|
||||||
|
vq->dlv_insecure_at_len = 0;
|
||||||
|
|
||||||
|
/* Determine the name for which we want to lookup DLV.
|
||||||
|
* This name is for the current message, or
|
||||||
|
* for the current RRset for CNAME, referral subtypes.
|
||||||
|
* If there is a signer, use that, otherwise the domain name */
|
||||||
|
if(vq->signer_name) {
|
||||||
|
nm = vq->signer_name;
|
||||||
|
nm_len = vq->signer_len;
|
||||||
|
} else {
|
||||||
|
/* use qchase */
|
||||||
|
nm = vq->qchase.qname;
|
||||||
|
nm_len = vq->qchase.qname_len;
|
||||||
|
}
|
||||||
|
log_nametypeclass(VERB_ALGO, "DLV init look", nm, LDNS_RR_TYPE_DS,
|
||||||
|
vq->qchase.qclass);
|
||||||
|
log_assert(nm && nm_len)
|
||||||
|
/* sanity check: no DLV lookups below the DLV anchor itself.
|
||||||
|
* Like, an securely insecure delegation there makes no sense. */
|
||||||
|
if(dname_subdomain_c(nm, qstate->env->anchors->dlv_anchor->name)) {
|
||||||
|
verbose(VERB_ALGO, "DLV lookup within DLV repository denied");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* concat name (minus root label) + dlv name */
|
||||||
|
vq->dlv_lookup_name_len = nm_len - 1 +
|
||||||
|
qstate->env->anchors->dlv_anchor->namelen;
|
||||||
|
vq->dlv_lookup_name = regional_alloc(qstate->region,
|
||||||
|
vq->dlv_lookup_name_len);
|
||||||
|
if(!vq->dlv_lookup_name) {
|
||||||
|
log_err("Out of memory preparing DLV lookup");
|
||||||
|
return val_error(qstate, id);
|
||||||
|
}
|
||||||
|
memmove(vq->dlv_lookup_name, nm, nm_len-1);
|
||||||
|
memmove(vq->dlv_lookup_name+nm_len-1,
|
||||||
|
qstate->env->anchors->dlv_anchor->name,
|
||||||
|
qstate->env->anchors->dlv_anchor->namelen);
|
||||||
|
log_nametypeclass(VERB_ALGO, "DLV name", vq->dlv_lookup_name,
|
||||||
|
LDNS_RR_TYPE_DLV, vq->qchase.qclass);
|
||||||
|
|
||||||
|
/* determine where the insecure point was determined, the DLV must
|
||||||
|
* be equal or below that to continue building the trust chain
|
||||||
|
* down. May be NULL if no trust chain was built yet */
|
||||||
|
nm = NULL;
|
||||||
|
if(vq->key_entry && key_entry_isnull(vq->key_entry)) {
|
||||||
|
nm = vq->key_entry->name;
|
||||||
|
nm_len = vq->key_entry->namelen;
|
||||||
|
}
|
||||||
|
if(nm) {
|
||||||
|
vq->dlv_insecure_at_len = nm_len - 1 +
|
||||||
|
qstate->env->anchors->dlv_anchor->namelen;
|
||||||
|
vq->dlv_insecure_at = regional_alloc(qstate->region,
|
||||||
|
vq->dlv_insecure_at_len);
|
||||||
|
if(!vq->dlv_insecure_at) {
|
||||||
|
log_err("Out of memory preparing DLV lookup");
|
||||||
|
return val_error(qstate, id);
|
||||||
|
}
|
||||||
|
memmove(vq->dlv_insecure_at, nm, nm_len-1);
|
||||||
|
memmove(vq->dlv_insecure_at+nm_len-1,
|
||||||
|
qstate->env->anchors->dlv_anchor->name,
|
||||||
|
qstate->env->anchors->dlv_anchor->namelen);
|
||||||
|
log_nametypeclass(VERB_ALGO, "insecure_at",
|
||||||
|
vq->dlv_insecure_at, 0, vq->qchase.qclass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we can find the name in the aggressive negative cache,
|
||||||
|
* give up; insecure is the answer */
|
||||||
|
/* lookup, several places in the tree may qualify
|
||||||
|
* between insecure_at and the lookup_name
|
||||||
|
* Check proof thoroughly
|
||||||
|
* TODO
|
||||||
|
* return 1;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* perform a lookup for the DLV; with validation */
|
||||||
|
vq->state = VAL_DLVLOOKUP_STATE;
|
||||||
|
if(!generate_request(qstate, id, vq->dlv_lookup_name,
|
||||||
|
vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV,
|
||||||
|
vq->qchase.qclass, 0)) {
|
||||||
|
return val_error(qstate, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the closest encloser DLV from the repository.
|
||||||
|
* then that is used to build another chain of trust
|
||||||
|
* This may first require a query 'too low' that has NSECs in
|
||||||
|
* the answer, from which we determine the closest encloser DLV.
|
||||||
|
* When determine the closest encloser, skip empty nonterminals,
|
||||||
|
* since we want a nonempty node in the DLV repository. */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Finished state. The validation status (good or bad) has been determined.
|
* The Finished state. The validation status (good or bad) has been determined.
|
||||||
*
|
*
|
||||||
|
|
@ -1490,6 +1616,18 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
qstate->query_flags, &qstate->qinfo, &vq->qchase,
|
qstate->query_flags, &qstate->qinfo, &vq->qchase,
|
||||||
vq->orig_msg->rep, vq->rrset_skip);
|
vq->orig_msg->rep, vq->rrset_skip);
|
||||||
|
|
||||||
|
/* if the result is insecure or indeterminate and we have not
|
||||||
|
* checked the DLV yet, check the DLV */
|
||||||
|
verbose(VERB_ALGO, "check for DLV");
|
||||||
|
verbose(VERB_ALGO, "sec %s", sec_status_to_string(vq->chase_reply->security));
|
||||||
|
if((vq->chase_reply->security == sec_status_insecure ||
|
||||||
|
vq->chase_reply->security == sec_status_indeterminate) &&
|
||||||
|
qstate->env->anchors->dlv_anchor && !vq->dlv_checked) {
|
||||||
|
vq->dlv_checked = 1;
|
||||||
|
if(!val_dlv_init(qstate, vq, ve, id))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* store overall validation result in orig_msg */
|
/* store overall validation result in orig_msg */
|
||||||
if(vq->rrset_skip == 0)
|
if(vq->rrset_skip == 0)
|
||||||
vq->orig_msg->rep->security = vq->chase_reply->security;
|
vq->orig_msg->rep->security = vq->chase_reply->security;
|
||||||
|
|
@ -1511,6 +1649,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
/* and restart for this rrset */
|
/* and restart for this rrset */
|
||||||
verbose(VERB_ALGO, "validator: go to next rrset");
|
verbose(VERB_ALGO, "validator: go to next rrset");
|
||||||
vq->chase_reply->security = sec_status_unchecked;
|
vq->chase_reply->security = sec_status_unchecked;
|
||||||
|
vq->dlv_checked = 0; /* can do DLV for this RR */
|
||||||
vq->state = VAL_INIT_STATE;
|
vq->state = VAL_INIT_STATE;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1528,6 +1667,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
log_query_info(VERB_ALGO, "validator: chased to",
|
log_query_info(VERB_ALGO, "validator: chased to",
|
||||||
&vq->qchase);
|
&vq->qchase);
|
||||||
vq->chase_reply->security = sec_status_unchecked;
|
vq->chase_reply->security = sec_status_unchecked;
|
||||||
|
vq->dlv_checked = 0; /* can do DLV for this RR */
|
||||||
vq->state = VAL_INIT_STATE;
|
vq->state = VAL_INIT_STATE;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1540,8 +1680,10 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
* that are not secure (if clean-additional option is set) */
|
* that are not secure (if clean-additional option is set) */
|
||||||
/* this may cause the msg to be marked bogus */
|
/* this may cause the msg to be marked bogus */
|
||||||
val_check_nonsecure(ve, vq->orig_msg->rep);
|
val_check_nonsecure(ve, vq->orig_msg->rep);
|
||||||
log_query_info(VERB_DETAIL, "validation success",
|
if(vq->orig_msg->rep->security == sec_status_secure) {
|
||||||
&qstate->qinfo);
|
log_query_info(VERB_DETAIL, "validation success",
|
||||||
|
&qstate->qinfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if the result is bogus - set message ttl to bogus ttl to avoid
|
/* if the result is bogus - set message ttl to bogus ttl to avoid
|
||||||
|
|
@ -1572,6 +1714,80 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DLVLookup state. Process DLV lookups.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
|
struct val_env* ve, int id)
|
||||||
|
{
|
||||||
|
/* see if this we are ready to continue normal resolution */
|
||||||
|
/* we may need more DLV lookups */
|
||||||
|
if(vq->dlv_status==dlv_error)
|
||||||
|
verbose(VERB_ALGO, "DLV woke up with status dlv_error");
|
||||||
|
else if(vq->dlv_status==dlv_success)
|
||||||
|
verbose(VERB_ALGO, "DLV woke up with status dlv_success");
|
||||||
|
else if(vq->dlv_status==dlv_ask_higher)
|
||||||
|
verbose(VERB_ALGO, "DLV woke up with status dlv_ask_higher");
|
||||||
|
else if(vq->dlv_status==dlv_there_is_no_dlv)
|
||||||
|
verbose(VERB_ALGO, "DLV woke up with status dlv_there_is_no_dlv");
|
||||||
|
else verbose(VERB_ALGO, "DLV woke up with status unknown");
|
||||||
|
log_nametypeclass(VERB_ALGO, "next look", vq->dlv_lookup_name,
|
||||||
|
LDNS_RR_TYPE_DLV, vq->qchase.qclass);
|
||||||
|
|
||||||
|
if(vq->dlv_status == dlv_error) {
|
||||||
|
verbose(VERB_QUERY, "failed DLV lookup");
|
||||||
|
return val_error(qstate, id);
|
||||||
|
} else if(vq->dlv_status == dlv_success) {
|
||||||
|
/* chain continues with DNSKEY, continue in FINDKEY */
|
||||||
|
vq->state = VAL_FINDKEY_STATE;
|
||||||
|
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
|
||||||
|
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
|
||||||
|
vq->qchase.qclass, BIT_CD)) {
|
||||||
|
log_err("mem error generating DNSKEY request");
|
||||||
|
return val_error(qstate, id);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else if(vq->dlv_status == dlv_there_is_no_dlv) {
|
||||||
|
/* continue with the insecure result we got */
|
||||||
|
vq->state = VAL_FINISHED_STATE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
log_assert(vq->dlv_status == dlv_ask_higher);
|
||||||
|
|
||||||
|
/* ask higher, make sure we stay in DLV repo, below dlv_at */
|
||||||
|
if(!dname_subdomain_c(vq->dlv_lookup_name,
|
||||||
|
qstate->env->anchors->dlv_anchor->name)) {
|
||||||
|
/* just like, there is no DLV */
|
||||||
|
verbose(VERB_ALGO, "ask above dlv repo");
|
||||||
|
vq->state = VAL_FINISHED_STATE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(vq->dlv_insecure_at && !dname_subdomain_c(vq->dlv_lookup_name,
|
||||||
|
vq->dlv_insecure_at)) {
|
||||||
|
/* already checked a chain lower than dlv_lookup_name */
|
||||||
|
verbose(VERB_ALGO, "ask above insecure endpoint");
|
||||||
|
log_nametypeclass(0, "enpt", vq->dlv_insecure_at, 0, 0);
|
||||||
|
vq->state = VAL_FINISHED_STATE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!generate_request(qstate, id, vq->dlv_lookup_name,
|
||||||
|
vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV,
|
||||||
|
vq->qchase.qclass, 0)) {
|
||||||
|
return val_error(qstate, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle validator state.
|
* Handle validator state.
|
||||||
* If a method returns true, the next state is started. If false, then
|
* If a method returns true, the next state is started. If false, then
|
||||||
|
|
@ -1602,6 +1818,9 @@ val_handle(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
case VAL_FINISHED_STATE:
|
case VAL_FINISHED_STATE:
|
||||||
cont = processFinished(qstate, vq, ve, id);
|
cont = processFinished(qstate, vq, ve, id);
|
||||||
break;
|
break;
|
||||||
|
case VAL_DLVLOOKUP_STATE:
|
||||||
|
cont = processDLVLookup(qstate, vq, ve, id);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
log_warn("validator: invalid state %d",
|
log_warn("validator: invalid state %d",
|
||||||
vq->state);
|
vq->state);
|
||||||
|
|
@ -2047,7 +2266,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
/* If good, we stay in the FINDKEY state. */
|
/* If good, we stay in the FINDKEY state. */
|
||||||
log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
|
log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process prime response
|
* Process prime response
|
||||||
* Sets the key entry in the state.
|
* Sets the key entry in the state.
|
||||||
|
|
@ -2075,6 +2294,96 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
/* the qstate will be reactivated after inform_super is done */
|
/* the qstate will be reactivated after inform_super is done */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process DLV response. Called from inform_supers.
|
||||||
|
* Because it is in inform_supers, the mesh itself is busy doing callbacks
|
||||||
|
* for a state that is to be deleted soon; don't touch the mesh; instead
|
||||||
|
* set a state in the super, as the super will be reactivated soon.
|
||||||
|
* Perform processing to determine what state to set in the super.
|
||||||
|
*
|
||||||
|
* @param qstate: query state that is validating and asked for a DLV.
|
||||||
|
* @param vq: validator query state
|
||||||
|
* @param id: module id.
|
||||||
|
* @param rcode: rcode result value.
|
||||||
|
* @param msg: result message (if rcode is OK).
|
||||||
|
* @param qinfo: from the sub query state, query info.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
process_dlv_response(struct module_qstate* qstate, struct val_qstate* vq,
|
||||||
|
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo)
|
||||||
|
{
|
||||||
|
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
|
||||||
|
|
||||||
|
verbose(VERB_ALGO, "process dlv response to super");
|
||||||
|
if(rcode != LDNS_RCODE_NOERROR) {
|
||||||
|
/* lookup failed, set in vq to give up */
|
||||||
|
vq->dlv_status = dlv_error;
|
||||||
|
verbose(VERB_ALGO, "response is error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(msg->rep->security != sec_status_secure) {
|
||||||
|
vq->dlv_status = dlv_error;
|
||||||
|
verbose(VERB_ALGO, "response is not secure");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* was the lookup a success? validated DLV? */
|
||||||
|
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NOERROR &&
|
||||||
|
msg->rep->an_numrrsets == 1 &&
|
||||||
|
msg->rep->security == sec_status_secure &&
|
||||||
|
ntohs(msg->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DLV &&
|
||||||
|
ntohs(msg->rep->rrsets[0]->rk.rrset_class) == qinfo->qclass &&
|
||||||
|
query_dname_compare(msg->rep->rrsets[0]->rk.dname,
|
||||||
|
vq->dlv_lookup_name) == 0) {
|
||||||
|
/* yay! it is just like a DS */
|
||||||
|
vq->ds_rrset = (struct ub_packed_rrset_key*)
|
||||||
|
regional_alloc_init(qstate->region,
|
||||||
|
msg->rep->rrsets[0], sizeof(*vq->ds_rrset));
|
||||||
|
if(!vq->ds_rrset) {
|
||||||
|
log_err("out of memory in process_dlv");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vq->ds_rrset->entry.key = vq->ds_rrset;
|
||||||
|
vq->ds_rrset->rk.dname = (uint8_t*)regional_alloc_init(
|
||||||
|
qstate->region, vq->ds_rrset->rk.dname,
|
||||||
|
vq->ds_rrset->rk.dname_len);
|
||||||
|
if(!vq->ds_rrset->rk.dname) {
|
||||||
|
log_err("out of memory in process_dlv");
|
||||||
|
vq->dlv_status = dlv_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vq->ds_rrset->entry.data = regional_alloc_init(qstate->region,
|
||||||
|
vq->ds_rrset->entry.data,
|
||||||
|
packed_rrset_sizeof(vq->ds_rrset->entry.data));
|
||||||
|
if(!vq->ds_rrset->entry.data) {
|
||||||
|
log_err("out of memory in process_dlv");
|
||||||
|
vq->dlv_status = dlv_error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
packed_rrset_ptr_fixup(vq->ds_rrset->entry.data);
|
||||||
|
/* make vq do a DNSKEY query next up */
|
||||||
|
vq->dlv_status = dlv_success;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* was the lookup a failure?
|
||||||
|
* if we have to go up into the DLV for a higher DLV anchor
|
||||||
|
* then set this in the vq, so it can make queries when activated.
|
||||||
|
* See if the NSECs indicate that we should look for higher DLV
|
||||||
|
* or, that there is no DLV securely */
|
||||||
|
if(!val_nsec_check_dlv(qinfo, msg->rep, &vq->dlv_lookup_name,
|
||||||
|
&vq->dlv_lookup_name_len)) {
|
||||||
|
vq->dlv_status = dlv_error;
|
||||||
|
verbose(VERB_ALGO, "nsec error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!dname_subdomain_c(vq->dlv_lookup_name,
|
||||||
|
qstate->env->anchors->dlv_anchor->name)) {
|
||||||
|
vq->dlv_status = dlv_there_is_no_dlv;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vq->dlv_status = dlv_ask_higher;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* inform validator super.
|
* inform validator super.
|
||||||
*
|
*
|
||||||
|
|
@ -2108,6 +2417,10 @@ val_inform_super(struct module_qstate* qstate, int id,
|
||||||
process_dnskey_response(super, vq, id, qstate->return_rcode,
|
process_dnskey_response(super, vq, id, qstate->return_rcode,
|
||||||
qstate->return_msg, &qstate->qinfo);
|
qstate->return_msg, &qstate->qinfo);
|
||||||
return;
|
return;
|
||||||
|
} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DLV) {
|
||||||
|
process_dlv_response(super, vq, id, qstate->return_rcode,
|
||||||
|
qstate->return_msg, &qstate->qinfo);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
log_err("internal error in validator: no inform_supers possible");
|
log_err("internal error in validator: no inform_supers possible");
|
||||||
}
|
}
|
||||||
|
|
@ -2155,6 +2468,7 @@ val_state_to_string(enum val_state state)
|
||||||
case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
|
case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
|
||||||
case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
|
case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
|
||||||
case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
|
case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
|
||||||
|
case VAL_DLVLOOKUP_STATE: return "VAL_DLVLOOKUP_STATE";
|
||||||
}
|
}
|
||||||
return "UNKNOWN VALIDATOR STATE";
|
return "UNKNOWN VALIDATOR STATE";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,9 @@ enum val_state {
|
||||||
/** validate the answer, using found key entry */
|
/** validate the answer, using found key entry */
|
||||||
VAL_VALIDATE_STATE,
|
VAL_VALIDATE_STATE,
|
||||||
/** finish up */
|
/** finish up */
|
||||||
VAL_FINISHED_STATE
|
VAL_FINISHED_STATE,
|
||||||
|
/** DLV lookup state, processing DLV queries */
|
||||||
|
VAL_DLVLOOKUP_STATE
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -184,6 +186,27 @@ struct val_qstate {
|
||||||
|
|
||||||
/** true if this state is waiting to prime a trust anchor */
|
/** true if this state is waiting to prime a trust anchor */
|
||||||
int wait_prime_ta;
|
int wait_prime_ta;
|
||||||
|
|
||||||
|
/** have we already checked the DLV? */
|
||||||
|
int dlv_checked;
|
||||||
|
/** The name for which the DLV is looked up. For the current message
|
||||||
|
* or for the current RRset (for CNAME, REFERRAL types).
|
||||||
|
* If there is signer name, that may be it, else a domain name */
|
||||||
|
uint8_t* dlv_lookup_name;
|
||||||
|
/** length of dlv lookup name */
|
||||||
|
size_t dlv_lookup_name_len;
|
||||||
|
/** Name at which chain of trust stopped with insecure, starting DLV
|
||||||
|
* DLV must result in chain going further down */
|
||||||
|
uint8_t* dlv_insecure_at;
|
||||||
|
/** length of dlv insecure point name */
|
||||||
|
size_t dlv_insecure_at_len;
|
||||||
|
/** status of DLV lookup. Indication to VAL_DLV_STATE what to do */
|
||||||
|
enum dlv_status {
|
||||||
|
dlv_error, /* server failure */
|
||||||
|
dlv_success, /* got a DLV */
|
||||||
|
dlv_ask_higher, /* ask again */
|
||||||
|
dlv_there_is_no_dlv /* got no DLV, sure of it */
|
||||||
|
} dlv_status;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue