resolved-dns-dnssec.c revision 2cd8727718632ed80fa3871fddf71506c548d770
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek This file is part of systemd.
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek Copyright 2015 Lennart Poettering
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek (at your option) any later version.
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers/* Open question:
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * How does the DNSSEC canonical form of a hostname with a label
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * containing a dot look like, the way DNS-SD does it?
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - Iterative validation
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - NSEC proof of non-existance
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - NSEC3 proof of non-existance
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - Trust anchor store
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - wildcard zones
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - multi-label zones
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - cname support
0b6b6787e3f0ae8906ce0212bd629edbe931b73dKay Sievers * - DNSSEC bus API extensions
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * - global + per-interface DNSSEC setting
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * The DNSSEC Chain of trust:
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * DS RRs are protected like normal RRs
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * Example chain:
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmekstatic bool dnssec_algorithm_supported(int algorithm) {
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmekstatic bool dnssec_digest_supported(int digest) {
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmekuint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek /* The algorithm from RFC 4034, Appendix B. */
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek assert(dnskey->key->type == DNS_TYPE_DNSKEY);
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek sum = (uint32_t) dnskey->dnskey.flags +
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek for (i = 0; i < dnskey->dnskey.key_size; i++)
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek sum += (sum >> 16) & UINT32_C(0xFFFF);
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmekstatic int rr_compare(const void *a, const void *b) {
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek /* Let's order the RRs according to RFC 4034, Section 6.3 */
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek m = MIN((*x)->wire_format_size, (*y)->wire_format_size);
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek r = memcmp((*x)->wire_format, (*y)->wire_format, m);
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek if ((*x)->wire_format_size < (*y)->wire_format_size)
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek else if ((*x)->wire_format_size > (*y)->wire_format_size)
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek const void *signature, size_t signature_size,
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek const void *data, size_t data_size,
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek const void *exponent, size_t exponent_size,
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek const void *modulus, size_t modulus_size) {
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek gcry_mpi_t n = NULL, e = NULL, s = NULL;
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak if (ge != 0) {
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak if (ge != 0) {
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak if (ge != 0) {
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak "(sig-val (rsa (s %m)))",
3519d230c8bafe834b2dac26ace49fcfba139823Karel Zak if (ge != 0) {
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek "(data (flags pkcs1) (hash %s %b))",
d15d0333be6a1ca7fdd99a1881d967b6be8f387aZbigniew Jędrzejewski-Szmek ge = gcry_sexp_build(&public_key_sexp,
a75f4e2a02e287294b21ae9e5b1f28b2f8faea39Zbigniew Jędrzejewski-Szmek "(public-key (rsa (n %m) (e %m)))",
c5e04d51277994cca29234b33a6b8fc90a183cf3Zbigniew Jędrzejewski-Szmek ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
a75f4e2a02e287294b21ae9e5b1f28b2f8faea39Zbigniew Jędrzejewski-Szmek if (ge == GPG_ERR_BAD_SIGNATURE)
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poetteringstatic void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poetteringstatic void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poetteringstatic void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poetteringstatic int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poettering assert(rrsig->key->type == DNS_TYPE_RRSIG);
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poettering expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poettering inception = rrsig->rrsig.inception * USEC_PER_SEC;
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poettering /* Permit a certain amount of clock skew of 10% of the valid
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poettering * time range. This takes inspiration from unbound's
6550203eb471595e41e27f46e5d0a00a4c0e47bbLennart Poettering * resolver. */
inception = 0;
int dnssec_verify_rrset(
DnsAnswer *a,
size_t k, n = 0;
return -EOPNOTSUPP;
return -E2BIG;
return DNSSEC_SIGNATURE_EXPIRED;
return -ENODATA;
case DNSSEC_ALGORITHM_RSASHA1:
if (!md)
return -EIO;
goto finish;
size_t l;
r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
goto finish;
if (!hash) {
r = -EIO;
goto finish;
r = -EINVAL;
goto finish;
r = -EINVAL;
goto finish;
if (exponent_size <= 0) {
r = -EINVAL;
goto finish;
r = -EINVAL;
goto finish;
r = dnssec_rsa_verify(
goto finish;
return -EINVAL;
DnsAnswer *a,
if (!a || a->n_rrs <= 0)
return -ENODATA;
found_rrsig = true;
found_dnskey = true;
if (r < 0 && r != EOPNOTSUPP)
if (r == DNSSEC_VERIFIED)
return DNSSEC_VERIFIED;
if (found_dnskey)
return DNSSEC_INVALID;
if (found_rrsig)
return DNSSEC_MISSING_KEY;
return DNSSEC_NO_SIGNATURE;
size_t c = 0;
return -ENOBUFS;
size_t i;
return -ENOBUFS;
return -EINVAL;
for (i = 0; i < (size_t) r; i ++) {
void *result;
return -EINVAL;
return -EINVAL;
return -EKEYREJECTED;
return -EKEYREJECTED;
return -EOPNOTSUPP;
return -EOPNOTSUPP;
case DNSSEC_DIGEST_SHA1:
case DNSSEC_DIGEST_SHA256:
if (!md)
return -EIO;
goto finish;
if (!result) {
r = -EIO;
goto finish;