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
,
278 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
279 size_t exponent_size
, modulus_size
, hash_size
;
280 void *exponent
, *modulus
, *hash
;
281 DnsResourceRecord
**list
, *rr
;
282 gcry_md_hd_t md
= NULL
;
289 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
290 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
292 /* Verifies the the RRSet matching the specified "key" in "a",
293 * using the signature "rrsig" and the key "dnskey". It's
294 * assumed the RRSIG and DNSKEY match. */
296 if (!dnssec_algorithm_supported(rrsig
->rrsig
.algorithm
))
299 if (a
->n_rrs
> VERIFY_RRS_MAX
)
302 r
= dnssec_rrsig_expired(rrsig
, realtime
);
306 return DNSSEC_SIGNATURE_EXPIRED
;
308 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
309 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
311 DNS_ANSWER_FOREACH(rr
, a
) {
312 r
= dns_resource_key_equal(key
, rr
->key
);
318 /* We need the wire format for ordering, and digest calculation */
319 r
= dns_resource_record_to_wire_format(rr
, true);
329 /* Bring the RRs into canonical order */
330 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
332 /* OK, the RRs are now in canonical order. Let's calculate the digest */
333 switch (rrsig
->rrsig
.algorithm
) {
335 case DNSSEC_ALGORITHM_RSASHA1
:
336 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
337 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
341 case DNSSEC_ALGORITHM_RSASHA256
:
342 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
346 case DNSSEC_ALGORITHM_RSASHA512
:
347 gcry_md_open(&md
, GCRY_MD_SHA512
, 0);
352 assert_not_reached("Unknown digest");
358 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
359 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
360 md_add_uint8(md
, rrsig
->rrsig
.labels
);
361 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
362 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
363 md_add_uint32(md
, rrsig
->rrsig
.inception
);
364 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
366 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
369 gcry_md_write(md
, wire_format_name
, r
);
371 for (k
= 0; k
< n
; k
++) {
375 r
= dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr
->key
), wire_format_name
, sizeof(wire_format_name
), true);
378 gcry_md_write(md
, wire_format_name
, r
);
380 md_add_uint16(md
, rr
->key
->type
);
381 md_add_uint16(md
, rr
->key
->class);
382 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
384 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
385 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
388 md_add_uint16(md
, (uint16_t) l
);
389 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
392 hash
= gcry_md_read(md
, 0);
398 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
399 /* exponent is > 255 bytes long */
401 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
403 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
404 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
406 if (exponent_size
< 256) {
411 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
) {
416 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
417 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
420 /* exponent is <= 255 bytes long */
422 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
423 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
425 if (exponent_size
<= 0) {
430 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
) {
435 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
436 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
439 r
= dnssec_rsa_verify(
440 gcry_md_algo_name(gcry_md_get_algo(md
)),
441 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
443 exponent
, exponent_size
,
444 modulus
, modulus_size
);
448 r
= r
? DNSSEC_VERIFIED
: DNSSEC_INVALID
;
455 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
460 /* Checks if the specified DNSKEY RR matches the key used for
461 * the signature in the specified RRSIG RR */
463 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
466 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
468 if (dnskey
->key
->class != rrsig
->key
->class)
470 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
472 if (dnskey
->dnskey
.protocol
!= 3)
474 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
477 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
480 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
483 int dnssec_key_match_rrsig(DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
487 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
489 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
491 if (rrsig
->key
->class != key
->class)
493 if (rrsig
->rrsig
.type_covered
!= key
->type
)
496 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
499 int dnssec_verify_rrset_search(
502 DnsAnswer
*validated_dnskeys
,
505 bool found_rrsig
= false, found_dnskey
= false;
506 DnsResourceRecord
*rrsig
;
511 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
513 if (!a
|| a
->n_rrs
<= 0)
516 /* Iterate through each RRSIG RR. */
517 DNS_ANSWER_FOREACH(rrsig
, a
) {
518 DnsResourceRecord
*dnskey
;
520 r
= dnssec_key_match_rrsig(key
, rrsig
);
528 DNS_ANSWER_FOREACH(dnskey
, validated_dnskeys
) {
530 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
538 /* Take the time here, if it isn't set yet, so
539 * that we do all validations with the same
541 if (realtime
== USEC_INFINITY
)
542 realtime
= now(CLOCK_REALTIME
);
544 /* Yay, we found a matching RRSIG with a matching
545 * DNSKEY, awesome. Now let's verify all entries of
546 * the RRSet against the RRSIG and DNSKEY
549 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
);
550 if (r
< 0 && r
!= EOPNOTSUPP
)
552 if (r
== DNSSEC_VERIFIED
)
553 return DNSSEC_VERIFIED
;
555 /* If the signature is invalid, or done using
556 an unsupported algorithm, let's try another
557 key and/or signature. After all they
558 key_tags and stuff are not unique, and
559 might be shared by multiple keys. */
564 return DNSSEC_INVALID
;
567 return DNSSEC_MISSING_KEY
;
569 return DNSSEC_NO_SIGNATURE
;
572 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
576 /* Converts the specified hostname into DNSSEC canonicalized
585 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
593 /* DNSSEC validation is always done on the ASCII version of the label */
594 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
601 if (buffer_max
< (size_t) r
+ 2)
604 /* The DNSSEC canonical form is not clear on what to
605 * do with dots appearing in labels, the way DNS-SD
606 * does it. Refuse it for now. */
608 if (memchr(buffer
, '.', r
))
611 for (i
= 0; i
< (size_t) r
; i
++) {
612 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
613 buffer
[i
] = buffer
[i
] - 'A' + 'a';
625 /* Not even a single label: this is the root domain name */
627 assert(buffer_max
> 2);
637 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
638 gcry_md_hd_t md
= NULL
;
639 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
646 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
648 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
650 if (ds
->key
->type
!= DNS_TYPE_DS
)
652 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
653 return -EKEYREJECTED
;
654 if (dnskey
->dnskey
.protocol
!= 3)
655 return -EKEYREJECTED
;
657 if (!dnssec_algorithm_supported(dnskey
->dnskey
.algorithm
))
659 if (!dnssec_digest_supported(ds
->ds
.digest_type
))
662 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
664 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
667 switch (ds
->ds
.digest_type
) {
669 case DNSSEC_DIGEST_SHA1
:
671 if (ds
->ds
.digest_size
!= 20)
674 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
677 case DNSSEC_DIGEST_SHA256
:
679 if (ds
->ds
.digest_size
!= 32)
682 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
686 assert_not_reached("Unknown digest");
692 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
696 gcry_md_write(md
, owner_name
, r
);
697 md_add_uint16(md
, dnskey
->dnskey
.flags
);
698 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
699 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
700 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
702 result
= gcry_md_read(md
, 0);
708 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
715 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
717 [DNSSEC_TRUST
] = "trust",
718 [DNSSEC_YES
] = "yes",
720 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);