summaryrefslogtreecommitdiffstats
path: root/debian/patches/0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch
blob: d6109e03d1a8cd70684ecc154f1697178a68d530 (plain)
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. */