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, &params->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, &params, 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, &params, 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, &params, 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, &params,
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, &params)
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, &params) :
428 	                                    knot_nsec_check_chain(update);
429 }
430