summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimír Čunát <vladimir.cunat@nic.cz>2024-01-01 16:21:10 +0100
committerVladimír Čunát <vladimir.cunat@nic.cz>2024-02-13 09:47:33 +0100
commitfeb65eb97b93f0f024d70c7f5f6cbc6802ba02ec (patch)
tree2e9354286c8aec2431afcd105157e4b929633af5
parentlib/resolve kr_request_set_extended_error(): tweak priorities (diff)
downloadknot-resolver-feb65eb97b93f0f024d70c7f5f6cbc6802ba02ec.tar.xz
knot-resolver-feb65eb97b93f0f024d70c7f5f6cbc6802ba02ec.zip
mitigate KeyTrap DoS = CVE-2023-50387
-rw-r--r--daemon/engine.c1
-rw-r--r--daemon/lua/kres-gen-30.lua3
-rw-r--r--daemon/lua/kres-gen-31.lua3
-rw-r--r--daemon/lua/kres-gen-32.lua3
-rw-r--r--lib/defines.h2
-rw-r--r--lib/dnssec.c28
-rw-r--r--lib/dnssec.h1
-rw-r--r--lib/layer/validate.c7
-rw-r--r--lib/resolve.h3
-rw-r--r--lib/rplan.h6
10 files changed, 57 insertions, 0 deletions
diff --git a/daemon/engine.c b/daemon/engine.c
index 7ee56512..1d387ea3 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -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;
diff --git a/daemon/lua/kres-gen-30.lua b/daemon/lua/kres-gen-30.lua
index bb462fcc..7639e797 100644
--- a/daemon/lua/kres-gen-30.lua
+++ b/daemon/lua/kres-gen-30.lua
@@ -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;
diff --git a/daemon/lua/kres-gen-31.lua b/daemon/lua/kres-gen-31.lua
index b7c31c7c..e555a6a0 100644
--- a/daemon/lua/kres-gen-31.lua
+++ b/daemon/lua/kres-gen-31.lua
@@ -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;
diff --git a/daemon/lua/kres-gen-32.lua b/daemon/lua/kres-gen-32.lua
index 624654d3..31a5c5dd 100644
--- a/daemon/lua/kres-gen-32.lua
+++ b/daemon/lua/kres-gen-32.lua
@@ -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;
diff --git a/lib/defines.h b/lib/defines.h
index 6b6dac56..e8328928 100644
--- a/lib/defines.h
+++ b/lib/defines.h
@@ -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.
*/
diff --git a/lib/dnssec.c b/lib/dnssec.c
index c5363572..1e6eb58c 100644
--- a/lib/dnssec.c
+++ b/lib/dnssec.c
@@ -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;
diff --git a/lib/dnssec.h b/lib/dnssec.h
index 0fbd47c0..ca737cfe 100644
--- a/lib/dnssec.h
+++ b/lib/dnssec.h
@@ -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.
*
diff --git a/lib/layer/validate.c b/lib/layer/validate.c
index 0b7b740d..9b953940 100644
--- a/lib/layer/validate.c
+++ b/lib/layer/validate.c
@@ -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;
diff --git a/lib/resolve.h b/lib/resolve.h
index 97ba07b7..a2d5ec9d 100644
--- a/lib/resolve.h
+++ b/lib/resolve.h
@@ -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;
diff --git a/lib/rplan.h b/lib/rplan.h
index 891781fc..68174af5 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -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. */