]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
layer/validate: NSEC authenticated denial of existence check
authorKarel Slany <karel.slany@nic.cz>
Wed, 5 Aug 2015 16:02:46 +0000 (18:02 +0200)
committerKarel Slany <karel.slany@nic.cz>
Wed, 5 Aug 2015 16:02:46 +0000 (18:02 +0200)
lib/dnssec.c
lib/dnssec.h
lib/dnssec/nsec.c [new file with mode: 0644]
lib/dnssec/nsec.h [new file with mode: 0644]
lib/dnssec/ta.h
lib/layer/validate.c
lib/lib.mk

index 23b7ac177ed103181cc2e04fe7fd48fa70cde3c6..6b9822c512fe2550d810605c13b259ae9a8e15b9 100644 (file)
@@ -94,7 +94,7 @@ static int validate_rrsig_rr(int *flags, const knot_rrset_t *covered,
        {
                int rrsig_labels = knot_rrsig_labels(&rrsigs->rrs, sig_pos);
                int dname_labels = knot_dname_labels(covered->owner, NULL);
-               if ((covered->owner[0] == 1) && (covered->owner[1] == '*')) {
+               if (knot_dname_is_wildcard(covered->owner)) {
                        /* The asterisk does not count, RFC4034 3.1.3, paragraph 3. */
                        --dname_labels;
                }
@@ -144,21 +144,6 @@ static int wildcard_radix_len_diff(const knot_dname_t *expanded,
        return knot_dname_labels(expanded, NULL) - knot_rrsig_labels(&rrsigs->rrs, sig_pos);
 }
 
-/* RFC4035 5.4, bullet 2 */
-static int nsec_nomatch_validate(const knot_rrset_t *nsec, const knot_dname_t *name)
-{
-       const knot_dname_t *next = knot_nsec_next(&nsec->rrs);
-
-       if ((knot_dname_cmp(nsec->owner, name) < 0) &&
-           (knot_dname_cmp(name, next) < 0)) {
-               return kr_ok();
-       } else {
-               return 1;
-       }
-
-#warning TODO: Is an additional request for NSEC name or wildcard necessary?
-}
-
 /**
  * Validates the non-existence of closer/exact match.
  * @param pkt        Packet to be validated.
@@ -186,7 +171,7 @@ static int closer_match_nonexistence_validate(const knot_pkt_t *pkt, knot_sectio
                        continue;
                }
                if (rrset->type == KNOT_RRTYPE_NSEC) {
-                       if (nsec_nomatch_validate(rrset, name) == 0) {
+                       if (kr_nsec_nomatch_validate(rrset, name) == 0) {
                                return kr_ok();
                        }
                } else {
index a23a7560f65ec610ffa712ed44fd073753165d30..77e819ff66b467067fcdc5cc9a2fdc052921f3c5 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <libknot/internal/consts.h>
 #include <libknot/packet/pkt.h>
 
 /**
diff --git a/lib/dnssec/nsec.c b/lib/dnssec/nsec.c
new file mode 100644 (file)
index 0000000..cb97692
--- /dev/null
@@ -0,0 +1,232 @@
+/*  Copyright (C) 2015 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
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+
+#include <libknot/descriptor.h>
+#include <libknot/dname.h>
+#include <libknot/rrset.h>
+#include <libknot/rrtype/nsec.h>
+#include <libknot/rrtype/rrsig.h>
+
+#include "lib/defines.h"
+#include "lib/dnssec/nsec.h"
+
+int kr_nsec_nomatch_validate(const knot_rrset_t *nsec, const knot_dname_t *name)
+{
+       const knot_dname_t *next = knot_nsec_next(&nsec->rrs);
+
+       if ((knot_dname_cmp(nsec->owner, name) < 0) &&
+           (knot_dname_cmp(name, next) < 0)) {
+               return kr_ok();
+       } else {
+               return 1;
+       }
+
+#warning TODO: Is an additional request for NSEC name or wildcard necessary?
+}
+
+#define FLG_NOEXIST_RRTYPE 0x01 /**< RR type has been proven not to exist. */
+#define FLG_NOEXIST_RRSET  0x02 /**< RRSet has been proven not to exist. */
+#define FLG_NOEXIST_WILDCARD 0x03 /**< No cowering wildcard exists. */
+
+/**
+ * Checks whether the given type exists in the supplied NSEC bitmap.
+ * @param nsec NSEC RR.
+ * @param type Type to search for.
+ */
+bool nsec_bitmap_has_type(const knot_rrset_t *nsec, uint16_t type)
+{
+       if (!nsec) {
+               return false;
+       }
+
+       uint8_t *bm;
+       uint16_t bm_size;
+       knot_nsec_bitmap(&nsec->rrs, &bm, &bm_size);
+       if (!bm) {
+               return false;
+       }
+
+       uint8_t sought_win = (type >> 8 ) & 0xff;
+       uint8_t bitmap_idx = (type >> 3) & 0x1f;
+       uint8_t bitmap_bit_mask = 1 << (7 - (type & 0x07));
+
+       size_t bm_pos = 0;
+       while (bm_pos < bm_size) {
+               uint8_t win = bm[bm_pos++];
+               uint8_t win_size = bm[bm_pos++];
+
+               if (win == sought_win) {
+                       if (win_size >= bitmap_idx) {
+                               return bm[bm_pos + bitmap_idx] & bitmap_bit_mask;
+                       }
+                       return false;
+               }
+
+               bm_pos += win_size;
+       }
+
+       return false;
+}
+
+/**
+ * Returns the labels from the covering RRSIG RRs.
+ * @note The number must be the same in all covering RRSIGs.
+ * @param nsec NSEC RR.
+ * @param sec  Packet section.
+ * @param      Number of labels or (negative) error code.
+ */
+static int coverign_rrsig_labels(const knot_rrset_t *nsec, const knot_pktsection_t *sec)
+{
+       assert(nsec && sec);
+
+       int ret = kr_error(ENOENT);
+
+       for (unsigned i = 0; i < sec->count; ++i) {
+               const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
+               if ((rrset->type != KNOT_RRTYPE_RRSIG) ||
+                   (!knot_dname_is_equal(rrset->owner, nsec->owner))) {
+                       continue;
+               }
+
+               for (uint16_t j = 0; j < rrset->rrs.rr_count; ++j) {
+                       if (knot_rrsig_type_covered(&rrset->rrs, j) != KNOT_RRTYPE_NSEC) {
+                               continue;
+                       }
+
+                       if (ret < 0) {
+                               ret = knot_rrsig_labels(&rrset->rrs, j);
+                       } else {
+                               if (ret != knot_rrsig_labels(&rrset->rrs, j)) {
+                                       return kr_error(EINVAL);
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * Perform check of RR type existence denial according to RFC4035 5.4, bullet 1.
+ * @param flags Flags to be set according to check outcome.
+ * @param nsec  NSEC RR.
+ * @param sec   Packet section to work with.
+ * @param type  Type to be checked.
+ */
+static int rr_type_existence_denial(int *flags, const knot_rrset_t *nsec,
+                                    const knot_pktsection_t *sec, uint16_t type)
+{
+       assert(flags && nsec && sec);
+
+       if (nsec_bitmap_has_type(nsec, type)) {
+               return kr_ok();
+       }
+       /* The type is not listed in the NSEC bitmap. */
+       *flags |= FLG_NOEXIST_RRTYPE;
+
+       int rrsig_labels = coverign_rrsig_labels(nsec, sec);
+       if (rrsig_labels < 0) {
+               return rrsig_labels;
+       }
+       int nsec_labels = knot_dname_labels(nsec->owner, NULL);
+       if (nsec_labels < 0) {
+               return nsec_labels;
+       }
+
+       if (rrsig_labels == nsec_labels) {
+               *flags |= FLG_NOEXIST_WILDCARD;
+       }
+
+       return kr_ok();
+}
+
+/**
+ * Perform check of RRSet existence denial according to RFC4035 5.4, bullet 2.
+ * @param flags Flags to be set according to check outcome.
+ * @param nsec  NSEC RR.
+ * @param name  Name to be checked.
+ * @param pool
+ * @return      0 or error code.
+ */
+static int rrset_existence_denial(int *flags, const knot_rrset_t *nsec,
+                                  const knot_dname_t *name, mm_ctx_t *pool)
+{
+       assert(flags && nsec && name);
+
+       if (kr_nsec_nomatch_validate(nsec, name) == 0) {
+               *flags |= FLG_NOEXIST_RRSET;
+               return kr_ok();
+       }
+
+       if (!pool) {
+               return kr_error(EINVAL);
+       }
+
+       knot_dname_t *name_copy = knot_dname_copy(name, pool);
+       if (!name_copy) {
+               return kr_error(ENOMEM);
+       }
+       knot_dname_t *ptr = name_copy;
+       while (ptr[0]) {
+               /* Remove leftmost label and replace it with '*.'. */
+               ptr = (uint8_t *) knot_wire_next_label(ptr, NULL);
+               *(--ptr) = '*';
+               *(--ptr) = 1;
+
+               if (kr_nsec_nomatch_validate(nsec, ptr) == 0) {
+                       *flags |= FLG_NOEXIST_WILDCARD;
+                       break;
+               }
+
+               /* Remove added leftmost asterisk. */
+               ptr += 2;
+       }
+
+       knot_dname_free(&name_copy, pool);
+       return kr_ok();
+}
+
+int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
+                             const knot_dname_t *name, uint16_t type, mm_ctx_t *pool)
+{
+       const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
+       if (!sec) {
+               return kr_error(EINVAL);
+       }
+
+       int ret = kr_error(ENOENT);
+       int flags = 0;
+       for (unsigned i = 0; i < sec->count; ++i) {
+               const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
+               if (rrset->type != KNOT_RRTYPE_NSEC) {
+                       continue;
+               }
+               if (knot_dname_is_equal(rrset->owner, name)) {
+                       rr_type_existence_denial(&flags, rrset, sec, type);
+               } else {
+                       rrset_existence_denial(&flags, rrset, name, pool);
+               }
+       }
+
+       if (((flags & FLG_NOEXIST_RRTYPE) || (flags & FLG_NOEXIST_RRSET)) &&
+           (flags & FLG_NOEXIST_WILDCARD)) {
+               ret = kr_ok();
+       }
+
+       return ret;
+}
diff --git a/lib/dnssec/nsec.h b/lib/dnssec/nsec.h
new file mode 100644 (file)
index 0000000..43d22d6
--- /dev/null
@@ -0,0 +1,42 @@
+/*  Copyright (C) 2015 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
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <libknot/internal/consts.h>
+#include <libknot/internal/mempattern.h>
+#include <libknot/packet/pkt.h>
+
+/**
+ * Check the non-existence of an exact/closer match according to RFC4035 5.4, bullet 2.
+ * @note No RRSIGs are checked.
+ * @param nsec NSEC RRSet containing a single record.
+ * @param name Domain name checked against the NSEC record.
+ * @return     0 or error code.
+ */
+int kr_nsec_nomatch_validate(const knot_rrset_t *nsec, const knot_dname_t *name);
+
+/**
+ * Authenticated denial of existence according to RFC4035 5.4.
+ * @note No RRSIGs are validated.
+ * @param pkt        Packet structure to be processed.
+ * @param section_id Packet section to be processed.
+ * @param name       Queried domain name.
+ * @param type       Queried type.
+ * @return           0 or error code.
+ */
+int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
+                             const knot_dname_t *name, uint16_t type, mm_ctx_t *pool);
index c00cdd49421603cca36e7208cd7b23633997c3d6..cf525059419723a18d55ed7a53816e81c2640d65 100644 (file)
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <libknot/internal/mempattern.h>
 #include <libknot/rrset.h>
 
 //#define ROOT_TA ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"
index e980cba3fea8a7cccca7eaf693d965698be75f6d..b49b558f21d81b999433cb6fd58332c8e9069ed7 100644 (file)
@@ -21,6 +21,7 @@
 #include <libknot/rrtype/rdname.h>
 #include <libknot/rrtype/rrsig.h>
 
+#include "lib/dnssec/nsec.h"
 #include "lib/dnssec/ta.h"
 #include "lib/dnssec.h"
 #include "lib/layer.h"
@@ -185,10 +186,11 @@ static int validate_records(struct kr_query *qry, knot_pkt_t *answer, mm_ctx_t *
        return ret;
 }
 
-static int validate_proof(struct kr_query *qry, knot_pkt_t *answer)
+static int validate_proof(struct kr_query *qry, knot_pkt_t *answer, mm_ctx_t *pool)
 {
 #warning TODO: validate NSECx proof, RRSIGs will be checked later if it matches
-       return kr_ok();
+       int ret = kr_nsec_existence_denial(answer, KNOT_AUTHORITY, qry->sname, qry->stype, pool);
+       return ret;
 }
 
 static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer)
@@ -342,7 +344,7 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 
        /* Validate non-existence proof if not positive answer. */
        if (knot_wire_get_rcode(pkt->wire) == KNOT_RCODE_NXDOMAIN) {
-               ret = validate_proof(qry, pkt);
+               ret = validate_proof(qry, pkt, &req->pool);
                if (ret != 0) {
                        DEBUG_MSG("<= bad NXDOMAIN proof\n");
                        qry->flags |= QUERY_DNSSEC_BOGUS;
@@ -409,6 +411,7 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
                mm_free(qry->parent->zone_cut.pool, qry->parent->zone_cut.name);
                qry->parent->zone_cut.name = knot_dname_copy(qry->zone_cut.trust_anchor->owner, qry->parent->zone_cut.pool);
        }
+
        if ((qtype == KNOT_RRTYPE_DNSKEY) && (qry->parent != NULL) && (qry->parent->zone_cut.key == NULL)) {
                DEBUG_MSG("updating keys in zone cut\n");
                qry->parent->zone_cut.key = knot_rrset_copy(qry->zone_cut.key, qry->parent->zone_cut.pool);
index d016aeecc7bc1325e0b13094af9d23b7c020dd0f..0407fe6b037b428cb93cd829a83a3da86013284a 100644 (file)
@@ -11,6 +11,7 @@ libkres_SOURCES := \
        lib/layer/validate.c   \
        lib/layer/rrcache.c    \
        lib/layer/pktcache.c   \
+       lib/dnssec/nsec.c      \
        lib/dnssec/signature.c \
        lib/dnssec/ta.c        \
        lib/dnssec.c           \
@@ -28,6 +29,8 @@ libkres_HEADERS := \
        lib/generic/set.h      \
        lib/layer.h            \
        lib/kayer/rrset/ds.h   \
+       lib/dnssec/nsec.h      \
+       lib/dnssec/rrtype/ds.h \
        lib/dnssec/signature.h \
        lib/dnssec/ta.h        \
        lib/dnssec.h           \