]> 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>
Thu, 22 Feb 2024 13:29:36 +0000 (14:29 +0100)
(cherry picked from commit feb65eb97b93f0f024d70c7f5f6cbc6802ba02ec)

daemon/engine.c
daemon/lua/kres-gen-29.lua
daemon/lua/kres-gen-31.lua
lib/defines.h
lib/dnssec.c
lib/dnssec.h
lib/layer/validate.c
lib/resolve.h
lib/rplan.h

index 2c86a1833fb53d8b7dd4f3eadd094a7db8fafbab..2b36ef539375d058fbc3f1fd16c47495e488a34a 100644 (file)
@@ -492,6 +492,7 @@ static int init_resolver(struct engine *engine)
        /* Open resolution context */
        ctx->trust_anchors = map_make(NULL);
        ctx->negative_anchors = map_make(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 b1a09f9aaf797f0e55e6dc7c823e9cc3e6550176..7c349844c047f1bbd3a8651802d17fc2e16c1ffb 100644 (file)
@@ -330,6 +330,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;
@@ -347,6 +349,7 @@ struct kr_context {
        knot_rrset_t *upstream_opt_rr;
        map_t trust_anchors;
        map_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 8fe3ac8c32ab7c9d9c88f4594914f0ddd763c799..b95574eb91bf14a5625e3f23ad45e7655fc3afb2 100644 (file)
@@ -330,6 +330,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;
@@ -347,6 +349,7 @@ struct kr_context {
        knot_rrset_t *upstream_opt_rr;
        map_t trust_anchors;
        map_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 618903f46e70d92efd83b93faa45fdddffce3ca4..a07a7a8c732a3663eaa142d44ae89d729f214780 100644 (file)
@@ -56,6 +56,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 abb3713bac00858dc5b44f3011fafb54a7749462..78d71ce3a42ef7ba36e42966cc26bb445ebef800 100644 (file)
@@ -229,6 +229,25 @@ 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);
+       }
+       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)
 {
@@ -247,6 +266,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;
@@ -356,6 +377,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 0ba3621d98c1fcc558172195e74ec52e4baefe70..c7fce4f995d0c8f58c7bc42c22c5be080116fa6c 100644 (file)
@@ -43,6 +43,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 06758f0fef0c2f2439f6eb71f1c7af01e6c63cbf..c6b5ea02d33402db5853dba1010e74ff8d6e22cc 100644 (file)
@@ -266,6 +266,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,
        };
 
@@ -372,6 +373,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);
@@ -1002,6 +1004,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 efd665671350f6fb84b6ee589fa6061ac8d82369..e3de1f2c86aecc6693d8d05a40475548680b2f20 100644 (file)
@@ -163,6 +163,9 @@ struct kr_context
 
        map_t trust_anchors;
        map_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 a007e9c61752b0c83bfdbd9b130ea60b6efd613a..a9fbd58df9829a512b6d3130ec50608767083b62 100644 (file)
@@ -85,6 +85,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. */