]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: when validating an RRset, store information about the synthesizing source...
authorLennart Poettering <lennart@poettering.net>
Thu, 14 Jan 2016 17:03:03 +0000 (18:03 +0100)
committerLennart Poettering <lennart@poettering.net>
Sun, 17 Jan 2016 19:47:45 +0000 (20:47 +0100)
Having this information available is useful when we need to check whether various RRs are suitable for proofs. This
information is stored in the RRs as number of labels to skip from the beginning of the owner name to reach the
synthesizing source/signer. Simple accessor calls are then added to retrieve the signer/source from the RR using this
information.

This also moves validation of a a number of RRSIG parameters into a new call dnssec_rrsig_prepare() that as side-effect
initializes the two numeric values.

src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-transaction.c
src/shared/dns-domain.c
src/shared/dns-domain.h

index b6fb362daa0c35b74405c7529118d46df2a53bc6..37fc3150f0a742b5b7863c6b05d7626a5a2e7e94 100644 (file)
@@ -430,6 +430,57 @@ static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
         gcry_md_write(md, &v, sizeof(v));
 }
 
+static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
+        int n_key_labels, n_signer_labels;
+        const char *name;
+        int r;
+
+        /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and
+         * .n_skip_labels_signer fields so that we can use them later on. */
+
+        assert(rrsig);
+        assert(rrsig->key->type == DNS_TYPE_RRSIG);
+
+        /* Check if this RRSIG RR is already prepared */
+        if (rrsig->n_skip_labels_source != (unsigned) -1)
+                return 0;
+
+        if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
+                return -EINVAL;
+
+        name = DNS_RESOURCE_KEY_NAME(rrsig->key);
+
+        n_key_labels = dns_name_count_labels(name);
+        if (n_key_labels < 0)
+                return n_key_labels;
+        if (rrsig->rrsig.labels > n_key_labels)
+                return -EINVAL;
+
+        n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
+        if (n_signer_labels < 0)
+                return n_signer_labels;
+        if (n_signer_labels > rrsig->rrsig.labels)
+                return -EINVAL;
+
+        r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        /* Check if the signer is really a suffix of us */
+        r = dns_name_equal(name, rrsig->rrsig.signer);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
+        rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
+
+        return 0;
+}
+
 static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
         usec_t expiration, inception, skew;
 
@@ -499,6 +550,35 @@ static int algorithm_to_gcrypt_md(uint8_t algorithm) {
         }
 }
 
+static void dnssec_fix_rrset_ttl(
+                DnsResourceRecord *list[],
+                unsigned n,
+                DnsResourceRecord *rrsig,
+                usec_t realtime) {
+
+        unsigned k;
+
+        assert(list);
+        assert(n > 0);
+        assert(rrsig);
+
+        for (k = 0; k < n; k++) {
+                DnsResourceRecord *rr = list[k];
+
+                /* Pick the TTL as the minimum of the RR's TTL, the
+                 * RR's original TTL according to the RRSIG and the
+                 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
+                rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
+                rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+
+                /* Copy over information about the signer and wildcard source of synthesis */
+                rr->n_skip_labels_source = rrsig->n_skip_labels_source;
+                rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
+        }
+
+        rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
+}
+
 int dnssec_verify_rrset(
                 DnsAnswer *a,
                 const DnsResourceKey *key,
@@ -536,6 +616,14 @@ int dnssec_verify_rrset(
         if (md_algorithm < 0)
                 return md_algorithm;
 
+        r = dnssec_rrsig_prepare(rrsig);
+        if (r == -EINVAL) {
+                *result = DNSSEC_INVALID;
+                return r;
+        }
+        if (r < 0)
+                return r;
+
         r = dnssec_rrsig_expired(rrsig, realtime);
         if (r < 0)
                 return r;
@@ -699,12 +787,17 @@ int dnssec_verify_rrset(
         if (r < 0)
                 goto finish;
 
-        if (!r)
+        /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
+        if (r > 0)
+                dnssec_fix_rrset_ttl(list, n, rrsig, realtime);
+
+        if (r == 0)
                 *result = DNSSEC_INVALID;
         else if (wildcard)
                 *result = DNSSEC_VALIDATED_WILDCARD;
         else
                 *result = DNSSEC_VALIDATED;
+
         r = 0;
 
 finish:
@@ -743,8 +836,6 @@ 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);
 
@@ -757,45 +848,9 @@ 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));
 }
 
-static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
-        DnsResourceRecord *rr;
-        int r;
-
-        assert(key);
-        assert(rrsig);
-
-        DNS_ANSWER_FOREACH(rr, a) {
-                r = dns_resource_key_equal(key, rr->key);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        continue;
-
-                /* Pick the TTL as the minimum of the RR's TTL, the
-                 * RR's original TTL according to the RRSIG and the
-                 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
-                rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
-                rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
-        }
-
-        return 0;
-}
-
 int dnssec_verify_rrset_search(
                 DnsAnswer *a,
                 const DnsResourceKey *key,
@@ -865,10 +920,6 @@ int dnssec_verify_rrset_search(
                         case DNSSEC_VALIDATED_WILDCARD:
                                 /* Yay, the RR has been validated,
                                  * return immediately, but fix up the expiry */
-                                r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
-                                if (r < 0)
-                                        return r;
-
                                 if (ret_rrsig)
                                         *ret_rrsig = rrsig;
 
@@ -1658,15 +1709,17 @@ int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name
                 if (rr->key->type != type && type != DNS_TYPE_ANY)
                         continue;
 
-                r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        continue;
-
                 switch (rr->key->type) {
 
                 case DNS_TYPE_NSEC:
+
+                        /* We only care for NSEC RRs from the indicated zone */
+                        r = dns_resource_record_is_signer(rr, zone);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
                         r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
                         if (r < 0)
                                 return r;
@@ -1677,6 +1730,13 @@ int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name
                 case DNS_TYPE_NSEC3: {
                         _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
 
+                        /* We only care for NSEC3 RRs from the indicated zone */
+                        r = dns_resource_record_is_signer(rr, zone);
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
+
                         r = nsec3_is_good(rr, NULL);
                         if (r < 0)
                                 return r;
index dbf840157f27530aeaca893f889ab49ba34c0ce0..53fd7083653af8357b57ba4ae0ecd3b93bb8df16 100644 (file)
@@ -344,6 +344,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
         rr->n_ref = 1;
         rr->key = dns_resource_key_ref(key);
         rr->expiry = USEC_INFINITY;
+        rr->n_skip_labels_signer = rr->n_skip_labels_source = (unsigned) -1;
 
         return rr;
 }
@@ -1085,6 +1086,63 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
         return 0;
 }
 
+int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) {
+        const char *n;
+        int r;
+
+        assert(rr);
+        assert(ret);
+
+        /* Returns the RRset's signer, if it is known. */
+
+        if (rr->n_skip_labels_signer == (unsigned) -1)
+                return -ENODATA;
+
+        n = DNS_RESOURCE_KEY_NAME(rr->key);
+        r = dns_name_skip(n, rr->n_skip_labels_signer, &n);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        *ret = n;
+        return 0;
+}
+
+int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) {
+        const char *n;
+        int r;
+
+        assert(rr);
+        assert(ret);
+
+        /* Returns the RRset's synthesizing source, if it is known. */
+
+        if (rr->n_skip_labels_source == (unsigned) -1)
+                return -ENODATA;
+
+        n = DNS_RESOURCE_KEY_NAME(rr->key);
+        r = dns_name_skip(n, rr->n_skip_labels_source, &n);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        *ret = n;
+        return 0;
+}
+
+int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone) {
+        const char *signer;
+        int r;
+
+        r = dns_resource_record_signer(rr, &signer);
+        if (r < 0)
+                return r;
+
+        return dns_name_equal(zone, signer);
+}
+
 static void dns_resource_record_hash_func(const void *i, struct siphash *state) {
         const DnsResourceRecord *rr = i;
 
index fe29a4156693eaf0a3b113372392ab14ea9e37fc..8e7bfaa7c730156dd643ef5d2ee0f869c3697e43 100644 (file)
@@ -108,14 +108,24 @@ struct DnsTxtItem {
 struct DnsResourceRecord {
         unsigned n_ref;
         DnsResourceKey *key;
+
         char *to_string;
+
         uint32_t ttl;
         usec_t expiry; /* RRSIG signature expiry */
+
+        /* How many labels to strip to determine "signer" of the RRSIG (aka, the zone). -1 if not signed. */
+        unsigned n_skip_labels_signer;
+        /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */
+        unsigned n_skip_labels_source;
+
         bool unparseable:1;
+
         bool wire_format_canonical:1;
         void *wire_format;
         size_t wire_format_size;
         size_t wire_format_rdata_offset;
+
         union {
                 struct {
                         void *data;
@@ -296,6 +306,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
 
 int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
 
+int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret);
+int dns_resource_record_source(DnsResourceRecord *rr, const char **ret);
+int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone);
+
 DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
 bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
 
index c7d2d82ecf84d00c8fd8eef0a612c9fd3661beb2..8fe581b33c8cd5348e5e5f46c77418f181108c3c 100644 (file)
@@ -2537,11 +2537,9 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                                  * that no matching non-wildcard RR exists.*/
 
                                 /* First step, determine the source of synthesis */
-                                r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &source);
+                                r = dns_resource_record_source(rrsig, &source);
                                 if (r < 0)
                                         return r;
-                                if (r == 0)
-                                        return -EBADMSG;
 
                                 r = dnssec_test_positive_wildcard(
                                                 validated,
index d1fb97fe9245a935f5afa9e370b40c253b7e2e13..ee0108715d262bf3ceab2f47c0ff83d6ace0cd4d 100644 (file)
@@ -1189,6 +1189,26 @@ int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
         return (int) (n - n_labels);
 }
 
+int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
+        int r;
+
+        assert(a);
+        assert(ret);
+
+        for (; n_labels > 0; n_labels --) {
+                r = dns_name_parent(&a);
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        *ret = "";
+                        return 0;
+                }
+        }
+
+        *ret = a;
+        return 1;
+}
+
 int dns_name_count_labels(const char *name) {
         unsigned n = 0;
         const char *p;
@@ -1219,14 +1239,9 @@ int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
         assert(a);
         assert(b);
 
-        while (n_labels > 0) {
-
-                r = dns_name_parent(&a);
-                if (r <= 0)
-                        return r;
-
-                n_labels --;
-        }
+        r = dns_name_skip(a, n_labels, &a);
+        if (r <= 0)
+                return r;
 
         return dns_name_equal(a, b);
 }
index 4fbe0a618f98c96cd1dec62718994015e4a6a9b8..a679d4095839b7c081aecb42c0068b239499801e 100644 (file)
@@ -104,4 +104,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_skip(const char *a, unsigned n_labels, const char **ret);
 int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b);