]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: in DNSSEC permissive mode, check if DO bit wasn't copied from request to...
authorLennart Poettering <lennart@poettering.net>
Thu, 12 Nov 2020 16:33:08 +0000 (17:33 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 17 Feb 2021 18:25:13 +0000 (19:25 +0100)
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
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-transaction.c

index da509a2c96b7eb63aab04bd5f6d85b7f469926d7..6a5a466af66fa5f6620a51dd5c040015bdaf762a 100644 (file)
@@ -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) {
index 7860e32183aa85abcfe61100b4a0884cd5188287..304d608f7a37faaa8dd245b0c69c191e9c7d200f 100644 (file)
@@ -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);
 
index 31ef8181382eb430872fe34a76a2b5a146b5552f..060a9d80a8998cb99bd3af3318424f241ca3aca8 100644 (file)
@@ -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);
         }