]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/dnssec: nsec, nsec3 (no-optout) referrals to unsigned subzones
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Mon, 14 Mar 2016 11:12:21 +0000 (12:12 +0100)
committerMarek Vavrusa <marek@vavrusa.com>
Thu, 14 Apr 2016 04:13:34 +0000 (21:13 -0700)
lib/dnssec/nsec.c
lib/dnssec/nsec.h
lib/dnssec/nsec3.c
lib/dnssec/nsec3.h
lib/layer/validate.c
tests/deckard

index 6f4af5332d6f42dc7971ebb7453e04c481ba0075..7503cfa88b3ac2d185d67708c86f580438fc93b8 100644 (file)
@@ -22,6 +22,7 @@
 #include <libknot/rrset.h>
 #include <libknot/rrtype/nsec.h>
 #include <libknot/rrtype/rrsig.h>
+#include <dnssec/error.h>
 
 #include "lib/defines.h"
 #include "lib/dnssec/nsec.h"
@@ -325,3 +326,66 @@ int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
 
        return kr_nsec_existence_denied(flags) ? kr_ok() : kr_error(ENOENT);
 }
+
+int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt)
+{
+       int nsec_found = 0;
+       uint8_t *bm = NULL;
+       uint16_t bm_size = 0;
+       const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_AUTHORITY);
+       if (!sec) {
+               return kr_error(EINVAL);
+       }
+       for (unsigned i = 0; i < sec->count; ++i) {
+               const knot_rrset_t *ns = knot_pkt_rr(sec, i);
+               if (ns->type == KNOT_RRTYPE_DS) {
+                       return kr_error(EEXIST);
+               }
+               if (ns->type != KNOT_RRTYPE_NS) {
+                       continue;
+               }
+               nsec_found = 0;
+               for (unsigned j = 0; j < sec->count; ++j) {
+                       const knot_rrset_t *nsec = knot_pkt_rr(sec, j);
+                       if (nsec->type == KNOT_RRTYPE_DS) {
+                               return kr_error(EEXIST);
+                       }
+                       if (nsec->type != KNOT_RRTYPE_NSEC) {
+                               continue;
+                       }
+                       /* nsec found
+                        * check if owner name matches the delegation name
+                        */
+                       if (knot_dname_is_equal(nsec->owner, ns->owner)) {
+                               /* nsec does not match the delegation */
+                               continue;
+                       }
+                       nsec_found = 1;
+                       knot_nsec_bitmap(&nsec->rrs, &bm, &bm_size);
+                       if (!bm) {
+                               return kr_error(EINVAL);
+                       }
+                       if (kr_nsec_bitmap_contains_type(bm, bm_size,
+                                                         KNOT_RRTYPE_NS) &&
+                           !kr_nsec_bitmap_contains_type(bm, bm_size,
+                                                         KNOT_RRTYPE_DS) &&
+                           !kr_nsec_bitmap_contains_type(bm, bm_size,
+                                                         KNOT_RRTYPE_SOA)) {
+                               /* rfc4035, 5.2 */
+                               return kr_ok();
+                       }
+               }
+               if (nsec_found) {
+                       /* nsec which owner matches
+                        * the delegation name was found,
+                        * but nsec type bitmap contains wrong types
+                        */
+                       return kr_error(EINVAL);
+               } else {
+                       /* nsec that matches delegation was not found */
+                       return kr_error(DNSSEC_NOT_FOUND);
+               }
+       }
+
+       return kr_error(EINVAL);
+}
index 21ac6e4d307073490a89d12a134351f4e905aa50..c5dba139d418c6476134628b67bfc551f897170a 100644 (file)
@@ -70,3 +70,15 @@ int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t
  */
 int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
                              const knot_dname_t *sname, uint16_t stype);
+
+/**
+ * Referral to unsigned subzone check (RFC4035 5.2).
+ * @note            No RRSIGs are validated.
+ * @param pkt        Packet structure to be processed.
+ * @return           0 or error code:
+ *                  DNSSEC_NOT_FOUND - neither ds nor nsec records
+ *                  were not found.
+ *                  EEXIST - ds record was found.
+ *                  EINVAL - bogus.
+ */
+int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt);
index 71a8949f9c39050e156e99ba01a37c180972c3e0..c1a529fd5a7cf630799661536c4a4ed5918e041c 100644 (file)
@@ -703,3 +703,85 @@ int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
        return ret;
 }
 
+int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
+{
+       int ret = kr_error(EINVAL);
+       int flags = 0;
+       uint8_t *bm = NULL;
+       uint16_t bm_size = 0;
+       const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_AUTHORITY);
+       if (!sec) {
+               return kr_error(EINVAL);
+       }
+       for (unsigned i = 0; i < sec->count; ++i) {
+               const knot_rrset_t *ns = knot_pkt_rr(sec, i);
+               if (ns->type == KNOT_RRTYPE_DS) {
+                       return kr_error(EEXIST);
+               }
+               if (ns->type != KNOT_RRTYPE_NS) {
+                       continue;
+               }
+               flags = 0;
+               for (unsigned j = 0; j < sec->count; ++j) {
+                       const knot_rrset_t *nsec3 = knot_pkt_rr(sec, j);
+                       if (nsec3->type == KNOT_RRTYPE_DS) {
+                               return kr_error(EEXIST);
+                       }
+                       if (nsec3->type != KNOT_RRTYPE_NSEC3) {
+                               continue;
+                       }
+                       /* nsec3 found, check if owner name matches
+                        * the delegation name
+                        */
+                       ret = matches_name(&flags, nsec3, ns->owner);
+                       if (ret != 0) {
+                               return kr_error(EINVAL);
+                       }
+                       if (!(flags & FLG_NAME_MATCHED)) {
+                               /* nsec3 owner name does not match
+                                * the delegation name
+                                */
+                               continue;
+                       }
+                       knot_nsec3_bitmap(&nsec3->rrs, 0, &bm, &bm_size);
+                       if (!bm) {
+                               return kr_error(EINVAL);
+                       }
+                       if (kr_nsec_bitmap_contains_type(bm, bm_size,
+                                                         KNOT_RRTYPE_NS) &&
+                           !kr_nsec_bitmap_contains_type(bm, bm_size,
+                                                         KNOT_RRTYPE_DS) &&
+                           !kr_nsec_bitmap_contains_type(bm, bm_size,
+                                                         KNOT_RRTYPE_SOA)) {
+                               /* Satisfies rfc5155, 8.9. paragraph 2 */
+                               return kr_ok();
+                       }
+               }
+               if (flags & FLG_NAME_MATCHED) {
+                       /* nsec3 which owner matches
+                        * the delegation name was found,
+                        * but nsec3 type bitmap contains wrong types
+                        */
+                       return kr_error(EINVAL);
+               }
+               /* nsec3 that matches the delegation was not found.
+                * Check rfc5155, 8.9. paragraph 4.
+                * Find closest provable encloser.
+                */
+               const knot_dname_t *encloser_name = NULL;
+               const knot_rrset_t *covering_next_nsec3 = NULL;
+               ret = closest_encloser_proof(pkt, KNOT_AUTHORITY, ns->owner, &encloser_name,
+                                     NULL, &covering_next_nsec3);
+               if (ret != 0) {
+                       return kr_error(EINVAL);
+               }
+
+               if (has_optout(covering_next_nsec3)) {
+                       return kr_error(DNSSEC_NOT_FOUND);
+               } else {
+                       return kr_error(EINVAL);
+               }
+       }
+       return kr_error(EINVAL);
+}
+
index 97f36d2a3fe47c2bf33c9cb0903ad8d65c7db735..6ccdcf2e0b1637746bae2270630f83455ef9f647 100644 (file)
@@ -53,3 +53,15 @@ int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_
  */
 int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
                      const knot_dname_t *sname, uint16_t stype);
+
+/**
+ * Referral to unsigned subzone check (RFC5155 8.9).
+ * @note            No RRSIGs are validated.
+ * @param pkt        Packet structure to be processed.
+ * @return           0 or error code:
+ *                  DNSSEC_NOT_FOUND - denial of existence can't be proven
+ *                  due to opt-out.
+ *                  EEXIST - ds record was found.
+ *                  EINVAL - bogus.
+ */
+int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt);
index 40ab2950cd33d8848eb97bdb6b99a7e3f6fe5c60..e745ea22632fffc3e1e4b4a487f7f5f9a9ed2993 100644 (file)
@@ -277,7 +277,6 @@ static int update_delegation(struct kr_request *req, struct kr_query *qry, knot_
         * If it contains neither, the referral is bogus (or an attempted downgrade attack).
         */
 
-       /* Aggregate DS records (if using multiple keys) */
        unsigned section = KNOT_ANSWER;
        if (!knot_wire_get_aa(answer->wire)) { /* Referral */
                section = KNOT_AUTHORITY;
@@ -287,17 +286,30 @@ static int update_delegation(struct kr_request *req, struct kr_query *qry, knot_
                return kr_ok();
        }
 
-       /* No DS provided, check for proof of non-existence. */
        int ret = 0;
        const knot_dname_t *proved_name = knot_pkt_qname(answer);
+       /* Aggregate DS records (if using multiple keys) */
        knot_rrset_t *new_ds = update_ds(cut, knot_pkt_section(answer, section));
        if (!new_ds) {
+               /* No DS provided, check for proof of non-existence. */
                if (!has_nsec3) {
-                       ret = kr_nsec_existence_denial(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS);
+                       if (!knot_wire_get_aa(answer->wire)) {
+                               /* Referral, check if it is referral to unsigned, rfc4035 5.2 */
+                               ret = kr_nsec_ref_to_unsigned(answer);
+                       } else {
+                               /* No-data answer */
+                               ret = kr_nsec_existence_denial(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS);
+                       }
                } else {
-                       ret = kr_nsec3_no_data(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS);
+                       if (!knot_wire_get_aa(answer->wire)) {
+                               /* Referral, check if it is referral to unsigned, rfc5155 8.9 */
+                               ret = kr_nsec3_ref_to_unsigned(answer);
+                       } else {
+                               /* No-data answer, QTYPE is DS, rfc5155 8.6 */
+                               ret = kr_nsec3_no_data(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS);
+                       }
                        if (ret == kr_error(DNSSEC_NOT_FOUND)) {
-                               /* Not bogus, but going insecure */
+                               /* Not bogus, going insecure due to optout */
                                ret = 0;
                        }
                }
index e73a0854552d94e8bc5306e75c3a2229bc739531..8b1a6c6873067d78e88adda48d381142aa107bd4 160000 (submodule)
@@ -1 +1 @@
-Subproject commit e73a0854552d94e8bc5306e75c3a2229bc739531
+Subproject commit 8b1a6c6873067d78e88adda48d381142aa107bd4