364146
From 1f5cb247ecd20ba57c472138f94856aa83caf042 Mon Sep 17 00:00:00 2001
364146
From: Mark Andrews <marka@isc.org>
364146
Date: Tue, 1 Mar 2022 09:48:05 +1100
364146
Subject: [PATCH] Add additional name checks when using a forwarder
364146
364146
When using a forwarder, check that the owner name of response
364146
records are within the bailiwick of the forwarded name space.
364146
364146
(cherry picked from commit e8df2802ac62016ea68585893eb4310fc3329028)
364146
364146
Check that the forward declaration is unchanged and not overridden
364146
364146
If we are using a fowarder, in addition to checking that names to
364146
be cached are subdomains of the forwarded namespace, we must also
364146
check that there are no subsidiary forwarded namespaces which would
364146
take precedence. To be safe, we don't cache any responses if the
364146
forwarding configuration has changed since the query was sent.
364146
364146
(cherry picked from commit 590f8698fc876d6d72f75cf35359e7546c3af972)
364146
364146
Check cached names for possible "forward only" clause
364146
364146
When caching additional and glue data *not* from a forwarder, we must
364146
check that there is no "forward only" clause covering the owner name
364146
that would take precedence.  Such names would normally be allowed by
364146
baliwick rules, but a "forward only" zone introduces a new baliwick
364146
scope.
364146
364146
(cherry picked from commit 4a144fae16e70517be894a971cef1d085ee68ebe)
364146
364146
Look for zones deeper than the current domain or forward name
364146
364146
When caching glue, we need to ensure that there is no closer
364146
source of truth for the name. If the owner name for the glue
364146
record would be answered by a locally configured zone, do not
364146
cache.
364146
364146
(cherry picked from commit 42f8c538d3fb9d075b98d82688aeb71621798754)
364146
364146
Avoid use of compound literals
364146
364146
Compound literals are not used in BIND 9.11, in order to ensure backward
364146
compatibility with ancient compilers.  Rework the relevant parts of the
364146
BIND 9.11 backport of the CVE-2021-25220 fix so that compound literals
364146
are not used.
364146
364146
(cherry picked from commit d4b1efbcbd4dfb8c6ef303968992440c5bdeed15)
364146
---
364146
 lib/dns/resolver.c | 130 +++++++++++++++++++++++++++++++++++++++++++--
364146
 1 file changed, 125 insertions(+), 5 deletions(-)
364146
364146
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
364146
index c912f3aea8..2c68973899 100644
364146
--- a/lib/dns/resolver.c
364146
+++ b/lib/dns/resolver.c
364146
@@ -63,6 +63,7 @@
364146
 #include <dns/stats.h>
364146
 #include <dns/tsig.h>
364146
 #include <dns/validator.h>
364146
+#include <dns/zone.h>
364146
 
364146
 #ifdef WANT_QUERYTRACE
364146
 #define RTRACE(m)       isc_log_write(dns_lctx, \
364146
@@ -312,6 +313,8 @@ struct fetchctx {
364146
 	bool			ns_ttl_ok;
364146
 	uint32_t			ns_ttl;
364146
 	isc_counter_t *			qc;
364146
+	dns_fixedname_t			fwdfname;
364146
+	dns_name_t			*fwdname;
364146
 
364146
 	/*%
364146
 	 * The number of events we're waiting for.
364146
@@ -3393,6 +3396,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) {
364146
 		if (result == ISC_R_SUCCESS) {
364146
 			fwd = ISC_LIST_HEAD(forwarders->fwdrs);
364146
 			fctx->fwdpolicy = forwarders->fwdpolicy;
364146
+			dns_name_copy(domain, fctx->fwdname, NULL);
364146
 			if (fctx->fwdpolicy == dns_fwdpolicy_only &&
364146
 			    isstrictsubdomain(domain, &fctx->domain)) {
364146
 				fcount_decr(fctx);
364146
@@ -4422,6 +4426,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
364146
 	fctx->restarts = 0;
364146
 	fctx->querysent = 0;
364146
 	fctx->referrals = 0;
364146
+
364146
+	fctx->fwdname = dns_fixedname_initname(&fctx->fwdfname);
364146
+
364146
 	TIME_NOW(&fctx->start);
364146
 	fctx->timeouts = 0;
364146
 	fctx->lamecount = 0;
364146
@@ -4480,8 +4487,10 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
364146
 		domain = dns_fixedname_initname(&fixed);
364146
 		result = dns_fwdtable_find2(fctx->res->view->fwdtable, fwdname,
364146
 					    domain, &forwarders);
364146
-		if (result == ISC_R_SUCCESS)
364146
+		if (result == ISC_R_SUCCESS) {
364146
 			fctx->fwdpolicy = forwarders->fwdpolicy;
364146
+			dns_name_copy(domain, fctx->fwdname, NULL);
364146
+		}
364146
 
364146
 		if (fctx->fwdpolicy != dns_fwdpolicy_only) {
364146
 			/*
364146
@@ -6231,6 +6240,112 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
364146
 		rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL;
364146
 }
364146
 
364146
+/*
364146
+ * Returns true if 'name' is external to the namespace for which
364146
+ * the server being queried can answer, either because it's not a
364146
+ * subdomain or because it's below a forward declaration or a
364146
+ * locally served zone.
364146
+ */
364146
+static inline bool
364146
+name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
364146
+	isc_result_t result;
364146
+	dns_forwarders_t *forwarders = NULL;
364146
+	dns_fixedname_t fixed, zfixed;
364146
+	dns_name_t *fname = dns_fixedname_initname(&fixed);
364146
+	dns_name_t *zfname = dns_fixedname_initname(&zfixed);
364146
+	dns_name_t *apex = NULL;
364146
+	dns_name_t suffix;
364146
+	dns_zone_t *zone = NULL;
364146
+	unsigned int labels;
364146
+	dns_namereln_t rel;
364146
+	/*
364146
+	 * The following two variables do not influence code flow; they are
364146
+	 * only necessary for calling dns_name_fullcompare().
364146
+	 */
364146
+	int _orderp = 0;
364146
+	unsigned int _nlabelsp = 0;
364146
+
364146
+	apex = ISFORWARDER(fctx->addrinfo) ? fctx->fwdname : &fctx->domain;
364146
+
364146
+	/*
364146
+	 * The name is outside the queried namespace.
364146
+	 */
364146
+	rel = dns_name_fullcompare(name, apex, &_orderp, &_nlabelsp);
364146
+	if (rel != dns_namereln_subdomain && rel != dns_namereln_equal) {
364146
+		return (true);
364146
+	}
364146
+
364146
+	/*
364146
+	 * If the record lives in the parent zone, adjust the name so we
364146
+	 * look for the correct zone or forward clause.
364146
+	 */
364146
+	labels = dns_name_countlabels(name);
364146
+	if (dns_rdatatype_atparent(type) && labels > 1U) {
364146
+		dns_name_init(&suffix, NULL);
364146
+		dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
364146
+		name = &suffix;
364146
+	} else if (rel == dns_namereln_equal) {
364146
+		/* If 'name' is 'apex', no further checking is needed. */
364146
+		return (false);
364146
+	}
364146
+
364146
+	/*
364146
+	 * If there is a locally served zone between 'apex' and 'name'
364146
+	 * then don't cache.
364146
+	 */
364146
+	LOCK(&fctx->res->view->lock);
364146
+	if (fctx->res->view->zonetable != NULL) {
364146
+		unsigned int options = DNS_ZTFIND_NOEXACT;
364146
+		result = dns_zt_find(fctx->res->view->zonetable, name, options,
364146
+				     zfname, &zone);
364146
+		if (zone != NULL) {
364146
+			dns_zone_detach(&zone);
364146
+		}
364146
+		if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
364146
+			if (dns_name_fullcompare(zfname, apex, &_orderp,
364146
+						 &_nlabelsp) ==
364146
+			    dns_namereln_subdomain)
364146
+			{
364146
+				UNLOCK(&fctx->res->view->lock);
364146
+				return (true);
364146
+			}
364146
+		}
364146
+	}
364146
+	UNLOCK(&fctx->res->view->lock);
364146
+
364146
+	/*
364146
+	 * Look for a forward declaration below 'name'.
364146
+	 */
364146
+	result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, fname,
364146
+				    &forwarders);
364146
+
364146
+	if (ISFORWARDER(fctx->addrinfo)) {
364146
+		/*
364146
+		 * See if the forwarder declaration is better.
364146
+		 */
364146
+		if (result == ISC_R_SUCCESS) {
364146
+			return (!dns_name_equal(fname, fctx->fwdname));
364146
+		}
364146
+
364146
+		/*
364146
+		 * If the lookup failed, the configuration must have
364146
+		 * changed: play it safe and don't cache.
364146
+		 */
364146
+		return (true);
364146
+	} else if (result == ISC_R_SUCCESS &&
364146
+		   forwarders->fwdpolicy == dns_fwdpolicy_only &&
364146
+		   !ISC_LIST_EMPTY(forwarders->fwdrs))
364146
+	{
364146
+		/*
364146
+		 * If 'name' is covered by a 'forward only' clause then we
364146
+		 * can't cache this repsonse.
364146
+		 */
364146
+		return (true);
364146
+	}
364146
+
364146
+	return (false);
364146
+}
364146
+
364146
 static isc_result_t
364146
 check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
364146
 	      dns_section_t section)
364146
@@ -6259,7 +6374,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
364146
 	result = dns_message_findname(rmessage, section, addname,
364146
 				      dns_rdatatype_any, 0, &name, NULL);
364146
 	if (result == ISC_R_SUCCESS) {
364146
-		external = !dns_name_issubdomain(name, &fctx->domain);
364146
+		external = name_external(name, type, fctx);
364146
 		if (type == dns_rdatatype_a) {
364146
 			for (rdataset = ISC_LIST_HEAD(name->list);
364146
 			     rdataset != NULL;
364146
@@ -7141,6 +7256,13 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
364146
 			break;
364146
 
364146
 		case dns_namereln_subdomain:
364146
+			/*
364146
+			 * Don't accept DNAME from parent namespace.
364146
+			 */
364146
+			if (name_external(name, dns_rdatatype_dname, fctx)) {
364146
+				continue;
364146
+			}
364146
+
364146
 			/*
364146
 			 * In-scope DNAME records must have at least
364146
 			 * as many labels as the domain being queried.
364146
@@ -7376,11 +7498,9 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
364146
 	 */
364146
 	result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
364146
 	while (!done && result == ISC_R_SUCCESS) {
364146
-		bool external;
364146
 		name = NULL;
364146
 		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
364146
-		external = !dns_name_issubdomain(name, &fctx->domain);
364146
-		if (!external) {
364146
+		if (!name_external(name, dns_rdatatype_ns, fctx)) {
364146
 			/*
364146
 			 * We expect to find NS or SIG NS rdatasets, and
364146
 			 * nothing else.
364146
-- 
364146
2.34.1
364146