]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Tighten DNS COOKIE response handling
authorMark Andrews <marka@isc.org>
Thu, 12 Nov 2020 22:45:47 +0000 (09:45 +1100)
committerMark Andrews <marka@isc.org>
Thu, 26 Nov 2020 21:15:11 +0000 (08:15 +1100)
Fallback to TCP when we have already seen a DNS COOKIE response
from the given address and don't have one in this UDP response. This
could be a server that has turned off DNS COOKIE support, a
misconfigured anycast server with partial DNS COOKIE support, or a
spoofed response. Falling back to TCP is the correct behaviour in
all 3 cases.

(cherry picked from commit 0e3b1f5a25c0518210db62191405f4b0bbe6bf50)

lib/dns/resolver.c

index 76da994ac5c4af9e2889a25b5d46d49d74bfa685..febe5106e6d5838c8e1f42ba57ccdf60b612843c 100644 (file)
@@ -2417,7 +2417,12 @@ add_serveraddr(uint8_t *buf, const size_t bufsize, const resquery_t *query) {
        return (addr2buf(buf, bufsize, &query->addrinfo->sockaddr));
 }
 
+/*
+ * Client cookie is 8 octets.
+ * Server cookie is [8..32] octets.
+ */
 #define CLIENT_COOKIE_SIZE 8U
+#define COOKIE_BUFFER_SIZE (8U + 32U)
 
 static void
 compute_cc(const resquery_t *query, uint8_t *cookie, const size_t len) {
@@ -2655,7 +2660,7 @@ resquery_send(resquery_t *query) {
                        bool reqnsid = res->view->requestnsid;
                        bool sendcookie = res->view->sendcookie;
                        bool tcpkeepalive = false;
-                       unsigned char cookie[64];
+                       unsigned char cookie[COOKIE_BUFFER_SIZE];
                        uint16_t padding = 0;
 
                        if ((flags & FCTX_ADDRINFO_EDNSOK) != 0 &&
@@ -2739,9 +2744,11 @@ resquery_send(resquery_t *query) {
                                                fctx->res,
                                                dns_resstatscounter_cookieout);
                                } else {
-                                       compute_cc(query, cookie, 8);
+                                       compute_cc(query, cookie,
+                                                  CLIENT_COOKIE_SIZE);
                                        ednsopts[ednsopt].value = cookie;
-                                       ednsopts[ednsopt].length = 8;
+                                       ednsopts[ednsopt].length =
+                                               CLIENT_COOKIE_SIZE;
                                        inc_stats(
                                                fctx->res,
                                                dns_resstatscounter_cookienew);
@@ -7716,6 +7723,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        result = dns_message_checksig(query->rmessage, fctx->res->view);
        if (result != ISC_R_SUCCESS) {
                FCTXTRACE3("signature check failed", result);
+               if (result == DNS_R_UNEXPECTEDTSIG ||
+                   result == DNS_R_EXPECTEDTSIG) {
+                       rctx.nextitem = true;
+               }
                rctx_done(&rctx, result);
                return;
        }
@@ -7732,6 +7743,40 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         * ensured by the dispatch code).
         */
 
+       /*
+        * If we have had a server cookie and don't get one retry over TCP.
+        * This may be a misconfigured anycast server or an attempt to send
+        * a spoofed response.  Skip if we have a valid tsig.
+        */
+       if (dns_message_gettsig(query->rmessage, NULL) == NULL &&
+           !query->rmessage->cc_ok && !query->rmessage->cc_bad &&
+           (rctx.retryopts & DNS_FETCHOPT_TCP) == 0)
+       {
+               unsigned char cookie[COOKIE_BUFFER_SIZE];
+               if (dns_adb_getcookie(fctx->adb, query->addrinfo, cookie,
+                                     sizeof(cookie)) > CLIENT_COOKIE_SIZE)
+               {
+                       if (isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) {
+                               char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+                               isc_sockaddr_format(&query->addrinfo->sockaddr,
+                                                   addrbuf, sizeof(addrbuf));
+                               isc_log_write(
+                                       dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+                                       DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+                                       "missing expected cookie from %s",
+                                       addrbuf);
+                       }
+                       rctx.retryopts |= DNS_FETCHOPT_TCP;
+                       rctx.resend = true;
+                       rctx_done(&rctx, result);
+                       return;
+               }
+               /*
+                * XXXMPA When support for DNS COOKIE becomes ubiquitous, fall
+                * back to TCP for all non-COOKIE responses.
+                */
+       }
+
        rctx_edns(&rctx);
 
        /*
@@ -8147,7 +8192,7 @@ rctx_opt(respctx_t *rctx) {
        uint16_t optlen;
        unsigned char *optvalue;
        dns_adbaddrinfo_t *addrinfo;
-       unsigned char cookie[8];
+       unsigned char cookie[CLIENT_COOKIE_SIZE];
        bool seen_cookie = false;
        bool seen_nsid = false;
 
@@ -8184,8 +8229,10 @@ rctx_opt(respctx_t *rctx) {
                                compute_cc(query, cookie, sizeof(cookie));
                                INSIST(query->rmessage->cc_bad == 0 &&
                                       query->rmessage->cc_ok == 0);
-                               if (optlen >= 8U &&
-                                   memcmp(cookie, optvalue, 8) == 0) {
+                               if (optlen >= CLIENT_COOKIE_SIZE &&
+                                   memcmp(cookie, optvalue,
+                                          CLIENT_COOKIE_SIZE) == 0)
+                               {
                                        query->rmessage->cc_ok = 1;
                                        inc_stats(fctx->res,
                                                  dns_resstatscounter_cookieok);