Date: Wed, 26 Nov 2025 19:59:17 +0000 From: Gordon Tetlow <gordon@FreeBSD.org> To: doc-committers@FreeBSD.org, dev-commits-doc-all@FreeBSD.org Subject: git: a5d8095437 - main - Add SA-25:10 and related patches. Message-ID: <69275c15.2d110.7c4f319@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by gordon: URL: https://cgit.FreeBSD.org/doc/commit/?id=a5d8095437944234f081c94198f92f14f7cc9981 commit a5d8095437944234f081c94198f92f14f7cc9981 Author: Gordon Tetlow <gordon@FreeBSD.org> AuthorDate: 2025-11-26 19:58:54 +0000 Commit: Gordon Tetlow <gordon@FreeBSD.org> 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-----help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69275c15.2d110.7c4f319>
