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