]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-dnssec.c
resolved: add basic proof of non-existance support for NSEC+NSEC3
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <gcrypt.h>
23
24 #include "alloc-util.h"
25 #include "dns-domain.h"
26 #include "hexdecoct.h"
27 #include "resolved-dns-dnssec.h"
28 #include "resolved-dns-packet.h"
29 #include "string-table.h"
30
31 /* Open question:
32 *
33 * How does the DNSSEC canonical form of a hostname with a label
34 * containing a dot look like, the way DNS-SD does it?
35 *
36 * TODO:
37 *
38 * - Iterative validation
39 * - NSEC proof of non-existance
40 * - NSEC3 proof of non-existance
41 * - Make trust anchor store read additional DS+DNSKEY data from disk
42 * - wildcard zones compatibility
43 * - multi-label zone compatibility
44 * - DNSSEC cname/dname compatibility
45 * - per-interface DNSSEC setting
46 * - DSA support
47 * - EC support?
48 *
49 * */
50
51 #define VERIFY_RRS_MAX 256
52 #define MAX_KEY_SIZE (32*1024)
53
54 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
55 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
56
57 /*
58 * The DNSSEC Chain of trust:
59 *
60 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
61 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
62 * DS RRs are protected like normal RRs
63 *
64 * Example chain:
65 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
66 */
67
68 static void initialize_libgcrypt(void) {
69 const char *p;
70
71 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
72 return;
73
74 p = gcry_check_version("1.4.5");
75 assert(p);
76
77 gcry_control(GCRYCTL_DISABLE_SECMEM);
78 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
79 }
80
81 static bool dnssec_algorithm_supported(int algorithm) {
82 return IN_SET(algorithm,
83 DNSSEC_ALGORITHM_RSASHA1,
84 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
85 DNSSEC_ALGORITHM_RSASHA256,
86 DNSSEC_ALGORITHM_RSASHA512);
87 }
88
89 uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
90 const uint8_t *p;
91 uint32_t sum;
92 size_t i;
93
94 /* The algorithm from RFC 4034, Appendix B. */
95
96 assert(dnskey);
97 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
98
99 sum = (uint32_t) dnskey->dnskey.flags +
100 ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
101
102 p = dnskey->dnskey.key;
103
104 for (i = 0; i < dnskey->dnskey.key_size; i++)
105 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
106
107 sum += (sum >> 16) & UINT32_C(0xFFFF);
108
109 return sum & UINT32_C(0xFFFF);
110 }
111
112 static int rr_compare(const void *a, const void *b) {
113 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
114 size_t m;
115 int r;
116
117 /* Let's order the RRs according to RFC 4034, Section 6.3 */
118
119 assert(x);
120 assert(*x);
121 assert((*x)->wire_format);
122 assert(y);
123 assert(*y);
124 assert((*y)->wire_format);
125
126 m = MIN((*x)->wire_format_size, (*y)->wire_format_size);
127
128 r = memcmp((*x)->wire_format, (*y)->wire_format, m);
129 if (r != 0)
130 return r;
131
132 if ((*x)->wire_format_size < (*y)->wire_format_size)
133 return -1;
134 else if ((*x)->wire_format_size > (*y)->wire_format_size)
135 return 1;
136
137 return 0;
138 }
139
140 static int dnssec_rsa_verify(
141 const char *hash_algorithm,
142 const void *signature, size_t signature_size,
143 const void *data, size_t data_size,
144 const void *exponent, size_t exponent_size,
145 const void *modulus, size_t modulus_size) {
146
147 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
148 gcry_mpi_t n = NULL, e = NULL, s = NULL;
149 gcry_error_t ge;
150 int r;
151
152 assert(hash_algorithm);
153
154 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
155 if (ge != 0) {
156 r = -EIO;
157 goto finish;
158 }
159
160 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
161 if (ge != 0) {
162 r = -EIO;
163 goto finish;
164 }
165
166 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
167 if (ge != 0) {
168 r = -EIO;
169 goto finish;
170 }
171
172 ge = gcry_sexp_build(&signature_sexp,
173 NULL,
174 "(sig-val (rsa (s %m)))",
175 s);
176
177 if (ge != 0) {
178 r = -EIO;
179 goto finish;
180 }
181
182 ge = gcry_sexp_build(&data_sexp,
183 NULL,
184 "(data (flags pkcs1) (hash %s %b))",
185 hash_algorithm,
186 (int) data_size,
187 data);
188 if (ge != 0) {
189 r = -EIO;
190 goto finish;
191 }
192
193 ge = gcry_sexp_build(&public_key_sexp,
194 NULL,
195 "(public-key (rsa (n %m) (e %m)))",
196 n,
197 e);
198 if (ge != 0) {
199 r = -EIO;
200 goto finish;
201 }
202
203 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
204 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
205 r = 0;
206 else if (ge != 0) {
207 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
208 r = -EIO;
209 } else
210 r = 1;
211
212 finish:
213 if (e)
214 gcry_mpi_release(e);
215 if (n)
216 gcry_mpi_release(n);
217 if (s)
218 gcry_mpi_release(s);
219
220 if (public_key_sexp)
221 gcry_sexp_release(public_key_sexp);
222 if (signature_sexp)
223 gcry_sexp_release(signature_sexp);
224 if (data_sexp)
225 gcry_sexp_release(data_sexp);
226
227 return r;
228 }
229
230 static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
231 gcry_md_write(md, &v, sizeof(v));
232 }
233
234 static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
235 v = htobe16(v);
236 gcry_md_write(md, &v, sizeof(v));
237 }
238
239 static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
240 v = htobe32(v);
241 gcry_md_write(md, &v, sizeof(v));
242 }
243
244 static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
245 usec_t expiration, inception, skew;
246
247 assert(rrsig);
248 assert(rrsig->key->type == DNS_TYPE_RRSIG);
249
250 if (realtime == USEC_INFINITY)
251 realtime = now(CLOCK_REALTIME);
252
253 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
254 inception = rrsig->rrsig.inception * USEC_PER_SEC;
255
256 if (inception > expiration)
257 return -EKEYREJECTED;
258
259 /* Permit a certain amount of clock skew of 10% of the valid
260 * time range. This takes inspiration from unbound's
261 * resolver. */
262 skew = (expiration - inception) / 10;
263 if (skew > SKEW_MAX)
264 skew = SKEW_MAX;
265
266 if (inception < skew)
267 inception = 0;
268 else
269 inception -= skew;
270
271 if (expiration + skew < expiration)
272 expiration = USEC_INFINITY;
273 else
274 expiration += skew;
275
276 return realtime < inception || realtime > expiration;
277 }
278
279 int dnssec_verify_rrset(
280 DnsAnswer *a,
281 DnsResourceKey *key,
282 DnsResourceRecord *rrsig,
283 DnsResourceRecord *dnskey,
284 usec_t realtime,
285 DnssecResult *result) {
286
287 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
288 size_t exponent_size, modulus_size, hash_size;
289 void *exponent, *modulus, *hash;
290 DnsResourceRecord **list, *rr;
291 gcry_md_hd_t md = NULL;
292 size_t k, n = 0;
293 int r;
294
295 assert(key);
296 assert(rrsig);
297 assert(dnskey);
298 assert(result);
299 assert(rrsig->key->type == DNS_TYPE_RRSIG);
300 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
301
302 /* Verifies the the RRSet matching the specified "key" in "a",
303 * using the signature "rrsig" and the key "dnskey". It's
304 * assumed the RRSIG and DNSKEY match. */
305
306 if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
307 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
308 return 0;
309 }
310
311 if (a->n_rrs > VERIFY_RRS_MAX)
312 return -E2BIG;
313
314 r = dnssec_rrsig_expired(rrsig, realtime);
315 if (r < 0)
316 return r;
317 if (r > 0) {
318 *result = DNSSEC_SIGNATURE_EXPIRED;
319 return 0;
320 }
321
322 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
323 list = newa(DnsResourceRecord *, a->n_rrs);
324
325 DNS_ANSWER_FOREACH(rr, a) {
326 r = dns_resource_key_equal(key, rr->key);
327 if (r < 0)
328 return r;
329 if (r == 0)
330 continue;
331
332 /* We need the wire format for ordering, and digest calculation */
333 r = dns_resource_record_to_wire_format(rr, true);
334 if (r < 0)
335 return r;
336
337 list[n++] = rr;
338 }
339
340 if (n <= 0)
341 return -ENODATA;
342
343 /* Bring the RRs into canonical order */
344 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
345
346 initialize_libgcrypt();
347
348 /* OK, the RRs are now in canonical order. Let's calculate the digest */
349 switch (rrsig->rrsig.algorithm) {
350
351 case DNSSEC_ALGORITHM_RSASHA1:
352 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
353 gcry_md_open(&md, GCRY_MD_SHA1, 0);
354 hash_size = 20;
355 break;
356
357 case DNSSEC_ALGORITHM_RSASHA256:
358 gcry_md_open(&md, GCRY_MD_SHA256, 0);
359 hash_size = 32;
360 break;
361
362 case DNSSEC_ALGORITHM_RSASHA512:
363 gcry_md_open(&md, GCRY_MD_SHA512, 0);
364 hash_size = 64;
365 break;
366
367 default:
368 assert_not_reached("Unknown digest");
369 }
370
371 if (!md)
372 return -EIO;
373
374 md_add_uint16(md, rrsig->rrsig.type_covered);
375 md_add_uint8(md, rrsig->rrsig.algorithm);
376 md_add_uint8(md, rrsig->rrsig.labels);
377 md_add_uint32(md, rrsig->rrsig.original_ttl);
378 md_add_uint32(md, rrsig->rrsig.expiration);
379 md_add_uint32(md, rrsig->rrsig.inception);
380 md_add_uint16(md, rrsig->rrsig.key_tag);
381
382 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
383 if (r < 0)
384 goto finish;
385 gcry_md_write(md, wire_format_name, r);
386
387 for (k = 0; k < n; k++) {
388 size_t l;
389 rr = list[k];
390
391 r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
392 if (r < 0)
393 goto finish;
394 gcry_md_write(md, wire_format_name, r);
395
396 md_add_uint16(md, rr->key->type);
397 md_add_uint16(md, rr->key->class);
398 md_add_uint32(md, rrsig->rrsig.original_ttl);
399
400 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
401 l = rr->wire_format_size - rr->wire_format_rdata_offset;
402 assert(l <= 0xFFFF);
403
404 md_add_uint16(md, (uint16_t) l);
405 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
406 }
407
408 hash = gcry_md_read(md, 0);
409 if (!hash) {
410 r = -EIO;
411 goto finish;
412 }
413
414 if (*(uint8_t*) dnskey->dnskey.key == 0) {
415 /* exponent is > 255 bytes long */
416
417 exponent = (uint8_t*) dnskey->dnskey.key + 3;
418 exponent_size =
419 ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
420 ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
421
422 if (exponent_size < 256) {
423 r = -EINVAL;
424 goto finish;
425 }
426
427 if (3 + exponent_size >= dnskey->dnskey.key_size) {
428 r = -EINVAL;
429 goto finish;
430 }
431
432 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
433 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
434
435 } else {
436 /* exponent is <= 255 bytes long */
437
438 exponent = (uint8_t*) dnskey->dnskey.key + 1;
439 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
440
441 if (exponent_size <= 0) {
442 r = -EINVAL;
443 goto finish;
444 }
445
446 if (1 + exponent_size >= dnskey->dnskey.key_size) {
447 r = -EINVAL;
448 goto finish;
449 }
450
451 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
452 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
453 }
454
455 r = dnssec_rsa_verify(
456 gcry_md_algo_name(gcry_md_get_algo(md)),
457 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
458 hash, hash_size,
459 exponent, exponent_size,
460 modulus, modulus_size);
461 if (r < 0)
462 goto finish;
463
464 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
465 r = 0;
466
467 finish:
468 gcry_md_close(md);
469 return r;
470 }
471
472 int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
473
474 assert(rrsig);
475 assert(dnskey);
476
477 /* Checks if the specified DNSKEY RR matches the key used for
478 * the signature in the specified RRSIG RR */
479
480 if (rrsig->key->type != DNS_TYPE_RRSIG)
481 return -EINVAL;
482
483 if (dnskey->key->type != DNS_TYPE_DNSKEY)
484 return 0;
485 if (dnskey->key->class != rrsig->key->class)
486 return 0;
487 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
488 return 0;
489 if (dnskey->dnskey.protocol != 3)
490 return 0;
491 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
492 return 0;
493
494 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
495 return 0;
496
497 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
498 }
499
500 int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
501 assert(key);
502 assert(rrsig);
503
504 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
505
506 if (rrsig->key->type != DNS_TYPE_RRSIG)
507 return 0;
508 if (rrsig->key->class != key->class)
509 return 0;
510 if (rrsig->rrsig.type_covered != key->type)
511 return 0;
512
513 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
514 }
515
516 int dnssec_verify_rrset_search(
517 DnsAnswer *a,
518 DnsResourceKey *key,
519 DnsAnswer *validated_dnskeys,
520 usec_t realtime,
521 DnssecResult *result) {
522
523 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
524 DnsResourceRecord *rrsig;
525 int r;
526
527 assert(key);
528 assert(result);
529
530 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
531
532 if (!a || a->n_rrs <= 0)
533 return -ENODATA;
534
535 /* Iterate through each RRSIG RR. */
536 DNS_ANSWER_FOREACH(rrsig, a) {
537 DnsResourceRecord *dnskey;
538
539 /* Is this an RRSIG RR that applies to RRs matching our key? */
540 r = dnssec_key_match_rrsig(key, rrsig);
541 if (r < 0)
542 return r;
543 if (r == 0)
544 continue;
545
546 found_rrsig = true;
547
548 /* Look for a matching key */
549 DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
550 DnssecResult one_result;
551
552 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
553 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
554 if (r < 0)
555 return r;
556 if (r == 0)
557 continue;
558
559 /* Take the time here, if it isn't set yet, so
560 * that we do all validations with the same
561 * time. */
562 if (realtime == USEC_INFINITY)
563 realtime = now(CLOCK_REALTIME);
564
565 /* Yay, we found a matching RRSIG with a matching
566 * DNSKEY, awesome. Now let's verify all entries of
567 * the RRSet against the RRSIG and DNSKEY
568 * combination. */
569
570 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
571 if (r < 0)
572 return r;
573
574 switch (one_result) {
575
576 case DNSSEC_VALIDATED:
577 /* Yay, the RR has been validated,
578 * return immediately. */
579 *result = DNSSEC_VALIDATED;
580 return 0;
581
582 case DNSSEC_INVALID:
583 /* If the signature is invalid, let's try another
584 key and/or signature. After all they
585 key_tags and stuff are not unique, and
586 might be shared by multiple keys. */
587 found_invalid = true;
588 continue;
589
590 case DNSSEC_UNSUPPORTED_ALGORITHM:
591 /* If the key algorithm is
592 unsupported, try another
593 RRSIG/DNSKEY pair, but remember we
594 encountered this, so that we can
595 return a proper error when we
596 encounter nothing better. */
597 found_unsupported_algorithm = true;
598 continue;
599
600 case DNSSEC_SIGNATURE_EXPIRED:
601 /* If the signature is expired, try
602 another one, but remember it, so
603 that we can return this */
604 found_expired_rrsig = true;
605 continue;
606
607 default:
608 assert_not_reached("Unexpected DNSSEC validation result");
609 }
610 }
611 }
612
613 if (found_expired_rrsig)
614 *result = DNSSEC_SIGNATURE_EXPIRED;
615 else if (found_unsupported_algorithm)
616 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
617 else if (found_invalid)
618 *result = DNSSEC_INVALID;
619 else if (found_rrsig)
620 *result = DNSSEC_MISSING_KEY;
621 else
622 *result = DNSSEC_NO_SIGNATURE;
623
624 return 0;
625 }
626
627 int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
628 size_t c = 0;
629 int r;
630
631 /* Converts the specified hostname into DNSSEC canonicalized
632 * form. */
633
634 if (buffer_max < 2)
635 return -ENOBUFS;
636
637 for (;;) {
638 size_t i;
639
640 r = dns_label_unescape(&n, buffer, buffer_max);
641 if (r < 0)
642 return r;
643 if (r == 0)
644 break;
645 if (r > 0) {
646 int k;
647
648 /* DNSSEC validation is always done on the ASCII version of the label */
649 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
650 if (k < 0)
651 return k;
652 if (k > 0)
653 r = k;
654 }
655
656 if (buffer_max < (size_t) r + 2)
657 return -ENOBUFS;
658
659 /* The DNSSEC canonical form is not clear on what to
660 * do with dots appearing in labels, the way DNS-SD
661 * does it. Refuse it for now. */
662
663 if (memchr(buffer, '.', r))
664 return -EINVAL;
665
666 for (i = 0; i < (size_t) r; i ++) {
667 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
668 buffer[i] = buffer[i] - 'A' + 'a';
669 }
670
671 buffer[r] = '.';
672
673 buffer += r + 1;
674 c += r + 1;
675
676 buffer_max -= r + 1;
677 }
678
679 if (c <= 0) {
680 /* Not even a single label: this is the root domain name */
681
682 assert(buffer_max > 2);
683 buffer[0] = '.';
684 buffer[1] = 0;
685
686 return 1;
687 }
688
689 return (int) c;
690 }
691
692 static int digest_to_gcrypt(uint8_t algorithm) {
693
694 /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
695
696 switch (algorithm) {
697
698 case DNSSEC_DIGEST_SHA1:
699 return GCRY_MD_SHA1;
700
701 case DNSSEC_DIGEST_SHA256:
702 return GCRY_MD_SHA256;
703
704 default:
705 return -EOPNOTSUPP;
706 }
707 }
708
709 int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
710 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
711 gcry_md_hd_t md = NULL;
712 size_t hash_size;
713 int algorithm;
714 void *result;
715 int r;
716
717 assert(dnskey);
718 assert(ds);
719
720 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
721
722 if (dnskey->key->type != DNS_TYPE_DNSKEY)
723 return -EINVAL;
724 if (ds->key->type != DNS_TYPE_DS)
725 return -EINVAL;
726 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
727 return -EKEYREJECTED;
728 if (dnskey->dnskey.protocol != 3)
729 return -EKEYREJECTED;
730
731 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
732 return 0;
733 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
734 return 0;
735
736 initialize_libgcrypt();
737
738 algorithm = digest_to_gcrypt(ds->ds.digest_type);
739 if (algorithm < 0)
740 return algorithm;
741
742 hash_size = gcry_md_get_algo_dlen(algorithm);
743 assert(hash_size > 0);
744
745 if (ds->ds.digest_size != hash_size)
746 return 0;
747
748 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
749 if (r < 0)
750 return r;
751
752 gcry_md_open(&md, algorithm, 0);
753 if (!md)
754 return -EIO;
755
756 gcry_md_write(md, owner_name, r);
757 md_add_uint16(md, dnskey->dnskey.flags);
758 md_add_uint8(md, dnskey->dnskey.protocol);
759 md_add_uint8(md, dnskey->dnskey.algorithm);
760 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
761
762 result = gcry_md_read(md, 0);
763 if (!result) {
764 r = -EIO;
765 goto finish;
766 }
767
768 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
769
770 finish:
771 gcry_md_close(md);
772 return r;
773 }
774
775 int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
776 DnsResourceRecord *ds;
777 int r;
778
779 assert(dnskey);
780
781 if (dnskey->key->type != DNS_TYPE_DNSKEY)
782 return 0;
783
784 DNS_ANSWER_FOREACH(ds, validated_ds) {
785
786 if (ds->key->type != DNS_TYPE_DS)
787 continue;
788
789 r = dnssec_verify_dnskey(dnskey, ds);
790 if (r < 0)
791 return r;
792 if (r > 0)
793 return 1;
794 }
795
796 return 0;
797 }
798
799 int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
800 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
801 gcry_md_hd_t md = NULL;
802 size_t hash_size;
803 int algorithm;
804 void *result;
805 unsigned k;
806 int r;
807
808 assert(nsec3);
809 assert(name);
810 assert(ret);
811
812 if (nsec3->key->type != DNS_TYPE_NSEC3)
813 return -EINVAL;
814
815 algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm);
816 if (algorithm < 0)
817 return algorithm;
818
819 initialize_libgcrypt();
820
821 hash_size = gcry_md_get_algo_dlen(algorithm);
822 assert(hash_size > 0);
823
824 if (nsec3->nsec3.next_hashed_name_size != hash_size)
825 return -EINVAL;
826
827 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
828 if (r < 0)
829 return r;
830
831 gcry_md_open(&md, algorithm, 0);
832 if (!md)
833 return -EIO;
834
835 gcry_md_write(md, wire_format, r);
836 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
837
838 result = gcry_md_read(md, 0);
839 if (!result) {
840 r = -EIO;
841 goto finish;
842 }
843
844 for (k = 0; k < nsec3->nsec3.iterations; k++) {
845 uint8_t tmp[hash_size];
846 memcpy(tmp, result, hash_size);
847
848 gcry_md_reset(md);
849 gcry_md_write(md, tmp, hash_size);
850 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
851
852 result = gcry_md_read(md, 0);
853 if (!result) {
854 r = -EIO;
855 goto finish;
856 }
857 }
858
859 memcpy(ret, result, hash_size);
860 r = (int) hash_size;
861
862 finish:
863 gcry_md_close(md);
864 return r;
865 }
866
867 int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
868 DnsResourceRecord *rr;
869 int r;
870
871 assert(key);
872 assert(result);
873
874 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
875
876 DNS_ANSWER_FOREACH(rr, answer) {
877
878 if (rr->key->class != key->class)
879 continue;
880
881 switch (rr->key->type) {
882
883 case DNS_TYPE_NSEC:
884
885 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
886 if (r < 0)
887 return r;
888 if (r > 0) {
889 *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
890 return 0;
891 }
892
893 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
894 if (r < 0)
895 return r;
896 if (r > 0) {
897 *result = DNSSEC_NSEC_NXDOMAIN;
898 return 0;
899 }
900 break;
901
902 case DNS_TYPE_NSEC3: {
903 _cleanup_free_ void *decoded = NULL;
904 size_t decoded_size;
905 char label[DNS_LABEL_MAX];
906 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
907 int label_length, c, q;
908 const char *p;
909 bool covered;
910
911 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
912 if (!IN_SET(rr->nsec3.flags, 0, 1))
913 continue;
914
915 p = DNS_RESOURCE_KEY_NAME(rr->key);
916 label_length = dns_label_unescape(&p, label, sizeof(label));
917 if (label_length < 0)
918 return label_length;
919 if (label_length == 0)
920 continue;
921
922 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), p);
923 if (r < 0)
924 return r;
925 if (r == 0)
926 continue;
927
928 r = unbase32hexmem(label, label_length, false, &decoded, &decoded_size);
929 if (r == -EINVAL)
930 continue;
931 if (r < 0)
932 return r;
933
934 if (decoded_size != rr->nsec3.next_hashed_name_size)
935 continue;
936
937 c = memcmp(decoded, rr->nsec3.next_hashed_name, decoded_size);
938 if (c == 0)
939 continue;
940
941 r = dnssec_nsec3_hash(rr, DNS_RESOURCE_KEY_NAME(key), hashed);
942 /* RFC 5155, Section 8.1 says we MUST ignore NSEC3 RRs with unknown algorithms */
943 if (r == -EOPNOTSUPP)
944 continue;
945 if (r < 0)
946 return r;
947 if ((size_t) r != decoded_size)
948 continue;
949
950 r = memcmp(decoded, hashed, decoded_size);
951 if (r == 0) {
952 *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
953 return 0;
954 }
955
956 q = memcmp(hashed, rr->nsec3.next_hashed_name, decoded_size);
957
958 covered = c < 0 ?
959 r < 0 && q < 0 :
960 q < 0 || r < 0;
961
962 if (covered) {
963 *result = DNSSEC_NSEC_NXDOMAIN;
964 return 0;
965 }
966
967 break;
968 }
969
970 default:
971 break;
972 }
973 }
974
975 /* No approproate NSEC RR found, report this. */
976 *result = DNSSEC_NSEC_NO_RR;
977 return 0;
978 }
979
980 static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
981 [DNSSEC_NO] = "no",
982 [DNSSEC_TRUST] = "trust",
983 [DNSSEC_YES] = "yes",
984 };
985 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
986
987 static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
988 [DNSSEC_VALIDATED] = "validated",
989 [DNSSEC_INVALID] = "invalid",
990 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
991 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
992 [DNSSEC_NO_SIGNATURE] = "no-signature",
993 [DNSSEC_MISSING_KEY] = "missing-key",
994 [DNSSEC_UNSIGNED] = "unsigned",
995 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
996 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
997 };
998 DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);