resolved: correctly prove the non-existense of wildcard

* Current logic:
For each NSEC RR find the common suffix between the owner name and
the next name, append asterisk to that suffix and check that
generated wildcard is covered by the NSEC RR in question.

* New logic:
Find NSEC RR covering queried name, generate wildcard as
<asterisk>.<closest encloser> using this RR, then check if any
of the NSEC RRs covers generated wildcard.
This commit is contained in:
Alexander Tsoy 2019-01-23 16:33:50 +03:00 committed by Lennart Poettering
parent 4b05f0c9d9
commit 13e6f3831d
1 changed files with 47 additions and 31 deletions

View File

@ -1797,22 +1797,14 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
return dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
}
static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
_cleanup_free_ char *wc = NULL;
const char *common_suffix, *signer;
int r;
static int dnssec_nsec_generate_wildcard(DnsResourceRecord *rr, const char *name, char **wc) {
const char *common_suffix1, *common_suffix2, *signer;
int r, labels1, labels2;
assert(rr);
assert(rr->key->type == DNS_TYPE_NSEC);
/* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified
* RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as
* suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label.
*
* NSEC bar waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist
* NSEC waldo.foo.bar yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...)
* NSEC yyy.zzz.xoo.bar bar: indicates that a number of wildcards don#t exist either...
*/
/* Generates "Wildcard at the Closest Encloser" for the given name and NSEC RR. */
r = dns_resource_record_signer(rr, &signer);
if (r < 0)
@ -1822,23 +1814,31 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name)
if (r <= 0)
return r;
r = dns_name_endswith(name, dns_resource_key_name(rr->key));
if (r < 0)
return r;
if (r > 0) /* If the name we are interested in is a child of the NSEC RR, then append the asterisk to the NSEC
* RR's name. */
r = dns_name_concat("*", dns_resource_key_name(rr->key), 0, &wc);
else {
r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
if (r < 0)
return r;
r = dns_name_concat("*", common_suffix, 0, &wc);
}
r = dns_name_common_suffix(name, dns_resource_key_name(rr->key), &common_suffix1);
if (r < 0)
return r;
return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
r = dns_name_common_suffix(name, rr->nsec.next_domain_name, &common_suffix2);
if (r < 0)
return r;
labels1 = dns_name_count_labels(common_suffix1);
if (labels1 < 0)
return labels1;
labels2 = dns_name_count_labels(common_suffix2);
if (labels2 < 0)
return labels2;
if (labels1 > labels2)
r = dns_name_concat("*", common_suffix1, 0, wc);
else
r = dns_name_concat("*", common_suffix2, 0, wc);
if (r < 0)
return r;
return 0;
}
int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
@ -1942,14 +1942,30 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
covering_rr = rr;
covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
}
}
/* Check if this NSEC RR proves the absence of a wildcard RR under this name */
r = dnssec_nsec_covers_wildcard(rr, name);
if (covering_rr) {
_cleanup_free_ char *wc = NULL;
r = dnssec_nsec_generate_wildcard(covering_rr, name, &wc);
if (r < 0)
return r;
if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
wildcard_rr = rr;
wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
if (rr->key->class != key->class)
continue;
if (rr->key->type != DNS_TYPE_NSEC)
continue;
/* Check if this NSEC RR proves the nonexistence of the wildcard */
r = dnssec_nsec_covers(rr, wc);
if (r < 0)
return r;
if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
wildcard_rr = rr;
wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
}
}
}