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