import CS bind9.18-9.18.29-16.el9
This commit is contained in:
parent
13c63d0259
commit
49ea88a72a
337
SOURCES/bind-9.18-CVE-2026-1519-test.patch
Normal file
337
SOURCES/bind-9.18-CVE-2026-1519-test.patch
Normal file
@ -0,0 +1,337 @@
|
||||
From 7df00188c93d1fb8496328976e00192dd657d5aa Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Nicki=20K=C5=99=C3=AD=C5=BEek?= <nicki@isc.org>
|
||||
Date: Tue, 3 Feb 2026 18:25:04 +0100
|
||||
Subject: [PATCH] Reproducer for CVE-2026-1519
|
||||
|
||||
When a validating resolver processes a delegation from a DNSSEC-signed
|
||||
zone which uses too many NSEC3 iterations, it should cease the attempt
|
||||
to validate due to an NSEC3 iteration limit being exceeded and fall back
|
||||
to insecure.
|
||||
|
||||
(cherry picked from commit 9bc14a89f1313aa38330e84674ac3b7691db3383)
|
||||
(cherry picked from commit 2c82f99a3c95f356861d5977f12ef9bbe2063cb6)
|
||||
---
|
||||
.../system/nsec3-delegation/ns1/named.conf.j2 | 35 +++++++++++
|
||||
bin/tests/system/nsec3-delegation/ns1/root.db | 25 ++++++++
|
||||
.../ns2/iter-too-many.db.j2.manual | 31 ++++++++++
|
||||
.../system/nsec3-delegation/ns2/named.conf.j2 | 40 ++++++++++++
|
||||
.../nsec3-delegation/ns2/sub.iter-too-many.db | 24 ++++++++
|
||||
.../system/nsec3-delegation/ns3/named.conf.j2 | 37 +++++++++++
|
||||
.../nsec3-delegation/ns3/trusted.conf.j2 | 1 +
|
||||
.../tests_excessive_nsec3_iterations.py | 61 +++++++++++++++++++
|
||||
8 files changed, 254 insertions(+)
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/ns1/named.conf.j2
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/ns1/root.db
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/ns2/iter-too-many.db.j2.manual
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/ns2/named.conf.j2
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/ns2/sub.iter-too-many.db
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/ns3/named.conf.j2
|
||||
create mode 120000 bin/tests/system/nsec3-delegation/ns3/trusted.conf.j2
|
||||
create mode 100644 bin/tests/system/nsec3-delegation/tests_excessive_nsec3_iterations.py
|
||||
|
||||
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..f85384bb1e
|
||||
--- /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 151 -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"
|
||||
+ )
|
||||
--
|
||||
2.53.0
|
||||
|
||||
259
SOURCES/bind-9.18-CVE-2026-1519.patch
Normal file
259
SOURCES/bind-9.18-CVE-2026-1519.patch
Normal file
@ -0,0 +1,259 @@
|
||||
From ca79e7575d281e3859afce73ac3276771ce7edbf Mon Sep 17 00:00:00 2001
|
||||
From: Matthijs Mekking <matthijs@isc.org>
|
||||
Date: Tue, 3 Mar 2026 10:40:36 +0100
|
||||
Subject: [PATCH] Check iterations in isdelegation()
|
||||
|
||||
When looking up an NSEC3 as part of an insecurity proof, check the
|
||||
number of iterations. If this is too high, treat the answer as insecure
|
||||
by marking the answer with trust level "answer", indicating that they
|
||||
did not validate, but could be cached as insecure.
|
||||
|
||||
(cherry picked from commit 988040a5e02f86f4a8cdb0704e8d501f9082a89c)
|
||||
(cherry picked from commit 85c21feff9acb0982fe60f2c88201bf55533bd0e)
|
||||
|
||||
Don't verify already trusted rdatasets
|
||||
|
||||
If we already marked an rdataset as secure (or it has even stronger
|
||||
trust), there is no need to cryptographically verify it again.
|
||||
|
||||
(cherry picked from commit 0ec08c212022d08c9717f2bc6bd3e8ebd6f034ce)
|
||||
(cherry picked from commit 8890a91c1c16129333139b9d8a4381e0f741f0d6)
|
||||
|
||||
Check RRset trust in validate_neg_rrset()
|
||||
|
||||
In many places we only create a validator if the RRset has too low
|
||||
trust (the RRset is pending validation, or could not be validated
|
||||
before). This check was missing prior to validating negative response
|
||||
data.
|
||||
|
||||
(cherry picked from commit 6ca67f65cd685cf8699540a852c1e3775bd48d64)
|
||||
(cherry picked from commit 85fcd704e2f7cc2a25d2195bc4bb28398c889ed3)
|
||||
---
|
||||
lib/dns/include/dns/types.h | 1 +
|
||||
lib/dns/validator.c | 91 ++++++++++++++++++++++++++++---------
|
||||
2 files changed, 70 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h
|
||||
index f0aaa24..a945f0e 100644
|
||||
--- a/lib/dns/include/dns/types.h
|
||||
+++ b/lib/dns/include/dns/types.h
|
||||
@@ -353,6 +353,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/validator.c b/lib/dns/validator.c
|
||||
index 5bc0b40..ae98b5f 100644
|
||||
--- a/lib/dns/validator.c
|
||||
+++ b/lib/dns/validator.c
|
||||
@@ -252,12 +252,25 @@ exit_check(dns_validator_t *val) {
|
||||
}
|
||||
|
||||
/*%
|
||||
- * 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;
|
||||
@@ -285,7 +298,7 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
|
||||
goto trynsec3;
|
||||
}
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
- return (false);
|
||||
+ return ISC_R_NOTFOUND;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +312,7 @@ isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
|
||||
dns_rdata_reset(&rdata);
|
||||
}
|
||||
dns_rdataset_disassociate(&set);
|
||||
- return (found);
|
||||
+ return found ? ISC_R_SUCCESS : ISC_R_NOTFOUND;
|
||||
|
||||
trynsec3:
|
||||
/*
|
||||
@@ -335,6 +348,18 @@ trynsec3:
|
||||
if (nsec3.hash != 1) {
|
||||
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);
|
||||
@@ -346,7 +371,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;
|
||||
@@ -362,12 +387,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;
|
||||
}
|
||||
|
||||
/*%
|
||||
@@ -583,8 +608,9 @@ fetch_callback_ds(isc_task_t *task, isc_event_t *event) {
|
||||
} else if (eresult == DNS_R_SERVFAIL) {
|
||||
goto unexpected;
|
||||
} else if (eresult != DNS_R_CNAME &&
|
||||
- isdelegation(devent->foundname, &val->frdataset,
|
||||
- eresult))
|
||||
+ isdelegation(val, devent->foundname, &val->frdataset,
|
||||
+ eresult,
|
||||
+ "fetch_callback_ds") == ISC_R_SUCCESS)
|
||||
{
|
||||
/*
|
||||
* Failed to find a DS while trying to prove
|
||||
@@ -744,10 +770,13 @@ validator_callback_ds(isc_task_t *task, isc_event_t *event) {
|
||||
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",
|
||||
"no DS and this is a delegation");
|
||||
@@ -1485,6 +1514,13 @@ 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->event->rdataset->trust)) {
|
||||
+ /*
|
||||
+ * This RRset was already verified before.
|
||||
+ */
|
||||
+ return ISC_R_SUCCESS;
|
||||
+ }
|
||||
+
|
||||
val->attributes |= VALATTR_TRIEDVERIFY;
|
||||
wild = dns_fixedname_initname(&fixed);
|
||||
again:
|
||||
@@ -2425,6 +2461,17 @@ 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->currentset = rdataset;
|
||||
result = create_validator(val, name, rdataset->type, rdataset,
|
||||
sigrdataset, validator_callback_nsec,
|
||||
@@ -2535,11 +2582,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;
|
||||
@@ -2566,7 +2611,7 @@ validate_nx(dns_validator_t *val, bool resume) {
|
||||
isc_result_t result;
|
||||
|
||||
if (resume) {
|
||||
- validator_log(val, ISC_LOG_DEBUG(3), "resuming validate_nx");
|
||||
+ validator_log(val, ISC_LOG_DEBUG(3), "%s: resuming validate_nx", __func__);
|
||||
}
|
||||
|
||||
if (val->event->message == NULL) {
|
||||
@@ -2588,7 +2633,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 (3)", NULL);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
@@ -2624,7 +2669,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)", NULL);
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
@@ -2841,8 +2886,10 @@ seek_ds(dns_validator_t *val, isc_result_t *resp) {
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
|
||||
- if (isdelegation(tname, &val->frdataset, result)) {
|
||||
- *resp = markanswer(val, "proveunsecure (4)",
|
||||
+ result = isdelegation(val, tname, &val->frdataset, result,
|
||||
+ "seek_ds");
|
||||
+ if (result == ISC_R_SUCCESS) {
|
||||
+ *resp = markanswer(val, "seek_ds (3)",
|
||||
"this is a delegation");
|
||||
return (ISC_R_COMPLETE);
|
||||
}
|
||||
--
|
||||
2.53.0
|
||||
|
||||
144
SOURCES/bind-9.20-robust-key-rollovers-tests.patch
Normal file
144
SOURCES/bind-9.20-robust-key-rollovers-tests.patch
Normal file
@ -0,0 +1,144 @@
|
||||
diff --git a/bin/tests/system/nsec3/ns3/named.conf.in b/bin/tests/system/nsec3/ns3/named.conf.in
|
||||
index 022e9421bc..2bee4f898a 100644
|
||||
--- a/bin/tests/system/nsec3/ns3/named.conf.in
|
||||
+++ b/bin/tests/system/nsec3/ns3/named.conf.in
|
||||
@@ -68,7 +68,7 @@ zone "nsec-to-nsec3.kasp" {
|
||||
|
||||
/*
|
||||
* This zone starts with NSEC, but will be reconfigured to use NSEC3.
|
||||
- * This should work despite the incompatible RSAHSHA1 algorithm,
|
||||
+ * This should work despite the incompatible RSASHA1 algorithm,
|
||||
* because the DS is still in hidden state.
|
||||
*/
|
||||
zone "rsasha1-to-nsec3.kasp" {
|
||||
@@ -92,7 +92,7 @@ zone "rsasha1-to-nsec3-wait.kasp" {
|
||||
|
||||
/*
|
||||
* This zone starts with NSEC3, but will be reconfigured to use NSEC with an
|
||||
- * NSEC only algorithm. This should work despite the incompatible RSAHSHA1
|
||||
+ * NSEC only algorithm. This should work despite the incompatible RSASHA1
|
||||
* algorithm, because the DS is still in hidden state.
|
||||
*/
|
||||
zone "nsec3-to-rsasha1.kasp" {
|
||||
diff --git a/bin/tests/system/nsec3/ns3/setup.sh b/bin/tests/system/nsec3/ns3/setup.sh
|
||||
index 5ddcfc01b0..3cd7210192 100644
|
||||
--- a/bin/tests/system/nsec3/ns3/setup.sh
|
||||
+++ b/bin/tests/system/nsec3/ns3/setup.sh
|
||||
@@ -35,24 +35,23 @@ if (
|
||||
cd ..
|
||||
$SHELL ../testcrypto.sh -q RSASHA1
|
||||
); then
|
||||
- for zn in rsasha1-to-nsec3 rsasha1-to-nsec3-wait nsec3-to-rsasha1 \
|
||||
- nsec3-to-rsasha1-ds; do
|
||||
- setup "${zn}.kasp"
|
||||
- done
|
||||
-
|
||||
longago="now-1y"
|
||||
- keytimes="-P ${longago} -A ${longago}"
|
||||
+ keytimes="-P ${longago} -A ${longago} -P sync ${longago}"
|
||||
O="omnipresent"
|
||||
|
||||
- zone="rsasha1-to-nsec3-wait.kasp"
|
||||
- CSK=$($KEYGEN -k "rsasha1" -l named.conf $keytimes $zone 2>keygen.out.$zone)
|
||||
- echo_i "Created key file $CSK"
|
||||
- $SETTIME -s -g $O -k $O $longago -r $O $longago -z $O $longago -d $O $longago "$CSK" >settime.out.$zone 2>&1
|
||||
+ for zn in nsec3-to-rsasha1 nsec3-to-rsasha1-ds; do
|
||||
+ setup "${zn}.kasp"
|
||||
+ CSK=$($KEYGEN -k "default" -l named.conf $keytimes $zone 2>keygen.out.$zone)
|
||||
+ $SETTIME -s -g $O -k $O $longago -r $O $longago -z $O $longago -d $O $longago "$CSK" >settime.out.$zone 2>&1
|
||||
+ cat $CSK.key >>$zonefile
|
||||
+ done
|
||||
|
||||
- zone="nsec3-to-rsasha1-ds.kasp"
|
||||
- CSK=$($KEYGEN -k "default" -l named.conf $keytimes $zone 2>keygen.out.$zone)
|
||||
- echo_i "Created key file $CSK"
|
||||
- $SETTIME -s -g $O -k $O $longago -r $O $longago -z $O $longago -d $O $longago "$CSK" >settime.out.$zone 2>&1
|
||||
+ for zn in rsasha1-to-nsec3 rsasha1-to-nsec3-wait; do
|
||||
+ setup "${zn}.kasp"
|
||||
+ CSK=$($KEYGEN -k "rsasha1" -l named.conf $keytimes $zone 2>keygen.out.$zone)
|
||||
+ $SETTIME -s -g $O -k $O $longago -r $O $longago -z $O $longago -d $O $longago "$CSK" >settime.out.$zone 2>&1
|
||||
+ cat $CSK.key >>$zonefile
|
||||
+ done
|
||||
else
|
||||
echo_i "skip: skip rsasha1 zones - signing with RSASHA1 not supported"
|
||||
fi
|
||||
diff --git a/bin/tests/system/nsec3/tests.sh b/bin/tests/system/nsec3/tests.sh
|
||||
index 6cb28583c6..b8e9152cf9 100644
|
||||
--- a/bin/tests/system/nsec3/tests.sh
|
||||
+++ b/bin/tests/system/nsec3/tests.sh
|
||||
@@ -80,10 +80,10 @@ set_key_rsasha1_values() {
|
||||
set_zonesigning $1 "yes"
|
||||
|
||||
set_keystate $1 "GOAL" "omnipresent"
|
||||
- set_keystate $1 "STATE_DNSKEY" "rumoured"
|
||||
- set_keystate $1 "STATE_KRRSIG" "rumoured"
|
||||
- set_keystate $1 "STATE_ZRRSIG" "rumoured"
|
||||
- set_keystate $1 "STATE_DS" "hidden"
|
||||
+ set_keystate $1 "STATE_DNSKEY" "omnipresent"
|
||||
+ set_keystate $1 "STATE_KRRSIG" "omnipresent"
|
||||
+ set_keystate $1 "STATE_ZRRSIG" "omnipresent"
|
||||
+ set_keystate $1 "STATE_DS" "omnipresent"
|
||||
}
|
||||
|
||||
# Update the key states.
|
||||
@@ -251,21 +251,21 @@ if ($SHELL ../testcrypto.sh -q RSASHA1); then
|
||||
set_zone_policy "rsasha1-to-nsec3-wait.kasp" "rsasha1" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_rsasha1_values "KEY1"
|
||||
- set_key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec
|
||||
|
||||
# Zone: nsec3-to-rsasha1.kasp.
|
||||
set_zone_policy "nsec3-to-rsasha1.kasp" "nsec3" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
- set_key_rsasha1_values "KEY1"
|
||||
+ set_key_default_values "KEY1"
|
||||
+ set_key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
|
||||
# Zone: nsec3-to-rsasha1-ds.kasp.
|
||||
set_zone_policy "nsec3-to-rsasha1-ds.kasp" "nsec3" 1 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
- set_key_rsasha1_values "KEY1"
|
||||
+ set_key_default_values "KEY1"
|
||||
set_key_states "KEY1" "omnipresent" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
echo_i "initial check zone ${ZONE}"
|
||||
check_nsec3
|
||||
@@ -385,12 +385,10 @@ if ($SHELL ../testcrypto.sh -q RSASHA1); then
|
||||
set_zone_policy "rsasha1-to-nsec3.kasp" "nsec3" 2 3600
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_rsasha1_values "KEY1"
|
||||
- set_key_states "KEY1" "hidden" "unretentive" "unretentive" "unretentive" "hidden"
|
||||
- set_keysigning "KEY1" "no"
|
||||
- set_zonesigning "KEY1" "no"
|
||||
+ set_key_states "KEY1" "hidden" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
set_key_default_values "KEY2"
|
||||
echo_i "check zone ${ZONE} after reconfig"
|
||||
- check_nsec3
|
||||
+ check_nsec
|
||||
|
||||
# Zone: rsasha1-to-nsec3-wait.kasp.
|
||||
set_zone_policy "rsasha1-to-nsec3-wait.kasp" "nsec3" 2 3600
|
||||
@@ -406,10 +404,9 @@ if ($SHELL ../testcrypto.sh -q RSASHA1); then
|
||||
set_nsec3param "1" "0" "0"
|
||||
set_server "ns3" "10.53.0.3"
|
||||
set_key_default_values "KEY1"
|
||||
- set_key_states "KEY1" "hidden" "unretentive" "unretentive" "unretentive" "hidden"
|
||||
- set_keysigning "KEY1" "no"
|
||||
- set_zonesigning "KEY1" "no"
|
||||
+ set_key_states "KEY1" "hidden" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
set_key_rsasha1_values "KEY2"
|
||||
+ set_key_states "KEY2" "omnipresent" "rumoured" "rumoured" "rumoured" "hidden"
|
||||
echo_i "check zone ${ZONE} after reconfig"
|
||||
check_nsec
|
||||
|
||||
@@ -420,6 +417,7 @@ if ($SHELL ../testcrypto.sh -q RSASHA1); then
|
||||
set_key_default_values "KEY1"
|
||||
set_key_states "KEY1" "hidden" "omnipresent" "omnipresent" "omnipresent" "omnipresent"
|
||||
set_key_rsasha1_values "KEY2"
|
||||
+ set_key_states "KEY2" "omnipresent" "rumoured" "rumoured" "rumoured" "hidden"
|
||||
echo_i "check zone ${ZONE} after reconfig"
|
||||
check_nsec
|
||||
|
||||
97
SOURCES/bind-9.20-robust-key-rollovers.patch
Normal file
97
SOURCES/bind-9.20-robust-key-rollovers.patch
Normal file
@ -0,0 +1,97 @@
|
||||
diff --git a/lib/dns/keymgr.c b/lib/dns/keymgr.c
|
||||
index 4fbebbcb6d..789f68a146 100644
|
||||
--- a/lib/dns/keymgr.c
|
||||
+++ b/lib/dns/keymgr.c
|
||||
@@ -1224,19 +1224,18 @@ static bool
|
||||
keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
|
||||
int type, dst_key_state_t next_state,
|
||||
bool secure_to_insecure) {
|
||||
+ bool rule1a, rule1b, rule2a, rule2b, rule3a, rule3b;
|
||||
+ rule1a = keymgr_have_ds(keyring, key, type, NA, secure_to_insecure);
|
||||
+ rule1b = keymgr_have_ds(keyring, key, type, next_state, secure_to_insecure);
|
||||
+ rule2a = keymgr_have_dnskey(keyring, key, type, NA);
|
||||
+ rule2b = keymgr_have_dnskey(keyring, key, type, next_state);
|
||||
+ rule3a = keymgr_have_rrsig(keyring, key, type, NA);
|
||||
+ rule3b = keymgr_have_rrsig(keyring, key, type, next_state);
|
||||
+
|
||||
/* Debug logging. */
|
||||
if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) {
|
||||
- bool rule1a, rule1b, rule2a, rule2b, rule3a, rule3b;
|
||||
char keystr[DST_KEY_FORMATSIZE];
|
||||
dst_key_format(key->key, keystr, sizeof(keystr));
|
||||
- rule1a = keymgr_have_ds(keyring, key, type, NA,
|
||||
- secure_to_insecure);
|
||||
- rule1b = keymgr_have_ds(keyring, key, type, next_state,
|
||||
- secure_to_insecure);
|
||||
- rule2a = keymgr_have_dnskey(keyring, key, type, NA);
|
||||
- rule2b = keymgr_have_dnskey(keyring, key, type, next_state);
|
||||
- rule3a = keymgr_have_rrsig(keyring, key, type, NA);
|
||||
- rule3b = keymgr_have_rrsig(keyring, key, type, next_state);
|
||||
isc_log_write(
|
||||
dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC,
|
||||
ISC_LOG_DEBUG(1),
|
||||
@@ -1249,30 +1248,40 @@ keymgr_transition_allowed(dns_dnsseckeylist_t *keyring, dns_dnsseckey_t *key,
|
||||
rule3a ? "true" : "false", rule3b ? "true" : "false");
|
||||
}
|
||||
|
||||
- return (
|
||||
- /*
|
||||
- * Rule 1: There must be a DS at all times.
|
||||
- * First check the current situation: if the rule check fails,
|
||||
- * we allow the transition to attempt to move us out of the
|
||||
- * invalid state. If the rule check passes, also check if
|
||||
- * the next state is also still a valid situation.
|
||||
- */
|
||||
- (!keymgr_have_ds(keyring, key, type, NA, secure_to_insecure) ||
|
||||
- keymgr_have_ds(keyring, key, type, next_state,
|
||||
- secure_to_insecure)) &&
|
||||
- /*
|
||||
- * Rule 2: There must be a DNSKEY at all times. Again, first
|
||||
- * check the current situation, then assess the next state.
|
||||
- */
|
||||
- (!keymgr_have_dnskey(keyring, key, type, NA) ||
|
||||
- keymgr_have_dnskey(keyring, key, type, next_state)) &&
|
||||
- /*
|
||||
- * Rule 3: There must be RRSIG records at all times. Again,
|
||||
- * first check the current situation, then assess the next
|
||||
- * state.
|
||||
- */
|
||||
- (!keymgr_have_rrsig(keyring, key, type, NA) ||
|
||||
- keymgr_have_rrsig(keyring, key, type, next_state)));
|
||||
+ /*
|
||||
+ * Rule checking:
|
||||
+ * First check the current situation: if the rule check fails,
|
||||
+ * we allow the transition to attempt to move us out of the
|
||||
+ * invalid state. If the rule check passes, also check if
|
||||
+ * the next state is also still a valid situation.
|
||||
+ */
|
||||
+ char keystr2[DST_KEY_FORMATSIZE];
|
||||
+ dst_key_format(key->key, keystr2, sizeof(keystr2));
|
||||
+
|
||||
+ /*
|
||||
+ * Rule 1: There must be a DS at all times.
|
||||
+ */
|
||||
+ if (!rule1a && !rule1b && next_state == UNRETENTIVE) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Rule 2: There must be a DNSKEY at all times. Again, first
|
||||
+ * check the current situation, then assess the next state.
|
||||
+ */
|
||||
+ if (!rule2a && !rule2b && next_state == UNRETENTIVE) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Rule 3: There must be RRSIG records at all times. Again,
|
||||
+ * first check the current situation, then assess the next
|
||||
+ * state.
|
||||
+ */
|
||||
+ if (!rule3a && !rule3b && next_state == UNRETENTIVE) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return (!rule1a || rule1b) && (!rule2a || rule2b) &&
|
||||
+ (!rule3a || rule3b);
|
||||
}
|
||||
|
||||
/*
|
||||
94
SOURCES/bind-9.20-stale-cname-tests.patch
Normal file
94
SOURCES/bind-9.20-stale-cname-tests.patch
Normal file
@ -0,0 +1,94 @@
|
||||
diff --git a/bin/tests/system/serve-stale/ns1/stale.test.db b/bin/tests/system/serve-stale/ns1/stale.test.db
|
||||
index d389e7c6a6..128fb25a10 100644
|
||||
--- a/bin/tests/system/serve-stale/ns1/stale.test.db
|
||||
+++ b/bin/tests/system/serve-stale/ns1/stale.test.db
|
||||
@@ -17,3 +17,11 @@ cname1.stale.test. 1 CNAME a1.stale.test.
|
||||
a1.stale.test. 1 A 192.0.2.1
|
||||
cname2.stale.test. 1 CNAME a2.stale.test.
|
||||
a2.stale.test. 300 A 192.0.2.2
|
||||
+
|
||||
+cname-a1 1 CNAME cname-a2
|
||||
+cname-a2 300 CNAME cname-a3
|
||||
+cname-a3 300 A 192.0.2.1
|
||||
+
|
||||
+cname-b1 300 CNAME cname-b2
|
||||
+cname-b2 1 CNAME cname-b3
|
||||
+cname-b3 1 A 192.0.2.2
|
||||
diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh
|
||||
index 96cd26505f..a30896b6a4 100755
|
||||
--- a/bin/tests/system/serve-stale/tests.sh
|
||||
+++ b/bin/tests/system/serve-stale/tests.sh
|
||||
@@ -2256,6 +2256,73 @@ if [ $ret != 0 ]; then
|
||||
fi
|
||||
status=$((status + ret))
|
||||
|
||||
+# New CNAME scenario (GL #5243)
|
||||
+n=$((n + 1))
|
||||
+echo_i "prime cache cname-a1.stale.test A (stale-answer-client-timeout 0) ($n)"
|
||||
+ret=0
|
||||
+$DIG -p ${PORT} @10.53.0.3 cname-a1.stale.test A >dig.out.test$n || ret=1
|
||||
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "ANSWER: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a1\.stale\.test\..*1.*IN.*CNAME.*cname-a2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a2\.stale\.test\..*300.*IN.*CNAME.*cname-a3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a3\.stale\.test\..*300.*IN.*A.*192\.0\.2\.1" dig.out.test$n >/dev/null || ret=1
|
||||
+if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
+status=$((status + ret))
|
||||
+
|
||||
+n=$((n + 1))
|
||||
+echo_i "prime cache cname-b1.stale.test A (stale-answer-client-timeout 0) ($n)"
|
||||
+ret=0
|
||||
+$DIG -p ${PORT} @10.53.0.3 cname-b1.stale.test A >dig.out.test$n || ret=1
|
||||
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "ANSWER: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b1\.stale\.test\..*300.*IN.*CNAME.*cname-b2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b2\.stale\.test\..*1.*IN.*CNAME.*cname-b3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b3\.stale\.test\..*1.*IN.*A.*192\.0\.2\.2" dig.out.test$n >/dev/null || ret=1
|
||||
+if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
+status=$((status + ret))
|
||||
+
|
||||
+# Allow RRset to become stale.
|
||||
+sleep 1
|
||||
+
|
||||
+n=$((n + 1))
|
||||
+ret=0
|
||||
+echo_i "check stale cname-a1.stale.test A comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
+nextpart ns3/named.run >/dev/null
|
||||
+$DIG -p ${PORT} @10.53.0.3 cname-a1.stale.test A >dig.out.test$n || ret=1
|
||||
+wait_for_log 5 "cname-a1.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+# Other records in chain are still good, so do not attempt a refresh
|
||||
+grep "cname-a2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run && ret=1
|
||||
+grep "cname-a3.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run && ret=1
|
||||
+# Check answer
|
||||
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "ANSWER: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a1\.stale\.test\..*3.*IN.*CNAME.*cname-a2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a2\.stale\.test\..*29[0-9].*IN.*CNAME.*cname-a3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-a3\.stale\.test\..*29[0-9].*IN.*A.*192\.0\.2\.1" dig.out.test$n >/dev/null || ret=1
|
||||
+if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
+status=$((status + ret))
|
||||
+
|
||||
+n=$((n + 1))
|
||||
+ret=0
|
||||
+echo_i "check stale cname-b1.stale.test A comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
+nextpart ns3/named.run >/dev/null
|
||||
+$DIG -p ${PORT} @10.53.0.3 cname-b1.stale.test A >dig.out.test$n || ret=1
|
||||
+wait_for_log 5 "cname-b2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+# The next one in the chain (cname-b3.stale.test) is likely not logged because
|
||||
+# there is already a refresh in progress. And the first record in the chain is
|
||||
+# still good, so do not attempt a refresh.
|
||||
+grep "cname-b1.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run && ret=1
|
||||
+# Check answer
|
||||
+grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
+grep "ANSWER: 3," dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b1\.stale\.test\..*29[0-9].*IN.*CNAME.*cname-b2\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b2\.stale\.test\..*3.*IN.*CNAME.*cname-b3\.stale\.test\." dig.out.test$n >/dev/null || ret=1
|
||||
+grep "cname-b3\.stale\.test\..*3.*IN.*A.*192\.0\.2\.2" dig.out.test$n >/dev/null || ret=1
|
||||
+if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
+status=$((status + ret))
|
||||
+
|
||||
####################################################################
|
||||
# Test for stale-answer-client-timeout 0 and stale-refresh-time 4. #
|
||||
####################################################################
|
||||
711
SOURCES/bind-9.20-stale-cname.patch
Normal file
711
SOURCES/bind-9.20-stale-cname.patch
Normal file
@ -0,0 +1,711 @@
|
||||
diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh
|
||||
index c001e7a071..96cd26505f 100755
|
||||
--- a/bin/tests/system/serve-stale/tests.sh
|
||||
+++ b/bin/tests/system/serve-stale/tests.sh
|
||||
@@ -2053,7 +2053,7 @@ ret=0
|
||||
echo_i "check stale nodata.example TXT comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 nodata.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "nodata.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "nodata.example TXT stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2066,7 +2066,7 @@ ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2196,7 +2196,7 @@ ret=0
|
||||
echo_i "check stale cname1.stale.test A comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 cname1.stale.test A >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "cname1.stale.test stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "cname1.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 2," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2235,7 +2235,7 @@ ret=0
|
||||
echo_i "check stale cname2.stale.test A comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 cname2.stale.test A >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "cname2.stale.test stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "cname2.stale.test A stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 2," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2312,7 +2312,7 @@ ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2356,7 +2356,7 @@ ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run >/dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
@@ -2368,7 +2368,7 @@ status=$((status + ret))
|
||||
n=$((n + 1))
|
||||
ret=0
|
||||
echo_i "wait until resolver query times out, activating stale-refresh-time"
|
||||
-wait_for_log 15 "data.example resolver failure, stale answer used" ns3/named.run || ret=1
|
||||
+wait_for_log 15 "data.example/TXT stale refresh failed: timed out" ns3/named.run || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
@@ -2419,7 +2419,7 @@ n=$((n + 1))
|
||||
ret=0
|
||||
echo_i "check stale data.example TXT comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT >dig.out.test$n || ret=1
|
||||
-wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
+wait_for_log 5 "data.example TXT stale answer used, an attempt to refresh the RRset" ns3/named.run || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h
|
||||
index f63591c5c7..f3f257ad7e 100644
|
||||
--- a/lib/dns/include/dns/rdataset.h
|
||||
+++ b/lib/dns/include/dns/rdataset.h
|
||||
@@ -199,7 +199,9 @@ struct dns_rdataset {
|
||||
#define DNS_RDATASETATTR_STALE 0x01000000
|
||||
#define DNS_RDATASETATTR_ANCIENT 0x02000000
|
||||
#define DNS_RDATASETATTR_STALE_WINDOW 0x04000000
|
||||
-#define DNS_RDATASETATTR_STALE_ADDED 0x08000000
|
||||
+#define DNS_RDATASETATTR_KEEPCASE 0x10000000
|
||||
+#define DNS_RDATASETATTR_STATICSTUB 0x20000000
|
||||
+
|
||||
|
||||
/*%
|
||||
* _OMITDNSSEC:
|
||||
diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h
|
||||
index ea2d83e079..db17f01d79 100644
|
||||
--- a/lib/ns/include/ns/client.h
|
||||
+++ b/lib/ns/include/ns/client.h
|
||||
@@ -184,6 +184,7 @@ struct ns_client {
|
||||
(query, update, notify) */
|
||||
isc_nmhandle_t *fetchhandle; /* Waiting for recursive fetch */
|
||||
isc_nmhandle_t *prefetchhandle; /* Waiting for prefetch / rpzfetch */
|
||||
+ isc_nmhandle_t *stalerefreshhandle;
|
||||
isc_nmhandle_t *updatehandle; /* Waiting for update callback */
|
||||
unsigned char *tcpbuf;
|
||||
size_t tcpbuf_size;
|
||||
diff --git a/lib/ns/include/ns/query.h b/lib/ns/include/ns/query.h
|
||||
index 37e55671c2..ce97643297 100644
|
||||
--- a/lib/ns/include/ns/query.h
|
||||
+++ b/lib/ns/include/ns/query.h
|
||||
@@ -68,6 +68,7 @@ struct ns_query {
|
||||
isc_mutex_t fetchlock;
|
||||
dns_fetch_t *fetch;
|
||||
dns_fetch_t *prefetch;
|
||||
+ dns_fetch_t *stalerefresh;
|
||||
ns_hookasync_t *hookactx;
|
||||
dns_rpz_st_t *rpz_st;
|
||||
isc_bufferlist_t namebufs;
|
||||
@@ -119,7 +120,6 @@ struct ns_query {
|
||||
#define NS_QUERYATTR_RRL_CHECKED 0x010000
|
||||
#define NS_QUERYATTR_REDIRECT 0x020000
|
||||
#define NS_QUERYATTR_ANSWERED 0x040000
|
||||
-#define NS_QUERYATTR_STALEOK 0x080000
|
||||
#define NS_QUERYATTR_STALEPENDING 0x100000
|
||||
|
||||
typedef struct query_ctx query_ctx_t;
|
||||
@@ -148,7 +148,6 @@ struct query_ctx {
|
||||
bool authoritative; /* authoritative query? */
|
||||
bool want_restart; /* CNAME chain or other
|
||||
* restart needed */
|
||||
- bool refresh_rrset; /* stale RRset refresh needed */
|
||||
bool need_wildcardproof; /* wildcard proof needed */
|
||||
bool nxrewrite; /* negative answer from RPZ */
|
||||
bool findcoveringnsec; /* lookup covering NSEC */
|
||||
diff --git a/lib/ns/query.c b/lib/ns/query.c
|
||||
index 5549e202df..274c044172 100644
|
||||
--- a/lib/ns/query.c
|
||||
+++ b/lib/ns/query.c
|
||||
@@ -139,9 +139,6 @@
|
||||
#define QUERY_STALEPENDING(q) \
|
||||
(((q)->attributes & NS_QUERYATTR_STALEPENDING) != 0)
|
||||
|
||||
-/*% Does the query allow stale data in the response? */
|
||||
-#define QUERY_STALEOK(q) (((q)->attributes & NS_QUERYATTR_STALEOK) != 0)
|
||||
-
|
||||
/*% Does the query wants to check for stale RRset due to a timeout? */
|
||||
#define QUERY_STALETIMEOUT(q) (((q)->dboptions & DNS_DBFIND_STALETIMEOUT) != 0)
|
||||
|
||||
@@ -248,6 +245,15 @@ query_addanswer(query_ctx_t *qctx);
|
||||
static isc_result_t
|
||||
query_prepare_delegation_response(query_ctx_t *qctx);
|
||||
|
||||
+static isc_result_t
|
||||
+qctx_prepare_buffers(query_ctx_t *qctx, isc_buffer_t *buffer);
|
||||
+
|
||||
+static void
|
||||
+qctx_freedata(query_ctx_t *qctx);
|
||||
+
|
||||
+static void
|
||||
+qctx_destroy(query_ctx_t *qctx);
|
||||
+
|
||||
/*
|
||||
* Return the hooktable in use with 'qctx', or if there isn't one
|
||||
* set, return the default hooktable.
|
||||
@@ -503,9 +509,6 @@ query_addwildcardproof(query_ctx_t *qctx, bool ispositive, bool nodata);
|
||||
static void
|
||||
query_addauth(query_ctx_t *qctx);
|
||||
|
||||
-static void
|
||||
-query_clear_stale(ns_client_t *client);
|
||||
-
|
||||
/*
|
||||
* Increment query statistics counters.
|
||||
*/
|
||||
@@ -811,6 +814,7 @@ ns_query_init(ns_client_t *client) {
|
||||
|
||||
client->query.fetch = NULL;
|
||||
client->query.prefetch = NULL;
|
||||
+ client->query.stalerefresh = NULL;
|
||||
client->query.authdb = NULL;
|
||||
client->query.authzone = NULL;
|
||||
client->query.authdbset = false;
|
||||
@@ -2241,10 +2245,6 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
|
||||
if ((rdataset->attributes & DNS_RDATASETATTR_REQUIRED) != 0) {
|
||||
mrdataset->attributes |= DNS_RDATASETATTR_REQUIRED;
|
||||
}
|
||||
- if ((rdataset->attributes & DNS_RDATASETATTR_STALE_ADDED) != 0)
|
||||
- {
|
||||
- mrdataset->attributes |= DNS_RDATASETATTR_STALE_ADDED;
|
||||
- }
|
||||
return;
|
||||
} else if (result == DNS_R_NXDOMAIN) {
|
||||
/*
|
||||
@@ -2530,6 +2530,88 @@ free_devent(ns_client_t *client, isc_event_t **eventp,
|
||||
isc_event_free(eventp);
|
||||
}
|
||||
|
||||
+static void
|
||||
+stale_refresh_aftermath(ns_client_t *client, isc_result_t result) {
|
||||
+ dns_db_t *db = NULL;
|
||||
+ unsigned int dboptions;
|
||||
+ isc_buffer_t buffer;
|
||||
+ query_ctx_t qctx;
|
||||
+ dns_clientinfomethods_t cm;
|
||||
+ dns_clientinfo_t ci;
|
||||
+ char namebuf[DNS_NAME_FORMATSIZE];
|
||||
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
+
|
||||
+ /*
|
||||
+ * If refreshing a stale RRset failed, we need to set the
|
||||
+ * stale-refresh-time window, so that on future requests for this
|
||||
+ * RRset the stale entry may be used immediately.
|
||||
+ */
|
||||
+ switch (result) {
|
||||
+ case ISC_R_SUCCESS:
|
||||
+ case DNS_R_GLUE:
|
||||
+ case DNS_R_ZONECUT:
|
||||
+ case ISC_R_NOTFOUND:
|
||||
+ case DNS_R_DELEGATION:
|
||||
+ case DNS_R_EMPTYNAME:
|
||||
+ case DNS_R_NXRRSET:
|
||||
+ case DNS_R_EMPTYWILD:
|
||||
+ case DNS_R_NXDOMAIN:
|
||||
+ case DNS_R_COVERINGNSEC:
|
||||
+ case DNS_R_NCACHENXDOMAIN:
|
||||
+ case DNS_R_NCACHENXRRSET:
|
||||
+ case DNS_R_CNAME:
|
||||
+ case DNS_R_DNAME:
|
||||
+ break;
|
||||
+ default:
|
||||
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
|
||||
+ dns_rdatatype_format(client->query.qtype, typebuf,
|
||||
+ sizeof(typebuf));
|
||||
+ ns_client_log(client, NS_LOGCATEGORY_SERVE_STALE,
|
||||
+ NS_LOGMODULE_QUERY, ISC_LOG_NOTICE,
|
||||
+ "%s/%s stale refresh failed: timed out", namebuf,
|
||||
+ typebuf);
|
||||
+
|
||||
+ /*
|
||||
+ * Set up a short lived query context, solely to set the
|
||||
+ * last refresh failure time on the RRset in the cache
|
||||
+ * database, starting the stale-refresh-time window for it.
|
||||
+ * This is a condensed form of query_lookup().
|
||||
+ */
|
||||
+ isc_stdtime_get(&client->now);
|
||||
+ client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
|
||||
+ qctx_init(client, NULL, 0, &qctx);
|
||||
+
|
||||
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
+ dns_clientinfo_init(&ci, qctx.client, NULL);
|
||||
+ if (HAVEECS(qctx.client)) {
|
||||
+ dns_clientinfo_setecs(&ci, &qctx.client->ecs);
|
||||
+ }
|
||||
+
|
||||
+ result = qctx_prepare_buffers(&qctx, &buffer);
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ dboptions = qctx.client->query.dboptions;
|
||||
+ dboptions |= DNS_DBFIND_STALEOK;
|
||||
+ dboptions |= DNS_DBFIND_STALESTART;
|
||||
+
|
||||
+ dns_db_attach(qctx.client->view->cachedb, &db);
|
||||
+ (void)dns_db_findext(db, qctx.client->query.qname, NULL,
|
||||
+ qctx.client->query.qtype, dboptions,
|
||||
+ qctx.client->now, &qctx.node, qctx.fname,
|
||||
+ &cm, &ci, qctx.rdataset, qctx.sigrdataset);
|
||||
+ if (qctx.node != NULL) {
|
||||
+ dns_db_detachnode(db, &qctx.node);
|
||||
+ }
|
||||
+ dns_db_detach(&db);
|
||||
+
|
||||
+ cleanup:
|
||||
+ qctx_freedata(&qctx);
|
||||
+ qctx_destroy(&qctx);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void
|
||||
prefetch_done(isc_task_t *task, isc_event_t *event) {
|
||||
dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
|
||||
@@ -2564,6 +2646,133 @@ prefetch_done(isc_task_t *task, isc_event_t *event) {
|
||||
isc_nmhandle_detach(&client->prefetchhandle);
|
||||
}
|
||||
|
||||
+static void
|
||||
+refresh_done(isc_task_t *task, isc_event_t *event) {
|
||||
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
|
||||
+ ns_client_t *client;
|
||||
+
|
||||
+ UNUSED(task);
|
||||
+
|
||||
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
|
||||
+ client = devent->ev_arg;
|
||||
+ REQUIRE(NS_CLIENT_VALID(client));
|
||||
+ REQUIRE(task == client->task);
|
||||
+
|
||||
+ CTRACE(ISC_LOG_DEBUG(3), "refresh_done");
|
||||
+
|
||||
+ LOCK(&client->query.fetchlock);
|
||||
+ if (client->query.stalerefresh != NULL) {
|
||||
+ INSIST(devent->fetch == client->query.stalerefresh);
|
||||
+ client->query.stalerefresh = NULL;
|
||||
+ }
|
||||
+ UNLOCK(&client->query.fetchlock);
|
||||
+
|
||||
+ stale_refresh_aftermath(client, devent->result);
|
||||
+
|
||||
+ if (client->recursionquota != NULL) {
|
||||
+ isc_quota_detach(&client->recursionquota);
|
||||
+ ns_stats_decrement(client->sctx->nsstats,
|
||||
+ ns_statscounter_recursclients);
|
||||
+ }
|
||||
+
|
||||
+ free_devent(client, &event, &devent);
|
||||
+ isc_nmhandle_detach(&client->stalerefreshhandle);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+query_stale_refresh(ns_client_t *client, dns_name_t *qname,
|
||||
+ dns_rdataset_t *rdataset) {
|
||||
+ isc_result_t result;
|
||||
+ isc_sockaddr_t *peeraddr;
|
||||
+ dns_rdataset_t *tmprdataset;
|
||||
+ unsigned int options;
|
||||
+
|
||||
+ CTRACE(ISC_LOG_DEBUG(3), "query_stale_refresh");
|
||||
+
|
||||
+ bool stale_refresh_window = false;
|
||||
+ bool stale_rrset = true;
|
||||
+
|
||||
+ if (rdataset != NULL) {
|
||||
+ stale_refresh_window =
|
||||
+ (STALE_WINDOW(rdataset) &&
|
||||
+ (client->query.dboptions & DNS_DBFIND_STALEENABLED) != 0);
|
||||
+ stale_rrset = STALE(rdataset);
|
||||
+ }
|
||||
+
|
||||
+ if (client->query.stalerefresh != NULL ||
|
||||
+ (client->query.dboptions & DNS_DBFIND_STALETIMEOUT) == 0 ||
|
||||
+ !stale_rrset || stale_refresh_window)
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ char namebuf[DNS_NAME_FORMATSIZE];
|
||||
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
+ dns_name_format(qname, namebuf, sizeof(namebuf));
|
||||
+ dns_rdatatype_format(client->query.qtype, typebuf, sizeof(typebuf));
|
||||
+ isc_log_write(ns_lctx, NS_LOGCATEGORY_SERVE_STALE, NS_LOGMODULE_QUERY,
|
||||
+ ISC_LOG_INFO,
|
||||
+ "%s %s stale answer used, an attempt "
|
||||
+ "to refresh the RRset will still be "
|
||||
+ "made",
|
||||
+ namebuf, typebuf);
|
||||
+
|
||||
+ client->query.dboptions &= ~(DNS_DBFIND_STALETIMEOUT |
|
||||
+ DNS_DBFIND_STALEOK |
|
||||
+ DNS_DBFIND_STALEENABLED);
|
||||
+
|
||||
+ if (client->recursionquota == NULL) {
|
||||
+ result = isc_quota_attach(&client->sctx->recursionquota,
|
||||
+ &client->recursionquota);
|
||||
+ switch (result) {
|
||||
+ case ISC_R_SUCCESS:
|
||||
+ ns_stats_increment(client->sctx->nsstats,
|
||||
+ ns_statscounter_recursclients);
|
||||
+ break;
|
||||
+ case ISC_R_SOFTQUOTA:
|
||||
+ isc_quota_detach(&client->recursionquota);
|
||||
+ FALLTHROUGH;
|
||||
+ default:
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ tmprdataset = ns_client_newrdataset(client);
|
||||
+ if (tmprdataset == NULL) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!TCP(client)) {
|
||||
+ peeraddr = &client->peeraddr;
|
||||
+ } else {
|
||||
+ peeraddr = NULL;
|
||||
+ }
|
||||
+
|
||||
+ isc_nmhandle_attach(client->handle, &client->stalerefreshhandle);
|
||||
+ options = client->query.fetchoptions;
|
||||
+ result = dns_resolver_createfetch(
|
||||
+ client->view->resolver, qname, client->query.qtype, NULL, NULL, NULL,
|
||||
+ peeraddr, client->message->id, options, 0, NULL, client->task,
|
||||
+ refresh_done, client, tmprdataset, NULL,
|
||||
+ &client->query.stalerefresh);
|
||||
+ if (result != ISC_R_SUCCESS) {
|
||||
+ ns_client_putrdataset(client, &tmprdataset);
|
||||
+ isc_nmhandle_detach(&client->stalerefreshhandle);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+query_stale_refresh_ncache(ns_client_t *client) {
|
||||
+ dns_name_t *qname;
|
||||
+
|
||||
+ if (client->query.origqname != NULL) {
|
||||
+ qname = client->query.origqname;
|
||||
+ } else {
|
||||
+ qname = client->query.qname;
|
||||
+ }
|
||||
+ query_stale_refresh(client, qname, NULL);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
query_prefetch(ns_client_t *client, dns_name_t *qname,
|
||||
dns_rdataset_t *rdataset) {
|
||||
@@ -2579,6 +2788,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
|
||||
rdataset->ttl > client->view->prefetch_trigger ||
|
||||
(rdataset->attributes & DNS_RDATASETATTR_PREFETCH) == 0)
|
||||
{
|
||||
+ query_stale_refresh(client, qname, rdataset);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2623,6 +2833,7 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
|
||||
|
||||
dns_rdataset_clearprefetch(rdataset);
|
||||
ns_stats_increment(client->sctx->nsstats, ns_statscounter_prefetch);
|
||||
+ return;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -5801,55 +6012,6 @@ error:
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Setup a new query context for resolving a query.
|
||||
- *
|
||||
- * This function is only called if both these conditions are met:
|
||||
- * 1. BIND is configured with stale-answer-client-timeout 0.
|
||||
- * 2. A stale RRset is found in cache during initial query
|
||||
- * database lookup.
|
||||
- *
|
||||
- * We continue with this function for refreshing/resolving an RRset
|
||||
- * after answering a client with stale data.
|
||||
- */
|
||||
-static void
|
||||
-query_refresh_rrset(query_ctx_t *orig_qctx) {
|
||||
- isc_buffer_t buffer;
|
||||
- query_ctx_t qctx;
|
||||
-
|
||||
- REQUIRE(orig_qctx != NULL);
|
||||
- REQUIRE(orig_qctx->client != NULL);
|
||||
-
|
||||
- qctx_copy(orig_qctx, &qctx);
|
||||
- qctx.client->query.dboptions &= ~(DNS_DBFIND_STALETIMEOUT |
|
||||
- DNS_DBFIND_STALEOK |
|
||||
- DNS_DBFIND_STALEENABLED);
|
||||
- qctx.client->nodetach = false;
|
||||
-
|
||||
- /*
|
||||
- * We'll need some resources...
|
||||
- */
|
||||
- if (qctx_prepare_buffers(&qctx, &buffer) != ISC_R_SUCCESS) {
|
||||
- dns_db_detach(&qctx.db);
|
||||
- qctx_destroy(&qctx);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * Pretend we didn't find anything in cache.
|
||||
- */
|
||||
- (void)query_gotanswer(&qctx, ISC_R_NOTFOUND);
|
||||
-
|
||||
- if (qctx.fname != NULL) {
|
||||
- ns_client_releasename(qctx.client, &qctx.fname);
|
||||
- }
|
||||
- if (qctx.rdataset != NULL) {
|
||||
- ns_client_putrdataset(qctx.client, &qctx.rdataset);
|
||||
- }
|
||||
-
|
||||
- qctx_destroy(&qctx);
|
||||
-}
|
||||
-
|
||||
/*%
|
||||
* Depending on the db lookup result, we can respond to the
|
||||
* client this stale answer.
|
||||
@@ -5921,7 +6083,7 @@ query_lookup(query_ctx_t *qctx) {
|
||||
rpzqname = qctx->client->query.qname;
|
||||
}
|
||||
|
||||
- if ((qctx->options & DNS_GETDB_STALEFIRST) != 0) {
|
||||
+ if ((qctx->options & DNS_GETDB_STALEFIRST) != 0 && !qctx->is_zone) {
|
||||
/*
|
||||
* If DNS_GETDB_STALEFIRST is set, it means that a stale
|
||||
* RRset may be returned as part of this lookup. An attempt
|
||||
@@ -5931,19 +6093,19 @@ query_lookup(query_ctx_t *qctx) {
|
||||
qctx->client->query.dboptions |= DNS_DBFIND_STALETIMEOUT;
|
||||
}
|
||||
|
||||
- dboptions = qctx->client->query.dboptions;
|
||||
- if (!qctx->is_zone && qctx->findcoveringnsec &&
|
||||
- (qctx->type != dns_rdatatype_null || !dns_name_istat(rpzqname)))
|
||||
- {
|
||||
- dboptions |= DNS_DBFIND_COVERINGNSEC;
|
||||
- }
|
||||
-
|
||||
(void)dns_db_getservestalerefresh(qctx->client->view->cachedb,
|
||||
&stale_refresh);
|
||||
if (stale_refresh > 0 &&
|
||||
dns_view_staleanswerenabled(qctx->client->view))
|
||||
{
|
||||
- dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
+ qctx->client->query.dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
+ }
|
||||
+
|
||||
+ dboptions = qctx->client->query.dboptions;
|
||||
+ if (!qctx->is_zone && qctx->findcoveringnsec &&
|
||||
+ (qctx->type != dns_rdatatype_null || !dns_name_istat(rpzqname)))
|
||||
+ {
|
||||
+ dboptions |= DNS_DBFIND_COVERINGNSEC;
|
||||
}
|
||||
|
||||
result = dns_db_findext(qctx->db, rpzqname, qctx->version, qctx->type,
|
||||
@@ -6092,19 +6254,7 @@ query_lookup(query_ctx_t *qctx) {
|
||||
* Immediately return the stale answer, start a
|
||||
* resolver fetch to refresh the data in cache.
|
||||
*/
|
||||
- isc_log_write(
|
||||
- ns_lctx, NS_LOGCATEGORY_SERVE_STALE,
|
||||
- NS_LOGMODULE_QUERY, ISC_LOG_INFO,
|
||||
- "%s stale answer used, an attempt to "
|
||||
- "refresh the RRset will still be made",
|
||||
- namebuf);
|
||||
-
|
||||
- qctx->refresh_rrset = STALE(qctx->rdataset);
|
||||
- /*
|
||||
- * If we are refreshing the RRSet, we must not
|
||||
- * detach from the client in query_send().
|
||||
- */
|
||||
- qctx->client->nodetach = qctx->refresh_rrset;
|
||||
+ qctx->client->nodetach = false;
|
||||
|
||||
if (stale_found) {
|
||||
ns_client_extendederror(
|
||||
@@ -6144,76 +6294,12 @@ query_lookup(query_ctx_t *qctx) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (stale_timeout && (answer_found || stale_found)) {
|
||||
- /*
|
||||
- * Mark RRsets that we are adding to the client message on a
|
||||
- * lookup during 'stale-answer-client-timeout', so we can
|
||||
- * clean it up if needed when we resume from recursion.
|
||||
- */
|
||||
- qctx->client->query.attributes |= NS_QUERYATTR_STALEOK;
|
||||
- qctx->rdataset->attributes |= DNS_RDATASETATTR_STALE_ADDED;
|
||||
- }
|
||||
-
|
||||
result = query_gotanswer(qctx, result);
|
||||
|
||||
cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Clear all rdatasets from the message that are in the given section and
|
||||
- * that have the 'attr' attribute set.
|
||||
- */
|
||||
-static void
|
||||
-message_clearrdataset(dns_message_t *msg, unsigned int attr) {
|
||||
- unsigned int i;
|
||||
- dns_name_t *name, *next_name;
|
||||
- dns_rdataset_t *rds, *next_rds;
|
||||
-
|
||||
- /*
|
||||
- * Clean up name lists by calling the rdataset disassociate function.
|
||||
- */
|
||||
- for (i = DNS_SECTION_ANSWER; i < DNS_SECTION_MAX; i++) {
|
||||
- name = ISC_LIST_HEAD(msg->sections[i]);
|
||||
- while (name != NULL) {
|
||||
- next_name = ISC_LIST_NEXT(name, link);
|
||||
-
|
||||
- rds = ISC_LIST_HEAD(name->list);
|
||||
- while (rds != NULL) {
|
||||
- next_rds = ISC_LIST_NEXT(rds, link);
|
||||
- if ((rds->attributes & attr) != attr) {
|
||||
- rds = next_rds;
|
||||
- continue;
|
||||
- }
|
||||
- ISC_LIST_UNLINK(name->list, rds, link);
|
||||
- INSIST(dns_rdataset_isassociated(rds));
|
||||
- dns_rdataset_disassociate(rds);
|
||||
- isc_mempool_put(msg->rdspool, rds);
|
||||
- rds = next_rds;
|
||||
- }
|
||||
-
|
||||
- if (ISC_LIST_EMPTY(name->list)) {
|
||||
- ISC_LIST_UNLINK(msg->sections[i], name, link);
|
||||
- if (dns_name_dynamic(name)) {
|
||||
- dns_name_free(name, msg->mctx);
|
||||
- }
|
||||
- isc_mempool_put(msg->namepool, name);
|
||||
- }
|
||||
-
|
||||
- name = next_name;
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * Clear any rdatasets from the client's message that were added on a lookup
|
||||
- * due to a client timeout.
|
||||
- */
|
||||
-static void
|
||||
-query_clear_stale(ns_client_t *client) {
|
||||
- message_clearrdataset(client->message, DNS_RDATASETATTR_STALE_ADDED);
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Create a new query context with the sole intent of looking up for a stale
|
||||
* RRset in cache. If an entry is found, we mark the original query as
|
||||
@@ -6293,6 +6379,7 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
|
||||
}
|
||||
client->query.fetchoptions &= ~DNS_FETCHOPT_TRYSTALE_ONTIMEOUT;
|
||||
client->query.dboptions &= ~DNS_DBFIND_STALETIMEOUT;
|
||||
+ client->query.dboptions &= ~DNS_DBFIND_STALEENABLED;
|
||||
client->nodetach = false;
|
||||
|
||||
LOCK(&client->query.fetchlock);
|
||||
@@ -7660,14 +7747,6 @@ query_usestale(query_ctx_t *qctx, isc_result_t result) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
- if (qctx->refresh_rrset) {
|
||||
- /*
|
||||
- * This is a refreshing query, we have already prioritized
|
||||
- * stale data, so don't enable serve-stale again.
|
||||
- */
|
||||
- return (false);
|
||||
- }
|
||||
-
|
||||
if (result == DNS_R_DUPLICATE || result == DNS_R_DROP ||
|
||||
result == ISC_R_ALREADYRUNNING)
|
||||
{
|
||||
@@ -8207,24 +8286,6 @@ query_addanswer(query_ctx_t *qctx) {
|
||||
|
||||
CALL_HOOK(NS_QUERY_ADDANSWER_BEGIN, qctx);
|
||||
|
||||
- /*
|
||||
- * On normal lookups, clear any rdatasets that were added on a
|
||||
- * lookup due to stale-answer-client-timeout. Do not clear if we
|
||||
- * are going to refresh the RRset, because the stale contents are
|
||||
- * prioritized.
|
||||
- */
|
||||
- if (QUERY_STALEOK(&qctx->client->query) &&
|
||||
- !QUERY_STALETIMEOUT(&qctx->client->query) && !qctx->refresh_rrset)
|
||||
- {
|
||||
- CCTRACE(ISC_LOG_DEBUG(3), "query_clear_stale");
|
||||
- query_clear_stale(qctx->client);
|
||||
- /*
|
||||
- * We can clear the attribute to prevent redundant clearing
|
||||
- * in subsequent lookups.
|
||||
- */
|
||||
- qctx->client->query.attributes &= ~NS_QUERYATTR_STALEOK;
|
||||
- }
|
||||
-
|
||||
if (qctx->dns64) {
|
||||
result = query_dns64(qctx);
|
||||
qctx->noqname = NULL;
|
||||
@@ -8258,9 +8319,7 @@ query_addanswer(query_ctx_t *qctx) {
|
||||
query_filter64(qctx);
|
||||
ns_client_putrdataset(qctx->client, &qctx->rdataset);
|
||||
} else {
|
||||
- if (!qctx->is_zone && RECURSIONOK(qctx->client) &&
|
||||
- !QUERY_STALETIMEOUT(&qctx->client->query))
|
||||
- {
|
||||
+ if (!qctx->is_zone && RECURSIONOK(qctx->client)) {
|
||||
query_prefetch(qctx->client, qctx->fname,
|
||||
qctx->rdataset);
|
||||
}
|
||||
@@ -10542,6 +10601,10 @@ query_ncache(query_ctx_t *qctx, isc_result_t result) {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (!qctx->is_zone && RECURSIONOK(qctx->client)) {
|
||||
+ query_stale_refresh_ncache(qctx->client);
|
||||
+ }
|
||||
+
|
||||
return (query_nodata(qctx, result));
|
||||
|
||||
cleanup:
|
||||
@@ -12044,20 +12107,6 @@ ns_query_done(query_ctx_t *qctx) {
|
||||
nodetach = qctx->client->nodetach;
|
||||
query_send(qctx->client);
|
||||
|
||||
- if (qctx->refresh_rrset) {
|
||||
- /*
|
||||
- * If we reached this point then it means that we have found a
|
||||
- * stale RRset entry in cache and BIND is configured to allow
|
||||
- * queries to be answered with stale data if no active RRset
|
||||
- * is available, i.e. "stale-anwer-client-timeout 0". But, we
|
||||
- * still need to refresh the RRset. To prevent adding duplicate
|
||||
- * RRsets, clear the RRsets from the message before doing the
|
||||
- * refresh.
|
||||
- */
|
||||
- message_clearrdataset(qctx->client->message, 0);
|
||||
- query_refresh_rrset(qctx);
|
||||
- }
|
||||
-
|
||||
if (!nodetach) {
|
||||
qctx->detach_client = true;
|
||||
}
|
||||
@ -75,7 +75,7 @@ License: MPL-2.0 AND ISC AND MIT AND BSD-3-Clause AND BSD-2-Clause
|
||||
# ./lib/isc/tm.c BSD-2-clause and/or MPL-2.0
|
||||
# ./lib/isccfg/parser.c BSD-2-clause and/or MPL-2.0
|
||||
Version: 9.18.29
|
||||
Release: 13%{?dist}
|
||||
Release: 16%{?dist}
|
||||
Epoch: 32
|
||||
Url: https://www.isc.org/downloads/bind/
|
||||
#
|
||||
@ -143,6 +143,15 @@ Patch225: bind-9.18-CVE-2025-40780.patch
|
||||
Patch226: bind-9.20-CVE-2025-8677-dual-signing.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11195
|
||||
Patch227: bind-9.20-CVE-2025-8677-dual-signing-test.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11329
|
||||
Patch228: bind-9.20-robust-key-rollovers.patch
|
||||
Patch229: bind-9.20-robust-key-rollovers-tests.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/10767
|
||||
Patch230: bind-9.20-stale-cname.patch
|
||||
Patch231: bind-9.20-stale-cname-tests.patch
|
||||
# https://gitlab.isc.org/isc-projects/bind9/-/commit/5ef459eeaa92222ad28d2186f5eae9a586dece70
|
||||
Patch232: bind-9.18-CVE-2026-1519.patch
|
||||
Patch233: bind-9.18-CVE-2026-1519-test.patch
|
||||
|
||||
%{?systemd_ordering}
|
||||
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
|
||||
@ -1006,6 +1015,17 @@ fi;
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Mar 27 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.29-16
|
||||
- Prevent Denial of Service via maliciously crafted DNSSEC-validated zone
|
||||
(CVE-2026-1519)
|
||||
- Correct backport issue in the patch (CVE-2026-1519)
|
||||
|
||||
* Thu Feb 26 2026 Fedor Vorobev <fvorobev@redhat.com> - 32:9.18.29-15
|
||||
- Backport fixes for stale CNAME chains. (RHEL-86172)
|
||||
|
||||
* Fri Jan 30 2026 Fedor Vorobev <fvorobev@redhat.com> - 32:9.18.29-14
|
||||
- Backport fix for manual DNSSEC key rolllovers. (RHEL-144422)
|
||||
|
||||
* Thu Jan 29 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.29-13
|
||||
- Correct changelog version of previous change
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user