From: Wouter Wijngaards Date: Mon, 20 Aug 2007 13:39:58 +0000 (+0000) Subject: nodata validation. X-Git-Tag: release-0.5~108 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5605f8d003954984cf3128a74f0dd9d40f88fa57;p=thirdparty%2Funbound.git nodata validation. git-svn-id: file:///svn/unbound/trunk@533 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/Changelog b/doc/Changelog index 761d65233..44fa414cc 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 18 August 2007: Wouter - process DNSKEY response in FINDKEY state. - validate and positive validation, positive wildcard NSEC validation. + - nodata validation. 17 August 2007: Wouter - work on DS2KE routine. diff --git a/validator/val_nsec.c b/validator/val_nsec.c index 89eb14a94..29a55ecbe 100644 --- a/validator/val_nsec.c +++ b/validator/val_nsec.c @@ -353,12 +353,8 @@ val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname) return 0; } -/** - * Determine closest encloser of a query name and the NSEC that covers it - * (and thus disproved it). - */ -static uint8_t* nsec_closest_encloser(uint8_t* qname, - struct ub_packed_rrset_key* nsec) +uint8_t* +nsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec) { uint8_t* next; size_t nlen; diff --git a/validator/val_nsec.h b/validator/val_nsec.h index bc73af859..296f9356c 100644 --- a/validator/val_nsec.h +++ b/validator/val_nsec.h @@ -112,4 +112,15 @@ int val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, struct query_info* qinf, uint8_t* wc); +/** + * Determine closest encloser of a query name and the NSEC that covers it + * (and thus disproved it). + * A name error must have been proven already, otherwise this will be invalid. + * @param qname: the name queried for. + * @param nsec: the nsec RRset. + * @return closest encloser dname or NULL on error (bad nsec RRset). + */ +uint8_t* nsec_closest_encloser(uint8_t* qname, + struct ub_packed_rrset_key* nsec); + #endif /* VALIDATOR_VAL_NSEC_H */ diff --git a/validator/validator.c b/validator/validator.c index a1cc0a269..dcfc38e3c 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -385,16 +385,110 @@ validate_positive_response(struct module_env* env, struct val_env* ve, return; } - verbose(VERB_ALGO, "Successfully validated postive response"); + verbose(VERB_ALGO, "Successfully validated positive response"); chase_reply->security = sec_status_secure; } -/** validate NODATA */ +/** + * Validate a NOERROR/NODATA signed response -- a response that has a + * NOERROR Rcode but no ANSWER section RRsets. This consists of verifying + * the authority section rrsets and making certain that the authority + * section NSEC/NSEC3s proves that the qname does exist and the qtype doesn't. + * + * 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_nodata_response(struct module_env* env, struct val_env* ve, struct query_info* qchase, struct reply_info* chase_reply, struct key_entry_key* key_entry) { + /* Since we are here, there must be nothing in the ANSWER section to + * validate. */ + /* (Note: CNAME/DNAME responses will not directly get here -- + * instead they are broken down into individual CNAME/DNAME/final answer + * responses. - TODO this will change though) */ + + /* validate the AUTHORITY section */ + int has_valid_nsec = 0; /* If true, then the NODATA has been proven.*/ + uint8_t* ce = NULL; /* for wildcard nodata responses. This is the + proven closest encloser. */ + uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ + int nsec3s_seen = 0; /* nsec3s seen */ + struct ub_packed_rrset_key* s; + enum sec_status sec; + size_t i; + + for(i=chase_reply->an_numrrsets; ian_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, "NODATA 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 we encounter an NSEC record, try to use it to prove + * NODATA. + * This needs to handle the ENT NODATA case. */ + if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { + if(nsec_proves_nodata(s, qchase)) { + has_valid_nsec = 1; + if(dname_is_wild(s->rk.dname)) + wc = s->rk.dname; + } + 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) { + nsec3s_seen = 1; + } + } + + /* check to see if we have a wildcard NODATA proof. */ + + /* The wildcard NODATA is 1 NSEC proving that qname does not exists + * (and also proving what the closest encloser is), and 1 NSEC + * showing the matching wildcard, which must be *.closest_encloser. */ + if(wc && !ce) + has_valid_nsec = 0; + else if(wc && ce) { + log_assert(dname_is_wild(wc)); + /* first label wc is \001*, so remove and compare to ce */ + if(query_dname_compare(wc+2, ce) != 0) { + has_valid_nsec = 0; + } + } + + if(!has_valid_nsec && nsec3s_seen) { + /* TODO handle NSEC3 proof here */ + /* and set has_valid_nsec=1; if so */ + } + + if(!has_valid_nsec) { + verbose(VERB_ALGO, "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; } /** validate NAME ERROR (nxdomain) response */ @@ -468,6 +562,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq, if(vq->key_entry == NULL || dname_strict_subdomain_c( vq->trust_anchor->name, vq->key_entry->name)) { /* fire off a trust anchor priming query. */ + verbose(VERB_ALGO, "prime trust anchor"); if(!prime_trust_anchor(qstate, vq, id, vq->trust_anchor)) return val_error(qstate, id); /* and otherwise, don't continue processing this event. @@ -1148,9 +1243,9 @@ 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=", + log_query_info(VERB_ALGO, "validator: inform_super, sub is", &qstate->qinfo); - log_query_info(VERB_ALGO, "super=", &super->qinfo); + log_query_info(VERB_ALGO, "super is", &super->qinfo); if(!vq) { verbose(VERB_ALGO, "super: has no validator state"); return;