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