]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
tree-wide: remove Lennart's copyright lines
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2b442ac8 2
cb9eeb06
MCO
3#include <stdio_ext.h>
4
349cc4a5 5#if HAVE_GCRYPT
2b442ac8 6#include <gcrypt.h>
47091522 7#endif
2b442ac8
LP
8
9#include "alloc-util.h"
10#include "dns-domain.h"
cb9eeb06
MCO
11#include "fd-util.h"
12#include "fileio.h"
91e023d8 13#include "gcrypt-util.h"
72667f08 14#include "hexdecoct.h"
2b442ac8
LP
15#include "resolved-dns-dnssec.h"
16#include "resolved-dns-packet.h"
24710c48 17#include "string-table.h"
2b442ac8 18
2b442ac8
LP
19#define VERIFY_RRS_MAX 256
20#define MAX_KEY_SIZE (32*1024)
21
896c5672
LP
22/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
23#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
24
3a33c81b
LP
25/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
26#define NSEC3_ITERATIONS_MAX 2500
a8f158b9 27
2b442ac8
LP
28/*
29 * The DNSSEC Chain of trust:
30 *
31 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
32 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
33 * DS RRs are protected like normal RRs
34 *
35 * Example chain:
36 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
37 */
38
0c857028 39uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
2b442ac8 40 const uint8_t *p;
0c857028 41 uint32_t sum, f;
2b442ac8
LP
42 size_t i;
43
44 /* The algorithm from RFC 4034, Appendix B. */
45
46 assert(dnskey);
47 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
48
0c857028
LP
49 f = (uint32_t) dnskey->dnskey.flags;
50
51 if (mask_revoke)
52 f &= ~DNSKEY_FLAG_REVOKE;
53
54 sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
2b442ac8
LP
55
56 p = dnskey->dnskey.key;
57
58 for (i = 0; i < dnskey->dnskey.key_size; i++)
59 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
60
61 sum += (sum >> 16) & UINT32_C(0xFFFF);
62
63 return sum & UINT32_C(0xFFFF);
64}
65
dbf0b8a2
MO
66int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
67 size_t c = 0;
68 int r;
69
70 /* Converts the specified hostname into DNSSEC canonicalized
71 * form. */
72
73 if (buffer_max < 2)
74 return -ENOBUFS;
75
76 for (;;) {
77 r = dns_label_unescape(&n, buffer, buffer_max);
78 if (r < 0)
79 return r;
80 if (r == 0)
81 break;
82
83 if (buffer_max < (size_t) r + 2)
84 return -ENOBUFS;
85
86 /* The DNSSEC canonical form is not clear on what to
87 * do with dots appearing in labels, the way DNS-SD
88 * does it. Refuse it for now. */
89
90 if (memchr(buffer, '.', r))
91 return -EINVAL;
92
93 ascii_strlower_n(buffer, (size_t) r);
94 buffer[r] = '.';
95
96 buffer += r + 1;
97 c += r + 1;
98
99 buffer_max -= r + 1;
100 }
101
102 if (c <= 0) {
103 /* Not even a single label: this is the root domain name */
104
105 assert(buffer_max > 2);
106 buffer[0] = '.';
107 buffer[1] = 0;
108
109 return 1;
110 }
111
112 return (int) c;
113}
114
349cc4a5 115#if HAVE_GCRYPT
47091522 116
2b442ac8
LP
117static int rr_compare(const void *a, const void *b) {
118 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
119 size_t m;
120 int r;
121
122 /* Let's order the RRs according to RFC 4034, Section 6.3 */
123
124 assert(x);
125 assert(*x);
126 assert((*x)->wire_format);
127 assert(y);
128 assert(*y);
129 assert((*y)->wire_format);
130
85aeaccc 131 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
2b442ac8 132
85aeaccc 133 r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
2b442ac8
LP
134 if (r != 0)
135 return r;
136
85aeaccc 137 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8 138 return -1;
85aeaccc 139 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8
LP
140 return 1;
141
142 return 0;
143}
144
ea3a892f 145static int dnssec_rsa_verify_raw(
2b442ac8
LP
146 const char *hash_algorithm,
147 const void *signature, size_t signature_size,
148 const void *data, size_t data_size,
149 const void *exponent, size_t exponent_size,
150 const void *modulus, size_t modulus_size) {
151
152 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
153 gcry_mpi_t n = NULL, e = NULL, s = NULL;
154 gcry_error_t ge;
155 int r;
156
157 assert(hash_algorithm);
158
159 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
160 if (ge != 0) {
161 r = -EIO;
162 goto finish;
163 }
164
165 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
166 if (ge != 0) {
167 r = -EIO;
168 goto finish;
169 }
170
171 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
172 if (ge != 0) {
173 r = -EIO;
174 goto finish;
175 }
176
177 ge = gcry_sexp_build(&signature_sexp,
178 NULL,
179 "(sig-val (rsa (s %m)))",
180 s);
181
182 if (ge != 0) {
183 r = -EIO;
184 goto finish;
185 }
186
187 ge = gcry_sexp_build(&data_sexp,
188 NULL,
189 "(data (flags pkcs1) (hash %s %b))",
190 hash_algorithm,
191 (int) data_size,
192 data);
193 if (ge != 0) {
194 r = -EIO;
195 goto finish;
196 }
197
198 ge = gcry_sexp_build(&public_key_sexp,
199 NULL,
200 "(public-key (rsa (n %m) (e %m)))",
201 n,
202 e);
203 if (ge != 0) {
204 r = -EIO;
205 goto finish;
206 }
207
208 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
d12bf2bd 209 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 210 r = 0;
d12bf2bd
LP
211 else if (ge != 0) {
212 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
2b442ac8 213 r = -EIO;
d12bf2bd 214 } else
2b442ac8
LP
215 r = 1;
216
217finish:
218 if (e)
219 gcry_mpi_release(e);
220 if (n)
221 gcry_mpi_release(n);
222 if (s)
223 gcry_mpi_release(s);
224
225 if (public_key_sexp)
226 gcry_sexp_release(public_key_sexp);
227 if (signature_sexp)
228 gcry_sexp_release(signature_sexp);
229 if (data_sexp)
230 gcry_sexp_release(data_sexp);
231
232 return r;
233}
234
ea3a892f
LP
235static int dnssec_rsa_verify(
236 const char *hash_algorithm,
237 const void *hash, size_t hash_size,
238 DnsResourceRecord *rrsig,
239 DnsResourceRecord *dnskey) {
240
241 size_t exponent_size, modulus_size;
242 void *exponent, *modulus;
243
244 assert(hash_algorithm);
245 assert(hash);
246 assert(hash_size > 0);
247 assert(rrsig);
248 assert(dnskey);
249
250 if (*(uint8_t*) dnskey->dnskey.key == 0) {
251 /* exponent is > 255 bytes long */
252
253 exponent = (uint8_t*) dnskey->dnskey.key + 3;
254 exponent_size =
ac04adbe
TG
255 ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
256 ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
ea3a892f
LP
257
258 if (exponent_size < 256)
259 return -EINVAL;
260
261 if (3 + exponent_size >= dnskey->dnskey.key_size)
262 return -EINVAL;
263
264 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
265 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
266
267 } else {
268 /* exponent is <= 255 bytes long */
269
270 exponent = (uint8_t*) dnskey->dnskey.key + 1;
271 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
272
273 if (exponent_size <= 0)
274 return -EINVAL;
275
276 if (1 + exponent_size >= dnskey->dnskey.key_size)
277 return -EINVAL;
278
279 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
280 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
281 }
282
283 return dnssec_rsa_verify_raw(
284 hash_algorithm,
285 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
286 hash, hash_size,
287 exponent, exponent_size,
288 modulus, modulus_size);
289}
290
e0240c64
LP
291static int dnssec_ecdsa_verify_raw(
292 const char *hash_algorithm,
293 const char *curve,
294 const void *signature_r, size_t signature_r_size,
295 const void *signature_s, size_t signature_s_size,
296 const void *data, size_t data_size,
297 const void *key, size_t key_size) {
298
299 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
300 gcry_mpi_t q = NULL, r = NULL, s = NULL;
301 gcry_error_t ge;
302 int k;
303
304 assert(hash_algorithm);
305
306 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
307 if (ge != 0) {
308 k = -EIO;
309 goto finish;
310 }
311
312 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
313 if (ge != 0) {
314 k = -EIO;
315 goto finish;
316 }
317
318 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
319 if (ge != 0) {
320 k = -EIO;
321 goto finish;
322 }
323
324 ge = gcry_sexp_build(&signature_sexp,
325 NULL,
326 "(sig-val (ecdsa (r %m) (s %m)))",
327 r,
328 s);
329 if (ge != 0) {
330 k = -EIO;
331 goto finish;
332 }
333
334 ge = gcry_sexp_build(&data_sexp,
335 NULL,
336 "(data (flags rfc6979) (hash %s %b))",
337 hash_algorithm,
338 (int) data_size,
339 data);
340 if (ge != 0) {
341 k = -EIO;
342 goto finish;
343 }
344
345 ge = gcry_sexp_build(&public_key_sexp,
346 NULL,
347 "(public-key (ecc (curve %s) (q %m)))",
348 curve,
349 q);
350 if (ge != 0) {
351 k = -EIO;
352 goto finish;
353 }
354
355 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
356 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
357 k = 0;
358 else if (ge != 0) {
359 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
360 k = -EIO;
361 } else
362 k = 1;
363finish:
364 if (r)
365 gcry_mpi_release(r);
366 if (s)
367 gcry_mpi_release(s);
368 if (q)
369 gcry_mpi_release(q);
370
371 if (public_key_sexp)
372 gcry_sexp_release(public_key_sexp);
373 if (signature_sexp)
374 gcry_sexp_release(signature_sexp);
375 if (data_sexp)
376 gcry_sexp_release(data_sexp);
377
378 return k;
379}
380
381static int dnssec_ecdsa_verify(
382 const char *hash_algorithm,
383 int algorithm,
384 const void *hash, size_t hash_size,
385 DnsResourceRecord *rrsig,
386 DnsResourceRecord *dnskey) {
387
388 const char *curve;
389 size_t key_size;
390 uint8_t *q;
391
392 assert(hash);
393 assert(hash_size);
394 assert(rrsig);
395 assert(dnskey);
396
397 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
398 key_size = 32;
399 curve = "NIST P-256";
400 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
401 key_size = 48;
402 curve = "NIST P-384";
403 } else
404 return -EOPNOTSUPP;
405
406 if (dnskey->dnskey.key_size != key_size * 2)
407 return -EINVAL;
408
409 if (rrsig->rrsig.signature_size != key_size * 2)
410 return -EINVAL;
411
412 q = alloca(key_size*2 + 1);
413 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
414 memcpy(q+1, dnskey->dnskey.key, key_size*2);
415
416 return dnssec_ecdsa_verify_raw(
417 hash_algorithm,
418 curve,
419 rrsig->rrsig.signature, key_size,
420 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
421 hash, hash_size,
422 q, key_size*2+1);
423}
424
cb9eeb06
MCO
425#if GCRYPT_VERSION_NUMBER >= 0x010600
426static int dnssec_eddsa_verify_raw(
427 const char *curve,
428 const void *signature_r, size_t signature_r_size,
429 const void *signature_s, size_t signature_s_size,
430 const void *data, size_t data_size,
431 const void *key, size_t key_size) {
432
433 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
434 gcry_error_t ge;
435 int k;
436
437 ge = gcry_sexp_build(&signature_sexp,
438 NULL,
439 "(sig-val (eddsa (r %b) (s %b)))",
440 (int) signature_r_size,
441 signature_r,
442 (int) signature_s_size,
443 signature_s);
444 if (ge != 0) {
445 k = -EIO;
446 goto finish;
447 }
448
449 ge = gcry_sexp_build(&data_sexp,
450 NULL,
451 "(data (flags eddsa) (hash-algo sha512) (value %b))",
452 (int) data_size,
453 data);
454 if (ge != 0) {
455 k = -EIO;
456 goto finish;
457 }
458
459 ge = gcry_sexp_build(&public_key_sexp,
460 NULL,
461 "(public-key (ecc (curve %s) (flags eddsa) (q %b)))",
462 curve,
463 (int) key_size,
464 key);
465 if (ge != 0) {
466 k = -EIO;
467 goto finish;
468 }
469
470 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
471 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
472 k = 0;
473 else if (ge != 0) {
474 log_debug("EdDSA signature check failed: %s", gpg_strerror(ge));
475 k = -EIO;
476 } else
477 k = 1;
478finish:
479 if (public_key_sexp)
480 gcry_sexp_release(public_key_sexp);
481 if (signature_sexp)
482 gcry_sexp_release(signature_sexp);
483 if (data_sexp)
484 gcry_sexp_release(data_sexp);
485
486 return k;
487}
488
489static int dnssec_eddsa_verify(
490 int algorithm,
491 const void *data, size_t data_size,
492 DnsResourceRecord *rrsig,
493 DnsResourceRecord *dnskey) {
494 const char *curve;
495 size_t key_size;
496
497 if (algorithm == DNSSEC_ALGORITHM_ED25519) {
498 curve = "Ed25519";
499 key_size = 32;
500 } else
501 return -EOPNOTSUPP;
502
503 if (dnskey->dnskey.key_size != key_size)
504 return -EINVAL;
505
506 if (rrsig->rrsig.signature_size != key_size * 2)
507 return -EINVAL;
508
509 return dnssec_eddsa_verify_raw(
510 curve,
511 rrsig->rrsig.signature, key_size,
512 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
513 data, data_size,
514 dnskey->dnskey.key, key_size);
515}
516#endif
517
2b442ac8
LP
518static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
519 gcry_md_write(md, &v, sizeof(v));
520}
521
522static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
523 v = htobe16(v);
524 gcry_md_write(md, &v, sizeof(v));
525}
526
cb9eeb06
MCO
527static void fwrite_uint8(FILE *fp, uint8_t v) {
528 fwrite(&v, sizeof(v), 1, fp);
529}
530
531static void fwrite_uint16(FILE *fp, uint16_t v) {
532 v = htobe16(v);
533 fwrite(&v, sizeof(v), 1, fp);
534}
535
536static void fwrite_uint32(FILE *fp, uint32_t v) {
2b442ac8 537 v = htobe32(v);
cb9eeb06 538 fwrite(&v, sizeof(v), 1, fp);
2b442ac8
LP
539}
540
97c67192
LP
541static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
542 int n_key_labels, n_signer_labels;
543 const char *name;
544 int r;
545
546 /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and
547 * .n_skip_labels_signer fields so that we can use them later on. */
548
549 assert(rrsig);
550 assert(rrsig->key->type == DNS_TYPE_RRSIG);
551
552 /* Check if this RRSIG RR is already prepared */
553 if (rrsig->n_skip_labels_source != (unsigned) -1)
554 return 0;
555
556 if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
557 return -EINVAL;
558
1c02e7ba 559 name = dns_resource_key_name(rrsig->key);
97c67192
LP
560
561 n_key_labels = dns_name_count_labels(name);
562 if (n_key_labels < 0)
563 return n_key_labels;
564 if (rrsig->rrsig.labels > n_key_labels)
565 return -EINVAL;
566
567 n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
568 if (n_signer_labels < 0)
569 return n_signer_labels;
570 if (n_signer_labels > rrsig->rrsig.labels)
571 return -EINVAL;
572
573 r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
574 if (r < 0)
575 return r;
576 if (r == 0)
577 return -EINVAL;
578
579 /* Check if the signer is really a suffix of us */
580 r = dns_name_equal(name, rrsig->rrsig.signer);
581 if (r < 0)
582 return r;
583 if (r == 0)
584 return -EINVAL;
585
586 rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
587 rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
588
589 return 0;
590}
591
2a326321
LP
592static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
593 usec_t expiration, inception, skew;
594
595 assert(rrsig);
596 assert(rrsig->key->type == DNS_TYPE_RRSIG);
597
598 if (realtime == USEC_INFINITY)
599 realtime = now(CLOCK_REALTIME);
600
601 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
602 inception = rrsig->rrsig.inception * USEC_PER_SEC;
603
5ae5cd40 604 /* Consider inverted validity intervals as expired */
2a326321 605 if (inception > expiration)
5ae5cd40 606 return true;
2a326321 607
896c5672
LP
608 /* Permit a certain amount of clock skew of 10% of the valid
609 * time range. This takes inspiration from unbound's
610 * resolver. */
2a326321 611 skew = (expiration - inception) / 10;
896c5672
LP
612 if (skew > SKEW_MAX)
613 skew = SKEW_MAX;
2a326321
LP
614
615 if (inception < skew)
616 inception = 0;
617 else
618 inception -= skew;
619
620 if (expiration + skew < expiration)
621 expiration = USEC_INFINITY;
622 else
623 expiration += skew;
624
625 return realtime < inception || realtime > expiration;
626}
627
ca994e85 628static int algorithm_to_gcrypt_md(uint8_t algorithm) {
fbf1a66d 629
6af47493
LP
630 /* Translates a DNSSEC signature algorithm into a gcrypt
631 * digest identifier.
632 *
633 * Note that we implement all algorithms listed as "Must
634 * implement" and "Recommended to Implement" in RFC6944. We
635 * don't implement any algorithms that are listed as
636 * "Optional" or "Must Not Implement". Specifically, we do not
637 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
638 * GOST-ECC. */
fbf1a66d
LP
639
640 switch (algorithm) {
641
642 case DNSSEC_ALGORITHM_RSASHA1:
643 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
644 return GCRY_MD_SHA1;
645
646 case DNSSEC_ALGORITHM_RSASHA256:
e0240c64 647 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
fbf1a66d
LP
648 return GCRY_MD_SHA256;
649
e0240c64
LP
650 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
651 return GCRY_MD_SHA384;
652
fbf1a66d
LP
653 case DNSSEC_ALGORITHM_RSASHA512:
654 return GCRY_MD_SHA512;
655
656 default:
657 return -EOPNOTSUPP;
658 }
659}
660
97c67192
LP
661static void dnssec_fix_rrset_ttl(
662 DnsResourceRecord *list[],
663 unsigned n,
664 DnsResourceRecord *rrsig,
665 usec_t realtime) {
666
667 unsigned k;
668
669 assert(list);
670 assert(n > 0);
671 assert(rrsig);
672
673 for (k = 0; k < n; k++) {
674 DnsResourceRecord *rr = list[k];
675
676 /* Pick the TTL as the minimum of the RR's TTL, the
677 * RR's original TTL according to the RRSIG and the
678 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
679 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
680 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
681
682 /* Copy over information about the signer and wildcard source of synthesis */
683 rr->n_skip_labels_source = rrsig->n_skip_labels_source;
684 rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
685 }
686
687 rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
688}
689
2a326321
LP
690int dnssec_verify_rrset(
691 DnsAnswer *a,
0c857028 692 const DnsResourceKey *key,
2a326321
LP
693 DnsResourceRecord *rrsig,
694 DnsResourceRecord *dnskey,
547973de
LP
695 usec_t realtime,
696 DnssecResult *result) {
2a326321 697
5e55cde9 698 uint8_t wire_format_name[DNS_WIRE_FORMAT_HOSTNAME_MAX];
2b442ac8 699 DnsResourceRecord **list, *rr;
588c53d0 700 const char *source, *name;
8530efc1 701 _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
ca994e85 702 int r, md_algorithm;
2b442ac8 703 size_t k, n = 0;
cb9eeb06
MCO
704 size_t sig_size = 0;
705 _cleanup_free_ char *sig_data = NULL;
706 _cleanup_fclose_ FILE *f = NULL;
588c53d0
LP
707 size_t hash_size;
708 void *hash;
7715f91d 709 bool wildcard;
2b442ac8
LP
710
711 assert(key);
712 assert(rrsig);
713 assert(dnskey);
547973de 714 assert(result);
2a326321
LP
715 assert(rrsig->key->type == DNS_TYPE_RRSIG);
716 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8 717
c629ff58 718 /* Verifies that the RRSet matches the specified "key" in "a",
2b442ac8 719 * using the signature "rrsig" and the key "dnskey". It's
c629ff58 720 * assumed that RRSIG and DNSKEY match. */
2b442ac8 721
97c67192
LP
722 r = dnssec_rrsig_prepare(rrsig);
723 if (r == -EINVAL) {
724 *result = DNSSEC_INVALID;
725 return r;
726 }
727 if (r < 0)
728 return r;
729
2a326321
LP
730 r = dnssec_rrsig_expired(rrsig, realtime);
731 if (r < 0)
732 return r;
547973de
LP
733 if (r > 0) {
734 *result = DNSSEC_SIGNATURE_EXPIRED;
735 return 0;
736 }
2a326321 737
1c02e7ba 738 name = dns_resource_key_name(key);
588c53d0
LP
739
740 /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
741 if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
742 r = dns_name_equal(rrsig->rrsig.signer, name);
743 if (r < 0)
744 return r;
745 if (r == 0) {
746 *result = DNSSEC_INVALID;
747 return 0;
748 }
749 }
750
751 /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
752 if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
753 r = dns_name_equal(rrsig->rrsig.signer, name);
754 if (r < 0)
755 return r;
756 if (r > 0) {
757 *result = DNSSEC_INVALID;
758 return 0;
759 }
760 }
761
7715f91d 762 /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
588c53d0 763 r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
7715f91d
LP
764 if (r < 0)
765 return r;
e8233bce
LP
766 if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
767 /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
768 *result = DNSSEC_INVALID;
769 return 0;
770 }
7160eb1b
LP
771 if (r == 1) {
772 /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
773 * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
588c53d0 774 r = dns_name_startswith(name, "*");
7160eb1b
LP
775 if (r < 0)
776 return r;
777 if (r > 0)
588c53d0 778 source = name;
7160eb1b
LP
779
780 wildcard = r == 0;
781 } else
782 wildcard = r > 0;
7715f91d 783
2b442ac8 784 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
0f23174c 785 list = newa(DnsResourceRecord *, dns_answer_size(a));
2b442ac8
LP
786
787 DNS_ANSWER_FOREACH(rr, a) {
788 r = dns_resource_key_equal(key, rr->key);
789 if (r < 0)
790 return r;
791 if (r == 0)
792 continue;
793
794 /* We need the wire format for ordering, and digest calculation */
795 r = dns_resource_record_to_wire_format(rr, true);
796 if (r < 0)
797 return r;
798
799 list[n++] = rr;
935a999f
TG
800
801 if (n > VERIFY_RRS_MAX)
802 return -E2BIG;
2b442ac8
LP
803 }
804
805 if (n <= 0)
806 return -ENODATA;
807
808 /* Bring the RRs into canonical order */
6c5e8fbf 809 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8 810
cb9eeb06
MCO
811 f = open_memstream(&sig_data, &sig_size);
812 if (!f)
813 return -ENOMEM;
814 __fsetlocking(f, FSETLOCKING_BYCALLER);
2b442ac8 815
cb9eeb06
MCO
816 fwrite_uint16(f, rrsig->rrsig.type_covered);
817 fwrite_uint8(f, rrsig->rrsig.algorithm);
818 fwrite_uint8(f, rrsig->rrsig.labels);
819 fwrite_uint32(f, rrsig->rrsig.original_ttl);
820 fwrite_uint32(f, rrsig->rrsig.expiration);
821 fwrite_uint32(f, rrsig->rrsig.inception);
822 fwrite_uint16(f, rrsig->rrsig.key_tag);
2b442ac8
LP
823
824 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
825 if (r < 0)
8530efc1 826 return r;
cb9eeb06 827 fwrite(wire_format_name, 1, r, f);
2b442ac8 828
7715f91d
LP
829 /* Convert the source of synthesis into wire format */
830 r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
831 if (r < 0)
8530efc1 832 return r;
7715f91d 833
2b442ac8
LP
834 for (k = 0; k < n; k++) {
835 size_t l;
7715f91d 836
2b442ac8
LP
837 rr = list[k];
838
7715f91d
LP
839 /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
840 if (wildcard)
cb9eeb06
MCO
841 fwrite((uint8_t[]) { 1, '*'}, sizeof(uint8_t), 2, f);
842 fwrite(wire_format_name, 1, r, f);
2b442ac8 843
cb9eeb06
MCO
844 fwrite_uint16(f, rr->key->type);
845 fwrite_uint16(f, rr->key->class);
846 fwrite_uint32(f, rrsig->rrsig.original_ttl);
2b442ac8 847
85aeaccc 848 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
2b442ac8
LP
849 assert(l <= 0xFFFF);
850
cb9eeb06
MCO
851 fwrite_uint16(f, (uint16_t) l);
852 fwrite(DNS_RESOURCE_RECORD_RDATA(rr), 1, l, f);
2b442ac8
LP
853 }
854
cb9eeb06
MCO
855 r = fflush_and_check(f);
856 if (r < 0)
857 return r;
858
859 initialize_libgcrypt(false);
860
861 switch (rrsig->rrsig.algorithm) {
862#if GCRYPT_VERSION_NUMBER >= 0x010600
863 case DNSSEC_ALGORITHM_ED25519:
864 break;
865#else
866 case DNSSEC_ALGORITHM_ED25519:
867#endif
868 case DNSSEC_ALGORITHM_ED448:
869 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
8530efc1 870 return 0;
cb9eeb06
MCO
871 default:
872 /* OK, the RRs are now in canonical order. Let's calculate the digest */
873 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
874 if (md_algorithm == -EOPNOTSUPP) {
875 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
8530efc1 876 return 0;
cb9eeb06 877 }
8530efc1
ZJS
878 if (md_algorithm < 0)
879 return md_algorithm;
cb9eeb06
MCO
880
881 gcry_md_open(&md, md_algorithm, 0);
8530efc1
ZJS
882 if (!md)
883 return -EIO;
cb9eeb06
MCO
884
885 hash_size = gcry_md_get_algo_dlen(md_algorithm);
886 assert(hash_size > 0);
887
888 gcry_md_write(md, sig_data, sig_size);
889
890 hash = gcry_md_read(md, 0);
8530efc1
ZJS
891 if (!hash)
892 return -EIO;
2b442ac8
LP
893 }
894
e0240c64
LP
895 switch (rrsig->rrsig.algorithm) {
896
897 case DNSSEC_ALGORITHM_RSASHA1:
898 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
899 case DNSSEC_ALGORITHM_RSASHA256:
900 case DNSSEC_ALGORITHM_RSASHA512:
901 r = dnssec_rsa_verify(
ca994e85 902 gcry_md_algo_name(md_algorithm),
e0240c64
LP
903 hash, hash_size,
904 rrsig,
905 dnskey);
906 break;
907
908 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
909 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
910 r = dnssec_ecdsa_verify(
ca994e85 911 gcry_md_algo_name(md_algorithm),
e0240c64
LP
912 rrsig->rrsig.algorithm,
913 hash, hash_size,
914 rrsig,
915 dnskey);
916 break;
cb9eeb06
MCO
917#if GCRYPT_VERSION_NUMBER >= 0x010600
918 case DNSSEC_ALGORITHM_ED25519:
919 r = dnssec_eddsa_verify(
920 rrsig->rrsig.algorithm,
921 sig_data, sig_size,
922 rrsig,
923 dnskey);
924 break;
925#endif
e0240c64 926 }
2b442ac8 927 if (r < 0)
8530efc1 928 return r;
2b442ac8 929
97c67192
LP
930 /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
931 if (r > 0)
932 dnssec_fix_rrset_ttl(list, n, rrsig, realtime);
933
934 if (r == 0)
0c7bff0a
LP
935 *result = DNSSEC_INVALID;
936 else if (wildcard)
937 *result = DNSSEC_VALIDATED_WILDCARD;
938 else
939 *result = DNSSEC_VALIDATED;
97c67192 940
8530efc1 941 return 0;
2b442ac8
LP
942}
943
0c857028 944int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
945
946 assert(rrsig);
947 assert(dnskey);
948
949 /* Checks if the specified DNSKEY RR matches the key used for
950 * the signature in the specified RRSIG RR */
951
952 if (rrsig->key->type != DNS_TYPE_RRSIG)
953 return -EINVAL;
954
955 if (dnskey->key->type != DNS_TYPE_DNSKEY)
956 return 0;
957 if (dnskey->key->class != rrsig->key->class)
958 return 0;
959 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
960 return 0;
0c857028 961 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 962 return 0;
2b442ac8
LP
963 if (dnskey->dnskey.protocol != 3)
964 return 0;
965 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
966 return 0;
967
0c857028 968 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
969 return 0;
970
1c02e7ba 971 return dns_name_equal(dns_resource_key_name(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
972}
973
105e1512 974int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
2b442ac8
LP
975 assert(key);
976 assert(rrsig);
977
978 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
979
980 if (rrsig->key->type != DNS_TYPE_RRSIG)
981 return 0;
982 if (rrsig->key->class != key->class)
983 return 0;
984 if (rrsig->rrsig.type_covered != key->type)
985 return 0;
986
1c02e7ba 987 return dns_name_equal(dns_resource_key_name(rrsig->key), dns_resource_key_name(key));
2b442ac8
LP
988}
989
2a326321
LP
990int dnssec_verify_rrset_search(
991 DnsAnswer *a,
0c857028 992 const DnsResourceKey *key,
2a326321 993 DnsAnswer *validated_dnskeys,
547973de 994 usec_t realtime,
0c7bff0a
LP
995 DnssecResult *result,
996 DnsResourceRecord **ret_rrsig) {
2a326321 997
203f1b35 998 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
999 DnsResourceRecord *rrsig;
1000 int r;
1001
1002 assert(key);
547973de 1003 assert(result);
2b442ac8 1004
105e1512 1005 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
1006
1007 if (!a || a->n_rrs <= 0)
1008 return -ENODATA;
1009
1010 /* Iterate through each RRSIG RR. */
1011 DNS_ANSWER_FOREACH(rrsig, a) {
1012 DnsResourceRecord *dnskey;
105e1512 1013 DnsAnswerFlags flags;
2b442ac8 1014
203f1b35 1015 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
1016 r = dnssec_key_match_rrsig(key, rrsig);
1017 if (r < 0)
1018 return r;
1019 if (r == 0)
1020 continue;
1021
1022 found_rrsig = true;
1023
547973de 1024 /* Look for a matching key */
105e1512 1025 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 1026 DnssecResult one_result;
2b442ac8 1027
105e1512
LP
1028 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1029 continue;
1030
203f1b35 1031 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
0c857028 1032 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
1033 if (r < 0)
1034 return r;
1035 if (r == 0)
1036 continue;
1037
2a326321
LP
1038 /* Take the time here, if it isn't set yet, so
1039 * that we do all validations with the same
1040 * time. */
1041 if (realtime == USEC_INFINITY)
1042 realtime = now(CLOCK_REALTIME);
1043
2b442ac8
LP
1044 /* Yay, we found a matching RRSIG with a matching
1045 * DNSKEY, awesome. Now let's verify all entries of
1046 * the RRSet against the RRSIG and DNSKEY
1047 * combination. */
1048
547973de 1049 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 1050 if (r < 0)
2b442ac8 1051 return r;
203f1b35
LP
1052
1053 switch (one_result) {
1054
1055 case DNSSEC_VALIDATED:
0c7bff0a 1056 case DNSSEC_VALIDATED_WILDCARD:
203f1b35 1057 /* Yay, the RR has been validated,
ee3d6aff 1058 * return immediately, but fix up the expiry */
0c7bff0a
LP
1059 if (ret_rrsig)
1060 *ret_rrsig = rrsig;
1061
1062 *result = one_result;
547973de 1063 return 0;
2b442ac8 1064
203f1b35
LP
1065 case DNSSEC_INVALID:
1066 /* If the signature is invalid, let's try another
1067 key and/or signature. After all they
1068 key_tags and stuff are not unique, and
1069 might be shared by multiple keys. */
1070 found_invalid = true;
1071 continue;
1072
1073 case DNSSEC_UNSUPPORTED_ALGORITHM:
1074 /* If the key algorithm is
1075 unsupported, try another
1076 RRSIG/DNSKEY pair, but remember we
1077 encountered this, so that we can
1078 return a proper error when we
1079 encounter nothing better. */
1080 found_unsupported_algorithm = true;
1081 continue;
1082
1083 case DNSSEC_SIGNATURE_EXPIRED:
1084 /* If the signature is expired, try
1085 another one, but remember it, so
1086 that we can return this */
1087 found_expired_rrsig = true;
1088 continue;
1089
1090 default:
1091 assert_not_reached("Unexpected DNSSEC validation result");
1092 }
2b442ac8
LP
1093 }
1094 }
1095
203f1b35
LP
1096 if (found_expired_rrsig)
1097 *result = DNSSEC_SIGNATURE_EXPIRED;
1098 else if (found_unsupported_algorithm)
1099 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
1100 else if (found_invalid)
547973de
LP
1101 *result = DNSSEC_INVALID;
1102 else if (found_rrsig)
1103 *result = DNSSEC_MISSING_KEY;
1104 else
1105 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 1106
0c7bff0a
LP
1107 if (ret_rrsig)
1108 *ret_rrsig = NULL;
1109
547973de 1110 return 0;
2b442ac8
LP
1111}
1112
105e1512
LP
1113int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
1114 DnsResourceRecord *rr;
1115 int r;
1116
1117 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
1118
1119 DNS_ANSWER_FOREACH(rr, a) {
1120 r = dnssec_key_match_rrsig(key, rr);
1121 if (r < 0)
1122 return r;
1123 if (r > 0)
1124 return 1;
1125 }
1126
1127 return 0;
1128}
1129
ca994e85 1130static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 1131
fbf1a66d 1132 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
1133
1134 switch (algorithm) {
1135
1136 case DNSSEC_DIGEST_SHA1:
1137 return GCRY_MD_SHA1;
1138
1139 case DNSSEC_DIGEST_SHA256:
1140 return GCRY_MD_SHA256;
1141
af22c65b
LP
1142 case DNSSEC_DIGEST_SHA384:
1143 return GCRY_MD_SHA384;
1144
a1972a91
LP
1145 default:
1146 return -EOPNOTSUPP;
1147 }
1148}
1149
96bb7673 1150int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
5e55cde9 1151 uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
15c53310 1152 _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
a1972a91 1153 size_t hash_size;
ca994e85 1154 int md_algorithm, r;
2b442ac8 1155 void *result;
2b442ac8
LP
1156
1157 assert(dnskey);
1158 assert(ds);
1159
1160 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
1161
1162 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1163 return -EINVAL;
1164 if (ds->key->type != DNS_TYPE_DS)
1165 return -EINVAL;
1166 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
1167 return -EKEYREJECTED;
0c857028
LP
1168 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
1169 return -EKEYREJECTED;
2b442ac8
LP
1170 if (dnskey->dnskey.protocol != 3)
1171 return -EKEYREJECTED;
1172
2b442ac8
LP
1173 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
1174 return 0;
0c857028 1175 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
1176 return 0;
1177
91e023d8 1178 initialize_libgcrypt(false);
0638401a 1179
ca994e85
LP
1180 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
1181 if (md_algorithm < 0)
1182 return md_algorithm;
2b442ac8 1183
ca994e85 1184 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 1185 assert(hash_size > 0);
2b442ac8 1186
a1972a91
LP
1187 if (ds->ds.digest_size != hash_size)
1188 return 0;
2b442ac8 1189
c910c520 1190 r = dns_name_to_wire_format(dns_resource_key_name(dnskey->key), wire_format, sizeof(wire_format), true);
a1972a91
LP
1191 if (r < 0)
1192 return r;
2b442ac8 1193
ca994e85 1194 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1195 if (!md)
1196 return -EIO;
1197
c910c520 1198 gcry_md_write(md, wire_format, r);
0c857028
LP
1199 if (mask_revoke)
1200 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1201 else
1202 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1203 md_add_uint8(md, dnskey->dnskey.protocol);
1204 md_add_uint8(md, dnskey->dnskey.algorithm);
1205 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1206
1207 result = gcry_md_read(md, 0);
15c53310
ZJS
1208 if (!result)
1209 return -EIO;
2b442ac8 1210
c910c520 1211 return memcmp(result, ds->ds.digest, ds->ds.digest_size) == 0;
2b442ac8 1212}
24710c48 1213
96bb7673 1214int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
547973de 1215 DnsResourceRecord *ds;
105e1512 1216 DnsAnswerFlags flags;
547973de
LP
1217 int r;
1218
1219 assert(dnskey);
1220
1221 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1222 return 0;
1223
105e1512
LP
1224 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1225
1226 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1227 continue;
547973de
LP
1228
1229 if (ds->key->type != DNS_TYPE_DS)
1230 continue;
d1c4ee32
LP
1231 if (ds->key->class != dnskey->key->class)
1232 continue;
1233
1c02e7ba 1234 r = dns_name_equal(dns_resource_key_name(dnskey->key), dns_resource_key_name(ds->key));
d1c4ee32
LP
1235 if (r < 0)
1236 return r;
1237 if (r == 0)
1238 continue;
1239
96bb7673 1240 r = dnssec_verify_dnskey_by_ds(dnskey, ds, false);
54b778e7
LP
1241 if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP))
1242 return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */
547973de
LP
1243 if (r < 0)
1244 return r;
1245 if (r > 0)
1246 return 1;
1247 }
1248
1249 return 0;
1250}
1251
d15ad742
LP
1252static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1253
1254 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1255
1256 switch (algorithm) {
1257
1258 case NSEC3_ALGORITHM_SHA1:
1259 return GCRY_MD_SHA1;
1260
1261 default:
1262 return -EOPNOTSUPP;
1263 }
1264}
1265
1d3db294 1266int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
5e55cde9 1267 uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
72667f08
LP
1268 gcry_md_hd_t md = NULL;
1269 size_t hash_size;
1270 int algorithm;
1271 void *result;
1272 unsigned k;
1273 int r;
1274
1275 assert(nsec3);
1276 assert(name);
1277 assert(ret);
1278
1279 if (nsec3->key->type != DNS_TYPE_NSEC3)
1280 return -EINVAL;
1281
1d3db294
LP
1282 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1283 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1284 return -EOPNOTSUPP;
1d3db294 1285 }
a8f158b9 1286
d15ad742 1287 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1288 if (algorithm < 0)
1289 return algorithm;
1290
91e023d8 1291 initialize_libgcrypt(false);
72667f08
LP
1292
1293 hash_size = gcry_md_get_algo_dlen(algorithm);
1294 assert(hash_size > 0);
1295
1296 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1297 return -EINVAL;
1298
1299 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1300 if (r < 0)
1301 return r;
1302
1303 gcry_md_open(&md, algorithm, 0);
1304 if (!md)
1305 return -EIO;
1306
1307 gcry_md_write(md, wire_format, r);
1308 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1309
1310 result = gcry_md_read(md, 0);
1311 if (!result) {
1312 r = -EIO;
1313 goto finish;
1314 }
1315
1316 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1317 uint8_t tmp[hash_size];
1318 memcpy(tmp, result, hash_size);
1319
1320 gcry_md_reset(md);
1321 gcry_md_write(md, tmp, hash_size);
1322 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1323
1324 result = gcry_md_read(md, 0);
1325 if (!result) {
1326 r = -EIO;
1327 goto finish;
1328 }
1329 }
1330
1331 memcpy(ret, result, hash_size);
1332 r = (int) hash_size;
1333
1334finish:
1335 gcry_md_close(md);
1336 return r;
1337}
1338
3f5ecaad 1339static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
db5b0e92
LP
1340 const char *a, *b;
1341 int r;
1342
1343 assert(rr);
1344
db5b0e92
LP
1345 if (rr->key->type != DNS_TYPE_NSEC3)
1346 return 0;
1347
1f133e0d 1348 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
db5b0e92
LP
1349 if (!IN_SET(rr->nsec3.flags, 0, 1))
1350 return 0;
1351
d15ad742
LP
1352 /* Ignore NSEC3 RRs whose algorithm we don't know */
1353 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1354 return 0;
a8f158b9
LP
1355 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1356 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1357 return 0;
d15ad742 1358
cbd100ac
LP
1359 /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
1360 * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
4c701096 1361 if (!IN_SET(rr->n_skip_labels_source, 0, (unsigned) -1))
93a3b929
LP
1362 return 0;
1363 /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
4c701096 1364 if (!IN_SET(rr->n_skip_labels_signer, 1, (unsigned) -1))
93a3b929
LP
1365 return 0;
1366
db5b0e92
LP
1367 if (!nsec3)
1368 return 1;
1369
1370 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1371
1372 if (nsec3 == rr) /* Shortcut */
1373 return 1;
1374
1375 if (rr->key->class != nsec3->key->class)
1376 return 0;
1377 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1378 return 0;
1379 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1380 return 0;
1381 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1382 return 0;
1383 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1384 return 0;
1385
1c02e7ba 1386 a = dns_resource_key_name(rr->key);
db5b0e92
LP
1387 r = dns_name_parent(&a); /* strip off hash */
1388 if (r < 0)
1389 return r;
1390 if (r == 0)
1391 return 0;
1392
1c02e7ba 1393 b = dns_resource_key_name(nsec3->key);
db5b0e92
LP
1394 r = dns_name_parent(&b); /* strip off hash */
1395 if (r < 0)
1396 return r;
1397 if (r == 0)
1398 return 0;
1399
93a3b929 1400 /* Make sure both have the same parent */
db5b0e92
LP
1401 return dns_name_equal(a, b);
1402}
1403
cdbffec0
LP
1404static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
1405 _cleanup_free_ char *l = NULL;
1406 char *j;
1407
1408 assert(hashed);
1409 assert(hashed_size > 0);
1410 assert(zone);
1411 assert(ret);
1412
1413 l = base32hexmem(hashed, hashed_size, false);
1414 if (!l)
1415 return -ENOMEM;
1416
605405c6 1417 j = strjoin(l, ".", zone);
cdbffec0
LP
1418 if (!j)
1419 return -ENOMEM;
1420
1421 *ret = j;
1422 return (int) hashed_size;
1423}
1424
1425static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
105e1512 1426 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1427 int hashed_size;
1428
1429 assert(nsec3);
1430 assert(domain);
1431 assert(zone);
1432 assert(ret);
1433
1434 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1435 if (hashed_size < 0)
1436 return hashed_size;
1437
cdbffec0 1438 return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
6f76ec5a
TG
1439}
1440
35ad41d3
TG
1441/* See RFC 5155, Section 8
1442 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1443 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1444 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1445 * matches the wildcard domain.
1446 *
1447 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1448 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1449 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1450 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1451static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
d41084a5
LP
1452 _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
1453 const char *zone, *p, *pp = NULL, *wildcard;
7e35195f 1454 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
105e1512
LP
1455 DnsAnswerFlags flags;
1456 int hashed_size, r;
35ad41d3 1457 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1458
1459 assert(key);
1460 assert(result);
1461
d1511b33
TG
1462 /* First step, find the zone name and the NSEC3 parameters of the zone.
1463 * it is sufficient to look for the longest common suffix we find with
1464 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1465 * records from a given zone in a response must use the same
1466 * parameters. */
1c02e7ba 1467 zone = dns_resource_key_name(key);
13b78323 1468 for (;;) {
7e35195f 1469 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
3f5ecaad 1470 r = nsec3_is_good(zone_rr, NULL);
db5b0e92
LP
1471 if (r < 0)
1472 return r;
1473 if (r == 0)
13b78323
LP
1474 continue;
1475
1c02e7ba 1476 r = dns_name_equal_skip(dns_resource_key_name(zone_rr->key), 1, zone);
13b78323
LP
1477 if (r < 0)
1478 return r;
1479 if (r > 0)
d1511b33 1480 goto found_zone;
13b78323
LP
1481 }
1482
1483 /* Strip one label from the front */
d1511b33 1484 r = dns_name_parent(&zone);
13b78323
LP
1485 if (r < 0)
1486 return r;
1487 if (r == 0)
1488 break;
1489 }
1490
1491 *result = DNSSEC_NSEC_NO_RR;
1492 return 0;
1493
d1511b33 1494found_zone:
13b78323 1495 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1c02e7ba 1496 p = dns_resource_key_name(key);
105e1512 1497 for (;;) {
6f76ec5a 1498 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1499
cdbffec0 1500 hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
db5b0e92
LP
1501 if (hashed_size == -EOPNOTSUPP) {
1502 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1503 return 0;
1504 }
1505 if (hashed_size < 0)
1506 return hashed_size;
72667f08 1507
d1511b33 1508 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1509
3f5ecaad 1510 r = nsec3_is_good(enclosure_rr, zone_rr);
72667f08
LP
1511 if (r < 0)
1512 return r;
105e1512
LP
1513 if (r == 0)
1514 continue;
1515
d1511b33 1516 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1517 continue;
105e1512 1518
1c02e7ba 1519 r = dns_name_equal(dns_resource_key_name(enclosure_rr->key), hashed_domain);
72667f08
LP
1520 if (r < 0)
1521 return r;
ed29bfdc
LP
1522 if (r > 0) {
1523 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1524 goto found_closest_encloser;
ed29bfdc 1525 }
105e1512
LP
1526 }
1527
1528 /* We didn't find the closest encloser with this name,
1529 * but let's remember this domain name, it might be
1530 * the next closer name */
1531
1532 pp = p;
1533
1534 /* Strip one label from the front */
1535 r = dns_name_parent(&p);
1536 if (r < 0)
1537 return r;
1538 if (r == 0)
72667f08 1539 break;
105e1512 1540 }
72667f08 1541
105e1512
LP
1542 *result = DNSSEC_NSEC_NO_RR;
1543 return 0;
72667f08 1544
13b78323 1545found_closest_encloser:
105e1512 1546 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1547
105e1512 1548 if (!pp) {
352af308
LP
1549 /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR
1550 * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are
1551 * appropriately set. */
1552
1553 if (key->type == DNS_TYPE_DS) {
1554 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1555 return -EBADMSG;
1556 } else {
1557 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1558 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1559 return -EBADMSG;
1560 }
1561
105e1512 1562 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1563 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1564 *result = DNSSEC_NSEC_FOUND;
1565 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1566 *result = DNSSEC_NSEC_CNAME;
1567 else
1568 *result = DNSSEC_NSEC_NODATA;
1569
d3760be0
LP
1570 if (authenticated)
1571 *authenticated = a;
1572 if (ttl)
1573 *ttl = enclosure_rr->ttl;
146035b3 1574
105e1512
LP
1575 return 0;
1576 }
72667f08 1577
352af308
LP
1578 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1579 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
1580 return -EBADMSG;
1581
1582 /* Ensure that this data is from the delegated domain
1583 * (i.e. originates from the "lower" DNS server), and isn't
1584 * just glue records (i.e. doesn't originate from the "upper"
1585 * DNS server). */
1586 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1587 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1588 return -EBADMSG;
1589
35ad41d3
TG
1590 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1591
d41084a5 1592 wildcard = strjoina("*.", p);
cdbffec0 1593 r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1594 if (r < 0)
1595 return r;
1596 if (r != hashed_size)
1597 return -EBADMSG;
72667f08 1598
cdbffec0 1599 r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1600 if (r < 0)
1601 return r;
1602 if (r != hashed_size)
1603 return -EBADMSG;
72667f08 1604
105e1512 1605 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
cdbffec0 1606 _cleanup_free_ char *next_hashed_domain = NULL;
105e1512 1607
3f5ecaad 1608 r = nsec3_is_good(rr, zone_rr);
105e1512
LP
1609 if (r < 0)
1610 return r;
1611 if (r == 0)
1612 continue;
1613
cdbffec0
LP
1614 r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
1615 if (r < 0)
1616 return r;
105e1512 1617
1c02e7ba 1618 r = dns_name_between(dns_resource_key_name(rr->key), next_closer_domain, next_hashed_domain);
105e1512
LP
1619 if (r < 0)
1620 return r;
1621 if (r > 0) {
1622 if (rr->nsec3.flags & 1)
35ad41d3 1623 optout = true;
105e1512 1624
35ad41d3
TG
1625 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1626
1627 no_closer = true;
1628 }
1629
1c02e7ba 1630 r = dns_name_equal(dns_resource_key_name(rr->key), wildcard_domain);
35ad41d3
TG
1631 if (r < 0)
1632 return r;
1633 if (r > 0) {
1634 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1635
1636 wildcard_rr = rr;
1637 }
1638
1c02e7ba 1639 r = dns_name_between(dns_resource_key_name(rr->key), wildcard_domain, next_hashed_domain);
35ad41d3
TG
1640 if (r < 0)
1641 return r;
1642 if (r > 0) {
1643 if (rr->nsec3.flags & 1)
1644 /* This only makes sense if we have a wildcard delegation, which is
1645 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1646 * this not happening, so hence cannot simply conclude NXDOMAIN as
1647 * we would wish */
1648 optout = true;
1649
1650 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1651
1652 no_wildcard = true;
105e1512
LP
1653 }
1654 }
1655
35ad41d3
TG
1656 if (wildcard_rr && no_wildcard)
1657 return -EBADMSG;
1658
1659 if (!no_closer) {
1660 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1661 return 0;
1662 }
1663
1664 if (wildcard_rr) {
1665 /* A wildcard exists that matches our query. */
1666 if (optout)
1667 /* This is not specified in any RFC to the best of my knowledge, but
1668 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1669 * it means that we cannot prove that the source of synthesis is
1670 * correct, as there may be a closer match. */
1671 *result = DNSSEC_NSEC_OPTOUT;
1672 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1673 *result = DNSSEC_NSEC_FOUND;
1674 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1675 *result = DNSSEC_NSEC_CNAME;
1676 else
1677 *result = DNSSEC_NSEC_NODATA;
1678 } else {
1679 if (optout)
1680 /* The RFC only specifies that we have to care for optout for NODATA for
1681 * DS records. However, children of an insecure opt-out delegation should
1682 * also be considered opt-out, rather than verified NXDOMAIN.
1683 * Note that we do not require a proof of wildcard non-existence if the
1684 * next closer domain is covered by an opt-out, as that would not provide
1685 * any additional information. */
1686 *result = DNSSEC_NSEC_OPTOUT;
1687 else if (no_wildcard)
1688 *result = DNSSEC_NSEC_NXDOMAIN;
1689 else {
1690 *result = DNSSEC_NSEC_NO_RR;
1691
1692 return 0;
1693 }
1694 }
1695
d3760be0
LP
1696 if (authenticated)
1697 *authenticated = a;
1698
1699 if (ttl)
1700 *ttl = enclosure_rr->ttl;
35ad41d3 1701
105e1512
LP
1702 return 0;
1703}
1704
ab481675
LP
1705static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
1706 char label[DNS_LABEL_MAX];
1707 const char *n;
1708 int r;
1709
1710 assert(rr);
1711 assert(rr->key->type == DNS_TYPE_NSEC);
1712
1713 /* Checks whether the specified RR has a name beginning in "*.", and if the rest is a suffix of our name */
1714
1715 if (rr->n_skip_labels_source != 1)
1716 return 0;
1717
1c02e7ba 1718 n = dns_resource_key_name(rr->key);
ab481675
LP
1719 r = dns_label_unescape(&n, label, sizeof(label));
1720 if (r <= 0)
1721 return r;
1722 if (r != 1 || label[0] != '*')
1723 return 0;
1724
1725 return dns_name_endswith(name, n);
1726}
1727
1728static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) {
b9282bc1
LP
1729 const char *nn, *common_suffix;
1730 int r;
1731
1732 assert(rr);
1733 assert(rr->key->type == DNS_TYPE_NSEC);
1734
1735 /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT)
1736 *
1737 * A couple of examples:
1738 *
1739 * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT
1740 * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs
1741 * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs
1742 */
1743
1744 /* First, determine parent of next domain. */
1745 nn = rr->nsec.next_domain_name;
1746 r = dns_name_parent(&nn);
1747 if (r <= 0)
1748 return r;
1749
1750 /* If the name we just determined is not equal or child of the name we are interested in, then we can't say
1751 * anything at all. */
1752 r = dns_name_endswith(nn, name);
1753 if (r <= 0)
1754 return r;
1755
61233823 1756 /* If the name we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
1c02e7ba 1757 r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
b9282bc1
LP
1758 if (r < 0)
1759 return r;
1760
1761 return dns_name_endswith(name, common_suffix);
1762}
1763
ab481675
LP
1764static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) {
1765 int r;
1766
1767 assert(rr);
1768 assert(rr->key->type == DNS_TYPE_NSEC);
1769
1770 /* Checks whether this NSEC originates to the parent zone or the child zone. */
1771
1772 r = dns_name_parent(&name);
1773 if (r <= 0)
1774 return r;
1775
1c02e7ba 1776 r = dns_name_equal(name, dns_resource_key_name(rr->key));
ab481675
LP
1777 if (r <= 0)
1778 return r;
1779
1780 /* DNAME, and NS without SOA is an indication for a delegation. */
1781 if (bitmap_isset(rr->nsec.types, DNS_TYPE_DNAME))
1782 return 1;
1783
1784 if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
1785 return 1;
1786
1787 return 0;
1788}
1789
1790static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
0b491556 1791 const char *signer;
ab481675
LP
1792 int r;
1793
1794 assert(rr);
1795 assert(rr->key->type == DNS_TYPE_NSEC);
1796
0b491556
LP
1797 /* Checks whether the name is covered by this NSEC RR. This means, that the name is somewhere below the NSEC's
1798 * signer name, and between the NSEC's two names. */
ab481675 1799
0b491556 1800 r = dns_resource_record_signer(rr, &signer);
ab481675
LP
1801 if (r < 0)
1802 return r;
1803
0b491556
LP
1804 r = dns_name_endswith(name, signer); /* this NSEC isn't suitable the name is not in the signer's domain */
1805 if (r <= 0)
1806 return r;
ab481675 1807
0b491556 1808 return dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
ab481675
LP
1809}
1810
1811static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) {
97c2ea26 1812 _cleanup_free_ char *wc = NULL;
0b491556 1813 const char *common_suffix, *signer;
d86c982a
LP
1814 int r;
1815
1816 assert(rr);
1817 assert(rr->key->type == DNS_TYPE_NSEC);
1818
1819 /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified
1820 * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as
1821 * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label.
1822 *
1823 * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist
1824 * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...)
1825 * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either...
1826 */
1827
0b491556 1828 r = dns_resource_record_signer(rr, &signer);
d86c982a
LP
1829 if (r < 0)
1830 return r;
1831
0b491556 1832 r = dns_name_endswith(name, signer); /* this NSEC isn't suitable the name is not in the signer's domain */
d86c982a
LP
1833 if (r <= 0)
1834 return r;
1835
0b491556
LP
1836 r = dns_name_endswith(name, dns_resource_key_name(rr->key));
1837 if (r < 0)
1838 return r;
1839 if (r > 0) /* If the name we are interested in is a child of the NSEC RR, then append the asterisk to the NSEC
1840 * RR's name. */
1841 r = dns_name_concat("*", dns_resource_key_name(rr->key), &wc);
1842 else {
1843 r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
1844 if (r < 0)
1845 return r;
1846
1847 r = dns_name_concat("*", common_suffix, &wc);
1848 }
97c2ea26
LP
1849 if (r < 0)
1850 return r;
1851
1c02e7ba 1852 return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name);
d86c982a
LP
1853}
1854
0c7bff0a 1855int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
d86c982a
LP
1856 bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false;
1857 DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL;
105e1512 1858 DnsAnswerFlags flags;
b9282bc1 1859 const char *name;
105e1512
LP
1860 int r;
1861
1862 assert(key);
1863 assert(result);
1864
1865 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1866
1c02e7ba 1867 name = dns_resource_key_name(key);
b9282bc1 1868
105e1512
LP
1869 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1870
1871 if (rr->key->class != key->class)
1872 continue;
1873
ab481675 1874 have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3);
105e1512 1875
ab481675
LP
1876 if (rr->key->type != DNS_TYPE_NSEC)
1877 continue;
1878
1879 /* The following checks only make sense for NSEC RRs that are not expanded from a wildcard */
1880 r = dns_resource_record_is_synthetic(rr);
1881 if (r < 0)
1882 return r;
1883 if (r > 0)
1884 continue;
105e1512 1885
ab481675 1886 /* Check if this is a direct match. If so, we have encountered a NODATA case */
1c02e7ba 1887 r = dns_name_equal(dns_resource_key_name(rr->key), name);
ab481675
LP
1888 if (r < 0)
1889 return r;
1890 if (r == 0) {
1891 /* If it's not a direct match, maybe it's a wild card match? */
1892 r = dnssec_nsec_wildcard_equal(rr, name);
105e1512
LP
1893 if (r < 0)
1894 return r;
ab481675
LP
1895 }
1896 if (r > 0) {
1897 if (key->type == DNS_TYPE_DS) {
1898 /* If we look for a DS RR and the server sent us the NSEC RR of the child zone
1899 * we have a problem. For DS RRs we want the NSEC RR from the parent */
1900 if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
1901 continue;
1902 } else {
1903 /* For all RR types, ensure that if NS is set SOA is set too, so that we know
1904 * we got the child's NSEC. */
1905 if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) &&
1906 !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
1907 continue;
72667f08
LP
1908 }
1909
ab481675
LP
1910 if (bitmap_isset(rr->nsec.types, key->type))
1911 *result = DNSSEC_NSEC_FOUND;
1912 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1913 *result = DNSSEC_NSEC_CNAME;
1914 else
b9282bc1 1915 *result = DNSSEC_NSEC_NODATA;
d3760be0 1916
ab481675
LP
1917 if (authenticated)
1918 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1919 if (ttl)
1920 *ttl = rr->ttl;
d3760be0 1921
ab481675
LP
1922 return 0;
1923 }
b9282bc1 1924
ab481675
LP
1925 /* Check if the name we are looking for is an empty non-terminal within the owner or next name
1926 * of the NSEC RR. */
1927 r = dnssec_nsec_in_path(rr, name);
1928 if (r < 0)
1929 return r;
1930 if (r > 0) {
1931 *result = DNSSEC_NSEC_NODATA;
b9282bc1 1932
ab481675
LP
1933 if (authenticated)
1934 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1935 if (ttl)
1936 *ttl = rr->ttl;
b9282bc1 1937
ab481675
LP
1938 return 0;
1939 }
72667f08 1940
ab481675
LP
1941 /* The following two "covering" checks, are not useful if the NSEC is from the parent */
1942 r = dnssec_nsec_from_parent_zone(rr, name);
1943 if (r < 0)
1944 return r;
1945 if (r > 0)
1946 continue;
1947
1948 /* Check if this NSEC RR proves the absence of an explicit RR under this name */
1949 r = dnssec_nsec_covers(rr, name);
1950 if (r < 0)
1951 return r;
1952 if (r > 0 && (!covering_rr || !covering_rr_authenticated)) {
1953 covering_rr = rr;
1954 covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1955 }
1956
1957 /* Check if this NSEC RR proves the absence of a wildcard RR under this name */
1958 r = dnssec_nsec_covers_wildcard(rr, name);
1959 if (r < 0)
1960 return r;
1961 if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
1962 wildcard_rr = rr;
1963 wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
72667f08
LP
1964 }
1965 }
1966
d86c982a
LP
1967 if (covering_rr && wildcard_rr) {
1968 /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we
1969 * proved the NXDOMAIN case. */
1970 *result = DNSSEC_NSEC_NXDOMAIN;
1971
1972 if (authenticated)
1973 *authenticated = covering_rr_authenticated && wildcard_rr_authenticated;
1974 if (ttl)
1975 *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl);
1976
1977 return 0;
1978 }
1979
105e1512
LP
1980 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1981 if (have_nsec3)
d3760be0 1982 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 1983
72667f08
LP
1984 /* No approproate NSEC RR found, report this. */
1985 *result = DNSSEC_NSEC_NO_RR;
1986 return 0;
1987}
1988
421cc89d 1989static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
0c7bff0a
LP
1990 DnsResourceRecord *rr;
1991 DnsAnswerFlags flags;
1992 int r;
1993
1994 assert(name);
1995 assert(zone);
1996
1997 /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
1998 * 'zone'. The 'zone' must be a suffix of the 'name'. */
1999
2000 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
2001 bool found = false;
2002
e926785a
LP
2003 if (rr->key->type != type && type != DNS_TYPE_ANY)
2004 continue;
2005
0c7bff0a
LP
2006 switch (rr->key->type) {
2007
2008 case DNS_TYPE_NSEC:
97c67192
LP
2009
2010 /* We only care for NSEC RRs from the indicated zone */
2011 r = dns_resource_record_is_signer(rr, zone);
2012 if (r < 0)
2013 return r;
2014 if (r == 0)
2015 continue;
2016
1c02e7ba 2017 r = dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
0c7bff0a
LP
2018 if (r < 0)
2019 return r;
2020
2021 found = r > 0;
2022 break;
2023
2024 case DNS_TYPE_NSEC3: {
2025 _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
2026
97c67192
LP
2027 /* We only care for NSEC3 RRs from the indicated zone */
2028 r = dns_resource_record_is_signer(rr, zone);
2029 if (r < 0)
2030 return r;
2031 if (r == 0)
2032 continue;
2033
0c7bff0a
LP
2034 r = nsec3_is_good(rr, NULL);
2035 if (r < 0)
2036 return r;
2037 if (r == 0)
2038 break;
2039
2040 /* Format the domain we are testing with the NSEC3 RR's hash function */
2041 r = nsec3_hashed_domain_make(
2042 rr,
2043 name,
2044 zone,
2045 &hashed_domain);
2046 if (r < 0)
2047 return r;
2048 if ((size_t) r != rr->nsec3.next_hashed_name_size)
2049 break;
2050
2051 /* Format the NSEC3's next hashed name as proper domain name */
2052 r = nsec3_hashed_domain_format(
2053 rr->nsec3.next_hashed_name,
2054 rr->nsec3.next_hashed_name_size,
2055 zone,
2056 &next_hashed_domain);
2057 if (r < 0)
2058 return r;
2059
1c02e7ba 2060 r = dns_name_between(dns_resource_key_name(rr->key), hashed_domain, next_hashed_domain);
0c7bff0a
LP
2061 if (r < 0)
2062 return r;
2063
2064 found = r > 0;
2065 break;
2066 }
2067
2068 default:
2069 continue;
2070 }
2071
2072 if (found) {
2073 if (authenticated)
2074 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
2075 return 1;
2076 }
2077 }
2078
2079 return 0;
2080}
2081
e926785a
LP
2082static int dnssec_test_positive_wildcard_nsec3(
2083 DnsAnswer *answer,
2084 const char *name,
2085 const char *source,
2086 const char *zone,
2087 bool *authenticated) {
2088
2089 const char *next_closer = NULL;
2090 int r;
2091
2092 /* Run a positive NSEC3 wildcard proof. Specifically:
2093 *
c629ff58 2094 * A proof that the "next closer" of the generating wildcard does not exist.
e926785a
LP
2095 *
2096 * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
2097 * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
2098 * exists for the NSEC3 RR and we are done.
2099 *
2100 * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
2101 * c.d.e.f does not exist. */
2102
2103 for (;;) {
2104 next_closer = name;
2105 r = dns_name_parent(&name);
2106 if (r < 0)
2107 return r;
2108 if (r == 0)
2109 return 0;
2110
2111 r = dns_name_equal(name, source);
2112 if (r < 0)
2113 return r;
2114 if (r > 0)
2115 break;
2116 }
2117
2118 return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
2119}
2120
2121static int dnssec_test_positive_wildcard_nsec(
2122 DnsAnswer *answer,
2123 const char *name,
2124 const char *source,
2125 const char *zone,
2126 bool *_authenticated) {
2127
2128 bool authenticated = true;
2129 int r;
2130
2131 /* Run a positive NSEC wildcard proof. Specifically:
2132 *
2133 * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
2134 * a prefix of the synthesizing source "source" in the zone "zone".
2135 *
2136 * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
2137 *
2138 * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
2139 * have to prove that none of the following exist:
2140 *
2141 * 1) a.b.c.d.e.f
2142 * 2) *.b.c.d.e.f
2143 * 3) b.c.d.e.f
2144 * 4) *.c.d.e.f
2145 * 5) c.d.e.f
2146 *
2147 */
2148
2149 for (;;) {
2150 _cleanup_free_ char *wc = NULL;
2151 bool a = false;
2152
2153 /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
2154 * i.e between the owner name and the next name of an NSEC RR. */
2155 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
2156 if (r <= 0)
2157 return r;
2158
2159 authenticated = authenticated && a;
2160
2161 /* Strip one label off */
2162 r = dns_name_parent(&name);
2163 if (r <= 0)
2164 return r;
2165
2166 /* Did we reach the source of synthesis? */
2167 r = dns_name_equal(name, source);
2168 if (r < 0)
2169 return r;
2170 if (r > 0) {
2171 /* Successful exit */
2172 *_authenticated = authenticated;
2173 return 1;
2174 }
2175
2176 /* Safety check, that the source of synthesis is still our suffix */
2177 r = dns_name_endswith(name, source);
2178 if (r < 0)
2179 return r;
2180 if (r == 0)
2181 return -EBADMSG;
2182
2183 /* Replace the label we stripped off with an asterisk */
2184 wc = strappend("*.", name);
2185 if (!wc)
2186 return -ENOMEM;
2187
2188 /* And check if the proof holds for the asterisk name, too */
2189 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
2190 if (r <= 0)
2191 return r;
2192
2193 authenticated = authenticated && a;
2194 /* In the next iteration we'll check the non-asterisk-prefixed version */
2195 }
2196}
2197
2198int dnssec_test_positive_wildcard(
2199 DnsAnswer *answer,
2200 const char *name,
2201 const char *source,
2202 const char *zone,
2203 bool *authenticated) {
2204
2205 int r;
2206
2207 assert(name);
2208 assert(source);
2209 assert(zone);
2210 assert(authenticated);
2211
2212 r = dns_answer_contains_zone_nsec3(answer, zone);
2213 if (r < 0)
2214 return r;
2215 if (r > 0)
2216 return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
2217 else
2218 return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
2219}
2220
47091522
MO
2221#else
2222
2223int dnssec_verify_rrset(
2224 DnsAnswer *a,
2225 const DnsResourceKey *key,
2226 DnsResourceRecord *rrsig,
2227 DnsResourceRecord *dnskey,
2228 usec_t realtime,
2229 DnssecResult *result) {
2230
2231 return -EOPNOTSUPP;
2232}
2233
2234int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2235
2236 return -EOPNOTSUPP;
2237}
2238
2239int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
2240
2241 return -EOPNOTSUPP;
2242}
2243
2244int dnssec_verify_rrset_search(
2245 DnsAnswer *a,
2246 const DnsResourceKey *key,
2247 DnsAnswer *validated_dnskeys,
2248 usec_t realtime,
2249 DnssecResult *result,
2250 DnsResourceRecord **ret_rrsig) {
2251
2252 return -EOPNOTSUPP;
2253}
2254
2255int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
2256
2257 return -EOPNOTSUPP;
2258}
2259
2260int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
2261
2262 return -EOPNOTSUPP;
2263}
2264
2265int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
2266
2267 return -EOPNOTSUPP;
2268}
2269
2270int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
2271
2272 return -EOPNOTSUPP;
2273}
2274
2275int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
2276
2277 return -EOPNOTSUPP;
2278}
2279
2280int dnssec_test_positive_wildcard(
2281 DnsAnswer *answer,
2282 const char *name,
2283 const char *source,
2284 const char *zone,
2285 bool *authenticated) {
2286
2287 return -EOPNOTSUPP;
2288}
2289
2290#endif
2291
547973de
LP
2292static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
2293 [DNSSEC_VALIDATED] = "validated",
0c7bff0a 2294 [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
547973de 2295 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
2296 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
2297 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
2298 [DNSSEC_NO_SIGNATURE] = "no-signature",
2299 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 2300 [DNSSEC_UNSIGNED] = "unsigned",
547973de 2301 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 2302 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 2303 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
2304};
2305DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
59c5b597
LP
2306
2307static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = {
2308 [DNSSEC_SECURE] = "secure",
2309 [DNSSEC_INSECURE] = "insecure",
2310 [DNSSEC_BOGUS] = "bogus",
2311 [DNSSEC_INDETERMINATE] = "indeterminate",
2312};
2313DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict);