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
)) {
299 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
303 if (a
->n_rrs
> VERIFY_RRS_MAX
)
306 r
= dnssec_rrsig_expired(rrsig
, realtime
);
310 *result
= DNSSEC_SIGNATURE_EXPIRED
;
314 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
315 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
317 DNS_ANSWER_FOREACH(rr
, a
) {
318 r
= dns_resource_key_equal(key
, rr
->key
);
324 /* We need the wire format for ordering, and digest calculation */
325 r
= dns_resource_record_to_wire_format(rr
, true);
335 /* Bring the RRs into canonical order */
336 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
338 /* OK, the RRs are now in canonical order. Let's calculate the digest */
339 switch (rrsig
->rrsig
.algorithm
) {
341 case DNSSEC_ALGORITHM_RSASHA1
:
342 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
343 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
347 case DNSSEC_ALGORITHM_RSASHA256
:
348 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
352 case DNSSEC_ALGORITHM_RSASHA512
:
353 gcry_md_open(&md
, GCRY_MD_SHA512
, 0);
358 assert_not_reached("Unknown digest");
364 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
365 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
366 md_add_uint8(md
, rrsig
->rrsig
.labels
);
367 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
368 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
369 md_add_uint32(md
, rrsig
->rrsig
.inception
);
370 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
372 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
375 gcry_md_write(md
, wire_format_name
, r
);
377 for (k
= 0; k
< n
; k
++) {
381 r
= dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr
->key
), wire_format_name
, sizeof(wire_format_name
), true);
384 gcry_md_write(md
, wire_format_name
, r
);
386 md_add_uint16(md
, rr
->key
->type
);
387 md_add_uint16(md
, rr
->key
->class);
388 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
390 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
391 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
394 md_add_uint16(md
, (uint16_t) l
);
395 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
398 hash
= gcry_md_read(md
, 0);
404 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
405 /* exponent is > 255 bytes long */
407 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
409 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
410 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
412 if (exponent_size
< 256) {
417 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
) {
422 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
423 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
426 /* exponent is <= 255 bytes long */
428 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
429 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
431 if (exponent_size
<= 0) {
436 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
) {
441 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
442 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
445 r
= dnssec_rsa_verify(
446 gcry_md_algo_name(gcry_md_get_algo(md
)),
447 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
449 exponent
, exponent_size
,
450 modulus
, modulus_size
);
454 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
462 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
467 /* Checks if the specified DNSKEY RR matches the key used for
468 * the signature in the specified RRSIG RR */
470 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
473 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
475 if (dnskey
->key
->class != rrsig
->key
->class)
477 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
479 if (dnskey
->dnskey
.protocol
!= 3)
481 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
484 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
487 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
490 int dnssec_key_match_rrsig(DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
494 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
496 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
498 if (rrsig
->key
->class != key
->class)
500 if (rrsig
->rrsig
.type_covered
!= key
->type
)
503 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
506 int dnssec_verify_rrset_search(
509 DnsAnswer
*validated_dnskeys
,
511 DnssecResult
*result
) {
513 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
514 DnsResourceRecord
*rrsig
;
520 /* Verifies all RRs from "a" that match the key "key", against DNSKEY and DS RRs in "validated_dnskeys" */
522 if (!a
|| a
->n_rrs
<= 0)
525 /* Iterate through each RRSIG RR. */
526 DNS_ANSWER_FOREACH(rrsig
, a
) {
527 DnsResourceRecord
*dnskey
;
529 /* Is this an RRSIG RR that applies to RRs matching our key? */
530 r
= dnssec_key_match_rrsig(key
, rrsig
);
538 /* Look for a matching key */
539 DNS_ANSWER_FOREACH(dnskey
, validated_dnskeys
) {
540 DnssecResult one_result
;
542 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
543 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
549 /* Take the time here, if it isn't set yet, so
550 * that we do all validations with the same
552 if (realtime
== USEC_INFINITY
)
553 realtime
= now(CLOCK_REALTIME
);
555 /* Yay, we found a matching RRSIG with a matching
556 * DNSKEY, awesome. Now let's verify all entries of
557 * the RRSet against the RRSIG and DNSKEY
560 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
564 switch (one_result
) {
566 case DNSSEC_VALIDATED
:
567 /* Yay, the RR has been validated,
568 * return immediately. */
569 *result
= DNSSEC_VALIDATED
;
573 /* If the signature is invalid, let's try another
574 key and/or signature. After all they
575 key_tags and stuff are not unique, and
576 might be shared by multiple keys. */
577 found_invalid
= true;
580 case DNSSEC_UNSUPPORTED_ALGORITHM
:
581 /* If the key algorithm is
582 unsupported, try another
583 RRSIG/DNSKEY pair, but remember we
584 encountered this, so that we can
585 return a proper error when we
586 encounter nothing better. */
587 found_unsupported_algorithm
= true;
590 case DNSSEC_SIGNATURE_EXPIRED
:
591 /* If the signature is expired, try
592 another one, but remember it, so
593 that we can return this */
594 found_expired_rrsig
= true;
598 assert_not_reached("Unexpected DNSSEC validation result");
603 if (found_expired_rrsig
)
604 *result
= DNSSEC_SIGNATURE_EXPIRED
;
605 else if (found_unsupported_algorithm
)
606 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
607 else if (found_invalid
)
608 *result
= DNSSEC_INVALID
;
609 else if (found_rrsig
)
610 *result
= DNSSEC_MISSING_KEY
;
612 *result
= DNSSEC_NO_SIGNATURE
;
617 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
621 /* Converts the specified hostname into DNSSEC canonicalized
630 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
638 /* DNSSEC validation is always done on the ASCII version of the label */
639 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
646 if (buffer_max
< (size_t) r
+ 2)
649 /* The DNSSEC canonical form is not clear on what to
650 * do with dots appearing in labels, the way DNS-SD
651 * does it. Refuse it for now. */
653 if (memchr(buffer
, '.', r
))
656 for (i
= 0; i
< (size_t) r
; i
++) {
657 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
658 buffer
[i
] = buffer
[i
] - 'A' + 'a';
670 /* Not even a single label: this is the root domain name */
672 assert(buffer_max
> 2);
682 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
683 gcry_md_hd_t md
= NULL
;
684 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
691 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
693 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
695 if (ds
->key
->type
!= DNS_TYPE_DS
)
697 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
698 return -EKEYREJECTED
;
699 if (dnskey
->dnskey
.protocol
!= 3)
700 return -EKEYREJECTED
;
702 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
704 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
707 if (!dnssec_digest_supported(ds
->ds
.digest_type
))
710 switch (ds
->ds
.digest_type
) {
712 case DNSSEC_DIGEST_SHA1
:
714 if (ds
->ds
.digest_size
!= 20)
717 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
720 case DNSSEC_DIGEST_SHA256
:
722 if (ds
->ds
.digest_size
!= 32)
725 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
729 assert_not_reached("Unknown digest");
735 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
739 gcry_md_write(md
, owner_name
, r
);
740 md_add_uint16(md
, dnskey
->dnskey
.flags
);
741 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
742 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
743 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
745 result
= gcry_md_read(md
, 0);
751 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
758 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
759 DnsResourceRecord
*ds
;
764 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
767 DNS_ANSWER_FOREACH(ds
, validated_ds
) {
769 if (ds
->key
->type
!= DNS_TYPE_DS
)
772 r
= dnssec_verify_dnskey(dnskey
, ds
);
782 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
784 [DNSSEC_TRUST
] = "trust",
785 [DNSSEC_YES
] = "yes",
787 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
789 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
790 [DNSSEC_VALIDATED
] = "validated",
791 [DNSSEC_INVALID
] = "invalid",
792 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
793 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
794 [DNSSEC_NO_SIGNATURE
] = "no-signature",
795 [DNSSEC_MISSING_KEY
] = "missing-key",
796 [DNSSEC_UNSIGNED
] = "unsigned",
797 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
799 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);