]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
update TODO
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.c
CommitLineData
2b442ac8
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <gcrypt.h>
23
24#include "alloc-util.h"
25#include "dns-domain.h"
72667f08 26#include "hexdecoct.h"
2b442ac8
LP
27#include "resolved-dns-dnssec.h"
28#include "resolved-dns-packet.h"
24710c48 29#include "string-table.h"
2b442ac8
LP
30
31/* Open question:
32 *
33 * How does the DNSSEC canonical form of a hostname with a label
34 * containing a dot look like, the way DNS-SD does it?
35 *
2cd87277
LP
36 * TODO:
37 *
3ecc3df8 38 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
bb1fa242 39 * - multi-label zone compatibility
3e92a719 40 * - cname/dname compatibility
160fbda9 41 * - nxdomain on qname
d1d1d4b8 42 * - bus calls to override DNSEC setting per interface
28bf03b5
LP
43 * - log all DNSSEC downgrades
44 * - enable by default
2cd87277 45 *
3a33c81b
LP
46 * - RFC 4035, Section 5.3.4 (When receiving a positive wildcard reply, use NSEC to ensure it actually really applies)
47 * - RFC 6840, Section 4.1 (ensure we don't get fed a glue NSEC from the parent zone)
48 * - RFC 6840, Section 4.3 (check for CNAME on NSEC too)
2b442ac8
LP
49 * */
50
51#define VERIFY_RRS_MAX 256
52#define MAX_KEY_SIZE (32*1024)
53
896c5672
LP
54/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
55#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
56
3a33c81b
LP
57/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
58#define NSEC3_ITERATIONS_MAX 2500
a8f158b9 59
2b442ac8
LP
60/*
61 * The DNSSEC Chain of trust:
62 *
63 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
64 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
65 * DS RRs are protected like normal RRs
66 *
67 * Example chain:
68 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
69 */
70
0638401a
LP
71static void initialize_libgcrypt(void) {
72 const char *p;
73
74 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
75 return;
76
77 p = gcry_check_version("1.4.5");
78 assert(p);
79
80 gcry_control(GCRYCTL_DISABLE_SECMEM);
81 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
82}
83
0c857028 84uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
2b442ac8 85 const uint8_t *p;
0c857028 86 uint32_t sum, f;
2b442ac8
LP
87 size_t i;
88
89 /* The algorithm from RFC 4034, Appendix B. */
90
91 assert(dnskey);
92 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
93
0c857028
LP
94 f = (uint32_t) dnskey->dnskey.flags;
95
96 if (mask_revoke)
97 f &= ~DNSKEY_FLAG_REVOKE;
98
99 sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
2b442ac8
LP
100
101 p = dnskey->dnskey.key;
102
103 for (i = 0; i < dnskey->dnskey.key_size; i++)
104 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
105
106 sum += (sum >> 16) & UINT32_C(0xFFFF);
107
108 return sum & UINT32_C(0xFFFF);
109}
110
111static int rr_compare(const void *a, const void *b) {
112 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
113 size_t m;
114 int r;
115
116 /* Let's order the RRs according to RFC 4034, Section 6.3 */
117
118 assert(x);
119 assert(*x);
120 assert((*x)->wire_format);
121 assert(y);
122 assert(*y);
123 assert((*y)->wire_format);
124
85aeaccc 125 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
2b442ac8 126
85aeaccc 127 r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
2b442ac8
LP
128 if (r != 0)
129 return r;
130
85aeaccc 131 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8 132 return -1;
85aeaccc 133 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8
LP
134 return 1;
135
136 return 0;
137}
138
ea3a892f 139static int dnssec_rsa_verify_raw(
2b442ac8
LP
140 const char *hash_algorithm,
141 const void *signature, size_t signature_size,
142 const void *data, size_t data_size,
143 const void *exponent, size_t exponent_size,
144 const void *modulus, size_t modulus_size) {
145
146 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
147 gcry_mpi_t n = NULL, e = NULL, s = NULL;
148 gcry_error_t ge;
149 int r;
150
151 assert(hash_algorithm);
152
153 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
154 if (ge != 0) {
155 r = -EIO;
156 goto finish;
157 }
158
159 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
160 if (ge != 0) {
161 r = -EIO;
162 goto finish;
163 }
164
165 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
166 if (ge != 0) {
167 r = -EIO;
168 goto finish;
169 }
170
171 ge = gcry_sexp_build(&signature_sexp,
172 NULL,
173 "(sig-val (rsa (s %m)))",
174 s);
175
176 if (ge != 0) {
177 r = -EIO;
178 goto finish;
179 }
180
181 ge = gcry_sexp_build(&data_sexp,
182 NULL,
183 "(data (flags pkcs1) (hash %s %b))",
184 hash_algorithm,
185 (int) data_size,
186 data);
187 if (ge != 0) {
188 r = -EIO;
189 goto finish;
190 }
191
192 ge = gcry_sexp_build(&public_key_sexp,
193 NULL,
194 "(public-key (rsa (n %m) (e %m)))",
195 n,
196 e);
197 if (ge != 0) {
198 r = -EIO;
199 goto finish;
200 }
201
202 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
d12bf2bd 203 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 204 r = 0;
d12bf2bd
LP
205 else if (ge != 0) {
206 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
2b442ac8 207 r = -EIO;
d12bf2bd 208 } else
2b442ac8
LP
209 r = 1;
210
211finish:
212 if (e)
213 gcry_mpi_release(e);
214 if (n)
215 gcry_mpi_release(n);
216 if (s)
217 gcry_mpi_release(s);
218
219 if (public_key_sexp)
220 gcry_sexp_release(public_key_sexp);
221 if (signature_sexp)
222 gcry_sexp_release(signature_sexp);
223 if (data_sexp)
224 gcry_sexp_release(data_sexp);
225
226 return r;
227}
228
ea3a892f
LP
229static int dnssec_rsa_verify(
230 const char *hash_algorithm,
231 const void *hash, size_t hash_size,
232 DnsResourceRecord *rrsig,
233 DnsResourceRecord *dnskey) {
234
235 size_t exponent_size, modulus_size;
236 void *exponent, *modulus;
237
238 assert(hash_algorithm);
239 assert(hash);
240 assert(hash_size > 0);
241 assert(rrsig);
242 assert(dnskey);
243
244 if (*(uint8_t*) dnskey->dnskey.key == 0) {
245 /* exponent is > 255 bytes long */
246
247 exponent = (uint8_t*) dnskey->dnskey.key + 3;
248 exponent_size =
ac04adbe
TG
249 ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
250 ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
ea3a892f
LP
251
252 if (exponent_size < 256)
253 return -EINVAL;
254
255 if (3 + exponent_size >= dnskey->dnskey.key_size)
256 return -EINVAL;
257
258 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
259 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
260
261 } else {
262 /* exponent is <= 255 bytes long */
263
264 exponent = (uint8_t*) dnskey->dnskey.key + 1;
265 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
266
267 if (exponent_size <= 0)
268 return -EINVAL;
269
270 if (1 + exponent_size >= dnskey->dnskey.key_size)
271 return -EINVAL;
272
273 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
274 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
275 }
276
277 return dnssec_rsa_verify_raw(
278 hash_algorithm,
279 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
280 hash, hash_size,
281 exponent, exponent_size,
282 modulus, modulus_size);
283}
284
e0240c64
LP
285static int dnssec_ecdsa_verify_raw(
286 const char *hash_algorithm,
287 const char *curve,
288 const void *signature_r, size_t signature_r_size,
289 const void *signature_s, size_t signature_s_size,
290 const void *data, size_t data_size,
291 const void *key, size_t key_size) {
292
293 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
294 gcry_mpi_t q = NULL, r = NULL, s = NULL;
295 gcry_error_t ge;
296 int k;
297
298 assert(hash_algorithm);
299
300 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
301 if (ge != 0) {
302 k = -EIO;
303 goto finish;
304 }
305
306 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
307 if (ge != 0) {
308 k = -EIO;
309 goto finish;
310 }
311
312 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
313 if (ge != 0) {
314 k = -EIO;
315 goto finish;
316 }
317
318 ge = gcry_sexp_build(&signature_sexp,
319 NULL,
320 "(sig-val (ecdsa (r %m) (s %m)))",
321 r,
322 s);
323 if (ge != 0) {
324 k = -EIO;
325 goto finish;
326 }
327
328 ge = gcry_sexp_build(&data_sexp,
329 NULL,
330 "(data (flags rfc6979) (hash %s %b))",
331 hash_algorithm,
332 (int) data_size,
333 data);
334 if (ge != 0) {
335 k = -EIO;
336 goto finish;
337 }
338
339 ge = gcry_sexp_build(&public_key_sexp,
340 NULL,
341 "(public-key (ecc (curve %s) (q %m)))",
342 curve,
343 q);
344 if (ge != 0) {
345 k = -EIO;
346 goto finish;
347 }
348
349 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
350 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
351 k = 0;
352 else if (ge != 0) {
353 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
354 k = -EIO;
355 } else
356 k = 1;
357finish:
358 if (r)
359 gcry_mpi_release(r);
360 if (s)
361 gcry_mpi_release(s);
362 if (q)
363 gcry_mpi_release(q);
364
365 if (public_key_sexp)
366 gcry_sexp_release(public_key_sexp);
367 if (signature_sexp)
368 gcry_sexp_release(signature_sexp);
369 if (data_sexp)
370 gcry_sexp_release(data_sexp);
371
372 return k;
373}
374
375static int dnssec_ecdsa_verify(
376 const char *hash_algorithm,
377 int algorithm,
378 const void *hash, size_t hash_size,
379 DnsResourceRecord *rrsig,
380 DnsResourceRecord *dnskey) {
381
382 const char *curve;
383 size_t key_size;
384 uint8_t *q;
385
386 assert(hash);
387 assert(hash_size);
388 assert(rrsig);
389 assert(dnskey);
390
391 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
392 key_size = 32;
393 curve = "NIST P-256";
394 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
395 key_size = 48;
396 curve = "NIST P-384";
397 } else
398 return -EOPNOTSUPP;
399
400 if (dnskey->dnskey.key_size != key_size * 2)
401 return -EINVAL;
402
403 if (rrsig->rrsig.signature_size != key_size * 2)
404 return -EINVAL;
405
406 q = alloca(key_size*2 + 1);
407 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
408 memcpy(q+1, dnskey->dnskey.key, key_size*2);
409
410 return dnssec_ecdsa_verify_raw(
411 hash_algorithm,
412 curve,
413 rrsig->rrsig.signature, key_size,
414 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
415 hash, hash_size,
416 q, key_size*2+1);
417}
418
2b442ac8
LP
419static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
420 gcry_md_write(md, &v, sizeof(v));
421}
422
423static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
424 v = htobe16(v);
425 gcry_md_write(md, &v, sizeof(v));
426}
427
428static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
429 v = htobe32(v);
430 gcry_md_write(md, &v, sizeof(v));
431}
432
2a326321
LP
433static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
434 usec_t expiration, inception, skew;
435
436 assert(rrsig);
437 assert(rrsig->key->type == DNS_TYPE_RRSIG);
438
439 if (realtime == USEC_INFINITY)
440 realtime = now(CLOCK_REALTIME);
441
442 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
443 inception = rrsig->rrsig.inception * USEC_PER_SEC;
444
445 if (inception > expiration)
2a44bec4 446 return -EKEYREJECTED;
2a326321 447
896c5672
LP
448 /* Permit a certain amount of clock skew of 10% of the valid
449 * time range. This takes inspiration from unbound's
450 * resolver. */
2a326321 451 skew = (expiration - inception) / 10;
896c5672
LP
452 if (skew > SKEW_MAX)
453 skew = SKEW_MAX;
2a326321
LP
454
455 if (inception < skew)
456 inception = 0;
457 else
458 inception -= skew;
459
460 if (expiration + skew < expiration)
461 expiration = USEC_INFINITY;
462 else
463 expiration += skew;
464
465 return realtime < inception || realtime > expiration;
466}
467
ca994e85 468static int algorithm_to_gcrypt_md(uint8_t algorithm) {
fbf1a66d 469
6af47493
LP
470 /* Translates a DNSSEC signature algorithm into a gcrypt
471 * digest identifier.
472 *
473 * Note that we implement all algorithms listed as "Must
474 * implement" and "Recommended to Implement" in RFC6944. We
475 * don't implement any algorithms that are listed as
476 * "Optional" or "Must Not Implement". Specifically, we do not
477 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
478 * GOST-ECC. */
fbf1a66d
LP
479
480 switch (algorithm) {
481
482 case DNSSEC_ALGORITHM_RSASHA1:
483 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
484 return GCRY_MD_SHA1;
485
486 case DNSSEC_ALGORITHM_RSASHA256:
e0240c64 487 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
fbf1a66d
LP
488 return GCRY_MD_SHA256;
489
e0240c64
LP
490 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
491 return GCRY_MD_SHA384;
492
fbf1a66d
LP
493 case DNSSEC_ALGORITHM_RSASHA512:
494 return GCRY_MD_SHA512;
495
496 default:
497 return -EOPNOTSUPP;
498 }
499}
500
2a326321
LP
501int dnssec_verify_rrset(
502 DnsAnswer *a,
0c857028 503 const DnsResourceKey *key,
2a326321
LP
504 DnsResourceRecord *rrsig,
505 DnsResourceRecord *dnskey,
547973de
LP
506 usec_t realtime,
507 DnssecResult *result) {
2a326321 508
2b442ac8 509 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
ea3a892f
LP
510 size_t hash_size;
511 void *hash;
2b442ac8
LP
512 DnsResourceRecord **list, *rr;
513 gcry_md_hd_t md = NULL;
ca994e85 514 int r, md_algorithm;
0c7bff0a 515 bool wildcard = false;
2b442ac8 516 size_t k, n = 0;
2b442ac8
LP
517
518 assert(key);
519 assert(rrsig);
520 assert(dnskey);
547973de 521 assert(result);
2a326321
LP
522 assert(rrsig->key->type == DNS_TYPE_RRSIG);
523 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
524
525 /* Verifies the the RRSet matching the specified "key" in "a",
526 * using the signature "rrsig" and the key "dnskey". It's
527 * assumed the RRSIG and DNSKEY match. */
528
ca994e85
LP
529 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
530 if (md_algorithm == -EOPNOTSUPP) {
203f1b35
LP
531 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
532 return 0;
533 }
ca994e85
LP
534 if (md_algorithm < 0)
535 return md_algorithm;
2b442ac8 536
2a326321
LP
537 r = dnssec_rrsig_expired(rrsig, realtime);
538 if (r < 0)
539 return r;
547973de
LP
540 if (r > 0) {
541 *result = DNSSEC_SIGNATURE_EXPIRED;
542 return 0;
543 }
2a326321 544
2b442ac8 545 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
0f23174c 546 list = newa(DnsResourceRecord *, dns_answer_size(a));
2b442ac8
LP
547
548 DNS_ANSWER_FOREACH(rr, a) {
549 r = dns_resource_key_equal(key, rr->key);
550 if (r < 0)
551 return r;
552 if (r == 0)
553 continue;
554
555 /* We need the wire format for ordering, and digest calculation */
556 r = dns_resource_record_to_wire_format(rr, true);
557 if (r < 0)
558 return r;
559
560 list[n++] = rr;
935a999f
TG
561
562 if (n > VERIFY_RRS_MAX)
563 return -E2BIG;
2b442ac8
LP
564 }
565
566 if (n <= 0)
567 return -ENODATA;
568
569 /* Bring the RRs into canonical order */
6c5e8fbf 570 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
571
572 /* OK, the RRs are now in canonical order. Let's calculate the digest */
fbf1a66d 573 initialize_libgcrypt();
2b442ac8 574
ca994e85 575 hash_size = gcry_md_get_algo_dlen(md_algorithm);
fbf1a66d 576 assert(hash_size > 0);
2b442ac8 577
ca994e85 578 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
579 if (!md)
580 return -EIO;
581
582 md_add_uint16(md, rrsig->rrsig.type_covered);
583 md_add_uint8(md, rrsig->rrsig.algorithm);
584 md_add_uint8(md, rrsig->rrsig.labels);
585 md_add_uint32(md, rrsig->rrsig.original_ttl);
586 md_add_uint32(md, rrsig->rrsig.expiration);
587 md_add_uint32(md, rrsig->rrsig.inception);
588 md_add_uint16(md, rrsig->rrsig.key_tag);
589
590 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
591 if (r < 0)
592 goto finish;
593 gcry_md_write(md, wire_format_name, r);
594
595 for (k = 0; k < n; k++) {
e7ff0e0b 596 const char *suffix;
2b442ac8
LP
597 size_t l;
598 rr = list[k];
599
e7ff0e0b
LP
600 r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
601 if (r < 0)
602 goto finish;
0c7bff0a 603 if (r > 0) /* This is a wildcard! */ {
e7ff0e0b 604 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
0c7bff0a
LP
605 wildcard = true;
606 }
e7ff0e0b
LP
607
608 r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
2b442ac8
LP
609 if (r < 0)
610 goto finish;
611 gcry_md_write(md, wire_format_name, r);
612
613 md_add_uint16(md, rr->key->type);
614 md_add_uint16(md, rr->key->class);
615 md_add_uint32(md, rrsig->rrsig.original_ttl);
616
85aeaccc 617 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
2b442ac8
LP
618 assert(l <= 0xFFFF);
619
620 md_add_uint16(md, (uint16_t) l);
85aeaccc 621 gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
2b442ac8
LP
622 }
623
624 hash = gcry_md_read(md, 0);
625 if (!hash) {
626 r = -EIO;
627 goto finish;
628 }
629
e0240c64
LP
630 switch (rrsig->rrsig.algorithm) {
631
632 case DNSSEC_ALGORITHM_RSASHA1:
633 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
634 case DNSSEC_ALGORITHM_RSASHA256:
635 case DNSSEC_ALGORITHM_RSASHA512:
636 r = dnssec_rsa_verify(
ca994e85 637 gcry_md_algo_name(md_algorithm),
e0240c64
LP
638 hash, hash_size,
639 rrsig,
640 dnskey);
641 break;
642
643 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
644 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
645 r = dnssec_ecdsa_verify(
ca994e85 646 gcry_md_algo_name(md_algorithm),
e0240c64
LP
647 rrsig->rrsig.algorithm,
648 hash, hash_size,
649 rrsig,
650 dnskey);
651 break;
652 }
653
2b442ac8
LP
654 if (r < 0)
655 goto finish;
656
0c7bff0a
LP
657 if (!r)
658 *result = DNSSEC_INVALID;
659 else if (wildcard)
660 *result = DNSSEC_VALIDATED_WILDCARD;
661 else
662 *result = DNSSEC_VALIDATED;
547973de 663 r = 0;
2b442ac8
LP
664
665finish:
666 gcry_md_close(md);
667 return r;
668}
669
0c857028 670int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
671
672 assert(rrsig);
673 assert(dnskey);
674
675 /* Checks if the specified DNSKEY RR matches the key used for
676 * the signature in the specified RRSIG RR */
677
678 if (rrsig->key->type != DNS_TYPE_RRSIG)
679 return -EINVAL;
680
681 if (dnskey->key->type != DNS_TYPE_DNSKEY)
682 return 0;
683 if (dnskey->key->class != rrsig->key->class)
684 return 0;
685 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
686 return 0;
0c857028 687 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 688 return 0;
2b442ac8
LP
689 if (dnskey->dnskey.protocol != 3)
690 return 0;
691 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
692 return 0;
693
0c857028 694 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
695 return 0;
696
15accc27 697 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
698}
699
105e1512 700int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
e7ff0e0b
LP
701 int r;
702
2b442ac8
LP
703 assert(key);
704 assert(rrsig);
705
706 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
707
708 if (rrsig->key->type != DNS_TYPE_RRSIG)
709 return 0;
710 if (rrsig->key->class != key->class)
711 return 0;
712 if (rrsig->rrsig.type_covered != key->type)
713 return 0;
714
e7ff0e0b
LP
715 /* Make sure signer is a parent of the RRset */
716 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
717 if (r <= 0)
718 return r;
719
720 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
721 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
722 if (r < 0)
723 return r;
724 if (r < rrsig->rrsig.labels)
725 return 0;
726
2b442ac8
LP
727 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
728}
729
ee3d6aff
LP
730static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
731 DnsResourceRecord *rr;
732 int r;
733
734 assert(key);
735 assert(rrsig);
736
737 DNS_ANSWER_FOREACH(rr, a) {
738 r = dns_resource_key_equal(key, rr->key);
739 if (r < 0)
740 return r;
741 if (r == 0)
742 continue;
743
744 /* Pick the TTL as the minimum of the RR's TTL, the
745 * RR's original TTL according to the RRSIG and the
746 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
747 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
748 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
749 }
750
751 return 0;
752}
753
2a326321
LP
754int dnssec_verify_rrset_search(
755 DnsAnswer *a,
0c857028 756 const DnsResourceKey *key,
2a326321 757 DnsAnswer *validated_dnskeys,
547973de 758 usec_t realtime,
0c7bff0a
LP
759 DnssecResult *result,
760 DnsResourceRecord **ret_rrsig) {
2a326321 761
203f1b35 762 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
763 DnsResourceRecord *rrsig;
764 int r;
765
766 assert(key);
547973de 767 assert(result);
2b442ac8 768
105e1512 769 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
770
771 if (!a || a->n_rrs <= 0)
772 return -ENODATA;
773
774 /* Iterate through each RRSIG RR. */
775 DNS_ANSWER_FOREACH(rrsig, a) {
776 DnsResourceRecord *dnskey;
105e1512 777 DnsAnswerFlags flags;
2b442ac8 778
203f1b35 779 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
780 r = dnssec_key_match_rrsig(key, rrsig);
781 if (r < 0)
782 return r;
783 if (r == 0)
784 continue;
785
786 found_rrsig = true;
787
547973de 788 /* Look for a matching key */
105e1512 789 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 790 DnssecResult one_result;
2b442ac8 791
105e1512
LP
792 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
793 continue;
794
203f1b35 795 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
0c857028 796 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
797 if (r < 0)
798 return r;
799 if (r == 0)
800 continue;
801
2a326321
LP
802 /* Take the time here, if it isn't set yet, so
803 * that we do all validations with the same
804 * time. */
805 if (realtime == USEC_INFINITY)
806 realtime = now(CLOCK_REALTIME);
807
2b442ac8
LP
808 /* Yay, we found a matching RRSIG with a matching
809 * DNSKEY, awesome. Now let's verify all entries of
810 * the RRSet against the RRSIG and DNSKEY
811 * combination. */
812
547973de 813 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 814 if (r < 0)
2b442ac8 815 return r;
203f1b35
LP
816
817 switch (one_result) {
818
819 case DNSSEC_VALIDATED:
0c7bff0a 820 case DNSSEC_VALIDATED_WILDCARD:
203f1b35 821 /* Yay, the RR has been validated,
ee3d6aff
LP
822 * return immediately, but fix up the expiry */
823 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
824 if (r < 0)
825 return r;
826
0c7bff0a
LP
827 if (ret_rrsig)
828 *ret_rrsig = rrsig;
829
830 *result = one_result;
547973de 831 return 0;
2b442ac8 832
203f1b35
LP
833 case DNSSEC_INVALID:
834 /* If the signature is invalid, let's try another
835 key and/or signature. After all they
836 key_tags and stuff are not unique, and
837 might be shared by multiple keys. */
838 found_invalid = true;
839 continue;
840
841 case DNSSEC_UNSUPPORTED_ALGORITHM:
842 /* If the key algorithm is
843 unsupported, try another
844 RRSIG/DNSKEY pair, but remember we
845 encountered this, so that we can
846 return a proper error when we
847 encounter nothing better. */
848 found_unsupported_algorithm = true;
849 continue;
850
851 case DNSSEC_SIGNATURE_EXPIRED:
852 /* If the signature is expired, try
853 another one, but remember it, so
854 that we can return this */
855 found_expired_rrsig = true;
856 continue;
857
858 default:
859 assert_not_reached("Unexpected DNSSEC validation result");
860 }
2b442ac8
LP
861 }
862 }
863
203f1b35
LP
864 if (found_expired_rrsig)
865 *result = DNSSEC_SIGNATURE_EXPIRED;
866 else if (found_unsupported_algorithm)
867 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
868 else if (found_invalid)
547973de
LP
869 *result = DNSSEC_INVALID;
870 else if (found_rrsig)
871 *result = DNSSEC_MISSING_KEY;
872 else
873 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 874
0c7bff0a
LP
875 if (ret_rrsig)
876 *ret_rrsig = NULL;
877
547973de 878 return 0;
2b442ac8
LP
879}
880
105e1512
LP
881int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
882 DnsResourceRecord *rr;
883 int r;
884
885 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
886
887 DNS_ANSWER_FOREACH(rr, a) {
888 r = dnssec_key_match_rrsig(key, rr);
889 if (r < 0)
890 return r;
891 if (r > 0)
892 return 1;
893 }
894
895 return 0;
896}
897
2b442ac8 898int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
899 size_t c = 0;
900 int r;
901
902 /* Converts the specified hostname into DNSSEC canonicalized
903 * form. */
904
905 if (buffer_max < 2)
906 return -ENOBUFS;
907
908 for (;;) {
2b442ac8
LP
909 r = dns_label_unescape(&n, buffer, buffer_max);
910 if (r < 0)
911 return r;
912 if (r == 0)
913 break;
914 if (r > 0) {
915 int k;
916
917 /* DNSSEC validation is always done on the ASCII version of the label */
918 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
919 if (k < 0)
920 return k;
921 if (k > 0)
922 r = k;
923 }
924
925 if (buffer_max < (size_t) r + 2)
926 return -ENOBUFS;
927
928 /* The DNSSEC canonical form is not clear on what to
929 * do with dots appearing in labels, the way DNS-SD
930 * does it. Refuse it for now. */
931
932 if (memchr(buffer, '.', r))
933 return -EINVAL;
934
b577e3d5 935 ascii_strlower_n(buffer, (size_t) r);
2b442ac8
LP
936 buffer[r] = '.';
937
938 buffer += r + 1;
939 c += r + 1;
940
941 buffer_max -= r + 1;
942 }
943
944 if (c <= 0) {
945 /* Not even a single label: this is the root domain name */
946
947 assert(buffer_max > 2);
948 buffer[0] = '.';
949 buffer[1] = 0;
950
951 return 1;
952 }
953
954 return (int) c;
955}
956
ca994e85 957static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 958
fbf1a66d 959 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
960
961 switch (algorithm) {
962
963 case DNSSEC_DIGEST_SHA1:
964 return GCRY_MD_SHA1;
965
966 case DNSSEC_DIGEST_SHA256:
967 return GCRY_MD_SHA256;
968
af22c65b
LP
969 case DNSSEC_DIGEST_SHA384:
970 return GCRY_MD_SHA384;
971
a1972a91
LP
972 default:
973 return -EOPNOTSUPP;
974 }
975}
976
0c857028 977int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
2b442ac8 978 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
979 gcry_md_hd_t md = NULL;
980 size_t hash_size;
ca994e85 981 int md_algorithm, r;
2b442ac8 982 void *result;
2b442ac8
LP
983
984 assert(dnskey);
985 assert(ds);
986
987 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
988
989 if (dnskey->key->type != DNS_TYPE_DNSKEY)
990 return -EINVAL;
991 if (ds->key->type != DNS_TYPE_DS)
992 return -EINVAL;
993 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
994 return -EKEYREJECTED;
0c857028
LP
995 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
996 return -EKEYREJECTED;
2b442ac8
LP
997 if (dnskey->dnskey.protocol != 3)
998 return -EKEYREJECTED;
999
2b442ac8
LP
1000 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
1001 return 0;
0c857028 1002 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
1003 return 0;
1004
0638401a
LP
1005 initialize_libgcrypt();
1006
ca994e85
LP
1007 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
1008 if (md_algorithm < 0)
1009 return md_algorithm;
2b442ac8 1010
ca994e85 1011 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 1012 assert(hash_size > 0);
2b442ac8 1013
a1972a91
LP
1014 if (ds->ds.digest_size != hash_size)
1015 return 0;
2b442ac8 1016
a1972a91
LP
1017 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1018 if (r < 0)
1019 return r;
2b442ac8 1020
ca994e85 1021 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1022 if (!md)
1023 return -EIO;
1024
2b442ac8 1025 gcry_md_write(md, owner_name, r);
0c857028
LP
1026 if (mask_revoke)
1027 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1028 else
1029 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1030 md_add_uint8(md, dnskey->dnskey.protocol);
1031 md_add_uint8(md, dnskey->dnskey.algorithm);
1032 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1033
1034 result = gcry_md_read(md, 0);
1035 if (!result) {
1036 r = -EIO;
1037 goto finish;
1038 }
1039
1040 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1041
1042finish:
1043 gcry_md_close(md);
1044 return r;
1045}
24710c48 1046
547973de
LP
1047int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1048 DnsResourceRecord *ds;
105e1512 1049 DnsAnswerFlags flags;
547973de
LP
1050 int r;
1051
1052 assert(dnskey);
1053
1054 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1055 return 0;
1056
105e1512
LP
1057 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1058
1059 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1060 continue;
547973de
LP
1061
1062 if (ds->key->type != DNS_TYPE_DS)
1063 continue;
1064
d1c4ee32
LP
1065 if (ds->key->class != dnskey->key->class)
1066 continue;
1067
1068 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1069 if (r < 0)
1070 return r;
1071 if (r == 0)
1072 continue;
1073
0c857028
LP
1074 r = dnssec_verify_dnskey(dnskey, ds, false);
1075 if (r == -EKEYREJECTED)
1076 return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
547973de
LP
1077 if (r < 0)
1078 return r;
1079 if (r > 0)
1080 return 1;
1081 }
1082
1083 return 0;
1084}
1085
d15ad742
LP
1086static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1087
1088 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1089
1090 switch (algorithm) {
1091
1092 case NSEC3_ALGORITHM_SHA1:
1093 return GCRY_MD_SHA1;
1094
1095 default:
1096 return -EOPNOTSUPP;
1097 }
1098}
1099
1d3db294 1100int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
72667f08
LP
1101 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1102 gcry_md_hd_t md = NULL;
1103 size_t hash_size;
1104 int algorithm;
1105 void *result;
1106 unsigned k;
1107 int r;
1108
1109 assert(nsec3);
1110 assert(name);
1111 assert(ret);
1112
1113 if (nsec3->key->type != DNS_TYPE_NSEC3)
1114 return -EINVAL;
1115
1d3db294
LP
1116 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1117 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1118 return -EOPNOTSUPP;
1d3db294 1119 }
a8f158b9 1120
d15ad742 1121 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1122 if (algorithm < 0)
1123 return algorithm;
1124
1125 initialize_libgcrypt();
1126
1127 hash_size = gcry_md_get_algo_dlen(algorithm);
1128 assert(hash_size > 0);
1129
1130 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1131 return -EINVAL;
1132
1133 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1134 if (r < 0)
1135 return r;
1136
1137 gcry_md_open(&md, algorithm, 0);
1138 if (!md)
1139 return -EIO;
1140
1141 gcry_md_write(md, wire_format, r);
1142 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1143
1144 result = gcry_md_read(md, 0);
1145 if (!result) {
1146 r = -EIO;
1147 goto finish;
1148 }
1149
1150 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1151 uint8_t tmp[hash_size];
1152 memcpy(tmp, result, hash_size);
1153
1154 gcry_md_reset(md);
1155 gcry_md_write(md, tmp, hash_size);
1156 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1157
1158 result = gcry_md_read(md, 0);
1159 if (!result) {
1160 r = -EIO;
1161 goto finish;
1162 }
1163 }
1164
1165 memcpy(ret, result, hash_size);
1166 r = (int) hash_size;
1167
1168finish:
1169 gcry_md_close(md);
1170 return r;
1171}
1172
3f5ecaad 1173static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
db5b0e92
LP
1174 const char *a, *b;
1175 int r;
1176
1177 assert(rr);
1178
db5b0e92
LP
1179 if (rr->key->type != DNS_TYPE_NSEC3)
1180 return 0;
1181
1182 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1183 if (!IN_SET(rr->nsec3.flags, 0, 1))
1184 return 0;
1185
d15ad742
LP
1186 /* Ignore NSEC3 RRs whose algorithm we don't know */
1187 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1188 return 0;
a8f158b9
LP
1189 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1190 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1191 return 0;
d15ad742 1192
db5b0e92
LP
1193 if (!nsec3)
1194 return 1;
1195
1196 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1197
1198 if (nsec3 == rr) /* Shortcut */
1199 return 1;
1200
1201 if (rr->key->class != nsec3->key->class)
1202 return 0;
1203 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1204 return 0;
1205 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1206 return 0;
1207 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1208 return 0;
1209 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1210 return 0;
1211
1212 a = DNS_RESOURCE_KEY_NAME(rr->key);
1213 r = dns_name_parent(&a); /* strip off hash */
1214 if (r < 0)
1215 return r;
1216 if (r == 0)
1217 return 0;
1218
1219 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1220 r = dns_name_parent(&b); /* strip off hash */
1221 if (r < 0)
1222 return r;
1223 if (r == 0)
1224 return 0;
1225
1226 return dns_name_equal(a, b);
1227}
1228
cdbffec0
LP
1229static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
1230 _cleanup_free_ char *l = NULL;
1231 char *j;
1232
1233 assert(hashed);
1234 assert(hashed_size > 0);
1235 assert(zone);
1236 assert(ret);
1237
1238 l = base32hexmem(hashed, hashed_size, false);
1239 if (!l)
1240 return -ENOMEM;
1241
1242 j = strjoin(l, ".", zone, NULL);
1243 if (!j)
1244 return -ENOMEM;
1245
1246 *ret = j;
1247 return (int) hashed_size;
1248}
1249
1250static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
105e1512 1251 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1252 int hashed_size;
1253
1254 assert(nsec3);
1255 assert(domain);
1256 assert(zone);
1257 assert(ret);
1258
1259 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1260 if (hashed_size < 0)
1261 return hashed_size;
1262
cdbffec0 1263 return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
6f76ec5a
TG
1264}
1265
35ad41d3
TG
1266/* See RFC 5155, Section 8
1267 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1268 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1269 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1270 * matches the wildcard domain.
1271 *
1272 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1273 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1274 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1275 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1276static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
35ad41d3 1277 _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
d1511b33 1278 const char *zone, *p, *pp = NULL;
7e35195f 1279 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
105e1512
LP
1280 DnsAnswerFlags flags;
1281 int hashed_size, r;
35ad41d3 1282 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1283
1284 assert(key);
1285 assert(result);
1286
d1511b33
TG
1287 /* First step, find the zone name and the NSEC3 parameters of the zone.
1288 * it is sufficient to look for the longest common suffix we find with
1289 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1290 * records from a given zone in a response must use the same
1291 * parameters. */
1292 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1293 for (;;) {
7e35195f 1294 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
3f5ecaad 1295 r = nsec3_is_good(zone_rr, NULL);
db5b0e92
LP
1296 if (r < 0)
1297 return r;
1298 if (r == 0)
13b78323
LP
1299 continue;
1300
7e35195f 1301 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
13b78323
LP
1302 if (r < 0)
1303 return r;
1304 if (r > 0)
d1511b33 1305 goto found_zone;
13b78323
LP
1306 }
1307
1308 /* Strip one label from the front */
d1511b33 1309 r = dns_name_parent(&zone);
13b78323
LP
1310 if (r < 0)
1311 return r;
1312 if (r == 0)
1313 break;
1314 }
1315
1316 *result = DNSSEC_NSEC_NO_RR;
1317 return 0;
1318
d1511b33 1319found_zone:
13b78323 1320 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1321 p = DNS_RESOURCE_KEY_NAME(key);
1322 for (;;) {
6f76ec5a 1323 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1324
cdbffec0 1325 hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
db5b0e92
LP
1326 if (hashed_size == -EOPNOTSUPP) {
1327 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1328 return 0;
1329 }
1330 if (hashed_size < 0)
1331 return hashed_size;
72667f08 1332
d1511b33 1333 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1334
3f5ecaad 1335 r = nsec3_is_good(enclosure_rr, zone_rr);
72667f08
LP
1336 if (r < 0)
1337 return r;
105e1512
LP
1338 if (r == 0)
1339 continue;
1340
d1511b33 1341 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1342 continue;
105e1512 1343
d1511b33 1344 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1345 if (r < 0)
1346 return r;
ed29bfdc
LP
1347 if (r > 0) {
1348 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1349 goto found_closest_encloser;
ed29bfdc 1350 }
105e1512
LP
1351 }
1352
1353 /* We didn't find the closest encloser with this name,
1354 * but let's remember this domain name, it might be
1355 * the next closer name */
1356
1357 pp = p;
1358
1359 /* Strip one label from the front */
1360 r = dns_name_parent(&p);
1361 if (r < 0)
1362 return r;
1363 if (r == 0)
72667f08 1364 break;
105e1512 1365 }
72667f08 1366
105e1512
LP
1367 *result = DNSSEC_NSEC_NO_RR;
1368 return 0;
72667f08 1369
13b78323 1370found_closest_encloser:
105e1512 1371 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1372
105e1512 1373 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1374 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1375 return -EBADMSG;
72667f08 1376
105e1512
LP
1377 /* Ensure that this data is from the delegated domain
1378 * (i.e. originates from the "lower" DNS server), and isn't
1379 * just glue records (i.e. doesn't originate from the "upper"
1380 * DNS server). */
d1511b33
TG
1381 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1382 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1383 return -EBADMSG;
72667f08 1384
105e1512
LP
1385 if (!pp) {
1386 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1387 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1388 *result = DNSSEC_NSEC_FOUND;
1389 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1390 *result = DNSSEC_NSEC_CNAME;
1391 else
1392 *result = DNSSEC_NSEC_NODATA;
1393
d3760be0
LP
1394 if (authenticated)
1395 *authenticated = a;
1396 if (ttl)
1397 *ttl = enclosure_rr->ttl;
146035b3 1398
105e1512
LP
1399 return 0;
1400 }
72667f08 1401
35ad41d3
TG
1402 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1403
1404 wildcard = strappend("*.", p);
1405 if (!wildcard)
1406 return -ENOMEM;
1407
cdbffec0 1408 r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1409 if (r < 0)
1410 return r;
1411 if (r != hashed_size)
1412 return -EBADMSG;
72667f08 1413
cdbffec0 1414 r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1415 if (r < 0)
1416 return r;
1417 if (r != hashed_size)
1418 return -EBADMSG;
72667f08 1419
105e1512 1420 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
cdbffec0 1421 _cleanup_free_ char *next_hashed_domain = NULL;
105e1512 1422
3f5ecaad 1423 r = nsec3_is_good(rr, zone_rr);
105e1512
LP
1424 if (r < 0)
1425 return r;
1426 if (r == 0)
1427 continue;
1428
cdbffec0
LP
1429 r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
1430 if (r < 0)
1431 return r;
105e1512
LP
1432
1433 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1434 if (r < 0)
1435 return r;
1436 if (r > 0) {
1437 if (rr->nsec3.flags & 1)
35ad41d3 1438 optout = true;
105e1512 1439
35ad41d3
TG
1440 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1441
1442 no_closer = true;
1443 }
1444
1445 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1446 if (r < 0)
1447 return r;
1448 if (r > 0) {
1449 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1450
1451 wildcard_rr = rr;
1452 }
1453
1454 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1455 if (r < 0)
1456 return r;
1457 if (r > 0) {
1458 if (rr->nsec3.flags & 1)
1459 /* This only makes sense if we have a wildcard delegation, which is
1460 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1461 * this not happening, so hence cannot simply conclude NXDOMAIN as
1462 * we would wish */
1463 optout = true;
1464
1465 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1466
1467 no_wildcard = true;
105e1512
LP
1468 }
1469 }
1470
35ad41d3
TG
1471 if (wildcard_rr && no_wildcard)
1472 return -EBADMSG;
1473
1474 if (!no_closer) {
1475 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1476 return 0;
1477 }
1478
1479 if (wildcard_rr) {
1480 /* A wildcard exists that matches our query. */
1481 if (optout)
1482 /* This is not specified in any RFC to the best of my knowledge, but
1483 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1484 * it means that we cannot prove that the source of synthesis is
1485 * correct, as there may be a closer match. */
1486 *result = DNSSEC_NSEC_OPTOUT;
1487 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1488 *result = DNSSEC_NSEC_FOUND;
1489 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1490 *result = DNSSEC_NSEC_CNAME;
1491 else
1492 *result = DNSSEC_NSEC_NODATA;
1493 } else {
1494 if (optout)
1495 /* The RFC only specifies that we have to care for optout for NODATA for
1496 * DS records. However, children of an insecure opt-out delegation should
1497 * also be considered opt-out, rather than verified NXDOMAIN.
1498 * Note that we do not require a proof of wildcard non-existence if the
1499 * next closer domain is covered by an opt-out, as that would not provide
1500 * any additional information. */
1501 *result = DNSSEC_NSEC_OPTOUT;
1502 else if (no_wildcard)
1503 *result = DNSSEC_NSEC_NXDOMAIN;
1504 else {
1505 *result = DNSSEC_NSEC_NO_RR;
1506
1507 return 0;
1508 }
1509 }
1510
d3760be0
LP
1511 if (authenticated)
1512 *authenticated = a;
1513
1514 if (ttl)
1515 *ttl = enclosure_rr->ttl;
35ad41d3 1516
105e1512
LP
1517 return 0;
1518}
1519
0c7bff0a 1520int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
105e1512
LP
1521 DnsResourceRecord *rr;
1522 bool have_nsec3 = false;
1523 DnsAnswerFlags flags;
1524 int r;
1525
1526 assert(key);
1527 assert(result);
1528
1529 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1530
1531 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1532
1533 if (rr->key->class != key->class)
1534 continue;
1535
105e1512
LP
1536 switch (rr->key->type) {
1537
1538 case DNS_TYPE_NSEC:
1539
1540 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1541 if (r < 0)
1542 return r;
1543 if (r > 0) {
146035b3
TG
1544 if (bitmap_isset(rr->nsec.types, key->type))
1545 *result = DNSSEC_NSEC_FOUND;
1546 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1547 *result = DNSSEC_NSEC_CNAME;
1548 else
1549 *result = DNSSEC_NSEC_NODATA;
d3760be0
LP
1550
1551 if (authenticated)
1552 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1553 if (ttl)
1554 *ttl = rr->ttl;
1555
72667f08
LP
1556 return 0;
1557 }
1558
105e1512
LP
1559 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1560 if (r < 0)
1561 return r;
1562 if (r > 0) {
1563 *result = DNSSEC_NSEC_NXDOMAIN;
d3760be0
LP
1564
1565 if (authenticated)
1566 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1567 if (ttl)
1568 *ttl = rr->ttl;
1569
105e1512
LP
1570 return 0;
1571 }
72667f08 1572 break;
72667f08 1573
105e1512
LP
1574 case DNS_TYPE_NSEC3:
1575 have_nsec3 = true;
72667f08
LP
1576 break;
1577 }
1578 }
1579
105e1512
LP
1580 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1581 if (have_nsec3)
d3760be0 1582 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 1583
72667f08
LP
1584 /* No approproate NSEC RR found, report this. */
1585 *result = DNSSEC_NSEC_NO_RR;
1586 return 0;
1587}
1588
0c7bff0a
LP
1589int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated) {
1590 DnsResourceRecord *rr;
1591 DnsAnswerFlags flags;
1592 int r;
1593
1594 assert(name);
1595 assert(zone);
1596
1597 /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
1598 * 'zone'. The 'zone' must be a suffix of the 'name'. */
1599
1600 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1601 bool found = false;
1602
1603 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
1604 if (r < 0)
1605 return r;
1606 if (r == 0)
1607 continue;
1608
1609 switch (rr->key->type) {
1610
1611 case DNS_TYPE_NSEC:
1612 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
1613 if (r < 0)
1614 return r;
1615
1616 found = r > 0;
1617 break;
1618
1619 case DNS_TYPE_NSEC3: {
1620 _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
1621
1622 r = nsec3_is_good(rr, NULL);
1623 if (r < 0)
1624 return r;
1625 if (r == 0)
1626 break;
1627
1628 /* Format the domain we are testing with the NSEC3 RR's hash function */
1629 r = nsec3_hashed_domain_make(
1630 rr,
1631 name,
1632 zone,
1633 &hashed_domain);
1634 if (r < 0)
1635 return r;
1636 if ((size_t) r != rr->nsec3.next_hashed_name_size)
1637 break;
1638
1639 /* Format the NSEC3's next hashed name as proper domain name */
1640 r = nsec3_hashed_domain_format(
1641 rr->nsec3.next_hashed_name,
1642 rr->nsec3.next_hashed_name_size,
1643 zone,
1644 &next_hashed_domain);
1645 if (r < 0)
1646 return r;
1647
1648 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
1649 if (r < 0)
1650 return r;
1651
1652 found = r > 0;
1653 break;
1654 }
1655
1656 default:
1657 continue;
1658 }
1659
1660 if (found) {
1661 if (authenticated)
1662 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1663 return 1;
1664 }
1665 }
1666
1667 return 0;
1668}
1669
547973de
LP
1670static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1671 [DNSSEC_VALIDATED] = "validated",
0c7bff0a 1672 [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
547973de 1673 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1674 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1675 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1676 [DNSSEC_NO_SIGNATURE] = "no-signature",
1677 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1678 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1679 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1680 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1681 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1682};
1683DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);