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