252 lines
8.3 KiB
Diff
252 lines
8.3 KiB
Diff
From e388088ed01874485d8bd38e332a54b0c1fb5605 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 | 90 ++++++++++++++++++++++++++++---------
|
|
2 files changed, 70 insertions(+), 21 deletions(-)
|
|
|
|
diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h
|
|
index f0aaa24e93..a945f0e73f 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 a9db844c27..0c805af6fa 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;
|
|
@@ -2588,7 +2633,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)", NULL);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
@@ -2624,7 +2670,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 +2887,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
|
|
|