1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= <vladimir.cunat@nic.cz>
Date: Mon, 1 Jan 2024 16:21:10 +0100
Subject: mitigate KeyTrap DoS = CVE-2023-50387
---
daemon/engine.c | 1 +
daemon/lua/kres-gen-30.lua | 3 +++
daemon/lua/kres-gen-31.lua | 3 +++
daemon/lua/kres-gen-32.lua | 3 +++
lib/defines.h | 2 ++
lib/dnssec.c | 28 ++++++++++++++++++++++++++++
lib/dnssec.h | 1 +
lib/layer/validate.c | 7 +++++++
lib/resolve.h | 3 +++
lib/rplan.h | 6 ++++++
10 files changed, 57 insertions(+)
diff --git a/daemon/engine.c b/daemon/engine.c
index 26c225f..a3f439f 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -492,6 +492,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 4353c5c..01ae6ac 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 a68dd65..c3845ec 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 222891e..2c407d4 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 6b6dac5..e832892 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 c536357..1e6eb58 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 0fbd47c..ca737cf 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 6c0d7ad..4882da8 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 97ba07b..a2d5ec9 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 891781f..68174af 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. */
|