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