]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: rework how and when the number of answer RRs to cache is determined
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.c
CommitLineData
2b442ac8
LP
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"
24710c48 28#include "string-table.h"
2b442ac8
LP
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 *
2cd87277
LP
35 * TODO:
36 *
37 * - Iterative validation
38 * - NSEC proof of non-existance
39 * - NSEC3 proof of non-existance
bb1fa242
LP
40 * - Make trust anchor store read additional DS+DNSKEY data from disk
41 * - wildcard zones compatibility
42 * - multi-label zone compatibility
d12bf2bd 43 * - DNSSEC cname/dname compatibility
bb1fa242 44 * - per-interface DNSSEC setting
2cd87277
LP
45 * - DSA support
46 * - EC support?
47 *
2b442ac8
LP
48 * */
49
50#define VERIFY_RRS_MAX 256
51#define MAX_KEY_SIZE (32*1024)
52
896c5672
LP
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
2b442ac8
LP
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
67static bool dnssec_algorithm_supported(int algorithm) {
964ef14c
LP
68 return IN_SET(algorithm,
69 DNSSEC_ALGORITHM_RSASHA1,
70 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
71 DNSSEC_ALGORITHM_RSASHA256,
72 DNSSEC_ALGORITHM_RSASHA512);
2b442ac8
LP
73}
74
75static bool dnssec_digest_supported(int digest) {
964ef14c
LP
76 return IN_SET(digest,
77 DNSSEC_DIGEST_SHA1,
78 DNSSEC_DIGEST_SHA256);
2b442ac8
LP
79}
80
81uint16_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
104static 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
132static 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);
d12bf2bd 196 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 197 r = 0;
d12bf2bd
LP
198 else if (ge != 0) {
199 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
2b442ac8 200 r = -EIO;
d12bf2bd 201 } else
2b442ac8
LP
202 r = 1;
203
204finish:
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
222static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
223 gcry_md_write(md, &v, sizeof(v));
224}
225
226static 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
231static 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
2a326321
LP
236static 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)
2a44bec4 249 return -EKEYREJECTED;
2a326321 250
896c5672
LP
251 /* Permit a certain amount of clock skew of 10% of the valid
252 * time range. This takes inspiration from unbound's
253 * resolver. */
2a326321 254 skew = (expiration - inception) / 10;
896c5672
LP
255 if (skew > SKEW_MAX)
256 skew = SKEW_MAX;
2a326321
LP
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
271int dnssec_verify_rrset(
272 DnsAnswer *a,
273 DnsResourceKey *key,
274 DnsResourceRecord *rrsig,
275 DnsResourceRecord *dnskey,
547973de
LP
276 usec_t realtime,
277 DnssecResult *result) {
2a326321 278
2b442ac8
LP
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);
547973de 290 assert(result);
2a326321
LP
291 assert(rrsig->key->type == DNS_TYPE_RRSIG);
292 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
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 return -EOPNOTSUPP;
300
301 if (a->n_rrs > VERIFY_RRS_MAX)
302 return -E2BIG;
303
2a326321
LP
304 r = dnssec_rrsig_expired(rrsig, realtime);
305 if (r < 0)
306 return r;
547973de
LP
307 if (r > 0) {
308 *result = DNSSEC_SIGNATURE_EXPIRED;
309 return 0;
310 }
2a326321 311
2b442ac8
LP
312 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
313 list = newa(DnsResourceRecord *, a->n_rrs);
314
315 DNS_ANSWER_FOREACH(rr, a) {
316 r = dns_resource_key_equal(key, rr->key);
317 if (r < 0)
318 return r;
319 if (r == 0)
320 continue;
321
322 /* We need the wire format for ordering, and digest calculation */
323 r = dns_resource_record_to_wire_format(rr, true);
324 if (r < 0)
325 return r;
326
327 list[n++] = rr;
328 }
329
330 if (n <= 0)
331 return -ENODATA;
332
333 /* Bring the RRs into canonical order */
6c5e8fbf 334 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
335
336 /* OK, the RRs are now in canonical order. Let's calculate the digest */
337 switch (rrsig->rrsig.algorithm) {
338
339 case DNSSEC_ALGORITHM_RSASHA1:
964ef14c 340 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
2b442ac8
LP
341 gcry_md_open(&md, GCRY_MD_SHA1, 0);
342 hash_size = 20;
343 break;
344
345 case DNSSEC_ALGORITHM_RSASHA256:
346 gcry_md_open(&md, GCRY_MD_SHA256, 0);
347 hash_size = 32;
348 break;
349
350 case DNSSEC_ALGORITHM_RSASHA512:
351 gcry_md_open(&md, GCRY_MD_SHA512, 0);
352 hash_size = 64;
353 break;
354
355 default:
356 assert_not_reached("Unknown digest");
357 }
358
359 if (!md)
360 return -EIO;
361
362 md_add_uint16(md, rrsig->rrsig.type_covered);
363 md_add_uint8(md, rrsig->rrsig.algorithm);
364 md_add_uint8(md, rrsig->rrsig.labels);
365 md_add_uint32(md, rrsig->rrsig.original_ttl);
366 md_add_uint32(md, rrsig->rrsig.expiration);
367 md_add_uint32(md, rrsig->rrsig.inception);
368 md_add_uint16(md, rrsig->rrsig.key_tag);
369
370 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
371 if (r < 0)
372 goto finish;
373 gcry_md_write(md, wire_format_name, r);
374
375 for (k = 0; k < n; k++) {
376 size_t l;
377 rr = list[k];
378
379 r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
380 if (r < 0)
381 goto finish;
382 gcry_md_write(md, wire_format_name, r);
383
384 md_add_uint16(md, rr->key->type);
385 md_add_uint16(md, rr->key->class);
386 md_add_uint32(md, rrsig->rrsig.original_ttl);
387
388 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
389 l = rr->wire_format_size - rr->wire_format_rdata_offset;
390 assert(l <= 0xFFFF);
391
392 md_add_uint16(md, (uint16_t) l);
393 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
394 }
395
396 hash = gcry_md_read(md, 0);
397 if (!hash) {
398 r = -EIO;
399 goto finish;
400 }
401
402 if (*(uint8_t*) dnskey->dnskey.key == 0) {
403 /* exponent is > 255 bytes long */
404
405 exponent = (uint8_t*) dnskey->dnskey.key + 3;
406 exponent_size =
407 ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
408 ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
409
410 if (exponent_size < 256) {
411 r = -EINVAL;
412 goto finish;
413 }
414
415 if (3 + exponent_size >= dnskey->dnskey.key_size) {
416 r = -EINVAL;
417 goto finish;
418 }
419
420 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
421 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
422
423 } else {
424 /* exponent is <= 255 bytes long */
425
426 exponent = (uint8_t*) dnskey->dnskey.key + 1;
427 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
428
429 if (exponent_size <= 0) {
430 r = -EINVAL;
431 goto finish;
432 }
433
434 if (1 + exponent_size >= dnskey->dnskey.key_size) {
435 r = -EINVAL;
436 goto finish;
437 }
438
439 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
440 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
441 }
442
443 r = dnssec_rsa_verify(
444 gcry_md_algo_name(gcry_md_get_algo(md)),
445 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
446 hash, hash_size,
447 exponent, exponent_size,
448 modulus, modulus_size);
449 if (r < 0)
450 goto finish;
451
547973de
LP
452 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
453 r = 0;
2b442ac8
LP
454
455finish:
456 gcry_md_close(md);
457 return r;
458}
459
460int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
461
462 assert(rrsig);
463 assert(dnskey);
464
465 /* Checks if the specified DNSKEY RR matches the key used for
466 * the signature in the specified RRSIG RR */
467
468 if (rrsig->key->type != DNS_TYPE_RRSIG)
469 return -EINVAL;
470
471 if (dnskey->key->type != DNS_TYPE_DNSKEY)
472 return 0;
473 if (dnskey->key->class != rrsig->key->class)
474 return 0;
475 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
476 return 0;
477 if (dnskey->dnskey.protocol != 3)
478 return 0;
479 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
480 return 0;
481
482 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
483 return 0;
484
15accc27 485 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
486}
487
488int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
489 assert(key);
490 assert(rrsig);
491
492 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
493
494 if (rrsig->key->type != DNS_TYPE_RRSIG)
495 return 0;
496 if (rrsig->key->class != key->class)
497 return 0;
498 if (rrsig->rrsig.type_covered != key->type)
499 return 0;
500
501 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
502}
503
2a326321
LP
504int dnssec_verify_rrset_search(
505 DnsAnswer *a,
506 DnsResourceKey *key,
507 DnsAnswer *validated_dnskeys,
547973de
LP
508 usec_t realtime,
509 DnssecResult *result) {
2a326321 510
2b442ac8
LP
511 bool found_rrsig = false, found_dnskey = false;
512 DnsResourceRecord *rrsig;
513 int r;
514
515 assert(key);
547973de 516 assert(result);
2b442ac8 517
15accc27 518 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
2b442ac8
LP
519
520 if (!a || a->n_rrs <= 0)
521 return -ENODATA;
522
523 /* Iterate through each RRSIG RR. */
524 DNS_ANSWER_FOREACH(rrsig, a) {
525 DnsResourceRecord *dnskey;
526
527 r = dnssec_key_match_rrsig(key, rrsig);
528 if (r < 0)
529 return r;
530 if (r == 0)
531 continue;
532
533 found_rrsig = true;
534
547973de 535 /* Look for a matching key */
2b442ac8 536 DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
547973de 537 DnssecResult one_result;
2b442ac8
LP
538
539 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
540 if (r < 0)
541 return r;
542 if (r == 0)
543 continue;
544
545 found_dnskey = true;
546
2a326321
LP
547 /* Take the time here, if it isn't set yet, so
548 * that we do all validations with the same
549 * time. */
550 if (realtime == USEC_INFINITY)
551 realtime = now(CLOCK_REALTIME);
552
2b442ac8
LP
553 /* Yay, we found a matching RRSIG with a matching
554 * DNSKEY, awesome. Now let's verify all entries of
555 * the RRSet against the RRSIG and DNSKEY
556 * combination. */
557
547973de 558 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
2b442ac8
LP
559 if (r < 0 && r != EOPNOTSUPP)
560 return r;
547973de
LP
561 if (one_result == DNSSEC_VALIDATED) {
562 *result = DNSSEC_VALIDATED;
563 return 0;
564 }
2b442ac8
LP
565
566 /* If the signature is invalid, or done using
567 an unsupported algorithm, let's try another
568 key and/or signature. After all they
569 key_tags and stuff are not unique, and
570 might be shared by multiple keys. */
571 }
572 }
573
574 if (found_dnskey)
547973de
LP
575 *result = DNSSEC_INVALID;
576 else if (found_rrsig)
577 *result = DNSSEC_MISSING_KEY;
578 else
579 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 580
547973de 581 return 0;
2b442ac8
LP
582}
583
584int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
585 size_t c = 0;
586 int r;
587
588 /* Converts the specified hostname into DNSSEC canonicalized
589 * form. */
590
591 if (buffer_max < 2)
592 return -ENOBUFS;
593
594 for (;;) {
595 size_t i;
596
597 r = dns_label_unescape(&n, buffer, buffer_max);
598 if (r < 0)
599 return r;
600 if (r == 0)
601 break;
602 if (r > 0) {
603 int k;
604
605 /* DNSSEC validation is always done on the ASCII version of the label */
606 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
607 if (k < 0)
608 return k;
609 if (k > 0)
610 r = k;
611 }
612
613 if (buffer_max < (size_t) r + 2)
614 return -ENOBUFS;
615
616 /* The DNSSEC canonical form is not clear on what to
617 * do with dots appearing in labels, the way DNS-SD
618 * does it. Refuse it for now. */
619
620 if (memchr(buffer, '.', r))
621 return -EINVAL;
622
623 for (i = 0; i < (size_t) r; i ++) {
624 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
625 buffer[i] = buffer[i] - 'A' + 'a';
626 }
627
628 buffer[r] = '.';
629
630 buffer += r + 1;
631 c += r + 1;
632
633 buffer_max -= r + 1;
634 }
635
636 if (c <= 0) {
637 /* Not even a single label: this is the root domain name */
638
639 assert(buffer_max > 2);
640 buffer[0] = '.';
641 buffer[1] = 0;
642
643 return 1;
644 }
645
646 return (int) c;
647}
648
649int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
650 gcry_md_hd_t md = NULL;
651 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
652 void *result;
653 int r;
654
655 assert(dnskey);
656 assert(ds);
657
658 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
659
660 if (dnskey->key->type != DNS_TYPE_DNSKEY)
661 return -EINVAL;
662 if (ds->key->type != DNS_TYPE_DS)
663 return -EINVAL;
664 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
665 return -EKEYREJECTED;
666 if (dnskey->dnskey.protocol != 3)
667 return -EKEYREJECTED;
668
2b442ac8
LP
669 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
670 return 0;
671 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
672 return 0;
673
aa899317
LP
674 if (!dnssec_digest_supported(ds->ds.digest_type))
675 return -EOPNOTSUPP;
676
2b442ac8
LP
677 switch (ds->ds.digest_type) {
678
679 case DNSSEC_DIGEST_SHA1:
680
681 if (ds->ds.digest_size != 20)
682 return 0;
683
684 gcry_md_open(&md, GCRY_MD_SHA1, 0);
685 break;
686
687 case DNSSEC_DIGEST_SHA256:
688
689 if (ds->ds.digest_size != 32)
690 return 0;
691
692 gcry_md_open(&md, GCRY_MD_SHA256, 0);
693 break;
694
695 default:
696 assert_not_reached("Unknown digest");
697 }
698
699 if (!md)
700 return -EIO;
701
702 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
703 if (r < 0)
704 goto finish;
705
706 gcry_md_write(md, owner_name, r);
707 md_add_uint16(md, dnskey->dnskey.flags);
708 md_add_uint8(md, dnskey->dnskey.protocol);
709 md_add_uint8(md, dnskey->dnskey.algorithm);
710 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
711
712 result = gcry_md_read(md, 0);
713 if (!result) {
714 r = -EIO;
715 goto finish;
716 }
717
718 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
719
720finish:
721 gcry_md_close(md);
722 return r;
723}
24710c48 724
547973de
LP
725int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
726 DnsResourceRecord *ds;
727 int r;
728
729 assert(dnskey);
730
731 if (dnskey->key->type != DNS_TYPE_DNSKEY)
732 return 0;
733
734 DNS_ANSWER_FOREACH(ds, validated_ds) {
735
736 if (ds->key->type != DNS_TYPE_DS)
737 continue;
738
739 r = dnssec_verify_dnskey(dnskey, ds);
740 if (r < 0)
741 return r;
742 if (r > 0)
743 return 1;
744 }
745
746 return 0;
747}
748
24710c48
LP
749static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
750 [DNSSEC_NO] = "no",
751 [DNSSEC_TRUST] = "trust",
752 [DNSSEC_YES] = "yes",
753};
754DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
755
756static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
757 [DNSSEC_VALIDATED] = "validated",
758 [DNSSEC_INVALID] = "invalid",
759 [DNSSEC_UNSIGNED] = "unsigned",
760 [DNSSEC_NO_SIGNATURE] = "no-signature",
761 [DNSSEC_MISSING_KEY] = "missing-key",
762 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
763 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
764};
765DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);