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