]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
bugfix: fix answering with opt-outed NSEC3 on empty-non-terminal
authorLibor Peltan <libor.peltan@nic.cz>
Mon, 30 Aug 2021 12:15:32 +0000 (14:15 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Tue, 31 Aug 2021 09:06:35 +0000 (11:06 +0200)
src/knot/nameserver/nsec_proofs.c
src/knot/zone/adjust.c
src/knot/zone/node.h
tests-extra/data/flags.zone
tests-extra/tests/basic/query/test.py

index c23f2863d5367fb433b75acc03841aa55909c598..93e47ffc2121ebac87e7f4becb45895453f22652 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -42,7 +42,7 @@ static bool wildcard_expanded(const zone_node_t *node, const knot_dname_t *qname
  */
 static bool ds_optout(const zone_node_t *node)
 {
-       return node_nsec3_get(node) == NULL && node->flags & NODE_FLAGS_DELEG;
+       return node_nsec3_get(node) == NULL && !(node->flags & NODE_FLAGS_SUBTREE_AUTH);
 }
 
 /*!
index ba53c48884b9f6e4201493fe9cd99fd939aa916b..b5006ad19c7bbdad6667d1753e37aad79f6fc742 100644 (file)
 #include "knot/zone/measure.h"
 #include "libdnssec/error.h"
 
+static bool node_non_dnssec_exists(const zone_node_t *node)
+{
+       assert(node);
+
+       for (uint16_t i = 0; i < node->rrset_count; ++i) {
+               switch (node->rrs[i].type) {
+               case KNOT_RRTYPE_NSEC:
+               case KNOT_RRTYPE_NSEC3:
+               case KNOT_RRTYPE_RRSIG:
+                       continue;
+               default:
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 int adjust_cb_flags(zone_node_t *node, adjust_ctx_t *ctx)
 {
        zone_node_t *parent = node_parent(node);
        uint16_t flags_orig = node->flags;
+       bool set_subt_auth = false;
 
        assert(!(node->flags & NODE_FLAGS_DELETED));
 
-       node->flags &= ~(NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH);
+       node->flags &= ~(NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH | NODE_FLAGS_SUBTREE_AUTH);
 
        if (parent && (parent->flags & NODE_FLAGS_DELEG || parent->flags & NODE_FLAGS_NONAUTH)) {
                node->flags |= NODE_FLAGS_NONAUTH;
        } else if (node_rrtype_exists(node, KNOT_RRTYPE_NS) && node != ctx->zone->apex) {
                node->flags |= NODE_FLAGS_DELEG;
+               if (node_rrtype_exists(node, KNOT_RRTYPE_DS)) {
+                       set_subt_auth = true;
+               }
+       } else if (node_non_dnssec_exists(node)) {
+               set_subt_auth = true;
+       }
+
+       if (set_subt_auth) {
+               node_set_flag_hierarch(node, NODE_FLAGS_SUBTREE_AUTH);
        }
 
        if (node->flags != flags_orig && ctx->changed_nodes != NULL) {
index 42c756170c65a4cc060311e5929fc4dded37d228..045eea534d05726cdd873d9a8a703d87b461b668 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -100,6 +100,8 @@ enum node_flags {
        NODE_FLAGS_SECOND =          1 << 9, // this value shall be fixed
        /*! \brief The node shall be deleted. It's just not because it's a bi-node and the counterpart still exists. */
        NODE_FLAGS_DELETED =         1 << 10,
+       /*! \brief The node or some node in subtree has some authoritative data in it (possibly also DS at deleg). */
+       NODE_FLAGS_SUBTREE_AUTH =    1 << 11,
 };
 
 typedef void (*node_addrem_cb)(zone_node_t *, void *);
@@ -296,6 +298,16 @@ inline static const zone_node_t *glue_node(const glue_t *glue, const zone_node_t
        return binode_node_as((zone_node_t *)glue->node, another_zone_node);
 }
 
+/*!
+ * \brief Add a flag to this node and all (grand-)parents until the flag is present.
+ */
+inline static void node_set_flag_hierarch(zone_node_t *node, uint16_t fl)
+{
+       for (zone_node_t *i = node; i != NULL && (i->flags & fl) != fl; i = node_parent(i)) {
+               i->flags |= fl;
+       }
+}
+
 /*!
  * \brief Checks whether node contains any RRSIG for given type.
  *
index 92b69700072b3d90d32895411696a3518e6b2c2a..01b89506d5ca3c37bb77d8698d5e6acac6fadade 100644 (file)
@@ -23,6 +23,9 @@ sub   NS      sub
 sub    A       192.0.0.1
 ns.sub A       192.0.2.4
 
+; Opt-outable delegation below empty-non-terminal
+deleg.ent      NS      sub
+
 ; CNAME to A record
 cname  CNAME   dns1
 
index 19f8c963126618ff76aafdd89ad9b99051e15873..4dd90e4f1ffbdeef8957f4370047e2d2b5225cf3 100644 (file)
@@ -69,6 +69,11 @@ def query_test(knot, bind, dnssec):
     resp.check(rcode="NOERROR", flags="QR AA", noflags="TC AD RA")
     resp.cmp(bind, additional=True)
 
+    # Positive (NODATA, at delegation, DS type, below empty-non-terminal)
+    resp = knot.dig("deleg.ent.flags", "DS", udp=True, dnssec=dnssec)
+    resp.check(rcode="NOERROR", flags="QR AA", noflags="TC AD RA")
+    resp.cmp(bind, additional=True)
+
     # Positive (REFERRAL, below delegation, DS type)
     resp = knot.dig("net.ds-sub.flags", "DS", udp=True, dnssec=dnssec)
     resp.check(rcode="NOERROR", flags="QR", noflags="AA TC AD RA")
@@ -348,6 +353,15 @@ query_test(knot, bind, False)
 
 knot.dnssec(zone[0]).enable = True
 knot.dnssec(zone[0]).nsec3 = False
+knot.dnssec(zone[0]).nsec3_opt_out = False
+knot.gen_confile()
+knot.reload()
+
+serial = bind.zone_wait(zone, serial)
+query_test(knot, bind, True)
+
+knot.dnssec(zone[0]).nsec3 = True
+knot.dnssec(zone[0]).nsec3_opt_out = False
 knot.gen_confile()
 knot.reload()
 
@@ -355,6 +369,7 @@ serial = bind.zone_wait(zone, serial)
 query_test(knot, bind, True)
 
 knot.dnssec(zone[0]).nsec3 = True
+knot.dnssec(zone[0]).nsec3_opt_out = True
 knot.gen_confile()
 knot.reload()