1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
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.
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.
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/>.
24 #include "alloc-util.h"
25 #include "dns-domain.h"
26 #include "resolved-dns-dnssec.h"
27 #include "resolved-dns-packet.h"
28 #include "string-table.h"
32 * How does the DNSSEC canonical form of a hostname with a label
33 * containing a dot look like, the way DNS-SD does it?
37 * - Iterative validation
38 * - NSEC proof of non-existance
39 * - NSEC3 proof of non-existance
40 * - Make trust anchor store read additional DS+DNSKEY data from disk
41 * - wildcard zones compatibility
42 * - multi-label zone compatibility
43 * - DNSSEC cname/dname compatibility
44 * - per-interface DNSSEC setting
50 #define VERIFY_RRS_MAX 256
51 #define MAX_KEY_SIZE (32*1024)
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)
57 * The DNSSEC Chain of trust:
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
64 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
67 static bool dnssec_algorithm_supported(int algorithm
) {
68 return IN_SET(algorithm
,
69 DNSSEC_ALGORITHM_RSASHA1
,
70 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
,
71 DNSSEC_ALGORITHM_RSASHA256
,
72 DNSSEC_ALGORITHM_RSASHA512
);
75 static bool dnssec_digest_supported(int digest
) {
78 DNSSEC_DIGEST_SHA256
);
81 uint16_t dnssec_keytag(DnsResourceRecord
*dnskey
) {
86 /* The algorithm from RFC 4034, Appendix B. */
89 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
91 sum
= (uint32_t) dnskey
->dnskey
.flags
+
92 ((((uint32_t) dnskey
->dnskey
.protocol
) << 8) + (uint32_t) dnskey
->dnskey
.algorithm
);
94 p
= dnskey
->dnskey
.key
;
96 for (i
= 0; i
< dnskey
->dnskey
.key_size
; i
++)
97 sum
+= (i
& 1) == 0 ? (uint32_t) p
[i
] << 8 : (uint32_t) p
[i
];
99 sum
+= (sum
>> 16) & UINT32_C(0xFFFF);
101 return sum
& UINT32_C(0xFFFF);
104 static int rr_compare(const void *a
, const void *b
) {
105 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
109 /* Let's order the RRs according to RFC 4034, Section 6.3 */
113 assert((*x
)->wire_format
);
116 assert((*y
)->wire_format
);
118 m
= MIN((*x
)->wire_format_size
, (*y
)->wire_format_size
);
120 r
= memcmp((*x
)->wire_format
, (*y
)->wire_format
, m
);
124 if ((*x
)->wire_format_size
< (*y
)->wire_format_size
)
126 else if ((*x
)->wire_format_size
> (*y
)->wire_format_size
)
132 static int dnssec_rsa_verify(
133 const char *hash_algorithm
,
134 const void *signature
, size_t signature_size
,
135 const void *data
, size_t data_size
,
136 const void *exponent
, size_t exponent_size
,
137 const void *modulus
, size_t modulus_size
) {
139 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
140 gcry_mpi_t n
= NULL
, e
= NULL
, s
= NULL
;
144 assert(hash_algorithm
);
146 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature
, signature_size
, NULL
);
152 ge
= gcry_mpi_scan(&e
, GCRYMPI_FMT_USG
, exponent
, exponent_size
, NULL
);
158 ge
= gcry_mpi_scan(&n
, GCRYMPI_FMT_USG
, modulus
, modulus_size
, NULL
);
164 ge
= gcry_sexp_build(&signature_sexp
,
166 "(sig-val (rsa (s %m)))",
174 ge
= gcry_sexp_build(&data_sexp
,
176 "(data (flags pkcs1) (hash %s %b))",
185 ge
= gcry_sexp_build(&public_key_sexp
,
187 "(public-key (rsa (n %m) (e %m)))",
195 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
196 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
199 log_debug("RSA signature check failed: %s", gpg_strerror(ge
));
213 gcry_sexp_release(public_key_sexp
);
215 gcry_sexp_release(signature_sexp
);
217 gcry_sexp_release(data_sexp
);
222 static void md_add_uint8(gcry_md_hd_t md
, uint8_t v
) {
223 gcry_md_write(md
, &v
, sizeof(v
));
226 static void md_add_uint16(gcry_md_hd_t md
, uint16_t v
) {
228 gcry_md_write(md
, &v
, sizeof(v
));
231 static void md_add_uint32(gcry_md_hd_t md
, uint32_t v
) {
233 gcry_md_write(md
, &v
, sizeof(v
));
236 static int dnssec_rrsig_expired(DnsResourceRecord
*rrsig
, usec_t realtime
) {
237 usec_t expiration
, inception
, skew
;
240 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
242 if (realtime
== USEC_INFINITY
)
243 realtime
= now(CLOCK_REALTIME
);
245 expiration
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
246 inception
= rrsig
->rrsig
.inception
* USEC_PER_SEC
;
248 if (inception
> expiration
)
249 return -EKEYREJECTED
;
251 /* Permit a certain amount of clock skew of 10% of the valid
252 * time range. This takes inspiration from unbound's
254 skew
= (expiration
- inception
) / 10;
258 if (inception
< skew
)
263 if (expiration
+ skew
< expiration
)
264 expiration
= USEC_INFINITY
;
268 return realtime
< inception
|| realtime
> expiration
;
271 int dnssec_verify_rrset(
274 DnsResourceRecord
*rrsig
,
275 DnsResourceRecord
*dnskey
,
277 DnssecResult
*result
) {
279 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
280 size_t exponent_size
, modulus_size
, hash_size
;
281 void *exponent
, *modulus
, *hash
;
282 DnsResourceRecord
**list
, *rr
;
283 gcry_md_hd_t md
= NULL
;
291 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
292 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
294 /* Verifies the the RRSet matching the specified "key" in "a",
295 * using the signature "rrsig" and the key "dnskey". It's
296 * assumed the RRSIG and DNSKEY match. */
298 if (!dnssec_algorithm_supported(rrsig
->rrsig
.algorithm
))
301 if (a
->n_rrs
> VERIFY_RRS_MAX
)
304 r
= dnssec_rrsig_expired(rrsig
, realtime
);
308 *result
= DNSSEC_SIGNATURE_EXPIRED
;
312 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
313 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
315 DNS_ANSWER_FOREACH(rr
, a
) {
316 r
= dns_resource_key_equal(key
, rr
->key
);
322 /* We need the wire format for ordering, and digest calculation */
323 r
= dns_resource_record_to_wire_format(rr
, true);
333 /* Bring the RRs into canonical order */
334 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
336 /* OK, the RRs are now in canonical order. Let's calculate the digest */
337 switch (rrsig
->rrsig
.algorithm
) {
339 case DNSSEC_ALGORITHM_RSASHA1
:
340 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
341 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
345 case DNSSEC_ALGORITHM_RSASHA256
:
346 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
350 case DNSSEC_ALGORITHM_RSASHA512
:
351 gcry_md_open(&md
, GCRY_MD_SHA512
, 0);
356 assert_not_reached("Unknown digest");
362 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
363 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
364 md_add_uint8(md
, rrsig
->rrsig
.labels
);
365 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
366 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
367 md_add_uint32(md
, rrsig
->rrsig
.inception
);
368 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
370 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
373 gcry_md_write(md
, wire_format_name
, r
);
375 for (k
= 0; k
< n
; k
++) {
379 r
= dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr
->key
), wire_format_name
, sizeof(wire_format_name
), true);
382 gcry_md_write(md
, wire_format_name
, r
);
384 md_add_uint16(md
, rr
->key
->type
);
385 md_add_uint16(md
, rr
->key
->class);
386 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
388 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
389 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
392 md_add_uint16(md
, (uint16_t) l
);
393 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
396 hash
= gcry_md_read(md
, 0);
402 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
403 /* exponent is > 255 bytes long */
405 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
407 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
408 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
410 if (exponent_size
< 256) {
415 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
) {
420 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
421 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
424 /* exponent is <= 255 bytes long */
426 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
427 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
429 if (exponent_size
<= 0) {
434 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
) {
439 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
440 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
443 r
= dnssec_rsa_verify(
444 gcry_md_algo_name(gcry_md_get_algo(md
)),
445 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
447 exponent
, exponent_size
,
448 modulus
, modulus_size
);
452 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
460 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
465 /* Checks if the specified DNSKEY RR matches the key used for
466 * the signature in the specified RRSIG RR */
468 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
471 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
473 if (dnskey
->key
->class != rrsig
->key
->class)
475 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
477 if (dnskey
->dnskey
.protocol
!= 3)
479 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
482 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
485 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
488 int dnssec_key_match_rrsig(DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
492 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
494 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
496 if (rrsig
->key
->class != key
->class)
498 if (rrsig
->rrsig
.type_covered
!= key
->type
)
501 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
504 int dnssec_verify_rrset_search(
507 DnsAnswer
*validated_dnskeys
,
509 DnssecResult
*result
) {
511 bool found_rrsig
= false, found_dnskey
= false;
512 DnsResourceRecord
*rrsig
;
518 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
520 if (!a
|| a
->n_rrs
<= 0)
523 /* Iterate through each RRSIG RR. */
524 DNS_ANSWER_FOREACH(rrsig
, a
) {
525 DnsResourceRecord
*dnskey
;
527 r
= dnssec_key_match_rrsig(key
, rrsig
);
535 /* Look for a matching key */
536 DNS_ANSWER_FOREACH(dnskey
, validated_dnskeys
) {
537 DnssecResult one_result
;
539 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
547 /* Take the time here, if it isn't set yet, so
548 * that we do all validations with the same
550 if (realtime
== USEC_INFINITY
)
551 realtime
= now(CLOCK_REALTIME
);
553 /* Yay, we found a matching RRSIG with a matching
554 * DNSKEY, awesome. Now let's verify all entries of
555 * the RRSet against the RRSIG and DNSKEY
558 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
559 if (r
< 0 && r
!= EOPNOTSUPP
)
561 if (one_result
== DNSSEC_VALIDATED
) {
562 *result
= DNSSEC_VALIDATED
;
566 /* If the signature is invalid, or done using
567 an unsupported algorithm, let's try another
568 key and/or signature. After all they
569 key_tags and stuff are not unique, and
570 might be shared by multiple keys. */
575 *result
= DNSSEC_INVALID
;
576 else if (found_rrsig
)
577 *result
= DNSSEC_MISSING_KEY
;
579 *result
= DNSSEC_NO_SIGNATURE
;
584 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
588 /* Converts the specified hostname into DNSSEC canonicalized
597 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
605 /* DNSSEC validation is always done on the ASCII version of the label */
606 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
613 if (buffer_max
< (size_t) r
+ 2)
616 /* The DNSSEC canonical form is not clear on what to
617 * do with dots appearing in labels, the way DNS-SD
618 * does it. Refuse it for now. */
620 if (memchr(buffer
, '.', r
))
623 for (i
= 0; i
< (size_t) r
; i
++) {
624 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
625 buffer
[i
] = buffer
[i
] - 'A' + 'a';
637 /* Not even a single label: this is the root domain name */
639 assert(buffer_max
> 2);
649 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
650 gcry_md_hd_t md
= NULL
;
651 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
658 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
660 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
662 if (ds
->key
->type
!= DNS_TYPE_DS
)
664 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
665 return -EKEYREJECTED
;
666 if (dnskey
->dnskey
.protocol
!= 3)
667 return -EKEYREJECTED
;
669 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
671 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
674 if (!dnssec_digest_supported(ds
->ds
.digest_type
))
677 switch (ds
->ds
.digest_type
) {
679 case DNSSEC_DIGEST_SHA1
:
681 if (ds
->ds
.digest_size
!= 20)
684 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
687 case DNSSEC_DIGEST_SHA256
:
689 if (ds
->ds
.digest_size
!= 32)
692 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
696 assert_not_reached("Unknown digest");
702 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
706 gcry_md_write(md
, owner_name
, r
);
707 md_add_uint16(md
, dnskey
->dnskey
.flags
);
708 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
709 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
710 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
712 result
= gcry_md_read(md
, 0);
718 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
725 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
726 DnsResourceRecord
*ds
;
731 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
734 DNS_ANSWER_FOREACH(ds
, validated_ds
) {
736 if (ds
->key
->type
!= DNS_TYPE_DS
)
739 r
= dnssec_verify_dnskey(dnskey
, ds
);
749 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
751 [DNSSEC_TRUST
] = "trust",
752 [DNSSEC_YES
] = "yes",
754 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
756 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
757 [DNSSEC_VALIDATED
] = "validated",
758 [DNSSEC_INVALID
] = "invalid",
759 [DNSSEC_UNSIGNED
] = "unsigned",
760 [DNSSEC_NO_SIGNATURE
] = "no-signature",
761 [DNSSEC_MISSING_KEY
] = "missing-key",
762 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
763 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
765 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);