resolved-dns-trust-anchor.c revision b3331c3970fe4aa08eed1a6864080e57a3fbbbd8
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering This file is part of systemd.
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering Copyright 2015 Lennart Poettering
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering systemd is free software; you can redistribute it and/or modify it
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering under the terms of the GNU Lesser General Public License as published by
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering (at your option) any later version.
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering systemd is distributed in the hope that it will be useful, but
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering Lesser General Public License for more details.
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering You should have received a copy of the GNU Lesser General Public License
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poetteringstatic const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
7410616cd9dbbec97cf98d75324da5cda2b2f7a2Lennart Poetteringstatic bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering /* Returns true if there's an entry for the specified domain
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering * name in our trust anchor */
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) ||
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poetteringstatic int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) {
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering /* Only add the built-in trust anchor if there's neither a DS
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering * nor a DNSKEY defined for the root domain. That way users
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering * have an easy way to override the root domain DS/DNSKEY
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering if (dns_trust_anchor_knows_domain_positive(d, "."))
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering rr->ds.digest_size = sizeof(root_digest);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering r = hashmap_put(d->positive_by_key, rr->key, answer);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poetteringstatic int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL;
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering const char *p = s;
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering log_warning("Missing class or type in line %s:%u", path, line);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering log_warning("Missing DS parameters on line %s:%u", path, line);
b5884878a2874447b2a9f07f324a7cd909d96d48Lennart Poettering return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering dt = dnssec_digest_from_string(digest_type);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
3c5a87a879e3eb66c9c159a26051d940ff2db7a1Lennart Poettering r = unhexmem(digest, strlen(digest), &dd, &l);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
326bb68c40ff2a7119c344b9ab7a7473f9fcdf3cLennart Poettering rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
return log_oom();
uint16_t f;
size_t l;
return -EINVAL;
return -EINVAL;
return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line);
if ((f & DNSKEY_FLAG_ZONE_KEY) == 0) {
return -EINVAL;
if ((f & DNSKEY_FLAG_REVOKE)) {
return -EINVAL;
return -EINVAL;
if (!rr)
return log_oom();
k = NULL;
return -EINVAL;
if (!isempty(p)) {
return -EINVAL;
return log_oom();
static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
assert(d);
return -EINVAL;
if (!isempty(p)) {
return -EINVAL;
return log_oom();
return log_oom();
static int dns_trust_anchor_load_files(
DnsTrustAnchor *d,
const char *suffix,
assert(d);
if (isempty(l))
(void) loader(d, *f, n, l);
DnsAnswer *a;
Iterator i;
assert(d);
assert(d);
r = dns_trust_anchor_add_builtin(d);
DnsAnswer *a;
assert(d);
dns_answer_unref(a);
int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
DnsAnswer *a;
assert(d);
assert(d);
if (!old_answer)
LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)),
NULL);
static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
DnsAnswer *a;
assert(d);
if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(revoked_dnskey->key)));
assert(d);