resolved: when we find a DNAME RR, don't insist in a signed CNAME RR
authorLennart Poettering <lennart@poettering.net>
Sun, 17 Jan 2016 20:50:10 +0000 (21:50 +0100)
committerLennart Poettering <lennart@poettering.net>
Sun, 17 Jan 2016 20:50:10 +0000 (21:50 +0100)
If we have a signed DNAME RR response, there's no need to insist on a signature for a CNAME RR response, after all it
is unlikely to be signed, given the implicit synthethis of CNAME through DNAME RRs.

src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-answer.h
src/resolve/resolved-dns-transaction.c

index c359432..f74e440 100644 (file)
@@ -821,3 +821,40 @@ void dns_answer_dump(DnsAnswer *answer, FILE *f) {
                 fputc('\n', f);
         }
 }
+
+bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
+        DnsResourceRecord *rr;
+        int r;
+
+        assert(cname);
+
+        /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
+         * synthesized from it */
+
+        if (cname->key->type != DNS_TYPE_CNAME)
+                return 0;
+
+        DNS_ANSWER_FOREACH(rr, a) {
+                _cleanup_free_ char *n = NULL;
+
+                if (rr->key->type != DNS_TYPE_DNAME)
+                        continue;
+                if (rr->key->class != cname->key->class)
+                        continue;
+
+                r = dns_name_change_suffix(cname->cname.name, rr->dname.name, DNS_RESOURCE_KEY_NAME(rr->key), &n);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                r = dns_name_equal(n, DNS_RESOURCE_KEY_NAME(cname->key));
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 1;
+
+        }
+
+        return 0;
+}
index 3eff21f..1875fd6 100644 (file)
@@ -83,6 +83,8 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr);
 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags);
 int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags);
 
+bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname);
+
 static inline unsigned dns_answer_size(DnsAnswer *a) {
         return a ? a->n_rrs : 0;
 }
index 72ef283..393171a 100644 (file)
@@ -1827,6 +1827,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
                         if (r > 0)
                                 continue;
 
+                        r = dns_answer_has_dname_for_cname(t->answer, rr);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                continue;
+
                         name = DNS_RESOURCE_KEY_NAME(rr->key);
                         r = dns_name_parent(&name);
                         if (r < 0)
@@ -2719,17 +2725,30 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                         if (r < 0)
                                 return r;
                         if (r > 0) {
-                                /* This is a primary response
-                                 * to our question, and it
-                                 * failed validation. That's
-                                 * fatal. */
-                                t->answer_dnssec_result = result;
-                                return 0;
+
+                                /* Look for a matching DNAME for this CNAME */
+                                r = dns_answer_has_dname_for_cname(t->answer, rr);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0) {
+                                        /* Also look among the stuff we already validated */
+                                        r = dns_answer_has_dname_for_cname(validated, rr);
+                                        if (r < 0)
+                                                return r;
+                                }
+
+                                if (r == 0) {
+                                        /* This is a primary response to our question, and it failed validation. That's
+                                         * fatal. */
+                                        t->answer_dnssec_result = result;
+                                        return 0;
+                                }
+
+                                /* This is a primary response, but we do have a DNAME RR in the RR that can replay this
+                                 * CNAME, hence rely on that, and we can remove the CNAME in favour of it. */
                         }
 
-                        /* This is just some auxiliary
-                         * data. Just remove the RRset and
-                         * continue. */
+                        /* This is just some auxiliary data. Just remove the RRset and continue. */
                         r = dns_answer_remove_by_key(&t->answer, rr->key);
                         if (r < 0)
                                 return r;