From 931851e8e492a4d2715e22dcde50a5e7ccef4b49 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 3 Dec 2015 21:04:52 +0100 Subject: resolved: add a concept of "authenticated" responses 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 | 2 ++ src/resolve/resolved-bus.c | 8 +++---- src/resolve/resolved-def.h | 1 + src/resolve/resolved-dns-cache.c | 44 ++++++++++++++++++++++++---------- src/resolve/resolved-dns-cache.h | 4 ++-- src/resolve/resolved-dns-packet.h | 11 +++++---- src/resolve/resolved-dns-query.c | 7 ++++++ src/resolve/resolved-dns-query.h | 1 + src/resolve/resolved-dns-scope.c | 2 +- src/resolve/resolved-dns-transaction.c | 25 +++++++++++++------ src/resolve/resolved-dns-transaction.h | 1 + 11 files changed, 75 insertions(+), 31 deletions(-) diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 36dfc70e00..0f154d9798 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -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) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index ddde3af0c3..0ceca56371 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -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; diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index be29f51663..db5ee57b51 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -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) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 241187dd51..bcb9994a8c 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -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; diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 561d31ad99..5f91164785 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -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); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index ffa6c44213..aa2823cfb9 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -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; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 040b3cbd19..089d9fb70d 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -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); diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index a9d7904a8d..b71bb2352b 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -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; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index dab8bed169..a90692cdf4 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -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, diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index d22acf085b..1103a34c6f 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -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) { diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 9a2662ec0e..a3058ce6e8 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -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; -- cgit v1.2.3-54-g00ecf