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 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
39 * - multi-label zone compatibility
40 * - cname/dname compatibility
42 * - per-interface DNSSEC setting
46 #define VERIFY_RRS_MAX 256
47 #define MAX_KEY_SIZE (32*1024)
49 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
50 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
52 /* Maximum number of NSEC3 iterations we'll do. */
53 #define NSEC3_ITERATIONS_MAX 2048
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
, bool mask_revoke
) {
84 /* The algorithm from RFC 4034, Appendix B. */
87 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
89 f
= (uint32_t) dnskey
->dnskey
.flags
;
92 f
&= ~DNSKEY_FLAG_REVOKE
;
94 sum
= f
+ ((((uint32_t) dnskey
->dnskey
.protocol
) << 8) + (uint32_t) dnskey
->dnskey
.algorithm
);
96 p
= dnskey
->dnskey
.key
;
98 for (i
= 0; i
< dnskey
->dnskey
.key_size
; i
++)
99 sum
+= (i
& 1) == 0 ? (uint32_t) p
[i
] << 8 : (uint32_t) p
[i
];
101 sum
+= (sum
>> 16) & UINT32_C(0xFFFF);
103 return sum
& UINT32_C(0xFFFF);
106 static int rr_compare(const void *a
, const void *b
) {
107 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
111 /* Let's order the RRs according to RFC 4034, Section 6.3 */
115 assert((*x
)->wire_format
);
118 assert((*y
)->wire_format
);
120 m
= MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x
), DNS_RESOURCE_RECORD_RDATA_SIZE(*y
));
122 r
= memcmp(DNS_RESOURCE_RECORD_RDATA(*x
), DNS_RESOURCE_RECORD_RDATA(*y
), m
);
126 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x
) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y
))
128 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x
) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y
))
134 static int dnssec_rsa_verify_raw(
135 const char *hash_algorithm
,
136 const void *signature
, size_t signature_size
,
137 const void *data
, size_t data_size
,
138 const void *exponent
, size_t exponent_size
,
139 const void *modulus
, size_t modulus_size
) {
141 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
142 gcry_mpi_t n
= NULL
, e
= NULL
, s
= NULL
;
146 assert(hash_algorithm
);
148 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature
, signature_size
, NULL
);
154 ge
= gcry_mpi_scan(&e
, GCRYMPI_FMT_USG
, exponent
, exponent_size
, NULL
);
160 ge
= gcry_mpi_scan(&n
, GCRYMPI_FMT_USG
, modulus
, modulus_size
, NULL
);
166 ge
= gcry_sexp_build(&signature_sexp
,
168 "(sig-val (rsa (s %m)))",
176 ge
= gcry_sexp_build(&data_sexp
,
178 "(data (flags pkcs1) (hash %s %b))",
187 ge
= gcry_sexp_build(&public_key_sexp
,
189 "(public-key (rsa (n %m) (e %m)))",
197 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
198 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
201 log_debug("RSA signature check failed: %s", gpg_strerror(ge
));
215 gcry_sexp_release(public_key_sexp
);
217 gcry_sexp_release(signature_sexp
);
219 gcry_sexp_release(data_sexp
);
224 static int dnssec_rsa_verify(
225 const char *hash_algorithm
,
226 const void *hash
, size_t hash_size
,
227 DnsResourceRecord
*rrsig
,
228 DnsResourceRecord
*dnskey
) {
230 size_t exponent_size
, modulus_size
;
231 void *exponent
, *modulus
;
233 assert(hash_algorithm
);
235 assert(hash_size
> 0);
239 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
240 /* exponent is > 255 bytes long */
242 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
244 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[1]) << 8) |
245 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[2]);
247 if (exponent_size
< 256)
250 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
)
253 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
254 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
257 /* exponent is <= 255 bytes long */
259 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
260 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
262 if (exponent_size
<= 0)
265 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
)
268 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
269 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
272 return dnssec_rsa_verify_raw(
274 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
276 exponent
, exponent_size
,
277 modulus
, modulus_size
);
280 static int dnssec_ecdsa_verify_raw(
281 const char *hash_algorithm
,
283 const void *signature_r
, size_t signature_r_size
,
284 const void *signature_s
, size_t signature_s_size
,
285 const void *data
, size_t data_size
,
286 const void *key
, size_t key_size
) {
288 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
289 gcry_mpi_t q
= NULL
, r
= NULL
, s
= NULL
;
293 assert(hash_algorithm
);
295 ge
= gcry_mpi_scan(&r
, GCRYMPI_FMT_USG
, signature_r
, signature_r_size
, NULL
);
301 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature_s
, signature_s_size
, NULL
);
307 ge
= gcry_mpi_scan(&q
, GCRYMPI_FMT_USG
, key
, key_size
, NULL
);
313 ge
= gcry_sexp_build(&signature_sexp
,
315 "(sig-val (ecdsa (r %m) (s %m)))",
323 ge
= gcry_sexp_build(&data_sexp
,
325 "(data (flags rfc6979) (hash %s %b))",
334 ge
= gcry_sexp_build(&public_key_sexp
,
336 "(public-key (ecc (curve %s) (q %m)))",
344 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
345 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
348 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge
));
361 gcry_sexp_release(public_key_sexp
);
363 gcry_sexp_release(signature_sexp
);
365 gcry_sexp_release(data_sexp
);
370 static int dnssec_ecdsa_verify(
371 const char *hash_algorithm
,
373 const void *hash
, size_t hash_size
,
374 DnsResourceRecord
*rrsig
,
375 DnsResourceRecord
*dnskey
) {
386 if (algorithm
== DNSSEC_ALGORITHM_ECDSAP256SHA256
) {
388 curve
= "NIST P-256";
389 } else if (algorithm
== DNSSEC_ALGORITHM_ECDSAP384SHA384
) {
391 curve
= "NIST P-384";
395 if (dnskey
->dnskey
.key_size
!= key_size
* 2)
398 if (rrsig
->rrsig
.signature_size
!= key_size
* 2)
401 q
= alloca(key_size
*2 + 1);
402 q
[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
403 memcpy(q
+1, dnskey
->dnskey
.key
, key_size
*2);
405 return dnssec_ecdsa_verify_raw(
408 rrsig
->rrsig
.signature
, key_size
,
409 (uint8_t*) rrsig
->rrsig
.signature
+ key_size
, key_size
,
414 static void md_add_uint8(gcry_md_hd_t md
, uint8_t v
) {
415 gcry_md_write(md
, &v
, sizeof(v
));
418 static void md_add_uint16(gcry_md_hd_t md
, uint16_t v
) {
420 gcry_md_write(md
, &v
, sizeof(v
));
423 static void md_add_uint32(gcry_md_hd_t md
, uint32_t v
) {
425 gcry_md_write(md
, &v
, sizeof(v
));
428 static int dnssec_rrsig_expired(DnsResourceRecord
*rrsig
, usec_t realtime
) {
429 usec_t expiration
, inception
, skew
;
432 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
434 if (realtime
== USEC_INFINITY
)
435 realtime
= now(CLOCK_REALTIME
);
437 expiration
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
438 inception
= rrsig
->rrsig
.inception
* USEC_PER_SEC
;
440 if (inception
> expiration
)
441 return -EKEYREJECTED
;
443 /* Permit a certain amount of clock skew of 10% of the valid
444 * time range. This takes inspiration from unbound's
446 skew
= (expiration
- inception
) / 10;
450 if (inception
< skew
)
455 if (expiration
+ skew
< expiration
)
456 expiration
= USEC_INFINITY
;
460 return realtime
< inception
|| realtime
> expiration
;
463 static int algorithm_to_gcrypt_md(uint8_t algorithm
) {
465 /* Translates a DNSSEC signature algorithm into a gcrypt
468 * Note that we implement all algorithms listed as "Must
469 * implement" and "Recommended to Implement" in RFC6944. We
470 * don't implement any algorithms that are listed as
471 * "Optional" or "Must Not Implement". Specifically, we do not
472 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
477 case DNSSEC_ALGORITHM_RSASHA1
:
478 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
481 case DNSSEC_ALGORITHM_RSASHA256
:
482 case DNSSEC_ALGORITHM_ECDSAP256SHA256
:
483 return GCRY_MD_SHA256
;
485 case DNSSEC_ALGORITHM_ECDSAP384SHA384
:
486 return GCRY_MD_SHA384
;
488 case DNSSEC_ALGORITHM_RSASHA512
:
489 return GCRY_MD_SHA512
;
496 int dnssec_verify_rrset(
498 const DnsResourceKey
*key
,
499 DnsResourceRecord
*rrsig
,
500 DnsResourceRecord
*dnskey
,
502 DnssecResult
*result
) {
504 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
507 DnsResourceRecord
**list
, *rr
;
508 gcry_md_hd_t md
= NULL
;
516 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
517 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
519 /* Verifies the the RRSet matching the specified "key" in "a",
520 * using the signature "rrsig" and the key "dnskey". It's
521 * assumed the RRSIG and DNSKEY match. */
523 md_algorithm
= algorithm_to_gcrypt_md(rrsig
->rrsig
.algorithm
);
524 if (md_algorithm
== -EOPNOTSUPP
) {
525 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
528 if (md_algorithm
< 0)
531 r
= dnssec_rrsig_expired(rrsig
, realtime
);
535 *result
= DNSSEC_SIGNATURE_EXPIRED
;
539 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
540 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
542 DNS_ANSWER_FOREACH(rr
, a
) {
543 r
= dns_resource_key_equal(key
, rr
->key
);
549 /* We need the wire format for ordering, and digest calculation */
550 r
= dns_resource_record_to_wire_format(rr
, true);
556 if (n
> VERIFY_RRS_MAX
)
563 /* Bring the RRs into canonical order */
564 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
566 /* OK, the RRs are now in canonical order. Let's calculate the digest */
567 initialize_libgcrypt();
569 hash_size
= gcry_md_get_algo_dlen(md_algorithm
);
570 assert(hash_size
> 0);
572 gcry_md_open(&md
, md_algorithm
, 0);
576 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
577 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
578 md_add_uint8(md
, rrsig
->rrsig
.labels
);
579 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
580 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
581 md_add_uint32(md
, rrsig
->rrsig
.inception
);
582 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
584 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
587 gcry_md_write(md
, wire_format_name
, r
);
589 for (k
= 0; k
< n
; k
++) {
594 r
= dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr
->key
), rrsig
->rrsig
.labels
, &suffix
);
597 if (r
> 0) /* This is a wildcard! */
598 gcry_md_write(md
, (uint8_t[]) { 1, '*'}, 2);
600 r
= dns_name_to_wire_format(suffix
, wire_format_name
, sizeof(wire_format_name
), true);
603 gcry_md_write(md
, wire_format_name
, r
);
605 md_add_uint16(md
, rr
->key
->type
);
606 md_add_uint16(md
, rr
->key
->class);
607 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
609 l
= DNS_RESOURCE_RECORD_RDATA_SIZE(rr
);
612 md_add_uint16(md
, (uint16_t) l
);
613 gcry_md_write(md
, DNS_RESOURCE_RECORD_RDATA(rr
), l
);
616 hash
= gcry_md_read(md
, 0);
622 switch (rrsig
->rrsig
.algorithm
) {
624 case DNSSEC_ALGORITHM_RSASHA1
:
625 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
626 case DNSSEC_ALGORITHM_RSASHA256
:
627 case DNSSEC_ALGORITHM_RSASHA512
:
628 r
= dnssec_rsa_verify(
629 gcry_md_algo_name(md_algorithm
),
635 case DNSSEC_ALGORITHM_ECDSAP256SHA256
:
636 case DNSSEC_ALGORITHM_ECDSAP384SHA384
:
637 r
= dnssec_ecdsa_verify(
638 gcry_md_algo_name(md_algorithm
),
639 rrsig
->rrsig
.algorithm
,
649 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
657 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
, bool revoked_ok
) {
662 /* Checks if the specified DNSKEY RR matches the key used for
663 * the signature in the specified RRSIG RR */
665 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
668 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
670 if (dnskey
->key
->class != rrsig
->key
->class)
672 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
674 if (!revoked_ok
&& (dnskey
->dnskey
.flags
& DNSKEY_FLAG_REVOKE
))
676 if (dnskey
->dnskey
.protocol
!= 3)
678 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
681 if (dnssec_keytag(dnskey
, false) != rrsig
->rrsig
.key_tag
)
684 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
687 int dnssec_key_match_rrsig(const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
693 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
695 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
697 if (rrsig
->key
->class != key
->class)
699 if (rrsig
->rrsig
.type_covered
!= key
->type
)
702 /* Make sure signer is a parent of the RRset */
703 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig
->key
), rrsig
->rrsig
.signer
);
707 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
708 r
= dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig
->key
));
711 if (r
< rrsig
->rrsig
.labels
)
714 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
717 static int dnssec_fix_rrset_ttl(DnsAnswer
*a
, const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
, usec_t realtime
) {
718 DnsResourceRecord
*rr
;
724 DNS_ANSWER_FOREACH(rr
, a
) {
725 r
= dns_resource_key_equal(key
, rr
->key
);
731 /* Pick the TTL as the minimum of the RR's TTL, the
732 * RR's original TTL according to the RRSIG and the
733 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
734 rr
->ttl
= MIN3(rr
->ttl
, rrsig
->rrsig
.original_ttl
, rrsig
->ttl
);
735 rr
->expiry
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
741 int dnssec_verify_rrset_search(
743 const DnsResourceKey
*key
,
744 DnsAnswer
*validated_dnskeys
,
746 DnssecResult
*result
) {
748 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
749 DnsResourceRecord
*rrsig
;
755 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
757 if (!a
|| a
->n_rrs
<= 0)
760 /* Iterate through each RRSIG RR. */
761 DNS_ANSWER_FOREACH(rrsig
, a
) {
762 DnsResourceRecord
*dnskey
;
763 DnsAnswerFlags flags
;
765 /* Is this an RRSIG RR that applies to RRs matching our key? */
766 r
= dnssec_key_match_rrsig(key
, rrsig
);
774 /* Look for a matching key */
775 DNS_ANSWER_FOREACH_FLAGS(dnskey
, flags
, validated_dnskeys
) {
776 DnssecResult one_result
;
778 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
781 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
782 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
, false);
788 /* Take the time here, if it isn't set yet, so
789 * that we do all validations with the same
791 if (realtime
== USEC_INFINITY
)
792 realtime
= now(CLOCK_REALTIME
);
794 /* Yay, we found a matching RRSIG with a matching
795 * DNSKEY, awesome. Now let's verify all entries of
796 * the RRSet against the RRSIG and DNSKEY
799 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
803 switch (one_result
) {
805 case DNSSEC_VALIDATED
:
806 /* Yay, the RR has been validated,
807 * return immediately, but fix up the expiry */
808 r
= dnssec_fix_rrset_ttl(a
, key
, rrsig
, realtime
);
812 *result
= DNSSEC_VALIDATED
;
816 /* If the signature is invalid, let's try another
817 key and/or signature. After all they
818 key_tags and stuff are not unique, and
819 might be shared by multiple keys. */
820 found_invalid
= true;
823 case DNSSEC_UNSUPPORTED_ALGORITHM
:
824 /* If the key algorithm is
825 unsupported, try another
826 RRSIG/DNSKEY pair, but remember we
827 encountered this, so that we can
828 return a proper error when we
829 encounter nothing better. */
830 found_unsupported_algorithm
= true;
833 case DNSSEC_SIGNATURE_EXPIRED
:
834 /* If the signature is expired, try
835 another one, but remember it, so
836 that we can return this */
837 found_expired_rrsig
= true;
841 assert_not_reached("Unexpected DNSSEC validation result");
846 if (found_expired_rrsig
)
847 *result
= DNSSEC_SIGNATURE_EXPIRED
;
848 else if (found_unsupported_algorithm
)
849 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
850 else if (found_invalid
)
851 *result
= DNSSEC_INVALID
;
852 else if (found_rrsig
)
853 *result
= DNSSEC_MISSING_KEY
;
855 *result
= DNSSEC_NO_SIGNATURE
;
860 int dnssec_has_rrsig(DnsAnswer
*a
, const DnsResourceKey
*key
) {
861 DnsResourceRecord
*rr
;
864 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
866 DNS_ANSWER_FOREACH(rr
, a
) {
867 r
= dnssec_key_match_rrsig(key
, rr
);
877 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
881 /* Converts the specified hostname into DNSSEC canonicalized
890 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
898 /* DNSSEC validation is always done on the ASCII version of the label */
899 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
906 if (buffer_max
< (size_t) r
+ 2)
909 /* The DNSSEC canonical form is not clear on what to
910 * do with dots appearing in labels, the way DNS-SD
911 * does it. Refuse it for now. */
913 if (memchr(buffer
, '.', r
))
916 for (i
= 0; i
< (size_t) r
; i
++) {
917 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
918 buffer
[i
] = buffer
[i
] - 'A' + 'a';
930 /* Not even a single label: this is the root domain name */
932 assert(buffer_max
> 2);
942 static int digest_to_gcrypt_md(uint8_t algorithm
) {
944 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
948 case DNSSEC_DIGEST_SHA1
:
951 case DNSSEC_DIGEST_SHA256
:
952 return GCRY_MD_SHA256
;
954 case DNSSEC_DIGEST_SHA384
:
955 return GCRY_MD_SHA384
;
962 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
, bool mask_revoke
) {
963 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
964 gcry_md_hd_t md
= NULL
;
972 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
974 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
976 if (ds
->key
->type
!= DNS_TYPE_DS
)
978 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
979 return -EKEYREJECTED
;
980 if (!mask_revoke
&& (dnskey
->dnskey
.flags
& DNSKEY_FLAG_REVOKE
))
981 return -EKEYREJECTED
;
982 if (dnskey
->dnskey
.protocol
!= 3)
983 return -EKEYREJECTED
;
985 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
987 if (dnssec_keytag(dnskey
, mask_revoke
) != ds
->ds
.key_tag
)
990 initialize_libgcrypt();
992 md_algorithm
= digest_to_gcrypt_md(ds
->ds
.digest_type
);
993 if (md_algorithm
< 0)
996 hash_size
= gcry_md_get_algo_dlen(md_algorithm
);
997 assert(hash_size
> 0);
999 if (ds
->ds
.digest_size
!= hash_size
)
1002 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
1006 gcry_md_open(&md
, md_algorithm
, 0);
1010 gcry_md_write(md
, owner_name
, r
);
1012 md_add_uint16(md
, dnskey
->dnskey
.flags
& ~DNSKEY_FLAG_REVOKE
);
1014 md_add_uint16(md
, dnskey
->dnskey
.flags
);
1015 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
1016 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
1017 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
1019 result
= gcry_md_read(md
, 0);
1025 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
1032 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
1033 DnsResourceRecord
*ds
;
1034 DnsAnswerFlags flags
;
1039 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
1042 DNS_ANSWER_FOREACH_FLAGS(ds
, flags
, validated_ds
) {
1044 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1047 if (ds
->key
->type
!= DNS_TYPE_DS
)
1050 if (ds
->key
->class != dnskey
->key
->class)
1053 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), DNS_RESOURCE_KEY_NAME(ds
->key
));
1059 r
= dnssec_verify_dnskey(dnskey
, ds
, false);
1060 if (r
== -EKEYREJECTED
)
1061 return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
1071 static int nsec3_hash_to_gcrypt_md(uint8_t algorithm
) {
1073 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1075 switch (algorithm
) {
1077 case NSEC3_ALGORITHM_SHA1
:
1078 return GCRY_MD_SHA1
;
1085 int dnssec_nsec3_hash(DnsResourceRecord
*nsec3
, const char *name
, void *ret
) {
1086 uint8_t wire_format
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
1087 gcry_md_hd_t md
= NULL
;
1098 if (nsec3
->key
->type
!= DNS_TYPE_NSEC3
)
1101 if (nsec3
->nsec3
.iterations
> NSEC3_ITERATIONS_MAX
) {
1102 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3
));
1106 algorithm
= nsec3_hash_to_gcrypt_md(nsec3
->nsec3
.algorithm
);
1110 initialize_libgcrypt();
1112 hash_size
= gcry_md_get_algo_dlen(algorithm
);
1113 assert(hash_size
> 0);
1115 if (nsec3
->nsec3
.next_hashed_name_size
!= hash_size
)
1118 r
= dns_name_to_wire_format(name
, wire_format
, sizeof(wire_format
), true);
1122 gcry_md_open(&md
, algorithm
, 0);
1126 gcry_md_write(md
, wire_format
, r
);
1127 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
1129 result
= gcry_md_read(md
, 0);
1135 for (k
= 0; k
< nsec3
->nsec3
.iterations
; k
++) {
1136 uint8_t tmp
[hash_size
];
1137 memcpy(tmp
, result
, hash_size
);
1140 gcry_md_write(md
, tmp
, hash_size
);
1141 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
1143 result
= gcry_md_read(md
, 0);
1150 memcpy(ret
, result
, hash_size
);
1151 r
= (int) hash_size
;
1158 static int nsec3_is_good(DnsResourceRecord
*rr
, DnsAnswerFlags flags
, DnsResourceRecord
*nsec3
) {
1164 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
1167 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1168 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
1171 /* Ignore NSEC3 RRs whose algorithm we don't know */
1172 if (nsec3_hash_to_gcrypt_md(rr
->nsec3
.algorithm
) < 0)
1174 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1175 if (rr
->nsec3
.iterations
> NSEC3_ITERATIONS_MAX
)
1181 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1183 if (nsec3
== rr
) /* Shortcut */
1186 if (rr
->key
->class != nsec3
->key
->class)
1188 if (rr
->nsec3
.algorithm
!= nsec3
->nsec3
.algorithm
)
1190 if (rr
->nsec3
.iterations
!= nsec3
->nsec3
.iterations
)
1192 if (rr
->nsec3
.salt_size
!= nsec3
->nsec3
.salt_size
)
1194 if (memcmp(rr
->nsec3
.salt
, nsec3
->nsec3
.salt
, rr
->nsec3
.salt_size
) != 0)
1197 a
= DNS_RESOURCE_KEY_NAME(rr
->key
);
1198 r
= dns_name_parent(&a
); /* strip off hash */
1204 b
= DNS_RESOURCE_KEY_NAME(nsec3
->key
);
1205 r
= dns_name_parent(&b
); /* strip off hash */
1211 return dns_name_equal(a
, b
);
1214 static int nsec3_hashed_domain(DnsResourceRecord
*nsec3
, const char *domain
, const char *zone
, char **ret
) {
1215 _cleanup_free_
char *l
= NULL
, *hashed_domain
= NULL
;
1216 uint8_t hashed
[DNSSEC_HASH_SIZE_MAX
];
1224 hashed_size
= dnssec_nsec3_hash(nsec3
, domain
, hashed
);
1225 if (hashed_size
< 0)
1228 l
= base32hexmem(hashed
, hashed_size
, false);
1232 hashed_domain
= strjoin(l
, ".", zone
, NULL
);
1236 *ret
= hashed_domain
;
1237 hashed_domain
= NULL
;
1242 /* See RFC 5155, Section 8
1243 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1244 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1245 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1246 * matches the wildcard domain.
1248 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1249 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1250 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1251 * to conclude anything we indicate this by returning NO_RR. */
1252 static int dnssec_test_nsec3(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
, bool *authenticated
, uint32_t *ttl
) {
1253 _cleanup_free_
char *next_closer_domain
= NULL
, *wildcard
= NULL
, *wildcard_domain
= NULL
;
1254 const char *zone
, *p
, *pp
= NULL
;
1255 DnsResourceRecord
*rr
, *enclosure_rr
, *suffix_rr
, *wildcard_rr
= NULL
;
1256 DnsAnswerFlags flags
;
1258 bool a
, no_closer
= false, no_wildcard
= false, optout
= false;
1263 /* First step, find the zone name and the NSEC3 parameters of the zone.
1264 * it is sufficient to look for the longest common suffix we find with
1265 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1266 * records from a given zone in a response must use the same
1268 zone
= DNS_RESOURCE_KEY_NAME(key
);
1270 DNS_ANSWER_FOREACH_FLAGS(suffix_rr
, flags
, answer
) {
1271 r
= nsec3_is_good(suffix_rr
, flags
, NULL
);
1277 r
= dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr
->key
), 1, zone
);
1284 /* Strip one label from the front */
1285 r
= dns_name_parent(&zone
);
1292 *result
= DNSSEC_NSEC_NO_RR
;
1296 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1297 p
= DNS_RESOURCE_KEY_NAME(key
);
1299 _cleanup_free_
char *hashed_domain
= NULL
;
1301 hashed_size
= nsec3_hashed_domain(suffix_rr
, p
, zone
, &hashed_domain
);
1302 if (hashed_size
== -EOPNOTSUPP
) {
1303 *result
= DNSSEC_NSEC_UNSUPPORTED_ALGORITHM
;
1306 if (hashed_size
< 0)
1309 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr
, flags
, answer
) {
1311 r
= nsec3_is_good(enclosure_rr
, flags
, suffix_rr
);
1317 if (enclosure_rr
->nsec3
.next_hashed_name_size
!= (size_t) hashed_size
)
1320 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr
->key
), hashed_domain
);
1324 a
= flags
& DNS_ANSWER_AUTHENTICATED
;
1325 goto found_closest_encloser
;
1329 /* We didn't find the closest encloser with this name,
1330 * but let's remember this domain name, it might be
1331 * the next closer name */
1335 /* Strip one label from the front */
1336 r
= dns_name_parent(&p
);
1343 *result
= DNSSEC_NSEC_NO_RR
;
1346 found_closest_encloser
:
1347 /* We found a closest encloser in 'p'; next closer is 'pp' */
1349 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1350 if (bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_DNAME
))
1353 /* Ensure that this data is from the delegated domain
1354 * (i.e. originates from the "lower" DNS server), and isn't
1355 * just glue records (i.e. doesn't originate from the "upper"
1357 if (bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_NS
) &&
1358 !bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_SOA
))
1362 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1363 if (bitmap_isset(enclosure_rr
->nsec3
.types
, key
->type
))
1364 *result
= DNSSEC_NSEC_FOUND
;
1365 else if (bitmap_isset(enclosure_rr
->nsec3
.types
, DNS_TYPE_CNAME
))
1366 *result
= DNSSEC_NSEC_CNAME
;
1368 *result
= DNSSEC_NSEC_NODATA
;
1373 *ttl
= enclosure_rr
->ttl
;
1378 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1380 wildcard
= strappend("*.", p
);
1384 r
= nsec3_hashed_domain(enclosure_rr
, wildcard
, zone
, &wildcard_domain
);
1387 if (r
!= hashed_size
)
1390 r
= nsec3_hashed_domain(enclosure_rr
, pp
, zone
, &next_closer_domain
);
1393 if (r
!= hashed_size
)
1396 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1397 _cleanup_free_
char *label
= NULL
, *next_hashed_domain
= NULL
;
1399 r
= nsec3_is_good(rr
, flags
, suffix_rr
);
1405 label
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
1409 next_hashed_domain
= strjoin(label
, ".", zone
, NULL
);
1410 if (!next_hashed_domain
)
1413 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), next_closer_domain
, next_hashed_domain
);
1417 if (rr
->nsec3
.flags
& 1)
1420 a
= a
&& (flags
& DNS_ANSWER_AUTHENTICATED
);
1425 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), wildcard_domain
);
1429 a
= a
&& (flags
& DNS_ANSWER_AUTHENTICATED
);
1434 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), wildcard_domain
, next_hashed_domain
);
1438 if (rr
->nsec3
.flags
& 1)
1439 /* This only makes sense if we have a wildcard delegation, which is
1440 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1441 * this not happening, so hence cannot simply conclude NXDOMAIN as
1445 a
= a
&& (flags
& DNS_ANSWER_AUTHENTICATED
);
1451 if (wildcard_rr
&& no_wildcard
)
1455 *result
= DNSSEC_NSEC_NO_RR
;
1460 /* A wildcard exists that matches our query. */
1462 /* This is not specified in any RFC to the best of my knowledge, but
1463 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1464 * it means that we cannot prove that the source of synthesis is
1465 * correct, as there may be a closer match. */
1466 *result
= DNSSEC_NSEC_OPTOUT
;
1467 else if (bitmap_isset(wildcard_rr
->nsec3
.types
, key
->type
))
1468 *result
= DNSSEC_NSEC_FOUND
;
1469 else if (bitmap_isset(wildcard_rr
->nsec3
.types
, DNS_TYPE_CNAME
))
1470 *result
= DNSSEC_NSEC_CNAME
;
1472 *result
= DNSSEC_NSEC_NODATA
;
1475 /* The RFC only specifies that we have to care for optout for NODATA for
1476 * DS records. However, children of an insecure opt-out delegation should
1477 * also be considered opt-out, rather than verified NXDOMAIN.
1478 * Note that we do not require a proof of wildcard non-existence if the
1479 * next closer domain is covered by an opt-out, as that would not provide
1480 * any additional information. */
1481 *result
= DNSSEC_NSEC_OPTOUT
;
1482 else if (no_wildcard
)
1483 *result
= DNSSEC_NSEC_NXDOMAIN
;
1485 *result
= DNSSEC_NSEC_NO_RR
;
1495 *ttl
= enclosure_rr
->ttl
;
1500 int dnssec_test_nsec(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
, bool *authenticated
, uint32_t *ttl
) {
1501 DnsResourceRecord
*rr
;
1502 bool have_nsec3
= false;
1503 DnsAnswerFlags flags
;
1509 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1511 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1513 if (rr
->key
->class != key
->class)
1516 switch (rr
->key
->type
) {
1520 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
1524 if (bitmap_isset(rr
->nsec
.types
, key
->type
))
1525 *result
= DNSSEC_NSEC_FOUND
;
1526 else if (bitmap_isset(rr
->nsec
.types
, DNS_TYPE_CNAME
))
1527 *result
= DNSSEC_NSEC_CNAME
;
1529 *result
= DNSSEC_NSEC_NODATA
;
1532 *authenticated
= flags
& DNS_ANSWER_AUTHENTICATED
;
1539 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
), rr
->nsec
.next_domain_name
);
1543 *result
= DNSSEC_NSEC_NXDOMAIN
;
1546 *authenticated
= flags
& DNS_ANSWER_AUTHENTICATED
;
1554 case DNS_TYPE_NSEC3
:
1560 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1562 return dnssec_test_nsec3(answer
, key
, result
, authenticated
, ttl
);
1564 /* No approproate NSEC RR found, report this. */
1565 *result
= DNSSEC_NSEC_NO_RR
;
1569 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
1571 [DNSSEC_ALLOW_DOWNGRADE
] = "allow-downgrade",
1572 [DNSSEC_YES
] = "yes",
1574 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
1576 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
1577 [DNSSEC_VALIDATED
] = "validated",
1578 [DNSSEC_INVALID
] = "invalid",
1579 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
1580 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
1581 [DNSSEC_NO_SIGNATURE
] = "no-signature",
1582 [DNSSEC_MISSING_KEY
] = "missing-key",
1583 [DNSSEC_UNSIGNED
] = "unsigned",
1584 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
1585 [DNSSEC_NSEC_MISMATCH
] = "nsec-mismatch",
1586 [DNSSEC_INCOMPATIBLE_SERVER
] = "incompatible-server",
1588 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);