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