From d96275d8eb627fa0bcaa1023d6d6610426311ace Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 12 Nov 2020 17:33:08 +0100 Subject: [PATCH 1/2] resolved: in DNSSEC permissive mode, check if DO bit wasn't copied from request to response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the server doesn't copy the DO bit from request to response, this is a very early and easy indication that it doesn#t support DNSSEC properly. Hence, let's immediately downgrade to non-DNSSEC mode if we see this – if permissive mode is on and this is allowed. --- src/resolve/resolved-dns-server.c | 32 ++++++++++++++++++++++++-- src/resolve/resolved-dns-server.h | 2 ++ src/resolve/resolved-dns-transaction.c | 4 ++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index da509a2c96..6a5a466af6 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -378,6 +378,17 @@ void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) { s->packet_invalid = true; } +void dns_server_packet_do_off(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + /* Invoked whenever the DO flag was not copied from our request to the response. */ + + if (s->possible_feature_level != level) + return; + + s->packet_do_off = true; +} + static bool dns_server_grace_period_expired(DnsServer *s) { usec_t ts; @@ -494,6 +505,17 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { * when we can't use EDNS because the DNS server doesn't support it. */ log_level = LOG_NOTICE; + } else if (s->packet_do_off && + DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(s->possible_feature_level) && + dns_server_get_dnssec_mode(s) != DNSSEC_YES) { + + /* The server didn't copy the DO bit from request to response, thus DNSSEC is not + * correctly implemented, let's downgrade if that's allowed. */ + + log_debug("Detected server didn't copy DO flag from request to response, downgrading feature level..."); + s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_IS_TLS(s->possible_feature_level) ? DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN : + DNS_SERVER_FEATURE_LEVEL_EDNS0; + } else if (s->packet_rrsig_missing && DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(s->possible_feature_level) && dns_server_get_dnssec_mode(s) != DNSSEC_YES) { @@ -653,6 +675,9 @@ bool dns_server_dnssec_supported(DnsServer *server) { if (server->packet_rrsig_missing) return false; + if (server->packet_do_off) + return false; + /* DNSSEC servers need to support TCP properly (see RFC5966), if they don't, we assume DNSSEC is borked too */ if (server->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS) return false; @@ -898,6 +923,7 @@ void dns_server_reset_features(DnsServer *s) { s->packet_bad_opt = false; s->packet_rrsig_missing = false; + s->packet_do_off = false; s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC; @@ -959,14 +985,16 @@ void dns_server_dump(DnsServer *s, FILE *f) { "\tSeen truncated packet: %s\n" "\tSeen OPT RR getting lost: %s\n" "\tSeen RRSIG RR missing: %s\n" - "\tSeen invalid packet: %s\n", + "\tSeen invalid packet: %s\n" + "\tServer dropped DO flag: %s\n", s->received_udp_packet_max, s->n_failed_udp, s->n_failed_tcp, yes_no(s->packet_truncated), yes_no(s->packet_bad_opt), yes_no(s->packet_rrsig_missing), - yes_no(s->packet_invalid)); + yes_no(s->packet_invalid), + yes_no(s->packet_do_off)); } void dns_server_unref_stream(DnsServer *s) { diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 7860e32183..304d608f7a 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -85,6 +85,7 @@ struct DnsServer { bool packet_bad_opt:1; /* Set when OPT was missing or otherwise bad on reply */ bool packet_rrsig_missing:1; /* Set when RRSIG was missing */ bool packet_invalid:1; /* Set when we failed to parse a reply */ + bool packet_do_off:1; /* Set when the server didn't copy DNSSEC DO flag from request to response */ usec_t verified_usec; usec_t features_grace_period_usec; @@ -124,6 +125,7 @@ void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level); +void dns_server_packet_do_off(DnsServer *s, DnsServerFeatureLevel level); DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 31ef818138..060a9d80a8 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1279,6 +1279,10 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt if (!p->opt) dns_server_packet_bad_opt(t->server, t->current_feature_level); + /* Report that the server didn't copy our query DO bit from request to response */ + if (DNS_PACKET_DO(t->sent) && !DNS_PACKET_DO(t->received)) + dns_server_packet_do_off(t->server, t->current_feature_level); + /* Report that we successfully received a packet */ dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, p->size); } From e30a62bfe6d6915a90eff4556fd189a6d5a252da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 12 Nov 2020 18:10:18 +0100 Subject: [PATCH 2/2] resolved: see if it's worth retrying a lookup on "failed-auxiliary" DNSSEC error Maybe we learnt something about the server feature set, let's hence retry. Fixes: #11102 --- src/resolve/resolved-dns-transaction.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 060a9d80a8..1f396239f9 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -844,6 +844,7 @@ static bool dns_transaction_dnssec_is_live(DnsTransaction *t) { static int dns_transaction_dnssec_ready(DnsTransaction *t) { DnsTransaction *dt; + int r; assert(t); @@ -877,7 +878,7 @@ static int dns_transaction_dnssec_ready(DnsTransaction *t) { case DNS_TRANSACTION_DNSSEC_FAILED: /* We handle DNSSEC failures different from other errors, as we care about the DNSSEC - * validationr result */ + * validation result */ log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(dt->answer_dnssec_result)); t->answer_dnssec_result = dt->answer_dnssec_result; /* Copy error code over */ @@ -894,6 +895,18 @@ static int dns_transaction_dnssec_ready(DnsTransaction *t) { return 1; fail: + /* Some auxiliary DNSSEC transaction failed for some reason. Maybe we learned something about the + * server due to this failure, and the feature level is now different? Let's see and restart the + * transaction if so. If not, let's propagate the auxiliary failure. + * + * This is particularly relevant if an auxiliary request figured out that DNSSEC doesn't work, and we + * are in permissive DNSSEC mode, and thus should restart things without DNSSEC magic. */ + r = dns_transaction_maybe_restart(t); + if (r < 0) + return r; + if (r > 0) + return 0; /* don't validate just yet, we restarted things */ + t->answer_dnssec_result = DNSSEC_FAILED_AUXILIARY; dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); return 0;