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