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