From 0aaaa8768f317db2a92fd311122889f6cb8afe50 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 2 Dec 2021 00:34:38 +1100 Subject: [PATCH] Reject NSEC records with next field with \000 label A number of DNS implementation produce NSEC records with bad type maps that don't contain types that exist at the name leading to NODATA responses being synthesize instead of the records in the zone. NSEC records with these bad type maps often have the NSEC NSEC field set to '\000.QNAME'. We look for the first label of this pattern. e.g. example.com NSEC \000.example.com SOA NS NSEC RRSIG example.com RRRSIG NSEC ... example.com SOA ... example.com RRRSIG SOA ... example.com NS ... example.com RRRSIG NS ... example.com A ... example.com RRRSIG A ... A is missing from the type map. This introduces a temporary option 'reject-000-label' to control this behaviour. --- bin/named/config.c | 1 + bin/named/named.conf.rst | 2 ++ bin/named/server.c | 5 +++++ doc/arm/reference.rst | 20 +++++++++++++++++++ doc/man/named.conf.5in | 2 ++ doc/misc/options | 2 ++ doc/misc/options.active | 2 ++ doc/misc/options.grammar.rst | 1 + lib/dns/include/dns/view.h | 1 + lib/dns/resolver.c | 37 ++++++++++++++++++++++++++++++++++++ lib/dns/view.c | 1 + lib/isccfg/namedconf.c | 1 + 12 files changed, 75 insertions(+) diff --git a/bin/named/config.c b/bin/named/config.c index 6a8fc599fb..e93ad273ed 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -177,6 +177,7 @@ options {\n\ query-source address *;\n\ query-source-v6 address *;\n\ recursion true;\n\ + reject-000-label yes;\n\ request-expire true;\n\ request-ixfr true;\n\ require-server-cookie no;\n\ diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index f02d52d425..6c1ec9b260 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -388,6 +388,7 @@ OPTIONS recursing-file quoted_string; recursion boolean; recursive-clients integer; + reject-000-label boolean;// deprecated request-expire boolean; request-ixfr boolean; request-nsid boolean; @@ -793,6 +794,7 @@ VIEW window integer; }; recursion boolean; + reject-000-label boolean;// deprecated request-expire boolean; request-ixfr boolean; request-nsid boolean; diff --git a/bin/named/server.c b/bin/named/server.c index 852d7dbf54..f7db73faf9 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -4456,6 +4456,11 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, INSIST(result == ISC_R_SUCCESS); view->acceptexpired = cfg_obj_asboolean(obj); + obj = NULL; + result = named_config_get(maps, "reject-000-label", &obj); + INSIST(result == ISC_R_SUCCESS); + view->reject_000_label = cfg_obj_asboolean(obj); + obj = NULL; /* 'optionmaps', not 'maps': don't check named_g_defaults yet */ (void)named_config_get(optionmaps, "dnssec-validation", &obj); diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 279ae583d3..97f4e92594 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2100,6 +2100,16 @@ Boolean Options default is ``no``. Setting this option to ``yes`` leaves ``named`` vulnerable to replay attacks. +``reject-000-label`` + This can be used to control whether NSEC records which have the + ``next`` field starting with the ``\\000`` label are cached for + ``synth-from-dnssec``. There are a number of DNSSEC implementations + that generate bad NSEC type maps where the ``next`` field starts with + the ``\\000`` label and between BIND 9.18 and BIND 9.20 there will be + a campaign to get these servers corrected. In BIND 9.18 this defaults + to ``yes``. In BIND 9.20 (BIND 9.19) this will default to ``no`` and + in BIND 9.22 (BIND 9.21) this option will be removed. + ``querylog`` Query logging provides a complete log of all incoming queries and all query errors. This provides more insight into the server's activity, but with a @@ -2247,6 +2257,16 @@ Boolean Options named caching broken NSEC records from negative responses from servers that emit broken NSEC records with missing types that actually exist. + ``reject-000-label`` can be used to control whether NSEC records + which have the ``next`` field starting with the ``\\000`` label + are cached for ``synth-from-dnssec``. There are a number of + DNSSEC implementations that generate bad NSEC type maps where + the ``next`` field starts with the ``\\000`` label and between + BIND 9.18 and BIND 9.20 there will be a campaign to get these + servers corrected. In BIND 9.18 this defaults to ``yes``. In + BIND 9.20 (BIND 9.19) this will default to ``no`` and in BIND 9.22 + (BIND 9.21) this option will be removed. + .. note:: DNSSEC validation must be enabled for this option to be effective. This initial implementation only covers synthesis of answers from NSEC records; synthesis from NSEC3 is planned for the future. This diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 52c1da4f40..ebfc33e975 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -455,6 +455,7 @@ options { recursing\-file quoted_string; recursion boolean; recursive\-clients integer; + reject\-000\-label boolean;// deprecated request\-expire boolean; request\-ixfr boolean; request\-nsid boolean; @@ -896,6 +897,7 @@ view string [ class ] { window integer; }; recursion boolean; + reject\-000\-label boolean;// deprecated request\-expire boolean; request\-ixfr boolean; request\-nsid boolean; diff --git a/doc/misc/options b/doc/misc/options index 52485fd338..f015f926a0 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -313,6 +313,7 @@ options { recursing-file ; recursion ; recursive-clients ; + reject-000-label ; // deprecated request-expire ; request-ixfr ; request-nsid ; @@ -673,6 +674,7 @@ view [ ] { window ; }; recursion ; + reject-000-label ; // deprecated request-expire ; request-ixfr ; request-nsid ; diff --git a/doc/misc/options.active b/doc/misc/options.active index 68aafb9ec9..882c247348 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -311,6 +311,7 @@ options { recursing-file ; recursion ; recursive-clients ; + reject-000-label ; // deprecated request-expire ; request-ixfr ; request-nsid ; @@ -669,6 +670,7 @@ view [ ] { window ; }; recursion ; + reject-000-label ; // deprecated request-expire ; request-ixfr ; request-nsid ; diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 8e69a09686..862461f690 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -231,6 +231,7 @@ recursing-file ; recursion ; recursive-clients ; + reject-000-label ; // deprecated request-expire ; request-ixfr ; request-nsid ; diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index f1ec93877f..43deee1039 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -130,6 +130,7 @@ struct dns_view { bool synthfromdnssec; bool trust_anchor_telemetry; bool root_key_sentinel; + bool reject_000_label; dns_transfer_format_t transfer_format; dns_acl_t *cacheacl; dns_acl_t *cacheonacl; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index e8b17aae19..64fe5d0b58 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -5210,6 +5210,32 @@ check_soa_and_dnskey(dns_rdataset_t *nsecset) { return (true); } +/* + * Look for NSEC next name that starts with the label '\000'. + */ +static bool +has_000_label(dns_rdataset_t *nsecset) { + dns_rdataset_t rdataset; + isc_result_t result; + + dns_rdataset_init(&rdataset); + dns_rdataset_clone(nsecset, &rdataset); + + for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(&rdataset, &rdata); + if (rdata.length > 1 && rdata.data[0] == 1 && + rdata.data[1] == 0) { + dns_rdataset_disassociate(&rdataset); + return (true); + } + } + dns_rdataset_disassociate(&rdataset); + return (false); +} + /* * The validator has finished. */ @@ -5645,6 +5671,17 @@ answer_response: !check_soa_and_dnskey(rdataset)) { continue; } + + /* + * Look for \000 label in next name. + */ + if (rdataset->type == dns_rdatatype_nsec && + fctx->res->view->reject_000_label && + has_000_label(rdataset)) + { + continue; + } + result = dns_db_findnode(fctx->cache, name, true, &nsnode); if (result != ISC_R_SUCCESS) { diff --git a/lib/dns/view.c b/lib/dns/view.c index de5464f288..5b6ef54388 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -240,6 +240,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, const char *name, view->synthfromdnssec = true; view->trust_anchor_telemetry = true; view->root_key_sentinel = true; + view->reject_000_label = true; view->new_zone_dir = NULL; view->new_zone_file = NULL; view->new_zone_db = NULL; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index ee0f3091f3..bf53b119d6 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2118,6 +2118,7 @@ static cfg_clausedef_t view_clauses[] = { { "queryport-pool-updateinterval", NULL, CFG_CLAUSEFLAG_ANCIENT }, { "rate-limit", &cfg_type_rrl, 0 }, { "recursion", &cfg_type_boolean, 0 }, + { "reject-000-label", &cfg_type_boolean, CFG_CLAUSEFLAG_DEPRECATED }, { "request-nsid", &cfg_type_boolean, 0 }, { "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT }, { "require-server-cookie", &cfg_type_boolean, 0 },