resolved: properly implement RRSIG validation of wildcarded RRsets
Note that this is still not complete, one additional step is still missing: when we verified that a wildcard RRset is properly signed, we still need to do an NSEC/NSEC3 proof that no more specific RRset exists.
This commit is contained in:
@@ -384,10 +384,17 @@ int dnssec_verify_rrset(
|
||||
gcry_md_write(md, wire_format_name, r);
|
||||
|
||||
for (k = 0; k < n; k++) {
|
||||
const char *suffix;
|
||||
size_t l;
|
||||
rr = list[k];
|
||||
|
||||
r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
|
||||
r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r > 0) /* This is a wildcard! */
|
||||
gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
|
||||
|
||||
r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
gcry_md_write(md, wire_format_name, r);
|
||||
@@ -497,6 +504,8 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske
|
||||
}
|
||||
|
||||
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
assert(rrsig);
|
||||
|
||||
@@ -509,6 +518,18 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig)
|
||||
if (rrsig->rrsig.type_covered != key->type)
|
||||
return 0;
|
||||
|
||||
/* Make sure signer is a parent of the RRset */
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Make sure the owner name has at least as many labels as the "label" fields indicates. */
|
||||
r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < rrsig->rrsig.labels)
|
||||
return 0;
|
||||
|
||||
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
|
||||
}
|
||||
|
||||
|
||||
@@ -1159,3 +1159,59 @@ finish:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
|
||||
const char* labels[DNS_N_LABELS_MAX+1];
|
||||
unsigned n = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
p = name;
|
||||
for (;;) {
|
||||
if (n > DNS_N_LABELS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
labels[n] = p;
|
||||
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n < n_labels)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = labels[n - n_labels];
|
||||
return (int) (n - n_labels);
|
||||
}
|
||||
|
||||
int dns_name_count_labels(const char *name) {
|
||||
unsigned n = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
p = name;
|
||||
for (;;) {
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (n >= DNS_N_LABELS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
/* Maximum length of a full hostname, on the wire, including the final NUL byte */
|
||||
#define DNS_WIRE_FOMAT_HOSTNAME_MAX 255
|
||||
|
||||
/* Maximum number of labels per valid hostname */
|
||||
#define DNS_N_LABELS_MAX 127
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz);
|
||||
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
|
||||
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
|
||||
@@ -96,3 +99,6 @@ bool dns_service_name_is_valid(const char *name);
|
||||
|
||||
int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
|
||||
int dns_service_split(const char *joined, char **name, char **type, char **domain);
|
||||
|
||||
int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);
|
||||
int dns_name_count_labels(const char *name);
|
||||
|
||||
@@ -475,6 +475,46 @@ static void test_dns_name_change_suffix(void) {
|
||||
test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
|
||||
}
|
||||
|
||||
static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) {
|
||||
const char *p = NULL;
|
||||
|
||||
assert_se(ret == dns_name_suffix(name, n_labels, &p));
|
||||
assert_se(streq_ptr(p, result));
|
||||
}
|
||||
|
||||
static void test_dns_name_suffix(void) {
|
||||
test_dns_name_suffix_one("foo.bar", 2, "foo.bar", 0);
|
||||
test_dns_name_suffix_one("foo.bar", 1, "bar", 1);
|
||||
test_dns_name_suffix_one("foo.bar", 0, "", 2);
|
||||
test_dns_name_suffix_one("foo.bar", 3, NULL, -EINVAL);
|
||||
test_dns_name_suffix_one("foo.bar", 4, NULL, -EINVAL);
|
||||
|
||||
test_dns_name_suffix_one("bar", 1, "bar", 0);
|
||||
test_dns_name_suffix_one("bar", 0, "", 1);
|
||||
test_dns_name_suffix_one("bar", 2, NULL, -EINVAL);
|
||||
test_dns_name_suffix_one("bar", 3, NULL, -EINVAL);
|
||||
|
||||
test_dns_name_suffix_one("", 0, "", 0);
|
||||
test_dns_name_suffix_one("", 1, NULL, -EINVAL);
|
||||
test_dns_name_suffix_one("", 2, NULL, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_dns_name_count_labels_one(const char *name, int n) {
|
||||
assert_se(dns_name_count_labels(name) == n);
|
||||
}
|
||||
|
||||
static void test_dns_name_count_labels(void) {
|
||||
test_dns_name_count_labels_one("foo.bar.quux.", 3);
|
||||
test_dns_name_count_labels_one("foo.bar.quux", 3);
|
||||
test_dns_name_count_labels_one("foo.bar.", 2);
|
||||
test_dns_name_count_labels_one("foo.bar", 2);
|
||||
test_dns_name_count_labels_one("foo.", 1);
|
||||
test_dns_name_count_labels_one("foo", 1);
|
||||
test_dns_name_count_labels_one("", 0);
|
||||
test_dns_name_count_labels_one(".", 0);
|
||||
test_dns_name_count_labels_one("..", -EINVAL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
test_dns_label_unescape();
|
||||
@@ -495,6 +535,8 @@ int main(int argc, char *argv[]) {
|
||||
test_dns_service_join();
|
||||
test_dns_service_split();
|
||||
test_dns_name_change_suffix();
|
||||
test_dns_name_suffix();
|
||||
test_dns_name_count_labels();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user