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(¶ms_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, ¶ms_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(¶ms_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