mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 14:47:09 +00:00
nsec work, canonical compare routine and tests.
git-svn-id: file:///svn/unbound/trunk@530 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
cedeaa8316
commit
453df0c66c
@ -2,6 +2,7 @@
|
||||
- work on DS2KE routine.
|
||||
- val_nsec.c for validator NSEC proofs.
|
||||
- unit test for NSEC bitmap reading.
|
||||
- dname iswild and canonical_compare with unit tests.
|
||||
|
||||
16 August 2007: Wouter
|
||||
- DS sig unit test.
|
||||
|
@ -482,6 +482,235 @@ dname_test_sigcount()
|
||||
(uint8_t*)"\001*\003www\007example\003xom\000") == 3);
|
||||
}
|
||||
|
||||
/** test dname_is_wild routine */
|
||||
static void
|
||||
dname_test_iswild()
|
||||
{
|
||||
unit_assert( !dname_is_wild((uint8_t*)"\000") );
|
||||
unit_assert( dname_is_wild((uint8_t*)"\001*\000") );
|
||||
unit_assert( !dname_is_wild((uint8_t*)"\003net\000") );
|
||||
unit_assert( dname_is_wild((uint8_t*)"\001*\003net\000") );
|
||||
}
|
||||
|
||||
/** test dname_canonical_compare */
|
||||
static void
|
||||
dname_test_canoncmp()
|
||||
{
|
||||
/* equality */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net\000",
|
||||
(uint8_t*)"\003net\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003net\000",
|
||||
(uint8_t*)"\007example\003net\000"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004test\007example\003net\000",
|
||||
(uint8_t*)"\004test\007example\003net\000"
|
||||
) == 0);
|
||||
|
||||
/* subdomains */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\000"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\003com"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\003com"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\007example\003com"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003com",
|
||||
(uint8_t*)"\000"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\000",
|
||||
(uint8_t*)"\007example\003com"
|
||||
) == -1);
|
||||
|
||||
/* compare rightmost label */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003com",
|
||||
(uint8_t*)"\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003com"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003net",
|
||||
(uint8_t*)"\003org"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example\003net",
|
||||
(uint8_t*)"\003org"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003org",
|
||||
(uint8_t*)"\007example\003net"
|
||||
) == 1);
|
||||
|
||||
/* label length makes a difference; but only if rest is equal */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004neta",
|
||||
(uint8_t*)"\003net"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\002ne",
|
||||
(uint8_t*)"\004neta"
|
||||
) == -1);
|
||||
|
||||
/* label content */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003aag\007example\003net",
|
||||
(uint8_t*)"\003bla\007example\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bla\007example\003net",
|
||||
(uint8_t*)"\003aag\007example\003net"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bla\003aag\007example\003net",
|
||||
(uint8_t*)"\003aag\003bla\007example\003net"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\02sn\003opt\003aag\007example\003net",
|
||||
(uint8_t*)"\02sn\003opt\003bla\007example\003net"
|
||||
) == -1);
|
||||
|
||||
/* lowercase during compare */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\003bLa\007examPLe\003net",
|
||||
(uint8_t*)"\003bla\007eXAmple\003nET"
|
||||
) == 0);
|
||||
|
||||
/* example from 4034 */
|
||||
/* example a.example yljkjljk.a.example Z.a.example zABC.a.EXAMPLE
|
||||
z.example \001.z.example *.z.example \200.z.example */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"",
|
||||
(uint8_t*)"\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == -1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001\200\001z\007example"
|
||||
) == -1);
|
||||
/* same example in reverse */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)""
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == 1);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\200\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == 1);
|
||||
/* same example for equality */
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\007example",
|
||||
(uint8_t*)"\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001a\007example",
|
||||
(uint8_t*)"\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\010yljkjljk\001a\007example",
|
||||
(uint8_t*)"\010yljkjljk\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001Z\001a\007example",
|
||||
(uint8_t*)"\001Z\001a\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE",
|
||||
(uint8_t*)"\004zABC\001a\007EXAMPLE"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001z\007example",
|
||||
(uint8_t*)"\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\001\001z\007example",
|
||||
(uint8_t*)"\001\001\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001*\001z\007example",
|
||||
(uint8_t*)"\001*\001z\007example"
|
||||
) == 0);
|
||||
unit_assert( dname_canonical_compare(
|
||||
(uint8_t*)"\001\200\001z\007example",
|
||||
(uint8_t*)"\001\200\001z\007example"
|
||||
) == 0);
|
||||
}
|
||||
|
||||
void dname_test()
|
||||
{
|
||||
ldns_buffer* buff = ldns_buffer_new(65800);
|
||||
@ -498,5 +727,7 @@ void dname_test()
|
||||
dname_test_isroot();
|
||||
dname_test_removelabel();
|
||||
dname_test_sigcount();
|
||||
dname_test_iswild();
|
||||
dname_test_canoncmp();
|
||||
ldns_buffer_free(buff);
|
||||
}
|
||||
|
@ -621,3 +621,107 @@ dname_signame_label_count(uint8_t* dname)
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
dname_is_wild(uint8_t* dname)
|
||||
{
|
||||
return (dname[0] == 1 && dname[1] == '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare labels in memory, lowercase while comparing.
|
||||
* Returns canonical order for labels. If all is equal, the
|
||||
* shortest is first.
|
||||
*
|
||||
* @param p1: label 1
|
||||
* @param len1: length of label 1.
|
||||
* @param p2: label 2
|
||||
* @param len2: length of label 2.
|
||||
* @return: 0, -1, +1 comparison result.
|
||||
*/
|
||||
static int
|
||||
memcanoncmp(uint8_t* p1, uint8_t len1, uint8_t* p2, uint8_t len2)
|
||||
{
|
||||
uint8_t min = (len1<len2)?len1:len2;
|
||||
int c = memlowercmp(p1, p2, min);
|
||||
if(c != 0)
|
||||
return c;
|
||||
/* equal, see who is shortest */
|
||||
if(len1 < len2)
|
||||
return -1;
|
||||
if(len1 > len2)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs)
|
||||
{
|
||||
/* like dname_lab_cmp, but with different label comparison,
|
||||
* empty character sorts before \000.
|
||||
* So ylyly is before z. */
|
||||
uint8_t len1, len2;
|
||||
int atlabel = labs1;
|
||||
int lastmlabs;
|
||||
int lastdiff = 0;
|
||||
int c;
|
||||
/* first skip so that we compare same label. */
|
||||
if(labs1 > labs2) {
|
||||
while(atlabel > labs2) {
|
||||
len1 = *d1++;
|
||||
d1 += len1;
|
||||
atlabel--;
|
||||
}
|
||||
log_assert(atlabel == labs2);
|
||||
} else if(labs1 < labs2) {
|
||||
atlabel = labs2;
|
||||
while(atlabel > labs1) {
|
||||
len2 = *d2++;
|
||||
d2 += len2;
|
||||
atlabel--;
|
||||
}
|
||||
log_assert(atlabel == labs1);
|
||||
}
|
||||
lastmlabs = atlabel+1;
|
||||
/* now at same label in d1 and d2, atlabel */
|
||||
/* www.example.com. */
|
||||
/* 4 3 2 1 atlabel number */
|
||||
/* repeat until at root label (which is always the same) */
|
||||
while(atlabel > 1) {
|
||||
len1 = *d1++;
|
||||
len2 = *d2++;
|
||||
|
||||
if((c=memcanoncmp(d1, len1, d2, len2)) != 0) {
|
||||
if(c<0)
|
||||
lastdiff = -1;
|
||||
else lastdiff = 1;
|
||||
lastmlabs = atlabel;
|
||||
}
|
||||
|
||||
d1 += len1;
|
||||
d2 += len2;
|
||||
atlabel--;
|
||||
}
|
||||
/* last difference atlabel number, so number of labels matching,
|
||||
* at the right side, is one less. */
|
||||
*mlabs = lastmlabs-1;
|
||||
if(lastdiff == 0) {
|
||||
/* all labels compared were equal, check if one has more
|
||||
* labels, so that example.com. > com. */
|
||||
if(labs1 > labs2)
|
||||
return 1;
|
||||
else if(labs1 < labs2)
|
||||
return -1;
|
||||
}
|
||||
return lastdiff;
|
||||
}
|
||||
|
||||
int
|
||||
dname_canonical_compare(uint8_t* d1, uint8_t* d2)
|
||||
{
|
||||
int labs1, labs2, m;
|
||||
labs1 = dname_count_labels(d1);
|
||||
labs2 = dname_count_labels(d2);
|
||||
return dname_canon_lab_cmp(d1, labs1, d2, labs2, &m);
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ int dname_count_size_labels(uint8_t* dname, size_t* size);
|
||||
* @param labs1: number of labels in first dname.
|
||||
* @param d2: second dname. pointer to uncompressed wireformat.
|
||||
* @param labs2: number of labels in second dname.
|
||||
* @param mlabs: number of labels that matched exactly.
|
||||
* @param mlabs: number of labels that matched exactly (the shared topdomain).
|
||||
* @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
|
||||
*/
|
||||
int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs);
|
||||
@ -249,4 +249,35 @@ void dname_remove_labels(uint8_t** dname, size_t* len, int n);
|
||||
*/
|
||||
int dname_signame_label_count(uint8_t* dname);
|
||||
|
||||
/**
|
||||
* Return true if the label is a wildcard, *.example.com.
|
||||
* @param dname: valid uncompressed wireformat.
|
||||
* @return true if wildcard, or false.
|
||||
*/
|
||||
int dname_is_wild(uint8_t* dname);
|
||||
|
||||
/**
|
||||
* Compare dnames, Canonical in rfc4034 sense, but by label.
|
||||
* Such that zone contents follows zone apex.
|
||||
*
|
||||
* @param d1: first dname. pointer to uncompressed wireformat.
|
||||
* @param labs1: number of labels in first dname.
|
||||
* @param d2: second dname. pointer to uncompressed wireformat.
|
||||
* @param labs2: number of labels in second dname.
|
||||
* @param mlabs: number of labels that matched exactly (the shared topdomain).
|
||||
* @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
|
||||
*/
|
||||
int dname_canon_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2,
|
||||
int* mlabs);
|
||||
|
||||
/**
|
||||
* Canonical dname compare. Takes care of counting labels.
|
||||
* Per rfc 4034 canonical order.
|
||||
*
|
||||
* @param d1: first dname. pointer to uncompressed wireformat.
|
||||
* @param d2: second dname. pointer to uncompressed wireformat.
|
||||
* @return: 0 for equal, -1 smaller, or +1 d1 larger than d2.
|
||||
*/
|
||||
int dname_canonical_compare(uint8_t* d1, uint8_t* d2);
|
||||
|
||||
#endif /* UTIL_DATA_DNAME_H */
|
||||
|
@ -71,7 +71,7 @@ nsec_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
|
||||
size_t mybyte = type_low>>3;
|
||||
if(winlen <= mybyte)
|
||||
return 0; /* window too short */
|
||||
return bitmap[mybyte] & masks[type_low&0x7];
|
||||
return (int)(bitmap[mybyte] & masks[type_low&0x7]);
|
||||
} else {
|
||||
/* not the window we are looking for */
|
||||
bitmap += winlen;
|
||||
@ -107,7 +107,36 @@ nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
|
||||
len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
|
||||
if(!len)
|
||||
return 0;
|
||||
nsec_has_type_rdata(d->rr_data[0]+2+len, d->rr_len[0]-2-len, type);
|
||||
return nsec_has_type_rdata(d->rr_data[0]+2+len,
|
||||
d->rr_len[0]-2-len, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next owner name from nsec record
|
||||
* @param nsec: the nsec RRset.
|
||||
* If there are multiple RRs, then this will only return one of them.
|
||||
* @param nm: the next name is returned.
|
||||
* @param ln: length of nm is returned.
|
||||
* @return false on a bad NSEC RR (too short, malformed dname).
|
||||
*/
|
||||
static int
|
||||
nsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln)
|
||||
{
|
||||
struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
|
||||
entry.data;
|
||||
if(!d || d->count == 0 || d->rr_len[0] < 2+1) {
|
||||
*nm = 0;
|
||||
*ln = 0;
|
||||
return 0;
|
||||
}
|
||||
*nm = d->rr_data[0]+2;
|
||||
*ln = dname_valid(*nm, d->rr_len[0]-2);
|
||||
if(!*ln) {
|
||||
*nm = 0;
|
||||
*ln = 0;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +148,7 @@ nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
|
||||
* insecure if it proves it is not a delegation point.
|
||||
* or bogus if something was wrong.
|
||||
*/
|
||||
enum sec_status
|
||||
static enum sec_status
|
||||
val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
|
||||
struct query_info* qinfo)
|
||||
{
|
||||
@ -128,17 +157,34 @@ val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
|
||||
/* this proof may also work if qname is a subdomain */
|
||||
log_assert(query_dname_compare(nsec->rk.dname, qinfo->qname) == 0);
|
||||
|
||||
return sec_status_bogus;
|
||||
if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) ||
|
||||
nsec_has_type(nsec, LDNS_RR_TYPE_DS)) {
|
||||
/* SOA present means that this is the NSEC from the child,
|
||||
* not the parent (so it is the wrong one).
|
||||
* DS present means that there should have been a positive
|
||||
* response to the DS query, so there is something wrong. */
|
||||
return sec_status_bogus;
|
||||
}
|
||||
|
||||
if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) {
|
||||
/* If there is no NS at this point at all, then this
|
||||
* doesn't prove anything one way or the other. */
|
||||
return sec_status_insecure;
|
||||
}
|
||||
/* Otherwise, this proves no DS. */
|
||||
return sec_status_secure;
|
||||
}
|
||||
|
||||
enum sec_status
|
||||
val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
|
||||
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 ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
|
||||
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
|
||||
qinfo->qclass);
|
||||
enum sec_status sec;
|
||||
size_t i;
|
||||
|
||||
/* If we have a NSEC at the same name, it must prove one
|
||||
* of two things
|
||||
@ -146,8 +192,7 @@ val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
|
||||
* 1) this is a delegation point and there is no DS
|
||||
* 2) this is not a delegation point */
|
||||
if(nsec) {
|
||||
enum sec_status sec = val_verify_rrset_entry(env, ve, nsec,
|
||||
kkey);
|
||||
sec = val_verify_rrset_entry(env, ve, nsec, kkey);
|
||||
if(sec != sec_status_secure) {
|
||||
verbose(VERB_ALGO, "NSEC RRset for the "
|
||||
"referral did not verify.");
|
||||
@ -171,8 +216,91 @@ val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
|
||||
/* Otherwise, there is no NSEC at qname. This could be an ENT.
|
||||
* (ENT=empty non terminal). If not, this is broken. */
|
||||
|
||||
/* verify NSEC rrsets in auth section, call */
|
||||
/* ValUtils.nsecProvesNodata, if so: NULL entry */
|
||||
/* verify NSEC rrsets in auth section */
|
||||
for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets;
|
||||
i++) {
|
||||
if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
|
||||
continue;
|
||||
sec = val_verify_rrset_entry(env, ve, rep->rrsets[i], kkey);
|
||||
if(sec != sec_status_secure) {
|
||||
verbose(VERB_ALGO, "NSEC for empty non-terminal "
|
||||
"did not verify.");
|
||||
return sec_status_bogus;
|
||||
}
|
||||
if(nsec_proves_nodata(rep->rrsets[i], qinfo)) {
|
||||
verbose(VERB_ALGO, "NSEC for empty non-terminal "
|
||||
"proved no DS.");
|
||||
return sec_status_secure;
|
||||
}
|
||||
}
|
||||
|
||||
return sec_status_bogus;
|
||||
/* NSEC proof did not conlusively point to DS or no DS */
|
||||
return sec_status_unchecked;
|
||||
}
|
||||
|
||||
int nsec_proves_nodata(struct ub_packed_rrset_key* nsec,
|
||||
struct query_info* qinfo)
|
||||
{
|
||||
if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) {
|
||||
uint8_t* nm;
|
||||
size_t ln;
|
||||
/* wildcard checking. */
|
||||
|
||||
/* If this is a wildcard NSEC, make sure that a) it was
|
||||
* possible to have generated qname from the wildcard and
|
||||
* b) the type map does not contain qtype. Note that this
|
||||
* does NOT prove that this wildcard was the applicable
|
||||
* wildcard. */
|
||||
if(dname_is_wild(nsec->rk.dname)) {
|
||||
/* the purported closest encloser. */
|
||||
uint8_t* ce = nsec->rk.dname;
|
||||
size_t ce_len = nsec->rk.dname_len;
|
||||
dname_remove_label(&ce, &ce_len);
|
||||
|
||||
/* The qname must be a strict subdomain of the
|
||||
* closest encloser, and the qtype must be absent
|
||||
* from the type map. */
|
||||
if(!dname_strict_subdomain_c(qinfo->qname, ce) ||
|
||||
nsec_has_type(nsec, qinfo->qtype)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* empty-non-terminal checking. */
|
||||
|
||||
/* If the nsec is proving that qname is an ENT, the nsec owner
|
||||
* will be less than qname, and the next name will be a child
|
||||
* domain of the qname. */
|
||||
if(!nsec_get_next(nsec, &nm, &ln))
|
||||
return 0; /* bad nsec */
|
||||
if(dname_strict_subdomain_c(nm, qinfo->qname) &&
|
||||
dname_canonical_compare(nsec->rk.dname,
|
||||
qinfo->qname) < 0) {
|
||||
return 1; /* proves ENT */
|
||||
}
|
||||
/* Otherwise, this NSEC does not prove ENT, so it does not
|
||||
* prove NODATA. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the qtype exists, then we should have gotten it. */
|
||||
if(nsec_has_type(nsec, qinfo->qtype)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if the name is a CNAME node, then we should have gotten the CNAME*/
|
||||
if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If an NS set exists at this name, and NOT a SOA (so this is a
|
||||
* zone cut, not a zone apex), then we should have gotten a
|
||||
* referral (or we just got the wrong NSEC). */
|
||||
if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) &&
|
||||
!nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -67,9 +67,9 @@ struct key_entry_key;
|
||||
* SECURE: proved absence of DS.
|
||||
* INSECURE: proved that this was not a delegation point.
|
||||
* BOGUS: crypto bad, or no absence of DS proven.
|
||||
* UNCHECKED: there was no way to prove anything (no nsecs, unknown algo).
|
||||
* UNCHECKED: there was no way to prove anything (no NSECs, unknown algo).
|
||||
*/
|
||||
enum sec_status val_nsec_prove_nodata_ds(struct module_env* env,
|
||||
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);
|
||||
@ -77,4 +77,18 @@ enum sec_status val_nsec_prove_nodata_ds(struct module_env* env,
|
||||
/** Unit test call to test function for nsec typemap check */
|
||||
int unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type);
|
||||
|
||||
/**
|
||||
* Determine if a NSEC proves the NOERROR/NODATA conditions. This will also
|
||||
* handle the empty non-terminal (ENT) case and partially handle the
|
||||
* wildcard case. If the ownername of 'nsec' is a wildcard, the validator
|
||||
* must still be provided proof that qname did not directly exist and that
|
||||
* the wildcard is, in fact, *.closest_encloser.
|
||||
*
|
||||
* @param nsec: the nsec record to check against.
|
||||
* @param qinfo: the query info.
|
||||
* @return true if NSEC proves this.
|
||||
*/
|
||||
int nsec_proves_nodata(struct ub_packed_rrset_key* nsec,
|
||||
struct query_info* qinfo);
|
||||
|
||||
#endif /* VALIDATOR_VAL_NSEC_H */
|
||||
|
@ -690,8 +690,9 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
|
||||
uint32_t proof_ttl = 0;
|
||||
|
||||
/* Try to prove absence of the DS with NSEC */
|
||||
enum sec_status sec = val_nsec_prove_nodata_ds(qstate->env, ve,
|
||||
qinfo, msg->rep, vq->key_entry, &proof_ttl);
|
||||
enum sec_status sec = val_nsec_prove_nodata_dsreply(
|
||||
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
|
||||
&proof_ttl);
|
||||
switch(sec) {
|
||||
case sec_status_secure:
|
||||
verbose(VERB_ALGO, "NSEC RRset for the "
|
||||
|
Loading…
Reference in New Issue
Block a user