]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: consider inverted RRSIG validity intervals expired
[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 *
3ecc3df8 38 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
bb1fa242 39 * - multi-label zone compatibility
3e92a719 40 * - cname/dname compatibility
160fbda9 41 * - nxdomain on qname
d1d1d4b8 42 * - bus calls to override DNSEC setting per interface
28bf03b5
LP
43 * - log all DNSSEC downgrades
44 * - enable by default
2cd87277 45 *
3a33c81b
LP
46 * - RFC 4035, Section 5.3.4 (When receiving a positive wildcard reply, use NSEC to ensure it actually really applies)
47 * - RFC 6840, Section 4.1 (ensure we don't get fed a glue NSEC from the parent zone)
48 * - RFC 6840, Section 4.3 (check for CNAME on NSEC too)
2b442ac8
LP
49 * */
50
51#define VERIFY_RRS_MAX 256
52#define MAX_KEY_SIZE (32*1024)
53
896c5672
LP
54/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
55#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
56
3a33c81b
LP
57/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
58#define NSEC3_ITERATIONS_MAX 2500
a8f158b9 59
2b442ac8
LP
60/*
61 * The DNSSEC Chain of trust:
62 *
63 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
64 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
65 * DS RRs are protected like normal RRs
66 *
67 * Example chain:
68 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
69 */
70
0638401a
LP
71static void initialize_libgcrypt(void) {
72 const char *p;
73
74 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
75 return;
76
77 p = gcry_check_version("1.4.5");
78 assert(p);
79
80 gcry_control(GCRYCTL_DISABLE_SECMEM);
81 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
82}
83
0c857028 84uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
2b442ac8 85 const uint8_t *p;
0c857028 86 uint32_t sum, f;
2b442ac8
LP
87 size_t i;
88
89 /* The algorithm from RFC 4034, Appendix B. */
90
91 assert(dnskey);
92 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
93
0c857028
LP
94 f = (uint32_t) dnskey->dnskey.flags;
95
96 if (mask_revoke)
97 f &= ~DNSKEY_FLAG_REVOKE;
98
99 sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
2b442ac8
LP
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
85aeaccc 125 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
2b442ac8 126
85aeaccc 127 r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
2b442ac8
LP
128 if (r != 0)
129 return r;
130
85aeaccc 131 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8 132 return -1;
85aeaccc 133 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8
LP
134 return 1;
135
136 return 0;
137}
138
ea3a892f 139static int dnssec_rsa_verify_raw(
2b442ac8
LP
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
ea3a892f
LP
229static int dnssec_rsa_verify(
230 const char *hash_algorithm,
231 const void *hash, size_t hash_size,
232 DnsResourceRecord *rrsig,
233 DnsResourceRecord *dnskey) {
234
235 size_t exponent_size, modulus_size;
236 void *exponent, *modulus;
237
238 assert(hash_algorithm);
239 assert(hash);
240 assert(hash_size > 0);
241 assert(rrsig);
242 assert(dnskey);
243
244 if (*(uint8_t*) dnskey->dnskey.key == 0) {
245 /* exponent is > 255 bytes long */
246
247 exponent = (uint8_t*) dnskey->dnskey.key + 3;
248 exponent_size =
ac04adbe
TG
249 ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
250 ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
ea3a892f
LP
251
252 if (exponent_size < 256)
253 return -EINVAL;
254
255 if (3 + exponent_size >= dnskey->dnskey.key_size)
256 return -EINVAL;
257
258 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
259 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
260
261 } else {
262 /* exponent is <= 255 bytes long */
263
264 exponent = (uint8_t*) dnskey->dnskey.key + 1;
265 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
266
267 if (exponent_size <= 0)
268 return -EINVAL;
269
270 if (1 + exponent_size >= dnskey->dnskey.key_size)
271 return -EINVAL;
272
273 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
274 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
275 }
276
277 return dnssec_rsa_verify_raw(
278 hash_algorithm,
279 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
280 hash, hash_size,
281 exponent, exponent_size,
282 modulus, modulus_size);
283}
284
e0240c64
LP
285static int dnssec_ecdsa_verify_raw(
286 const char *hash_algorithm,
287 const char *curve,
288 const void *signature_r, size_t signature_r_size,
289 const void *signature_s, size_t signature_s_size,
290 const void *data, size_t data_size,
291 const void *key, size_t key_size) {
292
293 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
294 gcry_mpi_t q = NULL, r = NULL, s = NULL;
295 gcry_error_t ge;
296 int k;
297
298 assert(hash_algorithm);
299
300 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
301 if (ge != 0) {
302 k = -EIO;
303 goto finish;
304 }
305
306 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
307 if (ge != 0) {
308 k = -EIO;
309 goto finish;
310 }
311
312 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
313 if (ge != 0) {
314 k = -EIO;
315 goto finish;
316 }
317
318 ge = gcry_sexp_build(&signature_sexp,
319 NULL,
320 "(sig-val (ecdsa (r %m) (s %m)))",
321 r,
322 s);
323 if (ge != 0) {
324 k = -EIO;
325 goto finish;
326 }
327
328 ge = gcry_sexp_build(&data_sexp,
329 NULL,
330 "(data (flags rfc6979) (hash %s %b))",
331 hash_algorithm,
332 (int) data_size,
333 data);
334 if (ge != 0) {
335 k = -EIO;
336 goto finish;
337 }
338
339 ge = gcry_sexp_build(&public_key_sexp,
340 NULL,
341 "(public-key (ecc (curve %s) (q %m)))",
342 curve,
343 q);
344 if (ge != 0) {
345 k = -EIO;
346 goto finish;
347 }
348
349 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
350 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
351 k = 0;
352 else if (ge != 0) {
353 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
354 k = -EIO;
355 } else
356 k = 1;
357finish:
358 if (r)
359 gcry_mpi_release(r);
360 if (s)
361 gcry_mpi_release(s);
362 if (q)
363 gcry_mpi_release(q);
364
365 if (public_key_sexp)
366 gcry_sexp_release(public_key_sexp);
367 if (signature_sexp)
368 gcry_sexp_release(signature_sexp);
369 if (data_sexp)
370 gcry_sexp_release(data_sexp);
371
372 return k;
373}
374
375static int dnssec_ecdsa_verify(
376 const char *hash_algorithm,
377 int algorithm,
378 const void *hash, size_t hash_size,
379 DnsResourceRecord *rrsig,
380 DnsResourceRecord *dnskey) {
381
382 const char *curve;
383 size_t key_size;
384 uint8_t *q;
385
386 assert(hash);
387 assert(hash_size);
388 assert(rrsig);
389 assert(dnskey);
390
391 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
392 key_size = 32;
393 curve = "NIST P-256";
394 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
395 key_size = 48;
396 curve = "NIST P-384";
397 } else
398 return -EOPNOTSUPP;
399
400 if (dnskey->dnskey.key_size != key_size * 2)
401 return -EINVAL;
402
403 if (rrsig->rrsig.signature_size != key_size * 2)
404 return -EINVAL;
405
406 q = alloca(key_size*2 + 1);
407 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
408 memcpy(q+1, dnskey->dnskey.key, key_size*2);
409
410 return dnssec_ecdsa_verify_raw(
411 hash_algorithm,
412 curve,
413 rrsig->rrsig.signature, key_size,
414 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
415 hash, hash_size,
416 q, key_size*2+1);
417}
418
2b442ac8
LP
419static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
420 gcry_md_write(md, &v, sizeof(v));
421}
422
423static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
424 v = htobe16(v);
425 gcry_md_write(md, &v, sizeof(v));
426}
427
428static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
429 v = htobe32(v);
430 gcry_md_write(md, &v, sizeof(v));
431}
432
2a326321
LP
433static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
434 usec_t expiration, inception, skew;
435
436 assert(rrsig);
437 assert(rrsig->key->type == DNS_TYPE_RRSIG);
438
439 if (realtime == USEC_INFINITY)
440 realtime = now(CLOCK_REALTIME);
441
442 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
443 inception = rrsig->rrsig.inception * USEC_PER_SEC;
444
5ae5cd40 445 /* Consider inverted validity intervals as expired */
2a326321 446 if (inception > expiration)
5ae5cd40 447 return true;
2a326321 448
896c5672
LP
449 /* Permit a certain amount of clock skew of 10% of the valid
450 * time range. This takes inspiration from unbound's
451 * resolver. */
2a326321 452 skew = (expiration - inception) / 10;
896c5672
LP
453 if (skew > SKEW_MAX)
454 skew = SKEW_MAX;
2a326321
LP
455
456 if (inception < skew)
457 inception = 0;
458 else
459 inception -= skew;
460
461 if (expiration + skew < expiration)
462 expiration = USEC_INFINITY;
463 else
464 expiration += skew;
465
466 return realtime < inception || realtime > expiration;
467}
468
ca994e85 469static int algorithm_to_gcrypt_md(uint8_t algorithm) {
fbf1a66d 470
6af47493
LP
471 /* Translates a DNSSEC signature algorithm into a gcrypt
472 * digest identifier.
473 *
474 * Note that we implement all algorithms listed as "Must
475 * implement" and "Recommended to Implement" in RFC6944. We
476 * don't implement any algorithms that are listed as
477 * "Optional" or "Must Not Implement". Specifically, we do not
478 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
479 * GOST-ECC. */
fbf1a66d
LP
480
481 switch (algorithm) {
482
483 case DNSSEC_ALGORITHM_RSASHA1:
484 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
485 return GCRY_MD_SHA1;
486
487 case DNSSEC_ALGORITHM_RSASHA256:
e0240c64 488 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
fbf1a66d
LP
489 return GCRY_MD_SHA256;
490
e0240c64
LP
491 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
492 return GCRY_MD_SHA384;
493
fbf1a66d
LP
494 case DNSSEC_ALGORITHM_RSASHA512:
495 return GCRY_MD_SHA512;
496
497 default:
498 return -EOPNOTSUPP;
499 }
500}
501
2a326321
LP
502int dnssec_verify_rrset(
503 DnsAnswer *a,
0c857028 504 const DnsResourceKey *key,
2a326321
LP
505 DnsResourceRecord *rrsig,
506 DnsResourceRecord *dnskey,
547973de
LP
507 usec_t realtime,
508 DnssecResult *result) {
2a326321 509
2b442ac8 510 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
ea3a892f
LP
511 size_t hash_size;
512 void *hash;
2b442ac8
LP
513 DnsResourceRecord **list, *rr;
514 gcry_md_hd_t md = NULL;
ca994e85 515 int r, md_algorithm;
0c7bff0a 516 bool wildcard = false;
2b442ac8 517 size_t k, n = 0;
2b442ac8
LP
518
519 assert(key);
520 assert(rrsig);
521 assert(dnskey);
547973de 522 assert(result);
2a326321
LP
523 assert(rrsig->key->type == DNS_TYPE_RRSIG);
524 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
525
526 /* Verifies the the RRSet matching the specified "key" in "a",
527 * using the signature "rrsig" and the key "dnskey". It's
528 * assumed the RRSIG and DNSKEY match. */
529
ca994e85
LP
530 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
531 if (md_algorithm == -EOPNOTSUPP) {
203f1b35
LP
532 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
533 return 0;
534 }
ca994e85
LP
535 if (md_algorithm < 0)
536 return md_algorithm;
2b442ac8 537
2a326321
LP
538 r = dnssec_rrsig_expired(rrsig, realtime);
539 if (r < 0)
540 return r;
547973de
LP
541 if (r > 0) {
542 *result = DNSSEC_SIGNATURE_EXPIRED;
543 return 0;
544 }
2a326321 545
2b442ac8 546 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
0f23174c 547 list = newa(DnsResourceRecord *, dns_answer_size(a));
2b442ac8
LP
548
549 DNS_ANSWER_FOREACH(rr, a) {
550 r = dns_resource_key_equal(key, rr->key);
551 if (r < 0)
552 return r;
553 if (r == 0)
554 continue;
555
556 /* We need the wire format for ordering, and digest calculation */
557 r = dns_resource_record_to_wire_format(rr, true);
558 if (r < 0)
559 return r;
560
561 list[n++] = rr;
935a999f
TG
562
563 if (n > VERIFY_RRS_MAX)
564 return -E2BIG;
2b442ac8
LP
565 }
566
567 if (n <= 0)
568 return -ENODATA;
569
570 /* Bring the RRs into canonical order */
6c5e8fbf 571 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
572
573 /* OK, the RRs are now in canonical order. Let's calculate the digest */
fbf1a66d 574 initialize_libgcrypt();
2b442ac8 575
ca994e85 576 hash_size = gcry_md_get_algo_dlen(md_algorithm);
fbf1a66d 577 assert(hash_size > 0);
2b442ac8 578
ca994e85 579 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
580 if (!md)
581 return -EIO;
582
583 md_add_uint16(md, rrsig->rrsig.type_covered);
584 md_add_uint8(md, rrsig->rrsig.algorithm);
585 md_add_uint8(md, rrsig->rrsig.labels);
586 md_add_uint32(md, rrsig->rrsig.original_ttl);
587 md_add_uint32(md, rrsig->rrsig.expiration);
588 md_add_uint32(md, rrsig->rrsig.inception);
589 md_add_uint16(md, rrsig->rrsig.key_tag);
590
591 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
592 if (r < 0)
593 goto finish;
594 gcry_md_write(md, wire_format_name, r);
595
596 for (k = 0; k < n; k++) {
e7ff0e0b 597 const char *suffix;
2b442ac8
LP
598 size_t l;
599 rr = list[k];
600
e7ff0e0b
LP
601 r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
602 if (r < 0)
603 goto finish;
0c7bff0a 604 if (r > 0) /* This is a wildcard! */ {
e7ff0e0b 605 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
0c7bff0a
LP
606 wildcard = true;
607 }
e7ff0e0b
LP
608
609 r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
2b442ac8
LP
610 if (r < 0)
611 goto finish;
612 gcry_md_write(md, wire_format_name, r);
613
614 md_add_uint16(md, rr->key->type);
615 md_add_uint16(md, rr->key->class);
616 md_add_uint32(md, rrsig->rrsig.original_ttl);
617
85aeaccc 618 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
2b442ac8
LP
619 assert(l <= 0xFFFF);
620
621 md_add_uint16(md, (uint16_t) l);
85aeaccc 622 gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
2b442ac8
LP
623 }
624
625 hash = gcry_md_read(md, 0);
626 if (!hash) {
627 r = -EIO;
628 goto finish;
629 }
630
e0240c64
LP
631 switch (rrsig->rrsig.algorithm) {
632
633 case DNSSEC_ALGORITHM_RSASHA1:
634 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
635 case DNSSEC_ALGORITHM_RSASHA256:
636 case DNSSEC_ALGORITHM_RSASHA512:
637 r = dnssec_rsa_verify(
ca994e85 638 gcry_md_algo_name(md_algorithm),
e0240c64
LP
639 hash, hash_size,
640 rrsig,
641 dnskey);
642 break;
643
644 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
645 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
646 r = dnssec_ecdsa_verify(
ca994e85 647 gcry_md_algo_name(md_algorithm),
e0240c64
LP
648 rrsig->rrsig.algorithm,
649 hash, hash_size,
650 rrsig,
651 dnskey);
652 break;
653 }
654
2b442ac8
LP
655 if (r < 0)
656 goto finish;
657
0c7bff0a
LP
658 if (!r)
659 *result = DNSSEC_INVALID;
660 else if (wildcard)
661 *result = DNSSEC_VALIDATED_WILDCARD;
662 else
663 *result = DNSSEC_VALIDATED;
547973de 664 r = 0;
2b442ac8
LP
665
666finish:
667 gcry_md_close(md);
668 return r;
669}
670
0c857028 671int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
672
673 assert(rrsig);
674 assert(dnskey);
675
676 /* Checks if the specified DNSKEY RR matches the key used for
677 * the signature in the specified RRSIG RR */
678
679 if (rrsig->key->type != DNS_TYPE_RRSIG)
680 return -EINVAL;
681
682 if (dnskey->key->type != DNS_TYPE_DNSKEY)
683 return 0;
684 if (dnskey->key->class != rrsig->key->class)
685 return 0;
686 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
687 return 0;
0c857028 688 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 689 return 0;
2b442ac8
LP
690 if (dnskey->dnskey.protocol != 3)
691 return 0;
692 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
693 return 0;
694
0c857028 695 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
696 return 0;
697
15accc27 698 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
699}
700
105e1512 701int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
e7ff0e0b
LP
702 int r;
703
2b442ac8
LP
704 assert(key);
705 assert(rrsig);
706
707 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
708
709 if (rrsig->key->type != DNS_TYPE_RRSIG)
710 return 0;
711 if (rrsig->key->class != key->class)
712 return 0;
713 if (rrsig->rrsig.type_covered != key->type)
714 return 0;
715
e7ff0e0b
LP
716 /* Make sure signer is a parent of the RRset */
717 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
718 if (r <= 0)
719 return r;
720
721 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
722 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
723 if (r < 0)
724 return r;
725 if (r < rrsig->rrsig.labels)
726 return 0;
727
2b442ac8
LP
728 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
729}
730
ee3d6aff
LP
731static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
732 DnsResourceRecord *rr;
733 int r;
734
735 assert(key);
736 assert(rrsig);
737
738 DNS_ANSWER_FOREACH(rr, a) {
739 r = dns_resource_key_equal(key, rr->key);
740 if (r < 0)
741 return r;
742 if (r == 0)
743 continue;
744
745 /* Pick the TTL as the minimum of the RR's TTL, the
746 * RR's original TTL according to the RRSIG and the
747 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
748 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
749 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
750 }
751
752 return 0;
753}
754
2a326321
LP
755int dnssec_verify_rrset_search(
756 DnsAnswer *a,
0c857028 757 const DnsResourceKey *key,
2a326321 758 DnsAnswer *validated_dnskeys,
547973de 759 usec_t realtime,
0c7bff0a
LP
760 DnssecResult *result,
761 DnsResourceRecord **ret_rrsig) {
2a326321 762
203f1b35 763 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
764 DnsResourceRecord *rrsig;
765 int r;
766
767 assert(key);
547973de 768 assert(result);
2b442ac8 769
105e1512 770 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
771
772 if (!a || a->n_rrs <= 0)
773 return -ENODATA;
774
775 /* Iterate through each RRSIG RR. */
776 DNS_ANSWER_FOREACH(rrsig, a) {
777 DnsResourceRecord *dnskey;
105e1512 778 DnsAnswerFlags flags;
2b442ac8 779
203f1b35 780 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
781 r = dnssec_key_match_rrsig(key, rrsig);
782 if (r < 0)
783 return r;
784 if (r == 0)
785 continue;
786
787 found_rrsig = true;
788
547973de 789 /* Look for a matching key */
105e1512 790 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 791 DnssecResult one_result;
2b442ac8 792
105e1512
LP
793 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
794 continue;
795
203f1b35 796 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
0c857028 797 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
798 if (r < 0)
799 return r;
800 if (r == 0)
801 continue;
802
2a326321
LP
803 /* Take the time here, if it isn't set yet, so
804 * that we do all validations with the same
805 * time. */
806 if (realtime == USEC_INFINITY)
807 realtime = now(CLOCK_REALTIME);
808
2b442ac8
LP
809 /* Yay, we found a matching RRSIG with a matching
810 * DNSKEY, awesome. Now let's verify all entries of
811 * the RRSet against the RRSIG and DNSKEY
812 * combination. */
813
547973de 814 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 815 if (r < 0)
2b442ac8 816 return r;
203f1b35
LP
817
818 switch (one_result) {
819
820 case DNSSEC_VALIDATED:
0c7bff0a 821 case DNSSEC_VALIDATED_WILDCARD:
203f1b35 822 /* Yay, the RR has been validated,
ee3d6aff
LP
823 * return immediately, but fix up the expiry */
824 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
825 if (r < 0)
826 return r;
827
0c7bff0a
LP
828 if (ret_rrsig)
829 *ret_rrsig = rrsig;
830
831 *result = one_result;
547973de 832 return 0;
2b442ac8 833
203f1b35
LP
834 case DNSSEC_INVALID:
835 /* If the signature is invalid, let's try another
836 key and/or signature. After all they
837 key_tags and stuff are not unique, and
838 might be shared by multiple keys. */
839 found_invalid = true;
840 continue;
841
842 case DNSSEC_UNSUPPORTED_ALGORITHM:
843 /* If the key algorithm is
844 unsupported, try another
845 RRSIG/DNSKEY pair, but remember we
846 encountered this, so that we can
847 return a proper error when we
848 encounter nothing better. */
849 found_unsupported_algorithm = true;
850 continue;
851
852 case DNSSEC_SIGNATURE_EXPIRED:
853 /* If the signature is expired, try
854 another one, but remember it, so
855 that we can return this */
856 found_expired_rrsig = true;
857 continue;
858
859 default:
860 assert_not_reached("Unexpected DNSSEC validation result");
861 }
2b442ac8
LP
862 }
863 }
864
203f1b35
LP
865 if (found_expired_rrsig)
866 *result = DNSSEC_SIGNATURE_EXPIRED;
867 else if (found_unsupported_algorithm)
868 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
869 else if (found_invalid)
547973de
LP
870 *result = DNSSEC_INVALID;
871 else if (found_rrsig)
872 *result = DNSSEC_MISSING_KEY;
873 else
874 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 875
0c7bff0a
LP
876 if (ret_rrsig)
877 *ret_rrsig = NULL;
878
547973de 879 return 0;
2b442ac8
LP
880}
881
105e1512
LP
882int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
883 DnsResourceRecord *rr;
884 int r;
885
886 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
887
888 DNS_ANSWER_FOREACH(rr, a) {
889 r = dnssec_key_match_rrsig(key, rr);
890 if (r < 0)
891 return r;
892 if (r > 0)
893 return 1;
894 }
895
896 return 0;
897}
898
2b442ac8 899int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
900 size_t c = 0;
901 int r;
902
903 /* Converts the specified hostname into DNSSEC canonicalized
904 * form. */
905
906 if (buffer_max < 2)
907 return -ENOBUFS;
908
909 for (;;) {
2b442ac8
LP
910 r = dns_label_unescape(&n, buffer, buffer_max);
911 if (r < 0)
912 return r;
913 if (r == 0)
914 break;
915 if (r > 0) {
916 int k;
917
918 /* DNSSEC validation is always done on the ASCII version of the label */
919 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
920 if (k < 0)
921 return k;
922 if (k > 0)
923 r = k;
924 }
925
926 if (buffer_max < (size_t) r + 2)
927 return -ENOBUFS;
928
929 /* The DNSSEC canonical form is not clear on what to
930 * do with dots appearing in labels, the way DNS-SD
931 * does it. Refuse it for now. */
932
933 if (memchr(buffer, '.', r))
934 return -EINVAL;
935
b577e3d5 936 ascii_strlower_n(buffer, (size_t) r);
2b442ac8
LP
937 buffer[r] = '.';
938
939 buffer += r + 1;
940 c += r + 1;
941
942 buffer_max -= r + 1;
943 }
944
945 if (c <= 0) {
946 /* Not even a single label: this is the root domain name */
947
948 assert(buffer_max > 2);
949 buffer[0] = '.';
950 buffer[1] = 0;
951
952 return 1;
953 }
954
955 return (int) c;
956}
957
ca994e85 958static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 959
fbf1a66d 960 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
961
962 switch (algorithm) {
963
964 case DNSSEC_DIGEST_SHA1:
965 return GCRY_MD_SHA1;
966
967 case DNSSEC_DIGEST_SHA256:
968 return GCRY_MD_SHA256;
969
af22c65b
LP
970 case DNSSEC_DIGEST_SHA384:
971 return GCRY_MD_SHA384;
972
a1972a91
LP
973 default:
974 return -EOPNOTSUPP;
975 }
976}
977
0c857028 978int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
2b442ac8 979 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
980 gcry_md_hd_t md = NULL;
981 size_t hash_size;
ca994e85 982 int md_algorithm, r;
2b442ac8 983 void *result;
2b442ac8
LP
984
985 assert(dnskey);
986 assert(ds);
987
988 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
989
990 if (dnskey->key->type != DNS_TYPE_DNSKEY)
991 return -EINVAL;
992 if (ds->key->type != DNS_TYPE_DS)
993 return -EINVAL;
994 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
995 return -EKEYREJECTED;
0c857028
LP
996 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
997 return -EKEYREJECTED;
2b442ac8
LP
998 if (dnskey->dnskey.protocol != 3)
999 return -EKEYREJECTED;
1000
2b442ac8
LP
1001 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
1002 return 0;
0c857028 1003 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
1004 return 0;
1005
0638401a
LP
1006 initialize_libgcrypt();
1007
ca994e85
LP
1008 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
1009 if (md_algorithm < 0)
1010 return md_algorithm;
2b442ac8 1011
ca994e85 1012 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 1013 assert(hash_size > 0);
2b442ac8 1014
a1972a91
LP
1015 if (ds->ds.digest_size != hash_size)
1016 return 0;
2b442ac8 1017
a1972a91
LP
1018 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1019 if (r < 0)
1020 return r;
2b442ac8 1021
ca994e85 1022 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1023 if (!md)
1024 return -EIO;
1025
2b442ac8 1026 gcry_md_write(md, owner_name, r);
0c857028
LP
1027 if (mask_revoke)
1028 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1029 else
1030 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1031 md_add_uint8(md, dnskey->dnskey.protocol);
1032 md_add_uint8(md, dnskey->dnskey.algorithm);
1033 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1034
1035 result = gcry_md_read(md, 0);
1036 if (!result) {
1037 r = -EIO;
1038 goto finish;
1039 }
1040
1041 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1042
1043finish:
1044 gcry_md_close(md);
1045 return r;
1046}
24710c48 1047
547973de
LP
1048int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1049 DnsResourceRecord *ds;
105e1512 1050 DnsAnswerFlags flags;
547973de
LP
1051 int r;
1052
1053 assert(dnskey);
1054
1055 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1056 return 0;
1057
105e1512
LP
1058 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1059
1060 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1061 continue;
547973de
LP
1062
1063 if (ds->key->type != DNS_TYPE_DS)
1064 continue;
1065
d1c4ee32
LP
1066 if (ds->key->class != dnskey->key->class)
1067 continue;
1068
1069 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1070 if (r < 0)
1071 return r;
1072 if (r == 0)
1073 continue;
1074
0c857028
LP
1075 r = dnssec_verify_dnskey(dnskey, ds, false);
1076 if (r == -EKEYREJECTED)
1077 return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
547973de
LP
1078 if (r < 0)
1079 return r;
1080 if (r > 0)
1081 return 1;
1082 }
1083
1084 return 0;
1085}
1086
d15ad742
LP
1087static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1088
1089 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1090
1091 switch (algorithm) {
1092
1093 case NSEC3_ALGORITHM_SHA1:
1094 return GCRY_MD_SHA1;
1095
1096 default:
1097 return -EOPNOTSUPP;
1098 }
1099}
1100
1d3db294 1101int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
72667f08
LP
1102 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1103 gcry_md_hd_t md = NULL;
1104 size_t hash_size;
1105 int algorithm;
1106 void *result;
1107 unsigned k;
1108 int r;
1109
1110 assert(nsec3);
1111 assert(name);
1112 assert(ret);
1113
1114 if (nsec3->key->type != DNS_TYPE_NSEC3)
1115 return -EINVAL;
1116
1d3db294
LP
1117 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1118 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1119 return -EOPNOTSUPP;
1d3db294 1120 }
a8f158b9 1121
d15ad742 1122 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1123 if (algorithm < 0)
1124 return algorithm;
1125
1126 initialize_libgcrypt();
1127
1128 hash_size = gcry_md_get_algo_dlen(algorithm);
1129 assert(hash_size > 0);
1130
1131 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1132 return -EINVAL;
1133
1134 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1135 if (r < 0)
1136 return r;
1137
1138 gcry_md_open(&md, algorithm, 0);
1139 if (!md)
1140 return -EIO;
1141
1142 gcry_md_write(md, wire_format, r);
1143 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1144
1145 result = gcry_md_read(md, 0);
1146 if (!result) {
1147 r = -EIO;
1148 goto finish;
1149 }
1150
1151 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1152 uint8_t tmp[hash_size];
1153 memcpy(tmp, result, hash_size);
1154
1155 gcry_md_reset(md);
1156 gcry_md_write(md, tmp, hash_size);
1157 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1158
1159 result = gcry_md_read(md, 0);
1160 if (!result) {
1161 r = -EIO;
1162 goto finish;
1163 }
1164 }
1165
1166 memcpy(ret, result, hash_size);
1167 r = (int) hash_size;
1168
1169finish:
1170 gcry_md_close(md);
1171 return r;
1172}
1173
3f5ecaad 1174static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
db5b0e92
LP
1175 const char *a, *b;
1176 int r;
1177
1178 assert(rr);
1179
db5b0e92
LP
1180 if (rr->key->type != DNS_TYPE_NSEC3)
1181 return 0;
1182
1183 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1184 if (!IN_SET(rr->nsec3.flags, 0, 1))
1185 return 0;
1186
d15ad742
LP
1187 /* Ignore NSEC3 RRs whose algorithm we don't know */
1188 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1189 return 0;
a8f158b9
LP
1190 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1191 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1192 return 0;
d15ad742 1193
db5b0e92
LP
1194 if (!nsec3)
1195 return 1;
1196
1197 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1198
1199 if (nsec3 == rr) /* Shortcut */
1200 return 1;
1201
1202 if (rr->key->class != nsec3->key->class)
1203 return 0;
1204 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1205 return 0;
1206 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1207 return 0;
1208 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1209 return 0;
1210 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1211 return 0;
1212
1213 a = DNS_RESOURCE_KEY_NAME(rr->key);
1214 r = dns_name_parent(&a); /* strip off hash */
1215 if (r < 0)
1216 return r;
1217 if (r == 0)
1218 return 0;
1219
1220 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1221 r = dns_name_parent(&b); /* strip off hash */
1222 if (r < 0)
1223 return r;
1224 if (r == 0)
1225 return 0;
1226
1227 return dns_name_equal(a, b);
1228}
1229
cdbffec0
LP
1230static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
1231 _cleanup_free_ char *l = NULL;
1232 char *j;
1233
1234 assert(hashed);
1235 assert(hashed_size > 0);
1236 assert(zone);
1237 assert(ret);
1238
1239 l = base32hexmem(hashed, hashed_size, false);
1240 if (!l)
1241 return -ENOMEM;
1242
1243 j = strjoin(l, ".", zone, NULL);
1244 if (!j)
1245 return -ENOMEM;
1246
1247 *ret = j;
1248 return (int) hashed_size;
1249}
1250
1251static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
105e1512 1252 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1253 int hashed_size;
1254
1255 assert(nsec3);
1256 assert(domain);
1257 assert(zone);
1258 assert(ret);
1259
1260 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1261 if (hashed_size < 0)
1262 return hashed_size;
1263
cdbffec0 1264 return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
6f76ec5a
TG
1265}
1266
35ad41d3
TG
1267/* See RFC 5155, Section 8
1268 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1269 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1270 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1271 * matches the wildcard domain.
1272 *
1273 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1274 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1275 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1276 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1277static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
35ad41d3 1278 _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
d1511b33 1279 const char *zone, *p, *pp = NULL;
7e35195f 1280 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
105e1512
LP
1281 DnsAnswerFlags flags;
1282 int hashed_size, r;
35ad41d3 1283 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1284
1285 assert(key);
1286 assert(result);
1287
d1511b33
TG
1288 /* First step, find the zone name and the NSEC3 parameters of the zone.
1289 * it is sufficient to look for the longest common suffix we find with
1290 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1291 * records from a given zone in a response must use the same
1292 * parameters. */
1293 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1294 for (;;) {
7e35195f 1295 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
3f5ecaad 1296 r = nsec3_is_good(zone_rr, NULL);
db5b0e92
LP
1297 if (r < 0)
1298 return r;
1299 if (r == 0)
13b78323
LP
1300 continue;
1301
7e35195f 1302 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
13b78323
LP
1303 if (r < 0)
1304 return r;
1305 if (r > 0)
d1511b33 1306 goto found_zone;
13b78323
LP
1307 }
1308
1309 /* Strip one label from the front */
d1511b33 1310 r = dns_name_parent(&zone);
13b78323
LP
1311 if (r < 0)
1312 return r;
1313 if (r == 0)
1314 break;
1315 }
1316
1317 *result = DNSSEC_NSEC_NO_RR;
1318 return 0;
1319
d1511b33 1320found_zone:
13b78323 1321 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1322 p = DNS_RESOURCE_KEY_NAME(key);
1323 for (;;) {
6f76ec5a 1324 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1325
cdbffec0 1326 hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
db5b0e92
LP
1327 if (hashed_size == -EOPNOTSUPP) {
1328 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1329 return 0;
1330 }
1331 if (hashed_size < 0)
1332 return hashed_size;
72667f08 1333
d1511b33 1334 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1335
3f5ecaad 1336 r = nsec3_is_good(enclosure_rr, zone_rr);
72667f08
LP
1337 if (r < 0)
1338 return r;
105e1512
LP
1339 if (r == 0)
1340 continue;
1341
d1511b33 1342 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1343 continue;
105e1512 1344
d1511b33 1345 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1346 if (r < 0)
1347 return r;
ed29bfdc
LP
1348 if (r > 0) {
1349 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1350 goto found_closest_encloser;
ed29bfdc 1351 }
105e1512
LP
1352 }
1353
1354 /* We didn't find the closest encloser with this name,
1355 * but let's remember this domain name, it might be
1356 * the next closer name */
1357
1358 pp = p;
1359
1360 /* Strip one label from the front */
1361 r = dns_name_parent(&p);
1362 if (r < 0)
1363 return r;
1364 if (r == 0)
72667f08 1365 break;
105e1512 1366 }
72667f08 1367
105e1512
LP
1368 *result = DNSSEC_NSEC_NO_RR;
1369 return 0;
72667f08 1370
13b78323 1371found_closest_encloser:
105e1512 1372 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1373
105e1512 1374 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1375 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1376 return -EBADMSG;
72667f08 1377
105e1512
LP
1378 /* Ensure that this data is from the delegated domain
1379 * (i.e. originates from the "lower" DNS server), and isn't
1380 * just glue records (i.e. doesn't originate from the "upper"
1381 * DNS server). */
d1511b33
TG
1382 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1383 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1384 return -EBADMSG;
72667f08 1385
105e1512
LP
1386 if (!pp) {
1387 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1388 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1389 *result = DNSSEC_NSEC_FOUND;
1390 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1391 *result = DNSSEC_NSEC_CNAME;
1392 else
1393 *result = DNSSEC_NSEC_NODATA;
1394
d3760be0
LP
1395 if (authenticated)
1396 *authenticated = a;
1397 if (ttl)
1398 *ttl = enclosure_rr->ttl;
146035b3 1399
105e1512
LP
1400 return 0;
1401 }
72667f08 1402
35ad41d3
TG
1403 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1404
1405 wildcard = strappend("*.", p);
1406 if (!wildcard)
1407 return -ENOMEM;
1408
cdbffec0 1409 r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1410 if (r < 0)
1411 return r;
1412 if (r != hashed_size)
1413 return -EBADMSG;
72667f08 1414
cdbffec0 1415 r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1416 if (r < 0)
1417 return r;
1418 if (r != hashed_size)
1419 return -EBADMSG;
72667f08 1420
105e1512 1421 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
cdbffec0 1422 _cleanup_free_ char *next_hashed_domain = NULL;
105e1512 1423
3f5ecaad 1424 r = nsec3_is_good(rr, zone_rr);
105e1512
LP
1425 if (r < 0)
1426 return r;
1427 if (r == 0)
1428 continue;
1429
cdbffec0
LP
1430 r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
1431 if (r < 0)
1432 return r;
105e1512
LP
1433
1434 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1435 if (r < 0)
1436 return r;
1437 if (r > 0) {
1438 if (rr->nsec3.flags & 1)
35ad41d3 1439 optout = true;
105e1512 1440
35ad41d3
TG
1441 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1442
1443 no_closer = true;
1444 }
1445
1446 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1447 if (r < 0)
1448 return r;
1449 if (r > 0) {
1450 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1451
1452 wildcard_rr = rr;
1453 }
1454
1455 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1456 if (r < 0)
1457 return r;
1458 if (r > 0) {
1459 if (rr->nsec3.flags & 1)
1460 /* This only makes sense if we have a wildcard delegation, which is
1461 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1462 * this not happening, so hence cannot simply conclude NXDOMAIN as
1463 * we would wish */
1464 optout = true;
1465
1466 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1467
1468 no_wildcard = true;
105e1512
LP
1469 }
1470 }
1471
35ad41d3
TG
1472 if (wildcard_rr && no_wildcard)
1473 return -EBADMSG;
1474
1475 if (!no_closer) {
1476 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1477 return 0;
1478 }
1479
1480 if (wildcard_rr) {
1481 /* A wildcard exists that matches our query. */
1482 if (optout)
1483 /* This is not specified in any RFC to the best of my knowledge, but
1484 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1485 * it means that we cannot prove that the source of synthesis is
1486 * correct, as there may be a closer match. */
1487 *result = DNSSEC_NSEC_OPTOUT;
1488 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1489 *result = DNSSEC_NSEC_FOUND;
1490 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1491 *result = DNSSEC_NSEC_CNAME;
1492 else
1493 *result = DNSSEC_NSEC_NODATA;
1494 } else {
1495 if (optout)
1496 /* The RFC only specifies that we have to care for optout for NODATA for
1497 * DS records. However, children of an insecure opt-out delegation should
1498 * also be considered opt-out, rather than verified NXDOMAIN.
1499 * Note that we do not require a proof of wildcard non-existence if the
1500 * next closer domain is covered by an opt-out, as that would not provide
1501 * any additional information. */
1502 *result = DNSSEC_NSEC_OPTOUT;
1503 else if (no_wildcard)
1504 *result = DNSSEC_NSEC_NXDOMAIN;
1505 else {
1506 *result = DNSSEC_NSEC_NO_RR;
1507
1508 return 0;
1509 }
1510 }
1511
d3760be0
LP
1512 if (authenticated)
1513 *authenticated = a;
1514
1515 if (ttl)
1516 *ttl = enclosure_rr->ttl;
35ad41d3 1517
105e1512
LP
1518 return 0;
1519}
1520
0c7bff0a 1521int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
105e1512
LP
1522 DnsResourceRecord *rr;
1523 bool have_nsec3 = false;
1524 DnsAnswerFlags flags;
1525 int r;
1526
1527 assert(key);
1528 assert(result);
1529
1530 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1531
1532 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1533
1534 if (rr->key->class != key->class)
1535 continue;
1536
105e1512
LP
1537 switch (rr->key->type) {
1538
1539 case DNS_TYPE_NSEC:
1540
1541 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1542 if (r < 0)
1543 return r;
1544 if (r > 0) {
146035b3
TG
1545 if (bitmap_isset(rr->nsec.types, key->type))
1546 *result = DNSSEC_NSEC_FOUND;
1547 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1548 *result = DNSSEC_NSEC_CNAME;
1549 else
1550 *result = DNSSEC_NSEC_NODATA;
d3760be0
LP
1551
1552 if (authenticated)
1553 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1554 if (ttl)
1555 *ttl = rr->ttl;
1556
72667f08
LP
1557 return 0;
1558 }
1559
105e1512
LP
1560 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1561 if (r < 0)
1562 return r;
1563 if (r > 0) {
1564 *result = DNSSEC_NSEC_NXDOMAIN;
d3760be0
LP
1565
1566 if (authenticated)
1567 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1568 if (ttl)
1569 *ttl = rr->ttl;
1570
105e1512
LP
1571 return 0;
1572 }
72667f08 1573 break;
72667f08 1574
105e1512
LP
1575 case DNS_TYPE_NSEC3:
1576 have_nsec3 = true;
72667f08
LP
1577 break;
1578 }
1579 }
1580
105e1512
LP
1581 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1582 if (have_nsec3)
d3760be0 1583 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 1584
72667f08
LP
1585 /* No approproate NSEC RR found, report this. */
1586 *result = DNSSEC_NSEC_NO_RR;
1587 return 0;
1588}
1589
0c7bff0a
LP
1590int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated) {
1591 DnsResourceRecord *rr;
1592 DnsAnswerFlags flags;
1593 int r;
1594
1595 assert(name);
1596 assert(zone);
1597
1598 /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
1599 * 'zone'. The 'zone' must be a suffix of the 'name'. */
1600
1601 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1602 bool found = false;
1603
1604 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
1605 if (r < 0)
1606 return r;
1607 if (r == 0)
1608 continue;
1609
1610 switch (rr->key->type) {
1611
1612 case DNS_TYPE_NSEC:
1613 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
1614 if (r < 0)
1615 return r;
1616
1617 found = r > 0;
1618 break;
1619
1620 case DNS_TYPE_NSEC3: {
1621 _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
1622
1623 r = nsec3_is_good(rr, NULL);
1624 if (r < 0)
1625 return r;
1626 if (r == 0)
1627 break;
1628
1629 /* Format the domain we are testing with the NSEC3 RR's hash function */
1630 r = nsec3_hashed_domain_make(
1631 rr,
1632 name,
1633 zone,
1634 &hashed_domain);
1635 if (r < 0)
1636 return r;
1637 if ((size_t) r != rr->nsec3.next_hashed_name_size)
1638 break;
1639
1640 /* Format the NSEC3's next hashed name as proper domain name */
1641 r = nsec3_hashed_domain_format(
1642 rr->nsec3.next_hashed_name,
1643 rr->nsec3.next_hashed_name_size,
1644 zone,
1645 &next_hashed_domain);
1646 if (r < 0)
1647 return r;
1648
1649 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
1650 if (r < 0)
1651 return r;
1652
1653 found = r > 0;
1654 break;
1655 }
1656
1657 default:
1658 continue;
1659 }
1660
1661 if (found) {
1662 if (authenticated)
1663 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1664 return 1;
1665 }
1666 }
1667
1668 return 0;
1669}
1670
547973de
LP
1671static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1672 [DNSSEC_VALIDATED] = "validated",
0c7bff0a 1673 [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
547973de 1674 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1675 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1676 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1677 [DNSSEC_NO_SIGNATURE] = "no-signature",
1678 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1679 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1680 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1681 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1682 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1683};
1684DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);