]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: never use data from failed transactions
[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
LP
38 * - Make trust anchor store read additional DS+DNSKEY data from disk
39 * - wildcard zones compatibility
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
278int dnssec_verify_rrset(
279 DnsAnswer *a,
280 DnsResourceKey *key,
281 DnsResourceRecord *rrsig,
282 DnsResourceRecord *dnskey,
547973de
LP
283 usec_t realtime,
284 DnssecResult *result) {
2a326321 285
2b442ac8
LP
286 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
287 size_t exponent_size, modulus_size, hash_size;
288 void *exponent, *modulus, *hash;
289 DnsResourceRecord **list, *rr;
290 gcry_md_hd_t md = NULL;
291 size_t k, n = 0;
292 int r;
293
294 assert(key);
295 assert(rrsig);
296 assert(dnskey);
547973de 297 assert(result);
2a326321
LP
298 assert(rrsig->key->type == DNS_TYPE_RRSIG);
299 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
300
301 /* Verifies the the RRSet matching the specified "key" in "a",
302 * using the signature "rrsig" and the key "dnskey". It's
303 * assumed the RRSIG and DNSKEY match. */
304
203f1b35
LP
305 if (!dnssec_algorithm_supported(rrsig->rrsig.algorithm)) {
306 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
307 return 0;
308 }
2b442ac8
LP
309
310 if (a->n_rrs > VERIFY_RRS_MAX)
311 return -E2BIG;
312
2a326321
LP
313 r = dnssec_rrsig_expired(rrsig, realtime);
314 if (r < 0)
315 return r;
547973de
LP
316 if (r > 0) {
317 *result = DNSSEC_SIGNATURE_EXPIRED;
318 return 0;
319 }
2a326321 320
2b442ac8
LP
321 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
322 list = newa(DnsResourceRecord *, a->n_rrs);
323
324 DNS_ANSWER_FOREACH(rr, a) {
325 r = dns_resource_key_equal(key, rr->key);
326 if (r < 0)
327 return r;
328 if (r == 0)
329 continue;
330
331 /* We need the wire format for ordering, and digest calculation */
332 r = dns_resource_record_to_wire_format(rr, true);
333 if (r < 0)
334 return r;
335
336 list[n++] = rr;
337 }
338
339 if (n <= 0)
340 return -ENODATA;
341
342 /* Bring the RRs into canonical order */
6c5e8fbf 343 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8 344
0638401a
LP
345 initialize_libgcrypt();
346
2b442ac8
LP
347 /* OK, the RRs are now in canonical order. Let's calculate the digest */
348 switch (rrsig->rrsig.algorithm) {
349
350 case DNSSEC_ALGORITHM_RSASHA1:
964ef14c 351 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
2b442ac8
LP
352 gcry_md_open(&md, GCRY_MD_SHA1, 0);
353 hash_size = 20;
354 break;
355
356 case DNSSEC_ALGORITHM_RSASHA256:
357 gcry_md_open(&md, GCRY_MD_SHA256, 0);
358 hash_size = 32;
359 break;
360
361 case DNSSEC_ALGORITHM_RSASHA512:
362 gcry_md_open(&md, GCRY_MD_SHA512, 0);
363 hash_size = 64;
364 break;
365
366 default:
367 assert_not_reached("Unknown digest");
368 }
369
370 if (!md)
371 return -EIO;
372
373 md_add_uint16(md, rrsig->rrsig.type_covered);
374 md_add_uint8(md, rrsig->rrsig.algorithm);
375 md_add_uint8(md, rrsig->rrsig.labels);
376 md_add_uint32(md, rrsig->rrsig.original_ttl);
377 md_add_uint32(md, rrsig->rrsig.expiration);
378 md_add_uint32(md, rrsig->rrsig.inception);
379 md_add_uint16(md, rrsig->rrsig.key_tag);
380
381 r = dns_name_to_wire_format(rrsig->rrsig.signer, 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 for (k = 0; k < n; k++) {
387 size_t l;
388 rr = list[k];
389
390 r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
391 if (r < 0)
392 goto finish;
393 gcry_md_write(md, wire_format_name, r);
394
395 md_add_uint16(md, rr->key->type);
396 md_add_uint16(md, rr->key->class);
397 md_add_uint32(md, rrsig->rrsig.original_ttl);
398
399 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
400 l = rr->wire_format_size - rr->wire_format_rdata_offset;
401 assert(l <= 0xFFFF);
402
403 md_add_uint16(md, (uint16_t) l);
404 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
405 }
406
407 hash = gcry_md_read(md, 0);
408 if (!hash) {
409 r = -EIO;
410 goto finish;
411 }
412
413 if (*(uint8_t*) dnskey->dnskey.key == 0) {
414 /* exponent is > 255 bytes long */
415
416 exponent = (uint8_t*) dnskey->dnskey.key + 3;
417 exponent_size =
418 ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
419 ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
420
421 if (exponent_size < 256) {
422 r = -EINVAL;
423 goto finish;
424 }
425
426 if (3 + exponent_size >= dnskey->dnskey.key_size) {
427 r = -EINVAL;
428 goto finish;
429 }
430
431 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
432 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
433
434 } else {
435 /* exponent is <= 255 bytes long */
436
437 exponent = (uint8_t*) dnskey->dnskey.key + 1;
438 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
439
440 if (exponent_size <= 0) {
441 r = -EINVAL;
442 goto finish;
443 }
444
445 if (1 + exponent_size >= dnskey->dnskey.key_size) {
446 r = -EINVAL;
447 goto finish;
448 }
449
450 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
451 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
452 }
453
454 r = dnssec_rsa_verify(
455 gcry_md_algo_name(gcry_md_get_algo(md)),
456 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
457 hash, hash_size,
458 exponent, exponent_size,
459 modulus, modulus_size);
460 if (r < 0)
461 goto finish;
462
547973de
LP
463 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
464 r = 0;
2b442ac8
LP
465
466finish:
467 gcry_md_close(md);
468 return r;
469}
470
471int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
472
473 assert(rrsig);
474 assert(dnskey);
475
476 /* Checks if the specified DNSKEY RR matches the key used for
477 * the signature in the specified RRSIG RR */
478
479 if (rrsig->key->type != DNS_TYPE_RRSIG)
480 return -EINVAL;
481
482 if (dnskey->key->type != DNS_TYPE_DNSKEY)
483 return 0;
484 if (dnskey->key->class != rrsig->key->class)
485 return 0;
486 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
487 return 0;
488 if (dnskey->dnskey.protocol != 3)
489 return 0;
490 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
491 return 0;
492
493 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
494 return 0;
495
15accc27 496 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
497}
498
105e1512 499int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
2b442ac8
LP
500 assert(key);
501 assert(rrsig);
502
503 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
504
505 if (rrsig->key->type != DNS_TYPE_RRSIG)
506 return 0;
507 if (rrsig->key->class != key->class)
508 return 0;
509 if (rrsig->rrsig.type_covered != key->type)
510 return 0;
511
512 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
513}
514
2a326321
LP
515int dnssec_verify_rrset_search(
516 DnsAnswer *a,
517 DnsResourceKey *key,
518 DnsAnswer *validated_dnskeys,
547973de
LP
519 usec_t realtime,
520 DnssecResult *result) {
2a326321 521
203f1b35 522 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
523 DnsResourceRecord *rrsig;
524 int r;
525
526 assert(key);
547973de 527 assert(result);
2b442ac8 528
105e1512 529 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
530
531 if (!a || a->n_rrs <= 0)
532 return -ENODATA;
533
534 /* Iterate through each RRSIG RR. */
535 DNS_ANSWER_FOREACH(rrsig, a) {
536 DnsResourceRecord *dnskey;
105e1512 537 DnsAnswerFlags flags;
2b442ac8 538
203f1b35 539 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
540 r = dnssec_key_match_rrsig(key, rrsig);
541 if (r < 0)
542 return r;
543 if (r == 0)
544 continue;
545
546 found_rrsig = true;
547
547973de 548 /* Look for a matching key */
105e1512 549 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 550 DnssecResult one_result;
2b442ac8 551
105e1512
LP
552 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
553 continue;
554
203f1b35 555 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
2b442ac8
LP
556 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
557 if (r < 0)
558 return r;
559 if (r == 0)
560 continue;
561
2a326321
LP
562 /* Take the time here, if it isn't set yet, so
563 * that we do all validations with the same
564 * time. */
565 if (realtime == USEC_INFINITY)
566 realtime = now(CLOCK_REALTIME);
567
2b442ac8
LP
568 /* Yay, we found a matching RRSIG with a matching
569 * DNSKEY, awesome. Now let's verify all entries of
570 * the RRSet against the RRSIG and DNSKEY
571 * combination. */
572
547973de 573 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 574 if (r < 0)
2b442ac8 575 return r;
203f1b35
LP
576
577 switch (one_result) {
578
579 case DNSSEC_VALIDATED:
580 /* Yay, the RR has been validated,
581 * return immediately. */
547973de
LP
582 *result = DNSSEC_VALIDATED;
583 return 0;
2b442ac8 584
203f1b35
LP
585 case DNSSEC_INVALID:
586 /* If the signature is invalid, let's try another
587 key and/or signature. After all they
588 key_tags and stuff are not unique, and
589 might be shared by multiple keys. */
590 found_invalid = true;
591 continue;
592
593 case DNSSEC_UNSUPPORTED_ALGORITHM:
594 /* If the key algorithm is
595 unsupported, try another
596 RRSIG/DNSKEY pair, but remember we
597 encountered this, so that we can
598 return a proper error when we
599 encounter nothing better. */
600 found_unsupported_algorithm = true;
601 continue;
602
603 case DNSSEC_SIGNATURE_EXPIRED:
604 /* If the signature is expired, try
605 another one, but remember it, so
606 that we can return this */
607 found_expired_rrsig = true;
608 continue;
609
610 default:
611 assert_not_reached("Unexpected DNSSEC validation result");
612 }
2b442ac8
LP
613 }
614 }
615
203f1b35
LP
616 if (found_expired_rrsig)
617 *result = DNSSEC_SIGNATURE_EXPIRED;
618 else if (found_unsupported_algorithm)
619 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
620 else if (found_invalid)
547973de
LP
621 *result = DNSSEC_INVALID;
622 else if (found_rrsig)
623 *result = DNSSEC_MISSING_KEY;
624 else
625 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 626
547973de 627 return 0;
2b442ac8
LP
628}
629
105e1512
LP
630int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
631 DnsResourceRecord *rr;
632 int r;
633
634 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
635
636 DNS_ANSWER_FOREACH(rr, a) {
637 r = dnssec_key_match_rrsig(key, rr);
638 if (r < 0)
639 return r;
640 if (r > 0)
641 return 1;
642 }
643
644 return 0;
645}
646
2b442ac8 647int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
648 size_t c = 0;
649 int r;
650
651 /* Converts the specified hostname into DNSSEC canonicalized
652 * form. */
653
654 if (buffer_max < 2)
655 return -ENOBUFS;
656
657 for (;;) {
658 size_t i;
659
660 r = dns_label_unescape(&n, buffer, buffer_max);
661 if (r < 0)
662 return r;
663 if (r == 0)
664 break;
665 if (r > 0) {
666 int k;
667
668 /* DNSSEC validation is always done on the ASCII version of the label */
669 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
670 if (k < 0)
671 return k;
672 if (k > 0)
673 r = k;
674 }
675
676 if (buffer_max < (size_t) r + 2)
677 return -ENOBUFS;
678
679 /* The DNSSEC canonical form is not clear on what to
680 * do with dots appearing in labels, the way DNS-SD
681 * does it. Refuse it for now. */
682
683 if (memchr(buffer, '.', r))
684 return -EINVAL;
685
686 for (i = 0; i < (size_t) r; i ++) {
687 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
688 buffer[i] = buffer[i] - 'A' + 'a';
689 }
690
691 buffer[r] = '.';
692
693 buffer += r + 1;
694 c += r + 1;
695
696 buffer_max -= r + 1;
697 }
698
699 if (c <= 0) {
700 /* Not even a single label: this is the root domain name */
701
702 assert(buffer_max > 2);
703 buffer[0] = '.';
704 buffer[1] = 0;
705
706 return 1;
707 }
708
709 return (int) c;
710}
711
a1972a91
LP
712static int digest_to_gcrypt(uint8_t algorithm) {
713
714 /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
715
716 switch (algorithm) {
717
718 case DNSSEC_DIGEST_SHA1:
719 return GCRY_MD_SHA1;
720
721 case DNSSEC_DIGEST_SHA256:
722 return GCRY_MD_SHA256;
723
724 default:
725 return -EOPNOTSUPP;
726 }
727}
728
2b442ac8 729int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
2b442ac8 730 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
731 gcry_md_hd_t md = NULL;
732 size_t hash_size;
733 int algorithm;
2b442ac8
LP
734 void *result;
735 int r;
736
737 assert(dnskey);
738 assert(ds);
739
740 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
741
742 if (dnskey->key->type != DNS_TYPE_DNSKEY)
743 return -EINVAL;
744 if (ds->key->type != DNS_TYPE_DS)
745 return -EINVAL;
746 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
747 return -EKEYREJECTED;
748 if (dnskey->dnskey.protocol != 3)
749 return -EKEYREJECTED;
750
2b442ac8
LP
751 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
752 return 0;
753 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
754 return 0;
755
0638401a
LP
756 initialize_libgcrypt();
757
a1972a91
LP
758 algorithm = digest_to_gcrypt(ds->ds.digest_type);
759 if (algorithm < 0)
760 return algorithm;
2b442ac8 761
a1972a91
LP
762 hash_size = gcry_md_get_algo_dlen(algorithm);
763 assert(hash_size > 0);
2b442ac8 764
a1972a91
LP
765 if (ds->ds.digest_size != hash_size)
766 return 0;
2b442ac8 767
a1972a91
LP
768 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
769 if (r < 0)
770 return r;
2b442ac8 771
a1972a91 772 gcry_md_open(&md, algorithm, 0);
2b442ac8
LP
773 if (!md)
774 return -EIO;
775
2b442ac8
LP
776 gcry_md_write(md, owner_name, r);
777 md_add_uint16(md, dnskey->dnskey.flags);
778 md_add_uint8(md, dnskey->dnskey.protocol);
779 md_add_uint8(md, dnskey->dnskey.algorithm);
780 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
781
782 result = gcry_md_read(md, 0);
783 if (!result) {
784 r = -EIO;
785 goto finish;
786 }
787
788 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
789
790finish:
791 gcry_md_close(md);
792 return r;
793}
24710c48 794
547973de
LP
795int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
796 DnsResourceRecord *ds;
105e1512 797 DnsAnswerFlags flags;
547973de
LP
798 int r;
799
800 assert(dnskey);
801
802 if (dnskey->key->type != DNS_TYPE_DNSKEY)
803 return 0;
804
105e1512
LP
805 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
806
807 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
808 continue;
547973de
LP
809
810 if (ds->key->type != DNS_TYPE_DS)
811 continue;
812
813 r = dnssec_verify_dnskey(dnskey, ds);
814 if (r < 0)
815 return r;
816 if (r > 0)
817 return 1;
818 }
819
820 return 0;
821}
822
72667f08
LP
823int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
824 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
825 gcry_md_hd_t md = NULL;
826 size_t hash_size;
827 int algorithm;
828 void *result;
829 unsigned k;
830 int r;
831
832 assert(nsec3);
833 assert(name);
834 assert(ret);
835
836 if (nsec3->key->type != DNS_TYPE_NSEC3)
837 return -EINVAL;
838
839 algorithm = digest_to_gcrypt(nsec3->nsec3.algorithm);
840 if (algorithm < 0)
841 return algorithm;
842
843 initialize_libgcrypt();
844
845 hash_size = gcry_md_get_algo_dlen(algorithm);
846 assert(hash_size > 0);
847
848 if (nsec3->nsec3.next_hashed_name_size != hash_size)
849 return -EINVAL;
850
851 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
852 if (r < 0)
853 return r;
854
855 gcry_md_open(&md, algorithm, 0);
856 if (!md)
857 return -EIO;
858
859 gcry_md_write(md, wire_format, r);
860 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
861
862 result = gcry_md_read(md, 0);
863 if (!result) {
864 r = -EIO;
865 goto finish;
866 }
867
868 for (k = 0; k < nsec3->nsec3.iterations; k++) {
869 uint8_t tmp[hash_size];
870 memcpy(tmp, result, hash_size);
871
872 gcry_md_reset(md);
873 gcry_md_write(md, tmp, hash_size);
874 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
875
876 result = gcry_md_read(md, 0);
877 if (!result) {
878 r = -EIO;
879 goto finish;
880 }
881 }
882
883 memcpy(ret, result, hash_size);
884 r = (int) hash_size;
885
886finish:
887 gcry_md_close(md);
888 return r;
889}
890
105e1512
LP
891static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
892 _cleanup_free_ char *next_closer_domain = NULL, *l = NULL;
893 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
894 const char *p, *pp = NULL;
72667f08 895 DnsResourceRecord *rr;
105e1512
LP
896 DnsAnswerFlags flags;
897 int hashed_size, r;
72667f08
LP
898
899 assert(key);
900 assert(result);
901
105e1512
LP
902 /* First step, look for the closest encloser NSEC3 RR in 'answer' that matches 'key' */
903 p = DNS_RESOURCE_KEY_NAME(key);
904 for (;;) {
905 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
906 _cleanup_free_ char *hashed_domain = NULL, *label = NULL;
72667f08 907
105e1512
LP
908 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
909 continue;
72667f08 910
105e1512
LP
911 if (rr->key->type != DNS_TYPE_NSEC3)
912 continue;
72667f08 913
105e1512
LP
914 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
915 if (!IN_SET(rr->nsec3.flags, 0, 1))
916 continue;
72667f08 917
105e1512 918 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), p);
72667f08
LP
919 if (r < 0)
920 return r;
105e1512
LP
921 if (r == 0)
922 continue;
923
924 hashed_size = dnssec_nsec3_hash(rr, p, hashed);
925 if (hashed_size == -EOPNOTSUPP) {
926 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
72667f08
LP
927 return 0;
928 }
105e1512
LP
929 if (hashed_size < 0)
930 return hashed_size;
931 if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
932 return -EBADMSG;
72667f08 933
105e1512
LP
934 label = base32hexmem(hashed, hashed_size, false);
935 if (!label)
936 return -ENOMEM;
937
938 hashed_domain = strjoin(label, ".", p, NULL);
939 if (!hashed_domain)
940 return -ENOMEM;
941
942 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);
72667f08
LP
943 if (r < 0)
944 return r;
105e1512
LP
945 if (r > 0)
946 goto found;
947 }
948
949 /* We didn't find the closest encloser with this name,
950 * but let's remember this domain name, it might be
951 * the next closer name */
952
953 pp = p;
954
955 /* Strip one label from the front */
956 r = dns_name_parent(&p);
957 if (r < 0)
958 return r;
959 if (r == 0)
72667f08 960 break;
105e1512 961 }
72667f08 962
105e1512
LP
963 *result = DNSSEC_NSEC_NO_RR;
964 return 0;
72667f08 965
105e1512
LP
966found:
967 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 968
105e1512
LP
969 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
970 if (bitmap_isset(rr->nsec3.types, DNS_TYPE_DNAME))
971 return -EBADMSG;
72667f08 972
105e1512
LP
973 /* Ensure that this data is from the delegated domain
974 * (i.e. originates from the "lower" DNS server), and isn't
975 * just glue records (i.e. doesn't originate from the "upper"
976 * DNS server). */
977 if (bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) &&
978 !bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA))
979 return -EBADMSG;
72667f08 980
105e1512
LP
981 if (!pp) {
982 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
983 *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
984 return 0;
985 }
72667f08 986
105e1512
LP
987 r = dnssec_nsec3_hash(rr, pp, hashed);
988 if (r < 0)
989 return r;
990 if (r != hashed_size)
991 return -EBADMSG;
72667f08 992
105e1512
LP
993 l = base32hexmem(hashed, hashed_size, false);
994 if (!l)
995 return -ENOMEM;
72667f08 996
105e1512
LP
997 next_closer_domain = strjoin(l, ".", p, NULL);
998 if (!next_closer_domain)
999 return -ENOMEM;
72667f08 1000
105e1512
LP
1001 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1002 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
1003 const char *nsec3_parent;
1004
1005 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1006 continue;
72667f08 1007
105e1512
LP
1008 if (rr->key->type != DNS_TYPE_NSEC3)
1009 continue;
72667f08 1010
105e1512
LP
1011 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1012 if (!IN_SET(rr->nsec3.flags, 0, 1))
1013 continue;
72667f08 1014
105e1512
LP
1015 nsec3_parent = DNS_RESOURCE_KEY_NAME(rr->key);
1016 r = dns_name_parent(&nsec3_parent);
1017 if (r < 0)
1018 return r;
1019 if (r == 0)
1020 continue;
1021
1022 r = dns_name_equal(p, nsec3_parent);
1023 if (r < 0)
1024 return r;
1025 if (r == 0)
1026 continue;
1027
1028 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1029 if (!label)
1030 return -ENOMEM;
1031
1032 next_hashed_domain = strjoin(label, ".", p, NULL);
1033 if (!next_hashed_domain)
1034 return -ENOMEM;
1035
1036 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1037 if (r < 0)
1038 return r;
1039 if (r > 0) {
1040 if (rr->nsec3.flags & 1)
1041 *result = DNSSEC_NSEC_OPTOUT;
1042 else
72667f08 1043 *result = DNSSEC_NSEC_NXDOMAIN;
105e1512
LP
1044
1045 return 1;
1046 }
1047 }
1048
1049 *result = DNSSEC_NSEC_NO_RR;
1050 return 0;
1051}
1052
1053int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
1054 DnsResourceRecord *rr;
1055 bool have_nsec3 = false;
1056 DnsAnswerFlags flags;
1057 int r;
1058
1059 assert(key);
1060 assert(result);
1061
1062 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1063
1064 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1065
1066 if (rr->key->class != key->class)
1067 continue;
1068
1069 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1070 continue;
1071
1072 switch (rr->key->type) {
1073
1074 case DNS_TYPE_NSEC:
1075
1076 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1077 if (r < 0)
1078 return r;
1079 if (r > 0) {
1080 *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
72667f08
LP
1081 return 0;
1082 }
1083
105e1512
LP
1084 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1085 if (r < 0)
1086 return r;
1087 if (r > 0) {
1088 *result = DNSSEC_NSEC_NXDOMAIN;
1089 return 0;
1090 }
72667f08 1091 break;
72667f08 1092
105e1512
LP
1093 case DNS_TYPE_NSEC3:
1094 have_nsec3 = true;
72667f08
LP
1095 break;
1096 }
1097 }
1098
105e1512
LP
1099 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1100 if (have_nsec3)
1101 return dnssec_test_nsec3(answer, key, result);
1102
72667f08
LP
1103 /* No approproate NSEC RR found, report this. */
1104 *result = DNSSEC_NSEC_NO_RR;
1105 return 0;
1106}
1107
24710c48
LP
1108static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
1109 [DNSSEC_NO] = "no",
24710c48
LP
1110 [DNSSEC_YES] = "yes",
1111};
1112DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
1113
1114static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1115 [DNSSEC_VALIDATED] = "validated",
1116 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1117 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1118 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1119 [DNSSEC_NO_SIGNATURE] = "no-signature",
1120 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1121 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1122 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1123 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
547973de
LP
1124};
1125DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);