1 /*  Copyright (C) 2021 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 <stdio.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 
23 #include "libdnssec/error.h"
24 #include "contrib/base32hex.h"
25 #include "contrib/string.h"
26 #include "libknot/libknot.h"
27 #include "knot/zone/semantic-check.h"
28 #include "knot/dnssec/rrset-sign.h"
29 #include "knot/dnssec/zone-keys.h"
30 #include "knot/dnssec/zone-nsec.h"
31 
32 static const char *error_messages[SEM_ERR_UNKNOWN + 1] = {
33 	[SEM_ERR_SOA_NONE] =
34 	"missing SOA at the zone apex",
35 
36 	[SEM_ERR_CNAME_EXTRA_RECORDS] =
37 	"more records exist at CNAME",
38 	[SEM_ERR_CNAME_MULTIPLE] =
39 	"multiple CNAME records",
40 
41 	[SEM_ERR_DNAME_CHILDREN] =
42 	"child record exists under DNAME",
43 	[SEM_ERR_DNAME_MULTIPLE] =
44 	"multiple DNAME records",
45 	[SEM_ERR_DNAME_EXTRA_NS] =
46 	"NS record exists at DNAME",
47 
48 	[SEM_ERR_NS_APEX] =
49 	"missing NS at the zone apex",
50 	[SEM_ERR_NS_GLUE] =
51 	"missing glue record",
52 
53 	[SEM_ERR_RRSIG_RDATA_TYPE_COVERED] =
54 	"wrong type covered in RRSIG",
55 	[SEM_ERR_RRSIG_RDATA_TTL] =
56 	"wrong original TTL in RRSIG",
57 	[SEM_ERR_RRSIG_RDATA_EXPIRATION] =
58 	"expired RRSIG",
59 	[SEM_ERR_RRSIG_RDATA_INCEPTION] =
60 	"RRSIG inception in the future",
61 	[SEM_ERR_RRSIG_RDATA_LABELS] =
62 	"wrong labels in RRSIG",
63 	[SEM_ERR_RRSIG_RDATA_OWNER] =
64 	"wrong signer's name in RRSIG",
65 	[SEM_ERR_RRSIG_NO_RRSIG] =
66 	"missing RRSIG",
67 	[SEM_ERR_RRSIG_SIGNED] =
68 	"signed RRSIG",
69 	[SEM_ERR_RRSIG_UNVERIFIABLE] =
70 	"unverifiable signature",
71 
72 	[SEM_ERR_NSEC_NONE] =
73 	"missing NSEC",
74 	[SEM_ERR_NSEC_RDATA_BITMAP] =
75 	"incorrect type bitmap in NSEC",
76 	[SEM_ERR_NSEC_RDATA_MULTIPLE] =
77 	"multiple NSEC records",
78 	[SEM_ERR_NSEC_RDATA_CHAIN] =
79 	"incoherent NSEC chain",
80 
81 	[SEM_ERR_NSEC3_NONE] =
82 	"missing NSEC3",
83 	[SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT] =
84 	"insecure delegation outside NSEC3 opt-out",
85 	[SEM_ERR_NSEC3_EXTRA_RECORD] =
86 	"invalid record type in NSEC3 chain",
87 	[SEM_ERR_NSEC3_RDATA_TTL] =
88 	"inconsistent TTL for NSEC3 and minimum TTL in SOA",
89 	[SEM_ERR_NSEC3_RDATA_CHAIN] =
90 	"incoherent NSEC3 chain",
91 	[SEM_ERR_NSEC3_RDATA_BITMAP] =
92 	"incorrect type bitmap in NSEC3",
93 	[SEM_ERR_NSEC3_RDATA_FLAGS] =
94 	"incorrect flags in NSEC3",
95 	[SEM_ERR_NSEC3_RDATA_SALT] =
96 	"incorrect salt in NSEC3",
97 	[SEM_ERR_NSEC3_RDATA_ALG] =
98 	"incorrect algorithm in NSEC3",
99 	[SEM_ERR_NSEC3_RDATA_ITERS] =
100 	"incorrect number of iterations in NSEC3",
101 
102 	[SEM_ERR_NSEC3PARAM_RDATA_FLAGS] =
103 	"invalid flags in NSEC3PARAM",
104 	[SEM_ERR_NSEC3PARAM_RDATA_ALG] =
105 	"invalid algorithm in NSEC3PARAM",
106 
107 	[SEM_ERR_DS_RDATA_ALG] =
108 	"invalid algorithm in DS",
109 	[SEM_ERR_DS_RDATA_DIGLEN] =
110 	"invalid digest length in DS",
111 
112 	[SEM_ERR_DNSKEY_NONE] =
113 	"missing DNSKEY",
114 	[SEM_ERR_DNSKEY_INVALID] =
115 	"invalid DNSKEY",
116 	[SEM_ERR_DNSKEY_RDATA_PROTOCOL] =
117 	"invalid protocol in DNSKEY",
118 
119 	[SEM_ERR_CDS_NONE] =
120 	"missing CDS",
121 	[SEM_ERR_CDS_NOT_MATCH] =
122 	"CDS not match CDNSKEY",
123 
124 	[SEM_ERR_CDNSKEY_NONE] =
125 	"missing CDNSKEY",
126 	[SEM_ERR_CDNSKEY_NO_DNSKEY] =
127 	"CDNSKEY not match DNSKEY",
128 	[SEM_ERR_CDNSKEY_NO_CDS] =
129 	"CDNSKEY without corresponding CDS",
130 	[SEM_ERR_CDNSKEY_INVALID_DELETE] =
131 	"invalid CDNSKEY/CDS for DNSSEC delete algorithm",
132 
133 	[SEM_ERR_UNKNOWN] =
134 	"unknown error"
135 };
136 
sem_error_msg(sem_error_t code)137 const char *sem_error_msg(sem_error_t code)
138 {
139 	if (code > SEM_ERR_UNKNOWN) {
140 		code = SEM_ERR_UNKNOWN;
141 	}
142 	return error_messages[code];
143 }
144 
145 typedef enum {
146 	MANDATORY = 1 << 0,
147 	OPTIONAL =  1 << 1,
148 	NSEC =      1 << 2,
149 	NSEC3 =     1 << 3,
150 } check_level_t;
151 
152 typedef struct {
153 	zone_contents_t *zone;
154 	sem_handler_t *handler;
155 	const zone_node_t *next_nsec;
156 	check_level_t level;
157 	time_t time;
158 } semchecks_data_t;
159 
160 static int check_soa(const zone_node_t *node, semchecks_data_t *data);
161 static int check_cname(const zone_node_t *node, semchecks_data_t *data);
162 static int check_dname(const zone_node_t *node, semchecks_data_t *data);
163 static int check_delegation(const zone_node_t *node, semchecks_data_t *data);
164 static int check_submission(const zone_node_t *node, semchecks_data_t *data);
165 static int check_ds(const zone_node_t *node, semchecks_data_t *data);
166 static int check_nsec(const zone_node_t *node, semchecks_data_t *data);
167 static int check_nsec3(const zone_node_t *node, semchecks_data_t *data);
168 static int check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data);
169 static int check_rrsig(const zone_node_t *node, semchecks_data_t *data);
170 static int check_rrsig_signed(const zone_node_t *node, semchecks_data_t *data);
171 static int check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data);
172 static int check_nsec3_presence(const zone_node_t *node, semchecks_data_t *data);
173 
174 struct check_function {
175 	int (*function)(const zone_node_t *, semchecks_data_t *);
176 	check_level_t level;
177 };
178 
179 /* List of function callbacks for defined check_level */
180 static const struct check_function CHECK_FUNCTIONS[] = {
181 	{ check_soa,            MANDATORY },
182 	{ check_cname,          MANDATORY },
183 	{ check_dname,          MANDATORY },
184 	{ check_delegation,     MANDATORY }, // mandatory for apex, optional for others
185 	{ check_ds,             OPTIONAL },
186 	{ check_submission,     NSEC | NSEC3 },
187 	{ check_rrsig,          NSEC | NSEC3 },
188 	{ check_rrsig_signed,   NSEC | NSEC3 },
189 	{ check_nsec_bitmap,    NSEC | NSEC3 },
190 	{ check_nsec,           NSEC },
191 	{ check_nsec3,          NSEC3 },
192 	{ check_nsec3_presence, NSEC3 },
193 	{ check_nsec3_opt_out,  NSEC3 },
194 };
195 
196 static const int CHECK_FUNCTIONS_LEN = sizeof(CHECK_FUNCTIONS)
197                                      / sizeof(struct check_function);
198 
check_signature(const knot_rdata_t * rrsig,const dnssec_key_t * key,const knot_rrset_t * covered)199 static int check_signature(const knot_rdata_t *rrsig, const dnssec_key_t *key,
200                            const knot_rrset_t *covered)
201 {
202 	if (!rrsig || !key || !dnssec_key_can_verify(key)) {
203 		return KNOT_EINVAL;
204 	}
205 
206 	int ret = KNOT_EOK;
207 	dnssec_sign_ctx_t *sign_ctx = NULL;
208 
209 	dnssec_binary_t signature = {
210 		.size = knot_rrsig_signature_len(rrsig),
211 		.data = (uint8_t *)knot_rrsig_signature(rrsig)
212 	};
213 	if (!signature.data || !signature.size) {
214 		ret = KNOT_EINVAL;
215 		goto fail;
216 	}
217 
218 	if (dnssec_sign_new(&sign_ctx, key) != KNOT_EOK) {
219 		ret = KNOT_ENOMEM;
220 		goto fail;
221 	}
222 
223 	if (knot_sign_ctx_add_data(sign_ctx, rrsig->data, covered) != KNOT_EOK) {
224 		ret = KNOT_ENOMEM;
225 		goto fail;
226 	}
227 
228 	if (dnssec_sign_verify(sign_ctx, false, &signature) != KNOT_EOK) {
229 		ret = KNOT_EINVAL;
230 		goto fail;
231 	}
232 
233 fail:
234 	dnssec_sign_free(sign_ctx);
235 	return ret;
236 }
237 
238 /*!
239  * \brief Semantic check - RRSIG rdata.
240  *
241  * \param handler    Pointer on function to be called in case of negative check.
242  * \param zone       The zone the rrset is in.
243  * \param node       The node in the zone contents.
244  * \param rrsig      RRSIG rdata.
245  * \param rrset      RRSet signed by the RRSIG.
246  * \param context    The time stamp we check the rrsig validity according to.
247  * \param level      Level of the check.
248  * \param verified   Out: the RRSIG has been verified to be signed by existing DNSKEY.
249  *
250  * \retval KNOT_EOK on success.
251  * \return Appropriate error code if error was found.
252  */
check_rrsig_rdata(sem_handler_t * handler,const zone_contents_t * zone,const zone_node_t * node,const knot_rdata_t * rrsig,const knot_rrset_t * rrset,time_t context,check_level_t level,bool * verified)253 static int check_rrsig_rdata(sem_handler_t *handler,
254                              const zone_contents_t *zone,
255                              const zone_node_t *node,
256                              const knot_rdata_t *rrsig,
257                              const knot_rrset_t *rrset,
258                              time_t context,
259                              check_level_t level,
260                              bool *verified)
261 {
262 	/* Prepare additional info string. */
263 	char info_str[50] = "";
264 	char type_str[16] = "";
265 	knot_rrtype_to_string(rrset->type, type_str, sizeof(type_str));
266 	int ret = snprintf(info_str, sizeof(info_str), "(record type %s)", type_str);
267 	if (ret < 0 || ret >= sizeof(info_str)) {
268 		return KNOT_ENOMEM;
269 	}
270 
271 	if (knot_rrsig_type_covered(rrsig) != rrset->type) {
272 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_TYPE_COVERED,
273 		            info_str);
274 	}
275 
276 	/* label number at the 2nd index should be same as owner's */
277 	uint8_t labels_rdata = knot_rrsig_labels(rrsig);
278 
279 	size_t tmp = knot_dname_labels(rrset->owner, NULL) - labels_rdata;
280 	if (tmp != 0) {
281 		/* if name has wildcard, label must not be included */
282 		if (!knot_dname_is_wildcard(rrset->owner)) {
283 			handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_LABELS,
284 			            info_str);
285 		} else if (tmp != 1) {
286 			handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_LABELS,
287 			            info_str);
288 		}
289 	}
290 
291 	/* Check original TTL. */
292 	uint32_t original_ttl = knot_rrsig_original_ttl(rrsig);
293 	if (original_ttl != rrset->ttl) {
294 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_TTL,
295 		            info_str);
296 	}
297 
298 	/* Check for expired signature. */
299 	if (knot_rrsig_sig_expiration(rrsig) < context) {
300 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_EXPIRATION,
301 		            info_str);
302 	}
303 
304 	/* Check inception */
305 	if (knot_rrsig_sig_inception(rrsig) > context) {
306 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_INCEPTION,
307 		            info_str);
308 	}
309 
310 	/* Check signer name. */
311 	const knot_dname_t *signer = knot_rrsig_signer_name(rrsig);
312 	if (!knot_dname_is_equal(signer, zone->apex->owner)) {
313 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_OWNER,
314 		            info_str);
315 	}
316 
317 	/* Verify with public key - only one RRSIG of covered record needed */
318 	if (level & OPTIONAL && !*verified) {
319 		const knot_rdataset_t *dnskeys = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY);
320 		if (dnskeys == NULL) {
321 			return KNOT_EOK;
322 		}
323 
324 		for (int i = 0; i < dnskeys->count; i++) {
325 			knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i);
326 			uint16_t flags = knot_dnskey_flags(dnskey);
327 			uint8_t proto = knot_dnskey_proto(dnskey);
328 			/* RFC 4034 2.1.1 & 2.1.2 */
329 			if (flags & DNSKEY_FLAGS_ZSK && proto == 3) {
330 				dnssec_key_t *key;
331 
332 				ret = dnssec_key_from_rdata(&key, zone->apex->owner,
333 				                            dnskey->data, dnskey->len);
334 				if (ret != KNOT_EOK) {
335 					continue;
336 				}
337 
338 				if (dnssec_key_get_keytag(key) != knot_rrsig_key_tag(rrsig)) {
339 					dnssec_key_free(key);
340 					continue;
341 				}
342 
343 				ret = check_signature(rrsig, key, rrset);
344 				dnssec_key_free(key);
345 				if (ret == KNOT_EOK) {
346 					*verified = true;
347 					break;
348 				}
349 			}
350 		}
351 	}
352 
353 	return KNOT_EOK;
354 }
355 
check_rrsig_signed(const zone_node_t * node,semchecks_data_t * data)356 static int check_rrsig_signed(const zone_node_t *node, semchecks_data_t *data)
357 {
358 	/* signed rrsig - nonsense */
359 	if (node_rrtype_is_signed(node, KNOT_RRTYPE_RRSIG)) {
360 		data->handler->cb(data->handler, data->zone, node,
361 		                  SEM_ERR_RRSIG_SIGNED, NULL);
362 	}
363 
364 	return KNOT_EOK;
365 }
366 /*!
367  * \brief Semantic check - RRSet's RRSIG.
368  *
369  * \param handler    Pointer on function to be called in case of negative check.
370  * \param zone       The zone the rrset is in.
371  * \param node       The node in the zone contents.
372  * \param rrset      RRSet signed by the RRSIG.
373  * \param context    The time stamp we check the rrsig validity according to.
374  * \param level      Level of the check.
375  *
376  * \retval KNOT_EOK on success.
377  * \return Appropriate error code if error was found.
378  */
check_rrsig_in_rrset(sem_handler_t * handler,const zone_contents_t * zone,const zone_node_t * node,const knot_rrset_t * rrset,time_t context,check_level_t level)379 static int check_rrsig_in_rrset(sem_handler_t *handler,
380                                 const zone_contents_t *zone,
381                                 const zone_node_t *node,
382                                 const knot_rrset_t *rrset,
383                                 time_t context,
384                                 check_level_t level)
385 {
386 	if (handler == NULL || node == NULL || rrset == NULL) {
387 		return KNOT_EINVAL;
388 	}
389 	/* Prepare additional info string. */
390 	char info_str[50] = "";
391 	char type_str[16] = "";
392 	knot_rrtype_to_string(rrset->type, type_str, sizeof(type_str));
393 	int ret = snprintf(info_str, sizeof(info_str), "(record type %s)", type_str);
394 	if (ret < 0 || ret >= sizeof(info_str)) {
395 		return KNOT_ENOMEM;
396 	}
397 
398 	knot_rrset_t node_rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
399 
400 	knot_rdataset_t rrsigs;
401 	knot_rdataset_init(&rrsigs);
402 	ret = knot_synth_rrsig(rrset->type, &node_rrsigs.rrs, &rrsigs, NULL);
403 	if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
404 		return ret;
405 	}
406 	if (ret == KNOT_ENOENT) {
407 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_NO_RRSIG, info_str);
408 		return KNOT_EOK;
409 	}
410 
411 	bool verified = false;
412 	knot_rdata_t *rrsig = rrsigs.rdata;
413 	for (uint16_t i = 0; ret == KNOT_EOK && i < rrsigs.count; ++i) {
414 		ret = check_rrsig_rdata(handler, zone, node, rrsig, rrset,
415 		                        context, level, &verified);
416 		rrsig = knot_rdataset_next(rrsig);
417 	}
418 	/* Only one rrsig of covered record needs to be verified by DNSKEY. */
419 	if (!verified) {
420 		handler->cb(handler, zone, node, SEM_ERR_RRSIG_UNVERIFIABLE,
421 		            info_str);
422 	}
423 
424 	knot_rdataset_clear(&rrsigs, NULL);
425 	return KNOT_EOK;;
426 }
427 
428 /*!
429  * \brief Check if glue record for delegation is present.
430  *
431  * Also check if there is NS record in the zone.
432  * \param node Node to check
433  * \param data Semantic checks context data
434  */
check_delegation(const zone_node_t * node,semchecks_data_t * data)435 static int check_delegation(const zone_node_t *node, semchecks_data_t *data)
436 {
437 	if (!((node->flags & NODE_FLAGS_DELEG) || data->zone->apex == node)) {
438 		return KNOT_EOK;
439 	}
440 
441 	// always check zone apex
442 	if (!(data->level & OPTIONAL) && data->zone->apex != node) {
443 		return KNOT_EOK;
444 	}
445 
446 	const knot_rdataset_t *ns_rrs = node_rdataset(node, KNOT_RRTYPE_NS);
447 	if (ns_rrs == NULL) {
448 		assert(data->zone->apex == node);
449 		data->handler->cb(data->handler, data->zone, node,
450 		                  SEM_ERR_NS_APEX, NULL);
451 		return KNOT_EOK;
452 	}
453 
454 	// check glue record for delegation
455 	for (int i = 0; i < ns_rrs->count; ++i) {
456 		knot_rdata_t *ns_rr = knot_rdataset_at(ns_rrs, i);
457 		const knot_dname_t *ns_dname = knot_ns_name(ns_rr);
458 		const zone_node_t *glue_node = NULL, *glue_encloser = NULL;
459 		int ret = zone_contents_find_dname(data->zone, ns_dname, &glue_node,
460 		                                   &glue_encloser, NULL);
461 		switch (ret) {
462 		case KNOT_EOUTOFZONE:
463 			continue; // NS is out of bailiwick
464 		case ZONE_NAME_NOT_FOUND:
465 			if (glue_encloser != node &&
466 			    glue_encloser->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH)) {
467 				continue; // NS is below another delegation
468 			}
469 
470 			// check if covered by wildcard
471 			knot_dname_storage_t wildcard = "\x01""*";
472 			knot_dname_to_wire(wildcard + 2, glue_encloser->owner,
473 			                   sizeof(wildcard) - 2);
474 			glue_node = zone_contents_find_node(data->zone, wildcard);
475 			break; // continue in checking glue existence
476 		case ZONE_NAME_FOUND:
477 			break; // continue in checking glue existence
478 		default:
479 			return ret;
480 		}
481 		if (!node_rrtype_exists(glue_node, KNOT_RRTYPE_A) &&
482 		    !node_rrtype_exists(glue_node, KNOT_RRTYPE_AAAA)) {
483 			data->handler->cb(data->handler, data->zone, node,
484 			                  SEM_ERR_NS_GLUE, NULL);
485 		}
486 	}
487 
488 	return KNOT_EOK;
489 }
490 
491 /*!
492  * \brief check_submission_records Check CDS and CDNSKEY
493  */
check_submission(const zone_node_t * node,semchecks_data_t * data)494 static int check_submission(const zone_node_t *node, semchecks_data_t *data)
495 {
496 	const knot_rdataset_t *cdss = node_rdataset(node, KNOT_RRTYPE_CDS);
497 	const knot_rdataset_t *cdnskeys = node_rdataset(node, KNOT_RRTYPE_CDNSKEY);
498 	if (cdss == NULL && cdnskeys == NULL) {
499 		return KNOT_EOK;
500 	} else if (cdss == NULL) {
501 		data->handler->cb(data->handler, data->zone, node,
502 		                  SEM_ERR_CDS_NONE, NULL);
503 		return KNOT_EOK;
504 	} else if (cdnskeys == NULL) {
505 		data->handler->cb(data->handler, data->zone, node,
506 		                  SEM_ERR_CDNSKEY_NONE, NULL);
507 		return KNOT_EOK;
508 	}
509 
510 	const knot_rdataset_t *dnskeys = node_rdataset(data->zone->apex,
511 	                                               KNOT_RRTYPE_DNSKEY);
512 	if (dnskeys == NULL) {
513 		data->handler->cb(data->handler, data->zone, node,
514 		                  SEM_ERR_DNSKEY_NONE, NULL);
515 	}
516 
517 	const uint8_t *empty_cds = (uint8_t *)"\x00\x00\x00\x00\x00";
518 	const uint8_t *empty_cdnskey = (uint8_t *)"\x00\x00\x03\x00\x00";
519 	bool delete_cds = false, delete_cdnskey = false;
520 
521 	// check every CDNSKEY for corresponding DNSKEY
522 	for (int i = 0; i < cdnskeys->count; i++) {
523 		knot_rdata_t *cdnskey = knot_rdataset_at(cdnskeys, i);
524 
525 		// skip delete-dnssec CDNSKEY
526 		if (cdnskey->len == 5 && memcmp(cdnskey->data, empty_cdnskey, 5) == 0) {
527 			delete_cdnskey = true;
528 			continue;
529 		}
530 
531 		bool match = false;
532 		for (int j = 0; dnskeys != NULL && j < dnskeys->count; j++) {
533 			knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, j);
534 
535 			if (knot_rdata_cmp(dnskey, cdnskey) == 0) {
536 				match = true;
537 				break;
538 			}
539 		}
540 		if (!match) {
541 			data->handler->cb(data->handler, data->zone, node,
542 			                  SEM_ERR_CDNSKEY_NO_DNSKEY, NULL);
543 		}
544 	}
545 
546 	// check every CDS for corresponding CDNSKEY
547 	for (int i = 0; i < cdss->count; i++) {
548 		knot_rdata_t *cds = knot_rdataset_at(cdss, i);
549 		uint8_t digest_type = knot_ds_digest_type(cds);
550 
551 		// skip delete-dnssec CDS
552 		if (cds->len == 5 && memcmp(cds->data, empty_cds, 5) == 0) {
553 			delete_cds = true;
554 			continue;
555 		}
556 
557 		bool match = false;
558 		for (int j = 0; j < cdnskeys->count; j++) {
559 			knot_rdata_t *cdnskey = knot_rdataset_at(cdnskeys, j);
560 
561 			dnssec_key_t *key;
562 			int ret = dnssec_key_from_rdata(&key, data->zone->apex->owner,
563 			                                cdnskey->data, cdnskey->len);
564 			if (ret != KNOT_EOK) {
565 				continue;
566 			}
567 
568 			dnssec_binary_t cds_calc = { 0 };
569 			dnssec_binary_t cds_orig = { .size = cds->len, .data = cds->data };
570 			ret = dnssec_key_create_ds(key, digest_type, &cds_calc);
571 			if (ret != KNOT_EOK) {
572 				dnssec_key_free(key);
573 				return ret;
574 			}
575 
576 			ret = dnssec_binary_cmp(&cds_orig, &cds_calc);
577 			dnssec_binary_free(&cds_calc);
578 			dnssec_key_free(key);
579 			if (ret == 0) {
580 				match = true;
581 				break;
582 			}
583 		}
584 		if (!match) {
585 			data->handler->cb(data->handler, data->zone, node,
586 			                  SEM_ERR_CDS_NOT_MATCH, NULL);
587 		}
588 	}
589 
590 	// check delete-dnssec records
591 	if ((delete_cds && (!delete_cdnskey || cdss->count > 1)) ||
592 	    (delete_cdnskey && (!delete_cds || cdnskeys->count > 1))) {
593 		data->handler->cb(data->handler, data->zone, node,
594 		                  SEM_ERR_CDNSKEY_INVALID_DELETE, NULL);
595 	}
596 
597 	// check orphaned CDS
598 	if (cdss->count < cdnskeys->count) {
599 		data->handler->cb(data->handler, data->zone, node,
600 		                  SEM_ERR_CDNSKEY_NO_CDS, NULL);
601 	}
602 
603 	return KNOT_EOK;
604 }
605 
606 /*!
607  * \brief Semantic check - DS record.
608  *
609  * \param node Node to check
610  * \param data Semantic checks context data
611  *
612  * \retval KNOT_EOK on success.
613  * \return Appropriate error code if error was found.
614  */
check_ds(const zone_node_t * node,semchecks_data_t * data)615 static int check_ds(const zone_node_t *node, semchecks_data_t *data)
616 {
617 	const knot_rdataset_t *dss = node_rdataset(node, KNOT_RRTYPE_DS);
618 	if (dss == NULL) {
619 		return KNOT_EOK;
620 	}
621 
622 	for (int i = 0; i < dss->count; i++) {
623 		knot_rdata_t *ds = knot_rdataset_at(dss, i);
624 		uint16_t keytag = knot_ds_key_tag(ds);
625 		uint8_t digest_type = knot_ds_digest_type(ds);
626 
627 		char info[100] = "";
628 		(void)snprintf(info, sizeof(info), "(keytag %d)", keytag);
629 
630 		if (!dnssec_algorithm_digest_support(digest_type)) {
631 			data->handler->cb(data->handler, data->zone, node,
632 			                  SEM_ERR_DS_RDATA_ALG, info);
633 		} else {
634 			// Sizes for different digest algorithms.
635 			const uint16_t digest_sizes [] = { 0, 20, 32, 32, 48};
636 
637 			uint16_t digest_size = knot_ds_digest_len(ds);
638 
639 			if (digest_sizes[digest_type] != digest_size) {
640 				data->handler->cb(data->handler, data->zone, node,
641 				                  SEM_ERR_DS_RDATA_DIGLEN, info);
642 			}
643 		}
644 	}
645 
646 	return KNOT_EOK;
647 }
648 
649 /*!
650  * \brief Run all semantic check related to RRSIG record
651  *
652  * \param node Node to check
653  * \param data Semantic checks context data
654  */
check_rrsig(const zone_node_t * node,semchecks_data_t * data)655 static int check_rrsig(const zone_node_t *node, semchecks_data_t *data)
656 {
657 	assert(node);
658 	if (node->flags & NODE_FLAGS_NONAUTH) {
659 		return KNOT_EOK;
660 	}
661 
662 	bool deleg = node->flags & NODE_FLAGS_DELEG;
663 
664 	int ret = KNOT_EOK;
665 
666 	int rrset_count = node->rrset_count;
667 	for (int i = 0; ret == KNOT_EOK && i < rrset_count; i++) {
668 		knot_rrset_t rrset = node_rrset_at(node, i);
669 		if (rrset.type == KNOT_RRTYPE_RRSIG) {
670 			continue;
671 		}
672 		if (deleg && rrset.type != KNOT_RRTYPE_NSEC &&
673 		    rrset.type != KNOT_RRTYPE_DS ) {
674 			continue;
675 		}
676 
677 		ret = check_rrsig_in_rrset(data->handler, data->zone, node, &rrset,
678 		                           data->time, data->level);
679 	}
680 	return ret;
681 }
682 
683 /*!
684  * \brief Add all RR types from a node into the bitmap.
685  */
bitmap_add_all_node_rrsets(dnssec_nsec_bitmap_t * bitmap,const zone_node_t * node)686 static void bitmap_add_all_node_rrsets(dnssec_nsec_bitmap_t *bitmap,
687                                        const zone_node_t *node)
688 {
689 	bool deleg = node->flags & NODE_FLAGS_DELEG;
690 	for (int i = 0; i < node->rrset_count; i++) {
691 		knot_rrset_t rr = node_rrset_at(node, i);
692 		if (deleg && (rr.type != KNOT_RRTYPE_NS &&
693 		              rr.type != KNOT_RRTYPE_DS &&
694 		              rr.type != KNOT_RRTYPE_NSEC &&
695 		              rr.type != KNOT_RRTYPE_RRSIG)) {
696 			continue;
697 		}
698 		dnssec_nsec_bitmap_add(bitmap, rr.type);
699 	}
700 }
701 
nsec3_info(const knot_dname_t * owner,char * out,size_t out_len)702 static char *nsec3_info(const knot_dname_t *owner, char *out, size_t out_len)
703 {
704 	knot_dname_txt_storage_t buff;
705 	char *str = knot_dname_to_str(buff, owner, sizeof(buff));
706 	if (str == NULL) {
707 		return NULL;
708 	}
709 
710 	int ret = snprintf(out, out_len, "(NSEC3 owner=%s)", str);
711 	if (ret <= 0 || ret >= out_len) {
712 		return NULL;
713 	}
714 
715 	return out;
716 }
717 
718 /*!
719  * \brief Check NSEC and NSEC3 type bitmap
720  *
721  * \param node Node to check
722  * \param data Semantic checks context data
723  */
check_nsec_bitmap(const zone_node_t * node,semchecks_data_t * data)724 static int check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data)
725 {
726 	assert(node);
727 	if (node->flags & NODE_FLAGS_NONAUTH) {
728 		return KNOT_EOK;
729 	}
730 
731 	bool nsec = data->level & NSEC;
732 	knot_rdataset_t *nsec_rrs = NULL;
733 
734 	zone_node_t *nsec3_node = node_nsec3_get(node);
735 
736 	if (nsec) {
737 		nsec_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC);
738 	} else if (nsec3_node != NULL) {
739 		nsec_rrs = node_rdataset(nsec3_node, KNOT_RRTYPE_NSEC3);
740 	}
741 	if (nsec_rrs == NULL) {
742 		return KNOT_EOK;
743 	}
744 
745 	// create NSEC bitmap from node
746 	dnssec_nsec_bitmap_t *node_bitmap = dnssec_nsec_bitmap_new();
747 	if (node_bitmap == NULL) {
748 		return KNOT_ENOMEM;
749 	}
750 	bitmap_add_all_node_rrsets(node_bitmap, node);
751 
752 	uint16_t node_wire_size = dnssec_nsec_bitmap_size(node_bitmap);
753 	uint8_t *node_wire = malloc(node_wire_size);
754 	if (node_wire == NULL) {
755 		dnssec_nsec_bitmap_free(node_bitmap);
756 		return KNOT_ENOMEM;
757 	}
758 	dnssec_nsec_bitmap_write(node_bitmap, node_wire);
759 	dnssec_nsec_bitmap_free(node_bitmap);
760 
761 	// get NSEC bitmap from NSEC node
762 	const uint8_t *nsec_wire = NULL;
763 	uint16_t nsec_wire_size = 0;
764 	if (nsec) {
765 		nsec_wire = knot_nsec_bitmap(nsec_rrs->rdata);
766 		nsec_wire_size = knot_nsec_bitmap_len(nsec_rrs->rdata);
767 	} else {
768 		nsec_wire = knot_nsec3_bitmap(nsec_rrs->rdata);
769 		nsec_wire_size = knot_nsec3_bitmap_len(nsec_rrs->rdata);
770 	}
771 
772 	if (node_wire_size != nsec_wire_size ||
773 	    memcmp(node_wire, nsec_wire, node_wire_size) != 0) {
774 		char buff[50 + KNOT_DNAME_TXT_MAXLEN];
775 		char *info = nsec ? NULL : nsec3_info(nsec3_node->owner,
776 		                                      buff, sizeof(buff));
777 		data->handler->cb(data->handler, data->zone, node,
778 		                  (nsec ? SEM_ERR_NSEC_RDATA_BITMAP : SEM_ERR_NSEC3_RDATA_BITMAP),
779 		                  info);
780 	}
781 
782 	free(node_wire);
783 	return KNOT_EOK;
784 }
785 
786 /*!
787  * \brief Run NSEC related semantic checks
788  *
789  * \param node Node to check
790  * \param data Semantic checks context data
791  */
check_nsec(const zone_node_t * node,semchecks_data_t * data)792 static int check_nsec(const zone_node_t *node, semchecks_data_t *data)
793 {
794 	assert(node);
795 	if (node->flags & NODE_FLAGS_NONAUTH) {
796 		return KNOT_EOK;
797 	}
798 
799 	if (node->rrset_count == 0) { // empty nonterminal
800 		return KNOT_EOK;
801 	}
802 
803 	/* check for NSEC record */
804 	const knot_rdataset_t *nsec_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC);
805 	if (nsec_rrs == NULL) {
806 		data->handler->cb(data->handler, data->zone, node,
807 		                  SEM_ERR_NSEC_NONE, NULL);
808 		return KNOT_EOK;
809 	}
810 
811 	/* Test that only one record is in the NSEC RRSet */
812 	if (nsec_rrs->count != 1) {
813 		data->handler->cb(data->handler, data->zone, node,
814 		                  SEM_ERR_NSEC_RDATA_MULTIPLE, NULL);
815 	}
816 
817 	if (data->next_nsec != node) {
818 		data->handler->cb(data->handler, data->zone, node,
819 		                  SEM_ERR_NSEC_RDATA_CHAIN, NULL);
820 	}
821 
822 	/*
823 	 * Test that NSEC chain is coherent.
824 	 * We have already checked that every
825 	 * authoritative node contains NSEC record
826 	 * so checking should only be matter of testing
827 	 * the next link in each node.
828 	 */
829 	knot_dname_storage_t next_domain;
830 	if (knot_dname_store(next_domain, knot_nsec_next(nsec_rrs->rdata)) == 0) {
831 		return KNOT_EINVAL;
832 	}
833 	knot_dname_to_lower(next_domain);
834 
835 	data->next_nsec = zone_contents_find_node(data->zone, next_domain);
836 	if (data->next_nsec == NULL) {
837 		data->handler->cb(data->handler, data->zone, node,
838 		                  SEM_ERR_NSEC_RDATA_CHAIN, NULL);
839 	}
840 
841 	return KNOT_EOK;
842 }
843 
nsec3_optout_allow(const zone_node_t * node)844 static bool nsec3_optout_allow(const zone_node_t *node)
845 {
846 	return (node->flags & NODE_FLAGS_DELEG) && !node_rrtype_exists(node, KNOT_RRTYPE_DS);
847 }
848 
849 /*!
850  * \brief Check if node has NSEC3 node.
851  *
852  * \param node Node to check
853  * \param data Semantic checks context data
854  */
check_nsec3_presence(const zone_node_t * node,semchecks_data_t * data)855 static int check_nsec3_presence(const zone_node_t *node, semchecks_data_t *data)
856 {
857 	bool auth = (node->flags & NODE_FLAGS_NONAUTH) == 0;
858 	bool empty = (node->flags & NODE_FLAGS_EMPTY) != 0;
859 
860 	if (!nsec3_optout_allow(node) && auth && !empty) {
861 		if (node_nsec3_get(node) == NULL) {
862 			data->handler->cb(data->handler, data->zone, node,
863 			                  SEM_ERR_NSEC3_NONE, NULL);
864 		}
865 	}
866 
867 	return KNOT_EOK;
868 }
869 
870 /*!
871  * \brief Check NSEC3 opt-out.
872  *
873  * \param node Node to check
874  * \param data Semantic checks context data
875  */
check_nsec3_opt_out(const zone_node_t * node,semchecks_data_t * data)876 static int check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data)
877 {
878 	if (!(node_nsec3_get(node) == NULL && node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_EMPTY))) {
879 		return KNOT_EOK;
880 	}
881 	/* Insecure delegation, check whether it is part of opt-out span. */
882 
883 	const zone_node_t *nsec3_previous = NULL;
884 	const zone_node_t *nsec3_node;
885 	zone_contents_find_nsec3_for_name(data->zone, node->owner, &nsec3_node,
886 	                                  &nsec3_previous);
887 
888 	if (nsec3_previous == NULL) {
889 		data->handler->cb(data->handler, data->zone, node,
890 		                  SEM_ERR_NSEC3_NONE, NULL);
891 		return KNOT_EOK;
892 	}
893 
894 	const knot_rdataset_t *previous_rrs;
895 	previous_rrs = node_rdataset(nsec3_previous, KNOT_RRTYPE_NSEC3);
896 	assert(previous_rrs);
897 
898 	/* Check for opt-out flag. */
899 	uint8_t flags = knot_nsec3_flags(previous_rrs->rdata);
900 	if (!(flags & 1)) {
901 		data->handler->cb(data->handler, data->zone, node,
902 		                  SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT, NULL);
903 	}
904 
905 	return KNOT_EOK;
906 }
907 
908 /*!
909  * \brief Run checks related to NSEC3.
910  *
911  * Check NSEC3 node for given node.
912  * Check if NSEC3 chain is coherent and cyclic.
913  * \param node Node to check
914  * \param data Semantic checks context data
915  */
check_nsec3(const zone_node_t * node,semchecks_data_t * data)916 static int check_nsec3(const zone_node_t *node, semchecks_data_t *data)
917 {
918 	assert(node);
919 	bool auth = (node->flags & NODE_FLAGS_NONAUTH) == 0;
920 	bool deleg = (node->flags & NODE_FLAGS_DELEG) != 0;
921 
922 	if (!auth && !deleg) {
923 		return KNOT_EOK;
924 	}
925 
926 	zone_node_t *nsec3_node = node_nsec3_get(node);
927 	if (nsec3_node == NULL) {
928 		return KNOT_EOK;
929 	}
930 
931 	dnssec_nsec3_params_t params_apex = { 0 };
932 	int ret = KNOT_EOK;
933 
934 	char buff[50 + KNOT_DNAME_TXT_MAXLEN];
935 	char *info = nsec3_info(nsec3_node->owner, buff, sizeof(buff));
936 
937 	knot_rrset_t nsec3_rrs = node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3);
938 	if (knot_rrset_empty(&nsec3_rrs)) {
939 		data->handler->cb(data->handler, data->zone, node,
940 		                  SEM_ERR_NSEC3_NONE, info);
941 		goto nsec3_cleanup;
942 	}
943 
944 	knot_rrset_t soa_rrset = node_rrset(data->zone->apex, KNOT_RRTYPE_SOA);
945 	assert(!knot_rrset_empty(&soa_rrset));
946 	uint32_t minimum_ttl = knot_soa_minimum(soa_rrset.rrs.rdata);
947 	if (nsec3_rrs.ttl != MIN(minimum_ttl, soa_rrset.ttl)) {
948 		data->handler->cb(data->handler, data->zone, node,
949 		                  SEM_ERR_NSEC3_RDATA_TTL, info);
950 	}
951 
952 	// Check parameters.
953 	const knot_rdataset_t *nsec3param = node_rdataset(data->zone->apex,
954 	                                                  KNOT_RRTYPE_NSEC3PARAM);
955 	dnssec_binary_t rdata = {
956 		.size = nsec3param->rdata->len,
957 		.data = nsec3param->rdata->data
958 	};
959 	ret = dnssec_nsec3_params_from_rdata(&params_apex, &rdata);
960 	if (ret != DNSSEC_EOK) {
961 		ret = knot_error_from_libdnssec(ret);
962 		goto nsec3_cleanup;
963 	}
964 
965 	if (knot_nsec3_flags(nsec3_rrs.rrs.rdata) > 1) {
966 		data->handler->cb(data->handler, data->zone, node,
967 		                  SEM_ERR_NSEC3_RDATA_FLAGS, info);
968 	}
969 
970 	dnssec_binary_t salt = {
971 		.size = knot_nsec3_salt_len(nsec3_rrs.rrs.rdata),
972 		.data = (uint8_t *)knot_nsec3_salt(nsec3_rrs.rrs.rdata),
973 	};
974 
975 	if (dnssec_binary_cmp(&salt, &params_apex.salt)) {
976 		data->handler->cb(data->handler, data->zone, node,
977 		                  SEM_ERR_NSEC3_RDATA_SALT, info);
978 	}
979 
980 	if (knot_nsec3_alg(nsec3_rrs.rrs.rdata) != params_apex.algorithm) {
981 		data->handler->cb(data->handler, data->zone, node,
982 		                  SEM_ERR_NSEC3_RDATA_ALG, info);
983 	}
984 
985 	if (knot_nsec3_iters(nsec3_rrs.rrs.rdata) != params_apex.iterations) {
986 		data->handler->cb(data->handler, data->zone, node,
987 		                  SEM_ERR_NSEC3_RDATA_ITERS, info);
988 	}
989 
990 	// Get next nsec3 node.
991 	const zone_node_t *apex = data->zone->apex;
992 	const uint8_t *next_dname_str = knot_nsec3_next(nsec3_rrs.rrs.rdata);
993 	uint8_t next_dname_str_size = knot_nsec3_next_len(nsec3_rrs.rrs.rdata);
994 	knot_dname_storage_t next_dname;
995 	ret = knot_nsec3_hash_to_dname(next_dname, sizeof(next_dname),
996 	                               next_dname_str, next_dname_str_size,
997 	                               apex->owner);
998 	if (ret != KNOT_EOK) {
999 		goto nsec3_cleanup;
1000 	}
1001 
1002 	const zone_node_t *next_nsec3 = zone_contents_find_nsec3_node(data->zone,
1003 	                                                              next_dname);
1004 	if (next_nsec3 == NULL || node_prev(next_nsec3) != nsec3_node) {
1005 		uint8_t *next = NULL;
1006 		int32_t next_len = knot_base32hex_encode_alloc(next_dname_str,
1007 		                                               next_dname_str_size,
1008 		                                               &next);
1009 		char *hash_info = NULL;
1010 		if (next != NULL) {
1011 			hash_info = sprintf_alloc("(next hash %.*s)", next_len, next);
1012 			free(next);
1013 		}
1014 		data->handler->cb(data->handler, data->zone, node,
1015 		                  SEM_ERR_NSEC3_RDATA_CHAIN, hash_info);
1016 		free(hash_info);
1017 	}
1018 
1019 	ret = check_rrsig(nsec3_node, data);
1020 	if (ret != KNOT_EOK) {
1021 		goto nsec3_cleanup;
1022 	}
1023 
1024 	// Check that the node only contains NSEC3 and RRSIG.
1025 	for (int i = 0; ret == KNOT_EOK && i < nsec3_node->rrset_count; i++) {
1026 		knot_rrset_t rrset = node_rrset_at(nsec3_node, i);
1027 		uint16_t type = rrset.type;
1028 		if (type != KNOT_RRTYPE_NSEC3 && type != KNOT_RRTYPE_RRSIG) {
1029 			data->handler->cb(data->handler, data->zone, nsec3_node,
1030 			                  SEM_ERR_NSEC3_EXTRA_RECORD, NULL);
1031 		}
1032 	}
1033 
1034 nsec3_cleanup:
1035 	dnssec_nsec3_params_free(&params_apex);
1036 
1037 	return ret;
1038 }
1039 
1040 /*!
1041  * \brief Check if apex node contains SOA record
1042  *
1043  * \param node Node to check
1044  * \param data Semantic checks context data
1045  */
check_soa(const zone_node_t * node,semchecks_data_t * data)1046 static int check_soa(const zone_node_t *node, semchecks_data_t *data)
1047 {
1048 	if (data->zone->apex != node) {
1049 		return KNOT_EOK;
1050 	}
1051 
1052 	const knot_rdataset_t *soa_rrs = node_rdataset(node, KNOT_RRTYPE_SOA);
1053 	if (soa_rrs == NULL) {
1054 		data->handler->error = true;
1055 		data->handler->cb(data->handler, data->zone, node,
1056 		                  SEM_ERR_SOA_NONE, NULL);
1057 	}
1058 
1059 	return KNOT_EOK;
1060 }
1061 
1062 /*!
1063  * \brief Check if CNAME record contains other records
1064  *
1065  * \param node Node to check
1066  * \param data Semantic checks context data
1067  */
check_cname(const zone_node_t * node,semchecks_data_t * data)1068 static int check_cname(const zone_node_t *node, semchecks_data_t *data)
1069 {
1070 	const knot_rdataset_t *cname_rrs = node_rdataset(node, KNOT_RRTYPE_CNAME);
1071 	if (cname_rrs == NULL) {
1072 		return KNOT_EOK;
1073 	}
1074 
1075 	unsigned rrset_limit = 1;
1076 	/* With DNSSEC node can contain RRSIGs or NSEC */
1077 	if (node_rrtype_exists(node, KNOT_RRTYPE_NSEC)) {
1078 		rrset_limit += 1;
1079 	}
1080 	if (node_rrtype_exists(node, KNOT_RRTYPE_RRSIG)) {
1081 		rrset_limit += 1;
1082 	}
1083 
1084 	if (node->rrset_count > rrset_limit) {
1085 		data->handler->error = true;
1086 		data->handler->cb(data->handler, data->zone, node,
1087 		                  SEM_ERR_CNAME_EXTRA_RECORDS, NULL);
1088 	}
1089 	if (cname_rrs->count != 1) {
1090 		data->handler->error = true;
1091 		data->handler->cb(data->handler, data->zone, node,
1092 		                  SEM_ERR_CNAME_MULTIPLE, NULL);
1093 	}
1094 
1095 	return KNOT_EOK;
1096 }
1097 
1098 /*!
1099  * \brief Check if node with DNAME record satisfies RFC 6672 Section 2.
1100  *
1101  * \param node Node to check
1102  * \param data Semantic checks context data
1103  */
check_dname(const zone_node_t * node,semchecks_data_t * data)1104 static int check_dname(const zone_node_t *node, semchecks_data_t *data)
1105 {
1106 	const knot_rdataset_t *dname_rrs = node_rdataset(node, KNOT_RRTYPE_DNAME);
1107 	if (dname_rrs == NULL) {
1108 		return KNOT_EOK;
1109 	}
1110 
1111 	/* RFC 6672 Section 2.3 Paragraph 3 */
1112 	bool is_apex = (node->flags & NODE_FLAGS_APEX);
1113 	if (!is_apex && node_rrtype_exists(node, KNOT_RRTYPE_NS)) {
1114 		data->handler->error = true;
1115 		data->handler->cb(data->handler, data->zone, node,
1116 		                  SEM_ERR_DNAME_EXTRA_NS, NULL);
1117 	}
1118 	/* RFC 6672 Section 2.4 Paragraph 1 */
1119 	/* If the NSEC3 node of the apex is present, it is counted as apex's child. */
1120 	unsigned allowed_children = (is_apex && node_nsec3_get(node) != NULL) ? 1 : 0;
1121 	if (node->children > allowed_children) {
1122 		data->handler->error = true;
1123 		data->handler->cb(data->handler, data->zone, node,
1124 		                  SEM_ERR_DNAME_CHILDREN, NULL);
1125 	}
1126 	/* RFC 6672 Section 2.4 Paragraph 2 */
1127 	if (dname_rrs->count != 1) {
1128 		data->handler->error = true;
1129 		data->handler->cb(data->handler, data->zone, node,
1130 		                  SEM_ERR_DNAME_MULTIPLE, NULL);
1131 	}
1132 
1133 	return KNOT_EOK;
1134 }
1135 
1136 /*!
1137  * \brief Check that NSEC chain is cyclic.
1138  *
1139  * Run only once per zone. Check that last NSEC node points to first one.
1140  * \param data Semantic checks context data
1141  */
check_nsec_cyclic(semchecks_data_t * data)1142 static int check_nsec_cyclic(semchecks_data_t *data)
1143 {
1144 	if (data->next_nsec == NULL) {
1145 		data->handler->cb(data->handler, data->zone, data->zone->apex,
1146 		                  SEM_ERR_NSEC_RDATA_CHAIN, NULL);
1147 		return KNOT_EOK;
1148 	}
1149 	if (!knot_dname_is_equal(data->next_nsec->owner, data->zone->apex->owner)) {
1150 		data->handler->cb(data->handler, data->zone, data->next_nsec,
1151 		                  SEM_ERR_NSEC_RDATA_CHAIN, NULL);
1152 	}
1153 
1154 	return KNOT_EOK;
1155 }
1156 
1157 /*!
1158  * \brief Call all semantic checks for each node.
1159  *
1160  * This function is called as callback from zone_contents_tree_apply_inorder.
1161  * Checks are functions from global const array check_functions.
1162  *
1163  * \param node Node to be checked
1164  * \param data Semantic checks context data
1165  */
do_checks_in_tree(zone_node_t * node,void * data)1166 static int do_checks_in_tree(zone_node_t *node, void *data)
1167 {
1168 	semchecks_data_t *s_data = (semchecks_data_t *)data;
1169 
1170 	int ret = KNOT_EOK;
1171 
1172 	for (int i = 0; ret == KNOT_EOK && i < CHECK_FUNCTIONS_LEN; ++i) {
1173 		if (CHECK_FUNCTIONS[i].level & s_data->level) {
1174 			ret = CHECK_FUNCTIONS[i].function(node, s_data);
1175 		}
1176 	}
1177 
1178 
1179 	return ret;
1180 }
1181 
check_nsec3param(knot_rdataset_t * nsec3param,zone_contents_t * zone,sem_handler_t * handler,semchecks_data_t * data)1182 static void check_nsec3param(knot_rdataset_t *nsec3param, zone_contents_t *zone,
1183                              sem_handler_t *handler, semchecks_data_t *data)
1184 {
1185 	assert(nsec3param);
1186 
1187 	data->level |= NSEC3;
1188 	uint8_t param = knot_nsec3param_flags(nsec3param->rdata);
1189 	if ((param & ~1) != 0) {
1190 		handler->cb(handler, zone, zone->apex, SEM_ERR_NSEC3PARAM_RDATA_FLAGS,
1191 		            NULL);
1192 	}
1193 
1194 	param = knot_nsec3param_alg(nsec3param->rdata);
1195 	if (param != DNSSEC_NSEC3_ALGORITHM_SHA1) {
1196 		handler->cb(handler, zone, zone->apex, SEM_ERR_NSEC3PARAM_RDATA_ALG,
1197 		            NULL);
1198 	}
1199 }
1200 
check_dnskey(zone_contents_t * zone,sem_handler_t * handler)1201 static void check_dnskey(zone_contents_t *zone, sem_handler_t *handler)
1202 {
1203 	const knot_rdataset_t *dnskeys = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY);
1204 	if (dnskeys == NULL) {
1205 		handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_NONE, NULL);
1206 		return;
1207 	}
1208 
1209 	for (int i = 0; i < dnskeys->count; i++) {
1210 		knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i);
1211 		dnssec_key_t *key;
1212 		int ret = dnssec_key_from_rdata(&key, zone->apex->owner,
1213 		                                dnskey->data, dnskey->len);
1214 		if (ret == KNOT_EOK) {
1215 			dnssec_key_free(key);
1216 		} else {
1217 			handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_INVALID, NULL);
1218 		}
1219 
1220 		if (knot_dnskey_proto(dnskey) != 3) {
1221 			handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_RDATA_PROTOCOL,
1222 			            NULL);
1223 		}
1224 
1225 		dnssec_key_algorithm_t alg = knot_dnskey_alg(dnskey);
1226 		if (!dnssec_algorithm_key_support(alg)) {
1227 			char *info = sprintf_alloc("(unsupported algorithm %d)", alg);
1228 			handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_INVALID, info);
1229 			free(info);
1230 		}
1231 	}
1232 }
1233 
mark_nsec3_optout(zone_node_t * node,_unused_ void * ctx)1234 static int mark_nsec3_optout(zone_node_t *node, _unused_ void *ctx)
1235 {
1236 	if (nsec3_optout_allow(node) && node_nsec3_get(node) == NULL) {
1237 		do {
1238 			assert(!(node->flags & NODE_FLAGS_APEX));
1239 			node->flags |= NODE_FLAGS_EMPTY;
1240 			node = node_parent(node);
1241 			node->children--;
1242 		} while (node->rrset_count == 0 && node->children == 0 && node_nsec3_get(node) == NULL);
1243 	}
1244 	return KNOT_EOK;
1245 }
1246 
unmark_nsec3_optout(zone_node_t * node,_unused_ void * ctx)1247 static int unmark_nsec3_optout(zone_node_t *node, _unused_ void *ctx)
1248 {
1249 	if (node->flags & NODE_FLAGS_EMPTY) {
1250 		node->flags &= ~NODE_FLAGS_EMPTY;
1251 		node_parent(node)->children++;
1252 	}
1253 	return KNOT_EOK;
1254 }
1255 
sem_checks_process(zone_contents_t * zone,semcheck_optional_t optional,sem_handler_t * handler,time_t time)1256 int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_handler_t *handler,
1257                        time_t time)
1258 {
1259 	if (handler == NULL) {
1260 		return KNOT_EINVAL;
1261 	}
1262 
1263 	if (zone == NULL) {
1264 		return KNOT_EEMPTYZONE;
1265 	}
1266 
1267 	semchecks_data_t data = {
1268 		.handler = handler,
1269 		.zone = zone,
1270 		.next_nsec = zone->apex,
1271 		.level = MANDATORY,
1272 		.time = time,
1273 	};
1274 
1275 	if (optional != SEMCHECK_MANDATORY_ONLY) {
1276 		data.level |= OPTIONAL;
1277 		if (optional == SEMCHECK_DNSSEC ||
1278 		    (optional == SEMCHECK_AUTO_DNSSEC && zone->dnssec)) {
1279 			knot_rdataset_t *nsec3param = node_rdataset(zone->apex,
1280 			                                            KNOT_RRTYPE_NSEC3PARAM);
1281 			if (nsec3param != NULL) {
1282 				data.level |= NSEC3;
1283 				check_nsec3param(nsec3param, zone, handler, &data);
1284 			} else {
1285 				data.level |= NSEC;
1286 			}
1287 			check_dnskey(zone, handler);
1288 		}
1289 	}
1290 
1291 	if (data.level & NSEC3) {
1292 		int ret = zone_tree_apply(zone->nodes, mark_nsec3_optout, NULL);
1293 		if (ret != KNOT_EOK) {
1294 			return ret;
1295 		}
1296 	}
1297 	int ret = zone_contents_apply(zone, do_checks_in_tree, &data);
1298 	if (data.level & NSEC3) {
1299 		(void)zone_tree_apply(zone->nodes, unmark_nsec3_optout, NULL);
1300 	}
1301 	if (ret != KNOT_EOK) {
1302 		return ret;
1303 	}
1304 	if (data.handler->fatal_error) {
1305 		return KNOT_ESEMCHECK;
1306 	}
1307 
1308 	// check cyclic chain after every node was checked
1309 	if (data.level & NSEC) {
1310 		check_nsec_cyclic(&data);
1311 	}
1312 	if (data.handler->fatal_error) {
1313 		return KNOT_ESEMCHECK;
1314 	}
1315 
1316 	return KNOT_EOK;
1317 }
1318