]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-dnssec.c
resolved: rework dnssec validation results
[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 "resolved-dns-dnssec.h"
27 #include "resolved-dns-packet.h"
28 #include "string-table.h"
29
30 /* Open question:
31 *
32 * How does the DNSSEC canonical form of a hostname with a label
33 * containing a dot look like, the way DNS-SD does it?
34 *
35 * TODO:
36 *
37 * - Iterative validation
38 * - NSEC proof of non-existance
39 * - NSEC3 proof of non-existance
40 * - Make trust anchor store read additional DS+DNSKEY data from disk
41 * - wildcard zones compatibility
42 * - multi-label zone compatibility
43 * - DNSSEC cname/dname compatibility
44 * - per-interface DNSSEC setting
45 * - DSA support
46 * - EC support?
47 *
48 * */
49
50 #define VERIFY_RRS_MAX 256
51 #define MAX_KEY_SIZE (32*1024)
52
53 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
54 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
55
56 /*
57 * The DNSSEC Chain of trust:
58 *
59 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
60 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
61 * DS RRs are protected like normal RRs
62 *
63 * Example chain:
64 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
65 */
66
67 static bool dnssec_algorithm_supported(int algorithm) {
68 return IN_SET(algorithm,
69 DNSSEC_ALGORITHM_RSASHA1,
70 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
71 DNSSEC_ALGORITHM_RSASHA256,
72 DNSSEC_ALGORITHM_RSASHA512);
73 }
74
75 static bool dnssec_digest_supported(int digest) {
76 return IN_SET(digest,
77 DNSSEC_DIGEST_SHA1,
78 DNSSEC_DIGEST_SHA256);
79 }
80
81 uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
82 const uint8_t *p;
83 uint32_t sum;
84 size_t i;
85
86 /* The algorithm from RFC 4034, Appendix B. */
87
88 assert(dnskey);
89 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
90
91 sum = (uint32_t) dnskey->dnskey.flags +
92 ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
93
94 p = dnskey->dnskey.key;
95
96 for (i = 0; i < dnskey->dnskey.key_size; i++)
97 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
98
99 sum += (sum >> 16) & UINT32_C(0xFFFF);
100
101 return sum & UINT32_C(0xFFFF);
102 }
103
104 static int rr_compare(const void *a, const void *b) {
105 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
106 size_t m;
107 int r;
108
109 /* Let's order the RRs according to RFC 4034, Section 6.3 */
110
111 assert(x);
112 assert(*x);
113 assert((*x)->wire_format);
114 assert(y);
115 assert(*y);
116 assert((*y)->wire_format);
117
118 m = MIN((*x)->wire_format_size, (*y)->wire_format_size);
119
120 r = memcmp((*x)->wire_format, (*y)->wire_format, m);
121 if (r != 0)
122 return r;
123
124 if ((*x)->wire_format_size < (*y)->wire_format_size)
125 return -1;
126 else if ((*x)->wire_format_size > (*y)->wire_format_size)
127 return 1;
128
129 return 0;
130 }
131
132 static int dnssec_rsa_verify(
133 const char *hash_algorithm,
134 const void *signature, size_t signature_size,
135 const void *data, size_t data_size,
136 const void *exponent, size_t exponent_size,
137 const void *modulus, size_t modulus_size) {
138
139 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
140 gcry_mpi_t n = NULL, e = NULL, s = NULL;
141 gcry_error_t ge;
142 int r;
143
144 assert(hash_algorithm);
145
146 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
147 if (ge != 0) {
148 r = -EIO;
149 goto finish;
150 }
151
152 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
153 if (ge != 0) {
154 r = -EIO;
155 goto finish;
156 }
157
158 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
159 if (ge != 0) {
160 r = -EIO;
161 goto finish;
162 }
163
164 ge = gcry_sexp_build(&signature_sexp,
165 NULL,
166 "(sig-val (rsa (s %m)))",
167 s);
168
169 if (ge != 0) {
170 r = -EIO;
171 goto finish;
172 }
173
174 ge = gcry_sexp_build(&data_sexp,
175 NULL,
176 "(data (flags pkcs1) (hash %s %b))",
177 hash_algorithm,
178 (int) data_size,
179 data);
180 if (ge != 0) {
181 r = -EIO;
182 goto finish;
183 }
184
185 ge = gcry_sexp_build(&public_key_sexp,
186 NULL,
187 "(public-key (rsa (n %m) (e %m)))",
188 n,
189 e);
190 if (ge != 0) {
191 r = -EIO;
192 goto finish;
193 }
194
195 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
196 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
197 r = 0;
198 else if (ge != 0) {
199 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
200 r = -EIO;
201 } else
202 r = 1;
203
204 finish:
205 if (e)
206 gcry_mpi_release(e);
207 if (n)
208 gcry_mpi_release(n);
209 if (s)
210 gcry_mpi_release(s);
211
212 if (public_key_sexp)
213 gcry_sexp_release(public_key_sexp);
214 if (signature_sexp)
215 gcry_sexp_release(signature_sexp);
216 if (data_sexp)
217 gcry_sexp_release(data_sexp);
218
219 return r;
220 }
221
222 static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
223 gcry_md_write(md, &v, sizeof(v));
224 }
225
226 static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
227 v = htobe16(v);
228 gcry_md_write(md, &v, sizeof(v));
229 }
230
231 static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
232 v = htobe32(v);
233 gcry_md_write(md, &v, sizeof(v));
234 }
235
236 static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
237 usec_t expiration, inception, skew;
238
239 assert(rrsig);
240 assert(rrsig->key->type == DNS_TYPE_RRSIG);
241
242 if (realtime == USEC_INFINITY)
243 realtime = now(CLOCK_REALTIME);
244
245 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
246 inception = rrsig->rrsig.inception * USEC_PER_SEC;
247
248 if (inception > expiration)
249 return -EKEYREJECTED;
250
251 /* Permit a certain amount of clock skew of 10% of the valid
252 * time range. This takes inspiration from unbound's
253 * resolver. */
254 skew = (expiration - inception) / 10;
255 if (skew > SKEW_MAX)
256 skew = SKEW_MAX;
257
258 if (inception < skew)
259 inception = 0;
260 else
261 inception -= skew;
262
263 if (expiration + skew < expiration)
264 expiration = USEC_INFINITY;
265 else
266 expiration += skew;
267
268 return realtime < inception || realtime > expiration;
269 }
270
271 int dnssec_verify_rrset(
272 DnsAnswer *a,
273 DnsResourceKey *key,
274 DnsResourceRecord *rrsig,
275 DnsResourceRecord *dnskey,
276 usec_t realtime,
277 DnssecResult *result) {
278
279 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
280 size_t exponent_size, modulus_size, hash_size;
281 void *exponent, *modulus, *hash;
282 DnsResourceRecord **list, *rr;
283 gcry_md_hd_t md = NULL;
284 size_t k, n = 0;
285 int r;
286
287 assert(key);
288 assert(rrsig);
289 assert(dnskey);
290 assert(result);
291 assert(rrsig->key->type == DNS_TYPE_RRSIG);
292 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
293
294 /* Verifies the the RRSet matching the specified "key" in "a",
295 * using the signature "rrsig" and the key "dnskey". It's
296 * assumed the RRSIG and DNSKEY match. */
297
298 if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
299 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
300 return 0;
301 }
302
303 if (a->n_rrs > VERIFY_RRS_MAX)
304 return -E2BIG;
305
306 r = dnssec_rrsig_expired(rrsig, realtime);
307 if (r < 0)
308 return r;
309 if (r > 0) {
310 *result = DNSSEC_SIGNATURE_EXPIRED;
311 return 0;
312 }
313
314 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
315 list = newa(DnsResourceRecord *, a->n_rrs);
316
317 DNS_ANSWER_FOREACH(rr, a) {
318 r = dns_resource_key_equal(key, rr->key);
319 if (r < 0)
320 return r;
321 if (r == 0)
322 continue;
323
324 /* We need the wire format for ordering, and digest calculation */
325 r = dns_resource_record_to_wire_format(rr, true);
326 if (r < 0)
327 return r;
328
329 list[n++] = rr;
330 }
331
332 if (n <= 0)
333 return -ENODATA;
334
335 /* Bring the RRs into canonical order */
336 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
337
338 /* OK, the RRs are now in canonical order. Let's calculate the digest */
339 switch (rrsig->rrsig.algorithm) {
340
341 case DNSSEC_ALGORITHM_RSASHA1:
342 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
343 gcry_md_open(&md, GCRY_MD_SHA1, 0);
344 hash_size = 20;
345 break;
346
347 case DNSSEC_ALGORITHM_RSASHA256:
348 gcry_md_open(&md, GCRY_MD_SHA256, 0);
349 hash_size = 32;
350 break;
351
352 case DNSSEC_ALGORITHM_RSASHA512:
353 gcry_md_open(&md, GCRY_MD_SHA512, 0);
354 hash_size = 64;
355 break;
356
357 default:
358 assert_not_reached("Unknown digest");
359 }
360
361 if (!md)
362 return -EIO;
363
364 md_add_uint16(md, rrsig->rrsig.type_covered);
365 md_add_uint8(md, rrsig->rrsig.algorithm);
366 md_add_uint8(md, rrsig->rrsig.labels);
367 md_add_uint32(md, rrsig->rrsig.original_ttl);
368 md_add_uint32(md, rrsig->rrsig.expiration);
369 md_add_uint32(md, rrsig->rrsig.inception);
370 md_add_uint16(md, rrsig->rrsig.key_tag);
371
372 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
373 if (r < 0)
374 goto finish;
375 gcry_md_write(md, wire_format_name, r);
376
377 for (k = 0; k < n; k++) {
378 size_t l;
379 rr = list[k];
380
381 r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
382 if (r < 0)
383 goto finish;
384 gcry_md_write(md, wire_format_name, r);
385
386 md_add_uint16(md, rr->key->type);
387 md_add_uint16(md, rr->key->class);
388 md_add_uint32(md, rrsig->rrsig.original_ttl);
389
390 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
391 l = rr->wire_format_size - rr->wire_format_rdata_offset;
392 assert(l <= 0xFFFF);
393
394 md_add_uint16(md, (uint16_t) l);
395 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
396 }
397
398 hash = gcry_md_read(md, 0);
399 if (!hash) {
400 r = -EIO;
401 goto finish;
402 }
403
404 if (*(uint8_t*) dnskey->dnskey.key == 0) {
405 /* exponent is > 255 bytes long */
406
407 exponent = (uint8_t*) dnskey->dnskey.key + 3;
408 exponent_size =
409 ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
410 ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
411
412 if (exponent_size < 256) {
413 r = -EINVAL;
414 goto finish;
415 }
416
417 if (3 + exponent_size >= dnskey->dnskey.key_size) {
418 r = -EINVAL;
419 goto finish;
420 }
421
422 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
423 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
424
425 } else {
426 /* exponent is <= 255 bytes long */
427
428 exponent = (uint8_t*) dnskey->dnskey.key + 1;
429 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
430
431 if (exponent_size <= 0) {
432 r = -EINVAL;
433 goto finish;
434 }
435
436 if (1 + exponent_size >= dnskey->dnskey.key_size) {
437 r = -EINVAL;
438 goto finish;
439 }
440
441 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
442 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
443 }
444
445 r = dnssec_rsa_verify(
446 gcry_md_algo_name(gcry_md_get_algo(md)),
447 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
448 hash, hash_size,
449 exponent, exponent_size,
450 modulus, modulus_size);
451 if (r < 0)
452 goto finish;
453
454 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
455 r = 0;
456
457 finish:
458 gcry_md_close(md);
459 return r;
460 }
461
462 int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
463
464 assert(rrsig);
465 assert(dnskey);
466
467 /* Checks if the specified DNSKEY RR matches the key used for
468 * the signature in the specified RRSIG RR */
469
470 if (rrsig->key->type != DNS_TYPE_RRSIG)
471 return -EINVAL;
472
473 if (dnskey->key->type != DNS_TYPE_DNSKEY)
474 return 0;
475 if (dnskey->key->class != rrsig->key->class)
476 return 0;
477 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
478 return 0;
479 if (dnskey->dnskey.protocol != 3)
480 return 0;
481 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
482 return 0;
483
484 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
485 return 0;
486
487 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
488 }
489
490 int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
491 assert(key);
492 assert(rrsig);
493
494 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
495
496 if (rrsig->key->type != DNS_TYPE_RRSIG)
497 return 0;
498 if (rrsig->key->class != key->class)
499 return 0;
500 if (rrsig->rrsig.type_covered != key->type)
501 return 0;
502
503 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
504 }
505
506 int dnssec_verify_rrset_search(
507 DnsAnswer *a,
508 DnsResourceKey *key,
509 DnsAnswer *validated_dnskeys,
510 usec_t realtime,
511 DnssecResult *result) {
512
513 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
514 DnsResourceRecord *rrsig;
515 int r;
516
517 assert(key);
518 assert(result);
519
520 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
521
522 if (!a || a->n_rrs <= 0)
523 return -ENODATA;
524
525 /* Iterate through each RRSIG RR. */
526 DNS_ANSWER_FOREACH(rrsig, a) {
527 DnsResourceRecord *dnskey;
528
529 /* Is this an RRSIG RR that applies to RRs matching our key? */
530 r = dnssec_key_match_rrsig(key, rrsig);
531 if (r < 0)
532 return r;
533 if (r == 0)
534 continue;
535
536 found_rrsig = true;
537
538 /* Look for a matching key */
539 DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
540 DnssecResult one_result;
541
542 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
543 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
544 if (r < 0)
545 return r;
546 if (r == 0)
547 continue;
548
549 /* Take the time here, if it isn't set yet, so
550 * that we do all validations with the same
551 * time. */
552 if (realtime == USEC_INFINITY)
553 realtime = now(CLOCK_REALTIME);
554
555 /* Yay, we found a matching RRSIG with a matching
556 * DNSKEY, awesome. Now let's verify all entries of
557 * the RRSet against the RRSIG and DNSKEY
558 * combination. */
559
560 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
561 if (r < 0)
562 return r;
563
564 switch (one_result) {
565
566 case DNSSEC_VALIDATED:
567 /* Yay, the RR has been validated,
568 * return immediately. */
569 *result = DNSSEC_VALIDATED;
570 return 0;
571
572 case DNSSEC_INVALID:
573 /* If the signature is invalid, let's try another
574 key and/or signature. After all they
575 key_tags and stuff are not unique, and
576 might be shared by multiple keys. */
577 found_invalid = true;
578 continue;
579
580 case DNSSEC_UNSUPPORTED_ALGORITHM:
581 /* If the key algorithm is
582 unsupported, try another
583 RRSIG/DNSKEY pair, but remember we
584 encountered this, so that we can
585 return a proper error when we
586 encounter nothing better. */
587 found_unsupported_algorithm = true;
588 continue;
589
590 case DNSSEC_SIGNATURE_EXPIRED:
591 /* If the signature is expired, try
592 another one, but remember it, so
593 that we can return this */
594 found_expired_rrsig = true;
595 continue;
596
597 default:
598 assert_not_reached("Unexpected DNSSEC validation result");
599 }
600 }
601 }
602
603 if (found_expired_rrsig)
604 *result = DNSSEC_SIGNATURE_EXPIRED;
605 else if (found_unsupported_algorithm)
606 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
607 else if (found_invalid)
608 *result = DNSSEC_INVALID;
609 else if (found_rrsig)
610 *result = DNSSEC_MISSING_KEY;
611 else
612 *result = DNSSEC_NO_SIGNATURE;
613
614 return 0;
615 }
616
617 int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
618 size_t c = 0;
619 int r;
620
621 /* Converts the specified hostname into DNSSEC canonicalized
622 * form. */
623
624 if (buffer_max < 2)
625 return -ENOBUFS;
626
627 for (;;) {
628 size_t i;
629
630 r = dns_label_unescape(&n, buffer, buffer_max);
631 if (r < 0)
632 return r;
633 if (r == 0)
634 break;
635 if (r > 0) {
636 int k;
637
638 /* DNSSEC validation is always done on the ASCII version of the label */
639 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
640 if (k < 0)
641 return k;
642 if (k > 0)
643 r = k;
644 }
645
646 if (buffer_max < (size_t) r + 2)
647 return -ENOBUFS;
648
649 /* The DNSSEC canonical form is not clear on what to
650 * do with dots appearing in labels, the way DNS-SD
651 * does it. Refuse it for now. */
652
653 if (memchr(buffer, '.', r))
654 return -EINVAL;
655
656 for (i = 0; i < (size_t) r; i ++) {
657 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
658 buffer[i] = buffer[i] - 'A' + 'a';
659 }
660
661 buffer[r] = '.';
662
663 buffer += r + 1;
664 c += r + 1;
665
666 buffer_max -= r + 1;
667 }
668
669 if (c <= 0) {
670 /* Not even a single label: this is the root domain name */
671
672 assert(buffer_max > 2);
673 buffer[0] = '.';
674 buffer[1] = 0;
675
676 return 1;
677 }
678
679 return (int) c;
680 }
681
682 int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
683 gcry_md_hd_t md = NULL;
684 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
685 void *result;
686 int r;
687
688 assert(dnskey);
689 assert(ds);
690
691 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
692
693 if (dnskey->key->type != DNS_TYPE_DNSKEY)
694 return -EINVAL;
695 if (ds->key->type != DNS_TYPE_DS)
696 return -EINVAL;
697 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
698 return -EKEYREJECTED;
699 if (dnskey->dnskey.protocol != 3)
700 return -EKEYREJECTED;
701
702 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
703 return 0;
704 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
705 return 0;
706
707 if (!dnssec_digest_supported(ds->ds.digest_type))
708 return -EOPNOTSUPP;
709
710 switch (ds->ds.digest_type) {
711
712 case DNSSEC_DIGEST_SHA1:
713
714 if (ds->ds.digest_size != 20)
715 return 0;
716
717 gcry_md_open(&md, GCRY_MD_SHA1, 0);
718 break;
719
720 case DNSSEC_DIGEST_SHA256:
721
722 if (ds->ds.digest_size != 32)
723 return 0;
724
725 gcry_md_open(&md, GCRY_MD_SHA256, 0);
726 break;
727
728 default:
729 assert_not_reached("Unknown digest");
730 }
731
732 if (!md)
733 return -EIO;
734
735 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
736 if (r < 0)
737 goto finish;
738
739 gcry_md_write(md, owner_name, r);
740 md_add_uint16(md, dnskey->dnskey.flags);
741 md_add_uint8(md, dnskey->dnskey.protocol);
742 md_add_uint8(md, dnskey->dnskey.algorithm);
743 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
744
745 result = gcry_md_read(md, 0);
746 if (!result) {
747 r = -EIO;
748 goto finish;
749 }
750
751 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
752
753 finish:
754 gcry_md_close(md);
755 return r;
756 }
757
758 int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
759 DnsResourceRecord *ds;
760 int r;
761
762 assert(dnskey);
763
764 if (dnskey->key->type != DNS_TYPE_DNSKEY)
765 return 0;
766
767 DNS_ANSWER_FOREACH(ds, validated_ds) {
768
769 if (ds->key->type != DNS_TYPE_DS)
770 continue;
771
772 r = dnssec_verify_dnskey(dnskey, ds);
773 if (r < 0)
774 return r;
775 if (r > 0)
776 return 1;
777 }
778
779 return 0;
780 }
781
782 static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
783 [DNSSEC_NO] = "no",
784 [DNSSEC_TRUST] = "trust",
785 [DNSSEC_YES] = "yes",
786 };
787 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
788
789 static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
790 [DNSSEC_VALIDATED] = "validated",
791 [DNSSEC_INVALID] = "invalid",
792 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
793 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
794 [DNSSEC_NO_SIGNATURE] = "no-signature",
795 [DNSSEC_MISSING_KEY] = "missing-key",
796 [DNSSEC_UNSIGNED] = "unsigned",
797 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
798 };
799 DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);