#include "config.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 "validator/val_nsec.h"
#include "validator/val_nsec3.h"
#include "validator/val_neg.h"
#include "validator/val_sigcrypt.h"
#include "validator/autotrust.h"
#include "services/cache/dns.h"
#include "util/data/dname.h"
#include "util/module.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/fptr_wlist.h"
#include "ldns/rrdef.h"
#include "ldns/wire2str.h"
static void process_ds_response(struct module_qstate* qstate,
struct val_qstate* vq, int id, int rcode, struct dns_msg* msg,
struct query_info* qinfo, struct sock_list* origin);
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
{
char* e;
int i;
free(ve->nsec3_keysize);
free(ve->nsec3_maxiter);
ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c);
ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c);
if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
log_err("out of memory");
return 0;
}
for(i=0; i<c; i++) {
ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
return 0;
}
s = e;
ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
return 0;
}
s = e;
if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
log_err("nsec3 key iterations not ascending: %d %d",
(int)ve->nsec3_keysize[i-1],
(int)ve->nsec3_keysize[i]);
return 0;
}
verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
}
return 1;
}
static int
val_apply_cfg(struct module_env* env, struct val_env* val_env,
struct config_file* cfg)
{
int c;
val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
val_env->clean_additional = cfg->val_clean_additional;
val_env->permissive_mode = cfg->val_permissive_mode;
if(!env->anchors)
env->anchors = anchors_create();
if(!env->anchors) {
log_err("out of memory");
return 0;
}
if(!val_env->kcache)
val_env->kcache = key_cache_create(cfg);
if(!val_env->kcache) {
log_err("out of memory");
return 0;
}
env->key_cache = val_env->kcache;
if(!anchors_apply_cfg(env->anchors, cfg)) {
log_err("validator: error in trustanchors config");
return 0;
}
val_env->date_override = cfg->val_date_override;
val_env->skew_min = cfg->val_sig_skew_min;
val_env->skew_max = cfg->val_sig_skew_max;
c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
if(c < 1 || (c&1)) {
log_err("validator: unparseable or odd nsec3 key "
"iterations: %s", cfg->val_nsec3_key_iterations);
return 0;
}
val_env->nsec3_keyiter_count = c/2;
if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
log_err("validator: cannot apply nsec3 key iterations");
return 0;
}
if(!val_env->neg_cache)
val_env->neg_cache = val_neg_create(cfg,
val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]);
if(!val_env->neg_cache) {
log_err("out of memory");
return 0;
}
env->neg_cache = val_env->neg_cache;
return 1;
}
int
val_init(struct module_env* env, int id)
{
struct val_env* val_env = (struct val_env*)calloc(1,
sizeof(struct val_env));
if(!val_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)val_env;
env->need_to_validate = 1;
val_env->permissive_mode = 0;
lock_basic_init(&val_env->bogus_lock);
lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
sizeof(val_env->num_rrset_bogus));
if(!val_apply_cfg(env, val_env, env->cfg)) {
log_err("validator: could not apply configuration settings.");
return 0;
}
return 1;
}
void
val_deinit(struct module_env* env, int id)
{
struct val_env* val_env;
if(!env || !env->modinfo[id])
return;
val_env = (struct val_env*)env->modinfo[id];
lock_basic_destroy(&val_env->bogus_lock);
anchors_delete(env->anchors);
env->anchors = NULL;
key_cache_delete(val_env->kcache);
neg_cache_delete(val_env->neg_cache);
free(val_env->nsec3_keysize);
free(val_env->nsec3_maxiter);
free(val_env);
env->modinfo[id] = NULL;
}
static struct val_qstate*
val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq)
{
if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) {
verbose(VERB_ALGO, "constructing reply for validation");
vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region,
sizeof(struct dns_msg));
if(!vq->orig_msg)
return NULL;
vq->orig_msg->qinfo = qstate->qinfo;
vq->orig_msg->rep = (struct reply_info*)regional_alloc(
qstate->region, sizeof(struct reply_info));
if(!vq->orig_msg->rep)
return NULL;
memset(vq->orig_msg->rep, 0, sizeof(struct reply_info));
vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
vq->orig_msg->rep->qdcount = 1;
} else {
vq->orig_msg = qstate->return_msg;
}
vq->qchase = qstate->qinfo;
vq->chase_reply = regional_alloc_init(qstate->region,
vq->orig_msg->rep,
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!vq->chase_reply)
return NULL;
vq->chase_reply->rrsets = regional_alloc_init(qstate->region,
vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*)
* vq->orig_msg->rep->rrset_count);
if(!vq->chase_reply->rrsets)
return NULL;
vq->rrset_skip = 0;
return vq;
}
static struct val_qstate*
val_new(struct module_qstate* qstate, int id)
{
struct val_qstate* vq = (struct val_qstate*)regional_alloc(
qstate->region, sizeof(*vq));
log_assert(!qstate->minfo[id]);
if(!vq)
return NULL;
memset(vq, 0, sizeof(*vq));
qstate->minfo[id] = vq;
vq->state = VAL_INIT_STATE;
return val_new_getmsg(qstate, vq);
}
static int
val_error(struct module_qstate* qstate, int id)
{
qstate->ext_state[id] = module_error;
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
return 0;
}
static int
needs_validation(struct module_qstate* qstate, int ret_rc,
struct dns_msg* ret_msg)
{
int rcode;
if(qstate->is_valrec) {
verbose(VERB_ALGO, "not validating response, is valrec"
"(validation recursion lookup)");
return 0;
}
if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg)
rcode = ret_rc;
else rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags);
if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
if(verbosity >= VERB_ALGO) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
verbose(VERB_ALGO, "cannot validate non-answer, rcode %s", rc);
}
return 0;
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG &&
rcode == LDNS_RCODE_NOERROR && ret_msg &&
ret_msg->rep->an_numrrsets > 0) {
verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs.");
return 0;
}
return 1;
}
static int
already_validated(struct dns_msg* ret_msg)
{
if (ret_msg && ret_msg->rep->security > sec_status_bogus)
{
verbose(VERB_ALGO, "response has already been validated: %s",
sec_status_to_string(ret_msg->rep->security));
return 1;
}
return 0;
}
static int
generate_request(struct module_qstate* qstate, int id, uint8_t* name,
size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
{
struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
struct module_qstate* newq;
struct query_info ask;
int valrec;
ask.qname = name;
ask.qname_len = namelen;
ask.qtype = qtype;
ask.qclass = qclass;
log_query_info(VERB_ALGO, "generate request", &ask);
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(qtype == LDNS_RR_TYPE_DLV)
valrec = 0;
else valrec = 1;
if(!(*qstate->env->attach_sub)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec, &newq)){
log_err("Could not generate request: out of memory");
return 0;
}
if(newq) {
sock_list_merge(&newq->blacklist, newq->region,
vq->chain_blacklist);
}
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
static int
prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
int id, struct trust_anchor* toprime)
{
int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD);
if(!ret) {
log_err("Could not prime trust anchor: out of memory");
return 0;
}
vq->wait_prime_ta = 1;
vq->trust_anchor_name = regional_alloc_init(qstate->region,
toprime->name, toprime->namelen);
vq->trust_anchor_len = toprime->namelen;
vq->trust_anchor_labs = toprime->namelabs;
if(!vq->trust_anchor_name) {
log_err("Could not prime trust anchor: out of memory");
return 0;
}
return 1;
}
static int
validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
struct val_env* ve, struct query_info* qchase,
struct reply_info* chase_reply, struct key_entry_key* key_entry)
{
uint8_t* sname;
size_t i, slen;
struct ub_packed_rrset_key* s;
enum sec_status sec;
int dname_seen = 0;
char* reason = NULL;
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
dname_seen = 0;
((struct packed_rrset_data*)s->entry.data)->security =
sec_status_secure;
((struct packed_rrset_data*)s->entry.data)->trust =
rrset_trust_validated;
continue;
}
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason);
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed ANSWER rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
errinf(qstate, reason);
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
errinf(qstate, "for CNAME");
else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
errinf(qstate, "for DNAME");
errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
return 0;
}
if(qchase->qtype != LDNS_RR_TYPE_DNAME &&
ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) {
dname_seen = 1;
}
}
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason);
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed AUTHORITY rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
errinf(qstate, reason);
errinf_rrset(qstate, s);
errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
return 0;
}
}
if(!ve->clean_additional)
return 1;
for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i<chase_reply->rrset_count; i++) {
s = chase_reply->rrsets[i];
val_find_rrset_signer(s, &sname, &slen);
if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason);
}
return 1;
}
static int
detect_wrongly_truncated(struct reply_info* rep)
{
size_t i;
if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0)
return 0;
if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS)
return 0;
if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ]
->entry.data)->security == sec_status_secure)
return 0;
for(i=0; i<rep->an_numrrsets; i++) {
if(((struct packed_rrset_data*)rep->rrsets[ i ]
->entry.data)->security != sec_status_secure)
return 0;
}
verbose(VERB_ALGO, "truncating to minimal response");
return 1;
}
static void
validate_positive_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
uint8_t* wc = NULL;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(!val_rrset_wildcard(s, &wc)) {
log_nametypeclass(VERB_QUERY, "Positive response has "
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
return;
}
}
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
}
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive wildcard response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
wc_NSEC_ok = 1;
}
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "positive response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
return;
}
verbose(VERB_ALGO, "Successfully validated positive response");
chase_reply->security = sec_status_secure;
}
static void
validate_nodata_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
int has_valid_nsec = 0;
uint8_t* ce = NULL;
uint8_t* wc = NULL;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(nsec_proves_nodata(s, qchase, &wc)) {
has_valid_nsec = 1;
}
if(val_nsec_proves_name_error(s, qchase->qname)) {
ce = nsec_closest_encloser(qchase->qname, s);
}
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc && !ce)
has_valid_nsec = 0;
else if(wc && ce) {
if(query_dname_compare(wc, ce) != 0) {
has_valid_nsec = 0;
}
}
if(!has_valid_nsec && nsec3s_seen) {
enum sec_status sec = nsec3_prove_nodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "NODATA response is insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
has_valid_nsec = 1;
}
if(!has_valid_nsec) {
verbose(VERB_QUERY, "NODATA response failed to prove NODATA "
"status with NSEC/NSEC3");
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed NODATA", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
return;
}
verbose(VERB_ALGO, "successfully validated NODATA response.");
chase_reply->security = sec_status_secure;
}
static void
validate_nameerror_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, int* rcode)
{
int has_valid_nsec = 0;
int has_valid_wnsec = 0;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_name_error(s, qchase->qname))
has_valid_nsec = 1;
if(val_nsec_proves_no_wc(s, qchase->qname,
qchase->qname_len))
has_valid_wnsec = 1;
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3)
nsec3s_seen = 1;
}
if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) {
chase_reply->security = nsec3_prove_nameerror(env, ve,
chase_reply->rrsets, chase_reply->an_numrrsets+
chase_reply->ns_numrrsets, qchase, kkey);
if(chase_reply->security != sec_status_secure) {
verbose(VERB_QUERY, "NameError response failed nsec, "
"nsec3 proof was %s", sec_status_to_string(
chase_reply->security));
return;
}
has_valid_nsec = 1;
has_valid_wnsec = 1;
}
if(!has_valid_nsec) {
verbose(VERB_QUERY, "NameError response has failed to prove: "
"qname does not exist");
chase_reply->security = sec_status_bogus;
validate_nodata_response(env, ve, qchase, chase_reply, kkey);
if (chase_reply->security == sec_status_secure)
*rcode = LDNS_RCODE_NOERROR;
return;
}
if(!has_valid_wnsec) {
verbose(VERB_QUERY, "NameError response has failed to prove: "
"covering wildcard does not exist");
chase_reply->security = sec_status_bogus;
validate_nodata_response(env, ve, qchase, chase_reply, kkey);
if (chase_reply->security == sec_status_secure)
*rcode = LDNS_RCODE_NOERROR;
return;
}
verbose(VERB_ALGO, "successfully validated NAME ERROR response.");
chase_reply->security = sec_status_secure;
}
static void
validate_referral_response(struct reply_info* chase_reply)
{
size_t i;
enum sec_status s;
chase_reply->security = sec_status_secure;
for(i=0; i<chase_reply->rrset_count; i++) {
s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
->entry.data)->security;
if(s < chase_reply->security)
chase_reply->security = s;
}
verbose(VERB_ALGO, "validated part of referral response as %s",
sec_status_to_string(chase_reply->security));
}
static void
validate_any_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
uint8_t* wc = NULL;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
if(qchase->qtype != LDNS_RR_TYPE_ANY) {
log_err("internal error: ANY validation called for non-ANY");
chase_reply->security = sec_status_bogus;
return;
}
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(!val_rrset_wildcard(s, &wc)) {
log_nametypeclass(VERB_QUERY, "Positive ANY response"
" has inconsistent wildcard sigs:",
s->rk.dname, ntohs(s->rk.type),
ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
return;
}
}
if(wc != NULL)
for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
}
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets,
chase_reply->an_numrrsets+chase_reply->ns_numrrsets,
qchase, kkey, wc);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive ANY wildcard response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
wc_NSEC_ok = 1;
}
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "positive ANY response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
return;
}
verbose(VERB_ALGO, "Successfully validated positive ANY response");
chase_reply->security = sec_status_secure;
}
static void
validate_cname_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
uint8_t* wc = NULL;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(!val_rrset_wildcard(s, &wc)) {
log_nametypeclass(VERB_QUERY, "Cname response has "
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
return;
}
if(qchase->qtype != LDNS_RR_TYPE_DNAME &&
ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) {
log_nametypeclass(VERB_QUERY, "cannot validate a "
"wildcarded DNAME:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
return;
}
if (ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
break;
}
}
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
}
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "wildcard CNAME response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
wc_NSEC_ok = 1;
}
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "CNAME response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
return;
}
verbose(VERB_ALGO, "Successfully validated CNAME response");
chase_reply->security = sec_status_secure;
}
static void
validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
int nodata_valid_nsec = 0;
uint8_t* ce = NULL;
uint8_t* wc = NULL;
int nxdomain_valid_nsec = 0;
int nxdomain_valid_wnsec = 0;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(nsec_proves_nodata(s, qchase, &wc)) {
nodata_valid_nsec = 1;
}
if(val_nsec_proves_name_error(s, qchase->qname)) {
ce = nsec_closest_encloser(qchase->qname, s);
nxdomain_valid_nsec = 1;
}
if(val_nsec_proves_no_wc(s, qchase->qname,
qchase->qname_len))
nxdomain_valid_wnsec = 1;
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc && !ce)
nodata_valid_nsec = 0;
else if(wc && ce) {
if(query_dname_compare(wc, ce) != 0) {
nodata_valid_nsec = 0;
}
}
if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) {
nxdomain_valid_nsec = 0;
}
if(nodata_valid_nsec && nxdomain_valid_nsec) {
verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name "
"exists and not exists, bogus");
chase_reply->security = sec_status_bogus;
return;
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) {
int nodata;
enum sec_status sec = nsec3_prove_nxornodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, &nodata);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "CNAMEchain to noanswer response "
"is insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
if(nodata)
nodata_valid_nsec = 1;
else nxdomain_valid_nsec = 1;
}
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec) {
verbose(VERB_QUERY, "CNAMEchain to noanswer response failed "
"to prove status with NSEC/NSEC3");
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
return;
}
if(nodata_valid_nsec)
verbose(VERB_ALGO, "successfully validated CNAME chain to a "
"NODATA response.");
else verbose(VERB_ALGO, "successfully validated CNAME chain to a "
"NAMEERROR response.");
chase_reply->security = sec_status_secure;
}
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;
struct trust_anchor* anchor;
enum val_classification subtype = val_classify_response(
qstate->query_flags, &qstate->qinfo, &vq->qchase,
vq->orig_msg->rep, vq->rrset_skip);
if(vq->restart_count > VAL_MAX_RESTART_COUNT) {
verbose(VERB_ALGO, "restart count exceeded");
return val_error(qstate, id);
}
verbose(VERB_ALGO, "validator classification %s",
val_classification_to_string(subtype));
if(subtype == VAL_CLASS_REFERRAL &&
vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
vq->qchase.qname = vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.dname;
vq->qchase.qname_len = vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.dname_len;
vq->qchase.qtype = ntohs(vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.type);
vq->qchase.qclass = ntohs(vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.rrset_class);
}
lookup_name = vq->qchase.qname;
lookup_len = vq->qchase.qname_len;
if(vq->qchase.qtype == LDNS_RR_TYPE_DS ||
(vq->qchase.qtype == LDNS_RR_TYPE_NSEC &&
vq->orig_msg->rep->rrset_count > vq->rrset_skip &&
ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) ==
LDNS_RR_TYPE_NSEC &&
!(vq->orig_msg->rep->rrsets[vq->rrset_skip]->
rk.flags&PACKED_RRSET_NSEC_AT_APEX))) {
dname_remove_label(&lookup_name, &lookup_len);
}
val_mark_indeterminate(vq->chase_reply, qstate->env->anchors,
qstate->env->rrset_cache, qstate->env);
vq->key_entry = NULL;
vq->empty_DS_name = NULL;
vq->ds_rrset = 0;
anchor = anchors_lookup(qstate->env->anchors,
lookup_name, lookup_len, vq->qchase.qclass);
val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep,
vq->rrset_skip, &vq->signer_name, &vq->signer_len);
if(vq->signer_name != NULL &&
!dname_subdomain_c(lookup_name, vq->signer_name)) {
log_nametypeclass(VERB_ALGO, "this signer name is not a parent "
"of lookupname, omitted", vq->signer_name, 0, 0);
vq->signer_name = NULL;
}
if(vq->signer_name == NULL) {
log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name,
0, 0);
} else {
lookup_name = vq->signer_name;
lookup_len = vq->signer_len;
log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0);
}
if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name &&
anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){
lock_basic_unlock(&anchor->lock);
anchor = anchors_lookup(qstate->env->anchors,
lookup_name, lookup_len, vq->qchase.qclass);
if(!anchor) {
verbose(VERB_QUERY, "unsigned parent zone denies"
" trust anchor, indeterminate");
vq->chase_reply->security = sec_status_indeterminate;
vq->state = VAL_FINISHED_STATE;
return 1;
}
verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent");
} else if(subtype == VAL_CLASS_POSITIVE &&
qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) {
dname_remove_label(&lookup_name, &lookup_len);
}
if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME ||
subtype == VAL_CLASS_REFERRAL) {
val_fill_reply(vq->chase_reply, vq->orig_msg->rep,
vq->rrset_skip, lookup_name, lookup_len,
vq->signer_name);
if(verbosity >= VERB_ALGO)
log_dns_msg("chased extract", &vq->qchase,
vq->chase_reply);
}
vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
vq->qchase.qclass, qstate->region, *qstate->env->now);
if(vq->key_entry == NULL && anchor == NULL) {
vq->chase_reply->security = sec_status_indeterminate;
vq->state = VAL_FINISHED_STATE;
return 1;
}
else if(vq->key_entry == NULL || (anchor &&
dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) {
if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) {
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, anchor->name,
qstate->env->rrset_cache, qstate->env);
lock_basic_unlock(&anchor->lock);
vq->dlv_checked=1;
vq->state = VAL_FINISHED_STATE;
return 1;
}
verbose(VERB_DETAIL, "prime trust anchor");
if(!prime_trust_anchor(qstate, vq, id, anchor)) {
lock_basic_unlock(&anchor->lock);
return val_error(qstate, id);
}
lock_basic_unlock(&anchor->lock);
vq->state = VAL_FINDKEY_STATE;
return 0;
}
if(anchor) {
lock_basic_unlock(&anchor->lock);
}
if(key_entry_isnull(vq->key_entry)) {
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry->name,
qstate->env->rrset_cache, qstate->env);
vq->state = VAL_FINISHED_STATE;
return 1;
} else if(key_entry_isbad(vq->key_entry)) {
errinf_dname(qstate, "key for validation", vq->key_entry->name);
errinf(qstate, "is marked as invalid");
if(key_entry_get_reason(vq->key_entry)) {
errinf(qstate, "because of a previous");
errinf(qstate, key_entry_get_reason(vq->key_entry));
}
vq->restart_count = VAL_MAX_RESTART_COUNT;
vq->chase_reply->security = sec_status_bogus;
vq->state = VAL_FINISHED_STATE;
return 1;
}
vq->state = VAL_FINDKEY_STATE;
return 1;
}
static int
processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
{
uint8_t* target_key_name, *current_key_name;
size_t target_key_len;
int strip_lab;
log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase);
log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry));
if(key_entry_isnull(vq->key_entry)) {
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;
}
target_key_name = vq->signer_name;
target_key_len = vq->signer_len;
if(!target_key_name) {
target_key_name = vq->qchase.qname;
target_key_len = vq->qchase.qname_len;
}
current_key_name = vq->key_entry->name;
if(query_dname_compare(target_key_name, current_key_name) == 0) {
vq->state = VAL_VALIDATE_STATE;
return 1;
}
if(vq->empty_DS_name) {
if(query_dname_compare(target_key_name,
vq->empty_DS_name) == 0) {
verbose(VERB_ALGO, "Cannot retrieve DS for signature");
errinf(qstate, "no signatures");
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
vq->state = VAL_FINISHED_STATE;
return 1;
}
current_key_name = vq->empty_DS_name;
}
log_nametypeclass(VERB_ALGO, "current keyname", current_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
log_nametypeclass(VERB_ALGO, "target keyname", target_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
if(!dname_subdomain_c(target_key_name, current_key_name)) {
verbose(VERB_ALGO, "bad signer name");
vq->chase_reply->security = sec_status_bogus;
vq->state = VAL_FINISHED_STATE;
return 1;
}
strip_lab = dname_count_labels(target_key_name) -
dname_count_labels(current_key_name) - 1;
log_assert(strip_lab >= -1);
verbose(VERB_ALGO, "striplab %d", strip_lab);
if(strip_lab > 0) {
dname_remove_labels(&target_key_name, &target_key_len,
strip_lab);
}
log_nametypeclass(VERB_ALGO, "next keyname", target_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
if(vq->ds_rrset)
log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN);
else verbose(VERB_ALGO, "No DS RRset");
if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname,
vq->key_entry->name) != 0) {
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;
}
if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
target_key_name) != 0) {
struct dns_msg* msg;
if(!qstate->blacklist && !vq->chain_blacklist &&
(msg=val_find_DS(qstate->env, target_key_name,
target_key_len, vq->qchase.qclass, qstate->region,
vq->key_entry->name)) ) {
verbose(VERB_ALGO, "Process cached DS response");
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
msg, &msg->qinfo, NULL);
return 1;
}
if(!generate_request(qstate, id, target_key_name,
target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
BIT_CD)) {
log_err("mem error generating DS request");
return val_error(qstate, id);
}
return 0;
}
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;
}
static int
processValidate(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
enum val_classification subtype;
int rcode;
if(!vq->key_entry) {
verbose(VERB_ALGO, "validate: no key entry, failed");
return val_error(qstate, id);
}
vq->state = VAL_FINISHED_STATE;
if(key_entry_isnull(vq->key_entry)) {
verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE",
vq->signer_name?"":"unsigned ");
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry->name,
qstate->env->rrset_cache, qstate->env);
key_cache_insert(ve->kcache, vq->key_entry, qstate);
return 1;
}
if(key_entry_isbad(vq->key_entry)) {
log_nametypeclass(VERB_DETAIL, "Could not establish a chain "
"of trust to keys for", vq->key_entry->name,
LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class);
vq->chase_reply->security = sec_status_bogus;
errinf(qstate, "while building chain of trust");
if(vq->restart_count >= VAL_MAX_RESTART_COUNT)
key_cache_insert(ve->kcache, vq->key_entry, qstate);
return 1;
}
if(vq->signer_name == NULL) {
log_query_info(VERB_ALGO, "processValidate: state has no "
"signer name", &vq->qchase);
verbose(VERB_DETAIL, "Could not establish validation of "
"INSECURE status of unsigned response.");
errinf(qstate, "no signatures");
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
return 1;
}
subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
&vq->qchase, vq->orig_msg->rep, vq->rrset_skip);
if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry)) {
if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY
|| subtype == VAL_CLASS_CNAME) &&
detect_wrongly_truncated(vq->orig_msg->rep)) {
vq->orig_msg->rep->ns_numrrsets = 0;
vq->orig_msg->rep->ar_numrrsets = 0;
vq->orig_msg->rep->rrset_count =
vq->orig_msg->rep->an_numrrsets;
vq->chase_reply->ns_numrrsets = 0;
vq->chase_reply->ar_numrrsets = 0;
vq->chase_reply->rrset_count =
vq->chase_reply->an_numrrsets;
qstate->errinf = NULL;
}
else {
verbose(VERB_DETAIL, "Validate: message contains "
"bad rrsets");
return 1;
}
}
switch(subtype) {
case VAL_CLASS_POSITIVE:
verbose(VERB_ALGO, "Validating a positive response");
validate_positive_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(positive): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_NODATA:
verbose(VERB_ALGO, "Validating a nodata response");
validate_nodata_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(nodata): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_NAMEERROR:
rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags);
verbose(VERB_ALGO, "Validating a nxdomain response");
validate_nameerror_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry, &rcode);
verbose(VERB_DETAIL, "validate(nxdomain): %s",
sec_status_to_string(
vq->chase_reply->security));
FLAGS_SET_RCODE(vq->orig_msg->rep->flags, rcode);
FLAGS_SET_RCODE(vq->chase_reply->flags, rcode);
break;
case VAL_CLASS_CNAME:
verbose(VERB_ALGO, "Validating a cname response");
validate_cname_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(cname): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_CNAMENOANSWER:
verbose(VERB_ALGO, "Validating a cname noanswer "
"response");
validate_cname_noanswer_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(cname_noanswer): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_REFERRAL:
verbose(VERB_ALGO, "Validating a referral response");
validate_referral_response(vq->chase_reply);
verbose(VERB_DETAIL, "validate(referral): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_ANY:
verbose(VERB_ALGO, "Validating a positive ANY "
"response");
validate_any_response(qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(positive_any): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
default:
log_err("validate: unhandled response subtype: %d",
subtype);
}
if(vq->chase_reply->security == sec_status_bogus) {
if(subtype == VAL_CLASS_POSITIVE)
errinf(qstate, "wildcard");
else errinf(qstate, val_classification_to_string(subtype));
errinf(qstate, "proof failed");
errinf_origin(qstate, qstate->reply_origin);
}
return 1;
}
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;
log_assert(qstate->env->anchors->dlv_anchor);
log_assert(vq->dlv_checked);
vq->dlv_lookup_name = NULL;
vq->dlv_lookup_name_len = 0;
vq->dlv_insecure_at = NULL;
vq->dlv_insecure_at_len = 0;
if(vq->signer_name) {
nm = vq->signer_name;
nm_len = vq->signer_len;
} else {
nm = vq->qchase.qname;
nm_len = vq->qchase.qname_len;
if(vq->qchase.qtype == LDNS_RR_TYPE_DS)
dname_remove_label(&nm, &nm_len);
}
log_nametypeclass(VERB_ALGO, "DLV init look", nm, LDNS_RR_TYPE_DS,
vq->qchase.qclass);
log_assert(nm && nm_len);
if(dname_subdomain_c(nm, qstate->env->anchors->dlv_anchor->name)) {
verbose(VERB_ALGO, "DLV lookup within DLV repository denied");
return 1;
}
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);
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);
}
while(val_neg_dlvlookup(ve->neg_cache, vq->dlv_lookup_name,
vq->dlv_lookup_name_len, vq->qchase.qclass,
qstate->env->rrset_cache, *qstate->env->now)) {
dname_remove_label(&vq->dlv_lookup_name,
&vq->dlv_lookup_name_len);
if(!dname_subdomain_c(vq->dlv_lookup_name,
qstate->env->anchors->dlv_anchor->name)) {
verbose(VERB_ALGO, "ask above dlv repo");
return 1;
}
if(vq->dlv_insecure_at && !dname_subdomain_c(
vq->dlv_lookup_name, vq->dlv_insecure_at)) {
verbose(VERB_ALGO, "ask above insecure endpoint");
return 1;
}
}
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);
}
return 0;
}
static int
processFinished(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
enum val_classification subtype = val_classify_response(
qstate->query_flags, &qstate->qinfo, &vq->qchase,
vq->orig_msg->rep, vq->rrset_skip);
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;
}
if(vq->rrset_skip == 0)
vq->orig_msg->rep->security = vq->chase_reply->security;
else if(subtype != VAL_CLASS_REFERRAL ||
vq->rrset_skip < vq->orig_msg->rep->an_numrrsets +
vq->orig_msg->rep->ns_numrrsets) {
if(vq->chase_reply->security < vq->orig_msg->rep->security)
vq->orig_msg->rep->security =
vq->chase_reply->security;
}
if(subtype == VAL_CLASS_REFERRAL) {
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->dlv_checked = 0;
vq->state = VAL_INIT_STATE;
return 1;
}
}
if(vq->chase_reply->security != sec_status_bogus &&
subtype == VAL_CLASS_CNAME) {
if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep,
&vq->rrset_skip)) {
verbose(VERB_ALGO, "validator: failed to chase CNAME");
vq->orig_msg->rep->security = sec_status_bogus;
} else {
log_query_info(VERB_ALGO, "validator: chased to",
&vq->qchase);
vq->chase_reply->security = sec_status_unchecked;
vq->dlv_checked = 0;
vq->state = VAL_INIT_STATE;
return 1;
}
}
if(vq->orig_msg->rep->security == sec_status_secure) {
val_check_nonsecure(ve, vq->orig_msg->rep);
if(vq->orig_msg->rep->security == sec_status_secure) {
log_query_info(VERB_DETAIL, "validation success",
&qstate->qinfo);
}
}
if(vq->orig_msg->rep->security == sec_status_bogus) {
if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
int restart_count = vq->restart_count+1;
verbose(VERB_ALGO, "validation failed, "
"blacklist and retry to fetch data");
val_blacklist(&qstate->blacklist, qstate->region,
qstate->reply_origin, 0);
qstate->reply_origin = NULL;
qstate->errinf = NULL;
memset(vq, 0, sizeof(*vq));
vq->restart_count = restart_count;
vq->state = VAL_INIT_STATE;
verbose(VERB_ALGO, "pass back to next module");
qstate->ext_state[id] = module_restart_next;
return 0;
}
vq->orig_msg->rep->ttl = ve->bogus_ttl;
vq->orig_msg->rep->prefetch_ttl =
PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl);
if(qstate->env->cfg->val_log_level >= 1 &&
!qstate->env->cfg->val_log_squelch) {
if(qstate->env->cfg->val_log_level < 2)
log_query_info(0, "validation failure",
&qstate->qinfo);
else {
char* err = errinf_to_str(qstate);
if(err) log_info("%s", err);
free(err);
}
}
if(ve->permissive_mode)
vq->orig_msg->rep->security = sec_status_indeterminate;
}
if(qstate->query_flags&BIT_RD) {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL,
qstate->query_flags)) {
log_err("out of memory caching validator results");
}
} else {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 1, 0, 0, NULL,
qstate->query_flags)) {
log_err("out of memory caching validator results");
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = vq->orig_msg;
qstate->ext_state[id] = module_finished;
return 0;
}
static int
processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
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");
if(vq->dlv_status == dlv_error) {
verbose(VERB_QUERY, "failed DLV lookup");
return val_error(qstate, id);
} else if(vq->dlv_status == dlv_success) {
uint8_t* nm;
size_t nmlen;
vq->state = VAL_FINDKEY_STATE;
log_assert(dname_subdomain_c(vq->ds_rrset->rk.dname,
qstate->env->anchors->dlv_anchor->name));
nmlen = vq->ds_rrset->rk.dname_len -
qstate->env->anchors->dlv_anchor->namelen + 1;
nm = regional_alloc_init(qstate->region,
vq->ds_rrset->rk.dname, nmlen);
if(!nm) {
log_err("Out of memory in DLVLook");
return val_error(qstate, id);
}
nm[nmlen-1] = 0;
vq->ds_rrset->rk.dname = nm;
vq->ds_rrset->rk.dname_len = nmlen;
vq->key_entry = key_entry_create_null(qstate->region,
nm, nmlen, vq->qchase.qclass, 0, 0);
if(!vq->key_entry) {
log_err("Out of memory in DLVLook");
return val_error(qstate, id);
}
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) {
vq->state = VAL_FINISHED_STATE;
return 1;
}
log_assert(vq->dlv_status == dlv_ask_higher);
if(!dname_subdomain_c(vq->dlv_lookup_name,
qstate->env->anchors->dlv_anchor->name)) {
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)) {
verbose(VERB_ALGO, "ask above insecure endpoint");
log_nametypeclass(VERB_ALGO, "enpt", vq->dlv_insecure_at, 0, 0);
vq->state = VAL_FINISHED_STATE;
return 1;
}
if(val_neg_dlvlookup(ve->neg_cache, vq->dlv_lookup_name,
vq->dlv_lookup_name_len, vq->qchase.qclass,
qstate->env->rrset_cache, *qstate->env->now)) {
dname_remove_label(&vq->dlv_lookup_name,
&vq->dlv_lookup_name_len);
return processDLVLookup(qstate, vq, ve, id);
}
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;
}
static void
val_handle(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
int cont = 1;
while(cont) {
verbose(VERB_ALGO, "val handle processing q with state %s",
val_state_to_string(vq->state));
switch(vq->state) {
case VAL_INIT_STATE:
cont = processInit(qstate, vq, ve, id);
break;
case VAL_FINDKEY_STATE:
cont = processFindKey(qstate, vq, id);
break;
case VAL_VALIDATE_STATE:
cont = processValidate(qstate, vq, ve, id);
break;
case VAL_FINISHED_STATE:
cont = processFinished(qstate, vq, ve, id);
break;
case VAL_DLVLOOKUP_STATE:
cont = processDLVLookup(qstate, vq, ve, id);
break;
default:
log_warn("validator: invalid state %d",
vq->state);
cont = 0;
break;
}
}
}
void
val_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s "
"event:%s", id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "validator operate: query",
&qstate->qinfo);
if(vq && qstate->qinfo.qname != vq->qchase.qname)
log_query_info(VERB_QUERY, "validator operate: chased to",
&vq->qchase);
(void)outbound;
if(event == module_event_new ||
(event == module_event_pass && vq == NULL)) {
verbose(VERB_ALGO, "validator: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
if(event == module_event_moddone) {
verbose(VERB_ALGO, "validator: nextmodule returned");
if(!needs_validation(qstate, qstate->return_rcode,
qstate->return_msg)) {
if(qstate->return_msg)
qstate->return_msg->rep->security =
sec_status_indeterminate;
qstate->ext_state[id] = module_finished;
return;
}
if(already_validated(qstate->return_msg)) {
qstate->ext_state[id] = module_finished;
return;
}
if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
verbose(VERB_ALGO, "cannot validate classANY: bogus");
if(qstate->return_msg)
qstate->return_msg->rep->security =
sec_status_bogus;
qstate->ext_state[id] = module_finished;
return;
}
qstate->ext_state[id] = module_error;
if(!vq) {
vq = val_new(qstate, id);
if(!vq) {
log_err("validator: malloc failure");
qstate->ext_state[id] = module_error;
return;
}
} else if(!vq->orig_msg) {
if(!val_new_getmsg(qstate, vq)) {
log_err("validator: malloc failure");
qstate->ext_state[id] = module_error;
return;
}
}
val_handle(qstate, vq, ve, id);
return;
}
if(event == module_event_pass) {
qstate->ext_state[id] = module_error;
val_handle(qstate, vq, ve, id);
return;
}
log_err("validator: bad event %s", strmodulevent(event));
qstate->ext_state[id] = module_error;
return;
}
static struct key_entry_key*
primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
struct trust_anchor* ta, struct module_qstate* qstate, int id)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* kkey = NULL;
enum sec_status sec = sec_status_unchecked;
char* reason = NULL;
int downprot = 1;
if(!dnskey_rrset) {
log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
"could not fetch DNSKEY rrset",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
if(qstate->env->cfg->harden_dnssec_stripped) {
errinf(qstate, "no DNSKEY rrset");
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
*qstate->env->now);
} else kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, NULL_KEY_TTL,
*qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate fail prime key");
return NULL;
}
return kkey;
}
kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve,
dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot,
&reason);
if(!kkey) {
log_err("out of memory: verifying prime TA");
return NULL;
}
if(key_entry_isgood(kkey))
sec = sec_status_secure;
else
sec = sec_status_bogus;
verbose(VERB_DETAIL, "validate keys with anchor(DS): %s",
sec_status_to_string(sec));
if(sec != sec_status_secure) {
log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
"DNSKEY rrset is not secure",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
if(qstate->env->cfg->harden_dnssec_stripped) {
errinf(qstate, reason);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
*qstate->env->now);
} else kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, NULL_KEY_TTL,
*qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate null prime key");
return NULL;
}
return kkey;
}
log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
return kkey;
}
static int
ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct key_entry_key** ke)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
char* reason = NULL;
enum val_classification subtype;
if(rcode != LDNS_RCODE_NOERROR) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
verbose(VERB_DETAIL, "DS response was error, thus bogus");
errinf(qstate, rc);
errinf(qstate, "no DS");
goto return_bogus;
}
subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0);
if(subtype == VAL_CLASS_POSITIVE) {
struct ub_packed_rrset_key* ds;
enum sec_status sec;
ds = reply_find_answer_rrset(qinfo, msg->rep);
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
errinf(qstate, "no DS record");
goto return_bogus;
}
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason);
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify");
errinf(qstate, reason);
goto return_bogus;
}
if(!val_dsset_isusable(ds)) {
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
ub_packed_rrset_ttl(ds), *qstate->env->now);
return (*ke) != NULL;
}
log_query_info(VERB_DETAIL, "validated DS", qinfo);
*ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
NULL, *qstate->env->now);
return (*ke) != NULL;
} else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) {
time_t proof_ttl = 0;
enum sec_status sec;
if(!val_has_signed_nsecs(msg->rep, &reason)) {
verbose(VERB_ALGO, "no NSECs: %s", reason);
errinf(qstate, reason);
goto return_bogus;
}
sec = val_nsec_prove_nodata_dsreply(
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
&proof_ttl, &reason);
switch(sec) {
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl,
*qstate->env->now);
return (*ke) != NULL;
case sec_status_insecure:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved not a delegation point");
*ke = NULL;
return 1;
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral did not prove no DS.");
errinf(qstate, reason);
goto return_bogus;
case sec_status_unchecked:
default:
break;
}
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason);
switch(sec) {
case sec_status_insecure:
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl,
*qstate->env->now);
return (*ke) != NULL;
case sec_status_indeterminate:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no delegation");
*ke = NULL;
return 1;
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
errinf(qstate, reason);
goto return_bogus;
case sec_status_unchecked:
default:
break;
}
verbose(VERB_DETAIL, "DS %s ran out of options, so return "
"bogus", val_classification_to_string(subtype));
errinf(qstate, "no DS but also no proof of that");
goto return_bogus;
} else if(subtype == VAL_CLASS_CNAME ||
subtype == VAL_CLASS_CNAMENOANSWER) {
enum sec_status sec;
struct ub_packed_rrset_key* cname;
cname = reply_find_rrset_section_an(msg->rep, qinfo->qname,
qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass);
if(!cname) {
errinf(qstate, "validator classified CNAME but no "
"CNAME of the queried name for DS");
goto return_bogus;
}
if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count
== 0) {
if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep->
rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) {
errinf(qstate, "DS got DNAME answer");
} else {
errinf(qstate, "DS got unsigned CNAME answer");
}
goto return_bogus;
}
sec = val_verify_rrset_entry(qstate->env, ve, cname,
vq->key_entry, &reason);
if(sec == sec_status_secure) {
verbose(VERB_ALGO, "CNAME validated, "
"proof that DS does not exist");
*ke = NULL;
return 1;
}
errinf(qstate, "CNAME in DS response was not secure.");
errinf(qstate, reason);
goto return_bogus;
} else {
verbose(VERB_QUERY, "Encountered an unhandled type of "
"DS response, thus bogus.");
errinf(qstate, "no DS and");
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE(
msg->rep->flags), rc, sizeof(rc));
errinf(qstate, rc);
} else errinf(qstate, val_classification_to_string(subtype));
errinf(qstate, "message fails to prove that");
goto return_bogus;
}
return_bogus:
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
qinfo->qname_len, qinfo->qclass,
BOGUS_KEY_TTL, *qstate->env->now);
return (*ke) != NULL;
}
static void
process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin)
{
struct key_entry_key* dske = NULL;
uint8_t* olds = vq->empty_DS_name;
vq->empty_DS_name = NULL;
if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) {
log_err("malloc failure in process_ds_response");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
if(dske == NULL) {
vq->empty_DS_name = regional_alloc_init(qstate->region,
qinfo->qname, qinfo->qname_len);
if(!vq->empty_DS_name) {
log_err("malloc failure in empty_DS_name");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->empty_DS_len = qinfo->qname_len;
vq->chain_blacklist = NULL;
} else if(key_entry_isgood(dske)) {
vq->ds_rrset = key_entry_get_rrset(dske, qstate->region);
if(!vq->ds_rrset) {
log_err("malloc failure in process DS");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->chain_blacklist = NULL;
} else if(key_entry_isbad(dske)
&& vq->restart_count < VAL_MAX_RESTART_COUNT) {
vq->empty_DS_name = olds;
val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
} else {
if(key_entry_isbad(dske)) {
errinf_origin(qstate, origin);
errinf_dname(qstate, "for DS", qinfo->qname);
}
vq->key_entry = dske;
vq->state = VAL_VALIDATE_STATE;
}
}
static void
process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* old = vq->key_entry;
struct ub_packed_rrset_key* dnskey = NULL;
int downprot;
char* reason = NULL;
if(rcode == LDNS_RCODE_NOERROR)
dnskey = reply_find_answer_rrset(qinfo, msg->rep);
if(dnskey == NULL) {
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
return;
}
vq->key_entry = key_entry_create_bad(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
BOGUS_KEY_TTL, *qstate->env->now);
if(!vq->key_entry) {
log_err("alloc failure in missing dnskey response");
}
errinf(qstate, "No DNSKEY record");
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
return;
}
if(!vq->ds_rrset) {
log_err("internal error: no DS rrset for new DNSKEY response");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
downprot = 1;
vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason);
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
vq->state = VAL_VALIDATE_STATE;
return;
}
if(!key_entry_isgood(vq->key_entry)) {
if(key_entry_isbad(vq->key_entry)) {
if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
val_blacklist(&vq->chain_blacklist,
qstate->region, origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = old;
return;
}
verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
"thus bogus.");
errinf(qstate, reason);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
}
vq->chain_blacklist = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->chain_blacklist = NULL;
qstate->errinf = NULL;
key_cache_insert(ve->kcache, vq->key_entry, qstate);
log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
}
static void
process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct sock_list* origin)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct ub_packed_rrset_key* dnskey_rrset = NULL;
struct trust_anchor* ta = anchor_find(qstate->env->anchors,
vq->trust_anchor_name, vq->trust_anchor_labs,
vq->trust_anchor_len, vq->qchase.qclass);
if(!ta) {
vq->state = VAL_INIT_STATE;
if(!vq->trust_anchor_name)
vq->state = VAL_VALIDATE_STATE;
vq->trust_anchor_name = NULL;
return;
}
if(rcode == LDNS_RCODE_NOERROR) {
dnskey_rrset = reply_find_rrset_section_an(msg->rep,
ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY,
ta->dclass);
}
if(ta->autr) {
if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset)) {
vq->state = VAL_INIT_STATE;
vq->trust_anchor_name = NULL;
return;
}
}
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id);
lock_basic_unlock(&ta->lock);
if(vq->key_entry) {
if(key_entry_isbad(vq->key_entry)
&& vq->restart_count < VAL_MAX_RESTART_COUNT) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = NULL;
vq->state = VAL_INIT_STATE;
return;
}
vq->chain_blacklist = NULL;
errinf_origin(qstate, origin);
errinf_dname(qstate, "for trust anchor", ta->name);
key_cache_insert(ve->kcache, vq->key_entry, qstate);
}
if(!vq->key_entry || key_entry_isnull(vq->key_entry) ||
key_entry_isbad(vq->key_entry)) {
vq->state = VAL_VALIDATE_STATE;
}
}
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) {
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, %s",
sec_status_to_string(msg->rep->security));
return;
}
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) {
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);
vq->dlv_status = dlv_success;
return;
}
val_neg_addreply(ve->neg_cache, msg->rep);
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;
}
void
val_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
struct val_qstate* vq = (struct val_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "validator: inform_super, sub is",
&qstate->qinfo);
log_query_info(VERB_ALGO, "super is", &super->qinfo);
if(!vq) {
verbose(VERB_ALGO, "super: has no validator state");
return;
}
if(vq->wait_prime_ta) {
vq->wait_prime_ta = 0;
process_prime_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, qstate->reply_origin);
return;
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) {
process_ds_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo,
qstate->reply_origin);
return;
} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) {
process_dnskey_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo,
qstate->reply_origin);
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");
}
void
val_clear(struct module_qstate* qstate, int id)
{
if(!qstate)
return;
qstate->minfo[id] = NULL;
}
size_t
val_get_mem(struct module_env* env, int id)
{
struct val_env* ve = (struct val_env*)env->modinfo[id];
if(!ve)
return 0;
return sizeof(*ve) + key_cache_get_mem(ve->kcache) +
val_neg_get_mem(ve->neg_cache) +
sizeof(size_t)*2*ve->nsec3_keyiter_count;
}
static struct module_func_block val_block = {
"validator",
&val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear,
&val_get_mem
};
struct module_func_block*
val_get_funcblock(void)
{
return &val_block;
}
const char*
val_state_to_string(enum val_state state)
{
switch(state) {
case VAL_INIT_STATE: return "VAL_INIT_STATE";
case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
case VAL_DLVLOOKUP_STATE: return "VAL_DLVLOOKUP_STATE";
}
return "UNKNOWN VALIDATOR STATE";
}