resolved: add a concept of "authenticated" responses
authorLennart Poettering <lennart@poettering.net>
Thu, 3 Dec 2015 20:04:52 +0000 (21:04 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 3 Dec 2015 20:17:49 +0000 (21:17 +0100)
This adds a new SD_RESOLVED_AUTHENTICATED flag for responses we return
on the bus. When set, then the data has been authenticated. For now this
mostly reflects the DNSSEC AD bit, if DNSSEC=trust is set. As soon as
the client-side validation is complete it will be hooked up to this flag
too.

We also set this bit whenver we generated the data ourselves, for
example, because it originates in our local LLMNR zone, or from the
built-in trust anchor database.

The "systemd-resolve-host" tool has been updated to show the flag state
for the data it shows.

src/resolve-host/resolve-host.c
src/resolve/resolved-bus.c
src/resolve/resolved-def.h
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-cache.h
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h

index 36dfc70..0f154d9 100644 (file)
@@ -67,6 +67,8 @@ static void print_source(uint64_t flags, usec_t rtt) {
 
         fputc('.', stdout);
         fputc('\n', stdout);
+
+        printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
 }
 
 static int resolve_host(sd_bus *bus, const char *name) {
index ddde3af..0ceca56 100644 (file)
@@ -197,7 +197,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
         r = sd_bus_message_append(
                         reply, "st",
                         DNS_RESOURCE_KEY_NAME(canonical->key),
-                        SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+                        SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
         if (r < 0)
                 goto finish;
 
@@ -344,7 +344,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
         if (r < 0)
                 goto finish;
 
-        r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+        r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
         if (r < 0)
                 goto finish;
 
@@ -510,7 +510,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
         if (r < 0)
                 goto finish;
 
-        r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+        r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
         if (r < 0)
                 goto finish;
 
@@ -859,7 +859,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
                         reply,
                         "ssst",
                         name, type, domain,
-                        SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
+                        SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
         if (r < 0)
                 goto finish;
 
index be29f51..db5ee57 100644 (file)
@@ -28,6 +28,7 @@
 #define SD_RESOLVED_NO_TXT        (UINT64_C(1) << 6)
 #define SD_RESOLVED_NO_ADDRESS    (UINT64_C(1) << 7)
 #define SD_RESOLVED_NO_SEARCH     (UINT64_C(1) << 8)
+#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
 
 #define SD_RESOLVED_LLMNR         (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
 #define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
index 241187d..bcb9994 100644 (file)
@@ -46,6 +46,7 @@ struct DnsCacheItem {
         usec_t until;
         DnsCacheItemType type;
         unsigned prioq_idx;
+        bool authenticated;
         int owner_family;
         union in_addr_union owner_address;
         LIST_FIELDS(DnsCacheItem, by_key);
@@ -237,7 +238,7 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
         return NULL;
 }
 
-static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, usec_t timestamp) {
+static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) {
         assert(c);
         assert(i);
         assert(rr);
@@ -257,6 +258,7 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
         dns_resource_key_unref(i->key);
         i->key = dns_resource_key_ref(rr->key);
 
+        i->authenticated = authenticated;
         i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
 
         prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
@@ -265,6 +267,7 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
 static int dns_cache_put_positive(
                 DnsCache *c,
                 DnsResourceRecord *rr,
+                bool authenticated,
                 usec_t timestamp,
                 int owner_family,
                 const union in_addr_union *owner_address) {
@@ -300,7 +303,7 @@ static int dns_cache_put_positive(
         /* Entry exists already? Update TTL and timestamp */
         existing = dns_cache_get(c, rr);
         if (existing) {
-                dns_cache_item_update_positive(c, existing, rr, timestamp);
+                dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
                 return 0;
         }
 
@@ -322,6 +325,7 @@ static int dns_cache_put_positive(
         i->prioq_idx = PRIOQ_IDX_NULL;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
+        i->authenticated = authenticated;
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
@@ -341,6 +345,7 @@ static int dns_cache_put_negative(
                 DnsCache *c,
                 DnsResourceKey *key,
                 int rcode,
+                bool authenticated,
                 usec_t timestamp,
                 uint32_t soa_ttl,
                 int owner_family,
@@ -389,6 +394,7 @@ static int dns_cache_put_negative(
         i->prioq_idx = PRIOQ_IDX_NULL;
         i->owner_family = owner_family;
         i->owner_address = *owner_address;
+        i->authenticated = authenticated;
 
         r = dns_cache_link_item(c, i);
         if (r < 0)
@@ -410,6 +416,7 @@ int dns_cache_put(
                 int rcode,
                 DnsAnswer *answer,
                 unsigned max_rrs,
+                bool authenticated,
                 usec_t timestamp,
                 int owner_family,
                 const union in_addr_union *owner_address) {
@@ -452,7 +459,7 @@ int dns_cache_put(
 
         /* Second, add in positive entries for all contained RRs */
         for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
-                r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address);
+                r = dns_cache_put_positive(c, answer->items[i].rr, authenticated, timestamp, owner_family, owner_address);
                 if (r < 0)
                         goto fail;
         }
@@ -496,13 +503,13 @@ int dns_cache_put(
                         if (!dns_answer_match_soa(canonical_key, soa->key))
                                 continue;
 
-                        r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+                        r = dns_cache_put_negative(c, canonical_key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
                         if (r < 0)
                                 goto fail;
                 }
         }
 
-        r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
+        r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
         if (r < 0)
                 goto fail;
 
@@ -568,24 +575,26 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
         return NULL;
 }
 
-int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {
+int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
         unsigned n = 0;
         int r;
         bool nxdomain = false;
         _cleanup_free_ char *key_str = NULL;
-        DnsResourceRecord *nsec = NULL;
-        DnsCacheItem *j, *first;
+        DnsCacheItem *j, *first, *nsec = NULL;
+        bool have_authenticated = false, have_non_authenticated = false;
 
         assert(c);
         assert(key);
         assert(rcode);
         assert(ret);
+        assert(authenticated);
 
         if (key->type == DNS_TYPE_ANY ||
             key->class == DNS_CLASS_ANY) {
 
-                /* If we have ANY lookups we simply refresh */
+                /* If we have ANY lookups we don't use the cache, so
+                 * that the caller refreshes via the network. */
 
                 r = dns_resource_key_to_string(key, &key_str);
                 if (r < 0)
@@ -616,10 +625,16 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
         LIST_FOREACH(by_key, j, first) {
                 if (j->rr) {
                         if (j->rr->key->type == DNS_TYPE_NSEC)
-                                nsec = j->rr;
+                                nsec = j;
+
                         n++;
                 } else if (j->type == DNS_CACHE_NXDOMAIN)
                         nxdomain = true;
+
+                if (j->authenticated)
+                        have_authenticated = true;
+                else
+                        have_non_authenticated = true;
         }
 
         r = dns_resource_key_to_string(key, &key_str);
@@ -635,10 +650,11 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
 
                 *ret = NULL;
                 *rcode = DNS_RCODE_SUCCESS;
+                *authenticated = nsec->authenticated;
 
-                return !bitmap_isset(nsec->nsec.types, key->type) &&
-                       !bitmap_isset(nsec->nsec.types, DNS_TYPE_CNAME) &&
-                       !bitmap_isset(nsec->nsec.types, DNS_TYPE_DNAME);
+                return !bitmap_isset(nsec->rr->nsec.types, key->type) &&
+                       !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
+                       !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME);
         }
 
         log_debug("%s cache hit for %s",
@@ -649,6 +665,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
         if (n <= 0) {
                 *ret = NULL;
                 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
+                *authenticated = have_authenticated && !have_non_authenticated;
                 return 1;
         }
 
@@ -667,6 +684,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
 
         *ret = answer;
         *rcode = DNS_RCODE_SUCCESS;
+        *authenticated = have_authenticated && !have_non_authenticated;
         answer = NULL;
 
         return n;
index 561d31a..5f91164 100644 (file)
@@ -38,8 +38,8 @@ typedef struct DnsCache {
 void dns_cache_flush(DnsCache *c);
 void dns_cache_prune(DnsCache *c);
 
-int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
-int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer);
+int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
+int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated);
 
 int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
 
index ffa6c44..aa2823c 100644 (file)
@@ -225,16 +225,19 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_;
 #define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) })
 #define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
 
-static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) {
+static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) {
+        uint64_t f;
 
-        /* Converts a protocol + family into a flags field as used in queries */
+        /* Converts a protocol + family into a flags field as used in queries and responses */
+
+        f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0;
 
         switch (protocol) {
         case DNS_PROTOCOL_DNS:
-                return SD_RESOLVED_DNS;
+                return f|SD_RESOLVED_DNS;
 
         case DNS_PROTOCOL_LLMNR:
-                return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4;
+                return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4);
 
         default:
                 break;
index 040b3cb..089d9fb 100644 (file)
@@ -972,6 +972,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
         DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
         DnsTransaction *t;
         Iterator i;
+        bool has_authenticated = false, has_non_authenticated = false;
 
         assert(q);
 
@@ -999,6 +1000,11 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
                         q->answer = merged;
                         q->answer_rcode = t->answer_rcode;
 
+                        if (t->answer_authenticated)
+                                has_authenticated = true;
+                        else
+                                has_non_authenticated = true;
+
                         state = DNS_TRANSACTION_SUCCESS;
                         break;
                 }
@@ -1028,6 +1034,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
 
         q->answer_protocol = c->scope->protocol;
         q->answer_family = c->scope->family;
+        q->answer_authenticated = has_authenticated && !has_non_authenticated;
 
         dns_search_domain_unref(q->answer_search_domain);
         q->answer_search_domain = dns_search_domain_ref(c->search_domain);
index a9d7904..b71bb23 100644 (file)
@@ -75,6 +75,7 @@ struct DnsQuery {
         DnsProtocol answer_protocol;
         int answer_family;
         DnsSearchDomain *answer_search_domain;
+        bool answer_authenticated;
 
         /* Bus client information */
         sd_bus_message *request;
index dab8bed..a90692c 100644 (file)
@@ -375,7 +375,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
         if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
                 return DNS_SCOPE_NO;
 
-        if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
+        if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0)
                 return DNS_SCOPE_NO;
 
         /* Never resolve any loopback hostname or IP address via DNS,
index d22acf0..1103a34 100644 (file)
@@ -481,20 +481,29 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
                 return;
         }
 
-        /* Install the answer as answer to the transaction */
-        dns_answer_unref(t->answer);
-        t->answer = dns_answer_ref(p->answer);
-        t->answer_rcode = DNS_PACKET_RCODE(p);
-
         /* Only consider responses with equivalent query section to the request */
         if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
                 return;
         }
 
+        /* Install the answer as answer to the transaction */
+        dns_answer_unref(t->answer);
+        t->answer = dns_answer_ref(p->answer);
+        t->answer_rcode = DNS_PACKET_RCODE(p);
+        t->answer_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p);
+
         /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
         if (DNS_PACKET_SHALL_CACHE(p))
-                dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
+                dns_cache_put(&t->scope->cache,
+                              t->key,
+                              DNS_PACKET_RCODE(p),
+                              p->answer,
+                              DNS_PACKET_ANCOUNT(p),
+                              t->answer_authenticated,
+                              0,
+                              p->family,
+                              &p->sender);
 
         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
@@ -683,6 +692,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 if (r > 0) {
                         t->answer_rcode = DNS_RCODE_SUCCESS;
                         t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
+                        t->answer_authenticated = true;
                         dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
                         return 0;
                 }
@@ -698,6 +708,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 if (r > 0) {
                         t->answer_rcode = DNS_RCODE_SUCCESS;
                         t->answer_source = DNS_TRANSACTION_ZONE;
+                        t->answer_authenticated = true;
                         dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
                         return 0;
                 }
@@ -715,7 +726,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 /* Let's then prune all outdated entries */
                 dns_cache_prune(&t->scope->cache);
 
-                r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer);
+                r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated);
                 if (r < 0)
                         return r;
                 if (r > 0) {
index 9a2662e..a3058ce 100644 (file)
@@ -69,6 +69,7 @@ struct DnsTransaction {
         DnsAnswer *answer;
         int answer_rcode;
         DnsTransactionSource answer_source;
+        bool answer_authenticated;
 
         usec_t start_usec;
         sd_event_source *timeout_event_source;