git: a5d8095437 - main - Add SA-25:10 and related patches.

Gordon Tetlow <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.doc
Message-ID <[email protected]>
The branch main has been updated by gordon:

URL: https://cgit.FreeBSD.org/doc/commit/?id=a5d8095437944234f081c94198f92f14f7cc9981

commit a5d8095437944234f081c94198f92f14f7cc9981
Author:     Gordon Tetlow <[email protected]>
AuthorDate: 2025-11-26 19:58:54 +0000
Commit:     Gordon Tetlow <[email protected]>
CommitDate: 2025-11-26 19:58:54 +0000

    Add SA-25:10 and related patches.
    
    Approved by:    so
---
 website/data/security/advisories.toml              |   4 +
 .../advisories/FreeBSD-SA-25:10.unbound.asc        | 164 +++++++++++++++++++++
 .../patches/SA-25:10/unbound-13and14.patch         | 104 +++++++++++++
 .../patches/SA-25:10/unbound-13and14.patch.asc     |  16 ++
 .../security/patches/SA-25:10/unbound-15.patch     |  88 +++++++++++
 .../security/patches/SA-25:10/unbound-15.patch.asc |  16 ++
 6 files changed, 392 insertions(+)

diff --git a/website/data/security/advisories.toml b/website/data/security/advisories.toml
index ad0f58dfe3..f792f093d3 100644
--- a/website/data/security/advisories.toml
+++ b/website/data/security/advisories.toml
@@ -1,6 +1,10 @@
 # Sort advisories by year, month and day
 # $FreeBSD$
 
+[[advisories]]
+name = "FreeBSD-SA-25:10.unbound"
+date = "2025-11-26"
+
 [[advisories]]
 name = "FreeBSD-SA-25:09.netinet"
 date = "2025-10-22"
diff --git a/website/static/security/advisories/FreeBSD-SA-25:10.unbound.asc b/website/static/security/advisories/FreeBSD-SA-25:10.unbound.asc
new file mode 100644
index 0000000000..4c7b59847e
--- /dev/null
+++ b/website/static/security/advisories/FreeBSD-SA-25:10.unbound.asc
@@ -0,0 +1,164 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-25:10.unbound                                    Security Advisory
+                                                          The FreeBSD Project
+
+Topic:          Cache poison in local-unbound service
+
+Category:       contrib
+Module:         unbound
+Announced:      2025-11-26
+Credits:        Yuxiao Wu, Yunyi Zhang, Baojun Liu, Haixin Duan Yang Luo,
+                and JianJun Chen from Tsinghua University along with TaoFei
+                Guo from Peking University.
+Affects:        All supported versions of FreeBSD.
+Corrected:      2025-11-26 16:00:04 UTC (stable/15, 15.0-STABLE)
+                2025-11-26 16:13:20 UTC (releng/15.0, 15.0-RC4-p1)
+                2025-11-26 16:01:01 UTC (stable/14, 14.3-STABLE)
+                2025-11-26 16:13:30 UTC (releng/14.3, 14.3-RELEASE-p6)
+                2025-11-26 16:02:40 UTC (stable/13, 13.5-STABLE)
+                2025-11-26 16:13:41 UTC (releng/13.5, 13.5-RELEASE-p7)
+CVE Name:       CVE-2025-11411
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit <URL:https://security.FreeBSD.org/>.
+
+I.   Background
+
+Unbound is a validating, recursive, and caching DNS resolver included in the
+FreeBSD base system as an optional service called 'local_unbound'.
+
+II.  Problem Description
+
+Promiscuous NS RRSets that complement DNS replies in the authority section
+can be used to trick resolvers to update their delegation information for the
+zone.  Usually these RRSets are used to update the resolver's knowledge of
+the zone's name servers.  If a malicious actor is able to attach such records
+in a reply they would be able to poison Unbound's cache for the delegation
+point.
+
+III. Impact
+
+A malicious actor can exploit the possible poisonous effect by injecting NS
+RRSets (and possibly their respective address records) in a reply.  This
+could be done, for example, by trying to spoof a packet or fragmentation
+attacks.
+
+Unbound would then proceed to update the NS RRSet data it already has since
+the new data has enough trust for it, i.e., in-zone data for the delegation
+point.
+
+FreeBSD 15.0 release candidates previously included a fix to mitigate the
+poison attempt.  This advisory includes an additional fix to mitigate a
+poison attempt through YXDOMAIN and nodata non-referral answers.
+
+This advisory includes patches for FreeBSD 14.3-RELEASE and 13.5-RELEASE that
+cover both the original fix and the additional fix.
+
+IV.  Workaround
+
+No workaround is available.  Systems not leveraging the local-unbound service
+are unaffected. Check 'sysrc local_unbound_enable' and
+'ps ax | grep local-unbound' to see if it is enabled and running.
+
+V.   Solution
+
+Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date.
+
+Perform one of the following:
+
+1) To update your vulnerable system via a binary patch:
+
+Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms,
+or the i386 platform on FreeBSD 13, can be updated via the freebsd-update(8)
+utility:
+
+# freebsd-update fetch
+# freebsd-update install
+# service local_unbound restart
+
+2) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 15.0]
+# fetch https://security.FreeBSD.org/patches/SA-25:10/unbound-15.patch
+# fetch https://security.FreeBSD.org/patches/SA-25.10/unbound-15.patch.asc
+# gpg --verify unbound-15.patch.asc
+
+[FreeBSD 14.3, FreeBSD 13.5]
+# fetch https://security.FreeBSD.org/patches/SA-25:10/unbound-13and14.patch
+# fetch https://security.FreeBSD.org/patches/SA-25.10/unbound-13and14.patch.asc
+# gpg --verify unbound-13and14.patch.asc
+
+b) Apply the patch.  Execute the following commands as root:
+
+# cd /usr/src
+# patch < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in <URL:https://www.FreeBSD.org/handbook/makeworld.html>.
+
+Restart the applicable daemons, or reboot the system.
+
+VI.  Correction details
+
+This issue is corrected as of the corresponding Git commit hash in the
+following stable and release branches:
+
+Branch/path                             Hash                     Revision
+- -------------------------------------------------------------------------
+stable/15/                              b01f35a4e19d    stable/15-n281339
+releng/15.0/                            dabd406d99a9  releng/15.0-n280990
+stable/14/                              cd40a23fb249    stable/14-n272947
+releng/14.3/                            18c4eb2cc642  releng/14.3-n271451
+stable/13/                              2aed524b2329    stable/13-n259573
+releng/13.5/                            9b0808259a8a  releng/13.5-n259183
+- -------------------------------------------------------------------------
+
+Run the following command to see which files were modified by a
+particular commit:
+
+# git show --stat <commit hash>
+
+Or visit the following URL, replacing NNNNNN with the hash:
+
+<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
+
+To determine the commit count in a working tree (for comparison against
+nNNNNNN in the table above), run:
+
+# git rev-list --count --first-parent HEAD
+
+VII. References
+
+<URL:https://www.nlnetlabs.nl/downloads/unbound/CVE-2025-11411.txt>
+
+<URL:https://www.cve.org/CVERecord?id=CVE-2025-11411>
+
+The latest revision of this advisory is available at
+<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-25:10.unbound.asc>
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmknL1AACgkQbljekB8A
+Gu+RNg/7Bc6vJo+NxKwMhwRO5kp0zzWLEPTgsHZXfzhNfoxg1Yie7tIeYPbYLS0E
+ZQ677a8Th1QhmkdvBDG83sNqPJXYpvai3s8SZxhie8sLPZQA1JFINOEgXo3Wvfyl
+9hZ+QdN8DtgZVA4yWFKVm018RefqUAsyd7e5Mw9ci6CHrTbzbjjxcGLEhatwfn/q
+ALfI17WT9wPeYyP1HnC6yT0eGzyPlg+2aZCQsEdfJOjnH3ycWK54Ucy4TF2prQss
++XOLXOHQ17HJFYymQshwPbME3O9jDRPnRuszTY+W51Yqq4XH/Vc+4pAXuLaJeFVj
+g0krR3aamoRO4fM/X/k+tH49dN6wEduE6lf3PzQIqxeudmK8f7rsTmI8kHUPl+EU
+6alEWN0kCC/g1LYPFX48LQ++ZH80I5PaodsUHanTVz2j19uMfqxLwmAjeOhtY1JW
+bqE12KmHArRQnOIfod1qWOEx1Bm9vAXCuMH7Wh5VjQf71MaNcZdbTtmuEC5g1+yO
+mcBZ2KoGUJvyN5Q7+RyLakcq0Ma+4/MbDcHMxSLoEYAWgivdH99svvDb4s4AGqmD
+VE7j2HiU1vOfLn/q8y4ZQYT5iXyIIXYEuTbM2Gs/C3bIy4Ke9DDt7m/JBM8rW0Fo
+hEomWA9nhage5U/10/YaIWwsre63gvCTMi2B5v6RlNR/Efkq3OY=
+=QjfE
+-----END PGP SIGNATURE-----
diff --git a/website/static/security/patches/SA-25:10/unbound-13and14.patch b/website/static/security/patches/SA-25:10/unbound-13and14.patch
new file mode 100644
index 0000000000..9e32487fe3
--- /dev/null
+++ b/website/static/security/patches/SA-25:10/unbound-13and14.patch
@@ -0,0 +1,104 @@
+--- contrib/unbound/iterator/iter_scrub.c.orig
++++ contrib/unbound/iterator/iter_scrub.c
+@@ -418,12 +418,13 @@
+  * @param qinfo: original query.
+  * @param region: where to allocate synthesized CNAMEs.
+  * @param env: module env with config options.
++ * @param zonename: name of server zone.
+  * @return 0 on error.
+  */
+ static int
+ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, 
+ 	struct query_info* qinfo, struct regional* region,
+-	struct module_env* env)
++	struct module_env* env, uint8_t* zonename)
+ {
+ 	uint8_t* sname = qinfo->qname;
+ 	size_t snamelen = qinfo->qname_len;
+@@ -431,7 +432,8 @@
+ 	int cname_length = 0; /* number of CNAMEs, or DNAMEs */
+ 
+ 	if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
+-		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
++		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN &&
++		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_YXDOMAIN)
+ 		return 1;
+ 
+ 	/* For the ANSWER section, remove all "irrelevant" records and add
+@@ -470,6 +472,11 @@
+ 				&aliaslen, pkt)) {
+ 				verbose(VERB_ALGO, "synthesized CNAME "
+ 					"too long");
++				if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) {
++					prev = rrset;
++					rrset = rrset->rrset_all_next;
++					continue;
++				}
+ 				return 0;
+ 			}
+ 			cname_length++;
+@@ -634,6 +641,45 @@
+ 					"RRset:", pkt, msg, prev, &rrset);
+ 				continue;
+ 			}
++			/* If the NS set is a promiscuous NS set, scrub that
++			 * to remove potential for poisonous contents that
++			 * affects other names in the same zone. Remove
++			 * promiscuous NS sets in positive answers, that
++			 * thus have records in the answer section. Nodata
++			 * and nxdomain promiscuous NS sets have been removed
++			 * already. Since the NS rrset is scrubbed, its
++			 * address records are also not marked to be allowed
++			 * and are removed later. */
++			if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR &&
++				msg->an_rrsets != 0 &&
++				1 /* env->cfg->iter_scrub_promiscuous */) {
++				remove_rrset("normalize: removing promiscuous "
++					"RRset:", pkt, msg, prev, &rrset);
++				continue;
++			}
++			/* Also delete promiscuous NS for other RCODEs */
++			if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR
++				&& 1 /* env->cfg->iter_scrub_promiscuous */) {
++				remove_rrset("normalize: removing promiscuous "
++					"RRset:", pkt, msg, prev, &rrset);
++				continue;
++			}
++			/* Also delete promiscuous NS for NOERROR with nodata
++			 * for authoritative answers, not for delegations.
++			 * NOERROR with an_rrsets!=0 already handled.
++			 * Also NOERROR and soa_in_auth already handled.
++			 * NOERROR with an_rrsets==0, and not a referral.
++			 * referral is (NS not the zonename, noSOA).
++			 */
++			if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR
++				&& msg->an_rrsets == 0
++				&& !(dname_pkt_compare(pkt, rrset->dname,
++				     zonename) != 0 && !soa_in_auth(msg))
++				&& 1 /* env->cfg->iter_scrub_promiscuous */) {
++				remove_rrset("normalize: removing promiscuous "
++					"RRset:", pkt, msg, prev, &rrset);
++				continue;
++			}
+ 			if(nsset == NULL) {
+ 				nsset = rrset;
+ 			} else {
+@@ -1044,7 +1090,8 @@
+ 	/* this is not required for basic operation but is a forgery 
+ 	 * resistance (security) feature */
+ 	if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
+-		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) &&
++		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN ||
++		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) &&
+ 		msg->qdcount == 0)
+ 		return 0;
+ 
+@@ -1058,7 +1105,7 @@
+ 	}
+ 
+ 	/* normalize the response, this cleans up the additional.  */
+-	if(!scrub_normalize(pkt, msg, qinfo, region, env))
++	if(!scrub_normalize(pkt, msg, qinfo, region, env, zonename))
+ 		return 0;
+ 	/* delete all out-of-zone information */
+ 	if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate))
diff --git a/website/static/security/patches/SA-25:10/unbound-13and14.patch.asc b/website/static/security/patches/SA-25:10/unbound-13and14.patch.asc
new file mode 100644
index 0000000000..eae49d2f15
--- /dev/null
+++ b/website/static/security/patches/SA-25:10/unbound-13and14.patch.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmknL1MACgkQbljekB8A
+Gu981RAAjwCv+jAp+x1WJ2mbLhYlbs7YAr581B/OZf9+m/hmdDl5pPNfQPN0zKOF
+1Fs6+53ioFvOAMTzGUejqJQPnmxUWWCNbHyhUzweXAoOxApeTPP8mGw2MUnksU0y
+s014rYRJBKATnfSefhTVZNAxv+J/IVYE2pOUvcnD40jXm5X7QwS2iIOzIir1wcDw
+6+fMBS0YnBVmWrKY07LlgVnzonpuX9SrjG7KnKZRq10PPF2j5VNauIz3/uCfDPl3
+2jBtqoE2eSGVtSx6gQ1eXeZWR3elFYExptzKSeUCbUzSHzyDA2srrgWCGWVBiPXQ
+veVmIj7SGzTOs3DS6Rk8/3JTKQhgkm6c+aptj0MW47sijoHvT6kd68XdtdUl2NDQ
+iz9g9On9JDq4Fm0PG82j8pKh/FQZbROpvcNiLdGUTijZ6lsH34OnS2kTdoLDsa3+
+4/k4mVldcuoiJt0SkIcOnUtE5BpzaE6/DO2+c2/kTfJ0bN1B44vJ13RbDAGn41VO
+cehkFz7Ati00fJmUTMnZ1U1FA1zNdEl0T6tHAC1ce9I8p38B+00FAMcb/EtvmxP2
+fEd8wO14aTrh5tbuGxgpqiWomDxb6q5Y6tpec79p9AFSyvR4JgfUK2zxpkSqEOdk
+sYX2jOAdUNBbM40BwOeQhEfNG4vD7eJ67jgZskiOg/eijnoh6fM=
+=c5bF
+-----END PGP SIGNATURE-----
diff --git a/website/static/security/patches/SA-25:10/unbound-15.patch b/website/static/security/patches/SA-25:10/unbound-15.patch
new file mode 100644
index 0000000000..00d55beda2
--- /dev/null
+++ b/website/static/security/patches/SA-25:10/unbound-15.patch
@@ -0,0 +1,88 @@
+--- contrib/unbound/iterator/iter_scrub.c.orig
++++ contrib/unbound/iterator/iter_scrub.c
+@@ -418,12 +418,13 @@
+  * @param qinfo: original query.
+  * @param region: where to allocate synthesized CNAMEs.
+  * @param env: module env with config options.
++ * @param zonename: name of server zone.
+  * @return 0 on error.
+  */
+ static int
+ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, 
+ 	struct query_info* qinfo, struct regional* region,
+-	struct module_env* env)
++	struct module_env* env, uint8_t* zonename)
+ {
+ 	uint8_t* sname = qinfo->qname;
+ 	size_t snamelen = qinfo->qname_len;
+@@ -431,7 +432,8 @@
+ 	int cname_length = 0; /* number of CNAMEs, or DNAMEs */
+ 
+ 	if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
+-		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
++		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN &&
++		FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_YXDOMAIN)
+ 		return 1;
+ 
+ 	/* For the ANSWER section, remove all "irrelevant" records and add
+@@ -470,6 +472,11 @@
+ 				&aliaslen, pkt)) {
+ 				verbose(VERB_ALGO, "synthesized CNAME "
+ 					"too long");
++				if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) {
++					prev = rrset;
++					rrset = rrset->rrset_all_next;
++					continue;
++				}
+ 				return 0;
+ 			}
+ 			cname_length++;
+@@ -650,6 +657,29 @@
+ 					"RRset:", pkt, msg, prev, &rrset);
+ 				continue;
+ 			}
++			/* Also delete promiscuous NS for other RCODEs */
++			if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR
++				&& env->cfg->iter_scrub_promiscuous) {
++				remove_rrset("normalize: removing promiscuous "
++					"RRset:", pkt, msg, prev, &rrset);
++				continue;
++			}
++			/* Also delete promiscuous NS for NOERROR with nodata
++			 * for authoritative answers, not for delegations.
++			 * NOERROR with an_rrsets!=0 already handled.
++			 * Also NOERROR and soa_in_auth already handled.
++			 * NOERROR with an_rrsets==0, and not a referral.
++			 * referral is (NS not the zonename, noSOA).
++			 */
++			if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR
++				&& msg->an_rrsets == 0
++				&& !(dname_pkt_compare(pkt, rrset->dname,
++				     zonename) != 0 && !soa_in_auth(msg))
++				&& env->cfg->iter_scrub_promiscuous) {
++				remove_rrset("normalize: removing promiscuous "
++					"RRset:", pkt, msg, prev, &rrset);
++				continue;
++			}
+ 			if(nsset == NULL) {
+ 				nsset = rrset;
+ 			} else {
+@@ -1060,7 +1090,8 @@
+ 	/* this is not required for basic operation but is a forgery 
+ 	 * resistance (security) feature */
+ 	if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
+-		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) &&
++		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN ||
++		FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_YXDOMAIN) &&
+ 		msg->qdcount == 0)
+ 		return 0;
+ 
+@@ -1074,7 +1105,7 @@
+ 	}
+ 
+ 	/* normalize the response, this cleans up the additional.  */
+-	if(!scrub_normalize(pkt, msg, qinfo, region, env))
++	if(!scrub_normalize(pkt, msg, qinfo, region, env, zonename))
+ 		return 0;
+ 	/* delete all out-of-zone information */
+ 	if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate))
diff --git a/website/static/security/patches/SA-25:10/unbound-15.patch.asc b/website/static/security/patches/SA-25:10/unbound-15.patch.asc
new file mode 100644
index 0000000000..91dfba735f
--- /dev/null
+++ b/website/static/security/patches/SA-25:10/unbound-15.patch.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmknL1QACgkQbljekB8A
+Gu+uwhAA2aGoVEi3MxWOKLlIuWeOuSavmVHa2f+TK8zkPQqj0o2OoUdN7W0GcreT
+gz2FTI9upQuncNpa97ylPgeX6vPH78CaH//vAmGjCPerwI5Gu5bvwjNVByOXFFA/
+k5hq/8Uh4UwUOPgsG7taq05JtapqXXt5GoDnotWOZv2I6DiarJjQmZMA0P7YGfPl
+8Psu4khHF/JBPL5tZeZp8XXGH+lZ1+W1/E5W2ECY1g26hsqVMTD4R/NL7N9SqaG1
+BB3Ij8Ski3geXNsyXFmNPsKVtxmUfTakU99nEONNx9bYnw42rUYz55QwjjaGmMNG
+bdX9T0dx8cz1sN04emqH2+G+1VmzOr3/50u4yjs5jfSiYKvQWQWiv/AWFJ8ml7xS
+rCFNz16CpUm0yYClxdDLaYTRCcSWuNE/wSVQzEMgSR/pOPfV6wXb/gFDvgBksqDw
+TmxBw8oYKgJjKdmRMCg/uzCWaxj3DvxC/Yh85ZbsrDMQKwdNX1aC1YIITnu0Z2rt
+zcQh2BJDETKTcQWOxx0qzeI6MMaK1eemENAobAmd6CRLb3nPXTHRGPiQSeHmCUSL
+vu8jMmqfrcflUpFtErI8tNzca37pDRIxBRrsRIyReiMwBZuzHs23QOksaBrYkRKz
+OwqqmDBWbUEm5jDjgDi/fqCZo8gj3CVtWHveciSSIK3R0YdN+a0=
+=TCFA
+-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.