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