From: Ondřej Surý Date: Mon, 20 Jul 2026 08:45:50 +0000 (+0200) Subject: Disclose Negative Trust Anchors with Extended DNS Error 33 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8ea265df02565e36162967f1f03b5e3d0160f242;p=thirdparty%2Fbind9.git Disclose Negative Trust Anchors with Extended DNS Error 33 A Negative Trust Anchor (RFC 7646) makes a validating resolver treat an otherwise-secure name as insecure, but there was no in-band way for a client to tell that an answer which should have failed DNSSEC validation was returned because an NTA was in place. Register Extended DNS Error INFO-CODE 33, "Negative Trust Anchor" (draft-farrokhi-dnsop-ede-nta), and attach it to a response whenever a covering NTA suppresses validation for the queried name. --- diff --git a/lib/dns/ede.c b/lib/dns/ede.c index 48fc1531886..7dc9075a84c 100644 --- a/lib/dns/ede.c +++ b/lib/dns/ede.c @@ -13,6 +13,8 @@ /*! \file */ +#include + #include #include @@ -22,12 +24,12 @@ #define DNS_EDE_VALID(v) ISC_MAGIC_VALID(v, DNS_EDE_MAGIC) static bool -dns__ede_checkandupdateedeused(dns_edectx_t *edectx, uint16_t code) { - if (edectx->edeused & (1 << code)) { +dns__ede_checkandupdateedeused(dns_edectx_t *edectx, dns_edecode_t code) { + if (edectx->edeused & (UINT64_C(1) << code)) { return true; } - edectx->edeused |= 1 << code; + edectx->edeused |= UINT64_C(1) << code; return false; } diff --git a/lib/dns/include/dns/ede.h b/lib/dns/include/dns/ede.h index a9470306cb6..df96237dfa3 100644 --- a/lib/dns/include/dns/ede.h +++ b/lib/dns/include/dns/ede.h @@ -13,12 +13,16 @@ #pragma once +#include + #include +#include #include /*%< EDNS0 extended DNS errors */ -enum { +typedef enum ISC_FIXED_ENUM(dns_edecode, uint16_t) { + // typedef enum ISC_FIXED_ENUM(dns_edecode, uint16_t) { DNS_EDE_OTHER = 0, /*%< Other Error */ DNS_EDE_DNSKEYALG = 1, /*%< Unsupported DNSKEY Algorithm */ DNS_EDE_DSDIGESTTYPE = 2, /*%< Unsupported DS Digest Type */ @@ -44,8 +48,13 @@ enum { DNS_EDE_NOREACHABLEAUTH = 22, /*%< No Reachable Authority */ DNS_EDE_NETWORKERROR = 23, /*%< Network Error */ DNS_EDE_INVALIDDATA = 24, /*%< Invalid Data */ - DNS_EDE_MAX_CODE -}; + DNS_EDE_NTA = 33, /*%< Negative Trust Anchor */ + DNS_EDE_MAX_CODE, + DNS_EDE_ENFORCE = UINT16_MAX, /*%< Enforce uint16_t size */ +} dns_edecode_t; + +STATIC_ASSERT(sizeof(dns_edecode_t) == sizeof(uint16_t), + "dns_edecode_t is not uint16_t sized"); /* * From RFC 8914: @@ -62,7 +71,7 @@ struct dns_edectx { int magic; isc_mem_t *mctx; dns_ednsopt_t *ede[DNS_EDE_MAX_ERRORS]; - uint32_t edeused; + uint64_t edeused; size_t nextede; }; /*%< @@ -74,6 +83,13 @@ struct dns_edectx { * the response client message. */ +STATIC_ASSERT(DNS_EDE_MAX_CODE <= + CHAR_BIT * sizeof(((dns_edectx_t *){ NULL })->edeused), + "DNS_EDE_MAX_CODE does not fit in the edeused bitmap"); +/* + * Make sure we can fit the currently supported EDE codes in the edeused bitmap. + */ + void dns_ede_init(isc_mem_t *mctx, dns_edectx_t *edectx); /*%< diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index a094eeb430b..b79e0821b85 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -2446,9 +2446,10 @@ compute_cc(const resquery_t *query, uint8_t *cookie, const size_t len) { static bool issecuredomain(fetchctx_t *fctx, const dns_name_t *name, dns_rdatatype_t type, - isc_stdtime_t now, bool *ntap) { + isc_stdtime_t now) { dns_name_t suffix; unsigned int labels; + bool secure_domain, nta = false; /* * For DS variants we need to check fom the parent domain, @@ -2463,8 +2464,22 @@ issecuredomain(fetchctx_t *fctx, const dns_name_t *name, dns_rdatatype_t type, name = &suffix; } - return dns_view_issecuredomain(fctx->res->view, name, now, - CHECKNTA(fctx), ntap); + secure_domain = dns_view_issecuredomain(fctx->res->view, name, now, + CHECKNTA(fctx), &nta); + + /* + * A covering negative trust anchor suppressed DNSSEC validation for + * an otherwise secure name (RFC 7646). Disclose that to the client + * via an Extended DNS Error (draft-farrokhi-dnsop-ede-nta). Duplicate + * codes are coalesced by dns_ede_add(), so this is emitted at most + * once per fetch. + */ + if (nta) { + dns_ede_add(&fctx->edectx, DNS_EDE_NTA, + "Negative Trust Anchor applied (RFC 7646)"); + } + + return secure_domain; } static isc_result_t @@ -6343,8 +6358,7 @@ rctx_cachename(respctx_t *rctx, dns_message_t *message, dns_name_t *name) { /* * Is DNSSEC validation required for this name? */ - bool secure_domain = issecuredomain(fctx, name, fctx->type, rctx->now, - NULL); + bool secure_domain = issecuredomain(fctx, name, fctx->type, rctx->now); bool need_validation = secure_domain && ((fctx->options & DNS_FETCHOPT_NOVALIDATE) == 0); @@ -6592,8 +6606,7 @@ rctx_ncache(respctx_t *rctx) { /* * Is DNSSEC validation required for this name? */ - bool secure_domain = issecuredomain(fctx, name, fctx->type, rctx->now, - NULL); + bool secure_domain = issecuredomain(fctx, name, fctx->type, rctx->now); bool need_validation = secure_domain && ((fctx->options & DNS_FETCHOPT_NOVALIDATE) == 0); @@ -9418,7 +9431,7 @@ rctx_authority_dnssec(respctx_t *rctx) { secure_domain = issecuredomain(fctx, name, dns_rdatatype_ds, - fctx->now, NULL); + fctx->now); if (secure_domain) { rdataset->trust = dns_trust_pending_answer; diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h index ddfce87c324..21abcccb2bf 100644 --- a/lib/isc/include/isc/util.h +++ b/lib/isc/include/isc/util.h @@ -313,3 +313,15 @@ mock_assert(const int result, const char *const expression, #else /* __has_builtin(__builtin_types_compatible_p) */ #define ISC_TYPES_COMPATIBLE(x, y) 1 #endif /* __has_builtin(__builtin_types_compatible_p) */ + +/* clang-format off */ +/* + * This needs some extra plumbing for non-C23 compilers, see + * lib/dns/include/dns/ede.h:dns_edecode_t for example. + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define ISC_FIXED_ENUM(name, type) name: type +#else +#define ISC_FIXED_ENUM(name, type) __attribute__((__packed__)) name +#endif +/* clang-format on */