resolved: introduce support for per-interface negative trust anchors

This commit is contained in:
Lennart Poettering 2016-01-06 18:36:32 +01:00
parent bec690501e
commit 8a516214c4
11 changed files with 231 additions and 66 deletions

View File

@ -179,6 +179,12 @@
<para>If no negative trust anchor files are configured a built-in
set of well-known private DNS zone domains is used as negative
trust anchors.</para>
<para>It is also possibly to define per-interface negative trust
anchors using the <varname>DNSSECNegativeTrustAnchors=</varname>
setting in
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
files.</para>
</refsect1>
<refsect1>
@ -186,7 +192,8 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -318,6 +318,20 @@
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DNSSECNegativeTrustAnchors=</varname></term>
<listitem><para>A space-separated list of DNSSEC negative
trust anchor domains. If specified and DNSSEC is enabled,
look-ups done via the interface's DNS server will be subject
to the list of negative trust anchors, and not require
authentication for the specified domains, or anything below
it. Use this to disable DNSSEC authentication for specific
private domains, that cannot be proven valid using the
Internet DNS hierarchy. Defaults to the empty list. This
setting is read by
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>LLDP=</varname></term>
<listitem>

View File

@ -123,60 +123,7 @@ static int network_link_get_string(int ifindex, const char *field, char **ret) {
return 0;
}
_public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "ADMIN_STATE", state);
}
_public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
return network_link_get_string(ifindex, "NETWORK_FILE", filename);
}
_public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "OPER_STATE", state);
}
_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
return network_link_get_string(ifindex, "LLMNR", llmnr);
}
_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) {
return network_link_get_string(ifindex, "MDNS", mdns);
}
_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) {
return network_link_get_string(ifindex, "DNSSEC", dnssec);
}
_public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
_cleanup_free_ char *s = NULL, *p = NULL;
size_t size;
int r;
assert_return(ifindex > 0, -EINVAL);
assert_return(lldp, -EINVAL);
if (asprintf(&p, "/run/systemd/netif/lldp/%d", ifindex) < 0)
return -ENOMEM;
r = read_full_file(p, &s, &size);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
return r;
if (size <= 0)
return -ENODATA;
*lldp = s;
s = NULL;
return 0;
}
int sd_network_link_get_timezone(int ifindex, char **ret) {
return network_link_get_string(ifindex, "TIMEZONE", ret);
}
static int network_get_link_strv(const char *key, int ifindex, char ***ret) {
static int network_link_get_strv(int ifindex, const char *key, char ***ret) {
_cleanup_free_ char *p = NULL, *s = NULL;
_cleanup_strv_free_ char **a = NULL;
int r;
@ -210,24 +157,81 @@ static int network_get_link_strv(const char *key, int ifindex, char ***ret) {
return r;
}
_public_ int sd_network_link_get_setup_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "ADMIN_STATE", state);
}
_public_ int sd_network_link_get_network_file(int ifindex, char **filename) {
return network_link_get_string(ifindex, "NETWORK_FILE", filename);
}
_public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
return network_link_get_string(ifindex, "OPER_STATE", state);
}
_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
return network_link_get_string(ifindex, "LLMNR", llmnr);
}
_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) {
return network_link_get_string(ifindex, "MDNS", mdns);
}
_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) {
return network_link_get_string(ifindex, "DNSSEC", dnssec);
}
_public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta) {
return network_link_get_strv(ifindex, "DNSSEC_NTA", nta);
}
_public_ int sd_network_link_get_lldp(int ifindex, char **lldp) {
_cleanup_free_ char *s = NULL, *p = NULL;
size_t size;
int r;
assert_return(ifindex > 0, -EINVAL);
assert_return(lldp, -EINVAL);
if (asprintf(&p, "/run/systemd/netif/lldp/%d", ifindex) < 0)
return -ENOMEM;
r = read_full_file(p, &s, &size);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
return r;
if (size <= 0)
return -ENODATA;
*lldp = s;
s = NULL;
return 0;
}
int sd_network_link_get_timezone(int ifindex, char **ret) {
return network_link_get_string(ifindex, "TIMEZONE", ret);
}
_public_ int sd_network_link_get_dns(int ifindex, char ***ret) {
return network_get_link_strv("DNS", ifindex, ret);
return network_link_get_strv(ifindex, "DNS", ret);
}
_public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
return network_get_link_strv("NTP", ifindex, ret);
return network_link_get_strv(ifindex, "NTP", ret);
}
_public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
return network_get_link_strv("DOMAINS", ifindex, ret);
return network_link_get_strv(ifindex, "DOMAINS", ret);
}
_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) {
return network_get_link_strv("CARRIER_BOUND_TO", ifindex, ret);
return network_link_get_strv(ifindex, "CARRIER_BOUND_TO", ret);
}
_public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
return network_get_link_strv("CARRIER_BOUND_BY", ifindex, ret);
return network_link_get_strv(ifindex, "CARRIER_BOUND_BY", ret);
}
_public_ int sd_network_link_get_wildcard_domain(int ifindex) {

View File

@ -2875,6 +2875,20 @@ int link_save(Link *link) {
fprintf(f, "DNSSEC=%s\n",
dnssec_mode_to_string(link->network->dnssec_mode));
if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
const char *n;
fputs("DNSSEC_NTA=", f);
space = false;
SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i) {
if (space)
fputc(' ', f);
fputs(n, f);
space = true;
}
fputc('\n', f);
}
fputs("ADDRESSES=", f);
space = false;
SET_FOREACH(a, link->addresses, i) {
@ -2887,7 +2901,6 @@ int link_save(Link *link) {
fprintf(f, "%s%s/%u", space ? " " : "", address_str, a->prefixlen);
space = true;
}
fputc('\n', f);
fputs("ROUTES=", f);

View File

@ -48,6 +48,7 @@ Network.DNS, config_parse_strv,
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, offsetof(Network, dnssec_negative_trust_anchors)
Network.NTP, config_parse_strv, 0, offsetof(Network, ntp)
Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward)
Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)

View File

@ -32,6 +32,7 @@
#include "networkd-network.h"
#include "networkd.h"
#include "parse-util.h"
#include "set.h"
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
@ -277,6 +278,8 @@ void network_free(Network *network) {
free(network->dhcp_server_dns);
free(network->dhcp_server_ntp);
set_free_free(network->dnssec_negative_trust_anchors);
free(network);
}
@ -910,3 +913,55 @@ int config_parse_dhcp_server_ntp(
n->dhcp_server_ntp = m;
}
}
int config_parse_dnssec_negative_trust_anchors(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
const char *p = rvalue;
Network *n = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
if (isempty(rvalue)) {
n->dnssec_negative_trust_anchors = set_free_free(n->dnssec_negative_trust_anchors);
return 0;
}
for (;;) {
_cleanup_free_ char *w = NULL;
r = extract_first_word(&p, &w, NULL, 0);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
break;
}
if (r == 0)
break;
r = dns_name_is_valid(w);
if (r <= 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w);
continue;
}
r = set_put(n->dnssec_negative_trust_anchors, w);
if (r < 0)
return log_oom();
if (r > 0)
w = NULL;
}
return 0;
}

View File

@ -147,6 +147,7 @@ struct Network {
ResolveSupport llmnr;
ResolveSupport mdns;
DnssecMode dnssec_mode;
Set *dnssec_negative_trust_anchors;
LIST_FIELDS(Network, networks);
};
@ -173,6 +174,7 @@ int config_parse_hostname(const char *unit, const char *filename, unsigned line,
int config_parse_timezone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* Legacy IPv4LL support */
int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

View File

@ -1406,6 +1406,25 @@ static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags
return false;
}
static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
int r;
assert(t);
/* Check whether the specified name is in the the NTA
* database, either in the global one, or the link-local
* one. */
r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, name);
if (r != 0)
return r;
if (!t->scope->link)
return 0;
return set_contains(t->scope->link->dnssec_negative_trust_anchors, name);
}
static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) {
int r;
@ -1422,7 +1441,7 @@ static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) {
/* Is this key explicitly listed as a negative trust anchor?
* If so, it's nothing we need to care about */
r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(t->key));
r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key));
if (r < 0)
return r;
if (r > 0)
@ -1513,7 +1532,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
continue;
/* If this RR is in the negative trust anchor, we don't need to validate it. */
r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(rr->key));
r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
if (r < 0)
return r;
if (r > 0)
@ -1863,7 +1882,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
if (dns_type_is_pseudo(rr->key->type))
return -EINVAL;
r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(rr->key));
r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
if (r < 0)
return r;
if (r > 0)
@ -2066,7 +2085,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
if (dns_type_is_pseudo(t->key->type))
return -EINVAL;
r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(t->key));
r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key));
if (r < 0)
return r;
if (r > 0)
@ -2135,7 +2154,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe
* the specified RRset is authenticated (i.e. has a matching
* DS RR). */
r = dns_trust_anchor_lookup_negative(&t->scope->manager->trust_anchor, DNS_RESOURCE_KEY_NAME(rr->key));
r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key));
if (r < 0)
return r;
if (r > 0)

View File

@ -82,6 +82,8 @@ Link *link_free(Link *l) {
dns_scope_free(l->mdns_ipv4_scope);
dns_scope_free(l->mdns_ipv6_scope);
set_free_free(l->dnssec_negative_trust_anchors);
free(l);
return NULL;
}
@ -302,6 +304,43 @@ clear:
return r;
}
static int link_update_dnssec_negative_trust_anchors(Link *l) {
_cleanup_strv_free_ char **ntas = NULL;
_cleanup_set_free_free_ Set *ns = NULL;
char **i;
int r;
assert(l);
r = sd_network_link_get_dnssec_negative_trust_anchors(l->ifindex, &ntas);
if (r == -ENODATA) {
r = 0;
goto clear;
}
if (r < 0)
goto clear;
ns = set_new(&dns_name_hash_ops);
if (!ns)
return -ENOMEM;
STRV_FOREACH(i, ntas) {
r = set_put_strdup(ns, *i);
if (r < 0)
return r;
}
set_free_free(l->dnssec_negative_trust_anchors);
l->dnssec_negative_trust_anchors = ns;
ns = NULL;
return 0;
clear:
l->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors);
return r;
}
static int link_update_search_domains(Link *l) {
_cleanup_strv_free_ char **domains = NULL;
char **i;
@ -365,6 +404,10 @@ int link_update_monitor(Link *l) {
if (r < 0)
log_warning_errno(r, "Failed to read DNSSEC mode for interface %s, ignoring: %m", l->name);
r = link_update_dnssec_negative_trust_anchors(l);
if (r < 0)
log_warning_errno(r, "Failed to read DNSSEC negative trust anchors for interface %s, ignoring: %m", l->name);
r = link_update_search_domains(l);
if (r < 0)
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);

View File

@ -70,6 +70,7 @@ struct Link {
ResolveSupport llmnr_support;
ResolveSupport mdns_support;
DnssecMode dnssec_mode;
Set *dnssec_negative_trust_anchors;
DnsScope *unicast_scope;
DnsScope *llmnr_ipv4_scope;

View File

@ -126,6 +126,12 @@ int sd_network_link_get_mdns(int ifindex, char **mdns);
*/
int sd_network_link_get_dnssec(int ifindex, char **dnssec);
/* Returns the list of per-interface DNSSEC negative trust anchors
* Possible return codes:
* -ENODATA: networkd is not aware of the link, or has no such data
*/
int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta);
int sd_network_link_get_lldp(int ifindex, char **lldp);
/* Get the DNS domain names for a given link. */