diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/resolve/resolved-dns-dnssec.c | 134 | ||||
| -rw-r--r-- | src/resolve/resolved-dns-transaction.c | 2 | ||||
| -rw-r--r-- | src/shared/dns-domain.c | 18 | ||||
| -rw-r--r-- | src/shared/dns-domain.h | 2 | ||||
| -rw-r--r-- | src/test/test-dns-domain.c | 32 | 
5 files changed, 131 insertions, 57 deletions
| diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index dbda71b03d..edd1efc739 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -909,11 +909,63 @@ finish:          return r;  } +static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) { +        const char *a, *b; +        int r; + +        assert(rr); + +        if ((flags & DNS_ANSWER_AUTHENTICATED) == 0) +                return 0; + +        if (rr->key->type != DNS_TYPE_NSEC3) +                return 0; + +        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */ +        if (!IN_SET(rr->nsec3.flags, 0, 1)) +                return 0; + +        if (!nsec3) +                return 1; + +        /* If a second NSEC3 RR is specified, also check if they are from the same zone. */ + +        if (nsec3 == rr) /* Shortcut */ +                return 1; + +        if (rr->key->class != nsec3->key->class) +                return 0; +        if (rr->nsec3.algorithm != nsec3->nsec3.algorithm) +                return 0; +        if (rr->nsec3.iterations != nsec3->nsec3.iterations) +                return 0; +        if (rr->nsec3.salt_size != nsec3->nsec3.salt_size) +                return 0; +        if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0) +                return 0; + +        a = DNS_RESOURCE_KEY_NAME(rr->key); +        r = dns_name_parent(&a); /* strip off hash */ +        if (r < 0) +                return r; +        if (r == 0) +                return 0; + +        b = DNS_RESOURCE_KEY_NAME(nsec3->key); +        r = dns_name_parent(&b); /* strip off hash */ +        if (r < 0) +                return r; +        if (r == 0) +                return 0; + +        return dns_name_equal(a, b); +} +  static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {          _cleanup_free_ char *next_closer_domain = NULL, *l = NULL;          uint8_t hashed[DNSSEC_HASH_SIZE_MAX];          const char *suffix, *p, *pp = NULL; -        DnsResourceRecord *rr; +        DnsResourceRecord *rr, *suffix_rr;          DnsAnswerFlags flags;          int hashed_size, r; @@ -923,20 +975,16 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR          /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */          suffix = DNS_RESOURCE_KEY_NAME(key);          for (;;) { -                DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { +                DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {                          _cleanup_free_ char *hashed_domain = NULL, *label = NULL; -                        if ((flags & DNS_ANSWER_AUTHENTICATED) == 0) -                                continue; - -                        if (rr->key->type != DNS_TYPE_NSEC3) -                                continue; - -                        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */ -                        if (!IN_SET(rr->nsec3.flags, 0, 1)) +                        r = nsec3_is_good(suffix_rr, flags, NULL); +                        if (r < 0) +                                return r; +                        if (r == 0)                                  continue; -                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), suffix); +                        r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, suffix);                          if (r < 0)                                  return r;                          if (r > 0) @@ -958,42 +1006,34 @@ found_suffix:          /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */          p = DNS_RESOURCE_KEY_NAME(key);          for (;;) { -                DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { -                        _cleanup_free_ char *hashed_domain = NULL, *label = NULL; +                _cleanup_free_ char *hashed_domain = NULL, *label = NULL; -                        if ((flags & DNS_ANSWER_AUTHENTICATED) == 0) -                                continue; +                hashed_size = dnssec_nsec3_hash(suffix_rr, p, hashed); +                if (hashed_size == -EOPNOTSUPP) { +                        *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM; +                        return 0; +                } +                if (hashed_size < 0) +                        return hashed_size; -                        if (rr->key->type != DNS_TYPE_NSEC3) -                                continue; +                label = base32hexmem(hashed, hashed_size, false); +                if (!label) +                        return -ENOMEM; -                        /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */ -                        if (!IN_SET(rr->nsec3.flags, 0, 1)) -                                continue; +                hashed_domain = strjoin(label, ".", suffix, NULL); +                if (!hashed_domain) +                        return -ENOMEM; + +                DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { -                        r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), suffix); +                        r = nsec3_is_good(rr, flags, suffix_rr);                          if (r < 0)                                  return r;                          if (r == 0)                                  continue; -                        hashed_size = dnssec_nsec3_hash(rr, p, hashed); -                        if (hashed_size == -EOPNOTSUPP) { -                                *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM; -                                return 0; -                        } -                        if (hashed_size < 0) -                                return hashed_size;                          if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size) -                                return -EBADMSG; - -                        label = base32hexmem(hashed, hashed_size, false); -                        if (!label) -                                return -ENOMEM; - -                        hashed_domain = strjoin(label, ".", suffix, NULL); -                        if (!hashed_domain) -                                return -ENOMEM; +                                continue;                          r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);                          if (r < 0) @@ -1056,26 +1096,8 @@ found_closest_encloser:          DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {                  _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL; -                const char *nsec3_parent; - -                if ((flags & DNS_ANSWER_AUTHENTICATED) == 0) -                        continue; - -                if (rr->key->type != DNS_TYPE_NSEC3) -                        continue; - -                /* RFC  5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */ -                if (!IN_SET(rr->nsec3.flags, 0, 1)) -                        continue; - -                nsec3_parent = DNS_RESOURCE_KEY_NAME(rr->key); -                r = dns_name_parent(&nsec3_parent); -                if (r < 0) -                        return r; -                if (r == 0) -                        continue; -                r = dns_name_equal(p, nsec3_parent); +                r = nsec3_is_good(rr, flags, suffix_rr);                  if (r < 0)                          return r;                  if (r == 0) diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index d2aec13264..1d02d7e942 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1516,7 +1516,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {                          if (!soa)                                  return -ENOMEM; -                        log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key)); +                        log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dns_resource_record_to_string(rr));                          r = dns_transaction_request_dnssec_rr(t, soa);                          if (r < 0)                                  return r; diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index f3dbf60395..f44e80c9db 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -1215,3 +1215,21 @@ int dns_name_count_labels(const char *name) {          return (int) n;  } + +int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) { +        int r; + +        assert(a); +        assert(b); + +        while (n_labels > 0) { + +                r = dns_name_parent(&a); +                if (r <= 0) +                        return r; + +                n_labels --; +        } + +        return dns_name_equal(a, b); +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 7b509729fb..dd8ae3ac98 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -102,3 +102,5 @@ int dns_service_split(const char *joined, char **name, char **type, char **domai  int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);  int dns_name_count_labels(const char *name); + +int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index 3a15ea5c06..78c5631ff1 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -515,6 +515,37 @@ static void test_dns_name_count_labels(void) {          test_dns_name_count_labels_one("..", -EINVAL);  } +static void test_dns_name_equal_skip_one(const char *a, unsigned n_labels, const char *b, int ret) { +        assert_se(dns_name_equal_skip(a, n_labels, b) == ret); +} + +static void test_dns_name_equal_skip(void) { +        test_dns_name_equal_skip_one("foo", 0, "bar", 0); +        test_dns_name_equal_skip_one("foo", 0, "foo", 1); +        test_dns_name_equal_skip_one("foo", 1, "foo", 0); +        test_dns_name_equal_skip_one("foo", 2, "foo", 0); + +        test_dns_name_equal_skip_one("foo.bar", 0, "foo.bar", 1); +        test_dns_name_equal_skip_one("foo.bar", 1, "foo.bar", 0); +        test_dns_name_equal_skip_one("foo.bar", 2, "foo.bar", 0); +        test_dns_name_equal_skip_one("foo.bar", 3, "foo.bar", 0); + +        test_dns_name_equal_skip_one("foo.bar", 0, "bar", 0); +        test_dns_name_equal_skip_one("foo.bar", 1, "bar", 1); +        test_dns_name_equal_skip_one("foo.bar", 2, "bar", 0); +        test_dns_name_equal_skip_one("foo.bar", 3, "bar", 0); + +        test_dns_name_equal_skip_one("foo.bar", 0, "", 0); +        test_dns_name_equal_skip_one("foo.bar", 1, "", 0); +        test_dns_name_equal_skip_one("foo.bar", 2, "", 1); +        test_dns_name_equal_skip_one("foo.bar", 3, "", 0); + +        test_dns_name_equal_skip_one("", 0, "", 1); +        test_dns_name_equal_skip_one("", 1, "", 0); +        test_dns_name_equal_skip_one("", 1, "foo", 0); +        test_dns_name_equal_skip_one("", 2, "foo", 0); +} +  int main(int argc, char *argv[]) {          test_dns_label_unescape(); @@ -537,6 +568,7 @@ int main(int argc, char *argv[]) {          test_dns_name_change_suffix();          test_dns_name_suffix();          test_dns_name_count_labels(); +        test_dns_name_equal_skip();          return 0;  } | 
