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