From: Grigorii Demidov Date: Wed, 31 May 2017 11:57:34 +0000 (+0200) Subject: lib: forwarding, some improvements in zone cut detection algorythm X-Git-Tag: 1.3.0-rc1~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a42585eff4d1d8e14ddaa76702d7385ac34c1751;p=thirdparty%2Fknot-resolver.git lib: forwarding, some improvements in zone cut detection algorythm --- diff --git a/lib/dnssec.c b/lib/dnssec.c index d2fcd91e2..6ed08a28d 100644 --- a/lib/dnssec.c +++ b/lib/dnssec.c @@ -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; +} diff --git a/lib/dnssec.h b/lib/dnssec.h index cab3cf46b..0bb120fb7 100644 --- a/lib/dnssec.h +++ b/lib/dnssec.h @@ -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); diff --git a/lib/dnssec/nsec.c b/lib/dnssec/nsec.c index cff0ab431..56d6aa271 100644 --- a/lib/dnssec/nsec.c +++ b/lib/dnssec/nsec.c @@ -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(); +} diff --git a/lib/dnssec/nsec.h b/lib/dnssec/nsec.h index fa09f828d..0c4f23d0f 100644 --- a/lib/dnssec/nsec.h +++ b/lib/dnssec/nsec.h @@ -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); diff --git a/lib/dnssec/nsec3.c b/lib/dnssec/nsec3.c index 5a201353e..14029297b 100644 --- a/lib/dnssec/nsec3.c +++ b/lib/dnssec/nsec3.c @@ -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(); +} diff --git a/lib/dnssec/nsec3.h b/lib/dnssec/nsec3.h index 24f182336..b07af7e53 100644 --- a/lib/dnssec/nsec3.h +++ b/lib/dnssec/nsec3.h @@ -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); diff --git a/lib/layer/validate.c b/lib/layer/validate.c index 8e20444f3..8cf8808ab 100644 --- a/lib/layer/validate.c +++ b/lib/layer/validate.c @@ -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; } } diff --git a/lib/resolve.c b/lib/resolve.c index 7e3d81719..0dee8c461 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -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)){