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;