]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: apparently not all names are used in canonical form for DNSSEC validation
[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
203f1b35
LP
298 if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
299 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
300 return 0;
301 }
2b442ac8
LP
302
303 if (a->n_rrs > VERIFY_RRS_MAX)
304 return -E2BIG;
305
2a326321
LP
306 r = dnssec_rrsig_expired(rrsig, realtime);
307 if (r < 0)
308 return r;
547973de
LP
309 if (r > 0) {
310 *result = DNSSEC_SIGNATURE_EXPIRED;
311 return 0;
312 }
2a326321 313
2b442ac8
LP
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 */
6c5e8fbf 336 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
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:
964ef14c 342 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
2b442ac8
LP
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
547973de
LP
454 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
455 r = 0;
2b442ac8
LP
456
457finish:
458 gcry_md_close(md);
459 return r;
460}
461
462int 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
15accc27 487 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
488}
489
490int 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
2a326321
LP
506int dnssec_verify_rrset_search(
507 DnsAnswer *a,
508 DnsResourceKey *key,
509 DnsAnswer *validated_dnskeys,
547973de
LP
510 usec_t realtime,
511 DnssecResult *result) {
2a326321 512
203f1b35 513 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
514 DnsResourceRecord *rrsig;
515 int r;
516
517 assert(key);
547973de 518 assert(result);
2b442ac8 519
15accc27 520 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
2b442ac8
LP
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
203f1b35 529 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
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
547973de 538 /* Look for a matching key */
2b442ac8 539 DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
547973de 540 DnssecResult one_result;
2b442ac8 541
203f1b35 542 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
2b442ac8
LP
543 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
544 if (r < 0)
545 return r;
546 if (r == 0)
547 continue;
548
2a326321
LP
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
2b442ac8
LP
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
547973de 560 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 561 if (r < 0)
2b442ac8 562 return r;
203f1b35
LP
563
564 switch (one_result) {
565
566 case DNSSEC_VALIDATED:
567 /* Yay, the RR has been validated,
568 * return immediately. */
547973de
LP
569 *result = DNSSEC_VALIDATED;
570 return 0;
2b442ac8 571
203f1b35
LP
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 }
2b442ac8
LP
600 }
601 }
602
203f1b35
LP
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)
547973de
LP
608 *result = DNSSEC_INVALID;
609 else if (found_rrsig)
610 *result = DNSSEC_MISSING_KEY;
611 else
612 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 613
547973de 614 return 0;
2b442ac8
LP
615}
616
617int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
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
682int 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
2b442ac8
LP
702 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
703 return 0;
704 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
705 return 0;
706
aa899317
LP
707 if (!dnssec_digest_supported(ds->ds.digest_type))
708 return -EOPNOTSUPP;
709
2b442ac8
LP
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
753finish:
754 gcry_md_close(md);
755 return r;
756}
24710c48 757
547973de
LP
758int 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
24710c48
LP
782static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
783 [DNSSEC_NO] = "no",
784 [DNSSEC_TRUST] = "trust",
785 [DNSSEC_YES] = "yes",
786};
787DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
788
789static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
790 [DNSSEC_VALIDATED] = "validated",
791 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
792 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
793 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
794 [DNSSEC_NO_SIGNATURE] = "no-signature",
795 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 796 [DNSSEC_UNSIGNED] = "unsigned",
547973de
LP
797 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
798};
799DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);