mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05: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
7 changed files with 525 additions and 15 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
- work on DS2KE routine.
|
- work on DS2KE routine.
|
||||||
- val_nsec.c for validator NSEC proofs.
|
- val_nsec.c for validator NSEC proofs.
|
||||||
- unit test for NSEC bitmap reading.
|
- unit test for NSEC bitmap reading.
|
||||||
|
- dname iswild and canonical_compare with unit tests.
|
||||||
|
|
||||||
16 August 2007: Wouter
|
16 August 2007: Wouter
|
||||||
- DS sig unit test.
|
- DS sig unit test.
|
||||||
|
|
|
||||||
|
|
@ -482,6 +482,235 @@ dname_test_sigcount()
|
||||||
(uint8_t*)"\001*\003www\007example\003xom\000") == 3);
|
(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()
|
void dname_test()
|
||||||
{
|
{
|
||||||
ldns_buffer* buff = ldns_buffer_new(65800);
|
ldns_buffer* buff = ldns_buffer_new(65800);
|
||||||
|
|
@ -498,5 +727,7 @@ void dname_test()
|
||||||
dname_test_isroot();
|
dname_test_isroot();
|
||||||
dname_test_removelabel();
|
dname_test_removelabel();
|
||||||
dname_test_sigcount();
|
dname_test_sigcount();
|
||||||
|
dname_test_iswild();
|
||||||
|
dname_test_canoncmp();
|
||||||
ldns_buffer_free(buff);
|
ldns_buffer_free(buff);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -621,3 +621,107 @@ dname_signame_label_count(uint8_t* dname)
|
||||||
}
|
}
|
||||||
return count;
|
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 labs1: number of labels in first dname.
|
||||||
* @param d2: second dname. pointer to uncompressed wireformat.
|
* @param d2: second dname. pointer to uncompressed wireformat.
|
||||||
* @param labs2: number of labels in second dname.
|
* @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.
|
* @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);
|
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);
|
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 */
|
#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;
|
size_t mybyte = type_low>>3;
|
||||||
if(winlen <= mybyte)
|
if(winlen <= mybyte)
|
||||||
return 0; /* window too short */
|
return 0; /* window too short */
|
||||||
return bitmap[mybyte] & masks[type_low&0x7];
|
return (int)(bitmap[mybyte] & masks[type_low&0x7]);
|
||||||
} else {
|
} else {
|
||||||
/* not the window we are looking for */
|
/* not the window we are looking for */
|
||||||
bitmap += winlen;
|
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);
|
len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
|
||||||
if(!len)
|
if(!len)
|
||||||
return 0;
|
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.
|
* insecure if it proves it is not a delegation point.
|
||||||
* or bogus if something was wrong.
|
* or bogus if something was wrong.
|
||||||
*/
|
*/
|
||||||
enum sec_status
|
static enum sec_status
|
||||||
val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
|
val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
|
||||||
struct query_info* qinfo)
|
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 */
|
/* this proof may also work if qname is a subdomain */
|
||||||
log_assert(query_dname_compare(nsec->rk.dname, qinfo->qname) == 0);
|
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
|
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 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)
|
||||||
{
|
{
|
||||||
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
|
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
|
||||||
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
|
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
|
||||||
qinfo->qclass);
|
qinfo->qclass);
|
||||||
|
enum sec_status sec;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
/* If we have a NSEC at the same name, it must prove one
|
/* If we have a NSEC at the same name, it must prove one
|
||||||
* of two things
|
* 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
|
* 1) this is a delegation point and there is no DS
|
||||||
* 2) this is not a delegation point */
|
* 2) this is not a delegation point */
|
||||||
if(nsec) {
|
if(nsec) {
|
||||||
enum sec_status sec = val_verify_rrset_entry(env, ve, nsec,
|
sec = val_verify_rrset_entry(env, ve, nsec, kkey);
|
||||||
kkey);
|
|
||||||
if(sec != sec_status_secure) {
|
if(sec != sec_status_secure) {
|
||||||
verbose(VERB_ALGO, "NSEC RRset for the "
|
verbose(VERB_ALGO, "NSEC RRset for the "
|
||||||
"referral did not verify.");
|
"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.
|
/* Otherwise, there is no NSEC at qname. This could be an ENT.
|
||||||
* (ENT=empty non terminal). If not, this is broken. */
|
* (ENT=empty non terminal). If not, this is broken. */
|
||||||
|
|
||||||
/* verify NSEC rrsets in auth section, call */
|
/* verify NSEC rrsets in auth section */
|
||||||
/* ValUtils.nsecProvesNodata, if so: NULL entry */
|
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.
|
* SECURE: proved absence of DS.
|
||||||
* INSECURE: proved that this was not a delegation point.
|
* INSECURE: proved that this was not a delegation point.
|
||||||
* BOGUS: crypto bad, or no absence of DS proven.
|
* 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 val_env* ve, struct query_info* qinfo,
|
||||||
struct reply_info* rep, struct key_entry_key* kkey,
|
struct reply_info* rep, struct key_entry_key* kkey,
|
||||||
uint32_t* proof_ttl);
|
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 */
|
/** Unit test call to test function for nsec typemap check */
|
||||||
int unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type);
|
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 */
|
#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;
|
uint32_t proof_ttl = 0;
|
||||||
|
|
||||||
/* Try to prove absence of the DS with NSEC */
|
/* Try to prove absence of the DS with NSEC */
|
||||||
enum sec_status sec = val_nsec_prove_nodata_ds(qstate->env, ve,
|
enum sec_status sec = val_nsec_prove_nodata_dsreply(
|
||||||
qinfo, msg->rep, vq->key_entry, &proof_ttl);
|
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
|
||||||
|
&proof_ttl);
|
||||||
switch(sec) {
|
switch(sec) {
|
||||||
case sec_status_secure:
|
case sec_status_secure:
|
||||||
verbose(VERB_ALGO, "NSEC RRset for the "
|
verbose(VERB_ALGO, "NSEC RRset for the "
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue