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