8 October 2009: Wouter
- please doxygen
- add val-log-level print to corner case (nameserver.epost.bg).
+ - more detail to errors from insecure delegation checks.
7 October 2009: Wouter
- retry for validation failure in DS and prime results. Less mem use.
enum sec_status
val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
struct query_info* qinfo, struct reply_info* rep,
- struct key_entry_key* kkey, uint32_t* proof_ttl)
+ struct key_entry_key* kkey, uint32_t* proof_ttl, char** reason)
{
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
uint8_t* wc = NULL, *ce = NULL;
int valid_nsec = 0;
struct ub_packed_rrset_key* wc_nsec = NULL;
- char* reason = NULL;
/* If we have a NSEC at the same name, it must prove one
* of two things
* 1) this is a delegation point and there is no DS
* 2) this is not a delegation point */
if(nsec) {
- sec = val_verify_rrset_entry(env, ve, nsec, kkey, &reason);
+ sec = val_verify_rrset_entry(env, ve, nsec, kkey, reason);
if(sec != sec_status_secure) {
verbose(VERB_ALGO, "NSEC RRset for the "
"referral did not verify.");
sec = val_nsec_proves_no_ds(nsec, qinfo);
if(sec == sec_status_bogus) {
/* something was wrong. */
+ *reason = "NSEC does not prove absence of DS";
return sec;
} else if(sec == sec_status_insecure) {
/* this wasn't a delegation point. */
if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
continue;
sec = val_verify_rrset_entry(env, ve, rep->rrsets[i], kkey,
- &reason);
+ reason);
if(sec != sec_status_secure) {
verbose(VERB_ALGO, "NSEC for empty non-terminal "
"did not verify.");
if(valid_nsec) {
if(wc) {
/* check if this is a delegation */
+ *reason = "NSEC for wildcard does not prove absence of DS";
return val_nsec_proves_no_ds(wc_nsec, qinfo);
}
/* valid nsec proves empty nonterminal */
* @param rep: reply received.
* @param kkey: key entry to use for verification of signatures.
* @param proof_ttl: if secure, the TTL of how long this proof lasts.
+ * @param reason: string explaining why bogus.
* @return security status.
* SECURE: proved absence of DS.
* INSECURE: proved that this was not a delegation point.
enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env,
struct val_env* ve, struct query_info* qinfo,
struct reply_info* rep, struct key_entry_key* kkey,
- uint32_t* proof_ttl);
+ uint32_t* proof_ttl, char** reason);
/**
* nsec typemap check, takes an NSEC-type bitmap as argument, checks for type.
static int
list_is_secure(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
- struct key_entry_key* kkey)
+ struct key_entry_key* kkey, char** reason)
{
size_t i;
enum sec_status sec;
- char* reason = NULL;
for(i=0; i<num; i++) {
if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3))
continue;
- sec = val_verify_rrset_entry(env, ve, list[i], kkey, &reason);
+ sec = val_verify_rrset_entry(env, ve, list[i], kkey, reason);
if(sec != sec_status_secure) {
verbose(VERB_ALGO, "NSEC3 did not verify");
return 0;
enum sec_status
nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
- struct query_info* qinfo, struct key_entry_key* kkey)
+ struct query_info* qinfo, struct key_entry_key* kkey, char** reason)
{
rbtree_t ct;
struct nsec3_filter flt;
int rr;
log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
- if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
+ if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) {
+ *reason = "no valid NSEC3s";
return sec_status_bogus; /* no valid NSEC3s, bogus */
- if(!list_is_secure(env, ve, list, num, kkey))
+ }
+ if(!list_is_secure(env, ve, list, num, kkey, reason))
return sec_status_bogus; /* not all NSEC3 records secure */
rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
filter_init(&flt, list, num, qinfo); /* init RR iterator */
- if(!flt.zone)
+ if(!flt.zone) {
+ *reason = "no NSEC3 records";
return sec_status_bogus; /* no RRs */
+ }
if(nsec3_iteration_count_high(ve, &flt, kkey))
return sec_status_insecure; /* iteration count too high */
qinfo->qname_len != 1) {
verbose(VERB_ALGO, "nsec3 provenods: NSEC3 is from"
" child zone, bogus");
+ *reason = "NSEC3 from child zone";
return sec_status_bogus;
} else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) {
verbose(VERB_ALGO, "nsec3 provenods: NSEC3 has qtype"
" DS, bogus");
+ *reason = "NSEC3 has DS in bitmap";
return sec_status_bogus;
}
/* If the NSEC3 RR doesn't have the NS bit set, then
if(!nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce)) {
verbose(VERB_ALGO, "nsec3 provenods: did not match qname, "
"nor found a proven closest encloser.");
+ *reason = "no NSEC3 closest encloser";
return sec_status_bogus;
}
if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) {
verbose(VERB_ALGO, "nsec3 provenods: covering NSEC3 was not "
"opt-out in an opt-out DS NOERROR/NODATA case.");
+ *reason = "covering NSEC3 was not opt-out in an opt-out "
+ "DS NOERROR/NODATA case";
return sec_status_bogus;
}
return sec_status_secure;
* @param num: number of RRsets in the array to examine.
* @param qinfo: query that is verified for.
* @param kkey: key entry that signed the NSEC3s.
+ * @param reason: string for bogus result.
* @return:
* sec_status SECURE of the proposition is proven by the NSEC3 RRs,
* BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored.
enum sec_status
nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
- struct query_info* qinfo, struct key_entry_key* kkey);
+ struct query_info* qinfo, struct key_entry_key* kkey, char** reason);
/**
* Prove NXDOMAIN or NODATA.
/* Try to prove absence of the DS with NSEC */
sec = val_nsec_prove_nodata_dsreply(
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
- &proof_ttl);
+ &proof_ttl, &reason);
switch(sec) {
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC RRset for the "
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral did not prove no DS.");
- val_errinf(qstate, vq, "NSEC DS absent proof "
- "failed");
+ val_errinf(qstate, vq, reason);
goto return_bogus;
case sec_status_unchecked:
default:
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
- msg->rep->ns_numrrsets, qinfo, vq->key_entry);
+ msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason);
switch(sec) {
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC3s for the "
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
- val_errinf(qstate, vq, "NSEC3 DS absent proof "
- "failed");
+ val_errinf(qstate, vq, reason);
goto return_bogus;
case sec_status_insecure:
case sec_status_unchecked: