1 /* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <assert.h>
18
19 #include "libdnssec/error.h"
20 #include "libknot/descriptor.h"
21 #include "libknot/rrtype/nsec3.h"
22 #include "libknot/rrtype/soa.h"
23 #include "knot/common/log.h"
24 #include "knot/dnssec/nsec-chain.h"
25 #include "knot/dnssec/nsec3-chain.h"
26 #include "knot/dnssec/key-events.h"
27 #include "knot/dnssec/rrset-sign.h"
28 #include "knot/dnssec/zone-nsec.h"
29 #include "knot/dnssec/zone-sign.h"
30 #include "knot/zone/zone-diff.h"
31 #include "contrib/base32hex.h"
32 #include "contrib/wire_ctx.h"
33
knot_nsec3_hash_to_dname(uint8_t * out,size_t out_size,const uint8_t * hash,size_t hash_size,const knot_dname_t * zone_apex)34 int knot_nsec3_hash_to_dname(uint8_t *out, size_t out_size, const uint8_t *hash,
35 size_t hash_size, const knot_dname_t *zone_apex)
36
37 {
38 if (out == NULL || hash == NULL || zone_apex == NULL) {
39 return KNOT_EINVAL;
40 }
41
42 // Encode raw hash to the first label.
43 uint8_t label[KNOT_DNAME_MAXLABELLEN];
44 int32_t label_size = knot_base32hex_encode(hash, hash_size, label, sizeof(label));
45 if (label_size <= 0) {
46 return label_size;
47 }
48
49 // Write the result, which already is in lower-case.
50 wire_ctx_t wire = wire_ctx_init(out, out_size);
51
52 wire_ctx_write_u8(&wire, label_size);
53 wire_ctx_write(&wire, label, label_size);
54 wire_ctx_write(&wire, zone_apex, knot_dname_size(zone_apex));
55
56 return wire.error;
57 }
58
knot_create_nsec3_owner(uint8_t * out,size_t out_size,const knot_dname_t * owner,const knot_dname_t * zone_apex,const dnssec_nsec3_params_t * params)59 int knot_create_nsec3_owner(uint8_t *out, size_t out_size,
60 const knot_dname_t *owner, const knot_dname_t *zone_apex,
61 const dnssec_nsec3_params_t *params)
62 {
63 if (out == NULL || owner == NULL || zone_apex == NULL || params == NULL) {
64 return KNOT_EINVAL;
65 }
66
67 dnssec_binary_t data = {
68 .data = (uint8_t *)owner,
69 .size = knot_dname_size(owner)
70 };
71
72 dnssec_binary_t hash = { 0 };
73
74 int ret = dnssec_nsec3_hash(&data, params, &hash);
75 if (ret != DNSSEC_EOK) {
76 return knot_error_from_libdnssec(ret);
77 }
78
79 ret = knot_nsec3_hash_to_dname(out, out_size, hash.data, hash.size, zone_apex);
80
81 dnssec_binary_free(&hash);
82
83 return ret;
84 }
85
node_nsec3_hash(zone_node_t * node,const zone_contents_t * zone)86 knot_dname_t *node_nsec3_hash(zone_node_t *node, const zone_contents_t *zone)
87 {
88 if (node->nsec3_hash == NULL && knot_is_nsec3_enabled(zone)) {
89 assert(!(node->flags & NODE_FLAGS_NSEC3_NODE));
90 size_t hash_size = zone_nsec3_name_len(zone);
91 knot_dname_t *hash = malloc(hash_size);
92 if (hash == NULL) {
93 return NULL;
94 }
95 if (knot_create_nsec3_owner(hash, hash_size, node->owner, zone->apex->owner,
96 &zone->nsec3_params) != KNOT_EOK) {
97 free(hash);
98 return NULL;
99 }
100 node->nsec3_hash = hash;
101 }
102
103 if (node->flags & NODE_FLAGS_NSEC3_NODE) {
104 return node->nsec3_node->owner;
105 } else {
106 return node->nsec3_hash;
107 }
108 }
109
node_nsec3_node(zone_node_t * node,const zone_contents_t * zone)110 zone_node_t *node_nsec3_node(zone_node_t *node, const zone_contents_t *zone)
111 {
112 if (!(node->flags & NODE_FLAGS_NSEC3_NODE) && knot_is_nsec3_enabled(zone)) {
113 knot_dname_t *hash = node_nsec3_hash(node, zone);
114 zone_node_t *nsec3 = zone_tree_get(zone->nsec3_nodes, hash);
115 if (nsec3 != NULL) {
116 if (node->nsec3_hash != binode_counterpart(node)->nsec3_hash) {
117 free(node->nsec3_hash);
118 }
119 node->nsec3_node = binode_first(nsec3);
120 node->flags |= NODE_FLAGS_NSEC3_NODE;
121 }
122 }
123
124 return node_nsec3_get(node);
125 }
126
binode_fix_nsec3_pointer(zone_node_t * node,const zone_contents_t * zone)127 int binode_fix_nsec3_pointer(zone_node_t *node, const zone_contents_t *zone)
128 {
129 zone_node_t *counter = binode_counterpart(node);
130 if (counter->nsec3_hash == NULL) {
131 (void)node_nsec3_node(node, zone);
132 return KNOT_EOK;
133 }
134 assert(counter->nsec3_node != NULL); // shut up cppcheck
135
136 zone_node_t *nsec3_counter = (counter->flags & NODE_FLAGS_NSEC3_NODE) ?
137 counter->nsec3_node : NULL;
138 if (nsec3_counter != NULL && !(binode_node_as(nsec3_counter, node)->flags & NODE_FLAGS_DELETED)) {
139 assert(node->flags & NODE_FLAGS_NSEC3_NODE);
140 node->flags |= NODE_FLAGS_NSEC3_NODE;
141 assert(!(nsec3_counter->flags & NODE_FLAGS_SECOND));
142 node->nsec3_node = nsec3_counter;
143 } else {
144 node->flags &= ~NODE_FLAGS_NSEC3_NODE;
145 if (counter->flags & NODE_FLAGS_NSEC3_NODE) {
146 // downgrade the NSEC3 node pointer to NSEC3 name
147 node->nsec3_hash = knot_dname_copy(counter->nsec3_node->owner, NULL);
148 } else {
149 node->nsec3_hash = counter->nsec3_hash;
150 }
151 (void)node_nsec3_node(node, zone);
152 }
153 return KNOT_EOK;
154 }
155
nsec3param_valid(const knot_rdataset_t * rrs,const dnssec_nsec3_params_t * params)156 static bool nsec3param_valid(const knot_rdataset_t *rrs,
157 const dnssec_nsec3_params_t *params)
158 {
159 assert(rrs);
160 assert(params);
161
162 // NSEC3 disabled
163 if (params->algorithm == 0) {
164 return false;
165 }
166
167 // multiple NSEC3 records
168 if (rrs->count != 1) {
169 return false;
170 }
171
172 dnssec_binary_t rdata = {
173 .size = rrs->rdata->len,
174 .data = rrs->rdata->data,
175 };
176
177 dnssec_nsec3_params_t parsed = { 0 };
178 int r = dnssec_nsec3_params_from_rdata(&parsed, &rdata);
179 if (r != DNSSEC_EOK) {
180 return false;
181 }
182
183 bool equal = parsed.algorithm == params->algorithm &&
184 parsed.flags == 0 && // opt-out flag is always 0 in NSEC3PARAM
185 parsed.iterations == params->iterations &&
186 dnssec_binary_cmp(&parsed.salt, ¶ms->salt) == 0;
187
188 dnssec_nsec3_params_free(&parsed);
189
190 return equal;
191 }
192
remove_nsec3param(zone_update_t * update,bool also_rrsig)193 static int remove_nsec3param(zone_update_t *update, bool also_rrsig)
194 {
195 knot_rrset_t rrset = node_rrset(update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
196 int ret = zone_update_remove(update, &rrset);
197
198 rrset = node_rrset(update->new_cont->apex, KNOT_RRTYPE_RRSIG);
199 if (!knot_rrset_empty(&rrset) && ret == KNOT_EOK && also_rrsig) {
200 knot_rrset_t rrsig;
201 knot_rrset_init(&rrsig, update->new_cont->apex->owner,
202 KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, 0);
203 ret = knot_synth_rrsig(KNOT_RRTYPE_NSEC3PARAM, &rrset.rrs, &rrsig.rrs, NULL);
204 if (ret == KNOT_EOK) {
205 ret = zone_update_remove(update, &rrsig);
206 }
207 knot_rdataset_clear(&rrsig.rrs, NULL);
208 }
209
210 return ret;
211 }
212
set_nsec3param(knot_rrset_t * rrset,const dnssec_nsec3_params_t * params)213 static int set_nsec3param(knot_rrset_t *rrset, const dnssec_nsec3_params_t *params)
214 {
215 assert(rrset);
216 assert(params);
217
218 // Prepare wire rdata.
219 size_t rdata_len = 3 * sizeof(uint8_t) + sizeof(uint16_t) + params->salt.size;
220 uint8_t rdata[rdata_len];
221 wire_ctx_t wire = wire_ctx_init(rdata, rdata_len);
222
223 wire_ctx_write_u8(&wire, params->algorithm);
224 wire_ctx_write_u8(&wire, 0); // (RFC 5155 Section 4.1.2)
225 wire_ctx_write_u16(&wire, params->iterations);
226 wire_ctx_write_u8(&wire, params->salt.size);
227 wire_ctx_write(&wire, params->salt.data, params->salt.size);
228
229 if (wire.error != KNOT_EOK) {
230 return wire.error;
231 }
232
233 assert(wire_ctx_available(&wire) == 0);
234
235 return knot_rrset_add_rdata(rrset, rdata, rdata_len, NULL);
236 }
237
add_nsec3param(zone_update_t * update,const dnssec_nsec3_params_t * params,uint32_t ttl)238 static int add_nsec3param(zone_update_t *update,
239 const dnssec_nsec3_params_t *params,
240 uint32_t ttl)
241 {
242 assert(update);
243 assert(params);
244
245 knot_rrset_t *rrset = NULL;
246 rrset = knot_rrset_new(update->new_cont->apex->owner, KNOT_RRTYPE_NSEC3PARAM,
247 KNOT_CLASS_IN, ttl, NULL);
248 if (rrset == NULL) {
249 return KNOT_ENOMEM;
250 }
251
252 int r = set_nsec3param(rrset, params);
253 if (r == KNOT_EOK) {
254 r = zone_update_add(update, rrset);
255 }
256 knot_rrset_free(rrset, NULL);
257 return r;
258 }
259
knot_nsec3param_uptodate(const zone_contents_t * zone,const dnssec_nsec3_params_t * params)260 bool knot_nsec3param_uptodate(const zone_contents_t *zone,
261 const dnssec_nsec3_params_t *params)
262 {
263 assert(zone);
264 assert(params);
265
266 knot_rdataset_t *nsec3param = node_rdataset(zone->apex, KNOT_RRTYPE_NSEC3PARAM);
267
268 return (nsec3param != NULL && nsec3param_valid(nsec3param, params));
269 }
270
knot_nsec3param_update(zone_update_t * update,const dnssec_nsec3_params_t * params,uint32_t ttl)271 int knot_nsec3param_update(zone_update_t *update,
272 const dnssec_nsec3_params_t *params,
273 uint32_t ttl)
274 {
275 assert(update);
276 assert(params);
277
278 knot_rdataset_t *nsec3param = node_rdataset(update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
279 bool valid = nsec3param && nsec3param_valid(nsec3param, params);
280
281 if (nsec3param && !valid) {
282 int r = remove_nsec3param(update, params->algorithm == 0);
283 if (r != KNOT_EOK) {
284 return r;
285 }
286 }
287
288 if (params->algorithm != 0 && !valid) {
289 return add_nsec3param(update, params, ttl);
290 }
291
292 return KNOT_EOK;
293 }
294
295 /*!
296 * \brief Initialize NSEC3PARAM based on the signing policy.
297 *
298 * \note For NSEC, the algorithm number is set to 0.
299 */
nsec3param_init(const knot_kasp_policy_t * policy,const knot_kasp_zone_t * zone)300 static dnssec_nsec3_params_t nsec3param_init(const knot_kasp_policy_t *policy,
301 const knot_kasp_zone_t *zone)
302 {
303 assert(policy);
304 assert(zone);
305
306 dnssec_nsec3_params_t params = { 0 };
307 if (policy->nsec3_enabled) {
308 params.algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1;
309 params.iterations = policy->nsec3_iterations;
310 params.salt = zone->nsec3_salt;
311 params.flags = (policy->nsec3_opt_out ? KNOT_NSEC3_FLAG_OPT_OUT : 0);
312 }
313
314 return params;
315 }
316
317 // int: returns KNOT_E* if error
zone_nsec_ttl(zone_contents_t * zone)318 static int zone_nsec_ttl(zone_contents_t *zone)
319 {
320 knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
321 if (knot_rrset_empty(&soa)) {
322 return KNOT_EINVAL;
323 }
324
325 return MIN(knot_soa_minimum(soa.rrs.rdata), soa.ttl);
326 }
327
knot_zone_create_nsec_chain(zone_update_t * update,const kdnssec_ctx_t * ctx)328 int knot_zone_create_nsec_chain(zone_update_t *update, const kdnssec_ctx_t *ctx)
329 {
330 if (update == NULL || ctx == NULL) {
331 return KNOT_EINVAL;
332 }
333
334 if (ctx->policy->unsafe & UNSAFE_NSEC) {
335 return KNOT_EOK;
336 }
337
338 int nsec_ttl = zone_nsec_ttl(update->new_cont);
339 if (nsec_ttl < 0) {
340 return nsec_ttl;
341 }
342
343 dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone);
344
345 int ret = knot_nsec3param_update(update, ¶ms, nsec_ttl);
346 if (ret != KNOT_EOK) {
347 return ret;
348 }
349
350 if (ctx->policy->nsec3_enabled) {
351 ret = knot_nsec3_create_chain(update->new_cont, ¶ms, nsec_ttl,
352 update);
353 } else {
354 ret = knot_nsec_create_chain(update, nsec_ttl);
355 if (ret == KNOT_EOK) {
356 ret = delete_nsec3_chain(update);
357 }
358 }
359 return ret;
360 }
361
knot_zone_fix_nsec_chain(zone_update_t * update,const zone_keyset_t * zone_keys,const kdnssec_ctx_t * ctx)362 int knot_zone_fix_nsec_chain(zone_update_t *update,
363 const zone_keyset_t *zone_keys,
364 const kdnssec_ctx_t *ctx)
365 {
366 if (update == NULL || ctx == NULL) {
367 return KNOT_EINVAL;
368 }
369
370 if (ctx->policy->unsafe & UNSAFE_NSEC) {
371 return KNOT_EOK;
372 }
373
374 int nsec_ttl_old = zone_nsec_ttl(update->zone->contents);
375 int nsec_ttl_new = zone_nsec_ttl(update->new_cont);
376 if (nsec_ttl_old < 0 || nsec_ttl_new < 0) {
377 return MIN(nsec_ttl_old, nsec_ttl_new);
378 }
379
380 dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone);
381
382 int ret;
383 if (nsec_ttl_old != nsec_ttl_new || (update->flags & UPDATE_CHANGED_NSEC)) {
384 ret = KNOT_ENORECORD;
385 } else if (ctx->policy->nsec3_enabled) {
386 ret = knot_nsec3_fix_chain(update, ¶ms, nsec_ttl_new);
387 } else {
388 ret = knot_nsec_fix_chain(update, nsec_ttl_new);
389 }
390 if (ret == KNOT_ENORECORD) {
391 log_zone_info(update->zone->name, "DNSSEC, re-creating whole NSEC%s chain",
392 (ctx->policy->nsec3_enabled ? "3" : ""));
393 if (ctx->policy->nsec3_enabled) {
394 ret = knot_nsec3_create_chain(update->new_cont, ¶ms,
395 nsec_ttl_new, update);
396 } else {
397 ret = knot_nsec_create_chain(update, nsec_ttl_new);
398 }
399 }
400 if (ret == KNOT_EOK) {
401 ret = knot_zone_sign_nsecs_in_changeset(zone_keys, ctx, update);
402 }
403 return ret;
404 }
405
knot_zone_check_nsec_chain(zone_update_t * update,const kdnssec_ctx_t * ctx,bool incremental)406 int knot_zone_check_nsec_chain(zone_update_t *update, const kdnssec_ctx_t *ctx,
407 bool incremental)
408 {
409 int ret = KNOT_EOK;
410 dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone);
411
412 if (incremental) {
413 ret = ctx->policy->nsec3_enabled
414 ? knot_nsec3_check_chain_fix(update, ¶ms)
415 : knot_nsec_check_chain_fix(update);
416 }
417 if (ret == KNOT_ENORECORD) {
418 log_zone_info(update->zone->name, "DNSSEC, re-validating whole NSEC%s chain",
419 (ctx->policy->nsec3_enabled ? "3" : ""));
420 incremental = false;
421 }
422
423 if (incremental) {
424 return ret;
425 }
426
427 return ctx->policy->nsec3_enabled ? knot_nsec3_check_chain(update, ¶ms) :
428 knot_nsec_check_chain(update);
429 }
430