0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering This file is part of systemd.
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering Copyright 2015 Lennart Poettering
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering systemd is free software; you can redistribute it and/or modify it
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering under the terms of the GNU Lesser General Public License as published by
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering (at your option) any later version.
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering systemd is distributed in the hope that it will be useful, but
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering Lesser General Public License for more details.
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering You should have received a copy of the GNU Lesser General Public License
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
e7d179acb9f50dec4a16c6c222dd147d7e53c6bbLennart Poetteringstatic const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poetteringstatic bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering /* Returns true if there's an entry for the specified domain
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering * name in our trust anchor */
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) ||
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poetteringstatic int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering /* Only add the built-in trust anchor if there's neither a DS
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering * nor a DNSKEY defined for the root domain. That way users
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering * have an easy way to override the root domain DS/DNSKEY
86e9cbcaeda647eb4f1db77333f7069d5bf549f1Lennart Poettering if (dns_trust_anchor_knows_domain_positive(d, "."))
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering rr->ds.digest_size = sizeof(root_digest);
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
105e151299dc1208855380be2b22d0db2d66ebc6Lennart Poettering r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = hashmap_put(d->positive_by_key, rr->key, answer);
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poetteringstatic int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* RFC 6761 says that .test is a special domain for
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * testing and not to be installed in the root zone */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* RFC 6761 says that these reverse IP lookup ranges
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * are for private addresses, and hence should not
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * show up in the root zone */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "10.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "16.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "17.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "18.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "19.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "20.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "21.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "22.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "23.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "24.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "25.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "26.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "27.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "28.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "29.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "30.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "31.172.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering "168.192.in-addr.arpa\0"
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* RFC 6762 reserves the .local domain for Multicast
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * DNS, it hence cannot appear in the root zone. (Note
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * that we by default do not route .local traffic to
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * DNS anyway, except when a configured search domain
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * suggests so.) */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* These two are well known, popular private zone
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * TLDs, that are blocked from delegation, according
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * http://icannwiki.com/Name_Collision#NGPC_Resolution
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * There's also ongoing work on making this official
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* The following four TLDs are suggested for private
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * zones in RFC 6762, Appendix G, and are hence very
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * unlikely to be made official TLDs any day soon */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* Only add the built-in trust anchor if there's no negative
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * trust anchor defined at all. This enables easy overriding
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * of negative trust anchors. */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering /* We add a couple of domains as default negative trust
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * anchors, where it's very unlikely they will be installed in
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * the root zone. If they exist they must be private, and thus
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering * unsigned. */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering if (dns_trust_anchor_knows_domain_positive(d, name))
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering r = set_put_strdup(d->negative_by_name, name);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poetteringstatic int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering const char *p = s;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Missing class or type in line %s:%u", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Missing DS parameters on line %s:%u", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering a = dnssec_algorithm_from_string(algorithm);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering dt = dnssec_digest_from_string(digest_type);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = unhexmem(digest, strlen(digest), &dd, &l);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line);
2a0d751be498be89e18f130ca02b2197f83b6baaLennart Poettering log_warning("DNSKEY lacks zone key bit set on line %s:%u", path, line);
2a0d751be498be89e18f130ca02b2197f83b6baaLennart Poettering log_warning("DNSKEY is already revoked on line %s:%u", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering a = dnssec_algorithm_from_string(algorithm);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = unbase64mem(key, strlen(key), &k, &l);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering old_answer = hashmap_get(d->positive_by_key, rr->key);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_error_errno(r, "Failed to add trust anchor RR: %m");
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = hashmap_replace(d->positive_by_key, rr->key, answer);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_error_errno(r, "Failed to add answer to trust anchor: %m");
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering old_answer = dns_answer_unref(old_answer);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poetteringstatic int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering const char *p = s;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = set_put(d->negative_by_name, domain);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering unsigned n = 0;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering log_warning_errno(errno, "Failed to open %s: %m", *f);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering if (*l == ';')
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering (void) loader(d, *f, n, l);
bec690501ed544199e72a292fbd6d28bc1e1727eLennart Poetteringstatic int domain_name_cmp(const void *a, const void *b) {
bec690501ed544199e72a292fbd6d28bc1e1727eLennart Poettering char **x = (char**) a, **y = (char**) b;
bec690501ed544199e72a292fbd6d28bc1e1727eLennart Poetteringstatic int dns_trust_anchor_dump(DnsTrustAnchor *d) {
105f6c4bdcdd9c7233370f1bc143913d5ab0d099Lennart Poettering log_info("No positive trust anchors defined.");
105f6c4bdcdd9c7233370f1bc143913d5ab0d099Lennart Poettering HASHMAP_FOREACH(a, d->positive_by_key, i) {
105f6c4bdcdd9c7233370f1bc143913d5ab0d099Lennart Poettering log_info("%s", dns_resource_record_to_string(rr));
105f6c4bdcdd9c7233370f1bc143913d5ab0d099Lennart Poettering log_info("No negative trust anchors defined.");
bec690501ed544199e72a292fbd6d28bc1e1727eLennart Poettering _cleanup_free_ char **l = NULL, *j = NULL;
bec690501ed544199e72a292fbd6d28bc1e1727eLennart Poettering qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
bec690501ed544199e72a292fbd6d28bc1e1727eLennart Poettering log_info("Negative trust anchors: %s", j);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poetteringint dns_trust_anchor_load(DnsTrustAnchor *d) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering /* If loading things from disk fails, we don't consider this fatal */
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering (void) dns_trust_anchor_load_files(d, ".positive", dns_trust_anchor_load_positive);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering /* However, if the built-in DS fails, then we have a problem. */
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering r = dns_trust_anchor_add_builtin_positive(d);
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering r = dns_trust_anchor_add_builtin_negative(d);
30c778094b90a637c6691c462a66df81eeb865b5Lennart Poettering return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poetteringvoid dns_trust_anchor_flush(DnsTrustAnchor *d) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering while ((a = hashmap_steal_first(d->positive_by_key)))
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering d->positive_by_key = hashmap_free(d->positive_by_key);
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering while ((rr = set_steal_first(d->revoked_by_rr)))
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering d->revoked_by_rr = set_free(d->revoked_by_rr);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering d->negative_by_name = set_free_free(d->negative_by_name);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poetteringint dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering /* We only serve DS and DNSKEY RRs. */
0d2cd47617b423f37d7425be7a56ae2fca8ff9f6Lennart Poettering if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering a = hashmap_get(d->positive_by_key, key);
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poetteringint dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering return set_contains(d->negative_by_name, name);
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poetteringstatic int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poetteringstatic int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering /* Remember that this is a revoked trust anchor RR */
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering /* Remove this from the positive trust anchor */
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering old_answer = hashmap_get(d->positive_by_key, rr->key);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering r = dns_answer_remove_by_rr(&new_answer, rr);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering /* We found the key! Warn the user */
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering 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)),
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poetteringstatic int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering /* First, look for the precise DNSKEY in our trust anchor database */
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering /* Note that we allow the REVOKE bit to be
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering * different! It will be set in the revoked
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering * key, but unset in our version of it */
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering dns_trust_anchor_remove_revoked(d, anchor);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering 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)));
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering /* We set mask_revoke to true here, since our
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering * DS fingerprint will be the one of the
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering * unrevoked DNSKEY, but the one we got passed
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering * here has the bit set. */
96bb76734d8e1c8520a2456901079610813eac6dLennart Poettering r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
0c8570287400ba57d3705a2f62dd26039121ea6fLennart Poettering dns_trust_anchor_remove_revoked(d, anchor);
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poetteringint dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering /* Looks if "dnskey" is a self-signed RR that has been revoked
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering * and matches one of our trust anchor entries. If so, removes
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering * it from the trust anchor and returns > 0. */
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering if (dnskey->key->type != DNS_TYPE_DNSKEY)
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering /* Is this DNSKEY revoked? */
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering /* Could this be interesting to us at all? If not,
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering * there's no point in looking for and verifying a
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering * self-signed RRSIG. */
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key)))
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering /* Bingo! This is a revoked self-signed DNSKEY. Let's
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering * see if this precise one exists in our trust anchor
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering * database, too. */
d424da2ae0860268ab863ce8945a425aa79e3826Lennart Poettering r = dns_trust_anchor_check_revoked_one(d, dnskey);
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poetteringint dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))