From: W.C.A. Wijngaards Date: Fri, 31 Jul 2026 07:53:47 +0000 (+0200) Subject: - For #1483: The failure reason when an NSEC NXDOMAIN is X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;ds=inline;p=thirdparty%2Funbound.git - For #1483: The failure reason when an NSEC NXDOMAIN is encountered when looking for an insecure delegation, is fixed to mention the NSEC records, instead of nonexistent NSEC3 records, that it attempted. --- diff --git a/doc/Changelog b/doc/Changelog index 9bd4356f9..32e525e3b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +31 July 2026: Wouter + - For #1483: The failure reason when an NSEC NXDOMAIN is + encountered when looking for an insecure delegation, is + fixed to mention the NSEC records, instead of nonexistent + NSEC3 records, that it attempted. + 30 July 2026: Wouter - Fix #1482: DNS-over-QUIC doesn't work with simple config. That fixes interface-automatic for use with doq service. diff --git a/testdata/val_nx_uns_resp.rpl b/testdata/val_nx_uns_resp.rpl new file mode 100644 index 000000000..88dc95567 --- /dev/null +++ b/testdata/val_nx_uns_resp.rpl @@ -0,0 +1,82 @@ +; config options +server: + ; This is the test key 29332 in the testdata. + trust-anchor: ". 3600 IN DS 29332 8 2 b75e26316631b6e37cbc977323a08769f86e36a10fee888676d35f61e2ff4181" + val-override-date: "20201020135527" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + fake-sha1: yes + trust-anchor-signaling: no + minimal-responses: no + log-servfail: yes + +forward-zone: + name: "." + forward-addr: 10.5.5.5 +CONFIG_END + +SCENARIO_BEGIN Test nxdomain that gets unsigned response +; and the DS lookup that it makes gets an NSEC NXDOMAIN response. + +; 10.5.5.5 forwarder +RANGE_BEGIN 0 100 + ADDRESS 10.5.5.5 + +; unsigned NXDOMAIN response, from the first forwarder, here it is served +; from the upstream. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NXDOMAIN +SECTION QUESTION +example.veryinvalid. IN TXT +SECTION AUTHORITY +. 3600 IN SOA ns.root. host.root. 1 3600 3600 3600 3600 +ENTRY_END + +; DNSKEY answer, using CSK for test simplicity. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN DNSKEY +SECTION ANSWER +. IN DNSKEY 257 3 8 AwEAAb4WMOTBLTFvmBra5m6SK4VfViOzmvyUAU0qv861ZQXeEFvwlndqNU9rwRsMxrSWAYs5nHErKDn49usC/HyxxW1477iGFHhfgL4mjNreJm9zft2QFB1VLbRbEPYdDMLCn4co0qnG7/KG8W2i8Pym1L7f+aREwbLo+/716AS2PbaKMhfWLKLiq5wnBcUClQMNzCiwhqxDJp1oePqfkVdeUgXOtgi0dYRIKyQFhJ5VWJ22npoi/Gif0XLCADAlAwRLKc8o/yJkCxskzgpHpw5Cki1lclg0aq4ssOuPRQ+ne6IHYCz9D2mwzulblhLFamKdq7aHzNt4NlyxhpANVFiKLD8= ;{id = 29332 (ksk), size = 2048b} +. 3600 IN RRSIG DNSKEY 8 0 3600 20201116135527 20201019135527 29332 . ToK8hJrGa+kNu6y8FpRwZq2FjDPBAk5Ctchia3Vu9yTth2dR7BhK2ALTWVBwAQGwiwxXKoVK9QCxdQM0ti7CVb9x75bejkd2E6UGWVmqyTRPpn3D43qYARm87y3ZVKG7LlWHp8UOf21XLp1H7R+wuipIvBJ1XA+QGXThPdbV9EEz1kKGdprBfdpFkQdcAiuYYrOTa5cJ11z32mGiQ12fWjpb4UUbfcoDD9YOoa/S5a6h7jYBOfm75ZB8UCW3Z/SlsN8KIfYZsg5CZphpf38XH5uNLMmzpaWYhfamJZJve9Isx4eILNmdMLK4E8ESwDFCVNzMIqdf20VRg6Lh7nwQeA== +ENTRY_END + +; answer for DS, from another forwarder, here returned from the test upstream. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NXDOMAIN +SECTION QUESTION +veryinvalid. IN DS +SECTION AUTHORITY +versicherung. 3600 IN NSEC vet. NS DS RRSIG NSEC +versicherung. 3600 IN RRSIG NSEC 8 1 3600 20201116135527 20201019135527 29332 . AVAON9Y7AVwX9YWQK8JPcB6Wk/tEfQT7JrLiCRlBBQA0+mpVYYYtMyrm4aMjkhusqYcnpIZoLGOI/dxJjIwDgnMkd4EqY2oICea3I260f8z2v9e7zNobyUTjkoWsmLPc7VRLtEGKu1XyVpt7DX6ElGoSUhU4JsTx7wkkXU0SGAakL0bhK8K68B92NEVwgKX4D7+kVfpjc0aHaB3rAkhQCM/G0jEFp0RuhTX1aru6IuYrZmjW0dvQ2niec6NaYzvuGnbhMLlFLuXqSmI2B7uIFx894usd1cVWnSRg49bAkuiEv5q04ltRel1huJBGGiZlLEwanS5g53C5DHfq10OUrw== +. 3600 IN NSEC aaa. NS SOA RRSIG NSEC DNSKEY ZONEMD +. 3600 IN RRSIG NSEC 8 0 3600 20201116135527 20201019135527 29332 . E8r6rpFgFBUda2GnFSMzHZLtjy1dT+ZS0wPRE12RNwVK547bo2vByv9EFhOHS6sEIFqX+AmIJotuiEPKnCFUTr6FKscaxtw38dJRZ3wldqV6dmqUiRmz91crDCV5nSL45FIbkWKk1Q+tnXie3sZ4zwBc12kGg2BttMAQ0i4sbMbf6EUNYZGwYzSB0/VhXVJcl8gl+5lfpiVqfWNZI7vTEaHqrC2gBC3UK1cQE9lQOqhJ6H5ThA1FR9j/mZFM9sG5vQ2Mqlzl2iiN2Y6mCptDY1vwfff6AnT0YeDwJ/XwGisMZrSvTCYaiRndb8CUUmCr23AFy5OER1rmeFGkHX5+WQ== +. 3600 IN SOA ns.root. host.root. 1 3600 3600 3600 3600 +. 3600 IN RRSIG SOA 8 0 3600 20201116135527 20201019135527 29332 . tVeReLMXPnl6rk4QX94xy9lCodQ+xc39lokbNkvbXnTURNCOAwtNiMMPlAAJ3/HTpIxo175gPfupACIveBtgajdp85jUIvLMOM5B6lX80+dUPBGZ4gHVjf+8EGnr7q2wnW2+KcJu0OhN2g+YqCV6aPi8pzuAp+AMsBYcMfXqEQq9Lxqv6TL50MUCJN3GPCyBIdjbs/A+ZB7D1EOO1YgdbsMHK/pWKYt4UfBFfekoA6joIGf4vBKKRTWnoo0BcrFob3AW1SyJkoxoqEsN3YAVL9jNkJCkU0/adLypHgDNayLgsWI5/o4Ng8LxN6tNxAilkMhcGY80T5g0uo+ukY7McA== +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +example.veryinvalid. IN TXT +ENTRY_END + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +example.veryinvalid. IN TXT +SECTION ANSWER +ENTRY_END + +SCENARIO_END diff --git a/validator/val_utils.c b/validator/val_utils.c index e77f93f5a..bfff19126 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -1323,6 +1323,20 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason) return 0; } +void val_has_auth_nsecs(struct reply_info* rep, int* has_nsec, int* has_nsec3) +{ + size_t i, num_nsec = 0, num_nsec3 = 0; + for(i=rep->an_numrrsets; ian_numrrsets+rep->ns_numrrsets; i++) { + if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC)) + num_nsec++; + else if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC3)) + num_nsec3++; + else continue; + } + *has_nsec = (num_nsec != 0); + *has_nsec3 = (num_nsec3 != 0); +} + struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, struct regional* region, uint8_t* topname) diff --git a/validator/val_utils.h b/validator/val_utils.h index 43386edbf..f05ff1d94 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -410,6 +410,14 @@ void val_blacklist(struct sock_list** blacklist, struct regional* region, */ int val_has_signed_nsecs(struct reply_info* rep, char** reason); +/** + * See if there are NSECs, or NSEC3s in the authority section. + * @param rep: reply to check + * @param has_nsec: returned true if it has nsecs. + * @param has_nsec3: returned true if it has nsec3s. + */ +void val_has_auth_nsecs(struct reply_info* rep, int* has_nsec, int* has_nsec3); + /** * Return algo number for favorite (best) algorithm that we support in DS. * @param ds_rrset: the DSes in this rrset are inspected and best algo chosen. diff --git a/validator/validator.c b/validator/validator.c index d3ed8be3e..3634e3215 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -3116,6 +3116,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, case sec_status_unchecked: default: /* NSEC proof did not work, try next */ + verbose(VERB_ALGO, "NSEC proof did not prove insecure delegation, try NSEC3"); break; } @@ -3151,6 +3152,25 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, *ke = NULL; return 0; case sec_status_bogus: + /* It could be that the NSEC proof failed, + * and, then tried NSEC3. */ + { + int has_nsec=0, has_nsec3=0; + val_has_auth_nsecs(msg->rep, &has_nsec, + &has_nsec3); + if(!has_nsec3 && has_nsec) { + /* The NSECs are the cause, mention that in the error message. */ + verbose(VERB_DETAIL, "NSECs for the " + "referral did not prove no DS."); + errinf_ede(qstate, "NSECs for the referral did not prove no DS", LDNS_EDE_DNSSEC_BOGUS); + goto return_bogus; + } + if(!has_nsec3 && !has_nsec) { + verbose(VERB_DETAIL, "absence of NSECs and NSEC3s when attempting to prove no DS."); + errinf_ede(qstate, "no NSECs or NSEC3s when attempting to prove no DS", LDNS_EDE_DNSSEC_BOGUS); + goto return_bogus; + } + } verbose(VERB_DETAIL, "NSEC3s for the " "referral did not prove no DS."); errinf_ede(qstate, reason, reason_bogus);