File bind-9.9-CVE-2025-40778.patch of Package bind.41588

commit 791a51bf0e6412d1a71ee95103c0adb728970a4a
Author: Michał Kępień <michal@isc.org>
Date:   Wed Oct 22 18:48:06 2025 +0200

    [9.9.9] [CVE-2025-40778] sec: usr: Address various spoofing attacks

diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
index 191920ff7a..d2d0aad338 100644
--- a/lib/dns/include/dns/message.h
+++ b/lib/dns/include/dns/message.h
@@ -217,6 +217,7 @@ struct dns_message {
 	unsigned int			free_saved : 1;
 	unsigned int			tkey : 1;
 	unsigned int			rdclass_set : 1;
+	unsigned int			has_dname : 1;
 
 	unsigned int			opt_reserved;
 	unsigned int			sig_reserved;
@@ -1390,6 +1391,13 @@ dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass);
  * \li   msg be a valid message with parsing intent.
  */
 
+isc_boolean_t
+dns_message_hasdname(dns_message_t *msg);
+/*%<
+ * Return whether a DNAME was detected in the ANSWER section of a QUERY
+ * message when it was parsed.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_MESSAGE_H */
diff --git a/lib/dns/message.c b/lib/dns/message.c
index 23e99ee712..f4c6caf798 100644
--- a/lib/dns/message.c
+++ b/lib/dns/message.c
@@ -420,6 +420,7 @@ msginit(dns_message_t *m) {
 	m->free_saved = 0;
 	m->tkey = 0;
 	m->rdclass_set = 0;
+	m->has_dname = 0;
 	m->querytsig = NULL;
 }
 
@@ -1609,6 +1610,11 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
 			rdataset = NULL;
 			free_rdataset = ISC_FALSE;
 			free_name = ISC_FALSE;
+		} else if (rdtype == dns_rdatatype_dname &&
+			   sectionid == DNS_SECTION_ANSWER &&
+			   msg->opcode == dns_opcode_query)
+		{
+			msg->has_dname = 1;
 		}
 
 		if (seen_problem) {
@@ -3722,3 +3728,9 @@ dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
 	msg->rdclass = rdclass;
 	msg->rdclass_set = 1;
 }
+
+isc_boolean_t
+dns_message_hasdname(dns_message_t *msg) {
+	REQUIRE(DNS_MESSAGE_VALID(msg));
+	return msg->has_dname;
+}
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index 82249599ed..c50160cf61 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -263,6 +263,7 @@ struct fetchctx {
 	unsigned int			dbucketnum;
 	char *				info;
 	isc_mem_t *			mctx;
+	dns_name_t *			ns_name;
 
 	/*% Locked by appropriate bucket lock. */
 	fetchstate			state;
@@ -5739,7 +5740,9 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
  * locally served zone.
  */
 static inline isc_boolean_t
-name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
+name_external(dns_name_t *name, dns_name_t *ns_name, dns_rdatatype_t type,
+	      fetchctx_t *fctx)
+{
 	isc_result_t result;
 	dns_forwarders_t *forwarders = NULL;
 	dns_fixedname_t fixed, zfixed;
@@ -5751,7 +5754,9 @@ name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
 	unsigned int labels;
 	dns_namereln_t rel;
 
-	apex = ISFORWARDER(fctx->addrinfo) ? fctx->fwdname : &fctx->domain;
+	apex = ISFORWARDER(fctx->addrinfo)
+		? fctx->fwdname
+		: (ns_name != NULL) ? ns_name : &fctx->domain;
 
 	/*
 	 * The name is outside the queried namespace.
@@ -5861,7 +5866,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
 	result = dns_message_findname(fctx->rmessage, section, addname,
 				      dns_rdatatype_any, 0, &name, NULL);
 	if (result == ISC_R_SUCCESS) {
-		external = ISC_TF(name_external(name, type, fctx));
+		external = ISC_TF(name_external(name, fctx->ns_name, type, fctx));
 		if (type == dns_rdatatype_a) {
 			for (rdataset = ISC_LIST_HEAD(name->list);
 			     rdataset != NULL;
@@ -6544,6 +6549,8 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
 		 */
 		INSIST(ns_rdataset != NULL);
 		fctx->attributes |= FCTX_ATTR_GLUING;
+		fctx->ns_name = ns_name;
+
 		/*
 		 * Mark the glue records in the additional section to be cached.
 		 */
@@ -6645,8 +6652,9 @@ validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) {
 
 
 static isc_result_t
-answer_response(fetchctx_t *fctx) {
+answer_response(resquery_t *query) {
 	isc_result_t result;
+	fetchctx_t *fctx = NULL;
 	dns_message_t *message = NULL;
 	dns_name_t *name = NULL, *qname = NULL, *ns_name = NULL;
 	dns_name_t *aname = NULL, *cname = NULL, *dname = NULL;
@@ -6660,6 +6668,8 @@ answer_response(fetchctx_t *fctx) {
 	dns_view_t *view = NULL;
 	dns_trust_t trust;
 
+	REQUIRE(VALID_QUERY(query));
+	fctx = query->fctx;
 	REQUIRE(VALID_FCTX(fctx));
 
 	FCTXTRACE("answer_response");
@@ -6725,7 +6735,9 @@ answer_response(fetchctx_t *fctx) {
 			/*
 			 * Don't accept DNAME from parent namespace.
 			 */
-			if (name_external(name, dns_rdatatype_dname, fctx)) {
+			if (name_external(name, NULL, dns_rdatatype_dname,
+					  fctx))
+			{
 				continue;
 			}
 
@@ -6807,6 +6819,7 @@ answer_response(fetchctx_t *fctx) {
 			rdataset->attributes |= DNS_RDATASETATTR_ANSWER;
 			rdataset->attributes |= DNS_RDATASETATTR_CACHE;
 			rdataset->trust = trust;
+			fctx->ns_name = ns_name;
 			(void)dns_rdataset_additionaldata(rdataset,
 							  check_related,
 							  fctx, 0);
@@ -6833,6 +6846,7 @@ answer_response(fetchctx_t *fctx) {
 		ardataset->attributes |= DNS_RDATASETATTR_ANSWER;
 		ardataset->attributes |= DNS_RDATASETATTR_CACHE;
 		ardataset->trust = trust;
+		fctx->ns_name = ns_name;
 		(void)dns_rdataset_additionaldata(ardataset, check_related,
 						  fctx, 0);
 		for (sigrdataset = ISC_LIST_HEAD(aname->list);
@@ -6959,7 +6973,9 @@ answer_response(fetchctx_t *fctx) {
 	while (!done && result == ISC_R_SUCCESS) {
 		name = NULL;
 		dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
-		if (ISC_TF(!name_external(name, dns_rdatatype_ns, fctx))) {
+		if (ISC_TF(!name_external(name, ns_name, dns_rdatatype_ns, fctx) &&
+                           dns_name_issubdomain(&fctx->name, name)))
+		{
 			/*
 			 * We expect to find NS or SIG NS rdatasets, and
 			 * nothing else.
@@ -6990,6 +7006,7 @@ answer_response(fetchctx_t *fctx) {
 					 * Mark any additional data related
 					 * to this rdataset.
 					 */
+					fctx->ns_name = ns_name;
 					(void)dns_rdataset_additionaldata(
 							rdataset,
 							check_related,
@@ -7849,7 +7866,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 ||
 		    ISFORWARDER(query->addrinfo))
 		{
-			result = answer_response(fctx);
+			result = answer_response(query);
 			if (result != ISC_R_SUCCESS)
 				FCTXTRACE3("answer_response (AA/fwd)", result);
 		} else if (iscname(fctx) &&
@@ -7861,7 +7878,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			 * answer when a CNAME is followed.  We should treat
 			 * it as a valid answer.
 			 */
-			result = answer_response(fctx);
+			result = answer_response(query);
 			if (result != ISC_R_SUCCESS)
 				FCTXTRACE3("answer_response (!ANY/!CNAME)",
 					   result);
@@ -7870,7 +7887,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			/*
 			 * Lame response !!!.
 			 */
-			result = answer_response(fctx);
+			result = answer_response(query);
 			if (result != ISC_R_SUCCESS)
 				FCTXTRACE3("answer_response (!NS)", result);
 		} else {
openSUSE Build Service is sponsored by