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