]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
dnssec: wildcard answer proof
authorGrigorii Demidov <grigorii.demidov@nic.cz>
Thu, 31 Mar 2016 16:41:08 +0000 (18:41 +0200)
committerMarek Vavrusa <marek@vavrusa.com>
Thu, 14 Apr 2016 05:24:31 +0000 (22:24 -0700)
lib/dnssec.c
lib/dnssec.h
lib/layer/iterate.c
lib/layer/pktcache.c
lib/layer/rrcache.c
lib/layer/validate.c
lib/rplan.h

index 91680adbf82880801de0ea39a64503c457821cee..28ed8fb5db6c4817b33fcefde8106f53fbd4f995 100644 (file)
@@ -129,17 +129,17 @@ static int wildcard_radix_len_diff(const knot_dname_t *expanded,
        return knot_dname_labels(expanded, NULL) - knot_rrsig_labels(&rrsigs->rrs, sig_pos);
 }
 
-int kr_rrset_validate(const knot_pkt_t *pkt, knot_section_t section_id,
-                      const knot_rrset_t *covered, const knot_rrset_t *keys,
-                      const knot_dname_t *zone_name, uint32_t timestamp,
-                      bool has_nsec3)
+int kr_rrset_validate(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *covered)
 {
-       if (!pkt || !covered || !keys || !zone_name) {
+       if (!vctx) {
+               return kr_error(EINVAL);
+       }
+       if (!vctx->pkt || !covered || !vctx->keys || !vctx->zone_name) {
                return kr_error(EINVAL);
        }
 
-       for (unsigned i = 0; i < keys->rrs.rr_count; ++i) {
-               int ret = kr_rrset_validate_with_key(pkt, section_id, covered, keys, i, NULL, zone_name, timestamp, has_nsec3);
+       for (unsigned i = 0; i < vctx->keys->rrs.rr_count; ++i) {
+               int ret = kr_rrset_validate_with_key(vctx, covered, i, NULL);
                if (ret == 0) {
                        return ret;
                }
@@ -148,19 +148,24 @@ int kr_rrset_validate(const knot_pkt_t *pkt, knot_section_t section_id,
        return kr_error(ENOENT);
 }
 
-int kr_rrset_validate_with_key(const knot_pkt_t *pkt, knot_section_t section_id,
-                               const knot_rrset_t *covered, const knot_rrset_t *keys,
-                               size_t key_pos, const struct dseckey *key,
-                               const knot_dname_t *zone_name, uint32_t timestamp,
-                               bool has_nsec3)
+int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx,
+                               const knot_rrset_t *covered,
+                               size_t key_pos, const struct dseckey *key)
 {
+       const knot_pkt_t *pkt         = vctx->pkt;
+       knot_section_t section_id     = vctx->section_id;
+       const knot_rrset_t *keys      = vctx->keys;
+       const knot_dname_t *zone_name = vctx->zone_name;
+       uint32_t timestamp            = vctx->timestamp;
+       bool has_nsec3                = vctx->has_nsec3;
        struct dseckey *created_key = NULL;
        if (key == NULL) {
                const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, key_pos);
                int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner,
                                               knot_rdata_data(krr), knot_rdata_rdlen(krr));
                if (ret != 0) {
-                       return ret;
+                       vctx->result = ret;
+                       return vctx->result;
                }
                key = created_key;
        }
@@ -211,21 +216,72 @@ int kr_rrset_validate_with_key(const knot_pkt_t *pkt, knot_section_t section_id,
                                if (ret != 0) {
                                        continue;
                                }
+                               vctx->flags |= KR_DNSSEC_VFLG_WEXPAND;
                        }
                        /* Validated with current key, OK */
                        kr_dnssec_key_free(&created_key);
-                       return kr_ok();
+                       vctx->result = kr_ok();
+                       return vctx->result;
                }
        }
        /* No applicable key found, cannot be validated. */
        kr_dnssec_key_free(&created_key);
-       return kr_error(ENOENT);
+       vctx->result = kr_error(ENOENT);
+       return vctx->result;
 }
 
-int kr_dnskeys_trusted(const knot_pkt_t *pkt, knot_section_t section_id, const knot_rrset_t *keys,
-                       const knot_rrset_t *ta, const knot_dname_t *zone_name, uint32_t timestamp,
-                       bool has_nsec3)
+int kr_section_check_wcard(kr_rrset_validation_ctx_t *vctx)
 {
+       const knot_pkt_t *pkt         = vctx->pkt;
+       knot_section_t section_id     = vctx->section_id;
+       const knot_dname_t *zone_name = vctx->zone_name;
+       const knot_pktsection_t *sec  = knot_pkt_section(pkt, section_id);
+       for (unsigned i = 0; i < sec->count; ++i) {
+               const knot_rrset_t *rr = knot_pkt_rr(sec, i);
+               if (rr->type == KNOT_RRTYPE_RRSIG) {
+                       continue;
+               }
+               if ((rr->type == KNOT_RRTYPE_NS) && (vctx->section_id == KNOT_AUTHORITY)) {
+                       continue;
+               }
+               if (!knot_dname_in(zone_name, rr->owner)) {
+                       continue;
+               }
+               int covered_labels = knot_dname_labels(rr->owner, NULL);
+               if (knot_dname_is_wildcard(rr->owner)) {
+                       /* The asterisk does not count, RFC4034 3.1.3, paragraph 3. */
+                       --covered_labels;
+               }
+               for (unsigned j = 0; j < sec->count; ++j) {
+                       const knot_rrset_t *rrsig = knot_pkt_rr(sec, j);
+                       if (rrsig->type != KNOT_RRTYPE_RRSIG) {
+                               continue;
+                       }
+                       if ((rr->rclass != rrsig->rclass) || !knot_dname_is_equal(rr->owner, rrsig->owner)) {
+                               continue;
+                       }
+                       for (uint16_t k = 0; k < rrsig->rrs.rr_count; ++k) {
+                               if (knot_rrsig_type_covered(&rrsig->rrs, k) != rr->type) {
+                                       continue;
+                               }
+                               int rrsig_labels = knot_rrsig_labels(&rrsig->rrs, k);
+                               if (rrsig_labels > covered_labels) {
+                                       return kr_error(EINVAL);
+                               }
+                               if (rrsig_labels < covered_labels) {
+                                       vctx->flags |= KR_DNSSEC_VFLG_WEXPAND;
+                               }
+                       }
+               }
+       }
+       return kr_ok();
+}
+
+int kr_dnskeys_trusted(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *ta)
+{
+       const knot_pkt_t *pkt         = vctx->pkt;
+       const knot_rrset_t *keys      = vctx->keys;
+
        if (!pkt || !keys || !ta) {
                return kr_error(EINVAL);
        }
@@ -250,15 +306,17 @@ int kr_dnskeys_trusted(const knot_pkt_t *pkt, knot_section_t section_id, const k
                        kr_dnssec_key_free(&key);
                        continue;
                }
-               if (kr_rrset_validate_with_key(pkt, section_id, keys, keys, i, key, zone_name, timestamp, has_nsec3) != 0) {
+               if (kr_rrset_validate_with_key(vctx, keys, i, key) != 0) {
                        kr_dnssec_key_free(&key);
                        continue;
                }
                kr_dnssec_key_free(&key);
-               return kr_ok();
+               assert (vctx->result == 0);
+               return vctx->result;
        }
        /* No useable key found */
-       return kr_error(ENOENT);
+       vctx->result = kr_error(ENOENT);
+       return vctx->result;
 }
 
 bool kr_dnssec_key_zsk(const uint8_t *dnskey_rdata)
index b6d41df969d326428155e4c0064ed20d03aea731..17a80306f92123313b6de10c843b46364f3270c2 100644 (file)
@@ -41,55 +41,50 @@ void kr_crypto_reinit(void);
 /** Opaque DNSSEC key pointer. */
 struct dseckey;
 
+#define KR_DNSSEC_VFLG_WEXPAND 0x01
+
+/** DNSSEC validation context. */
+struct kr_rrset_validation_ctx {
+       const knot_pkt_t *pkt;          /*!< Packet to be validated. */
+       knot_section_t section_id;      /*!< Section to work with. */
+       const knot_rrset_t *keys;       /*!< DNSKEY RRSet. */
+        const knot_dname_t *zone_name; /*!< Name of the zone containing the RRSIG RRSet. */
+       uint32_t timestamp;             /*!< Validation time. */
+        bool has_nsec3;                        /*!< Whether to use NSEC3 validation. */
+       uint32_t flags;                 /*!< Output - Flags. */
+       int result;                     /*!< Output - 0 or error code. */
+};
+
+typedef struct kr_rrset_validation_ctx kr_rrset_validation_ctx_t;
+
 /**
  * Validate RRSet.
- * @param pkt        Packet to be validated.
- * @param section_id Section to work with.
- * @param covered    RRSet covered by a signature. It must be in canonical format.
- * @param keys       DNSKEY RRSet.
- * @param zone_name  Name of the zone containing the RRSIG RRSet.
- * @param timestamp  Validation time.
- * @param has_nsec3  Whether to use NSEC3 validation.
- * @return           0 or error code.
+ * @param vctx    Pointer to validation context.
+ * @param covered RRSet covered by a signature. It must be in canonical format.
+ * @return        0 or error code, same as vctx->result.
  */
-int kr_rrset_validate(const knot_pkt_t *pkt, knot_section_t section_id,
-                      const knot_rrset_t *covered, const knot_rrset_t *keys,
-                      const knot_dname_t *zone_name, uint32_t timestamp,
-                      bool has_nsec3);
+int kr_rrset_validate(kr_rrset_validation_ctx_t *vctx,
+                       const knot_rrset_t *covered);
 
 /**
  * Validate RRSet using a specific key.
- * @param pkt        Packet to be validated.
- * @param section_id Section to work with.
- * @param covered    RRSet covered by a signature. It must be in canonical format.
- * @param keys       DNSKEY RRSet.
- * @param key_pos    Position of the key to be validated with.
- * @param key        Key to be used to validate. If NULL, then key from DNSKEY RRSet is used.
- * @param zone_name  Name of the zone containing the RRSIG RRSet.
- * @param timestamp  Validation time.
- * @param has_nsec3  Whether to use NSEC3 validation.
- * @return           0 or error code.
+ * @param vctx    Pointer to validation context.
+ * @param covered RRSet covered by a signature. It must be in canonical format.
+ * @param key_pos Position of the key to be validated with.
+ * @param key     Key to be used to validate.
+ *               If NULL, then key from DNSKEY RRSet is used.
+ * @return        0 or error code, same as vctx->result.
  */
-int kr_rrset_validate_with_key(const knot_pkt_t *pkt, knot_section_t section_id,
-                               const knot_rrset_t *covered, const knot_rrset_t *keys,
-                               size_t key_pos, const struct dseckey *key,
-                               const knot_dname_t *zone_name, uint32_t timestamp,
-                               bool has_nsec3);
-
+int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx,
+                               const knot_rrset_t *covered,
+                               size_t key_pos, const struct dseckey *key);
 /**
  * Check whether the DNSKEY rrset matches the supplied trust anchor RRSet.
- * @param pkt        Packet to be validated.
- * @param section_id Section to work with.
- * @param keys       DNSKEY RRSet to check.
- * @param ta         Trust anchor RRSet against which to validate the DNSKEY RRSet.
- * @param zone_name  Name of the zone containing the RRSet.
- * @param timestamp  Time stamp.
- * @param has_nsec3  Whether to use NSEC3 validation.
- * @return     0 or error code.
+ * @param vctx Pointer to validation context.
+ * @param ta   Trust anchor RRSet against which to validate the DNSKEY RRSet.
+ * @return     0 or error code, same as vctx->result.
  */
-int kr_dnskeys_trusted(const knot_pkt_t *pkt, knot_section_t section_id, const knot_rrset_t *keys,
-                       const knot_rrset_t *ta, const knot_dname_t *zone_name, uint32_t timestamp,
-                       bool has_nsec3);
+int kr_dnskeys_trusted(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *ta);
 
 /** Return true if the DNSKEY can be used as a ZSK.  */
 KR_EXPORT KR_PURE
@@ -124,6 +119,14 @@ KR_EXPORT KR_PURE
 int kr_dnssec_key_match(const uint8_t *key_a_rdata, size_t key_a_rdlen,
                         const uint8_t *key_b_rdata, size_t key_b_rdlen);
 
+/** Return 0 if wildcard expansion occurs in specified section.
+ * @param vctx Pointer to validation context.
+ * @note vctx->keys, vctx->timestamp, vctx->has_nsec3 has no meanings.
+ * @return 0 if wildcard expansion occurs or an error code.
+ */
+KR_EXPORT KR_PURE
+int kr_section_check_wcard(kr_rrset_validation_ctx_t *vctx);
+
 /**
  * Construct a DNSSEC key.
  * @param key   Pointer to be set to newly created DNSSEC key.
index cb2ab6b460e72dd383edb217bd59a789daa3377b..773e418c8f4e5f894d1b4932b5bfb22b99961a8e 100644 (file)
@@ -315,7 +315,7 @@ static void finalize_answer(knot_pkt_t *pkt, struct kr_query *qry, struct kr_req
        const uint16_t qtype = knot_pkt_qtype(answer);
        struct kr_zonecut *cut = &qry->zone_cut;
        int pkt_class = kr_response_classify(pkt);
-       if (pkt_class & (PKT_NXDOMAIN|PKT_NODATA)) {
+       if ((pkt_class & (PKT_NXDOMAIN|PKT_NODATA))) {
                const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
                for (unsigned i = 0; i < ns->count; ++i) {
                        const knot_rrset_t *rr = knot_pkt_rr(ns, i);
@@ -414,17 +414,31 @@ static int process_answer(knot_pkt_t *pkt, struct kr_request *req)
        query->flags |= QUERY_RESOLVED;
        /* Follow canonical name as next SNAME. */
        if (!knot_dname_is_equal(cname, query->sname)) {
-               DEBUG_MSG("<= cname chain, following\n");
-               /* Check if already resolved */
-               if (cname && !knot_dname_is_equal(cname, query->sname)) {
-                       for (int i = 0; i < req->rplan.resolved.len; ++i) {
-                               struct kr_query * q = req->rplan.resolved.at[i];
-                               if (q->sclass == query->sclass &&
-                                   q->stype == query->stype   &&
-                                   knot_dname_is_equal(q->sname, cname)) {
-                                       DEBUG_MSG("<= cname chain loop\n");
-                                       return KNOT_STATE_FAIL;
+               /* Check if target record has been already copied */
+               if (is_final) {
+                       const knot_pktsection_t *an = knot_pkt_section(req->answer, KNOT_ANSWER);
+                       for (unsigned i = 0; i < an->count; ++i) {
+                               const knot_rrset_t *rr = knot_pkt_rr(an, i);
+                               if (!knot_dname_is_equal(rr->owner, cname)) {
+                                       continue;
+                               }
+                               if ((rr->rclass != query->sclass) ||
+                                   (rr->type != query->stype)) {
+                                       continue;
                                }
+                               finalize_answer(pkt, query, req);
+                               return KNOT_STATE_DONE;
+                       }
+               }
+               DEBUG_MSG("<= cname chain, following\n");
+               /* Check if the same query was already resolved */
+               for (int i = 0; i < req->rplan.resolved.len; ++i) {
+                       struct kr_query * q = req->rplan.resolved.at[i];
+                       if (q->sclass == query->sclass &&
+                           q->stype == query->stype   &&
+                           knot_dname_is_equal(q->sname, cname)) {
+                               DEBUG_MSG("<= cname chain loop\n");
+                               return KNOT_STATE_FAIL;
                        }
                }
                struct kr_query *next = kr_rplan_push(&req->rplan, query->parent, cname, query->sclass, query->stype);
index b9b6db957fa3d64f6a72322dd633be8ed9fab543..c047951e6fdf6fe4d8702990683245f1c29156e2 100644 (file)
@@ -171,11 +171,14 @@ static int pktcache_stash(knot_layer_t *ctx, knot_pkt_t *pkt)
        if (!knot_wire_get_aa(pkt->wire) || knot_pkt_qclass(pkt) != KNOT_CLASS_IN) {
                return ctx->state;
        }
-       /* Cache only NODATA/NXDOMAIN or metatype/RRSIG answers. */
+       /* Cache only NODATA/NXDOMAIN or metatype/RRSIG or
+         * wildcard expanded answers. */
        const uint16_t qtype = knot_pkt_qtype(pkt);
-       const bool is_eligible = (knot_rrtype_is_metatype(qtype) || qtype == KNOT_RRTYPE_RRSIG);
+       const bool is_eligible = (knot_rrtype_is_metatype(qtype) ||
+                                 qtype == KNOT_RRTYPE_RRSIG);
        int pkt_class = kr_response_classify(pkt);
-       if (!(is_eligible || (pkt_class & (PKT_NODATA|PKT_NXDOMAIN)))) {
+       if (!(is_eligible || (pkt_class & (PKT_NODATA|PKT_NXDOMAIN)) ||
+           (qry->flags & QUERY_DNSSEC_WEXPAND))) {
                return ctx->state;
        }
        uint32_t ttl = packet_ttl(pkt);
index 7226bfc5102e1b99f8797e311c5fa7e89fb247e8..d75dff3284d1961418ba82ce11c4e31af0e20ab9 100644 (file)
@@ -302,6 +302,12 @@ static int rrcache_stash(knot_layer_t *ctx, knot_pkt_t *pkt)
        if (knot_wire_get_tc(pkt->wire)) {
                return ctx->state;
        }
+       /* Do not cache wildcard expanded anwsers,
+        * as they must deal with packet cache */
+       if (qry->flags & QUERY_DNSSEC_WEXPAND) {
+               return ctx->state;
+       }
+
        /* Cache only positive answers, not meta types or RRSIG. */
        const uint16_t qtype = knot_pkt_qtype(pkt);
        const bool is_eligible = !(knot_rrtype_is_metatype(qtype) || qtype == KNOT_RRTYPE_RRSIG);
@@ -351,7 +357,6 @@ static int rrcache_stash(knot_layer_t *ctx, knot_pkt_t *pkt)
                        }
                }
        }
-       
        return ctx->state;
 }
 
index e745ea22632fffc3e1e4b4a487f7f5f9a9ed2993..a587e1f744067b6ea70ba9611cc4be8631d82d91 100644 (file)
@@ -74,37 +74,25 @@ static bool pkt_has_type(const knot_pkt_t *pkt, uint16_t type)
        return section_has_type(knot_pkt_section(pkt, KNOT_ADDITIONAL), type);
 }
 
-
-/** @internal Baton for validate_section */
-struct validate_baton {
-       const knot_pkt_t *pkt;
-       knot_section_t section_id;
-       const knot_rrset_t *keys;
-       const knot_dname_t *zone_name;
-       uint32_t timestamp;
-       bool has_nsec3;
-       int result;
-};
-
 static int validate_rrset(const char *key, void *val, void *data)
 {
        knot_rrset_t *rr = val;
-       struct validate_baton *baton = data;
-
-       if (baton->result != 0) {
-               return baton->result;
+       kr_rrset_validation_ctx_t *vctx = data;
+       if (vctx->result != 0) {
+               return vctx->result;
        }
-       baton->result = kr_rrset_validate(baton->pkt, baton->section_id, rr,
-                                         baton->keys, baton->zone_name,
-                                         baton->timestamp, baton->has_nsec3);
-       return baton->result;
+
+       return kr_rrset_validate(vctx, rr);
 }
 
-static int validate_section(struct kr_query *qry, knot_pkt_t *answer,
-                            knot_section_t section_id, knot_mm_t *pool,
-                            bool has_nsec3)
+static int validate_section(kr_rrset_validation_ctx_t *vctx, knot_mm_t *pool)
 {
-       const knot_pktsection_t *sec = knot_pkt_section(answer, section_id);
+       if (!vctx) {
+               return kr_error(EINVAL);
+       }
+
+       const knot_pktsection_t *sec = knot_pkt_section(vctx->pkt,
+                                                       vctx->section_id);
        if (!sec) {
                return kr_ok();
        }
@@ -122,11 +110,11 @@ static int validate_section(struct kr_query *qry, knot_pkt_t *answer,
                if (rr->type == KNOT_RRTYPE_RRSIG) {
                        continue;
                }
-               if ((rr->type == KNOT_RRTYPE_NS) && (section_id == KNOT_AUTHORITY)) {
+               if ((rr->type == KNOT_RRTYPE_NS) && (vctx->section_id == KNOT_AUTHORITY)) {
                        continue;
                }
                /* Only validate answers from current cut, records above the cut are stripped. */
-               if (!knot_dname_in(qry->zone_cut.name, rr->owner)) {
+               if (!knot_dname_in(vctx->zone_name, rr->owner)) {
                        continue;
                }
                ret = kr_rrmap_add(&stash, rr, 0, pool);
@@ -135,24 +123,16 @@ static int validate_section(struct kr_query *qry, knot_pkt_t *answer,
                }
        }
 
-       struct validate_baton baton = {
-               .pkt = answer,
-               .section_id = section_id,
-               .keys = qry->zone_cut.key,
-               /* Can't use qry->zone_cut.name directly, as this name can
-                * change when updating cut information before validation.
-                */
-               .zone_name = qry->zone_cut.key ? qry->zone_cut.key->owner : NULL,
-               .timestamp = qry->timestamp.tv_sec,
-               .has_nsec3 = has_nsec3,
-               .result = 0
-       };
+       /* Can't use qry->zone_cut.name directly, as this name can
+        * change when updating cut information before validation.
+        */
+       vctx->zone_name = vctx->keys ? vctx->keys->owner  : NULL;
 
-       ret = map_walk(&stash, &validate_rrset, &baton);
+       ret = map_walk(&stash, &validate_rrset, vctx);
        if (ret != 0) {
                return ret;
        }
-       ret = baton.result;
+       ret = vctx->result;
 
 fail:
        return ret;
@@ -165,14 +145,67 @@ static int validate_records(struct kr_query *qry, knot_pkt_t *answer, knot_mm_t
                return kr_error(EBADMSG);
        }
 
-       int ret = validate_section(qry, answer, KNOT_ANSWER, pool, has_nsec3);
+       kr_rrset_validation_ctx_t vctx = {
+               .pkt            = answer,
+               .section_id     = KNOT_ANSWER,
+               .keys           = qry->zone_cut.key,
+               .zone_name      = qry->zone_cut.name,
+               .timestamp      = qry->timestamp.tv_sec,
+               .has_nsec3      = has_nsec3,
+               .flags          = 0,
+               .result         = 0
+       };
+
+       int ret = validate_section(&vctx, pool);
        if (ret != 0) {
                return ret;
        }
 
-       return validate_section(qry, answer, KNOT_AUTHORITY, pool, has_nsec3);
+       uint32_t an_flags = vctx.flags;
+       vctx.section_id   = KNOT_AUTHORITY;
+       /* zone_name can be changed by validate_section(), restore it */
+       vctx.zone_name    = qry->zone_cut.name;
+       vctx.flags        = 0;
+       vctx.result       = 0;
+
+       ret = validate_section(&vctx, pool);
+       if (ret != 0) {
+               return ret;
+       }
+
+       /* Records were validated.
+        * If there is wildcard expansion in answer, flag the query.
+         */
+       if (an_flags & KR_DNSSEC_VFLG_WEXPAND) {
+               qry->flags |= QUERY_DNSSEC_WEXPAND;
+       }
+
+       return ret;
 }
 
+static int check_wcard_expanded(struct kr_query *qry, knot_pkt_t *pkt, knot_section_t section_id)
+{
+       kr_rrset_validation_ctx_t vctx = {
+               .pkt            = pkt,
+               .section_id     = section_id,
+               .keys           = NULL,
+               .zone_name      = qry->zone_cut.name,
+               .timestamp      = 0,
+               .has_nsec3      = false,
+               .flags          = 0,
+               .result         = 0
+       };
+       int ret = kr_section_check_wcard(&vctx);
+       if (ret != 0) {
+               return ret;
+       }
+       if (vctx.flags & KR_DNSSEC_VFLG_WEXPAND) {
+               qry->flags |= QUERY_DNSSEC_WEXPAND;
+       }
+       return kr_ok();
+}
+
+
 static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_nsec3)
 {
        /* Merge DNSKEY records from answer that are below/at current cut. */
@@ -203,13 +236,28 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_ns
 
        /* Check if there's a key for current TA. */
        if (updated_key && !(qry->flags & QUERY_CACHED)) {
-               int ret = kr_dnskeys_trusted(answer, KNOT_ANSWER, qry->zone_cut.key,
-                                            qry->zone_cut.trust_anchor, qry->zone_cut.name,
-                                            qry->timestamp.tv_sec, has_nsec3);
+
+               kr_rrset_validation_ctx_t vctx = {
+                       .pkt            = answer,
+                       .section_id     = KNOT_ANSWER,
+                       .keys           = qry->zone_cut.key,
+                       .zone_name      = qry->zone_cut.name,
+                       .timestamp      = qry->timestamp.tv_sec,
+                       .has_nsec3      = has_nsec3,
+                       .flags          = 0,
+                       .result         = 0
+               };
+               int ret = kr_dnskeys_trusted(&vctx, qry->zone_cut.trust_anchor);
                if (ret != 0) {
                        knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool);
                        return ret;
                }
+
+               if (vctx.flags & KR_DNSSEC_VFLG_WEXPAND)
+               {
+                       qry->flags |= QUERY_DNSSEC_WEXPAND;
+               }
+
        }
        return kr_ok();
 }
@@ -458,10 +506,24 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
         * Do not revalidate data from cache, as it's already trusted. */
        if (!(qry->flags & QUERY_CACHED)) {
                ret = validate_records(qry, pkt, req->rplan.pool, has_nsec3);
-               if (ret != 0) {
-                       DEBUG_MSG(qry, "<= couldn't validate RRSIGs\n");
-                       qry->flags |= QUERY_DNSSEC_BOGUS;
-                       return KNOT_STATE_FAIL;
+       } else {
+               /* Records already were validated.
+                * Check if wildcard answer. */
+               ret = check_wcard_expanded(qry, pkt, KNOT_ANSWER);
+       }
+       if (ret != 0) {
+               DEBUG_MSG(qry, "<= couldn't validate RRSIGs\n");
+               qry->flags |= QUERY_DNSSEC_BOGUS;
+               return KNOT_STATE_FAIL;
+       }
+
+       if ((qry->parent == NULL) && (qry->flags & QUERY_DNSSEC_WEXPAND)) {
+               /* Wildcard expansion detected for final query.
+                * Copy authority. */
+               const knot_pktsection_t *auth = knot_pkt_section(pkt, KNOT_AUTHORITY);
+               for (unsigned i = 0; i < auth->count; ++i) {
+                       const knot_rrset_t *rr = knot_pkt_rr(auth, i);
+                       kr_rrarray_add(&req->authority, rr, &pkt->mm);
                }
        }
 
index baf4728ac72e8017b171825fd0d2349d0fca6e46..17df1475cfe382797ce366f6d1335fe002db86c7 100644 (file)
@@ -43,7 +43,8 @@
        X(DNSSEC_BOGUS,    1 << 15) /**< Query response is DNSSEC bogus. */ \
        X(DNSSEC_INSECURE, 1 << 16) /**< Query response is DNSSEC insecure. */ \
        X(STUB,            1 << 17) /**< Stub resolution, accept received answer as solved. */ \
-       X(ALWAYS_CUT,      1 << 18) /**< Always recover zone cut (even if cached). */
+       X(ALWAYS_CUT,      1 << 18) /**< Always recover zone cut (even if cached). */ \
+       X(DNSSEC_WEXPAND,  1 << 19) /**< Query response has wildcard expansion. */
 
 /** Query flags */
 enum kr_query_flag {
@@ -151,4 +152,4 @@ struct kr_query *kr_rplan_resolved(struct kr_rplan *rplan);
 
 /** Return query predecessor. */
 KR_EXPORT KR_PURE
-struct kr_query *kr_rplan_next(struct kr_query *qry);
\ No newline at end of file
+struct kr_query *kr_rplan_next(struct kr_query *qry);