mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-28 10:39:33 -05:00
nxdomain validation.
git-svn-id: file:///svn/unbound/trunk@534 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
5605f8d003
commit
0f9ae7acd8
4 changed files with 131 additions and 16 deletions
|
|
@ -1,7 +1,7 @@
|
|||
18 August 2007: Wouter
|
||||
- process DNSKEY response in FINDKEY state.
|
||||
- validate and positive validation, positive wildcard NSEC validation.
|
||||
- nodata validation.
|
||||
- nodata validation, nxdomain validation.
|
||||
|
||||
17 August 2007: Wouter
|
||||
- work on DS2KE routine.
|
||||
|
|
|
|||
|
|
@ -330,19 +330,21 @@ val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* see if this nsec is the only nsec */
|
||||
if(query_dname_compare(owner, next) == 0) {
|
||||
/* only zone.name NSEC zone.name, disproves everything else */
|
||||
return 1;
|
||||
}
|
||||
/* see if this nsec is the last nsec */
|
||||
if(dname_canonical_compare(owner, next) > 0) {
|
||||
/* this is the last nsec, ....(bigger) NSEC zonename(smaller) */
|
||||
/* the names after the last (owner) name do not exist */
|
||||
if(dname_canonical_compare(owner, qname) < 0)
|
||||
/* this nsec is the only nsec */
|
||||
/* zone.name NSEC zone.name, disproves everything else */
|
||||
/* but only for subdomains of that zone */
|
||||
if(dname_strict_subdomain_c(qname, next))
|
||||
return 1;
|
||||
}
|
||||
else if(dname_canonical_compare(owner, next) > 0) {
|
||||
/* this is the last nsec, ....(bigger) NSEC zonename(smaller) */
|
||||
/* the names after the last (owner) name do not exist
|
||||
* there are no names before the zone name in the zone
|
||||
* but the qname must be a subdomain of the zone name(next). */
|
||||
if(dname_canonical_compare(owner, qname) < 0 &&
|
||||
dname_strict_subdomain_c(qname, next))
|
||||
return 1;
|
||||
/* there are no names before the zone name in the zone */
|
||||
/* if(dname_canonical_compare(qname, next) < 0) return 1; */
|
||||
} else {
|
||||
/* regular NSEC, (smaller) NSEC (larger) */
|
||||
if(dname_canonical_compare(owner, qname) < 0 &&
|
||||
|
|
@ -387,3 +389,38 @@ int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec,
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
|
||||
size_t qnamelen)
|
||||
{
|
||||
/* Determine if a NSEC record proves the non-existence of a
|
||||
* wildcard that could have produced qname. */
|
||||
int labs;
|
||||
int i;
|
||||
uint8_t* ce = nsec_closest_encloser(qname, nsec);
|
||||
uint8_t* strip;
|
||||
size_t striplen;
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+3];
|
||||
if(!ce)
|
||||
return 0;
|
||||
/* we can subtract the closest encloser count - since that is the
|
||||
* largest shared topdomain with owner and next NSEC name,
|
||||
* because the NSEC is no proof for names shorter than the owner
|
||||
* and next names. */
|
||||
labs = dname_count_labels(qname) - dname_count_labels(ce);
|
||||
|
||||
for(i=labs; i>0; i--) {
|
||||
/* i is number of labels to strip off qname, prepend * wild */
|
||||
strip = qname;
|
||||
striplen = qnamelen;
|
||||
dname_remove_labels(&strip, &striplen, i);
|
||||
buf[0] = 1;
|
||||
buf[1] = (uint8_t)'*';
|
||||
memmove(buf+2, strip, striplen);
|
||||
if(val_nsec_proves_name_error(nsec, buf)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,4 +123,15 @@ int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec,
|
|||
uint8_t* nsec_closest_encloser(uint8_t* qname,
|
||||
struct ub_packed_rrset_key* nsec);
|
||||
|
||||
/**
|
||||
* Determine if the given NSEC proves that a wildcard match does not exist.
|
||||
*
|
||||
* @param nsec: the nsec RRset.
|
||||
* @param qname: the name queried for.
|
||||
* @param qnamelen: length of qname.
|
||||
* @return true if proven.
|
||||
*/
|
||||
int val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname,
|
||||
size_t qnamelen);
|
||||
|
||||
#endif /* VALIDATOR_VAL_NSEC_H */
|
||||
|
|
|
|||
|
|
@ -451,9 +451,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
|
|||
if(val_nsec_proves_name_error(s, qchase->qname)) {
|
||||
ce = nsec_closest_encloser(qchase->qname, s);
|
||||
}
|
||||
}
|
||||
|
||||
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
|
||||
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
|
||||
nsec3s_seen = 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -491,12 +489,81 @@ validate_nodata_response(struct module_env* env, struct val_env* ve,
|
|||
chase_reply->security = sec_status_secure;
|
||||
}
|
||||
|
||||
/** validate NAME ERROR (nxdomain) response */
|
||||
/**
|
||||
* Validate a NAMEERROR signed response -- a response that has a NXDOMAIN
|
||||
* Rcode. This consists of verifying the authority section rrsets and making
|
||||
* certain that the authority section NSEC proves that the qname doesn't
|
||||
* exist and the covering wildcard also doesn't exist..
|
||||
*
|
||||
* Note that by the time this method is called, the process of finding the
|
||||
* trusted DNSKEY rrset that signs this response must already have been
|
||||
* completed.
|
||||
*
|
||||
* @param env: module env for verify.
|
||||
* @param ve: validator env for verify.
|
||||
* @param qchase: query that was made.
|
||||
* @param chase_reply: answer to that query to validate.
|
||||
* @param key_entry: the key entry, which is trusted, and which matches
|
||||
* the signer of the answer. The key entry isgood().
|
||||
*/
|
||||
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* key_entry)
|
||||
{
|
||||
/* FIXME: should we check to see if there is anything in the answer
|
||||
* section? if so, what should the result be? */
|
||||
|
||||
int has_valid_nsec = 0;
|
||||
int has_valid_wnsec = 0;
|
||||
int nsec3s_seen = 0;
|
||||
struct ub_packed_rrset_key* s;
|
||||
enum sec_status sec;
|
||||
size_t i;
|
||||
|
||||
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);
|
||||
if(sec != sec_status_secure) {
|
||||
log_nametypeclass(VERB_ALGO, "NameError response has "
|
||||
"failed AUTHORITY rrset: ", 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_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;
|
||||
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3)
|
||||
nsec3s_seen = 1;
|
||||
}
|
||||
|
||||
if(!has_valid_nsec || !has_valid_wnsec) {
|
||||
/* TODO: use NSEC3 proof */
|
||||
}
|
||||
|
||||
/* If the message fails to prove either condition, it is bogus. */
|
||||
if(!has_valid_nsec) {
|
||||
verbose(VERB_ALGO, "NameError response has failed to prove: "
|
||||
"qname does not exist");
|
||||
chase_reply->security = sec_status_bogus;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!has_valid_wnsec) {
|
||||
verbose(VERB_ALGO, "NameError response has failed to prove: "
|
||||
"covering wildcard does not exist");
|
||||
chase_reply->security = sec_status_bogus;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, we consider the message secure. */
|
||||
verbose(VERB_ALGO, "successfully validated NAME ERROR response.");
|
||||
chase_reply->security = sec_status_secure;
|
||||
}
|
||||
|
||||
/** validate positive ANY response */
|
||||
|
|
|
|||
Loading…
Reference in a new issue