From: Mark Andrews Date: Thu, 12 Nov 2020 22:45:47 +0000 (+1100) Subject: Tighten DNS COOKIE response handling X-Git-Tag: v9.16.10~6^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=df5f076a02c617b0baaee18d5a9f905b1290e833;p=thirdparty%2Fbind9.git Tighten DNS COOKIE response handling 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) --- diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 76da994ac5c..febe5106e6d 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -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);