]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
mitigate KeyTrap DoS = CVE-2023-50387
authorVladimír Čunát <vladimir.cunat@nic.cz>
Mon, 1 Jan 2024 15:21:10 +0000 (16:21 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 13 Feb 2024 08:47:33 +0000 (09:47 +0100)
daemon/engine.c
daemon/lua/kres-gen-30.lua
daemon/lua/kres-gen-31.lua
daemon/lua/kres-gen-32.lua
lib/defines.h
lib/dnssec.c
lib/dnssec.h
lib/layer/validate.c
lib/resolve.h
lib/rplan.h

index 7ee56512166300d9e69b6f69b1c0c992b6b7e9bb..1d387ea31d9d74780066446cca236f090a58b35f 100644 (file)
@@ -495,6 +495,7 @@ static int init_resolver(struct engine *engine)
        /* Open resolution context */
        ctx->trust_anchors = trie_create(NULL);
        ctx->negative_anchors = trie_create(NULL);
+       ctx->vld_limit_crypto = KR_VLD_LIMIT_CRYPTO_DEFAULT;
        ctx->pool = engine->pool;
        ctx->modules = &engine->modules;
        ctx->cache_rtt_tout_retry_interval = KR_NS_TIMEOUT_RETRY_INTERVAL;
index bb462fcc2769a5400ef59bbeb2c876701427c875..7639e797e09d359866095213372a1e5338595919 100644 (file)
@@ -336,6 +336,8 @@ struct kr_query {
        struct kr_qflags forward_flags;
        uint32_t secret;
        uint32_t uid;
+       int32_t vld_limit_crypto_remains;
+       uint32_t vld_limit_uid;
        uint64_t creation_time_mono;
        uint64_t timestamp_mono;
        struct timeval timestamp;
@@ -353,6 +355,7 @@ struct kr_context {
        knot_rrset_t *upstream_opt_rr;
        trie_t *trust_anchors;
        trie_t *negative_anchors;
+       int32_t vld_limit_crypto;
        struct kr_zonecut root_hints;
        struct kr_cache cache;
        unsigned int cache_rtt_tout_retry_interval;
index b7c31c7cc874f9f1cd1ceec3bcfe3b9c28e05ad2..e555a6a07ed01a9d86c615b97ce3b5893dcf6f0d 100644 (file)
@@ -336,6 +336,8 @@ struct kr_query {
        struct kr_qflags forward_flags;
        uint32_t secret;
        uint32_t uid;
+       int32_t vld_limit_crypto_remains;
+       uint32_t vld_limit_uid;
        uint64_t creation_time_mono;
        uint64_t timestamp_mono;
        struct timeval timestamp;
@@ -353,6 +355,7 @@ struct kr_context {
        knot_rrset_t *upstream_opt_rr;
        trie_t *trust_anchors;
        trie_t *negative_anchors;
+       int32_t vld_limit_crypto;
        struct kr_zonecut root_hints;
        struct kr_cache cache;
        unsigned int cache_rtt_tout_retry_interval;
index 624654d36fd3bb9db519956a1312d11fef622b3d..31a5c5dde1e7ca4afaa6c90277943c66ea6e6ced 100644 (file)
@@ -337,6 +337,8 @@ struct kr_query {
        struct kr_qflags forward_flags;
        uint32_t secret;
        uint32_t uid;
+       int32_t vld_limit_crypto_remains;
+       uint32_t vld_limit_uid;
        uint64_t creation_time_mono;
        uint64_t timestamp_mono;
        struct timeval timestamp;
@@ -354,6 +356,7 @@ struct kr_context {
        knot_rrset_t *upstream_opt_rr;
        trie_t *trust_anchors;
        trie_t *negative_anchors;
+       int32_t vld_limit_crypto;
        struct kr_zonecut root_hints;
        struct kr_cache cache;
        unsigned int cache_rtt_tout_retry_interval;
index 6b6dac564e550803eab1253fa7ae89642e385a79..e83289284a3b866dca13cd460c5c2b4fc0ac11fd 100644 (file)
@@ -57,6 +57,8 @@ static inline int KR_COLD kr_error(int x) {
 #define KR_COUNT_NO_NSADDR_LIMIT 5
 #define KR_CONSUME_FAIL_ROW_LIMIT 3 /* Maximum number of KR_STATE_FAIL in a row. */
 
+#define KR_VLD_LIMIT_CRYPTO_DEFAULT 32 /**< default for struct kr_query::vld_limit_crypto */
+
 /*
  * Defines.
  */
index c5363572e7766b0df3184754b34ea63b5a2b2e3f..1e6eb58c16eddc1bf8bf34576f1160a69101126b 100644 (file)
@@ -240,6 +240,29 @@ fail:
        return NULL;
 }
 
+/// Return if we want to afford yet another crypto-validation (and account it).
+static bool check_crypto_limit(const kr_rrset_validation_ctx_t *vctx)
+{
+       if (vctx->limit_crypto_remains == NULL)
+               return true; // no limiting
+       if (*vctx->limit_crypto_remains > 0) {
+               --*vctx->limit_crypto_remains;
+               return true;
+       }
+       // We got over limit.  There are optional actions to do.
+       if (vctx->log_qry && kr_log_is_debug_qry(VALIDATOR, vctx->log_qry)) {
+               auto_free char *name_str = kr_dname_text(vctx->zone_name);
+               kr_log_q(vctx->log_qry, VALIDATOR,
+                       "expensive crypto limited, mitigating CVE-2023-50387, current zone: %s\n",
+                       name_str);
+       }
+       if (vctx->log_qry && vctx->log_qry->request) {
+               kr_request_set_extended_error(vctx->log_qry->request, KNOT_EDNS_EDE_BOGUS,
+                               "EAIE: expensive crypto limited, mitigating CVE-2023-50387");
+       }
+       return false;
+}
+
 static int kr_svldr_rrset_with_key(knot_rrset_t *rrs, const knot_rdataset_t *rrsigs,
                                kr_rrset_validation_ctx_t *vctx, const kr_svldr_key_t *key)
 {
@@ -258,6 +281,8 @@ static int kr_svldr_rrset_with_key(knot_rrset_t *rrs, const knot_rdataset_t *rrs
                } else if (retv != 0) {
                        continue;
                }
+               if (!check_crypto_limit(vctx))
+                       return vctx->result = kr_error(E2BIG);
                // We only expect non-expanded wildcard records in input;
                // that also means we don't need to perform non-existence proofs.
                const int trim_labels = (val_flgs & FLG_WILDCARD_EXPANSION) ? 1 : 0;
@@ -367,6 +392,9 @@ static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx,
                                        break;
                                }
                        }
+                       if (!check_crypto_limit(vctx)) {
+                               goto finish;
+                       }
                        if (kr_check_signature(rdata_j, key, covered, trim_labels) != 0) {
                                vctx->rrs_counters.crypto_invalid++;
                                continue;
index 0fbd47c025bc8ccf74a6012e1b1a30b18a646b7c..ca737cfe126915f48e36e947945a4373aad3a2d7 100644 (file)
@@ -44,6 +44,7 @@ struct kr_rrset_validation_ctx {
        uint32_t flags;                 /*!< Output - Flags. */
        uint32_t err_cnt;               /*!< Output - Number of validation failures. */
        uint32_t cname_norrsig_cnt;     /*!< Output - Number of CNAMEs missing RRSIGs. */
+       int32_t *limit_crypto_remains;  /*!< Optional pointer to struct kr_query::vld_limit_crypto_remains */
 
        /** Validation result: kr_error() code.
         *
index 0b7b740d52166d1387629048fd1d49800f8dd2bc..9b953940e346b2f0bf67767c99996e84a6063ea7 100644 (file)
@@ -276,6 +276,7 @@ static int validate_records(struct kr_request *req, knot_pkt_t *answer, knot_mm_
                .err_cnt        = 0,
                .cname_norrsig_cnt = 0,
                .result         = 0,
+               .limit_crypto_remains = &qry->vld_limit_crypto_remains,
                .log_qry        = qry,
        };
 
@@ -384,6 +385,7 @@ static int validate_keyset(struct kr_request *req, knot_pkt_t *answer, bool has_
                        .has_nsec3      = has_nsec3,
                        .flags          = 0,
                        .result         = 0,
+                       .limit_crypto_remains = &qry->vld_limit_crypto_remains,
                        .log_qry        = qry,
                };
                int ret = kr_dnskeys_trusted(&vctx, sig_rds, qry->zone_cut.trust_anchor);
@@ -1030,6 +1032,11 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt)
        struct kr_request *req = ctx->req;
        struct kr_query *qry = req->current_query;
 
+       if (qry->vld_limit_uid != qry->uid) {
+               qry->vld_limit_uid = qry->uid;
+               qry->vld_limit_crypto_remains = req->ctx->vld_limit_crypto;
+       }
+
        /* Ignore faulty or unprocessed responses. */
        if (ctx->state & (KR_STATE_FAIL|KR_STATE_CONSUME)) {
                return ctx->state;
index 97ba07b7ade9b6a919fd58ead0d40f2e88d114c7..a2d5ec9dbd3b599f55141e538b198ccf9712bb19 100644 (file)
@@ -162,6 +162,9 @@ struct kr_context
 
        trie_t *trust_anchors;
        trie_t *negative_anchors;
+       /** Validator's limit on the number of cryptographic steps for a single upstream packet. */
+       int32_t vld_limit_crypto;
+
        struct kr_zonecut root_hints;
        struct kr_cache cache;
        unsigned cache_rtt_tout_retry_interval;
index 891781fcb29926d6fd4e35ce1a1a63fd6df1b2ad..68174af5d513d5b035b5116f5126afe70ff7fd1b 100644 (file)
@@ -87,6 +87,12 @@ struct kr_query {
        struct kr_qflags forward_flags;
        uint32_t secret;
        uint32_t uid; /**< Query iteration number, unique within the kr_rplan. */
+
+       /** Remaining limit; see kr_query::vld_limit_crypto docs */
+       int32_t  vld_limit_crypto_remains;
+       /** ::uid value to which this remaining limit applies. */
+       uint32_t vld_limit_uid;
+
        uint64_t creation_time_mono; /* The time of query's creation (milliseconds).
                                 * Or time of creation of an oldest
                                 * ancestor if it is a subquery. */