]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib: forwarding, some improvements in zone cut detection algorythm
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Wed, 31 May 2017 11:57:34 +0000 (13:57 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Thu, 1 Jun 2017 14:27:16 +0000 (16:27 +0200)
lib/dnssec.c
lib/dnssec.h
lib/dnssec/nsec.c
lib/dnssec/nsec.h
lib/dnssec/nsec3.c
lib/dnssec/nsec3.h
lib/layer/validate.c
lib/resolve.c

index d2fcd91e2417a6525d2aa57d8d625fc997b33a09..6ed08a28d9cb100dc507aec59e7a2077ac8c01b9 100644 (file)
@@ -34,6 +34,7 @@
 #include "lib/dnssec/nsec3.h"
 #include "lib/dnssec/signature.h"
 #include "lib/dnssec.h"
+#include "lib/resolve.h"
 
 void kr_crypto_init(void)
 {
@@ -383,3 +384,34 @@ void kr_dnssec_key_free(struct dseckey **key)
        dnssec_key_free((dnssec_key_t *) *key);
        *key = NULL;
 }
+
+int kr_dnssec_matches_name_and_type(const ranked_rr_array_t *rrs, uint32_t qry_uid,
+                                   const knot_dname_t *name, uint16_t type)
+{
+       int ret = kr_error(ENOENT);
+       for (size_t i = 0; i < rrs->len; ++i) {
+               const ranked_rr_array_entry_t *entry = rrs->at[i];
+               const knot_rrset_t *nsec = entry->rr;
+               if (entry->qry_uid != qry_uid || entry->yielded) {
+                       continue;
+               }
+               if (nsec->type != KNOT_RRTYPE_NSEC &&
+                   nsec->type != KNOT_RRTYPE_NSEC3) {
+                       continue;
+               }
+               if (!kr_rank_test(entry->rank, KR_RANK_SECURE)) {
+                       continue;
+               }
+               if (nsec->type == KNOT_RRTYPE_NSEC) {
+                       ret = kr_nsec_matches_name_and_type(nsec, name, type);
+               } else {
+                       ret = kr_nsec3_matches_name_and_type(nsec, name, type);
+               }
+               if (ret == kr_ok()) {
+                       return kr_ok();
+               } else if (ret != kr_error(ENOENT)) {
+                       return ret;
+               }
+       }
+       return ret;
+}
index cab3cf46b935ce0f035b4b447c9155b6992b2864..0bb120fb711163c2a57a92c0254e251bf179f63d 100644 (file)
@@ -138,3 +138,14 @@ int kr_dnssec_key_from_rdata(struct dseckey **key, const knot_dname_t *kown, con
  * @param key Pointer to freed key.
  */
 void kr_dnssec_key_free(struct dseckey **key);
+
+/**
+ * Checks whether NSEC\NSEC3 RR selected by iterator matches the supplied name and type.
+ * @param request Records selected by iterator.
+ * @param qry_uid Query unique identifier where NSEC\NSEC3 belongs to.
+ * @param name  Name to be checked.
+ * @param type  Type to be checked.
+ * @return      0 or error code.
+ */
+int kr_dnssec_matches_name_and_type(const ranked_rr_array_t *rrs, uint32_t qry_uid,
+                                   const knot_dname_t *name, uint16_t type);
index cff0ab43125cacf544b20c47d14776d9b7a23640..56d6aa271a5222925851bb54b4880bc769d862d6 100644 (file)
@@ -462,3 +462,24 @@ int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt)
 
        return kr_error(EINVAL);
 }
+
+int kr_nsec_matches_name_and_type(const knot_rrset_t *nsec,
+                                  const knot_dname_t *name, uint16_t type)
+{
+       if (!nsec || !name) {
+               return (EINVAL);
+       }
+       if (!knot_dname_is_equal(nsec->owner, name)) {
+               return (ENOENT);
+       }
+       uint8_t *bm = NULL;
+       uint16_t bm_size = 0;
+       knot_nsec_bitmap(&nsec->rrs, &bm, &bm_size);
+       if (!bm) {
+               return kr_error(EINVAL);
+       }
+       if (!kr_nsec_bitmap_contains_type(bm, bm_size, type)) {
+               return (ENOENT);
+       }
+       return kr_ok();
+}
index fa09f828d64996c7a4647c8cd274c56944caf37c..0c4f23d0fc3ebcf0f7642e6067a11a0783f7c362 100644 (file)
@@ -82,3 +82,13 @@ int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id,
  *                  EINVAL - bogus.
  */
 int kr_nsec_ref_to_unsigned(const knot_pkt_t *pkt);
+
+/**
+ * Checks whether supplied NSEC RR matches the supplied name and type.
+ * @param nsec  NSEC RR.
+ * @param name  Name to be checked.
+ * @param type  Type to be checked.
+ * @return      0 or error code.
+ */
+int kr_nsec_matches_name_and_type(const knot_rrset_t *nsec,
+                                  const knot_dname_t *name, uint16_t type);
index 5a201353e71ed2c5cdadc6b777a3931cae6a666f..14029297b35b5a573be7e58113fc4e1eac63d257 100644 (file)
@@ -818,3 +818,14 @@ int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
        return kr_error(EINVAL);
 }
 
+int kr_nsec3_matches_name_and_type(const knot_rrset_t *nsec3,
+                                  const knot_dname_t *name, uint16_t type)
+{
+       int flags = 0;
+       int ret = matches_name_and_type(&flags, nsec3, name, type);
+       if (ret != kr_ok()) {
+               return ret;
+       }
+       return ((flags & (FLG_NAME_MATCHED | FLG_TYPE_BIT_MISSING)) != FLG_NAME_MATCHED) ?
+              kr_error(ENOENT) : kr_ok();
+}
index 24f1823366166e3de51b4dcb6e6c0b1e5286d5cc..b07af7e533e8f8bc5023293e3fdc6eead17a5c8c 100644 (file)
@@ -70,3 +70,13 @@ int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
  *                   EINVAL - bogus.
  */
 int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt);
+
+/**
+ * Checks whether supplied NSEC3 RR matches the supplied name and type.
+ * @param nsec3 NSEC3 RR.
+ * @param name  Name to be checked.
+ * @param type  Type to be checked.
+ * @return      0 or error code.
+ */
+int kr_nsec3_matches_name_and_type(const knot_rrset_t *nsec3,
+                                  const knot_dname_t *name, uint16_t type);
index 8e20444f3990cd9302aa495cec801b96cb8def06..8cf8808ab4c69b42b98779761c7638c272aa8a87 100644 (file)
@@ -289,8 +289,9 @@ static void mark_insecure_parents(const struct kr_query *qry)
        }
 }
 
-static int update_parent_keys(struct kr_query *qry, uint16_t answer_type)
+static int update_parent_keys(struct kr_request *req, uint16_t answer_type)
 {
+       struct kr_query *qry = req->current_query;
        struct kr_query *parent = qry->parent;
        assert(parent);
        switch(answer_type) {
@@ -306,7 +307,22 @@ static int update_parent_keys(struct kr_query *qry, uint16_t answer_type)
                if (qry->flags & (QUERY_DNSSEC_INSECURE)) { /* DS non-existence proven. */
                        mark_insecure_parents(qry);
                } else if ((qry->flags & (QUERY_DNSSEC_NODS | QUERY_FORWARD)) == QUERY_DNSSEC_NODS) {
-                       mark_insecure_parents(qry);
+                       if (qry->flags & QUERY_DNSSEC_OPTOUT) {
+                               mark_insecure_parents(qry);
+                       } else {
+                               int ret = kr_dnssec_matches_name_and_type(&req->auth_selected, qry->uid,
+                                                                         qry->sname, KNOT_RRTYPE_NS);
+                               if (ret == kr_ok()) {
+                                       mark_insecure_parents(qry);
+                               }
+                       }
+               } else if ((qry->flags & (QUERY_DNSSEC_NODS | QUERY_FORWARD | QUERY_DNSSEC_OPTOUT)) ==
+                          (QUERY_DNSSEC_NODS | QUERY_FORWARD)) {
+                       int ret = kr_dnssec_matches_name_and_type(&req->auth_selected, qry->uid,
+                                                                 qry->sname, KNOT_RRTYPE_NS);
+                       if (ret == kr_ok()) {
+                               mark_insecure_parents(qry);
+                       }
                } else { /* DS existence proven. */
                        parent->zone_cut.trust_anchor = knot_rrset_copy(qry->zone_cut.trust_anchor, parent->zone_cut.pool);
                        if (!parent->zone_cut.trust_anchor) {
@@ -555,16 +571,22 @@ static int unsigned_forward(kr_layer_t *ctx, knot_pkt_t *pkt)
        const uint16_t qtype = knot_pkt_qtype(pkt);
        const uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire);
        bool nods = false;
+       bool ns_exist = true;
        for (int i = 0; i < req->rplan.resolved.len; ++i) {
                struct kr_query *q = req->rplan.resolved.at[i];
                if (q->sclass == qry->sclass &&
                    q->stype == KNOT_RRTYPE_DS &&
                    knot_dname_is_equal(q->sname, qry->sname)) {
                        nods = true;
+                       if (!(q->flags & QUERY_DNSSEC_OPTOUT)) {
+                               int ret = kr_dnssec_matches_name_and_type(&req->auth_selected, q->uid,
+                                                                         qry->sname, KNOT_RRTYPE_NS);
+                               ns_exist = (ret == kr_ok());
+                       }
                }
        }
 
-       if (nods && qtype == KNOT_RRTYPE_NS && !(qry->flags & QUERY_CNAME)) {
+       if (nods && ns_exist && qtype == KNOT_RRTYPE_NS && !(qry->flags & QUERY_CNAME)) {
                qry->flags &= ~QUERY_DNSSEC_WANT;
                qry->flags |= QUERY_DNSSEC_INSECURE;
                if (qry->forward_flags & QUERY_CNAME) {
@@ -593,6 +615,10 @@ static int unsigned_forward(kr_layer_t *ctx, knot_pkt_t *pkt)
                return KR_STATE_DONE;
        }
 
+       if (ctx->state == KR_STATE_YIELD) {
+               return KR_STATE_DONE;
+       }
+
        if (!nods && qtype != KNOT_RRTYPE_DS) {
                struct kr_rplan *rplan = &req->rplan;
                struct kr_query *next = kr_rplan_push(rplan, qry, qry->sname, qry->sclass, KNOT_RRTYPE_DS);
@@ -742,6 +768,7 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
        int ret = 0;
        struct kr_request *req = ctx->req;
        struct kr_query *qry = req->current_query;
+
        /* Ignore faulty or unprocessed responses. */
        if (ctx->state & (KR_STATE_FAIL|KR_STATE_CONSUME)) {
                return ctx->state;
@@ -959,7 +986,7 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
        }
        /* Update parent query zone cut */
        if (qry->parent) {
-               if (update_parent_keys(qry, qtype) != 0) {
+               if (update_parent_keys(req, qtype) != 0) {
                        return KR_STATE_FAIL;
                }
        }
index 7e3d817191ba985a4378dbed80060f6fa6c90691..0dee8c461366b2d1b816a842bc1764d02c272a0b 100644 (file)
@@ -27,6 +27,7 @@
 #include "lib/rplan.h"
 #include "lib/layer/iterate.h"
 #include "lib/dnssec/ta.h"
+#include "lib/dnssec.h"
 #if defined(ENABLE_COOKIES)
 #include "lib/cookies/control.h"
 #include "lib/cookies/helper.h"
@@ -982,6 +983,7 @@ static int forward_trust_chain_check(struct kr_request *request, struct kr_query
        bool nods = false;
        bool ds_req = false;
        bool ns_req = false;
+       bool ns_exist = true;
        bool minimized = false;
        int name_offset = 1;
        do {
@@ -990,6 +992,7 @@ static int forward_trust_chain_check(struct kr_request *request, struct kr_query
                ds_req = false;
                ns_req = false;
                minimized = false;
+               ns_exist = true;
 
                int cut_labels = knot_dname_labels(qry->zone_cut.name, NULL);
                int wanted_name_labels = knot_dname_labels(wanted_name, NULL);
@@ -1010,13 +1013,18 @@ static int forward_trust_chain_check(struct kr_request *request, struct kr_query
                                        if (qry->flags & QUERY_DNSSEC_NODS) {
                                                nods = true;
                                        }
+                                       if (!(q->flags & QUERY_DNSSEC_OPTOUT)) {
+                                               int ret = kr_dnssec_matches_name_and_type(&request->auth_selected, q->uid,
+                                                                                         wanted_name, KNOT_RRTYPE_NS);
+                                               ns_exist = (ret == kr_ok());
+                                       }
                                } else {
                                        ns_req = true;
                                }
                        }
                }
 
-               if (ds_req && !ns_req && (minimized || resume)) {
+               if (ds_req && ns_exist && !ns_req && (minimized || resume)) {
                        struct kr_query *next = zone_cut_subreq(rplan, qry, wanted_name,
                                                                KNOT_RRTYPE_NS);
                        if (!next) {
@@ -1036,7 +1044,7 @@ static int forward_trust_chain_check(struct kr_request *request, struct kr_query
                        nods = ds_req;
                }
                name_offset += 1;
-       } while (ds_req && ns_req && minimized);
+       } while (ds_req && (ns_req || !ns_exist) && minimized);
 
        /* Disable DNSSEC if it enters NTA. */
        if (kr_ta_get(negative_anchors, wanted_name)){