]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: remove one level of indentation in dns_transaction_validate_dnssec()
[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;
2b442ac8 515 size_t k, n = 0;
2b442ac8
LP
516
517 assert(key);
518 assert(rrsig);
519 assert(dnskey);
547973de 520 assert(result);
2a326321
LP
521 assert(rrsig->key->type == DNS_TYPE_RRSIG);
522 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
523
524 /* Verifies the the RRSet matching the specified "key" in "a",
525 * using the signature "rrsig" and the key "dnskey". It's
526 * assumed the RRSIG and DNSKEY match. */
527
ca994e85
LP
528 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
529 if (md_algorithm == -EOPNOTSUPP) {
203f1b35
LP
530 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
531 return 0;
532 }
ca994e85
LP
533 if (md_algorithm < 0)
534 return md_algorithm;
2b442ac8 535
2a326321
LP
536 r = dnssec_rrsig_expired(rrsig, realtime);
537 if (r < 0)
538 return r;
547973de
LP
539 if (r > 0) {
540 *result = DNSSEC_SIGNATURE_EXPIRED;
541 return 0;
542 }
2a326321 543
2b442ac8
LP
544 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
545 list = newa(DnsResourceRecord *, a->n_rrs);
546
547 DNS_ANSWER_FOREACH(rr, a) {
548 r = dns_resource_key_equal(key, rr->key);
549 if (r < 0)
550 return r;
551 if (r == 0)
552 continue;
553
554 /* We need the wire format for ordering, and digest calculation */
555 r = dns_resource_record_to_wire_format(rr, true);
556 if (r < 0)
557 return r;
558
559 list[n++] = rr;
935a999f
TG
560
561 if (n > VERIFY_RRS_MAX)
562 return -E2BIG;
2b442ac8
LP
563 }
564
565 if (n <= 0)
566 return -ENODATA;
567
568 /* Bring the RRs into canonical order */
6c5e8fbf 569 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
570
571 /* OK, the RRs are now in canonical order. Let's calculate the digest */
fbf1a66d 572 initialize_libgcrypt();
2b442ac8 573
ca994e85 574 hash_size = gcry_md_get_algo_dlen(md_algorithm);
fbf1a66d 575 assert(hash_size > 0);
2b442ac8 576
ca994e85 577 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
578 if (!md)
579 return -EIO;
580
581 md_add_uint16(md, rrsig->rrsig.type_covered);
582 md_add_uint8(md, rrsig->rrsig.algorithm);
583 md_add_uint8(md, rrsig->rrsig.labels);
584 md_add_uint32(md, rrsig->rrsig.original_ttl);
585 md_add_uint32(md, rrsig->rrsig.expiration);
586 md_add_uint32(md, rrsig->rrsig.inception);
587 md_add_uint16(md, rrsig->rrsig.key_tag);
588
589 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
590 if (r < 0)
591 goto finish;
592 gcry_md_write(md, wire_format_name, r);
593
594 for (k = 0; k < n; k++) {
e7ff0e0b 595 const char *suffix;
2b442ac8
LP
596 size_t l;
597 rr = list[k];
598
e7ff0e0b
LP
599 r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
600 if (r < 0)
601 goto finish;
602 if (r > 0) /* This is a wildcard! */
603 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
604
605 r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
2b442ac8
LP
606 if (r < 0)
607 goto finish;
608 gcry_md_write(md, wire_format_name, r);
609
610 md_add_uint16(md, rr->key->type);
611 md_add_uint16(md, rr->key->class);
612 md_add_uint32(md, rrsig->rrsig.original_ttl);
613
85aeaccc 614 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
2b442ac8
LP
615 assert(l <= 0xFFFF);
616
617 md_add_uint16(md, (uint16_t) l);
85aeaccc 618 gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
2b442ac8
LP
619 }
620
621 hash = gcry_md_read(md, 0);
622 if (!hash) {
623 r = -EIO;
624 goto finish;
625 }
626
e0240c64
LP
627 switch (rrsig->rrsig.algorithm) {
628
629 case DNSSEC_ALGORITHM_RSASHA1:
630 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
631 case DNSSEC_ALGORITHM_RSASHA256:
632 case DNSSEC_ALGORITHM_RSASHA512:
633 r = dnssec_rsa_verify(
ca994e85 634 gcry_md_algo_name(md_algorithm),
e0240c64
LP
635 hash, hash_size,
636 rrsig,
637 dnskey);
638 break;
639
640 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
641 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
642 r = dnssec_ecdsa_verify(
ca994e85 643 gcry_md_algo_name(md_algorithm),
e0240c64
LP
644 rrsig->rrsig.algorithm,
645 hash, hash_size,
646 rrsig,
647 dnskey);
648 break;
649 }
650
2b442ac8
LP
651 if (r < 0)
652 goto finish;
653
547973de
LP
654 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
655 r = 0;
2b442ac8
LP
656
657finish:
658 gcry_md_close(md);
659 return r;
660}
661
0c857028 662int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
663
664 assert(rrsig);
665 assert(dnskey);
666
667 /* Checks if the specified DNSKEY RR matches the key used for
668 * the signature in the specified RRSIG RR */
669
670 if (rrsig->key->type != DNS_TYPE_RRSIG)
671 return -EINVAL;
672
673 if (dnskey->key->type != DNS_TYPE_DNSKEY)
674 return 0;
675 if (dnskey->key->class != rrsig->key->class)
676 return 0;
677 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
678 return 0;
0c857028 679 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 680 return 0;
2b442ac8
LP
681 if (dnskey->dnskey.protocol != 3)
682 return 0;
683 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
684 return 0;
685
0c857028 686 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
687 return 0;
688
15accc27 689 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
690}
691
105e1512 692int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
e7ff0e0b
LP
693 int r;
694
2b442ac8
LP
695 assert(key);
696 assert(rrsig);
697
698 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
699
700 if (rrsig->key->type != DNS_TYPE_RRSIG)
701 return 0;
702 if (rrsig->key->class != key->class)
703 return 0;
704 if (rrsig->rrsig.type_covered != key->type)
705 return 0;
706
e7ff0e0b
LP
707 /* Make sure signer is a parent of the RRset */
708 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
709 if (r <= 0)
710 return r;
711
712 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
713 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
714 if (r < 0)
715 return r;
716 if (r < rrsig->rrsig.labels)
717 return 0;
718
2b442ac8
LP
719 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
720}
721
ee3d6aff
LP
722static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
723 DnsResourceRecord *rr;
724 int r;
725
726 assert(key);
727 assert(rrsig);
728
729 DNS_ANSWER_FOREACH(rr, a) {
730 r = dns_resource_key_equal(key, rr->key);
731 if (r < 0)
732 return r;
733 if (r == 0)
734 continue;
735
736 /* Pick the TTL as the minimum of the RR's TTL, the
737 * RR's original TTL according to the RRSIG and the
738 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
739 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
740 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
741 }
742
743 return 0;
744}
745
2a326321
LP
746int dnssec_verify_rrset_search(
747 DnsAnswer *a,
0c857028 748 const DnsResourceKey *key,
2a326321 749 DnsAnswer *validated_dnskeys,
547973de
LP
750 usec_t realtime,
751 DnssecResult *result) {
2a326321 752
203f1b35 753 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
754 DnsResourceRecord *rrsig;
755 int r;
756
757 assert(key);
547973de 758 assert(result);
2b442ac8 759
105e1512 760 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
761
762 if (!a || a->n_rrs <= 0)
763 return -ENODATA;
764
765 /* Iterate through each RRSIG RR. */
766 DNS_ANSWER_FOREACH(rrsig, a) {
767 DnsResourceRecord *dnskey;
105e1512 768 DnsAnswerFlags flags;
2b442ac8 769
203f1b35 770 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
771 r = dnssec_key_match_rrsig(key, rrsig);
772 if (r < 0)
773 return r;
774 if (r == 0)
775 continue;
776
777 found_rrsig = true;
778
547973de 779 /* Look for a matching key */
105e1512 780 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 781 DnssecResult one_result;
2b442ac8 782
105e1512
LP
783 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
784 continue;
785
203f1b35 786 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
0c857028 787 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
788 if (r < 0)
789 return r;
790 if (r == 0)
791 continue;
792
2a326321
LP
793 /* Take the time here, if it isn't set yet, so
794 * that we do all validations with the same
795 * time. */
796 if (realtime == USEC_INFINITY)
797 realtime = now(CLOCK_REALTIME);
798
2b442ac8
LP
799 /* Yay, we found a matching RRSIG with a matching
800 * DNSKEY, awesome. Now let's verify all entries of
801 * the RRSet against the RRSIG and DNSKEY
802 * combination. */
803
547973de 804 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 805 if (r < 0)
2b442ac8 806 return r;
203f1b35
LP
807
808 switch (one_result) {
809
810 case DNSSEC_VALIDATED:
811 /* Yay, the RR has been validated,
ee3d6aff
LP
812 * return immediately, but fix up the expiry */
813 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
814 if (r < 0)
815 return r;
816
547973de
LP
817 *result = DNSSEC_VALIDATED;
818 return 0;
2b442ac8 819
203f1b35
LP
820 case DNSSEC_INVALID:
821 /* If the signature is invalid, let's try another
822 key and/or signature. After all they
823 key_tags and stuff are not unique, and
824 might be shared by multiple keys. */
825 found_invalid = true;
826 continue;
827
828 case DNSSEC_UNSUPPORTED_ALGORITHM:
829 /* If the key algorithm is
830 unsupported, try another
831 RRSIG/DNSKEY pair, but remember we
832 encountered this, so that we can
833 return a proper error when we
834 encounter nothing better. */
835 found_unsupported_algorithm = true;
836 continue;
837
838 case DNSSEC_SIGNATURE_EXPIRED:
839 /* If the signature is expired, try
840 another one, but remember it, so
841 that we can return this */
842 found_expired_rrsig = true;
843 continue;
844
845 default:
846 assert_not_reached("Unexpected DNSSEC validation result");
847 }
2b442ac8
LP
848 }
849 }
850
203f1b35
LP
851 if (found_expired_rrsig)
852 *result = DNSSEC_SIGNATURE_EXPIRED;
853 else if (found_unsupported_algorithm)
854 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
855 else if (found_invalid)
547973de
LP
856 *result = DNSSEC_INVALID;
857 else if (found_rrsig)
858 *result = DNSSEC_MISSING_KEY;
859 else
860 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 861
547973de 862 return 0;
2b442ac8
LP
863}
864
105e1512
LP
865int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
866 DnsResourceRecord *rr;
867 int r;
868
869 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
870
871 DNS_ANSWER_FOREACH(rr, a) {
872 r = dnssec_key_match_rrsig(key, rr);
873 if (r < 0)
874 return r;
875 if (r > 0)
876 return 1;
877 }
878
879 return 0;
880}
881
2b442ac8 882int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
883 size_t c = 0;
884 int r;
885
886 /* Converts the specified hostname into DNSSEC canonicalized
887 * form. */
888
889 if (buffer_max < 2)
890 return -ENOBUFS;
891
892 for (;;) {
893 size_t i;
894
895 r = dns_label_unescape(&n, buffer, buffer_max);
896 if (r < 0)
897 return r;
898 if (r == 0)
899 break;
900 if (r > 0) {
901 int k;
902
903 /* DNSSEC validation is always done on the ASCII version of the label */
904 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
905 if (k < 0)
906 return k;
907 if (k > 0)
908 r = k;
909 }
910
911 if (buffer_max < (size_t) r + 2)
912 return -ENOBUFS;
913
914 /* The DNSSEC canonical form is not clear on what to
915 * do with dots appearing in labels, the way DNS-SD
916 * does it. Refuse it for now. */
917
918 if (memchr(buffer, '.', r))
919 return -EINVAL;
920
921 for (i = 0; i < (size_t) r; i ++) {
922 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
923 buffer[i] = buffer[i] - 'A' + 'a';
924 }
925
926 buffer[r] = '.';
927
928 buffer += r + 1;
929 c += r + 1;
930
931 buffer_max -= r + 1;
932 }
933
934 if (c <= 0) {
935 /* Not even a single label: this is the root domain name */
936
937 assert(buffer_max > 2);
938 buffer[0] = '.';
939 buffer[1] = 0;
940
941 return 1;
942 }
943
944 return (int) c;
945}
946
ca994e85 947static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 948
fbf1a66d 949 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
950
951 switch (algorithm) {
952
953 case DNSSEC_DIGEST_SHA1:
954 return GCRY_MD_SHA1;
955
956 case DNSSEC_DIGEST_SHA256:
957 return GCRY_MD_SHA256;
958
af22c65b
LP
959 case DNSSEC_DIGEST_SHA384:
960 return GCRY_MD_SHA384;
961
a1972a91
LP
962 default:
963 return -EOPNOTSUPP;
964 }
965}
966
0c857028 967int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
2b442ac8 968 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
969 gcry_md_hd_t md = NULL;
970 size_t hash_size;
ca994e85 971 int md_algorithm, r;
2b442ac8 972 void *result;
2b442ac8
LP
973
974 assert(dnskey);
975 assert(ds);
976
977 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
978
979 if (dnskey->key->type != DNS_TYPE_DNSKEY)
980 return -EINVAL;
981 if (ds->key->type != DNS_TYPE_DS)
982 return -EINVAL;
983 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
984 return -EKEYREJECTED;
0c857028
LP
985 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
986 return -EKEYREJECTED;
2b442ac8
LP
987 if (dnskey->dnskey.protocol != 3)
988 return -EKEYREJECTED;
989
2b442ac8
LP
990 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
991 return 0;
0c857028 992 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
993 return 0;
994
0638401a
LP
995 initialize_libgcrypt();
996
ca994e85
LP
997 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
998 if (md_algorithm < 0)
999 return md_algorithm;
2b442ac8 1000
ca994e85 1001 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 1002 assert(hash_size > 0);
2b442ac8 1003
a1972a91
LP
1004 if (ds->ds.digest_size != hash_size)
1005 return 0;
2b442ac8 1006
a1972a91
LP
1007 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1008 if (r < 0)
1009 return r;
2b442ac8 1010
ca994e85 1011 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1012 if (!md)
1013 return -EIO;
1014
2b442ac8 1015 gcry_md_write(md, owner_name, r);
0c857028
LP
1016 if (mask_revoke)
1017 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1018 else
1019 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1020 md_add_uint8(md, dnskey->dnskey.protocol);
1021 md_add_uint8(md, dnskey->dnskey.algorithm);
1022 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1023
1024 result = gcry_md_read(md, 0);
1025 if (!result) {
1026 r = -EIO;
1027 goto finish;
1028 }
1029
1030 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1031
1032finish:
1033 gcry_md_close(md);
1034 return r;
1035}
24710c48 1036
547973de
LP
1037int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1038 DnsResourceRecord *ds;
105e1512 1039 DnsAnswerFlags flags;
547973de
LP
1040 int r;
1041
1042 assert(dnskey);
1043
1044 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1045 return 0;
1046
105e1512
LP
1047 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1048
1049 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1050 continue;
547973de
LP
1051
1052 if (ds->key->type != DNS_TYPE_DS)
1053 continue;
1054
d1c4ee32
LP
1055 if (ds->key->class != dnskey->key->class)
1056 continue;
1057
1058 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1059 if (r < 0)
1060 return r;
1061 if (r == 0)
1062 continue;
1063
0c857028
LP
1064 r = dnssec_verify_dnskey(dnskey, ds, false);
1065 if (r == -EKEYREJECTED)
1066 return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
547973de
LP
1067 if (r < 0)
1068 return r;
1069 if (r > 0)
1070 return 1;
1071 }
1072
1073 return 0;
1074}
1075
d15ad742
LP
1076static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1077
1078 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1079
1080 switch (algorithm) {
1081
1082 case NSEC3_ALGORITHM_SHA1:
1083 return GCRY_MD_SHA1;
1084
1085 default:
1086 return -EOPNOTSUPP;
1087 }
1088}
1089
1d3db294 1090int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
72667f08
LP
1091 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1092 gcry_md_hd_t md = NULL;
1093 size_t hash_size;
1094 int algorithm;
1095 void *result;
1096 unsigned k;
1097 int r;
1098
1099 assert(nsec3);
1100 assert(name);
1101 assert(ret);
1102
1103 if (nsec3->key->type != DNS_TYPE_NSEC3)
1104 return -EINVAL;
1105
1d3db294
LP
1106 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1107 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1108 return -EOPNOTSUPP;
1d3db294 1109 }
a8f158b9 1110
d15ad742 1111 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1112 if (algorithm < 0)
1113 return algorithm;
1114
1115 initialize_libgcrypt();
1116
1117 hash_size = gcry_md_get_algo_dlen(algorithm);
1118 assert(hash_size > 0);
1119
1120 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1121 return -EINVAL;
1122
1123 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1124 if (r < 0)
1125 return r;
1126
1127 gcry_md_open(&md, algorithm, 0);
1128 if (!md)
1129 return -EIO;
1130
1131 gcry_md_write(md, wire_format, r);
1132 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1133
1134 result = gcry_md_read(md, 0);
1135 if (!result) {
1136 r = -EIO;
1137 goto finish;
1138 }
1139
1140 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1141 uint8_t tmp[hash_size];
1142 memcpy(tmp, result, hash_size);
1143
1144 gcry_md_reset(md);
1145 gcry_md_write(md, tmp, hash_size);
1146 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1147
1148 result = gcry_md_read(md, 0);
1149 if (!result) {
1150 r = -EIO;
1151 goto finish;
1152 }
1153 }
1154
1155 memcpy(ret, result, hash_size);
1156 r = (int) hash_size;
1157
1158finish:
1159 gcry_md_close(md);
1160 return r;
1161}
1162
db5b0e92
LP
1163static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
1164 const char *a, *b;
1165 int r;
1166
1167 assert(rr);
1168
db5b0e92
LP
1169 if (rr->key->type != DNS_TYPE_NSEC3)
1170 return 0;
1171
1172 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1173 if (!IN_SET(rr->nsec3.flags, 0, 1))
1174 return 0;
1175
d15ad742
LP
1176 /* Ignore NSEC3 RRs whose algorithm we don't know */
1177 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1178 return 0;
a8f158b9
LP
1179 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1180 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1181 return 0;
d15ad742 1182
db5b0e92
LP
1183 if (!nsec3)
1184 return 1;
1185
1186 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1187
1188 if (nsec3 == rr) /* Shortcut */
1189 return 1;
1190
1191 if (rr->key->class != nsec3->key->class)
1192 return 0;
1193 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1194 return 0;
1195 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1196 return 0;
1197 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1198 return 0;
1199 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1200 return 0;
1201
1202 a = DNS_RESOURCE_KEY_NAME(rr->key);
1203 r = dns_name_parent(&a); /* strip off hash */
1204 if (r < 0)
1205 return r;
1206 if (r == 0)
1207 return 0;
1208
1209 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1210 r = dns_name_parent(&b); /* strip off hash */
1211 if (r < 0)
1212 return r;
1213 if (r == 0)
1214 return 0;
1215
1216 return dns_name_equal(a, b);
1217}
1218
1d3db294 1219static int nsec3_hashed_domain(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
6f76ec5a 1220 _cleanup_free_ char *l = NULL, *hashed_domain = NULL;
105e1512 1221 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1222 int hashed_size;
1223
1224 assert(nsec3);
1225 assert(domain);
1226 assert(zone);
1227 assert(ret);
1228
1229 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1230 if (hashed_size < 0)
1231 return hashed_size;
1232
1233 l = base32hexmem(hashed, hashed_size, false);
1234 if (!l)
1235 return -ENOMEM;
1236
1237 hashed_domain = strjoin(l, ".", zone, NULL);
1238 if (!hashed_domain)
1239 return -ENOMEM;
1240
1241 *ret = hashed_domain;
1242 hashed_domain = NULL;
1243
1244 return hashed_size;
1245}
1246
35ad41d3
TG
1247/* See RFC 5155, Section 8
1248 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1249 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1250 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1251 * matches the wildcard domain.
1252 *
1253 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1254 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1255 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1256 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1257static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
35ad41d3 1258 _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
d1511b33 1259 const char *zone, *p, *pp = NULL;
7e35195f 1260 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
105e1512
LP
1261 DnsAnswerFlags flags;
1262 int hashed_size, r;
35ad41d3 1263 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1264
1265 assert(key);
1266 assert(result);
1267
d1511b33
TG
1268 /* First step, find the zone name and the NSEC3 parameters of the zone.
1269 * it is sufficient to look for the longest common suffix we find with
1270 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1271 * records from a given zone in a response must use the same
1272 * parameters. */
1273 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1274 for (;;) {
7e35195f
LP
1275 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
1276 r = nsec3_is_good(zone_rr, flags, NULL);
db5b0e92
LP
1277 if (r < 0)
1278 return r;
1279 if (r == 0)
13b78323
LP
1280 continue;
1281
7e35195f 1282 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
13b78323
LP
1283 if (r < 0)
1284 return r;
1285 if (r > 0)
d1511b33 1286 goto found_zone;
13b78323
LP
1287 }
1288
1289 /* Strip one label from the front */
d1511b33 1290 r = dns_name_parent(&zone);
13b78323
LP
1291 if (r < 0)
1292 return r;
1293 if (r == 0)
1294 break;
1295 }
1296
1297 *result = DNSSEC_NSEC_NO_RR;
1298 return 0;
1299
d1511b33 1300found_zone:
13b78323 1301 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1302 p = DNS_RESOURCE_KEY_NAME(key);
1303 for (;;) {
6f76ec5a 1304 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1305
7e35195f 1306 hashed_size = nsec3_hashed_domain(zone_rr, p, zone, &hashed_domain);
db5b0e92
LP
1307 if (hashed_size == -EOPNOTSUPP) {
1308 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1309 return 0;
1310 }
1311 if (hashed_size < 0)
1312 return hashed_size;
72667f08 1313
d1511b33 1314 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1315
7e35195f 1316 r = nsec3_is_good(enclosure_rr, flags, zone_rr);
72667f08
LP
1317 if (r < 0)
1318 return r;
105e1512
LP
1319 if (r == 0)
1320 continue;
1321
d1511b33 1322 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1323 continue;
105e1512 1324
d1511b33 1325 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1326 if (r < 0)
1327 return r;
ed29bfdc
LP
1328 if (r > 0) {
1329 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1330 goto found_closest_encloser;
ed29bfdc 1331 }
105e1512
LP
1332 }
1333
1334 /* We didn't find the closest encloser with this name,
1335 * but let's remember this domain name, it might be
1336 * the next closer name */
1337
1338 pp = p;
1339
1340 /* Strip one label from the front */
1341 r = dns_name_parent(&p);
1342 if (r < 0)
1343 return r;
1344 if (r == 0)
72667f08 1345 break;
105e1512 1346 }
72667f08 1347
105e1512
LP
1348 *result = DNSSEC_NSEC_NO_RR;
1349 return 0;
72667f08 1350
13b78323 1351found_closest_encloser:
105e1512 1352 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1353
105e1512 1354 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1355 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1356 return -EBADMSG;
72667f08 1357
105e1512
LP
1358 /* Ensure that this data is from the delegated domain
1359 * (i.e. originates from the "lower" DNS server), and isn't
1360 * just glue records (i.e. doesn't originate from the "upper"
1361 * DNS server). */
d1511b33
TG
1362 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1363 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1364 return -EBADMSG;
72667f08 1365
105e1512
LP
1366 if (!pp) {
1367 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1368 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1369 *result = DNSSEC_NSEC_FOUND;
1370 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1371 *result = DNSSEC_NSEC_CNAME;
1372 else
1373 *result = DNSSEC_NSEC_NODATA;
1374
d3760be0
LP
1375 if (authenticated)
1376 *authenticated = a;
1377 if (ttl)
1378 *ttl = enclosure_rr->ttl;
146035b3 1379
105e1512
LP
1380 return 0;
1381 }
72667f08 1382
35ad41d3
TG
1383 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1384
1385 wildcard = strappend("*.", p);
1386 if (!wildcard)
1387 return -ENOMEM;
1388
1389 r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1390 if (r < 0)
1391 return r;
1392 if (r != hashed_size)
1393 return -EBADMSG;
72667f08 1394
6f76ec5a 1395 r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1396 if (r < 0)
1397 return r;
1398 if (r != hashed_size)
1399 return -EBADMSG;
72667f08 1400
105e1512
LP
1401 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1402 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
105e1512 1403
7e35195f 1404 r = nsec3_is_good(rr, flags, zone_rr);
105e1512
LP
1405 if (r < 0)
1406 return r;
1407 if (r == 0)
1408 continue;
1409
1410 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1411 if (!label)
1412 return -ENOMEM;
1413
b2c2a1b9 1414 next_hashed_domain = strjoin(label, ".", zone, NULL);
105e1512
LP
1415 if (!next_hashed_domain)
1416 return -ENOMEM;
1417
1418 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1419 if (r < 0)
1420 return r;
1421 if (r > 0) {
1422 if (rr->nsec3.flags & 1)
35ad41d3 1423 optout = true;
105e1512 1424
35ad41d3
TG
1425 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1426
1427 no_closer = true;
1428 }
1429
1430 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1431 if (r < 0)
1432 return r;
1433 if (r > 0) {
1434 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1435
1436 wildcard_rr = rr;
1437 }
1438
1439 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1440 if (r < 0)
1441 return r;
1442 if (r > 0) {
1443 if (rr->nsec3.flags & 1)
1444 /* This only makes sense if we have a wildcard delegation, which is
1445 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1446 * this not happening, so hence cannot simply conclude NXDOMAIN as
1447 * we would wish */
1448 optout = true;
1449
1450 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1451
1452 no_wildcard = true;
105e1512
LP
1453 }
1454 }
1455
35ad41d3
TG
1456 if (wildcard_rr && no_wildcard)
1457 return -EBADMSG;
1458
1459 if (!no_closer) {
1460 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1461 return 0;
1462 }
1463
1464 if (wildcard_rr) {
1465 /* A wildcard exists that matches our query. */
1466 if (optout)
1467 /* This is not specified in any RFC to the best of my knowledge, but
1468 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1469 * it means that we cannot prove that the source of synthesis is
1470 * correct, as there may be a closer match. */
1471 *result = DNSSEC_NSEC_OPTOUT;
1472 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1473 *result = DNSSEC_NSEC_FOUND;
1474 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1475 *result = DNSSEC_NSEC_CNAME;
1476 else
1477 *result = DNSSEC_NSEC_NODATA;
1478 } else {
1479 if (optout)
1480 /* The RFC only specifies that we have to care for optout for NODATA for
1481 * DS records. However, children of an insecure opt-out delegation should
1482 * also be considered opt-out, rather than verified NXDOMAIN.
1483 * Note that we do not require a proof of wildcard non-existence if the
1484 * next closer domain is covered by an opt-out, as that would not provide
1485 * any additional information. */
1486 *result = DNSSEC_NSEC_OPTOUT;
1487 else if (no_wildcard)
1488 *result = DNSSEC_NSEC_NXDOMAIN;
1489 else {
1490 *result = DNSSEC_NSEC_NO_RR;
1491
1492 return 0;
1493 }
1494 }
1495
d3760be0
LP
1496 if (authenticated)
1497 *authenticated = a;
1498
1499 if (ttl)
1500 *ttl = enclosure_rr->ttl;
35ad41d3 1501
105e1512
LP
1502 return 0;
1503}
1504
d3760be0 1505int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
105e1512
LP
1506 DnsResourceRecord *rr;
1507 bool have_nsec3 = false;
1508 DnsAnswerFlags flags;
1509 int r;
1510
1511 assert(key);
1512 assert(result);
1513
1514 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1515
1516 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1517
1518 if (rr->key->class != key->class)
1519 continue;
1520
105e1512
LP
1521 switch (rr->key->type) {
1522
1523 case DNS_TYPE_NSEC:
1524
1525 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1526 if (r < 0)
1527 return r;
1528 if (r > 0) {
146035b3
TG
1529 if (bitmap_isset(rr->nsec.types, key->type))
1530 *result = DNSSEC_NSEC_FOUND;
1531 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1532 *result = DNSSEC_NSEC_CNAME;
1533 else
1534 *result = DNSSEC_NSEC_NODATA;
d3760be0
LP
1535
1536 if (authenticated)
1537 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1538 if (ttl)
1539 *ttl = rr->ttl;
1540
72667f08
LP
1541 return 0;
1542 }
1543
105e1512
LP
1544 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1545 if (r < 0)
1546 return r;
1547 if (r > 0) {
1548 *result = DNSSEC_NSEC_NXDOMAIN;
d3760be0
LP
1549
1550 if (authenticated)
1551 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1552 if (ttl)
1553 *ttl = rr->ttl;
1554
105e1512
LP
1555 return 0;
1556 }
72667f08 1557 break;
72667f08 1558
105e1512
LP
1559 case DNS_TYPE_NSEC3:
1560 have_nsec3 = true;
72667f08
LP
1561 break;
1562 }
1563 }
1564
105e1512
LP
1565 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1566 if (have_nsec3)
d3760be0 1567 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 1568
72667f08
LP
1569 /* No approproate NSEC RR found, report this. */
1570 *result = DNSSEC_NSEC_NO_RR;
1571 return 0;
1572}
1573
547973de
LP
1574static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1575 [DNSSEC_VALIDATED] = "validated",
1576 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1577 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1578 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1579 [DNSSEC_NO_SIGNATURE] = "no-signature",
1580 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1581 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1582 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1583 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1584 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1585};
1586DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);