diff --git a/bin/named/server.c b/bin/named/server.c index 78176773b2..70efe66b08 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -24,6 +24,8 @@ #include #include +#include + #ifdef HAVE_DNSTAP #include #endif @@ -272,10 +274,10 @@ struct zonelistentry { * asynchronously. */ typedef struct matching_view_ctx { - isc_netaddr_t *srcaddr; - isc_netaddr_t *destaddr; + isc_netaddr_t srcaddr; + isc_netaddr_t destaddr; dns_message_t *message; - dns_aclenv_t *env; + dns_aclenv_t *aclenv; ns_server_t *sctx; isc_loop_t *loop; isc_job_cb cb; @@ -9208,6 +9210,8 @@ get_matching_view_done(void *cbarg) { mvctx->cb(mvctx->cbarg); + dns_aclenv_detach(&mvctx->aclenv); + if (mvctx->quota_result == ISC_R_SUCCESS) { isc_quota_release(&mvctx->sctx->sig0checksquota); } @@ -9249,10 +9253,10 @@ get_matching_view_continue(void *cbarg, isc_result_t result) { tsig = dns_tsigkey_identity(mvctx->message->tsigkey); } - if (dns_acl_allowed(mvctx->srcaddr, tsig, mvctx->view->matchclients, - mvctx->env) && - dns_acl_allowed(mvctx->destaddr, tsig, - mvctx->view->matchdestinations, mvctx->env) && + if (dns_acl_allowed(&mvctx->srcaddr, tsig, mvctx->view->matchclients, + mvctx->aclenv) && + dns_acl_allowed(&mvctx->destaddr, tsig, + mvctx->view->matchdestinations, mvctx->aclenv) && !(mvctx->view->matchrecursiveonly && (mvctx->message->flags & DNS_MESSAGEFLAG_RD) == 0)) { @@ -9324,9 +9328,9 @@ get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr, matching_view_ctx_t *mvctx = isc_mem_get(message->mctx, sizeof(*mvctx)); *mvctx = (matching_view_ctx_t){ - .srcaddr = srcaddr, - .destaddr = destaddr, - .env = env, + .srcaddr = *srcaddr, + .destaddr = *destaddr, + .aclenv = dns_aclenv_ref(env), .cb = cb, .cbarg = cbarg, .sigresult = sigresult, diff --git a/bin/tests/system/nsec/ns1/named.conf.j2 b/bin/tests/system/nsec/ns1/named.conf.j2 new file mode 100644 index 0000000000..eb079c95ab --- /dev/null +++ b/bin/tests/system/nsec/ns1/named.conf.j2 @@ -0,0 +1,29 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; +}; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/nsec/ns1/root.db b/bin/tests/system/nsec/ns1/root.db new file mode 100644 index 0000000000..49f0d671cb --- /dev/null +++ b/bin/tests/system/nsec/ns1/root.db @@ -0,0 +1,24 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +. IN SOA . a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +excessive-nsec-rrsigs. NS ns2.excessive-nsec-rrsigs. +ns2.excessive-nsec-rrsigs. A 10.53.0.2 diff --git a/bin/tests/system/nsec/ns2/excessive-nsec-rrsigs.db.in b/bin/tests/system/nsec/ns2/excessive-nsec-rrsigs.db.in new file mode 100644 index 0000000000..135e93d287 --- /dev/null +++ b/bin/tests/system/nsec/ns2/excessive-nsec-rrsigs.db.in @@ -0,0 +1,24 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) + +@ NS ns2 +ns2 A 10.53.0.2 + +* A 127.0.0.1 diff --git a/bin/tests/system/nsec/ns2/named.conf.j2 b/bin/tests/system/nsec/ns2/named.conf.j2 new file mode 100644 index 0000000000..32a566ad79 --- /dev/null +++ b/bin/tests/system/nsec/ns2/named.conf.j2 @@ -0,0 +1,29 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion no; + notify yes; +}; + +zone "excessive-nsec-rrsigs" { + type primary; + file "excessive-nsec-rrsigs.db.signed"; +}; diff --git a/bin/tests/system/nsec/ns3/named.conf.j2 b/bin/tests/system/nsec/ns3/named.conf.j2 new file mode 100644 index 0000000000..5a8ef09276 --- /dev/null +++ b/bin/tests/system/nsec/ns3/named.conf.j2 @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// validating resolver + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation yes; + + max-records-per-type 2; +}; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/nsec/ns3/trusted.conf.j2 b/bin/tests/system/nsec/ns3/trusted.conf.j2 new file mode 120000 index 0000000000..cb0be77b22 --- /dev/null +++ b/bin/tests/system/nsec/ns3/trusted.conf.j2 @@ -0,0 +1 @@ +../../_common/trusted.conf.j2 \ No newline at end of file diff --git a/bin/tests/system/nsec/tests_excessive_rrsigs.py b/bin/tests/system/nsec/tests_excessive_rrsigs.py new file mode 100755 index 0000000000..8bc62fda71 --- /dev/null +++ b/bin/tests/system/nsec/tests_excessive_rrsigs.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.ANY.RRSIG +import dns.zone + +from isctest.run import EnvCmd + +import isctest + + +def duplicate_rrsig(rdata, i): + return dns.rdtypes.ANY.RRSIG.RRSIG( + rdclass=rdata.rdclass, + rdtype=rdata.rdtype, + type_covered=rdata.type_covered, + algorithm=rdata.algorithm, + labels=rdata.labels, + # increment orig TTL so the rdataset isn't treated as identical record by dnspython + original_ttl=rdata.original_ttl + i, + expiration=rdata.expiration, + inception=rdata.inception, + key_tag=rdata.key_tag, + signer=rdata.signer, + signature=rdata.signature, + ) + + +def bootstrap(): + keygen = EnvCmd("KEYGEN", "-a ECDSA256 -Kns2 -q") + signer = EnvCmd("SIGNER", "-S -g") + + zone = "excessive-nsec-rrsigs" + infile = f"{zone}.db.in" + signedfile = f"{zone}.db.signed" + + isctest.log.info(f"{zone}: generate ksk and zsk") + ksk_name = keygen(f"-f KSK {zone}").out.strip() + keygen(f"{zone}").out.strip() + ksk = isctest.kasp.Key(ksk_name, keydir="ns2") + + isctest.log.info(f"{zone}: sign zone") + signer(f"-P -x -O full -o {zone} -f {signedfile} {infile}", cwd="ns2") + + isctest.log.info( + f"{zone}: duplicate the RRSIG(NSEC) to exceed max-records-per-type" + ) + zone = dns.zone.from_file(f"ns2/{signedfile}", origin=zone) + for node in zone.values(): + rrsig_rdataset = node.find_rdataset( + dns.rdataclass.IN, dns.rdatatype.RRSIG, dns.rdatatype.NSEC + ) + orig = rrsig_rdataset[0] + rrsig_rdataset.add(duplicate_rrsig(orig, 1)) + rrsig_rdataset.add(duplicate_rrsig(orig, 2)) + zone.to_file(f"ns2/{signedfile}", sorted=True) + + return { + "trust_anchors": [ + ksk.into_ta("static-key"), + ], + } + + +# reproducer for CVE-2026-3104 [GL#5742] +def test_excessive_rrsigs(ns3): + # the real test is that there is no crash on shutdown - checked by the test + # framework when the test finishes + + # multiple queries seem more reliable to reproduce the memory leak, using a + # single query sometimes didn't cause a crash on shutdown + for i in range(10): + msg = isctest.query.create(f"x{i}.excessive-nsec-rrsigs", "A") + res = isctest.query.udp(msg, ns3.ip, attempts=1) + isctest.check.servfail(res) diff --git a/bin/tests/system/nsec3-delegation/ns1/named.conf.j2 b/bin/tests/system/nsec3-delegation/ns1/named.conf.j2 new file mode 100644 index 0000000000..65016d1c67 --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns1/named.conf.j2 @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation no; +}; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +include "../../_common/rndc.key"; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/nsec3-delegation/ns1/root.db b/bin/tests/system/nsec3-delegation/ns1/root.db new file mode 100644 index 0000000000..c3f80d0d4b --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns1/root.db @@ -0,0 +1,25 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +. IN SOA . . ( + 2025063000 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. + +a.root-servers.nil A 10.53.0.1 + +iter-too-many. NS ns2.iter-too-many. +ns2.iter-too-many. A 10.53.0.2 diff --git a/bin/tests/system/nsec3-delegation/ns2/iter-too-many.db.j2.manual b/bin/tests/system/nsec3-delegation/ns2/iter-too-many.db.j2.manual new file mode 100644 index 0000000000..fa5023d21b --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns2/iter-too-many.db.j2.manual @@ -0,0 +1,31 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +{% raw %} +$TTL 300 +@ IN SOA ns2.iter-too-many. hostmaster.iter-too-many. ( + 2026020300 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) +) + +@ IN NS ns2.iter-too-many. +ns2 IN A 10.53.0.2 + +sub IN NS ns2.sub.iter-too-many. +ns2.sub IN A 10.53.0.2 +{% endraw %} + +{% for dnskey in dnskeys %} +@dnskey@ +{% endfor %} diff --git a/bin/tests/system/nsec3-delegation/ns2/named.conf.j2 b/bin/tests/system/nsec3-delegation/ns2/named.conf.j2 new file mode 100644 index 0000000000..2f4823574f --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns2/named.conf.j2 @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation no; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +include "../../_common/rndc.key"; + +zone "iter-too-many" { + type primary; + file "iter-too-many.signed.db"; +}; + +zone "sub.iter-too-many" { + type primary; + file "sub.iter-too-many.db"; +}; diff --git a/bin/tests/system/nsec3-delegation/ns2/sub.iter-too-many.db b/bin/tests/system/nsec3-delegation/ns2/sub.iter-too-many.db new file mode 100644 index 0000000000..09b2bb6fb3 --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns2/sub.iter-too-many.db @@ -0,0 +1,24 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA ns2.sub.iter-too-many. hostmaster.sub.iter-too-many. ( + 2026020300 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) +) + +@ IN NS ns2.sub.iter-too-many. +ns2 IN A 10.53.0.2 + +example IN A 127.0.0.1 diff --git a/bin/tests/system/nsec3-delegation/ns3/named.conf.j2 b/bin/tests/system/nsec3-delegation/ns3/named.conf.j2 new file mode 100644 index 0000000000..e36b88c53e --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns3/named.conf.j2 @@ -0,0 +1,37 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation yes; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +include "../../_common/rndc.key"; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/nsec3-delegation/ns3/trusted.conf.j2 b/bin/tests/system/nsec3-delegation/ns3/trusted.conf.j2 new file mode 120000 index 0000000000..cb0be77b22 --- /dev/null +++ b/bin/tests/system/nsec3-delegation/ns3/trusted.conf.j2 @@ -0,0 +1 @@ +../../_common/trusted.conf.j2 \ No newline at end of file diff --git a/bin/tests/system/nsec3-delegation/tests_excessive_nsec3_iterations.py b/bin/tests/system/nsec3-delegation/tests_excessive_nsec3_iterations.py new file mode 100644 index 0000000000..5bd17ed874 --- /dev/null +++ b/bin/tests/system/nsec3-delegation/tests_excessive_nsec3_iterations.py @@ -0,0 +1,61 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +from isctest.run import EnvCmd + +import isctest + + +def bootstrap(): + templates = isctest.template.TemplateEngine(".") + keygen = EnvCmd("KEYGEN", "-a ECDSA256") + signer = EnvCmd("SIGNER") + + isctest.log.info("setup iter-too-many.") + zonename = "iter-too-many." + ksk_name = keygen(f"-f KSK {zonename}", cwd="ns2").out.strip() + zsk_name = keygen(f"{zonename}", cwd="ns2").out.strip() + ksk = isctest.kasp.Key(ksk_name, keydir="ns2") + zsk = isctest.kasp.Key(zsk_name, keydir="ns2") + dnskeys = [ksk.dnskey, zsk.dnskey] + + tdata = { + "dnskeys": dnskeys, + } + templates.render(f"ns2/{zonename}db", tdata, template=f"ns2/{zonename}db.j2.manual") + signer( + f"-P -o {zonename} -f {zonename}signed.db -3 A1B2C3D4 -H too-many -H 51 -S {zonename}db", + cwd="ns2", + ) + + return { + "trust_anchors": [ + ksk.into_ta("static-key"), + ], + } + + +def test_excessive_nsec3_iterations_delegation(ns3): + # reproducer for CVE-2026-1519 [GL#5708] + zone = "example.sub.iter-too-many" + msg = isctest.query.create(zone, "A") + res = isctest.query.tcp(msg, ns3.ip) + + # an insecure response is expected regardless of the NSEC3 iteration limit, + # because the sub.iter-too-many. zone is unsigned. the real difference is + # in the CPU usage required for generating such response, but that can't be + # easily and reliably tested in an automated fashion + isctest.check.noerror(res) + + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"validating {zone}/A: validator_callback_ds: too many iterations" + ) diff --git a/bin/tests/system/sig0/ns1/named.conf.j2 b/bin/tests/system/sig0/ns1/named.conf.j2 new file mode 100644 index 0000000000..724a40c58c --- /dev/null +++ b/bin/tests/system/sig0/ns1/named.conf.j2 @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +view "v1" { + match-clients { any; }; + zone "." { + type hint; + file "/dev/null"; + }; +}; diff --git a/bin/tests/system/sig0/setup.sh b/bin/tests/system/sig0/setup.sh new file mode 100644 index 0000000000..64a8c3a481 --- /dev/null +++ b/bin/tests/system/sig0/setup.sh @@ -0,0 +1,17 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../conf.sh + +key=$($KEYGEN -q -a RSASHA256 -b 2048 sig0.) diff --git a/bin/tests/system/sig0/tests_sig0.py b/bin/tests/system/sig0/tests_sig0.py new file mode 100644 index 0000000000..500d2b5eab --- /dev/null +++ b/bin/tests/system/sig0/tests_sig0.py @@ -0,0 +1,119 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import base64 +import glob +import os +import struct +import time + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding, rsa + +import dns.flags +import dns.message +import dns.name +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.renderer +import dns.rrset + +import isctest + + +def load_bind_private_key(filename): + """Parses a BIND 9 .private key file.""" + with open(filename, "r", encoding="utf-8") as f: + lines = f.readlines() + + data = {} + for line in lines: + if ":" in line: + key, value = line.split(":", 1) + data[key.strip()] = value.strip() + + def b64int(k): + return int.from_bytes(base64.b64decode(data[k]), byteorder="big") + + rsa_key = rsa.RSAPrivateNumbers( + p=b64int("Prime1"), + q=b64int("Prime2"), + d=b64int("PrivateExponent"), + dmp1=b64int("Exponent1"), + dmq1=b64int("Exponent2"), + iqmp=b64int("Coefficient"), + public_numbers=rsa.RSAPublicNumbers( + e=b64int("PublicExponent"), n=b64int("Modulus") + ), + ).private_key(default_backend()) + + return rsa_key + + +def make_sig0_query(key_file, key_name_str): + private_key = load_bind_private_key(key_file) + + qname = dns.name.from_text(".") + query = dns.message.make_query(qname, dns.rdatatype.SOA) + query.flags |= dns.flags.RD + + # Render message to bytes (needed for signing) + renderer = dns.renderer.Renderer() + query.to_wire(renderer) + msg_bytes = renderer.get_wire() + + # SIG(0) Constants + basename = os.path.basename(key_file) + key_tag = int(basename.split("+")[2].split(".")[0]) + + now = int(time.time()) + expiration = now + 3600 + inception = now - 3600 + signer_name = dns.name.from_text(key_name_str) + + # Construct SIG RDATA header (0=SIG(0), 8=RSASHA256, 0=Labels) + sig_rdata_header = struct.pack( + "!HBBIIIH", 0, 8, 0, 0, expiration, inception, key_tag + ) + + sig_rdata_pre_sig = sig_rdata_header + signer_name.to_wire() + + # Sign: ( SIG RDATA sans signature ) + ( Message ) + signature = private_key.sign( + sig_rdata_pre_sig + msg_bytes, padding.PKCS1v15(), hashes.SHA256() + ) + + # Create the SIG RR + full_sig_rdata = sig_rdata_pre_sig + signature + sig_rr = dns.rdata.from_wire( + dns.rdataclass.ANY, + dns.rdatatype.SIG, + full_sig_rdata, + 0, + len(full_sig_rdata), + ) + sig_rrset = dns.rrset.from_rdata(qname, 0, sig_rr) + query.additional.append(sig_rrset) + + return query + + +def test_sig0_acl_bypass(): + key_files = glob.glob("Ksig0.+*.private") + assert len(key_files) == 1 + + query = make_sig0_query(key_files[0], "sig0.") + + # Send the query + res = isctest.query.tcp(query, "10.53.0.1") + isctest.check.servfail(res) diff --git a/bin/tests/system/tkey/ns1/example.db b/bin/tests/system/tkey/ns1/example.db new file mode 100644 index 0000000000..49c499c3a0 --- /dev/null +++ b/bin/tests/system/tkey/ns1/example.db @@ -0,0 +1,23 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$ORIGIN . +$TTL 300 ; 5 minutes +example.nil IN SOA ns1.example.nil. hostmaster.example.nil. ( + 1 ; serial + 2000 ; refresh (2000 seconds) + 2000 ; retry (2000 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) +example.nil. NS ns1.example.nil. +ns1.example.nil. A 10.53.0.1 +a.example.nil. A 10.53.0.1 diff --git a/bin/tests/system/tkey/ns1/named.conf.j2 b/bin/tests/system/tkey/ns1/named.conf.j2 new file mode 100644 index 0000000000..4603956744 --- /dev/null +++ b/bin/tests/system/tkey/ns1/named.conf.j2 @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation no; + notify no; +}; + +key "test-key" { + algorithm "hmac-sha256"; + secret "R16NojROxtxH/xbDl//ehDsHm5DjWTQ2YXV+hGC2iBY="; +}; + +zone "example.nil" { + type primary; + file "example.db"; +}; diff --git a/bin/tests/system/tkey/tests_cve_2026_3119.py b/bin/tests/system/tkey/tests_cve_2026_3119.py new file mode 100644 index 0000000000..bbf673d8df --- /dev/null +++ b/bin/tests/system/tkey/tests_cve_2026_3119.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# pylint: disable=unused-variable + +import time + +import dns.message +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.ANY.TKEY +import dns.rrset +import dns.tsigkeyring +import pytest + +import isctest + +pytestmark = pytest.mark.extra_artifacts([]) + + +def create_tkey_msg(qname, mode, alg="hmac-sha256"): + msg = dns.message.make_query(qname, "TKEY") + now = int(time.time()) + rdata = dns.rdtypes.ANY.TKEY.TKEY( + rdclass=dns.rdataclass.ANY, + rdtype=dns.rdatatype.TKEY, + algorithm=alg, + inception=now - 3600, + expiration=now + 86400, + mode=mode, + error=0, + key=b"", + ) + rrset = dns.rrset.from_rdata(qname, dns.rdatatype.TKEY, rdata) + msg.additional.append(rrset) + return msg + + +def test_tkey_cve_2026_3119(ns1): + keyring = dns.tsigkeyring.from_text( + { + "test-key": "R16NojROxtxH/xbDl//ehDsHm5DjWTQ2YXV+hGC2iBY=", + } + ) + + msg_delete = create_tkey_msg("a.example.nil.", 5) + msg_delete.use_tsig(keyring, keyname="test-key") + isctest.query.tcp(msg_delete, ns1.ip, attempts=1) + + msg_unsupported = create_tkey_msg("a.example.nil.", 99) + msg_unsupported.use_tsig(keyring, keyname="test-key") + isctest.query.tcp(msg_unsupported, ns1.ip, attempts=1) diff --git a/doc/arm/changelog.rst b/doc/arm/changelog.rst index 2a52bd975b..d7129b1d55 100644 --- a/doc/arm/changelog.rst +++ b/doc/arm/changelog.rst @@ -18,6 +18,7 @@ Changelog development. Regular users should refer to :ref:`Release Notes ` for changes relevant to them. +.. include:: ../changelog/changelog-9.21.20.rst .. include:: ../changelog/changelog-9.21.19.rst .. include:: ../changelog/changelog-9.21.18.rst .. include:: ../changelog/changelog-9.21.17.rst diff --git a/doc/arm/notes.rst b/doc/arm/notes.rst index ae427f36be..472ebf2ea9 100644 --- a/doc/arm/notes.rst +++ b/doc/arm/notes.rst @@ -47,6 +47,7 @@ The list of known issues affecting the latest version in the 9.21 branch can be found at https://gitlab.isc.org/isc-projects/bind9/-/wikis/Known-Issues-in-BIND-9.21 +.. include:: ../notes/notes-9.21.20.rst .. include:: ../notes/notes-9.21.19.rst .. include:: ../notes/notes-9.21.18.rst .. include:: ../notes/notes-9.21.17.rst diff --git a/doc/changelog/changelog-9.21.20.rst b/doc/changelog/changelog-9.21.20.rst new file mode 100644 index 0000000000..bf0696b001 --- /dev/null +++ b/doc/changelog/changelog-9.21.20.rst @@ -0,0 +1,137 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +BIND 9.21.20 +------------ + +Security Fixes +~~~~~~~~~~~~~~ + +- [CVE-2026-1519] Fix unbounded NSEC3 iterations when validating + referrals to unsigned delegations. ``779463a703e`` + + DNSSEC-signed zones may contain high iteration-count NSEC3 records, + which prove that certain delegations are insecure. Previously, a + validating resolver encountering such a delegation processed these + iterations up to the number given, which could be a maximum of 65,535. + This has been addressed by introducing a processing limit, set at 50. + Now, if such an NSEC3 record is encountered, the delegation will be + treated as insecure. + + ISC would like to thank Samy Medjahed/Ap4sh for bringing this + vulnerability to our attention. :gl:`#5708` + +- [CVE-2026-3104] Fix memory leaks in code preparing DNSSEC proofs of + non-existence. ``5e29b24dcd0`` + + An attacker controlling a DNSSEC-signed zone could trigger a memory + leak in the logic preparing DNSSEC proofs of non-existence, by + creating more than :any:`max-records-per-type` RRSIGs for NSEC + records. These memory leaks have been fixed. + + ISC would like to thank Vitaly Simonovich for bringing this + vulnerability to our attention. :gl:`#5742` + +- [CVE-2026-3119] Prevent a crash in code processing queries containing + a TKEY record. ``adbe0358089`` + + The :iscman:`named` process could terminate unexpectedly when + processing a correctly signed query containing a TKEY record. This has + been fixed. + + ISC would like to thank Vitaly Simonovich for bringing this + vulnerability to our attention. :gl:`#5748` + +- [CVE-2026-3591] Fix a stack use-after-return flaw in SIG(0) handling + code. ``c64392c731b`` + + A stack use-after-return flaw in SIG(0) handling code could enable ACL + bypass and/or assertion failures in certain circumstances. This flaw + has been fixed. + + ISC would like to thank Mcsky23 for bringing this vulnerability to our + attention. :gl:`#5754` + +New Features +~~~~~~~~~~~~ + +- Provide response round-trip time (RTT) counters via statistics + channel. ``e7b1a44b8c5`` + + Previously, :iscman:`named` provided RTT counters for outgoing queries + performed by itself during name resolutions. Now this has been + improved to provide more granular counters (histogram), and to also + provide RTT counters for the incoming queries. :gl:`#5279` + :gl:`!11508` + +Feature Changes +~~~~~~~~~~~~~~~ + +- Introduce max-delegation-servers configuration option. ``d2cb28d43ee`` + + Make the maximum number of processed delegation nameservers + configurable via the new 'max-delegation-servers' option (default: + 13), replacing the hardcoded NS_PROCESSING_LIMIT (20). + + The default is reduced to 13 to precisely match the maximum number of + root servers that can fit into a classic 512-byte UDP payload. This + provides a natural, historically sound cap that mitigates resource + exhaustion and amplification attacks from artificially inflated or + misconfigured delegations. + + The configuration option is strictly bounded between 1 and 100 to + ensure resolver stability. :gl:`!11607` + +- Replace lock keyfile hashmap with lock pool. ``4fd84193c73`` + + Kasp used a lock per zone origin in order to prevent concurrent access + to keyfiles. This lead to substantial memory consumption in the case + of authoritative servers with many small zones, as lots of locks need + to be allocated. + + Since the number of keyfile locks taken cannot exceed the number of + helper threads, it makes more sense to use a lock pool of fixed size + keyed by the hash of the origin name, leading to memory savings. + :gl:`!11633` + +Bug Fixes +~~~~~~~~~ + +- Fix setting retire in dns_keymgr_key_init. ``a6701c37b98`` + + A wrong-variable bug in `dns_keymgr_key_init()` causes the DNSSEC key + inactive time to never be read. This means the key state is retracting + zone signatures where it should have, delaying the key rollover. + + ISC would like to thank Naresh Kandula Parmar (Nottiboy) for reporting + this. :gl:`#5774` :gl:`!11624` + +- Fix resquery reference imbalance on TCP connect failure. + ``7c82cb0f14e`` + + In fctx_query(), resquery_ref(query) is called before + dns_dispatch_connect() in anticipation of the resquery_connected() + callback consuming the reference. When dns_dispatch_connect() fails + synchronously on TCP (e.g. from dns_transport_get_tlsctx() failing in + tcp_dispatch_connect()), the connect callback is never scheduled, so + the extra reference is never consumed. This has been fixed. + :gl:`!11640` + +- Resolve "key defined in view is not found" ``0d5f47e3ec2`` + + A recent change in `2956e4fc45b3c2142a3351682d4200647448f193` hardened + the `key` name check when used in `primaries` to immediately reject + the configuration if the key was not defined (rather than only + checking whether the key name was correctly formed). However, the + change introduced a regression that prevented the use of a `key` + defined in a view. This is now fixed. :gl:`!11588` + + diff --git a/doc/notes/notes-9.21.20.rst b/doc/notes/notes-9.21.20.rst new file mode 100644 index 0000000000..7e042d1b8f --- /dev/null +++ b/doc/notes/notes-9.21.20.rst @@ -0,0 +1,108 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +Notes for BIND 9.21.20 +---------------------- + +Security Fixes +~~~~~~~~~~~~~~ + +- Fix unbounded NSEC3 iterations when validating referrals to unsigned + delegations. :cve:`2026-1519` + + DNSSEC-signed zones may contain high iteration-count NSEC3 records, + which prove that certain delegations are insecure. Previously, a + validating resolver encountering such a delegation processed these + iterations up to the number given, which could be a maximum of 65,535. + This has been addressed by introducing a processing limit, set at 50. + Now, if such an NSEC3 record is encountered, the delegation will be + treated as insecure. + + ISC would like to thank Samy Medjahed/Ap4sh for bringing this + vulnerability to our attention. :gl:`#5708` + +- Fix memory leaks in code preparing DNSSEC proofs of non-existence. + :cve:`2026-3104` + + An attacker controlling a DNSSEC-signed zone could trigger a memory + leak in the logic preparing DNSSEC proofs of non-existence, by + creating more than :any:`max-records-per-type` RRSIGs for NSEC + records. These memory leaks have been fixed. + + ISC would like to thank Vitaly Simonovich for bringing this + vulnerability to our attention. :gl:`#5742` + +- Prevent a crash in code processing queries containing a TKEY record. + :cve:`2026-3119` + + The :iscman:`named` process could terminate unexpectedly when + processing a correctly signed query containing a TKEY record. This has + been fixed. + + ISC would like to thank Vitaly Simonovich for bringing this + vulnerability to our attention. :gl:`#5748` + +- Fix a stack use-after-return flaw in SIG(0) handling code. + :cve:`2026-3591` + + A stack use-after-return flaw in SIG(0) handling code could enable ACL + bypass and/or assertion failures in certain circumstances. This flaw + has been fixed. + + ISC would like to thank Mcsky23 for bringing this vulnerability to our + attention. :gl:`#5754` + +New Features +~~~~~~~~~~~~ + +- Provide response round-trip time (RTT) counters via statistics + channel. + + Previously, :iscman:`named` provided RTT counters for outgoing queries + that it performed during name resolutions. This has now been improved + to provide more granular counters (histogram), and to also provide RTT + counters for the incoming queries. :gl:`#5279` + +- Introduce :any:`max-delegation-servers` configuration option. + + Make the maximum number of processed delegation nameservers + configurable via the new :any:`max-delegation-servers` option + (default: 13), replacing the hardcoded ``NS_PROCESSING_LIMIT`` (20). + + The default is reduced to 13 to precisely match the maximum number of + root servers that can fit into a classic 512-byte UDP payload. This + provides a natural, historically sound cap that mitigates resource + exhaustion and amplification attacks from artificially inflated or + misconfigured delegations. + + The configuration option is strictly bounded between 1 and 100 to + ensure resolver stability. :gl:`!11607` + +Bug Fixes +~~~~~~~~~ + +- Fix parsing key inactivation time in KASP code. + + A wrong-variable bug in KASP code caused the DNSSEC key inactivation + time to never be read. As a result, zone signatures were being + retracted later than they should be, which caused unnecessary key + rollover delays. This has now been fixed. :gl:`#5774` + +- Fix the handling of :namedconf:ref:`key` statements defined inside + views. + + A recent change introduced in BIND 9.21.16 hardened the + :namedconf:ref:`key` name check when used in :any:`primaries`, to + immediately reject the configuration if the key was not defined + (rather than only checking whether the key name was correctly formed). + However, that change introduced a regression that prevented the use of + a :namedconf:ref:`key` defined in a view. This has now been fixed. + :gl:`#5761` diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index ef1a546154..f8d228c6d0 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -384,6 +384,7 @@ enum { ((x) == dns_trust_additional || (x) == dns_trust_pending_additional) #define DNS_TRUST_GLUE(x) ((x) == dns_trust_glue) #define DNS_TRUST_ANSWER(x) ((x) == dns_trust_answer) +#define DNS_TRUST_SECURE(x) ((x) >= dns_trust_secure) /*% * Name checking severities. diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index dd16406659..3851480d40 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -2981,7 +2981,7 @@ addnoqname(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, dns_slabheader_proof_t *noqname = NULL; dns_name_t name = DNS_NAME_INITEMPTY; dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; - isc_region_t r1, r2; + isc_region_t r1 = { .base = NULL }, r2 = { .base = NULL }; result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -3001,6 +3001,14 @@ addnoqname(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, newheader->noqname = noqname; cleanup: + if (result != ISC_R_SUCCESS) { + if (r1.base != NULL) { + isc_mem_put(mctx, r1.base, r1.length); + } + if (r2.base != NULL) { + isc_mem_put(mctx, r2.base, r2.length); + } + } dns_rdataset_disassociate(&neg); dns_rdataset_disassociate(&negsig); @@ -3014,7 +3022,7 @@ addclosest(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, dns_slabheader_proof_t *closest = NULL; dns_name_t name = DNS_NAME_INITEMPTY; dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; - isc_region_t r1, r2; + isc_region_t r1 = { .base = NULL }, r2 = { .base = NULL }; result = dns_rdataset_getclosest(rdataset, &name, &neg, &negsig); RUNTIME_CHECK(result == ISC_R_SUCCESS); @@ -3034,6 +3042,14 @@ addclosest(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, newheader->closest = closest; cleanup: + if (result != ISC_R_SUCCESS) { + if (r1.base != NULL) { + isc_mem_put(mctx, r1.base, r1.length); + } + if (r2.base != NULL) { + isc_mem_put(mctx, r2.base, r2.length); + } + } dns_rdataset_disassociate(&neg); dns_rdataset_disassociate(&negsig); return result; @@ -3106,12 +3122,12 @@ qpcache_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_OPTOUT); } if (rdataset->attributes.noqname) { - RETERR(addnoqname(qpnode->mctx, newheader, qpdb->maxrrperset, - rdataset)); + CHECK(addnoqname(qpnode->mctx, newheader, qpdb->maxrrperset, + rdataset)); } if (rdataset->attributes.closest) { - RETERR(addclosest(qpnode->mctx, newheader, qpdb->maxrrperset, - rdataset)); + CHECK(addclosest(qpnode->mctx, newheader, qpdb->maxrrperset, + rdataset)); } nlock = &qpdb->buckets[qpnode->locknum].lock; @@ -3179,6 +3195,9 @@ qpcache_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, INSIST(tlocktype == isc_rwlocktype_none); + return result; +cleanup: + dns_slabheader_destroy(&newheader); return result; } diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c index 9f71c4e41c..f09edc2a9c 100644 --- a/lib/dns/tkey.c +++ b/lib/dns/tkey.c @@ -401,7 +401,8 @@ dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx, /* * A delete operation uses the fully specified qname. */ - CHECK(process_deletetkey(signer, qname, &tkeyin, &tkeyout, + keyname = qname; + CHECK(process_deletetkey(signer, keyname, &tkeyin, &tkeyout, ring)); break; case DNS_TKEYMODE_GSSAPI: @@ -443,6 +444,10 @@ dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx, result = DNS_R_NOTIMP; goto cleanup; default: + /* + * For unrecognized modes also use the fully specified qname. + */ + keyname = qname; tkeyout.error = dns_tsigerror_badmode; } diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 400143f79e..da01b52a09 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -217,7 +217,8 @@ markanswer(dns_validator_t *val, const char *where) { * Mark the RRsets in val->vstat with trust level secure. */ static void -marksecure(dns_validator_t *val) { +marksecure(dns_validator_t *val, const char *where) { + validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (%s)", where); dns_rdataset_settrust(val->rdataset, dns_trust_secure); if (val->sigrdataset != NULL) { dns_rdataset_settrust(val->sigrdataset, dns_trust_secure); @@ -244,12 +245,25 @@ validator_done(dns_validator_t *val, isc_result_t result) { } /*% - * Look in the NSEC record returned from a DS query to see if there is - * a NS RRset at this name. If it is found we are at a delegation point. + * The isdelegation() function is called as part of seeking the DS record. + * Look in the NSEC or NSEC3 record returned from a DS query to see if the + * record has the NS bitmap set. If so, we are at a delegation point. + * + * If the response contains NSEC3 records with too high iterations, we cannot + * (or rather we are not going to) validate the insecurity proof. Instead we + * are going to treat the message as insecure and just assume the DS was at + * the delegation. + * + * Returns: + *\li #ISC_R_SUCCESS the NS bitmap was set in the NSEC or NSEC3 record, or + * the NSEC3 covers the name (in case of opt-out), or + * we cannot validate the insecurity proof and are going + * to treat the message as isnecure. + *\li #ISC_R_NOTFOUND the NS bitmap was not set, */ -static bool -isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, - isc_result_t dbresult) { +static isc_result_t +isdelegation(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset, + isc_result_t dbresult, const char *caller) { dns_fixedname_t fixed; dns_label_t hashlabel; dns_name_t nsec3name; @@ -276,7 +290,7 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, goto trynsec3; } if (result != ISC_R_SUCCESS) { - return false; + return ISC_R_NOTFOUND; } } @@ -290,7 +304,7 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, found = dns_nsec_typepresent(&rdata, dns_rdatatype_ns); } dns_rdataset_disassociate(&set); - return found; + return found ? ISC_R_SUCCESS : ISC_R_NOTFOUND; trynsec3: /* @@ -325,6 +339,18 @@ trynsec3: if (nsec3.next_length > NSEC3_MAX_HASH_LENGTH) { continue; } + /* + * If there are too many iterations assume bad things + * are happening and bail out early. Treat as if the + * DS was at the delegation. + */ + if (nsec3.iterations > DNS_NSEC3_MAXITERATIONS) { + validator_log(val, ISC_LOG_DEBUG(3), + "%s: too many iterations", + caller); + dns_rdataset_disassociate(&set); + return ISC_R_SUCCESS; + } length = isc_iterated_hash( hash, nsec3.hash, nsec3.iterations, nsec3.salt, nsec3.salt_length, name->ndata, name->length); @@ -336,7 +362,7 @@ trynsec3: found = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns); dns_rdataset_disassociate(&set); - return found; + return found ? ISC_R_SUCCESS : ISC_R_NOTFOUND; } if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0) { continue; @@ -352,12 +378,12 @@ trynsec3: memcmp(hash, nsec3.next, length) < 0))) { dns_rdataset_disassociate(&set); - return true; + return ISC_R_SUCCESS; } } dns_rdataset_disassociate(&set); } - return found; + return found ? ISC_R_SUCCESS : ISC_R_NOTFOUND; } static void @@ -593,9 +619,10 @@ fetch_callback_ds(void *arg) { break; case DNS_R_NXRRSET: case DNS_R_NCACHENXRRSET: - if (isdelegation(resp->foundname, &val->frdataset, - eresult)) - { + result = isdelegation(val, resp->foundname, + &val->frdataset, eresult, + "fetch_callback_ds"); + if (result == ISC_R_SUCCESS) { /* * Failed to find a DS while trying to prove * insecurity. If this is a zone cut, that @@ -708,10 +735,13 @@ validator_callback_ds(void *arg) { dns_trust_totext(val->frdataset.trust)); have_dsset = (val->frdataset.type == dns_rdatatype_ds); name = dns_fixedname_name(&val->fname); + if ((val->attributes & VALATTR_INSECURITY) != 0 && val->frdataset.covers == dns_rdatatype_ds && NEGATIVE(&val->frdataset) && - isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) + isdelegation(val, name, &val->frdataset, + DNS_R_NCACHENXRRSET, + "validator_callback_ds") == ISC_R_SUCCESS) { result = markanswer(val, "validator_callback_ds"); } else if ((val->attributes & VALATTR_INSECURITY) != 0) { @@ -1441,11 +1471,19 @@ verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, bool ignore = false; dns_name_t *wild; + if (DNS_TRUST_SECURE(val->rdataset->trust)) { + /* + * This RRset was already verified before. + */ + return ISC_R_SUCCESS; + } + val->attributes |= VALATTR_TRIEDVERIFY; - wild = dns_fixedname_initname(&fixed); if (over_max_validations(val)) { return ISC_R_QUOTA; } + wild = dns_fixedname_initname(&fixed); + again: result = dns_dnssec_verify(val->name, val->rdataset, key, ignore, val->view->mctx, rdata, wild); @@ -1797,9 +1835,7 @@ validate_answer_finish(void *arg) { } if (val->result == ISC_R_SUCCESS) { - marksecure(val); - validator_log(val, ISC_LOG_DEBUG(3), - "marking as secure, noqname proof not needed"); + marksecure(val, "noqname proof not needed"); validate_async_done(val, val->result); return; } @@ -2002,8 +2038,7 @@ validate_dnskey_dsset_done(dns_validator_t *val, isc_result_t result) { /* Abort, abort, abort! */ break; case ISC_R_SUCCESS: - marksecure(val); - validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)"); + marksecure(val, "validate_dnskey (DS)"); break; case ISC_R_NOMORE: if (val->unsupported_algorithm != 0 || @@ -2742,11 +2777,21 @@ validate_neg_rrset(dns_validator_t *val, dns_name_t *name, } } + if (rdataset->type != dns_rdatatype_nsec && + DNS_TRUST_SECURE(rdataset->trust)) + { + /* + * The negative response data is already verified. + * We skip NSEC records, because they require special + * processing in validator_callback_nsec(). + */ + return DNS_R_CONTINUE; + } + val->nxset = rdataset; RETERR(create_validator(val, name, rdataset->type, rdataset, sigrdataset, validator_callback_nsec, "validate_neg_rrset")); - val->authcount++; return DNS_R_WAIT; } @@ -2849,11 +2894,9 @@ validate_ncache(dns_validator_t *val, bool resume) { } result = validate_neg_rrset(val, name, rdataset, sigrdataset); - if (result == DNS_R_CONTINUE) { - continue; + if (result != DNS_R_CONTINUE) { + return result; } - - return result; } if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; @@ -2902,7 +2945,8 @@ validate_nx(dns_validator_t *val, bool resume) { result = findnsec3proofs(val); if (result == DNS_R_NSEC3ITERRANGE) { validator_log(val, ISC_LOG_DEBUG(3), - "too many iterations"); + "%s: too many iterations", + __func__); markanswer(val, "validate_nx (3)"); return ISC_R_SUCCESS; } @@ -2910,9 +2954,7 @@ validate_nx(dns_validator_t *val, bool resume) { if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) && !FOUNDOPTOUT(val)) { - validator_log(val, ISC_LOG_DEBUG(3), - "marking as secure, noqname proof found"); - marksecure(val); + marksecure(val, "validate_nx (noqname proof found)"); return ISC_R_SUCCESS; } else if (FOUNDOPTOUT(val) && dns_name_countlabels( @@ -2938,7 +2980,7 @@ validate_nx(dns_validator_t *val, bool resume) { result = findnsec3proofs(val); if (result == DNS_R_NSEC3ITERRANGE) { validator_log(val, ISC_LOG_DEBUG(3), - "too many iterations"); + "%s: too many iterations", __func__); markanswer(val, "validate_nx (4)"); return ISC_R_SUCCESS; } @@ -2963,7 +3005,8 @@ validate_nx(dns_validator_t *val, bool resume) { validator_log(val, ISC_LOG_DEBUG(3), "nonexistence proof(s) found"); if (val->message == NULL) { - marksecure(val); + marksecure(val, + "validate_nx (nonexistence proofs found)"); } else { val->secure = true; } @@ -3272,7 +3315,9 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) { return ISC_R_COMPLETE; } - if (isdelegation(tname, &val->frdataset, result)) { + result = isdelegation(val, tname, &val->frdataset, result, + "seek_ds"); + if (result == ISC_R_SUCCESS) { *resp = markanswer(val, "seek_ds (3)"); return ISC_R_COMPLETE; }