]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
validator: accept a confusing NODATA proof with insecure delegation docs-develop-noda-trz0bs/deployments/6297
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 26 Feb 2025 08:29:12 +0000 (09:29 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 26 Feb 2025 08:29:12 +0000 (09:29 +0100)
Honestly, I find it ugly and probably unintended,
but it's correctly signed and other vendors tend to accept it.

Example:
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 24204
;; Flags: qr aa rd; QUERY: 1; ANSWER: 0; AUTHORITY: 4; ADDITIONAL: 1

;; EDNS PSEUDOSECTION:
;; Version: 0; flags: do; UDP size: 4096 B; ext-rcode: NOERROR

;; QUESTION SECTION:
;; _domainkey.mail.cez.cz.              TXT

;; AUTHORITY SECTION:
cez.cz.                 3600    SOA     ns10.cez.cz. netmaster.cez.cz. 2025021801 14400 3600 604800 7200
cez.cz.                 3600    RRSIG   SOA 10 2 3600 20250302073317 20250223063317 45620 cez.cz. JnAonhCOi234lF2A40lYaHcuKtxACKz8X6UFILSgSaK00xyXDk6gWDWo3nmMjXxBwgfP98Gaj8nLMqRZ7ezAEUfWi+5P4YCQzax5Habu3nKB+XKocIPMCHHMhOMf410w4Taz4N2rKgi1p71QkuujISi3JZWzqG4bqzot2cGL12w=
1vk9lupeivbv7dhsb7udm5da1hkd089j.cez.cz. 7200   NSEC3   1 0 1 ACB298B834ADA5FD 1vk9lupeivbv7dhsb7udm5da1hkd089k A NS HINFO MX AAAA SRV RRSIG CAA
1vk9lupeivbv7dhsb7udm5da1hkd089j.cez.cz. 7200   RRSIG   NSEC3 10 3 7200 20250303115912 20250224105912 45620 cez.cz. OBW90lof86IoVsiuKkNEf4useG3fikE+npAVkpbiVsgMZWLHRNzAAlIU9wPMH5S4CWpnwoMVTaNtWJxegsG7cvCDZrjVVNOHE9hLOG2eG9f57vx/tVFTe4/DegO9KOyColOOYt4nt/uj7LTJZbzJY3Ev8I9971LEkFf5IxVwwPU=

NEWS
lib/dnssec/nsec.c
lib/dnssec/nsec.h
lib/dnssec/nsec3.c
lib/dnssec/nsec3.h
lib/layer/validate.c

diff --git a/NEWS b/NEWS
index 4191f517c950559ada935cb03f95632e548188c7..b58a3b36bf532eeeeecc6a6a9b0a7f04d88d67f6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,7 @@ Improvements
 - manager: allow multiple instances with different rundirs (!1656)
 - tests: disable problematic config.http test (#925, !1662)
 - /management/unix-socket: allow path relative to rundir (!1657)
+- validator: accept a confusing NODATA proof with insecure delegation (!1659)
 
 
 Knot Resolver 6.0.10 (2025-01-20)
index be34d92db0967cf14d9d44dc37753a7e398a1d1f..07672cc8c7a2e33a70e16b9769dee90a6a41c678 100644 (file)
@@ -161,10 +161,16 @@ int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t ty
                break;
        default:
                /* Parent-side delegation record isn't authoritative for non-DS;
-                * see RFC6840 4.1. */
+                * see RFC6840 4.1.
+                *
+                * Additionally, we signal if the NODATA would belong
+                * to an *insecure* child zone.
+                */
                if (dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_NS)
                    && !dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_SOA)) {
-                       return NO_PROOF;
+                       return dnssec_nsec_bitmap_contains(bm, bm_size, KNOT_RRTYPE_DS)
+                               ? NO_PROOF
+                               : KNOT_EDOWNGRADED;
                }
                /* LATER(opt): perhaps short-circuit test if we repeat it here. */
        }
@@ -218,9 +224,12 @@ int kr_nsec_negative(const ranked_rr_array_t *rrrs, uint32_t qry_uid,
                        && kr_rank_test(rrrs->at[i]->rank, KR_RANK_SECURE);
                if (!ok) continue;
                const int covers = nsec_covers(nsec, sname);
-               if (covers == abs(EEXIST)
-                   && no_data_response_check_rrtype(nsec, stype) == 0) {
-                       return PKT_NODATA; // proven NODATA by matching NSEC
+               if (covers == abs(EEXIST)) {
+                       int ret = no_data_response_check_rrtype(nsec, stype);
+                       if (ret == 0)
+                               return PKT_NODATA; // proven NODATA by matching NSEC
+                       if (ret == KNOT_EDOWNGRADED)
+                               return ret;
                }
                if (covers != 0) continue;
 
index a173fa54470f9ffd3298dbd7317b9b85b081ad0d..1981a2038282696298ff4f0e58ed0df1b31c51d8 100644 (file)
@@ -8,6 +8,8 @@
 
 #include "lib/layer/iterate.h"
 
+#define KNOT_EDOWNGRADED (KNOT_ERROR_MIN - 1)
+
 /**
  * Check bitmap that child names are contained in the same zone.
  * @note see RFC6840 4.1.
@@ -25,6 +27,7 @@ int kr_nsec_children_in_zone_check(const uint8_t *bm, uint16_t bm_size);
  * @param owner   NSEC record owner.
  * @note This includes special checks for zone cuts, e.g. from RFC 6840 sec. 4.
  * @return 0, abs(ENOENT) (no proof), kr_error(EINVAL)
+ *   KNOT_EDOWNGRADED: special case where the RR would be in an insecure child zone.
  */
 int kr_nsec_bitmap_nodata_check(const uint8_t *bm, uint16_t bm_size, uint16_t type, const knot_dname_t *owner);
 
@@ -44,6 +47,7 @@ int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t
  * @param rrrs       list of RRs to search; typically kr_request::auth_selected
  * @param qry_uid    only consider NSECs from this packet, for better efficiency
  * @return           negative error code, or PKT_NXDOMAIN | PKT_NODATA (both for NXDOMAIN)
+ *                   KNOT_EDOWNGRADED: special case where the RR would be in an insecure child zone.
  */
 int kr_nsec_negative(const ranked_rr_array_t *rrrs, uint32_t qry_uid,
                        const knot_dname_t *sname, uint16_t stype);
index 4ff2750021f22571d2eb82f83ec67e4052f4b6cd..da1bf72d50e75dfe75f75e521f05dd91e7bb3c83 100644 (file)
@@ -507,6 +507,7 @@ int kr_nsec3_name_error_response_check(const knot_pkt_t *pkt, knot_section_t sec
  * @param sname      Name to be checked.
  * @param stype      Type to be checked.
  * @return           0 or error code.
+ *                   KNOT_EDOWNGRADED: special case where the RR would be in an insecure child zone.
  * @note             This does NOT check the opt-out case if type is DS;
  *                   see RFC 5155 8.6.
  */
@@ -528,8 +529,9 @@ static int nodata_find(const knot_pkt_t *pkt, knot_section_t section_id,
 
                const uint8_t *bm = knot_nsec3_bitmap(nsec3->rrs.rdata);
                uint16_t bm_size = knot_nsec3_bitmap_len(nsec3->rrs.rdata);
-               if (kr_nsec_bitmap_nodata_check(bm, bm_size, type, nsec3->owner) == kr_ok())
-                       return kr_ok();
+               int ret = kr_nsec_bitmap_nodata_check(bm, bm_size, type, nsec3->owner);
+               if (ret == kr_ok() || ret == KNOT_EDOWNGRADED)
+                       return ret;
        }
 
        return kr_error(ENOENT);
@@ -602,8 +604,8 @@ int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
 {
        /* DS record may be also matched by an existing NSEC3 RR. */
        int ret = nodata_find(pkt, section_id, sname, stype);
-       if (ret == 0) {
-               /* Satisfies RFC5155 8.5 and 8.6, both first paragraph. */
+       if (ret == 0 || ret == KNOT_EDOWNGRADED) {
+               /* If 0, satisfies RFC5155 8.5 and 8.6, both first paragraph. */
                return ret;
        }
 
index a28d3c78144d78104efa1aaf072ec94c799fe8ba..1a3a6d60be89a2ca065757f7403aea25477dab8b 100644 (file)
@@ -87,6 +87,8 @@ int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_
  * @return           0 or error code:
  *                   DNSSEC_NOT_FOUND - neither ds nor nsec records
  *                   were not found.
+ *                   KNOT_EDOWNGRADED - special case where
+ *                     the RR would be in an insecure child zone.
  *                   KNOT_ERANGE - denial of existence can't be proven
  *                   due to opt-out, otherwise - bogus.
  */
index 321b0a2541f5ec652010e79424670ff7ac42d6a5..549d99a821e1c3283ce2ac0815699a982b3467cf 100644 (file)
@@ -146,8 +146,6 @@ do_downgrade: // we do this deep inside calls because of having signer name avai
        return true;
 }
 
-#define KNOT_EDOWNGRADED (KNOT_ERROR_MIN - 1)
-
 static int validate_section(kr_rrset_validation_ctx_t *vctx, struct kr_query *qry,
                            knot_mm_t *pool)
 {
@@ -1323,6 +1321,12 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
                                         * we must continue, validate NSEC\NSEC3 and
                                         * call update_parent_keys() to mark
                                         * parent queries as insecure */
+                               } else if (ret == KNOT_EDOWNGRADED) { // either NSEC3 or NSEC
+                                       VERBOSE_MSG(qry, "<= DNSSEC downgraded by a weird proof confusing NODATA with insecure delegation\n");
+                                       qry->flags.DNSSEC_WANT = false;
+                                       qry->flags.DNSSEC_INSECURE = true;
+                                       rank_records(qry, true, KR_RANK_INSECURE, qry->sname);
+                                       mark_insecure_parents(qry);
                                } else {
                                        VERBOSE_MSG(qry, "<= bad NODATA proof\n");
                                        kr_request_set_extended_error(req, KNOT_EDNS_EDE_NSEC_MISS, "AHXI");