]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: cache stringified transaction key once per transaction
[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 *
38 * - Iterative validation
39 * - NSEC proof of non-existance
40 * - NSEC3 proof of non-existance
bb1fa242
LP
41 * - Make trust anchor store read additional DS+DNSKEY data from disk
42 * - wildcard zones compatibility
43 * - multi-label zone compatibility
d12bf2bd 44 * - DNSSEC cname/dname compatibility
bb1fa242 45 * - per-interface DNSSEC setting
73b8d8e9
LP
46 * - retry on failed validation
47 * - fix TTL for cache entries to match RRSIG TTL
2cd87277
LP
48 * - DSA support
49 * - EC support?
50 *
2b442ac8
LP
51 * */
52
53#define VERIFY_RRS_MAX 256
54#define MAX_KEY_SIZE (32*1024)
55
896c5672
LP
56/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
57#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
58
2b442ac8
LP
59/*
60 * The DNSSEC Chain of trust:
61 *
62 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
63 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
64 * DS RRs are protected like normal RRs
65 *
66 * Example chain:
67 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
68 */
69
0638401a
LP
70static void initialize_libgcrypt(void) {
71 const char *p;
72
73 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
74 return;
75
76 p = gcry_check_version("1.4.5");
77 assert(p);
78
79 gcry_control(GCRYCTL_DISABLE_SECMEM);
80 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
81}
82
2b442ac8 83static bool dnssec_algorithm_supported(int algorithm) {
964ef14c
LP
84 return IN_SET(algorithm,
85 DNSSEC_ALGORITHM_RSASHA1,
86 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
87 DNSSEC_ALGORITHM_RSASHA256,
88 DNSSEC_ALGORITHM_RSASHA512);
2b442ac8
LP
89}
90
2b442ac8
LP
91uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
92 const uint8_t *p;
93 uint32_t sum;
94 size_t i;
95
96 /* The algorithm from RFC 4034, Appendix B. */
97
98 assert(dnskey);
99 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
100
101 sum = (uint32_t) dnskey->dnskey.flags +
102 ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
103
104 p = dnskey->dnskey.key;
105
106 for (i = 0; i < dnskey->dnskey.key_size; i++)
107 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
108
109 sum += (sum >> 16) & UINT32_C(0xFFFF);
110
111 return sum & UINT32_C(0xFFFF);
112}
113
114static int rr_compare(const void *a, const void *b) {
115 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
116 size_t m;
117 int r;
118
119 /* Let's order the RRs according to RFC 4034, Section 6.3 */
120
121 assert(x);
122 assert(*x);
123 assert((*x)->wire_format);
124 assert(y);
125 assert(*y);
126 assert((*y)->wire_format);
127
128 m = MIN((*x)->wire_format_size, (*y)->wire_format_size);
129
130 r = memcmp((*x)->wire_format, (*y)->wire_format, m);
131 if (r != 0)
132 return r;
133
134 if ((*x)->wire_format_size < (*y)->wire_format_size)
135 return -1;
136 else if ((*x)->wire_format_size > (*y)->wire_format_size)
137 return 1;
138
139 return 0;
140}
141
142static int dnssec_rsa_verify(
143 const char *hash_algorithm,
144 const void *signature, size_t signature_size,
145 const void *data, size_t data_size,
146 const void *exponent, size_t exponent_size,
147 const void *modulus, size_t modulus_size) {
148
149 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
150 gcry_mpi_t n = NULL, e = NULL, s = NULL;
151 gcry_error_t ge;
152 int r;
153
154 assert(hash_algorithm);
155
156 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
157 if (ge != 0) {
158 r = -EIO;
159 goto finish;
160 }
161
162 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
163 if (ge != 0) {
164 r = -EIO;
165 goto finish;
166 }
167
168 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
169 if (ge != 0) {
170 r = -EIO;
171 goto finish;
172 }
173
174 ge = gcry_sexp_build(&signature_sexp,
175 NULL,
176 "(sig-val (rsa (s %m)))",
177 s);
178
179 if (ge != 0) {
180 r = -EIO;
181 goto finish;
182 }
183
184 ge = gcry_sexp_build(&data_sexp,
185 NULL,
186 "(data (flags pkcs1) (hash %s %b))",
187 hash_algorithm,
188 (int) data_size,
189 data);
190 if (ge != 0) {
191 r = -EIO;
192 goto finish;
193 }
194
195 ge = gcry_sexp_build(&public_key_sexp,
196 NULL,
197 "(public-key (rsa (n %m) (e %m)))",
198 n,
199 e);
200 if (ge != 0) {
201 r = -EIO;
202 goto finish;
203 }
204
205 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
d12bf2bd 206 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 207 r = 0;
d12bf2bd
LP
208 else if (ge != 0) {
209 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
2b442ac8 210 r = -EIO;
d12bf2bd 211 } else
2b442ac8
LP
212 r = 1;
213
214finish:
215 if (e)
216 gcry_mpi_release(e);
217 if (n)
218 gcry_mpi_release(n);
219 if (s)
220 gcry_mpi_release(s);
221
222 if (public_key_sexp)
223 gcry_sexp_release(public_key_sexp);
224 if (signature_sexp)
225 gcry_sexp_release(signature_sexp);
226 if (data_sexp)
227 gcry_sexp_release(data_sexp);
228
229 return r;
230}
231
232static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
233 gcry_md_write(md, &v, sizeof(v));
234}
235
236static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
237 v = htobe16(v);
238 gcry_md_write(md, &v, sizeof(v));
239}
240
241static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
242 v = htobe32(v);
243 gcry_md_write(md, &v, sizeof(v));
244}
245
2a326321
LP
246static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
247 usec_t expiration, inception, skew;
248
249 assert(rrsig);
250 assert(rrsig->key->type == DNS_TYPE_RRSIG);
251
252 if (realtime == USEC_INFINITY)
253 realtime = now(CLOCK_REALTIME);
254
255 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
256 inception = rrsig->rrsig.inception * USEC_PER_SEC;
257
258 if (inception > expiration)
2a44bec4 259 return -EKEYREJECTED;
2a326321 260
896c5672
LP
261 /* Permit a certain amount of clock skew of 10% of the valid
262 * time range. This takes inspiration from unbound's
263 * resolver. */
2a326321 264 skew = (expiration - inception) / 10;
896c5672
LP
265 if (skew > SKEW_MAX)
266 skew = SKEW_MAX;
2a326321
LP
267
268 if (inception < skew)
269 inception = 0;
270 else
271 inception -= skew;
272
273 if (expiration + skew < expiration)
274 expiration = USEC_INFINITY;
275 else
276 expiration += skew;
277
278 return realtime < inception || realtime > expiration;
279}
280
281int dnssec_verify_rrset(
282 DnsAnswer *a,
283 DnsResourceKey *key,
284 DnsResourceRecord *rrsig,
285 DnsResourceRecord *dnskey,
547973de
LP
286 usec_t realtime,
287 DnssecResult *result) {
2a326321 288
2b442ac8
LP
289 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
290 size_t exponent_size, modulus_size, hash_size;
291 void *exponent, *modulus, *hash;
292 DnsResourceRecord **list, *rr;
293 gcry_md_hd_t md = NULL;
294 size_t k, n = 0;
295 int r;
296
297 assert(key);
298 assert(rrsig);
299 assert(dnskey);
547973de 300 assert(result);
2a326321
LP
301 assert(rrsig->key->type == DNS_TYPE_RRSIG);
302 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
303
304 /* Verifies the the RRSet matching the specified "key" in "a",
305 * using the signature "rrsig" and the key "dnskey". It's
306 * assumed the RRSIG and DNSKEY match. */
307
203f1b35
LP
308 if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
309 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
310 return 0;
311 }
2b442ac8
LP
312
313 if (a->n_rrs > VERIFY_RRS_MAX)
314 return -E2BIG;
315
2a326321
LP
316 r = dnssec_rrsig_expired(rrsig, realtime);
317 if (r < 0)
318 return r;
547973de
LP
319 if (r > 0) {
320 *result = DNSSEC_SIGNATURE_EXPIRED;
321 return 0;
322 }
2a326321 323
2b442ac8
LP
324 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
325 list = newa(DnsResourceRecord *, a->n_rrs);
326
327 DNS_ANSWER_FOREACH(rr, a) {
328 r = dns_resource_key_equal(key, rr->key);
329 if (r < 0)
330 return r;
331 if (r == 0)
332 continue;
333
334 /* We need the wire format for ordering, and digest calculation */
335 r = dns_resource_record_to_wire_format(rr, true);
336 if (r < 0)
337 return r;
338
339 list[n++] = rr;
340 }
341
342 if (n <= 0)
343 return -ENODATA;
344
345 /* Bring the RRs into canonical order */
6c5e8fbf 346 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8 347
0638401a
LP
348 initialize_libgcrypt();
349
2b442ac8
LP
350 /* OK, the RRs are now in canonical order. Let's calculate the digest */
351 switch (rrsig->rrsig.algorithm) {
352
353 case DNSSEC_ALGORITHM_RSASHA1:
964ef14c 354 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
2b442ac8
LP
355 gcry_md_open(&md, GCRY_MD_SHA1, 0);
356 hash_size = 20;
357 break;
358
359 case DNSSEC_ALGORITHM_RSASHA256:
360 gcry_md_open(&md, GCRY_MD_SHA256, 0);
361 hash_size = 32;
362 break;
363
364 case DNSSEC_ALGORITHM_RSASHA512:
365 gcry_md_open(&md, GCRY_MD_SHA512, 0);
366 hash_size = 64;
367 break;
368
369 default:
370 assert_not_reached("Unknown digest");
371 }
372
373 if (!md)
374 return -EIO;
375
376 md_add_uint16(md, rrsig->rrsig.type_covered);
377 md_add_uint8(md, rrsig->rrsig.algorithm);
378 md_add_uint8(md, rrsig->rrsig.labels);
379 md_add_uint32(md, rrsig->rrsig.original_ttl);
380 md_add_uint32(md, rrsig->rrsig.expiration);
381 md_add_uint32(md, rrsig->rrsig.inception);
382 md_add_uint16(md, rrsig->rrsig.key_tag);
383
384 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
385 if (r < 0)
386 goto finish;
387 gcry_md_write(md, wire_format_name, r);
388
389 for (k = 0; k < n; k++) {
390 size_t l;
391 rr = list[k];
392
393 r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
394 if (r < 0)
395 goto finish;
396 gcry_md_write(md, wire_format_name, r);
397
398 md_add_uint16(md, rr->key->type);
399 md_add_uint16(md, rr->key->class);
400 md_add_uint32(md, rrsig->rrsig.original_ttl);
401
402 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
403 l = rr->wire_format_size - rr->wire_format_rdata_offset;
404 assert(l <= 0xFFFF);
405
406 md_add_uint16(md, (uint16_t) l);
407 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
408 }
409
410 hash = gcry_md_read(md, 0);
411 if (!hash) {
412 r = -EIO;
413 goto finish;
414 }
415
416 if (*(uint8_t*) dnskey->dnskey.key == 0) {
417 /* exponent is > 255 bytes long */
418
419 exponent = (uint8_t*) dnskey->dnskey.key + 3;
420 exponent_size =
421 ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
422 ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
423
424 if (exponent_size < 256) {
425 r = -EINVAL;
426 goto finish;
427 }
428
429 if (3 + exponent_size >= dnskey->dnskey.key_size) {
430 r = -EINVAL;
431 goto finish;
432 }
433
434 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
435 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
436
437 } else {
438 /* exponent is <= 255 bytes long */
439
440 exponent = (uint8_t*) dnskey->dnskey.key + 1;
441 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
442
443 if (exponent_size <= 0) {
444 r = -EINVAL;
445 goto finish;
446 }
447
448 if (1 + exponent_size >= dnskey->dnskey.key_size) {
449 r = -EINVAL;
450 goto finish;
451 }
452
453 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
454 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
455 }
456
457 r = dnssec_rsa_verify(
458 gcry_md_algo_name(gcry_md_get_algo(md)),
459 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
460 hash, hash_size,
461 exponent, exponent_size,
462 modulus, modulus_size);
463 if (r < 0)
464 goto finish;
465
547973de
LP
466 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
467 r = 0;
2b442ac8
LP
468
469finish:
470 gcry_md_close(md);
471 return r;
472}
473
474int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
475
476 assert(rrsig);
477 assert(dnskey);
478
479 /* Checks if the specified DNSKEY RR matches the key used for
480 * the signature in the specified RRSIG RR */
481
482 if (rrsig->key->type != DNS_TYPE_RRSIG)
483 return -EINVAL;
484
485 if (dnskey->key->type != DNS_TYPE_DNSKEY)
486 return 0;
487 if (dnskey->key->class != rrsig->key->class)
488 return 0;
489 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
490 return 0;
491 if (dnskey->dnskey.protocol != 3)
492 return 0;
493 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
494 return 0;
495
496 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
497 return 0;
498
15accc27 499 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
500}
501
502int dnssec_key_match_rrsig(DnsResourceKey *key, DnsResourceRecord *rrsig) {
503 assert(key);
504 assert(rrsig);
505
506 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
507
508 if (rrsig->key->type != DNS_TYPE_RRSIG)
509 return 0;
510 if (rrsig->key->class != key->class)
511 return 0;
512 if (rrsig->rrsig.type_covered != key->type)
513 return 0;
514
515 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
516}
517
2a326321
LP
518int dnssec_verify_rrset_search(
519 DnsAnswer *a,
520 DnsResourceKey *key,
521 DnsAnswer *validated_dnskeys,
547973de
LP
522 usec_t realtime,
523 DnssecResult *result) {
2a326321 524
203f1b35 525 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
526 DnsResourceRecord *rrsig;
527 int r;
528
529 assert(key);
547973de 530 assert(result);
2b442ac8 531
15accc27 532 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
2b442ac8
LP
533
534 if (!a || a->n_rrs <= 0)
535 return -ENODATA;
536
537 /* Iterate through each RRSIG RR. */
538 DNS_ANSWER_FOREACH(rrsig, a) {
539 DnsResourceRecord *dnskey;
540
203f1b35 541 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
542 r = dnssec_key_match_rrsig(key, rrsig);
543 if (r < 0)
544 return r;
545 if (r == 0)
546 continue;
547
548 found_rrsig = true;
549
547973de 550 /* Look for a matching key */
2b442ac8 551 DNS_ANSWER_FOREACH(dnskey, validated_dnskeys) {
547973de 552 DnssecResult one_result;
2b442ac8 553
203f1b35 554 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
2b442ac8
LP
555 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
556 if (r < 0)
557 return r;
558 if (r == 0)
559 continue;
560
2a326321
LP
561 /* Take the time here, if it isn't set yet, so
562 * that we do all validations with the same
563 * time. */
564 if (realtime == USEC_INFINITY)
565 realtime = now(CLOCK_REALTIME);
566
2b442ac8
LP
567 /* Yay, we found a matching RRSIG with a matching
568 * DNSKEY, awesome. Now let's verify all entries of
569 * the RRSet against the RRSIG and DNSKEY
570 * combination. */
571
547973de 572 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 573 if (r < 0)
2b442ac8 574 return r;
203f1b35
LP
575
576 switch (one_result) {
577
578 case DNSSEC_VALIDATED:
579 /* Yay, the RR has been validated,
580 * return immediately. */
547973de
LP
581 *result = DNSSEC_VALIDATED;
582 return 0;
2b442ac8 583
203f1b35
LP
584 case DNSSEC_INVALID:
585 /* If the signature is invalid, let's try another
586 key and/or signature. After all they
587 key_tags and stuff are not unique, and
588 might be shared by multiple keys. */
589 found_invalid = true;
590 continue;
591
592 case DNSSEC_UNSUPPORTED_ALGORITHM:
593 /* If the key algorithm is
594 unsupported, try another
595 RRSIG/DNSKEY pair, but remember we
596 encountered this, so that we can
597 return a proper error when we
598 encounter nothing better. */
599 found_unsupported_algorithm = true;
600 continue;
601
602 case DNSSEC_SIGNATURE_EXPIRED:
603 /* If the signature is expired, try
604 another one, but remember it, so
605 that we can return this */
606 found_expired_rrsig = true;
607 continue;
608
609 default:
610 assert_not_reached("Unexpected DNSSEC validation result");
611 }
2b442ac8
LP
612 }
613 }
614
203f1b35
LP
615 if (found_expired_rrsig)
616 *result = DNSSEC_SIGNATURE_EXPIRED;
617 else if (found_unsupported_algorithm)
618 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
619 else if (found_invalid)
547973de
LP
620 *result = DNSSEC_INVALID;
621 else if (found_rrsig)
622 *result = DNSSEC_MISSING_KEY;
623 else
624 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 625
547973de 626 return 0;
2b442ac8
LP
627}
628
629int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
630 size_t c = 0;
631 int r;
632
633 /* Converts the specified hostname into DNSSEC canonicalized
634 * form. */
635
636 if (buffer_max < 2)
637 return -ENOBUFS;
638
639 for (;;) {
640 size_t i;
641
642 r = dns_label_unescape(&n, buffer, buffer_max);
643 if (r < 0)
644 return r;
645 if (r == 0)
646 break;
647 if (r > 0) {
648 int k;
649
650 /* DNSSEC validation is always done on the ASCII version of the label */
651 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
652 if (k < 0)
653 return k;
654 if (k > 0)
655 r = k;
656 }
657
658 if (buffer_max < (size_t) r + 2)
659 return -ENOBUFS;
660
661 /* The DNSSEC canonical form is not clear on what to
662 * do with dots appearing in labels, the way DNS-SD
663 * does it. Refuse it for now. */
664
665 if (memchr(buffer, '.', r))
666 return -EINVAL;
667
668 for (i = 0; i < (size_t) r; i ++) {
669 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
670 buffer[i] = buffer[i] - 'A' + 'a';
671 }
672
673 buffer[r] = '.';
674
675 buffer += r + 1;
676 c += r + 1;
677
678 buffer_max -= r + 1;
679 }
680
681 if (c <= 0) {
682 /* Not even a single label: this is the root domain name */
683
684 assert(buffer_max > 2);
685 buffer[0] = '.';
686 buffer[1] = 0;
687
688 return 1;
689 }
690
691 return (int) c;
692}
693
a1972a91
LP
694static int digest_to_gcrypt(uint8_t algorithm) {
695
696 /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
697
698 switch (algorithm) {
699
700 case DNSSEC_DIGEST_SHA1:
701 return GCRY_MD_SHA1;
702
703 case DNSSEC_DIGEST_SHA256:
704 return GCRY_MD_SHA256;
705
706 default:
707 return -EOPNOTSUPP;
708 }
709}
710
2b442ac8 711int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
2b442ac8 712 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
713 gcry_md_hd_t md = NULL;
714 size_t hash_size;
715 int algorithm;
2b442ac8
LP
716 void *result;
717 int r;
718
719 assert(dnskey);
720 assert(ds);
721
722 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
723
724 if (dnskey->key->type != DNS_TYPE_DNSKEY)
725 return -EINVAL;
726 if (ds->key->type != DNS_TYPE_DS)
727 return -EINVAL;
728 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
729 return -EKEYREJECTED;
730 if (dnskey->dnskey.protocol != 3)
731 return -EKEYREJECTED;
732
2b442ac8
LP
733 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
734 return 0;
735 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
736 return 0;
737
0638401a
LP
738 initialize_libgcrypt();
739
a1972a91
LP
740 algorithm = digest_to_gcrypt(ds->ds.digest_type);
741 if (algorithm < 0)
742 return algorithm;
2b442ac8 743
a1972a91
LP
744 hash_size = gcry_md_get_algo_dlen(algorithm);
745 assert(hash_size > 0);
2b442ac8 746
a1972a91
LP
747 if (ds->ds.digest_size != hash_size)
748 return 0;
2b442ac8 749
a1972a91
LP
750 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
751 if (r < 0)
752 return r;
2b442ac8 753
a1972a91 754 gcry_md_open(&md, algorithm, 0);
2b442ac8
LP
755 if (!md)
756 return -EIO;
757
2b442ac8
LP
758 gcry_md_write(md, owner_name, r);
759 md_add_uint16(md, dnskey->dnskey.flags);
760 md_add_uint8(md, dnskey->dnskey.protocol);
761 md_add_uint8(md, dnskey->dnskey.algorithm);
762 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
763
764 result = gcry_md_read(md, 0);
765 if (!result) {
766 r = -EIO;
767 goto finish;
768 }
769
770 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
771
772finish:
773 gcry_md_close(md);
774 return r;
775}
24710c48 776
547973de
LP
777int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
778 DnsResourceRecord *ds;
779 int r;
780
781 assert(dnskey);
782
783 if (dnskey->key->type != DNS_TYPE_DNSKEY)
784 return 0;
785
786 DNS_ANSWER_FOREACH(ds, validated_ds) {
787
788 if (ds->key->type != DNS_TYPE_DS)
789 continue;
790
791 r = dnssec_verify_dnskey(dnskey, ds);
792 if (r < 0)
793 return r;
794 if (r > 0)
795 return 1;
796 }
797
798 return 0;
799}
800
72667f08
LP
801int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
802 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
803 gcry_md_hd_t md = NULL;
804 size_t hash_size;
805 int algorithm;
806 void *result;
807 unsigned k;
808 int r;
809
810 assert(nsec3);
811 assert(name);
812 assert(ret);
813
814 if (nsec3->key->type != DNS_TYPE_NSEC3)
815 return -EINVAL;
816
817 algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm);
818 if (algorithm < 0)
819 return algorithm;
820
821 initialize_libgcrypt();
822
823 hash_size = gcry_md_get_algo_dlen(algorithm);
824 assert(hash_size > 0);
825
826 if (nsec3->nsec3.next_hashed_name_size != hash_size)
827 return -EINVAL;
828
829 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
830 if (r < 0)
831 return r;
832
833 gcry_md_open(&md, algorithm, 0);
834 if (!md)
835 return -EIO;
836
837 gcry_md_write(md, wire_format, r);
838 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
839
840 result = gcry_md_read(md, 0);
841 if (!result) {
842 r = -EIO;
843 goto finish;
844 }
845
846 for (k = 0; k < nsec3->nsec3.iterations; k++) {
847 uint8_t tmp[hash_size];
848 memcpy(tmp, result, hash_size);
849
850 gcry_md_reset(md);
851 gcry_md_write(md, tmp, hash_size);
852 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
853
854 result = gcry_md_read(md, 0);
855 if (!result) {
856 r = -EIO;
857 goto finish;
858 }
859 }
860
861 memcpy(ret, result, hash_size);
862 r = (int) hash_size;
863
864finish:
865 gcry_md_close(md);
866 return r;
867}
868
869int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
870 DnsResourceRecord *rr;
871 int r;
872
873 assert(key);
874 assert(result);
875
876 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
877
878 DNS_ANSWER_FOREACH(rr, answer) {
879
880 if (rr->key->class != key->class)
881 continue;
882
883 switch (rr->key->type) {
884
885 case DNS_TYPE_NSEC:
886
887 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
888 if (r < 0)
889 return r;
890 if (r > 0) {
891 *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
892 return 0;
893 }
894
895 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
896 if (r < 0)
897 return r;
898 if (r > 0) {
899 *result = DNSSEC_NSEC_NXDOMAIN;
900 return 0;
901 }
902 break;
903
904 case DNS_TYPE_NSEC3: {
905 _cleanup_free_ void *decoded = NULL;
906 size_t decoded_size;
907 char label[DNS_LABEL_MAX];
908 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
909 int label_length, c, q;
910 const char *p;
911 bool covered;
912
913 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
914 if (!IN_SET(rr->nsec3.flags, 0, 1))
915 continue;
916
917 p = DNS_RESOURCE_KEY_NAME(rr->key);
918 label_length = dns_label_unescape(&p, label, sizeof(label));
919 if (label_length < 0)
920 return label_length;
921 if (label_length == 0)
922 continue;
923
924 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), p);
925 if (r < 0)
926 return r;
927 if (r == 0)
928 continue;
929
930 r = unbase32hexmem(label, label_length, false, &decoded, &decoded_size);
931 if (r == -EINVAL)
932 continue;
933 if (r < 0)
934 return r;
935
936 if (decoded_size != rr->nsec3.next_hashed_name_size)
937 continue;
938
939 c = memcmp(decoded, rr->nsec3.next_hashed_name, decoded_size);
940 if (c == 0)
941 continue;
942
943 r = dnssec_nsec3_hash(rr, DNS_RESOURCE_KEY_NAME(key), hashed);
944 /* RFC 5155, Section 8.1 says we MUST ignore NSEC3 RRs with unknown algorithms */
945 if (r == -EOPNOTSUPP)
946 continue;
947 if (r < 0)
948 return r;
949 if ((size_t) r != decoded_size)
950 continue;
951
952 r = memcmp(decoded, hashed, decoded_size);
953 if (r == 0) {
954 *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
955 return 0;
956 }
957
958 q = memcmp(hashed, rr->nsec3.next_hashed_name, decoded_size);
959
960 covered = c < 0 ?
961 r < 0 && q < 0 :
962 q < 0 || r < 0;
963
964 if (covered) {
965 *result = DNSSEC_NSEC_NXDOMAIN;
966 return 0;
967 }
968
969 break;
970 }
971
972 default:
973 break;
974 }
975 }
976
977 /* No approproate NSEC RR found, report this. */
978 *result = DNSSEC_NSEC_NO_RR;
979 return 0;
980}
981
24710c48
LP
982static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
983 [DNSSEC_NO] = "no",
984 [DNSSEC_TRUST] = "trust",
985 [DNSSEC_YES] = "yes",
986};
987DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
988
989static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
990 [DNSSEC_VALIDATED] = "validated",
991 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
992 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
993 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
994 [DNSSEC_NO_SIGNATURE] = "no-signature",
995 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 996 [DNSSEC_UNSIGNED] = "unsigned",
547973de 997 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 998 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
547973de
LP
999};
1000DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);