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