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