| Upstream-Status: Backport [https://downloads.isc.org/isc/bind9/9.11.19/patches/CVE-2020-8616.patch] |
| CVE: CVE-2020-8616 |
| Signed-off-by: Lee Chee Yang <chee.yang.lee@intel.com> |
| --- |
| diff --git a/lib/dns/adb.c b/lib/dns/adb.c |
| index 058495f6a5..6b8a9537f0 100644 |
| --- a/lib/dns/adb.c |
| +++ b/lib/dns/adb.c |
| @@ -404,14 +404,13 @@ static void log_quota(dns_adbentry_t *entry, const char *fmt, ...) |
| */ |
| #define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0) |
| #define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0) |
| -#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \ |
| - != 0) |
| -#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \ |
| - != 0) |
| -#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) |
| -#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) |
| -#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) |
| -#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) |
| +#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) != 0) |
| +#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) != 0) |
| +#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0) |
| +#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0) |
| +#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) |
| +#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0) |
| +#define FIND_NOFETCH(fn) (((fn)->options & DNS_ADBFIND_NOFETCH) != 0) |
| |
| /* |
| * These are currently used on simple unsigned ints, so they are |
| @@ -3155,21 +3154,26 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, |
| * Listen to negative cache hints, and don't start |
| * another query. |
| */ |
| - if (NCACHE_RESULT(result) || AUTH_NX(result)) |
| + if (NCACHE_RESULT(result) || AUTH_NX(result)) { |
| goto fetch; |
| + } |
| |
| - if (!NAME_FETCH_V6(adbname)) |
| + if (!NAME_FETCH_V6(adbname)) { |
| wanted_fetches |= DNS_ADBFIND_INET6; |
| + } |
| } |
| |
| fetch: |
| if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) || |
| (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname))) |
| + { |
| have_address = true; |
| - else |
| + } else { |
| have_address = false; |
| - if (wanted_fetches != 0 && |
| - ! (FIND_AVOIDFETCHES(find) && have_address)) { |
| + } |
| + if (wanted_fetches != 0 && !(FIND_AVOIDFETCHES(find) && have_address) && |
| + !FIND_NOFETCH(find)) |
| + { |
| /* |
| * We're missing at least one address family. Either the |
| * caller hasn't instructed us to avoid fetches, or we don't |
| @@ -3177,8 +3181,9 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, |
| * be acceptable so we have to launch fetches. |
| */ |
| |
| - if (FIND_STARTATZONE(find)) |
| + if (FIND_STARTATZONE(find)) { |
| start_at_zone = true; |
| + } |
| |
| /* |
| * Start V4. |
| diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h |
| index 63a13c4e41..edf6e54935 100644 |
| --- a/lib/dns/include/dns/adb.h |
| +++ b/lib/dns/include/dns/adb.h |
| @@ -207,6 +207,10 @@ struct dns_adbfind { |
| * lame for this query. |
| */ |
| #define DNS_ADBFIND_OVERQUOTA 0x00000400 |
| +/*% |
| + * Don't perform a fetch even if there are no address records available. |
| + */ |
| +#define DNS_ADBFIND_NOFETCH 0x00000800 |
| |
| /*% |
| * The answers to queries come back as a list of these. |
| diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c |
| index 7c44478a26..0a40859d08 100644 |
| --- a/lib/dns/resolver.c |
| +++ b/lib/dns/resolver.c |
| @@ -172,6 +172,14 @@ |
| #define DEFAULT_MAX_QUERIES 75 |
| #endif |
| |
| +/* |
| + * After NS_FAIL_LIMIT attempts to fetch a name server address, |
| + * if the number of addresses in the NS RRset exceeds NS_RR_LIMIT, |
| + * stop trying to fetch, in order to avoid wasting resources. |
| + */ |
| +#define NS_FAIL_LIMIT 4 |
| +#define NS_RR_LIMIT 5 |
| + |
| /* Number of hash buckets for zone counters */ |
| #ifndef RES_DOMAIN_BUCKETS |
| #define RES_DOMAIN_BUCKETS 523 |
| @@ -3130,8 +3138,7 @@ sort_finds(dns_adbfindlist_t *findlist, unsigned int bias) { |
| static void |
| findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, |
| unsigned int options, unsigned int flags, isc_stdtime_t now, |
| - bool *overquota, bool *need_alternate) |
| -{ |
| + bool *overquota, bool *need_alternate, unsigned int *no_addresses) { |
| dns_adbaddrinfo_t *ai; |
| dns_adbfind_t *find; |
| dns_resolver_t *res; |
| @@ -3219,7 +3226,12 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, |
| find->result_v6 != DNS_R_NXDOMAIN) || |
| (res->dispatches6 == NULL && |
| find->result_v4 != DNS_R_NXDOMAIN))) |
| + { |
| *need_alternate = true; |
| + } |
| + if (no_addresses != NULL) { |
| + (*no_addresses)++; |
| + } |
| } else { |
| if ((find->options & DNS_ADBFIND_OVERQUOTA) != 0) { |
| if (overquota != NULL) |
| @@ -3270,6 +3282,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) { |
| dns_rdata_ns_t ns; |
| bool need_alternate = false; |
| bool all_spilled = true; |
| + unsigned int no_addresses = 0; |
| |
| FCTXTRACE5("getaddresses", "fctx->depth=", fctx->depth); |
| |
| @@ -3437,20 +3450,28 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) { |
| * Extract the name from the NS record. |
| */ |
| result = dns_rdata_tostruct(&rdata, &ns, NULL); |
| - if (result != ISC_R_SUCCESS) |
| + if (result != ISC_R_SUCCESS) { |
| continue; |
| + } |
| |
| - findname(fctx, &ns.name, 0, stdoptions, 0, now, |
| - &overquota, &need_alternate); |
| + if (no_addresses > NS_FAIL_LIMIT && |
| + dns_rdataset_count(&fctx->nameservers) > NS_RR_LIMIT) |
| + { |
| + stdoptions |= DNS_ADBFIND_NOFETCH; |
| + } |
| + findname(fctx, &ns.name, 0, stdoptions, 0, now, &overquota, |
| + &need_alternate, &no_addresses); |
| |
| - if (!overquota) |
| + if (!overquota) { |
| all_spilled = false; |
| + } |
| |
| dns_rdata_reset(&rdata); |
| dns_rdata_freestruct(&ns); |
| } |
| - if (result != ISC_R_NOMORE) |
| + if (result != ISC_R_NOMORE) { |
| return (result); |
| + } |
| |
| /* |
| * Do we need to use 6 to 4? |
| @@ -3465,7 +3486,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) { |
| if (!a->isaddress) { |
| findname(fctx, &a->_u._n.name, a->_u._n.port, |
| stdoptions, FCTX_ADDRINFO_FORWARDER, |
| - now, NULL, NULL); |
| + now, NULL, NULL, NULL); |
| continue; |
| } |
| if (isc_sockaddr_pf(&a->_u.addr) != family) |
| @@ -3827,16 +3827,14 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) { |
| } |
| } |
| |
| - if (dns_name_countlabels(&fctx->domain) > 2) { |
| - result = isc_counter_increment(fctx->qc); |
| - if (result != ISC_R_SUCCESS) { |
| - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, |
| - DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3), |
| - "exceeded max queries resolving '%s'", |
| - fctx->info); |
| - fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); |
| - return; |
| - } |
| + result = isc_counter_increment(fctx->qc); |
| + if (result != ISC_R_SUCCESS) { |
| + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, |
| + DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3), |
| + "exceeded max queries resolving '%s'", |
| + fctx->info); |
| + fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); |
| + return; |
| } |
| |
| bucketnum = fctx->bucketnum; |