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 "hexdecoct.h"
27 #include "resolved-dns-dnssec.h"
28 #include "resolved-dns-packet.h"
29 #include "string-table.h"
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?
38 * - Make trust anchor store read additional DS+DNSKEY data from disk
39 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
40 * - multi-label zone compatibility
41 * - cname/dname compatibility
42 * - per-interface DNSSEC setting
44 * - retry on failed validation?
49 #define VERIFY_RRS_MAX 256
50 #define MAX_KEY_SIZE (32*1024)
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)
56 * The DNSSEC Chain of trust:
58 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
59 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
60 * DS RRs are protected like normal RRs
63 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
66 static void initialize_libgcrypt(void) {
69 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P
))
72 p
= gcry_check_version("1.4.5");
75 gcry_control(GCRYCTL_DISABLE_SECMEM
);
76 gcry_control(GCRYCTL_INITIALIZATION_FINISHED
, 0);
79 uint16_t dnssec_keytag(DnsResourceRecord
*dnskey
) {
84 /* The algorithm from RFC 4034, Appendix B. */
87 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
89 sum
= (uint32_t) dnskey
->dnskey
.flags
+
90 ((((uint32_t) dnskey
->dnskey
.protocol
) << 8) + (uint32_t) dnskey
->dnskey
.algorithm
);
92 p
= dnskey
->dnskey
.key
;
94 for (i
= 0; i
< dnskey
->dnskey
.key_size
; i
++)
95 sum
+= (i
& 1) == 0 ? (uint32_t) p
[i
] << 8 : (uint32_t) p
[i
];
97 sum
+= (sum
>> 16) & UINT32_C(0xFFFF);
99 return sum
& UINT32_C(0xFFFF);
102 static int rr_compare(const void *a
, const void *b
) {
103 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
107 /* Let's order the RRs according to RFC 4034, Section 6.3 */
111 assert((*x
)->wire_format
);
114 assert((*y
)->wire_format
);
116 m
= MIN((*x
)->wire_format_size
, (*y
)->wire_format_size
);
118 r
= memcmp((*x
)->wire_format
, (*y
)->wire_format
, m
);
122 if ((*x
)->wire_format_size
< (*y
)->wire_format_size
)
124 else if ((*x
)->wire_format_size
> (*y
)->wire_format_size
)
130 static int dnssec_rsa_verify_raw(
131 const char *hash_algorithm
,
132 const void *signature
, size_t signature_size
,
133 const void *data
, size_t data_size
,
134 const void *exponent
, size_t exponent_size
,
135 const void *modulus
, size_t modulus_size
) {
137 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
138 gcry_mpi_t n
= NULL
, e
= NULL
, s
= NULL
;
142 assert(hash_algorithm
);
144 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature
, signature_size
, NULL
);
150 ge
= gcry_mpi_scan(&e
, GCRYMPI_FMT_USG
, exponent
, exponent_size
, NULL
);
156 ge
= gcry_mpi_scan(&n
, GCRYMPI_FMT_USG
, modulus
, modulus_size
, NULL
);
162 ge
= gcry_sexp_build(&signature_sexp
,
164 "(sig-val (rsa (s %m)))",
172 ge
= gcry_sexp_build(&data_sexp
,
174 "(data (flags pkcs1) (hash %s %b))",
183 ge
= gcry_sexp_build(&public_key_sexp
,
185 "(public-key (rsa (n %m) (e %m)))",
193 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
194 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
197 log_debug("RSA signature check failed: %s", gpg_strerror(ge
));
211 gcry_sexp_release(public_key_sexp
);
213 gcry_sexp_release(signature_sexp
);
215 gcry_sexp_release(data_sexp
);
220 static int dnssec_rsa_verify(
221 const char *hash_algorithm
,
222 const void *hash
, size_t hash_size
,
223 DnsResourceRecord
*rrsig
,
224 DnsResourceRecord
*dnskey
) {
226 size_t exponent_size
, modulus_size
;
227 void *exponent
, *modulus
;
229 assert(hash_algorithm
);
231 assert(hash_size
> 0);
235 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
236 /* exponent is > 255 bytes long */
238 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
240 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
241 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
243 if (exponent_size
< 256)
246 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
)
249 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
250 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
253 /* exponent is <= 255 bytes long */
255 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
256 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
258 if (exponent_size
<= 0)
261 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
)
264 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
265 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
268 return dnssec_rsa_verify_raw(
270 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
272 exponent
, exponent_size
,
273 modulus
, modulus_size
);
276 static int dnssec_ecdsa_verify_raw(
277 const char *hash_algorithm
,
279 const void *signature_r
, size_t signature_r_size
,
280 const void *signature_s
, size_t signature_s_size
,
281 const void *data
, size_t data_size
,
282 const void *key
, size_t key_size
) {
284 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
285 gcry_mpi_t q
= NULL
, r
= NULL
, s
= NULL
;
289 assert(hash_algorithm
);
291 ge
= gcry_mpi_scan(&r
, GCRYMPI_FMT_USG
, signature_r
, signature_r_size
, NULL
);
297 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature_s
, signature_s_size
, NULL
);
303 ge
= gcry_mpi_scan(&q
, GCRYMPI_FMT_USG
, key
, key_size
, NULL
);
309 ge
= gcry_sexp_build(&signature_sexp
,
311 "(sig-val (ecdsa (r %m) (s %m)))",
319 ge
= gcry_sexp_build(&data_sexp
,
321 "(data (flags rfc6979) (hash %s %b))",
330 ge
= gcry_sexp_build(&public_key_sexp
,
332 "(public-key (ecc (curve %s) (q %m)))",
340 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
341 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
344 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge
));
357 gcry_sexp_release(public_key_sexp
);
359 gcry_sexp_release(signature_sexp
);
361 gcry_sexp_release(data_sexp
);
366 static int dnssec_ecdsa_verify(
367 const char *hash_algorithm
,
369 const void *hash
, size_t hash_size
,
370 DnsResourceRecord
*rrsig
,
371 DnsResourceRecord
*dnskey
) {
382 if (algorithm
== DNSSEC_ALGORITHM_ECDSAP256SHA256
) {
384 curve
= "NIST P-256";
385 } else if (algorithm
== DNSSEC_ALGORITHM_ECDSAP384SHA384
) {
387 curve
= "NIST P-384";
391 if (dnskey
->dnskey
.key_size
!= key_size
* 2)
394 if (rrsig
->rrsig
.signature_size
!= key_size
* 2)
397 q
= alloca(key_size
*2 + 1);
398 q
[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
399 memcpy(q
+1, dnskey
->dnskey
.key
, key_size
*2);
401 return dnssec_ecdsa_verify_raw(
404 rrsig
->rrsig
.signature
, key_size
,
405 (uint8_t*) rrsig
->rrsig
.signature
+ key_size
, key_size
,
410 static void md_add_uint8(gcry_md_hd_t md
, uint8_t v
) {
411 gcry_md_write(md
, &v
, sizeof(v
));
414 static void md_add_uint16(gcry_md_hd_t md
, uint16_t v
) {
416 gcry_md_write(md
, &v
, sizeof(v
));
419 static void md_add_uint32(gcry_md_hd_t md
, uint32_t v
) {
421 gcry_md_write(md
, &v
, sizeof(v
));
424 static int dnssec_rrsig_expired(DnsResourceRecord
*rrsig
, usec_t realtime
) {
425 usec_t expiration
, inception
, skew
;
428 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
430 if (realtime
== USEC_INFINITY
)
431 realtime
= now(CLOCK_REALTIME
);
433 expiration
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
434 inception
= rrsig
->rrsig
.inception
* USEC_PER_SEC
;
436 if (inception
> expiration
)
437 return -EKEYREJECTED
;
439 /* Permit a certain amount of clock skew of 10% of the valid
440 * time range. This takes inspiration from unbound's
442 skew
= (expiration
- inception
) / 10;
446 if (inception
< skew
)
451 if (expiration
+ skew
< expiration
)
452 expiration
= USEC_INFINITY
;
456 return realtime
< inception
|| realtime
> expiration
;
459 static int algorithm_to_gcrypt_md(uint8_t algorithm
) {
461 /* Translates a DNSSEC signature algorithm into a gcrypt
464 * Note that we implement all algorithms listed as "Must
465 * implement" and "Recommended to Implement" in RFC6944. We
466 * don't implement any algorithms that are listed as
467 * "Optional" or "Must Not Implement". Specifically, we do not
468 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
473 case DNSSEC_ALGORITHM_RSASHA1
:
474 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
477 case DNSSEC_ALGORITHM_RSASHA256
:
478 case DNSSEC_ALGORITHM_ECDSAP256SHA256
:
479 return GCRY_MD_SHA256
;
481 case DNSSEC_ALGORITHM_ECDSAP384SHA384
:
482 return GCRY_MD_SHA384
;
484 case DNSSEC_ALGORITHM_RSASHA512
:
485 return GCRY_MD_SHA512
;
492 int dnssec_verify_rrset(
495 DnsResourceRecord
*rrsig
,
496 DnsResourceRecord
*dnskey
,
498 DnssecResult
*result
) {
500 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
503 DnsResourceRecord
**list
, *rr
;
504 gcry_md_hd_t md
= NULL
;
512 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
513 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
515 /* Verifies the the RRSet matching the specified "key" in "a",
516 * using the signature "rrsig" and the key "dnskey". It's
517 * assumed the RRSIG and DNSKEY match. */
519 md_algorithm
= algorithm_to_gcrypt_md(rrsig
->rrsig
.algorithm
);
520 if (md_algorithm
== -EOPNOTSUPP
) {
521 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
524 if (md_algorithm
< 0)
527 if (a
->n_rrs
> VERIFY_RRS_MAX
)
530 r
= dnssec_rrsig_expired(rrsig
, realtime
);
534 *result
= DNSSEC_SIGNATURE_EXPIRED
;
538 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
539 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
541 DNS_ANSWER_FOREACH(rr
, a
) {
542 r
= dns_resource_key_equal(key
, rr
->key
);
548 /* We need the wire format for ordering, and digest calculation */
549 r
= dns_resource_record_to_wire_format(rr
, true);
559 /* Bring the RRs into canonical order */
560 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
562 /* OK, the RRs are now in canonical order. Let's calculate the digest */
563 initialize_libgcrypt();
565 hash_size
= gcry_md_get_algo_dlen(md_algorithm
);
566 assert(hash_size
> 0);
568 gcry_md_open(&md
, md_algorithm
, 0);
572 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
573 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
574 md_add_uint8(md
, rrsig
->rrsig
.labels
);
575 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
576 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
577 md_add_uint32(md
, rrsig
->rrsig
.inception
);
578 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
580 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
583 gcry_md_write(md
, wire_format_name
, r
);
585 for (k
= 0; k
< n
; k
++) {
590 r
= dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr
->key
), rrsig
->rrsig
.labels
, &suffix
);
593 if (r
> 0) /* This is a wildcard! */
594 gcry_md_write(md
, (uint8_t[]) { 1, '*'}, 2);
596 r
= dns_name_to_wire_format(suffix
, wire_format_name
, sizeof(wire_format_name
), true);
599 gcry_md_write(md
, wire_format_name
, r
);
601 md_add_uint16(md
, rr
->key
->type
);
602 md_add_uint16(md
, rr
->key
->class);
603 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
605 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
606 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
609 md_add_uint16(md
, (uint16_t) l
);
610 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
613 hash
= gcry_md_read(md
, 0);
619 switch (rrsig
->rrsig
.algorithm
) {
621 case DNSSEC_ALGORITHM_RSASHA1
:
622 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
623 case DNSSEC_ALGORITHM_RSASHA256
:
624 case DNSSEC_ALGORITHM_RSASHA512
:
625 r
= dnssec_rsa_verify(
626 gcry_md_algo_name(md_algorithm
),
632 case DNSSEC_ALGORITHM_ECDSAP256SHA256
:
633 case DNSSEC_ALGORITHM_ECDSAP384SHA384
:
634 r
= dnssec_ecdsa_verify(
635 gcry_md_algo_name(md_algorithm
),
636 rrsig
->rrsig
.algorithm
,
646 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
654 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
659 /* Checks if the specified DNSKEY RR matches the key used for
660 * the signature in the specified RRSIG RR */
662 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
665 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
667 if (dnskey
->key
->class != rrsig
->key
->class)
669 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
671 if (dnskey
->dnskey
.protocol
!= 3)
673 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
676 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
679 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
682 int dnssec_key_match_rrsig(const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
688 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
690 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
692 if (rrsig
->key
->class != key
->class)
694 if (rrsig
->rrsig
.type_covered
!= key
->type
)
697 /* Make sure signer is a parent of the RRset */
698 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig
->key
), rrsig
->rrsig
.signer
);
702 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
703 r
= dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig
->key
));
706 if (r
< rrsig
->rrsig
.labels
)
709 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
712 static int dnssec_fix_rrset_ttl(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
, usec_t realtime
) {
713 DnsResourceRecord
*rr
;
719 DNS_ANSWER_FOREACH(rr
, a
) {
720 r
= dns_resource_key_equal(key
, rr
->key
);
726 /* Pick the TTL as the minimum of the RR's TTL, the
727 * RR's original TTL according to the RRSIG and the
728 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
729 rr
->ttl
= MIN3(rr
->ttl
, rrsig
->rrsig
.original_ttl
, rrsig
->ttl
);
730 rr
->expiry
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
736 int dnssec_verify_rrset_search(
739 DnsAnswer
*validated_dnskeys
,
741 DnssecResult
*result
) {
743 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
744 DnsResourceRecord
*rrsig
;
750 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
752 if (!a
|| a
->n_rrs
<= 0)
755 /* Iterate through each RRSIG RR. */
756 DNS_ANSWER_FOREACH(rrsig
, a
) {
757 DnsResourceRecord
*dnskey
;
758 DnsAnswerFlags flags
;
760 /* Is this an RRSIG RR that applies to RRs matching our key? */
761 r
= dnssec_key_match_rrsig(key
, rrsig
);
769 /* Look for a matching key */
770 DNS_ANSWER_FOREACH_FLAGS(dnskey
, flags
, validated_dnskeys
) {
771 DnssecResult one_result
;
773 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
776 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
777 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
783 /* Take the time here, if it isn't set yet, so
784 * that we do all validations with the same
786 if (realtime
== USEC_INFINITY
)
787 realtime
= now(CLOCK_REALTIME
);
789 /* Yay, we found a matching RRSIG with a matching
790 * DNSKEY, awesome. Now let's verify all entries of
791 * the RRSet against the RRSIG and DNSKEY
794 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
798 switch (one_result
) {
800 case DNSSEC_VALIDATED
:
801 /* Yay, the RR has been validated,
802 * return immediately, but fix up the expiry */
803 r
= dnssec_fix_rrset_ttl(a
, key
, rrsig
, realtime
);
807 *result
= DNSSEC_VALIDATED
;
811 /* If the signature is invalid, let's try another
812 key and/or signature. After all they
813 key_tags and stuff are not unique, and
814 might be shared by multiple keys. */
815 found_invalid
= true;
818 case DNSSEC_UNSUPPORTED_ALGORITHM
:
819 /* If the key algorithm is
820 unsupported, try another
821 RRSIG/DNSKEY pair, but remember we
822 encountered this, so that we can
823 return a proper error when we
824 encounter nothing better. */
825 found_unsupported_algorithm
= true;
828 case DNSSEC_SIGNATURE_EXPIRED
:
829 /* If the signature is expired, try
830 another one, but remember it, so
831 that we can return this */
832 found_expired_rrsig
= true;
836 assert_not_reached("Unexpected DNSSEC validation result");
841 if (found_expired_rrsig
)
842 *result
= DNSSEC_SIGNATURE_EXPIRED
;
843 else if (found_unsupported_algorithm
)
844 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
845 else if (found_invalid
)
846 *result
= DNSSEC_INVALID
;
847 else if (found_rrsig
)
848 *result
= DNSSEC_MISSING_KEY
;
850 *result
= DNSSEC_NO_SIGNATURE
;
855 int dnssec_has_rrsig(DnsAnswer
*a
, const DnsResourceKey
*key
) {
856 DnsResourceRecord
*rr
;
859 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
861 DNS_ANSWER_FOREACH(rr
, a
) {
862 r
= dnssec_key_match_rrsig(key
, rr
);
872 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
876 /* Converts the specified hostname into DNSSEC canonicalized
885 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
893 /* DNSSEC validation is always done on the ASCII version of the label */
894 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
901 if (buffer_max
< (size_t) r
+ 2)
904 /* The DNSSEC canonical form is not clear on what to
905 * do with dots appearing in labels, the way DNS-SD
906 * does it. Refuse it for now. */
908 if (memchr(buffer
, '.', r
))
911 for (i
= 0; i
< (size_t) r
; i
++) {
912 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
913 buffer
[i
] = buffer
[i
] - 'A' + 'a';
925 /* Not even a single label: this is the root domain name */
927 assert(buffer_max
> 2);
937 static int digest_to_gcrypt_md(uint8_t algorithm
) {
939 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
943 case DNSSEC_DIGEST_SHA1
:
946 case DNSSEC_DIGEST_SHA256
:
947 return GCRY_MD_SHA256
;
949 case DNSSEC_DIGEST_SHA384
:
950 return GCRY_MD_SHA384
;
957 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
958 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
959 gcry_md_hd_t md
= NULL
;
967 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
969 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
971 if (ds
->key
->type
!= DNS_TYPE_DS
)
973 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
974 return -EKEYREJECTED
;
975 if (dnskey
->dnskey
.protocol
!= 3)
976 return -EKEYREJECTED
;
978 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
980 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
983 initialize_libgcrypt();
985 md_algorithm
= digest_to_gcrypt_md(ds
->ds
.digest_type
);
986 if (md_algorithm
< 0)
989 hash_size
= gcry_md_get_algo_dlen(md_algorithm
);
990 assert(hash_size
> 0);
992 if (ds
->ds
.digest_size
!= hash_size
)
995 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
999 gcry_md_open(&md
, md_algorithm
, 0);
1003 gcry_md_write(md
, owner_name
, r
);
1004 md_add_uint16(md
, dnskey
->dnskey
.flags
);
1005 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
1006 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
1007 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
1009 result
= gcry_md_read(md
, 0);
1015 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
1022 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
1023 DnsResourceRecord
*ds
;
1024 DnsAnswerFlags flags
;
1029 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
1032 DNS_ANSWER_FOREACH_FLAGS(ds
, flags
, validated_ds
) {
1034 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1037 if (ds
->key
->type
!= DNS_TYPE_DS
)
1040 if (ds
->key
->class != dnskey
->key
->class)
1043 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), DNS_RESOURCE_KEY_NAME(ds
->key
));
1049 r
= dnssec_verify_dnskey(dnskey
, ds
);
1059 int dnssec_nsec3_hash(DnsResourceRecord
*nsec3
, const char *name
, void *ret
) {
1060 uint8_t wire_format
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
1061 gcry_md_hd_t md
= NULL
;
1072 if (nsec3
->key
->type
!= DNS_TYPE_NSEC3
)
1075 algorithm
= digest_to_gcrypt_md(nsec3
->nsec3
.algorithm
);
1079 initialize_libgcrypt();
1081 hash_size
= gcry_md_get_algo_dlen(algorithm
);
1082 assert(hash_size
> 0);
1084 if (nsec3
->nsec3
.next_hashed_name_size
!= hash_size
)
1087 r
= dns_name_to_wire_format(name
, wire_format
, sizeof(wire_format
), true);
1091 gcry_md_open(&md
, algorithm
, 0);
1095 gcry_md_write(md
, wire_format
, r
);
1096 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
1098 result
= gcry_md_read(md
, 0);
1104 for (k
= 0; k
< nsec3
->nsec3
.iterations
; k
++) {
1105 uint8_t tmp
[hash_size
];
1106 memcpy(tmp
, result
, hash_size
);
1109 gcry_md_write(md
, tmp
, hash_size
);
1110 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
1112 result
= gcry_md_read(md
, 0);
1119 memcpy(ret
, result
, hash_size
);
1120 r
= (int) hash_size
;
1127 static int nsec3_is_good(DnsResourceRecord
*rr
, DnsAnswerFlags flags
, DnsResourceRecord
*nsec3
) {
1133 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
1136 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1137 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
1143 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1145 if (nsec3
== rr
) /* Shortcut */
1148 if (rr
->key
->class != nsec3
->key
->class)
1150 if (rr
->nsec3
.algorithm
!= nsec3
->nsec3
.algorithm
)
1152 if (rr
->nsec3
.iterations
!= nsec3
->nsec3
.iterations
)
1154 if (rr
->nsec3
.salt_size
!= nsec3
->nsec3
.salt_size
)
1156 if (memcmp(rr
->nsec3
.salt
, nsec3
->nsec3
.salt
, rr
->nsec3
.salt_size
) != 0)
1159 a
= DNS_RESOURCE_KEY_NAME(rr
->key
);
1160 r
= dns_name_parent(&a
); /* strip off hash */
1166 b
= DNS_RESOURCE_KEY_NAME(nsec3
->key
);
1167 r
= dns_name_parent(&b
); /* strip off hash */
1173 return dns_name_equal(a
, b
);
1176 static int dnssec_test_nsec3(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
, bool *authenticated
) {
1177 _cleanup_free_
char *next_closer_domain
= NULL
, *l
= NULL
;
1178 uint8_t hashed
[DNSSEC_HASH_SIZE_MAX
];
1179 const char *suffix
, *p
, *pp
= NULL
;
1180 DnsResourceRecord
*rr
, *suffix_rr
;
1181 DnsAnswerFlags flags
;
1187 assert(authenticated
);
1189 /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */
1190 suffix
= DNS_RESOURCE_KEY_NAME(key
);
1192 DNS_ANSWER_FOREACH_FLAGS(suffix_rr
, flags
, answer
) {
1193 r
= nsec3_is_good(suffix_rr
, flags
, NULL
);
1199 r
= dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr
->key
), 1, suffix
);
1206 /* Strip one label from the front */
1207 r
= dns_name_parent(&suffix
);
1214 *result
= DNSSEC_NSEC_NO_RR
;
1218 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1219 p
= DNS_RESOURCE_KEY_NAME(key
);
1221 _cleanup_free_
char *hashed_domain
= NULL
, *label
= NULL
;
1223 hashed_size
= dnssec_nsec3_hash(suffix_rr
, p
, hashed
);
1224 if (hashed_size
== -EOPNOTSUPP
) {
1225 *result
= DNSSEC_NSEC_UNSUPPORTED_ALGORITHM
;
1228 if (hashed_size
< 0)
1231 label
= base32hexmem(hashed
, hashed_size
, false);
1235 hashed_domain
= strjoin(label
, ".", suffix
, NULL
);
1239 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1241 r
= nsec3_is_good(rr
, flags
, suffix_rr
);
1247 if (rr
->nsec3
.next_hashed_name_size
!= (size_t) hashed_size
)
1250 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), hashed_domain
);
1254 a
= flags
& DNS_ANSWER_AUTHENTICATED
;
1255 goto found_closest_encloser
;
1259 /* We didn't find the closest encloser with this name,
1260 * but let's remember this domain name, it might be
1261 * the next closer name */
1265 /* Strip one label from the front */
1266 r
= dns_name_parent(&p
);
1273 *result
= DNSSEC_NSEC_NO_RR
;
1276 found_closest_encloser
:
1277 /* We found a closest encloser in 'p'; next closer is 'pp' */
1279 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1280 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_DNAME
))
1283 /* Ensure that this data is from the delegated domain
1284 * (i.e. originates from the "lower" DNS server), and isn't
1285 * just glue records (i.e. doesn't originate from the "upper"
1287 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_NS
) &&
1288 !bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_SOA
))
1292 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1293 *result
= bitmap_isset(rr
->nsec3
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1298 r
= dnssec_nsec3_hash(rr
, pp
, hashed
);
1301 if (r
!= hashed_size
)
1304 l
= base32hexmem(hashed
, hashed_size
, false);
1308 next_closer_domain
= strjoin(l
, ".", p
, NULL
);
1309 if (!next_closer_domain
)
1312 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1313 _cleanup_free_
char *label
= NULL
, *next_hashed_domain
= NULL
;
1315 r
= nsec3_is_good(rr
, flags
, suffix_rr
);
1321 label
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
1325 next_hashed_domain
= strjoin(label
, ".", p
, NULL
);
1326 if (!next_hashed_domain
)
1329 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), next_closer_domain
, next_hashed_domain
);
1333 if (rr
->nsec3
.flags
& 1)
1334 *result
= DNSSEC_NSEC_OPTOUT
;
1336 *result
= DNSSEC_NSEC_NXDOMAIN
;
1338 *authenticated
= a
&& (flags
& DNS_ANSWER_AUTHENTICATED
);
1343 *result
= DNSSEC_NSEC_NO_RR
;
1347 int dnssec_test_nsec(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
, bool *authenticated
) {
1348 DnsResourceRecord
*rr
;
1349 bool have_nsec3
= false;
1350 DnsAnswerFlags flags
;
1355 assert(authenticated
);
1357 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1359 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1361 if (rr
->key
->class != key
->class)
1364 switch (rr
->key
->type
) {
1368 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
1372 *result
= bitmap_isset(rr
->nsec
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1373 *authenticated
= flags
& DNS_ANSWER_AUTHENTICATED
;
1377 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
), rr
->nsec
.next_domain_name
);
1381 *result
= DNSSEC_NSEC_NXDOMAIN
;
1382 *authenticated
= flags
& DNS_ANSWER_AUTHENTICATED
;
1387 case DNS_TYPE_NSEC3
:
1393 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1395 return dnssec_test_nsec3(answer
, key
, result
, authenticated
);
1397 /* No approproate NSEC RR found, report this. */
1398 *result
= DNSSEC_NSEC_NO_RR
;
1402 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
1404 [DNSSEC_DOWNGRADE_OK
] = "downgrade-ok",
1405 [DNSSEC_YES
] = "yes",
1407 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
1409 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
1410 [DNSSEC_VALIDATED
] = "validated",
1411 [DNSSEC_INVALID
] = "invalid",
1412 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
1413 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
1414 [DNSSEC_NO_SIGNATURE
] = "no-signature",
1415 [DNSSEC_MISSING_KEY
] = "missing-key",
1416 [DNSSEC_UNSIGNED
] = "unsigned",
1417 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
1418 [DNSSEC_NSEC_MISMATCH
] = "nsec-mismatch",
1419 [DNSSEC_INCOMPATIBLE_SERVER
] = "incompatible-server",
1421 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);