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