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?
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
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 void initialize_libgcrypt(void) {
70 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P
))
73 p
= gcry_check_version("1.4.5");
76 gcry_control(GCRYCTL_DISABLE_SECMEM
);
77 gcry_control(GCRYCTL_INITIALIZATION_FINISHED
, 0);
80 uint16_t dnssec_keytag(DnsResourceRecord
*dnskey
) {
85 /* The algorithm from RFC 4034, Appendix B. */
88 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
90 sum
= (uint32_t) dnskey
->dnskey
.flags
+
91 ((((uint32_t) dnskey
->dnskey
.protocol
) << 8) + (uint32_t) dnskey
->dnskey
.algorithm
);
93 p
= dnskey
->dnskey
.key
;
95 for (i
= 0; i
< dnskey
->dnskey
.key_size
; i
++)
96 sum
+= (i
& 1) == 0 ? (uint32_t) p
[i
] << 8 : (uint32_t) p
[i
];
98 sum
+= (sum
>> 16) & UINT32_C(0xFFFF);
100 return sum
& UINT32_C(0xFFFF);
103 static int rr_compare(const void *a
, const void *b
) {
104 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
108 /* Let's order the RRs according to RFC 4034, Section 6.3 */
112 assert((*x
)->wire_format
);
115 assert((*y
)->wire_format
);
117 m
= MIN((*x
)->wire_format_size
, (*y
)->wire_format_size
);
119 r
= memcmp((*x
)->wire_format
, (*y
)->wire_format
, m
);
123 if ((*x
)->wire_format_size
< (*y
)->wire_format_size
)
125 else if ((*x
)->wire_format_size
> (*y
)->wire_format_size
)
131 static int dnssec_rsa_verify_raw(
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
) {
138 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
139 gcry_mpi_t n
= NULL
, e
= NULL
, s
= NULL
;
143 assert(hash_algorithm
);
145 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature
, signature_size
, NULL
);
151 ge
= gcry_mpi_scan(&e
, GCRYMPI_FMT_USG
, exponent
, exponent_size
, NULL
);
157 ge
= gcry_mpi_scan(&n
, GCRYMPI_FMT_USG
, modulus
, modulus_size
, NULL
);
163 ge
= gcry_sexp_build(&signature_sexp
,
165 "(sig-val (rsa (s %m)))",
173 ge
= gcry_sexp_build(&data_sexp
,
175 "(data (flags pkcs1) (hash %s %b))",
184 ge
= gcry_sexp_build(&public_key_sexp
,
186 "(public-key (rsa (n %m) (e %m)))",
194 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
195 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
198 log_debug("RSA signature check failed: %s", gpg_strerror(ge
));
212 gcry_sexp_release(public_key_sexp
);
214 gcry_sexp_release(signature_sexp
);
216 gcry_sexp_release(data_sexp
);
221 static int dnssec_rsa_verify(
222 const char *hash_algorithm
,
223 const void *hash
, size_t hash_size
,
224 DnsResourceRecord
*rrsig
,
225 DnsResourceRecord
*dnskey
) {
227 size_t exponent_size
, modulus_size
;
228 void *exponent
, *modulus
;
230 assert(hash_algorithm
);
232 assert(hash_size
> 0);
236 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
237 /* exponent is > 255 bytes long */
239 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
241 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[1]) << 8) |
242 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[2]);
244 if (exponent_size
< 256)
247 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
)
250 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
251 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
254 /* exponent is <= 255 bytes long */
256 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
257 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
259 if (exponent_size
<= 0)
262 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
)
265 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
266 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
269 return dnssec_rsa_verify_raw(
271 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
273 exponent
, exponent_size
,
274 modulus
, modulus_size
);
277 static int dnssec_ecdsa_verify_raw(
278 const char *hash_algorithm
,
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
) {
285 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
286 gcry_mpi_t q
= NULL
, r
= NULL
, s
= NULL
;
290 assert(hash_algorithm
);
292 ge
= gcry_mpi_scan(&r
, GCRYMPI_FMT_USG
, signature_r
, signature_r_size
, NULL
);
298 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature_s
, signature_s_size
, NULL
);
304 ge
= gcry_mpi_scan(&q
, GCRYMPI_FMT_USG
, key
, key_size
, NULL
);
310 ge
= gcry_sexp_build(&signature_sexp
,
312 "(sig-val (ecdsa (r %m) (s %m)))",
320 ge
= gcry_sexp_build(&data_sexp
,
322 "(data (flags rfc6979) (hash %s %b))",
331 ge
= gcry_sexp_build(&public_key_sexp
,
333 "(public-key (ecc (curve %s) (q %m)))",
341 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
342 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
345 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge
));
358 gcry_sexp_release(public_key_sexp
);
360 gcry_sexp_release(signature_sexp
);
362 gcry_sexp_release(data_sexp
);
367 static int dnssec_ecdsa_verify(
368 const char *hash_algorithm
,
370 const void *hash
, size_t hash_size
,
371 DnsResourceRecord
*rrsig
,
372 DnsResourceRecord
*dnskey
) {
383 if (algorithm
== DNSSEC_ALGORITHM_ECDSAP256SHA256
) {
385 curve
= "NIST P-256";
386 } else if (algorithm
== DNSSEC_ALGORITHM_ECDSAP384SHA384
) {
388 curve
= "NIST P-384";
392 if (dnskey
->dnskey
.key_size
!= key_size
* 2)
395 if (rrsig
->rrsig
.signature_size
!= key_size
* 2)
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);
402 return dnssec_ecdsa_verify_raw(
405 rrsig
->rrsig
.signature
, key_size
,
406 (uint8_t*) rrsig
->rrsig
.signature
+ key_size
, key_size
,
411 static void md_add_uint8(gcry_md_hd_t md
, uint8_t v
) {
412 gcry_md_write(md
, &v
, sizeof(v
));
415 static void md_add_uint16(gcry_md_hd_t md
, uint16_t v
) {
417 gcry_md_write(md
, &v
, sizeof(v
));
420 static void md_add_uint32(gcry_md_hd_t md
, uint32_t v
) {
422 gcry_md_write(md
, &v
, sizeof(v
));
425 static int dnssec_rrsig_expired(DnsResourceRecord
*rrsig
, usec_t realtime
) {
426 usec_t expiration
, inception
, skew
;
429 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
431 if (realtime
== USEC_INFINITY
)
432 realtime
= now(CLOCK_REALTIME
);
434 expiration
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
435 inception
= rrsig
->rrsig
.inception
* USEC_PER_SEC
;
437 if (inception
> expiration
)
438 return -EKEYREJECTED
;
440 /* Permit a certain amount of clock skew of 10% of the valid
441 * time range. This takes inspiration from unbound's
443 skew
= (expiration
- inception
) / 10;
447 if (inception
< skew
)
452 if (expiration
+ skew
< expiration
)
453 expiration
= USEC_INFINITY
;
457 return realtime
< inception
|| realtime
> expiration
;
460 static int algorithm_to_gcrypt_md(uint8_t algorithm
) {
462 /* Translates a DNSSEC signature algorithm into a gcrypt
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
474 case DNSSEC_ALGORITHM_RSASHA1
:
475 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
478 case DNSSEC_ALGORITHM_RSASHA256
:
479 case DNSSEC_ALGORITHM_ECDSAP256SHA256
:
480 return GCRY_MD_SHA256
;
482 case DNSSEC_ALGORITHM_ECDSAP384SHA384
:
483 return GCRY_MD_SHA384
;
485 case DNSSEC_ALGORITHM_RSASHA512
:
486 return GCRY_MD_SHA512
;
493 int dnssec_verify_rrset(
496 DnsResourceRecord
*rrsig
,
497 DnsResourceRecord
*dnskey
,
499 DnssecResult
*result
) {
501 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
504 DnsResourceRecord
**list
, *rr
;
505 gcry_md_hd_t md
= NULL
;
513 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
514 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
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. */
520 md_algorithm
= algorithm_to_gcrypt_md(rrsig
->rrsig
.algorithm
);
521 if (md_algorithm
== -EOPNOTSUPP
) {
522 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
525 if (md_algorithm
< 0)
528 r
= dnssec_rrsig_expired(rrsig
, realtime
);
532 *result
= DNSSEC_SIGNATURE_EXPIRED
;
536 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
537 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
539 DNS_ANSWER_FOREACH(rr
, a
) {
540 r
= dns_resource_key_equal(key
, rr
->key
);
546 /* We need the wire format for ordering, and digest calculation */
547 r
= dns_resource_record_to_wire_format(rr
, true);
553 if (n
> VERIFY_RRS_MAX
)
560 /* Bring the RRs into canonical order */
561 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
563 /* OK, the RRs are now in canonical order. Let's calculate the digest */
564 initialize_libgcrypt();
566 hash_size
= gcry_md_get_algo_dlen(md_algorithm
);
567 assert(hash_size
> 0);
569 gcry_md_open(&md
, md_algorithm
, 0);
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
);
581 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
584 gcry_md_write(md
, wire_format_name
, r
);
586 for (k
= 0; k
< n
; k
++) {
591 r
= dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr
->key
), rrsig
->rrsig
.labels
, &suffix
);
594 if (r
> 0) /* This is a wildcard! */
595 gcry_md_write(md
, (uint8_t[]) { 1, '*'}, 2);
597 r
= dns_name_to_wire_format(suffix
, wire_format_name
, sizeof(wire_format_name
), true);
600 gcry_md_write(md
, wire_format_name
, r
);
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
);
606 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
607 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
610 md_add_uint16(md
, (uint16_t) l
);
611 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
614 hash
= gcry_md_read(md
, 0);
620 switch (rrsig
->rrsig
.algorithm
) {
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(
627 gcry_md_algo_name(md_algorithm
),
633 case DNSSEC_ALGORITHM_ECDSAP256SHA256
:
634 case DNSSEC_ALGORITHM_ECDSAP384SHA384
:
635 r
= dnssec_ecdsa_verify(
636 gcry_md_algo_name(md_algorithm
),
637 rrsig
->rrsig
.algorithm
,
647 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
655 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
660 /* Checks if the specified DNSKEY RR matches the key used for
661 * the signature in the specified RRSIG RR */
663 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
666 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
668 if (dnskey
->key
->class != rrsig
->key
->class)
670 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
672 if (dnskey
->dnskey
.protocol
!= 3)
674 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
677 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
680 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
683 int dnssec_key_match_rrsig(const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
689 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
691 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
693 if (rrsig
->key
->class != key
->class)
695 if (rrsig
->rrsig
.type_covered
!= key
->type
)
698 /* Make sure signer is a parent of the RRset */
699 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig
->key
), rrsig
->rrsig
.signer
);
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
));
707 if (r
< rrsig
->rrsig
.labels
)
710 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
713 static int dnssec_fix_rrset_ttl(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
, usec_t realtime
) {
714 DnsResourceRecord
*rr
;
720 DNS_ANSWER_FOREACH(rr
, a
) {
721 r
= dns_resource_key_equal(key
, rr
->key
);
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
;
737 int dnssec_verify_rrset_search(
740 DnsAnswer
*validated_dnskeys
,
742 DnssecResult
*result
) {
744 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
745 DnsResourceRecord
*rrsig
;
751 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
753 if (!a
|| a
->n_rrs
<= 0)
756 /* Iterate through each RRSIG RR. */
757 DNS_ANSWER_FOREACH(rrsig
, a
) {
758 DnsResourceRecord
*dnskey
;
759 DnsAnswerFlags flags
;
761 /* Is this an RRSIG RR that applies to RRs matching our key? */
762 r
= dnssec_key_match_rrsig(key
, rrsig
);
770 /* Look for a matching key */
771 DNS_ANSWER_FOREACH_FLAGS(dnskey
, flags
, validated_dnskeys
) {
772 DnssecResult one_result
;
774 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
777 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
778 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
784 /* Take the time here, if it isn't set yet, so
785 * that we do all validations with the same
787 if (realtime
== USEC_INFINITY
)
788 realtime
= now(CLOCK_REALTIME
);
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
795 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
799 switch (one_result
) {
801 case DNSSEC_VALIDATED
:
802 /* Yay, the RR has been validated,
803 * return immediately, but fix up the expiry */
804 r
= dnssec_fix_rrset_ttl(a
, key
, rrsig
, realtime
);
808 *result
= DNSSEC_VALIDATED
;
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;
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;
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;
837 assert_not_reached("Unexpected DNSSEC validation result");
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
)
847 *result
= DNSSEC_INVALID
;
848 else if (found_rrsig
)
849 *result
= DNSSEC_MISSING_KEY
;
851 *result
= DNSSEC_NO_SIGNATURE
;
856 int dnssec_has_rrsig(DnsAnswer
*a
, const DnsResourceKey
*key
) {
857 DnsResourceRecord
*rr
;
860 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
862 DNS_ANSWER_FOREACH(rr
, a
) {
863 r
= dnssec_key_match_rrsig(key
, rr
);
873 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
877 /* Converts the specified hostname into DNSSEC canonicalized
886 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
894 /* DNSSEC validation is always done on the ASCII version of the label */
895 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
902 if (buffer_max
< (size_t) r
+ 2)
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. */
909 if (memchr(buffer
, '.', r
))
912 for (i
= 0; i
< (size_t) r
; i
++) {
913 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
914 buffer
[i
] = buffer
[i
] - 'A' + 'a';
926 /* Not even a single label: this is the root domain name */
928 assert(buffer_max
> 2);
938 static int digest_to_gcrypt_md(uint8_t algorithm
) {
940 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
944 case DNSSEC_DIGEST_SHA1
:
947 case DNSSEC_DIGEST_SHA256
:
948 return GCRY_MD_SHA256
;
950 case DNSSEC_DIGEST_SHA384
:
951 return GCRY_MD_SHA384
;
958 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
959 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
960 gcry_md_hd_t md
= NULL
;
968 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
970 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
972 if (ds
->key
->type
!= DNS_TYPE_DS
)
974 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
975 return -EKEYREJECTED
;
976 if (dnskey
->dnskey
.protocol
!= 3)
977 return -EKEYREJECTED
;
979 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
981 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
984 initialize_libgcrypt();
986 md_algorithm
= digest_to_gcrypt_md(ds
->ds
.digest_type
);
987 if (md_algorithm
< 0)
990 hash_size
= gcry_md_get_algo_dlen(md_algorithm
);
991 assert(hash_size
> 0);
993 if (ds
->ds
.digest_size
!= hash_size
)
996 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
1000 gcry_md_open(&md
, md_algorithm
, 0);
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
);
1010 result
= gcry_md_read(md
, 0);
1016 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
1023 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
1024 DnsResourceRecord
*ds
;
1025 DnsAnswerFlags flags
;
1030 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
1033 DNS_ANSWER_FOREACH_FLAGS(ds
, flags
, validated_ds
) {
1035 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1038 if (ds
->key
->type
!= DNS_TYPE_DS
)
1041 if (ds
->key
->class != dnskey
->key
->class)
1044 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), DNS_RESOURCE_KEY_NAME(ds
->key
));
1050 r
= dnssec_verify_dnskey(dnskey
, ds
);
1060 static int nsec3_hash_to_gcrypt_md(uint8_t algorithm
) {
1062 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1064 switch (algorithm
) {
1066 case NSEC3_ALGORITHM_SHA1
:
1067 return GCRY_MD_SHA1
;
1074 int dnssec_nsec3_hash(DnsResourceRecord
*nsec3
, const char *name
, void *ret
) {
1075 uint8_t wire_format
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
1076 gcry_md_hd_t md
= NULL
;
1087 if (nsec3
->key
->type
!= DNS_TYPE_NSEC3
)
1090 algorithm
= nsec3_hash_to_gcrypt_md(nsec3
->nsec3
.algorithm
);
1094 initialize_libgcrypt();
1096 hash_size
= gcry_md_get_algo_dlen(algorithm
);
1097 assert(hash_size
> 0);
1099 if (nsec3
->nsec3
.next_hashed_name_size
!= hash_size
)
1102 r
= dns_name_to_wire_format(name
, wire_format
, sizeof(wire_format
), true);
1106 gcry_md_open(&md
, algorithm
, 0);
1110 gcry_md_write(md
, wire_format
, r
);
1111 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
1113 result
= gcry_md_read(md
, 0);
1119 for (k
= 0; k
< nsec3
->nsec3
.iterations
; k
++) {
1120 uint8_t tmp
[hash_size
];
1121 memcpy(tmp
, result
, hash_size
);
1124 gcry_md_write(md
, tmp
, hash_size
);
1125 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
1127 result
= gcry_md_read(md
, 0);
1134 memcpy(ret
, result
, hash_size
);
1135 r
= (int) hash_size
;
1142 static int nsec3_is_good(DnsResourceRecord
*rr
, DnsAnswerFlags flags
, DnsResourceRecord
*nsec3
) {
1148 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
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))
1155 /* Ignore NSEC3 RRs whose algorithm we don't know */
1156 if (nsec3_hash_to_gcrypt_md(rr
->nsec3
.algorithm
) < 0)
1162 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1164 if (nsec3
== rr
) /* Shortcut */
1167 if (rr
->key
->class != nsec3
->key
->class)
1169 if (rr
->nsec3
.algorithm
!= nsec3
->nsec3
.algorithm
)
1171 if (rr
->nsec3
.iterations
!= nsec3
->nsec3
.iterations
)
1173 if (rr
->nsec3
.salt_size
!= nsec3
->nsec3
.salt_size
)
1175 if (memcmp(rr
->nsec3
.salt
, nsec3
->nsec3
.salt
, rr
->nsec3
.salt_size
) != 0)
1178 a
= DNS_RESOURCE_KEY_NAME(rr
->key
);
1179 r
= dns_name_parent(&a
); /* strip off hash */
1185 b
= DNS_RESOURCE_KEY_NAME(nsec3
->key
);
1186 r
= dns_name_parent(&b
); /* strip off hash */
1192 return dns_name_equal(a
, b
);
1195 static int dnssec_test_nsec3(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
, bool *authenticated
) {
1196 _cleanup_free_
char *next_closer_domain
= NULL
, *l
= NULL
;
1197 uint8_t hashed
[DNSSEC_HASH_SIZE_MAX
];
1198 const char *zone
, *p
, *pp
= NULL
;
1199 DnsResourceRecord
*rr
, *enclosure_rr
, *suffix_rr
;
1200 DnsAnswerFlags flags
;
1206 assert(authenticated
);
1208 /* First step, find the zone name and the NSEC3 parameters of the zone.
1209 * it is sufficient to look for the longest common suffix we find with
1210 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1211 * records from a given zone in a response must use the same
1213 zone
= DNS_RESOURCE_KEY_NAME(key
);
1215 DNS_ANSWER_FOREACH_FLAGS(suffix_rr
, flags
, answer
) {
1216 r
= nsec3_is_good(suffix_rr
, flags
, NULL
);
1222 r
= dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr
->key
), 1, zone
);
1229 /* Strip one label from the front */
1230 r
= dns_name_parent(&zone
);
1237 *result
= DNSSEC_NSEC_NO_RR
;
1241 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1242 p
= DNS_RESOURCE_KEY_NAME(key
);
1244 _cleanup_free_
char *hashed_domain
= NULL
, *label
= NULL
;
1246 hashed_size
= dnssec_nsec3_hash(suffix_rr
, p
, hashed
);
1247 if (hashed_size
== -EOPNOTSUPP
) {
1248 *result
= DNSSEC_NSEC_UNSUPPORTED_ALGORITHM
;
1251 if (hashed_size
< 0)
1254 label
= base32hexmem(hashed
, hashed_size
, false);
1258 hashed_domain
= strjoin(label
, ".", zone
, NULL
);
1262 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr
, flags
, answer
) {
1264 r
= nsec3_is_good(enclosure_rr
, flags
, suffix_rr
);
1270 if (enclosure_rr
->nsec3
.next_hashed_name_size
!= (size_t) hashed_size
)
1273 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr
->key
), hashed_domain
);
1277 a
= flags
& DNS_ANSWER_AUTHENTICATED
;
1278 goto found_closest_encloser
;
1282 /* We didn't find the closest encloser with this name,
1283 * but let's remember this domain name, it might be
1284 * the next closer name */
1288 /* Strip one label from the front */
1289 r
= dns_name_parent(&p
);
1296 *result
= DNSSEC_NSEC_NO_RR
;
1299 found_closest_encloser
:
1300 /* We found a closest encloser in 'p'; next closer is 'pp' */
1302 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1303 if (bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_DNAME
))
1306 /* Ensure that this data is from the delegated domain
1307 * (i.e. originates from the "lower" DNS server), and isn't
1308 * just glue records (i.e. doesn't originate from the "upper"
1310 if (bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_NS
) &&
1311 !bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_SOA
))
1315 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1316 *result
= bitmap_isset(enclosure_rr
->nsec3
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1321 r
= dnssec_nsec3_hash(enclosure_rr
, pp
, hashed
);
1324 if (r
!= hashed_size
)
1327 l
= base32hexmem(hashed
, hashed_size
, false);
1331 next_closer_domain
= strjoin(l
, ".", p
, NULL
);
1332 if (!next_closer_domain
)
1335 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1336 _cleanup_free_
char *label
= NULL
, *next_hashed_domain
= NULL
;
1338 r
= nsec3_is_good(rr
, flags
, suffix_rr
);
1344 label
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
1348 next_hashed_domain
= strjoin(label
, ".", p
, NULL
);
1349 if (!next_hashed_domain
)
1352 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), next_closer_domain
, next_hashed_domain
);
1356 if (rr
->nsec3
.flags
& 1)
1357 *result
= DNSSEC_NSEC_OPTOUT
;
1359 *result
= DNSSEC_NSEC_NXDOMAIN
;
1361 *authenticated
= a
&& (flags
& DNS_ANSWER_AUTHENTICATED
);
1366 *result
= DNSSEC_NSEC_NO_RR
;
1370 int dnssec_test_nsec(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
, bool *authenticated
) {
1371 DnsResourceRecord
*rr
;
1372 bool have_nsec3
= false;
1373 DnsAnswerFlags flags
;
1378 assert(authenticated
);
1380 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1382 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1384 if (rr
->key
->class != key
->class)
1387 switch (rr
->key
->type
) {
1391 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
1395 *result
= bitmap_isset(rr
->nsec
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1396 *authenticated
= flags
& DNS_ANSWER_AUTHENTICATED
;
1400 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
), rr
->nsec
.next_domain_name
);
1404 *result
= DNSSEC_NSEC_NXDOMAIN
;
1405 *authenticated
= flags
& DNS_ANSWER_AUTHENTICATED
;
1410 case DNS_TYPE_NSEC3
:
1416 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1418 return dnssec_test_nsec3(answer
, key
, result
, authenticated
);
1420 /* No approproate NSEC RR found, report this. */
1421 *result
= DNSSEC_NSEC_NO_RR
;
1425 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
1427 [DNSSEC_DOWNGRADE_OK
] = "downgrade-ok",
1428 [DNSSEC_YES
] = "yes",
1430 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
1432 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
1433 [DNSSEC_VALIDATED
] = "validated",
1434 [DNSSEC_INVALID
] = "invalid",
1435 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
1436 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
1437 [DNSSEC_NO_SIGNATURE
] = "no-signature",
1438 [DNSSEC_MISSING_KEY
] = "missing-key",
1439 [DNSSEC_UNSIGNED
] = "unsigned",
1440 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
1441 [DNSSEC_NSEC_MISMATCH
] = "nsec-mismatch",
1442 [DNSSEC_INCOMPATIBLE_SERVER
] = "incompatible-server",
1444 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);