From 38e5900fc6e77b595151ae8e1548c5aace406bf8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 16:43:07 +0100 Subject: sd-network: unify parsing of /run/systemd/netif/links/* string fields --- src/libsystemd/sd-network/sd-network.c | 103 +++++---------------------------- 1 file changed, 14 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index efbceba83d..d01c4ae8e2 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -99,17 +99,17 @@ _public_ int sd_network_get_domains(char ***ret) { return network_get_strv("DOMAINS", ret); } -_public_ int sd_network_link_get_setup_state(int ifindex, char **state) { +static int network_link_get_string(int ifindex, const char *field, char **ret) { _cleanup_free_ char *s = NULL, *p = NULL; int r; assert_return(ifindex > 0, -EINVAL); - assert_return(state, -EINVAL); + assert_return(ret, -EINVAL); - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) + if (asprintf(&p, "/run/systemd/netif/links/%i", ifindex) < 0) return -ENOMEM; - r = parse_env_file(p, NEWLINE, "ADMIN_STATE", &s, NULL); + r = parse_env_file(p, NEWLINE, field, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) @@ -117,82 +117,26 @@ _public_ int sd_network_link_get_setup_state(int ifindex, char **state) { if (isempty(s)) return -ENODATA; - *state = s; + *ret = s; s = NULL; return 0; } -_public_ int sd_network_link_get_network_file(int ifindex, char **filename) { - _cleanup_free_ char *s = NULL, *p = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(filename, -EINVAL); - - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) - return -ENOMEM; - - r = parse_env_file(p, NEWLINE, "NETWORK_FILE", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *filename = s; - s = NULL; +_public_ int sd_network_link_get_setup_state(int ifindex, char **state) { + return network_link_get_string(ifindex, "ADMIN_STATE", state); +} - return 0; +_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) { - _cleanup_free_ char *s = NULL, *p = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(state, -EINVAL); - - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) - return -ENOMEM; - - r = parse_env_file(p, NEWLINE, "OPER_STATE", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *state = s; - s = NULL; - - return 0; + return network_link_get_string(ifindex, "OPER_STATE", state); } _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { - _cleanup_free_ char *s = NULL, *p = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(llmnr, -EINVAL); - - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) - return -ENOMEM; - - r = parse_env_file(p, NEWLINE, "LLMNR", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *llmnr = s; - s = NULL; - - return 0; + return network_link_get_string(ifindex, "LLMNR", llmnr); } _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) { @@ -221,26 +165,7 @@ _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) { } int sd_network_link_get_timezone(int ifindex, char **ret) { - _cleanup_free_ char *s = NULL, *p = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) - return -ENOMEM; - - r = parse_env_file(p, NEWLINE, "TIMEZONE", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *ret = s; - s = NULL; - return 0; + return network_link_get_string(ifindex, "TIMEZONE", ret); } static int network_get_link_strv(const char *key, int ifindex, char ***ret) { @@ -298,8 +223,8 @@ _public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) { } _public_ int sd_network_link_get_wildcard_domain(int ifindex) { - int r; _cleanup_free_ char *p = NULL, *s = NULL; + int r; assert_return(ifindex > 0, -EINVAL); -- cgit v1.2.3-54-g00ecf From b18b8662154b46f03ddeac00e52d157c87d474e3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 17:10:17 +0100 Subject: basic: add string table macros for "extended boolean" enums In a couple of cases we maintain configuration settings that know an on and off state, like a boolean, plus some additional states. We generally parse them as booleans first, and if that fails check for specific additional values. This adds a generalized set of macros for parsing such settings, and ports one use in resolved and another in networkd over to it. --- src/basic/string-table.h | 18 +++++++++++++++++ src/network/networkd-util.c | 45 ++---------------------------------------- src/resolve/resolved-manager.c | 2 +- 3 files changed, 21 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/basic/string-table.h b/src/basic/string-table.h index 2181a3a767..588404ab5a 100644 --- a/src/basic/string-table.h +++ b/src/basic/string-table.h @@ -47,16 +47,34 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ } +#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ + scope type name##_from_string(const char *s) { \ + int b; \ + b = parse_boolean(s); \ + if (b == 0) \ + return (type) 0; \ + else if (b > 0) \ + return yes; \ + return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ + } + #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ struct __useless_struct_to_allow_trailing_semicolon__ +#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ + struct __useless_struct_to_allow_trailing_semicolon__ + #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) +#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) + /* For string conversions where numbers are also acceptable */ #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ int name##_to_string_alloc(type i, char **str) { \ diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 2545621a93..1cc4f07e1e 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -108,47 +108,6 @@ static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { [RESOLVE_SUPPORT_RESOLVE] = "resolve", }; -DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport); +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolve_support, ResolveSupport, RESOLVE_SUPPORT_YES); -int config_parse_resolve( - 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) { - - ResolveSupport *resolve = data; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(resolve); - - /* Our enum shall be a superset of booleans, hence first try - * to parse as boolean, and then as enum */ - - k = parse_boolean(rvalue); - if (k > 0) - *resolve = RESOLVE_SUPPORT_YES; - else if (k == 0) - *resolve = RESOLVE_SUPPORT_NO; - else { - ResolveSupport s; - - s = resolve_support_from_string(rvalue); - if (s < 0){ - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue); - return 0; - } - - *resolve = s; - } - - return 0; -} +DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve, resolve_support, ResolveSupport, "Failed to parse resolve support"); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 20955b3f6b..2241df70c2 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -1169,4 +1169,4 @@ static const char* const support_table[_SUPPORT_MAX] = { [SUPPORT_YES] = "yes", [SUPPORT_RESOLVE] = "resolve", }; -DEFINE_STRING_TABLE_LOOKUP(support, Support); +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(support, Support, SUPPORT_YES); -- cgit v1.2.3-54-g00ecf From af49ca27ffd790d78dbbb465b978266dfd5c93da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 17:25:10 +0100 Subject: resolved,networkd: unify ResolveSupport enum networkd previously knew an enum "ResolveSupport" for configuring per-interface LLMNR support, resolved had a similar enum just called "Support", with the same value and similar pasers. Unify this, call the enum ResolveSupport, and port both daemons to it. --- Makefile.am | 4 ++- src/network/networkd-network-gperf.gperf | 2 +- src/network/networkd-network.h | 1 + src/network/networkd-util.c | 10 -------- src/network/networkd-util.h | 12 --------- src/resolve/resolved-conf.c | 34 ------------------------ src/resolve/resolved-conf.h | 1 - src/resolve/resolved-gperf.gperf | 2 +- src/resolve/resolved-link.c | 44 ++++++++++++++------------------ src/resolve/resolved-link.h | 5 ++-- src/resolve/resolved-llmnr.c | 4 +-- src/resolve/resolved-manager.c | 9 +------ src/resolve/resolved-manager.h | 17 +++--------- src/resolve/resolved-mdns.c | 4 +-- src/shared/resolve-util.c | 33 ++++++++++++++++++++++++ src/shared/resolve-util.h | 39 ++++++++++++++++++++++++++++ 16 files changed, 108 insertions(+), 113 deletions(-) create mode 100644 src/shared/resolve-util.c create mode 100644 src/shared/resolve-util.h (limited to 'src') diff --git a/Makefile.am b/Makefile.am index 3516b50385..3c5506249d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1054,7 +1054,9 @@ libshared_la_SOURCES = \ src/shared/machine-image.c \ src/shared/machine-image.h \ src/shared/machine-pool.c \ - src/shared/machine-pool.h + src/shared/machine-pool.h \ + src/shared/resolve-util.c \ + src/shared/resolve-util.h if HAVE_UTMP libshared_la_SOURCES += \ diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index de2c66d153..5c962e5709 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -45,7 +45,7 @@ Network.Address, config_parse_address, Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, offsetof(Network, domains) Network.DNS, config_parse_strv, 0, offsetof(Network, dns) -Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr) +Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr) 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) diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index cb3a50d9ba..297823da24 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -22,6 +22,7 @@ ***/ #include "condition.h" +#include "resolve-util.h" typedef struct Network Network; diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 1cc4f07e1e..93135bb658 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -101,13 +101,3 @@ int config_parse_address_family_boolean_with_kernel( return 0; } - -static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { - [RESOLVE_SUPPORT_NO] = "no", - [RESOLVE_SUPPORT_YES] = "yes", - [RESOLVE_SUPPORT_RESOLVE] = "resolve", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolve_support, ResolveSupport, RESOLVE_SUPPORT_YES); - -DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve, resolve_support, ResolveSupport, "Failed to parse resolve support"); diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index cc41aae85a..021ce4b128 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -33,20 +33,8 @@ typedef enum AddressFamilyBoolean { _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, } AddressFamilyBoolean; -typedef enum ResolveSupport { - RESOLVE_SUPPORT_NO, - RESOLVE_SUPPORT_YES, - RESOLVE_SUPPORT_RESOLVE, - _RESOLVE_SUPPORT_MAX, - _RESOLVE_SUPPORT_INVALID = -1, -} ResolveSupport; - -int config_parse_resolve(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_address_family_boolean(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_address_family_boolean_with_kernel(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* resolve_support_to_string(ResolveSupport i) _const_; -ResolveSupport resolve_support_from_string(const char *s) _pure_; - const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_; AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_; diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 1b2f3e336e..6afa61fa56 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -200,40 +200,6 @@ int config_parse_search_domains( return 0; } -int config_parse_support( - 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) { - - Support support, *v = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - support = support_from_string(rvalue); - if (support < 0) { - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse support level '%s'. Ignoring.", rvalue); - return 0; - } - - support = r ? SUPPORT_YES : SUPPORT_NO; - } - - *v = support; - return 0; -} - int config_parse_dnssec( const char *unit, const char *filename, diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index 668ea02bba..b4ef1b0378 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -35,5 +35,4 @@ const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned len int config_parse_dns_servers(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_search_domains(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_support(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(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); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index c815eae850..9bbf45454a 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -17,5 +17,5 @@ struct ConfigPerfItem; Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 Resolve.Domains, config_parse_search_domains, 0, 0 -Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) +Resolve.LLMNR, config_parse_resolve_support,0, offsetof(Manager, llmnr_support) Resolve.DNSSEC, config_parse_dnssec, 0, 0 diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 0fe2bb30bd..6202ddeca1 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -46,7 +46,7 @@ int link_new(Manager *m, Link **ret, int ifindex) { return -ENOMEM; l->ifindex = ifindex; - l->llmnr_support = SUPPORT_YES; + l->llmnr_support = RESOLVE_SUPPORT_YES; r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); if (r < 0) @@ -99,8 +99,8 @@ static void link_allocate_scopes(Link *l) { l->unicast_scope = dns_scope_free(l->unicast_scope); if (link_relevant(l, AF_INET) && - l->llmnr_support != SUPPORT_NO && - l->manager->llmnr_support != SUPPORT_NO) { + l->llmnr_support != RESOLVE_SUPPORT_NO && + l->manager->llmnr_support != RESOLVE_SUPPORT_NO) { if (!l->llmnr_ipv4_scope) { r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET); if (r < 0) @@ -110,8 +110,8 @@ static void link_allocate_scopes(Link *l) { l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); if (link_relevant(l, AF_INET6) && - l->llmnr_support != SUPPORT_NO && - l->manager->llmnr_support != SUPPORT_NO && + l->llmnr_support != RESOLVE_SUPPORT_NO && + l->manager->llmnr_support != RESOLVE_SUPPORT_NO && socket_ipv6_is_supported()) { if (!l->llmnr_ipv6_scope) { r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6); @@ -122,8 +122,8 @@ static void link_allocate_scopes(Link *l) { l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); if (link_relevant(l, AF_INET) && - l->mdns_support != SUPPORT_NO && - l->manager->mdns_support != SUPPORT_NO) { + l->mdns_support != RESOLVE_SUPPORT_NO && + l->manager->mdns_support != RESOLVE_SUPPORT_NO) { if (!l->mdns_ipv4_scope) { r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET); if (r < 0) @@ -133,8 +133,8 @@ static void link_allocate_scopes(Link *l) { l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope); if (link_relevant(l, AF_INET6) && - l->mdns_support != SUPPORT_NO && - l->manager->mdns_support != SUPPORT_NO) { + l->mdns_support != RESOLVE_SUPPORT_NO && + l->manager->mdns_support != RESOLVE_SUPPORT_NO) { if (!l->mdns_ipv6_scope) { r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6); if (r < 0) @@ -233,22 +233,16 @@ static int link_update_llmnr_support(Link *l) { if (r < 0) goto clear; - r = parse_boolean(b); - if (r < 0) { - if (streq(b, "resolve")) - l->llmnr_support = SUPPORT_RESOLVE; - else - goto clear; - - } else if (r > 0) - l->llmnr_support = SUPPORT_YES; - else - l->llmnr_support = SUPPORT_NO; + l->llmnr_support = resolve_support_from_string(b); + if (l->llmnr_support < 0) { + r = -EINVAL; + goto clear; + } return 0; clear: - l->llmnr_support = SUPPORT_YES; + l->llmnr_support = RESOLVE_SUPPORT_YES; return r; } @@ -459,8 +453,8 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv4_scope && - a->link->llmnr_support == SUPPORT_YES && - a->link->manager->llmnr_support == SUPPORT_YES) { + a->link->llmnr_support == RESOLVE_SUPPORT_YES && + a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) { if (!a->link->manager->llmnr_host_ipv4_key) { a->link->manager->llmnr_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->llmnr_hostname); @@ -516,8 +510,8 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { if (!force_remove && link_address_relevant(a) && a->link->llmnr_ipv6_scope && - a->link->llmnr_support == SUPPORT_YES && - a->link->manager->llmnr_support == SUPPORT_YES) { + a->link->llmnr_support == RESOLVE_SUPPORT_YES && + a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) { if (!a->link->manager->llmnr_host_ipv6_key) { a->link->manager->llmnr_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->llmnr_hostname); diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index a3b406bbc2..bb3fe8ebff 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -25,6 +25,7 @@ #include "in-addr-util.h" #include "ratelimit.h" +#include "resolve-util.h" typedef struct Link Link; typedef struct LinkAddress LinkAddress; @@ -66,8 +67,8 @@ struct Link { LIST_HEAD(DnsSearchDomain, search_domains); unsigned n_search_domains; - Support llmnr_support; - Support mdns_support; + ResolveSupport llmnr_support; + ResolveSupport mdns_support; DnsScope *unicast_scope; DnsScope *llmnr_ipv4_scope; diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 182c6e45ea..dd4d9508ba 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -47,7 +47,7 @@ int manager_llmnr_start(Manager *m) { assert(m); - if (m->llmnr_support == SUPPORT_NO) + if (m->llmnr_support == RESOLVE_SUPPORT_NO) return 0; r = manager_llmnr_ipv4_udp_fd(m); @@ -80,7 +80,7 @@ int manager_llmnr_start(Manager *m) { eaddrinuse: log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support."); - m->llmnr_support = SUPPORT_NO; + m->llmnr_support = RESOLVE_SUPPORT_NO; manager_llmnr_stop(m); return 0; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 2241df70c2..ed1ac8286f 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -476,7 +476,7 @@ int manager_new(Manager **ret) { m->mdns_ipv4_fd = m->mdns_ipv6_fd = -1; m->hostname_fd = -1; - m->llmnr_support = SUPPORT_YES; + m->llmnr_support = RESOLVE_SUPPORT_YES; m->read_resolv_conf = true; m->need_builtin_fallbacks = true; @@ -1163,10 +1163,3 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains) { return 0; } - -static const char* const support_table[_SUPPORT_MAX] = { - [SUPPORT_NO] = "no", - [SUPPORT_YES] = "yes", - [SUPPORT_RESOLVE] = "resolve", -}; -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(support, Support, SUPPORT_YES); diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index dd85d3ba47..d61804fff2 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -28,17 +28,9 @@ #include "hashmap.h" #include "list.h" #include "ordered-set.h" +#include "resolve-util.h" typedef struct Manager Manager; -typedef enum Support Support; - -enum Support { - SUPPORT_NO, - SUPPORT_YES, - SUPPORT_RESOLVE, - _SUPPORT_MAX, - _SUPPORT_INVALID = -1 -}; #include "resolved-dns-query.h" #include "resolved-dns-search-domain.h" @@ -53,8 +45,8 @@ enum Support { struct Manager { sd_event *event; - Support llmnr_support; - Support mdns_support; + ResolveSupport llmnr_support; + ResolveSupport mdns_support; /* Network */ Hashmap *links; @@ -165,6 +157,3 @@ int manager_is_own_hostname(Manager *m, const char *name); int manager_compile_dns_servers(Manager *m, OrderedSet **servers); int manager_compile_search_domains(Manager *m, OrderedSet **domains); - -const char* support_to_string(Support p) _const_; -int support_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 7c1012f4ea..d5b253d4f5 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -42,7 +42,7 @@ int manager_mdns_start(Manager *m) { assert(m); - if (m->mdns_support == SUPPORT_NO) + if (m->mdns_support == RESOLVE_SUPPORT_NO) return 0; r = manager_mdns_ipv4_fd(m); @@ -63,7 +63,7 @@ int manager_mdns_start(Manager *m) { eaddrinuse: log_warning("There appears to be another mDNS responder running. Turning off mDNS support."); - m->mdns_support = SUPPORT_NO; + m->mdns_support = RESOLVE_SUPPORT_NO; manager_mdns_stop(m); return 0; diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c new file mode 100644 index 0000000000..487de3e077 --- /dev/null +++ b/src/shared/resolve-util.c @@ -0,0 +1,33 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "conf-parser.h" +#include "resolve-util.h" +#include "string-table.h" + +DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_support, resolve_support, ResolveSupport, "Failed to parse resolve support setting"); + +static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { + [RESOLVE_SUPPORT_NO] = "no", + [RESOLVE_SUPPORT_YES] = "yes", + [RESOLVE_SUPPORT_RESOLVE] = "resolve", +}; +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolve_support, ResolveSupport, RESOLVE_SUPPORT_YES); diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h new file mode 100644 index 0000000000..e585c5024f --- /dev/null +++ b/src/shared/resolve-util.h @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "macro.h" + +typedef enum ResolveSupport ResolveSupport; + +enum ResolveSupport { + RESOLVE_SUPPORT_NO, + RESOLVE_SUPPORT_YES, + RESOLVE_SUPPORT_RESOLVE, + _RESOLVE_SUPPORT_MAX, + _RESOLVE_SUPPORT_INVALID = -1 +}; + +int config_parse_resolve_support(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* resolve_support_to_string(ResolveSupport p) _const_; +ResolveSupport resolve_support_from_string(const char *s) _pure_; -- cgit v1.2.3-54-g00ecf From aaa297d4e5401fd4466632555432774863457f1d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 17:32:25 +0100 Subject: networkd,resolved: add a per-interface mdns configuration option --- man/systemd.network.xml | 28 +++++++++++++++++++++++----- src/libsystemd/sd-network/sd-network.c | 4 ++++ src/network/networkd-link.c | 2 ++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 1 + src/network/networkd-network.h | 1 + src/resolve/resolved-link.c | 28 ++++++++++++++++++++++++++++ src/systemd/sd-network.h | 7 +++++++ 8 files changed, 67 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 5ad03f75e6..6091f2fdf5 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -277,10 +277,27 @@ LLMNR= - A boolean or resolve. When true, enables - Link-Local Multicast Name Resolution on the link. When set to - resolve, only resolution is enabled, but not - announcement. Defaults to true. + A boolean or resolve. When true, + enables Link-Local + Multicast Name Resolution on the link. When set to + resolve, only resolution is enabled, + but not host registration and announcement. Defaults to + true. This setting is read by + systemd-resolved.service8. + + + + MulticastDNS= + + A boolean or resolve. When true, + enables Multicast + DNS support on the link. When set to + resolve, only resolution is enabled, + but not host or service registration and + announcement. Defaults to false. This setting is read by + systemd-resolved.service8. @@ -1013,7 +1030,8 @@ DHCP=yes systemd1, systemd-networkd8, systemd.link5, - systemd.netdev5 + systemd.netdev5, + systemd-resolved.service8 diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index d01c4ae8e2..a63ff46c68 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -139,6 +139,10 @@ _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_lldp(int ifindex, char **lldp) { _cleanup_free_ char *s = NULL, *p = NULL; size_t size; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 9811526c6d..2013a8eae7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2868,6 +2868,8 @@ int link_save(Link *link) { fprintf(f, "LLMNR=%s\n", resolve_support_to_string(link->network->llmnr)); + fprintf(f, "MDNS=%s\n", + resolve_support_to_string(link->network->mdns)); fputs("ADDRESSES=", f); space = false; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 5c962e5709..c66ca3c1cc 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -46,6 +46,7 @@ Network.Gateway, config_parse_gateway, Network.Domains, config_parse_domains, 0, offsetof(Network, domains) Network.DNS, config_parse_strv, 0, offsetof(Network, dns) Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr) +Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns) 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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 29723a852f..7544280a7a 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -121,6 +121,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->unicast_flood = true; network->llmnr = RESOLVE_SUPPORT_YES; + network->mdns = RESOLVE_SUPPORT_NO; network->link_local = ADDRESS_FAMILY_IPV6; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 297823da24..91e90e6d74 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -145,6 +145,7 @@ struct Network { char **domains, **dns, **ntp, **bind_carrier; ResolveSupport llmnr; + ResolveSupport mdns; LIST_FIELDS(Network, networks); }; diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 6202ddeca1..a1f28633ba 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -246,6 +246,33 @@ clear: return r; } +static int link_update_mdns_support(Link *l) { + _cleanup_free_ char *b = NULL; + int r; + + assert(l); + + r = sd_network_link_get_mdns(l->ifindex, &b); + if (r == -ENODATA) { + r = 0; + goto clear; + } + if (r < 0) + goto clear; + + l->mdns_support = resolve_support_from_string(b); + if (l->mdns_support < 0) { + r = -EINVAL; + goto clear; + } + + return 0; + +clear: + l->mdns_support = RESOLVE_SUPPORT_NO; + return r; +} + static int link_update_search_domains(Link *l) { _cleanup_strv_free_ char **domains = NULL; char **i; @@ -295,6 +322,7 @@ int link_update_monitor(Link *l) { link_update_dns_servers(l); link_update_llmnr_support(l); + link_update_mdns_support(l); link_allocate_scopes(l); r = link_update_search_domains(l); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 79b4bf9ea3..6765be041b 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -111,6 +111,13 @@ int sd_network_link_get_ntp(int ifindex, char ***addr); */ int sd_network_link_get_llmnr(int ifindex, char **llmnr); +/* Indicates whether or not MDNS should be enabled for the link + * Possible levels of support: yes, no, resolve + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_mdns(int ifindex, char **mdns); + int sd_network_link_get_lldp(int ifindex, char **lldp); /* Get the DNS domain names for a given link. */ -- cgit v1.2.3-54-g00ecf From b83d91c02947585df06207c604534d25d87b611f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 17:37:09 +0100 Subject: resolved: make MulticastDNS support configurable in resolved.conf The option is already there, but wasn't exported in the configuration file so far. Fix that. --- man/resolved.conf.xml | 42 +++++++++++++++++++++++++++------------- src/resolve/resolved-gperf.gperf | 1 + src/resolve/resolved.conf.in | 1 + 3 files changed, 31 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 786b096ef6..3c1e698d33 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -124,23 +124,39 @@ global setting is on. + + MulticastDNS= + Takes a boolean argument or + resolve. Controls Multicast DNS support + (RFC + 6762) on the local host. If true, enables full + Multicast DNS responder and resolver support. If false, + disables both. If set to resolve, only + resolution support is enabled, but responding is + disabled. Note that + systemd-networkd.service8 + also maintains per-interface Multicast DNS settings. Multicast + DNS will be enabled on an interface only if the per-interface + and the global setting is on. + + DNSSEC= Takes a boolean argument or downgrade-ok. If true all DNS lookups are - DNSSEC-validated locally. If a response for a lookup request - is detected invalid this is returned as lookup failure to - applications. Note that this mode requires a DNS server that - supports DNSSEC. If the DNS server does not properly support - DNSSEC all validations will fail. If set to - downgrade-ok DNSSEC validation is - attempted, but if the server does not support DNSSEC properly, - DNSSEC mode is automatically disabled. Note that this mode - makes DNSSEC validation vulnerable to "downgrade" attacks, - where an attacker might be able to trigger a downgrade to - non-DNSSEC mode by synthesizing a DNS response that suggests - DNSSEC was not supported. If set to false, DNS lookups are not - DNSSEC validated. + DNSSEC-validated locally (excluding LLMNR and Multicast + DNS). If a response for a lookup request is detected invalid + this is returned as lookup failure to applications. Note that + this mode requires a DNS server that supports DNSSEC. If the + DNS server does not properly support DNSSEC all validations + will fail. If set to downgrade-ok DNSSEC + validation is attempted, but if the server does not support + DNSSEC properly, DNSSEC mode is automatically disabled. Note + that this mode makes DNSSEC validation vulnerable to + "downgrade" attacks, where an attacker might be able to + trigger a downgrade to non-DNSSEC mode by synthesizing a DNS + response that suggests DNSSEC was not supported. If set to + false, DNS lookups are not DNSSEC validated. Note that DNSSEC validation requires retrieval of additional DNS data, and thus results in a small DNS look-up diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 9bbf45454a..fb3fe9cfb1 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -18,4 +18,5 @@ Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 Resolve.Domains, config_parse_search_domains, 0, 0 Resolve.LLMNR, config_parse_resolve_support,0, offsetof(Manager, llmnr_support) +Resolve.MulticastDNS, config_parse_resolve_support,0, offsetof(Manager, mdns_support) Resolve.DNSSEC, config_parse_dnssec, 0, 0 diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index efc9c6733a..0ba572d113 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -16,4 +16,5 @@ #FallbackDNS=@DNS_SERVERS@ #Domains= #LLMNR=yes +#MulticastDNS=no #DNSSEC=no -- cgit v1.2.3-54-g00ecf From 1ed8c0fbb4cc51413f3a6025233f41c19f154bc1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 17:44:16 +0100 Subject: resolved: rename "downgrade-ok" mode to "allow-downgrade" After discussing this with Tom, we figured out "allow-downgrade" sounds nicer. --- man/resolved.conf.xml | 8 ++++---- src/resolve/resolved-dns-dnssec.c | 2 +- src/resolve/resolved-dns-dnssec.h | 2 +- src/resolve/resolved-dns-transaction.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 3c1e698d33..c2c277b606 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -143,13 +143,13 @@ DNSSEC= Takes a boolean argument or - downgrade-ok. If true all DNS lookups are + allow-downgrade. If true all DNS lookups are DNSSEC-validated locally (excluding LLMNR and Multicast DNS). If a response for a lookup request is detected invalid this is returned as lookup failure to applications. Note that this mode requires a DNS server that supports DNSSEC. If the DNS server does not properly support DNSSEC all validations - will fail. If set to downgrade-ok DNSSEC + will fail. If set to allow-downgrade DNSSEC validation is attempted, but if the server does not support DNSSEC properly, DNSSEC mode is automatically disabled. Note that this mode makes DNSSEC validation vulnerable to @@ -176,7 +176,7 @@ lookups will fail, as it cannot be proved anymore whether lookups are correctly signed, or validly unsigned. If DNSSEC= is set to - downgrade-ok the resolver will + allow-downgrade the resolver will automatically turn off DNSSEC validation in such a case. Client programs looking up DNS data will be informed @@ -193,7 +193,7 @@ DNSSEC correctly, and where software or trust anchor updates happen regularly. On other systems it is recommended to set DNSSEC= to - downgrade-ok. + allow-downgrade. diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 32d4834aa1..ff571986c0 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1568,7 +1568,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = { [DNSSEC_NO] = "no", - [DNSSEC_DOWNGRADE_OK] = "downgrade-ok", + [DNSSEC_ALLOW_DOWNGRADE] = "allow-downgrade", [DNSSEC_YES] = "yes", }; DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode); diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index 94d0b23f80..d818d1a906 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -37,7 +37,7 @@ enum DnssecMode { * DNSSEC properly, downgrade to non-DNSSEC operation. Of * course, we then are vulnerable to a downgrade attack, but * that's life and what is configured. */ - DNSSEC_DOWNGRADE_OK, + DNSSEC_ALLOW_DOWNGRADE, /* Insist on DNSSEC server support, and rather fail than downgrading. */ DNSSEC_YES, diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 870b7586fd..44267c6b2d 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -939,7 +939,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { * this means we cannot do any DNSSEC logic * anymore. */ - if (t->scope->dnssec_mode == DNSSEC_DOWNGRADE_OK) { + if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { /* We are in downgrade mode. In this * case, synthesize an unsigned empty * response, so that the any lookup @@ -2266,7 +2266,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { dns_server_packet_rrsig_missing(t->server); - if (t->scope->dnssec_mode == DNSSEC_DOWNGRADE_OK) { + if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { /* Downgrading is OK? If so, just consider the information unsigned */ -- cgit v1.2.3-54-g00ecf From 00f0a16ab4576535021456f8955446d3ae8f0b5f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 19:42:34 +0100 Subject: resolved: properly release all DnsServers that belong to a link --- src/resolve/resolved-link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index a1f28633ba..9ebaf137d8 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -65,7 +65,7 @@ Link *link_free(Link *l) { if (!l) return NULL; - dns_server_unlink_marked(l->dns_servers); + dns_server_unlink_all(l->dns_servers); dns_search_domain_unlink_all(l->search_domains); while (l->addresses) -- cgit v1.2.3-54-g00ecf From 125ae29d1bc3a6362c9bb1acddbe09fe1b274cfc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 19:43:51 +0100 Subject: resolved: log about per-interface setting parse errors --- src/resolve/resolved-link.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 9ebaf137d8..8785079de8 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -320,9 +320,18 @@ int link_update_monitor(Link *l) { assert(l); - link_update_dns_servers(l); - link_update_llmnr_support(l); - link_update_mdns_support(l); + r = link_update_dns_servers(l); + if (r < 0) + log_warning_errno(r, "Failed to read DNS servers for interface %s, ignoring: %m", l->name); + + r = link_update_llmnr_support(l); + if (r < 0) + log_warning_errno(r, "Failed to read LLMNR support for interface %s, ignoring: %m", l->name); + + r = link_update_mdns_support(l); + if (r < 0) + log_warning_errno(r, "Failed to read mDNS support for interface %s, ignoring: %m", l->name); + link_allocate_scopes(l); r = link_update_search_domains(l); -- cgit v1.2.3-54-g00ecf From ad6c04756115809d615dede330213d73edf732a8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 19:57:33 +0100 Subject: resolved,networkd: add a per-interface DNSSEC setting This adds a DNSSEC= setting to .network files, and makes resolved honour them. --- man/resolved.conf.xml | 10 +++++++++ man/systemd.network.xml | 18 ++++++++++++++++ src/libsystemd/sd-network/sd-network.c | 4 ++++ src/network/networkd-link.c | 4 ++++ src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 1 + src/network/networkd-network.h | 1 + src/resolve/resolved-conf.c | 35 -------------------------------- src/resolve/resolved-dns-dnssec.c | 7 ------- src/resolve/resolved-dns-dnssec.h | 21 ------------------- src/resolve/resolved-dns-scope.c | 17 ++++++++++++++++ src/resolve/resolved-gperf.gperf | 12 +++++------ src/resolve/resolved-link.c | 34 ++++++++++++++++++++++++++++++- src/resolve/resolved-link.h | 1 + src/resolve/resolved-manager.c | 6 ++++++ src/resolve/resolved-manager.h | 1 + src/resolve/resolved.c | 6 ------ src/shared/resolve-util.c | 8 ++++++++ src/shared/resolve-util.h | 23 +++++++++++++++++++++ src/systemd/sd-network.h | 10 ++++++++- 20 files changed, 143 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index c2c277b606..3209f73bc1 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -194,6 +194,16 @@ happen regularly. On other systems it is recommended to set DNSSEC= to allow-downgrade. + + In addition to this global DNSSEC setting + systemd-networkd.service8 + also maintains per-interface DNSSEC settings. For system DNS + servers (see above), only the global DNSSEC setting is in + effect. For per-interface DNS servers the per-interface + setting is in effect, unless it is unset in which case the + global setting is used instead. + + Defaults to off. diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 36172ae8b5..1dfa559c8b 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -300,6 +300,24 @@ systemd-resolved.service8. + + DNSSEC= + + A boolean or + allow-downgrade. When true, enables + DNSSEC + DNS validation support on the link. When set to + allow-downgrade, compatibility with + non-DNSSEC capable networks is increased, by automatically + turning off DNSEC in this case. This option defines a + per-interface setting for + resolved.conf5's + global DNSSEC= option. Defaults to + false. This setting is read by + systemd-resolved.service8. + + LLDP= diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index a63ff46c68..517b48a04f 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -143,6 +143,10 @@ _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; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 2013a8eae7..18a82fdeb9 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2871,6 +2871,10 @@ int link_save(Link *link) { fprintf(f, "MDNS=%s\n", resolve_support_to_string(link->network->mdns)); + if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID) + fprintf(f, "DNSSEC=%s\n", + dnssec_mode_to_string(link->network->dnssec_mode)); + fputs("ADDRESSES=", f); space = false; SET_FOREACH(a, link->addresses, i) { diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index c66ca3c1cc..75a2620804 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -47,6 +47,7 @@ Network.Domains, config_parse_domains, Network.DNS, config_parse_strv, 0, offsetof(Network, dns) 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.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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 7544280a7a..c91531ca69 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -122,6 +122,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->llmnr = RESOLVE_SUPPORT_YES; network->mdns = RESOLVE_SUPPORT_NO; + network->dnssec_mode = _DNSSEC_MODE_INVALID; network->link_local = ADDRESS_FAMILY_IPV6; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 91e90e6d74..7817b13cc4 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -146,6 +146,7 @@ struct Network { ResolveSupport llmnr; ResolveSupport mdns; + DnssecMode dnssec_mode; LIST_FIELDS(Network, networks); }; diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 6afa61fa56..88df7534c4 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -200,41 +200,6 @@ int config_parse_search_domains( return 0; } -int config_parse_dnssec( - 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) { - - Manager *m = data; - DnssecMode mode; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - mode = dnssec_mode_from_string(rvalue); - if (mode < 0) { - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNSSEC mode '%s'. Ignoring.", rvalue); - return 0; - } - - mode = r ? DNSSEC_YES : DNSSEC_NO; - } - - m->unicast_scope->dnssec_mode = mode; - return 0; -} - int manager_parse_config_file(Manager *m) { int r; diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index ff571986c0..edbd83ce05 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1566,13 +1566,6 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r return 0; } -static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = { - [DNSSEC_NO] = "no", - [DNSSEC_ALLOW_DOWNGRADE] = "allow-downgrade", - [DNSSEC_YES] = "yes", -}; -DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode); - static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = { [DNSSEC_VALIDATED] = "validated", [DNSSEC_INVALID] = "invalid", diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index d818d1a906..6977faca75 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -28,24 +28,6 @@ typedef enum DnssecResult DnssecResult; #include "resolved-dns-answer.h" #include "resolved-dns-rr.h" -enum DnssecMode { - /* No DNSSEC validation is done */ - DNSSEC_NO, - - /* Validate locally, if the server knows DO, but if not, - * don't. Don't trust the AD bit. If the server doesn't do - * DNSSEC properly, downgrade to non-DNSSEC operation. Of - * course, we then are vulnerable to a downgrade attack, but - * that's life and what is configured. */ - DNSSEC_ALLOW_DOWNGRADE, - - /* Insist on DNSSEC server support, and rather fail than downgrading. */ - DNSSEC_YES, - - _DNSSEC_MODE_MAX, - _DNSSEC_MODE_INVALID = -1 -}; - enum DnssecResult { /* These four are returned by dnssec_verify_rrset() */ DNSSEC_VALIDATED, @@ -101,8 +83,5 @@ typedef enum DnssecNsecResult { int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl); -const char* dnssec_mode_to_string(DnssecMode m) _const_; -DnssecMode dnssec_mode_from_string(const char *s) _pure_; - const char* dnssec_result_to_string(DnssecResult m) _const_; DnssecResult dnssec_result_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 13be2a3792..c96bed04b0 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -57,6 +57,23 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int s->family = family; s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC; + s->dnssec_mode = _DNSSEC_MODE_INVALID; + + if (protocol == DNS_PROTOCOL_DNS) { + /* Copy DNSSEC mode from the link if it is set there, + * otherwise take the manager's DNSSEC mode. Note that + * we copy this only at scope creation time, and do + * not update it from the on, even if the setting + * changes. */ + + if (l) + s->dnssec_mode = l->dnssec_mode; + if (s->dnssec_mode == _DNSSEC_MODE_INVALID) + s->dnssec_mode = m->dnssec_mode; + if (s->dnssec_mode == _DNSSEC_MODE_INVALID) + s->dnssec_mode = DNSSEC_NO; + } + LIST_PREPEND(scopes, m->dns_scopes, s); dns_scope_llmnr_membership(s, true); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index fb3fe9cfb1..c5ad04afd7 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -14,9 +14,9 @@ struct ConfigPerfItem; %struct-type %includes %% -Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 -Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 -Resolve.Domains, config_parse_search_domains, 0, 0 -Resolve.LLMNR, config_parse_resolve_support,0, offsetof(Manager, llmnr_support) -Resolve.MulticastDNS, config_parse_resolve_support,0, offsetof(Manager, mdns_support) -Resolve.DNSSEC, config_parse_dnssec, 0, 0 +Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 +Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 +Resolve.Domains, config_parse_search_domains, 0, 0 +Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) +Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support) +Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 8785079de8..50d3f9096b 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -47,6 +47,8 @@ int link_new(Manager *m, Link **ret, int ifindex) { l->ifindex = ifindex; l->llmnr_support = RESOLVE_SUPPORT_YES; + l->mdns_support = RESOLVE_SUPPORT_NO; + l->dnssec_mode = _DNSSEC_MODE_INVALID; r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); if (r < 0) @@ -273,6 +275,33 @@ clear: return r; } +static int link_update_dnssec_mode(Link *l) { + _cleanup_free_ char *m = NULL; + int r; + + assert(l); + + r = sd_network_link_get_dnssec(l->ifindex, &m); + if (r == -ENODATA) { + r = 0; + goto clear; + } + if (r < 0) + goto clear; + + l->dnssec_mode = dnssec_mode_from_string(m); + if (l->dnssec_mode < 0) { + r = -EINVAL; + goto clear; + } + + return 0; + +clear: + l->dnssec_mode = _DNSSEC_MODE_INVALID; + return r; +} + static int link_update_search_domains(Link *l) { _cleanup_strv_free_ char **domains = NULL; char **i; @@ -332,12 +361,15 @@ int link_update_monitor(Link *l) { if (r < 0) log_warning_errno(r, "Failed to read mDNS support for interface %s, ignoring: %m", l->name); - link_allocate_scopes(l); + r = link_update_dnssec_mode(l); + if (r < 0) + log_warning_errno(r, "Failed to read DNSSEC mode 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); + link_allocate_scopes(l); link_add_rrs(l, false); return 0; diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index bb3fe8ebff..d17b57253b 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -69,6 +69,7 @@ struct Link { ResolveSupport llmnr_support; ResolveSupport mdns_support; + DnssecMode dnssec_mode; DnsScope *unicast_scope; DnsScope *llmnr_ipv4_scope; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index ed1ac8286f..b32bad456b 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -477,6 +477,8 @@ int manager_new(Manager **ret) { m->hostname_fd = -1; m->llmnr_support = RESOLVE_SUPPORT_YES; + m->mdns_support = RESOLVE_SUPPORT_NO; + m->dnssec_mode = DNSSEC_NO; m->read_resolv_conf = true; m->need_builtin_fallbacks = true; @@ -484,6 +486,10 @@ int manager_new(Manager **ret) { if (r < 0) return r; + r = manager_parse_config_file(m); + if (r < 0) + return r; + r = sd_event_default(&m->event); if (r < 0) return r; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index d61804fff2..1907d2e1bc 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -47,6 +47,7 @@ struct Manager { ResolveSupport llmnr_support; ResolveSupport mdns_support; + DnssecMode dnssec_mode; /* Network */ Hashmap *links; diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index be406b71fe..472bb32764 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -81,12 +81,6 @@ int main(int argc, char *argv[]) { goto finish; } - r = manager_parse_config_file(m); - if (r < 0) { - log_error_errno(r, "Failed to parse configuration file: %m"); - goto finish; - } - r = manager_start(m); if (r < 0) { log_error_errno(r, "Failed to start manager: %m"); diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c index 487de3e077..bf6fc26841 100644 --- a/src/shared/resolve-util.c +++ b/src/shared/resolve-util.c @@ -24,6 +24,7 @@ #include "string-table.h" DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_support, resolve_support, ResolveSupport, "Failed to parse resolve support setting"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_dnssec_mode, dnssec_mode, DnssecMode, "Failed to parse DNSSEC mode setting"); static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { [RESOLVE_SUPPORT_NO] = "no", @@ -31,3 +32,10 @@ static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { [RESOLVE_SUPPORT_RESOLVE] = "resolve", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(resolve_support, ResolveSupport, RESOLVE_SUPPORT_YES); + +static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = { + [DNSSEC_NO] = "no", + [DNSSEC_ALLOW_DOWNGRADE] = "allow-downgrade", + [DNSSEC_YES] = "yes", +}; +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dnssec_mode, DnssecMode, DNSSEC_YES); diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h index e585c5024f..fd93a13f73 100644 --- a/src/shared/resolve-util.h +++ b/src/shared/resolve-util.h @@ -24,6 +24,7 @@ #include "macro.h" typedef enum ResolveSupport ResolveSupport; +typedef enum DnssecMode DnssecMode; enum ResolveSupport { RESOLVE_SUPPORT_NO, @@ -33,7 +34,29 @@ enum ResolveSupport { _RESOLVE_SUPPORT_INVALID = -1 }; +enum DnssecMode { + /* No DNSSEC validation is done */ + DNSSEC_NO, + + /* Validate locally, if the server knows DO, but if not, + * don't. Don't trust the AD bit. If the server doesn't do + * DNSSEC properly, downgrade to non-DNSSEC operation. Of + * course, we then are vulnerable to a downgrade attack, but + * that's life and what is configured. */ + DNSSEC_ALLOW_DOWNGRADE, + + /* Insist on DNSSEC server support, and rather fail than downgrading. */ + DNSSEC_YES, + + _DNSSEC_MODE_MAX, + _DNSSEC_MODE_INVALID = -1 +}; + int config_parse_resolve_support(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_mode(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* resolve_support_to_string(ResolveSupport p) _const_; ResolveSupport resolve_support_from_string(const char *s) _pure_; + +const char* dnssec_mode_to_string(DnssecMode p) _const_; +DnssecMode dnssec_mode_from_string(const char *s) _pure_; diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 6765be041b..00aaa102ce 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -111,13 +111,21 @@ int sd_network_link_get_ntp(int ifindex, char ***addr); */ int sd_network_link_get_llmnr(int ifindex, char **llmnr); -/* Indicates whether or not MDNS should be enabled for the link +/* Indicates whether or not MulticastDNS should be enabled for the + * link. * Possible levels of support: yes, no, resolve * Possible return codes: * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_mdns(int ifindex, char **mdns); +/* Indicates whether or not DNSSEC should be enabled for the link + * Possible levels of support: yes, no, allow-downgrade + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_dnssec(int ifindex, char **dnssec); + int sd_network_link_get_lldp(int ifindex, char **lldp); /* Get the DNS domain names for a given link. */ -- cgit v1.2.3-54-g00ecf From d1d1d4b807bc0fdffb64077fe724588ca3af8376 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 19:59:19 +0100 Subject: update DNSSEC TODO --- src/resolve/resolved-dns-dnssec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index edbd83ce05..387355d8f8 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -39,7 +39,8 @@ * - multi-label zone compatibility * - cname/dname compatibility * - nxdomain on qname - * - per-interface DNSSEC setting + * - workable hack for the .corp, .home, .box case + * - bus calls to override DNSEC setting per interface * * */ -- cgit v1.2.3-54-g00ecf From 3eb6aa009dd0b64dc2b326acaa38821462b8c6bf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 20:26:35 +0100 Subject: resolved: fix DNSSEC transaction dependency recursion check We followed the wrong connection. This only worked sometimes at all, because we also return the wrong error code. --- src/resolve/resolved-dns-transaction.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 44267c6b2d..7cf299bac8 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1284,13 +1284,13 @@ static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) { if (t == aux) return 1; - SET_FOREACH(n, aux->notify_transactions, i) { + SET_FOREACH(n, aux->dnssec_transactions, i) { r = dns_transaction_find_cyclic(t, n); if (r != 0) return r; } - return r; + return 0; } static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) { -- cgit v1.2.3-54-g00ecf From 105f6c4bdcdd9c7233370f1bc143913d5ab0d099 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 20:27:29 +0100 Subject: resolved: when dumping trust anchor contents, clarify when it is empty --- src/resolve/resolved-dns-trust-anchor.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 53b49b091a..0f34695696 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -346,15 +346,21 @@ static void dns_trust_anchor_dump(DnsTrustAnchor *d) { assert(d); - log_info("Positive Trust Anchors:"); - HASHMAP_FOREACH(a, d->positive_by_key, i) { - DnsResourceRecord *rr; - - DNS_ANSWER_FOREACH(rr, a) - log_info("%s", dns_resource_record_to_string(rr)); + if (hashmap_isempty(d->positive_by_key)) + log_info("No positive trust anchors defined."); + else { + log_info("Positive Trust Anchors:"); + HASHMAP_FOREACH(a, d->positive_by_key, i) { + DnsResourceRecord *rr; + + DNS_ANSWER_FOREACH(rr, a) + log_info("%s", dns_resource_record_to_string(rr)); + } } - if (!set_isempty(d->negative_by_name)) { + if (set_isempty(d->negative_by_name)) + log_info("No negative trust anchors defined."); + else { char *n; log_info("Negative trust anchors:"); -- cgit v1.2.3-54-g00ecf From d33b6cf343f5a1e073c3060878d2cc5fed54d150 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 22:13:56 +0100 Subject: resolved: try to detect fritz.box-style private DNS zones, and downgrade to non-DNSSEC mode for them This adds logic to detect cases like the Fritz!Box routers which serve a private DNS domain "fritz.box" under the TLD "box" that does not exist in the root servers. If this is detected DNSSEC validation is turned off for this private domain, thus improving compatibility with such private DNS zones. This should be fairly secure as we first rely on the proof that .box does not exist before this logic is applied. Nevertheless the logic is only enabled for DNSSEC=allow-downgrade mode. This logic does not work for routers that set up a full DNS zone directly under a non-existing TLD, as in that case we cannot prove that the domain is truly non-existing according to the root servers. --- man/resolved.conf.xml | 9 ++++ src/resolve/resolved-dns-transaction.c | 93 ++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) (limited to 'src') diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index 3209f73bc1..5da2d5488e 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -203,6 +203,15 @@ setting is in effect, unless it is unset in which case the global setting is used instead. + Site-private DNS zones generally conflict with DNSSEC + operation, unless a negative (if the private zone is not + signed) or positive (if the private zone is signed) trust + anchor is configured for them. If + allow-downgrade mode is selected, it is + attempted to detect site-private DNS zones using top-level + domains (TLDs) that are not known by the DNS root server. This + logic does not work in all private zone setups. + Defaults to off. diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 7cf299bac8..7212fb9c4d 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1989,6 +1989,66 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * }} } +static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKey *key) { + DnsTransaction *dt; + const char *tld; + Iterator i; + int r; + + /* If DNSSEC downgrade mode is on, checks whether the + * specified RR is one level below a TLD we have proven not to + * exist. In such a case we assume that this is a private + * domain, and permit it. + * + * This detects cases like the Fritz!Box router networks. Each + * Fritz!Box router serves a private "fritz.box" zone, in the + * non-existing TLD "box". Requests for the "fritz.box" domain + * are served by the router itself, while requests for the + * "box" domain will result in NXDOMAIN. + * + * Note that this logic is unable to detect cases where a + * router serves a private DNS zone directly under + * non-existing TLD. In such a case we cannot detect whether + * the TLD is supposed to exist or not, as all requests we + * make for it will be answered by the router's zone, and not + * by the root zone. */ + + assert(t); + + if (t->scope->dnssec_mode != DNSSEC_ALLOW_DOWNGRADE) + return false; /* In strict DNSSEC mode what doesn't exist, doesn't exist */ + + tld = DNS_RESOURCE_KEY_NAME(key); + r = dns_name_parent(&tld); + if (r < 0) + return r; + if (r == 0) + return false; /* Already the root domain */ + + if (!dns_name_is_single_label(tld)) + return false; + + SET_FOREACH(dt, t->dnssec_transactions, i) { + + if (dt->key->class != key->class) + continue; + + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), tld); + if (r < 0) + return r; + if (r == 0) + continue; + + /* We found an auxiliary lookup we did for the TLD. If + * that returned with NXDOMAIN, we know the TLD didn't + * exist, and hence this might be a private zone. */ + + return dt->answer_rcode == DNS_RCODE_NXDOMAIN; + } + + return false; +} + static int dns_transaction_requires_nsec(DnsTransaction *t) { DnsTransaction *dt; const char *name; @@ -2012,6 +2072,18 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (r > 0) return false; + r = dns_transaction_in_private_tld(t, t->key); + if (r < 0) + return r; + if (r > 0) { + /* The lookup is from a TLD that is proven not to + * exist, and we are in downgrade mode, hence ignore + * that fact that we didn't get any NSEC RRs.*/ + + log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.", dns_transaction_key_string(t)); + return false; + } + name = DNS_RESOURCE_KEY_NAME(t->key); if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { @@ -2283,6 +2355,27 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; return 0; } + + r = dns_transaction_in_private_tld(t, rr->key); + if (r < 0) + return r; + if (r > 0) { + _cleanup_free_ char *s = NULL; + + /* The data is from a TLD that is proven not to exist, and we are in downgrade + * mode, hence ignore the fact that this was not signed. */ + + (void) dns_resource_key_to_string(rr->key, &s); + log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", strna(s ? strstrip(s) : NULL)); + + r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); + if (r < 0) + return r; + + t->scope->manager->n_dnssec_insecure++; + changed = true; + break; + } } if (IN_SET(result, -- cgit v1.2.3-54-g00ecf From e497292abac908cae4f814bcdda2654ffb4bef0b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 00:57:21 +0100 Subject: resolved: count unsupported dnssec algorithm as indeterminate RRset After all, when we don't support the algorithm we cannot determine validity. --- src/resolve/resolved-dns-transaction.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 7212fb9c4d..8631afadb2 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -2404,10 +2404,9 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (IN_SET(result, DNSSEC_INVALID, DNSSEC_SIGNATURE_EXPIRED, - DNSSEC_NO_SIGNATURE, - DNSSEC_UNSUPPORTED_ALGORITHM)) + DNSSEC_NO_SIGNATURE)) t->scope->manager->n_dnssec_bogus++; - else + else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */ t->scope->manager->n_dnssec_indeterminate++; r = dns_transaction_is_primary_response(t, rr); -- cgit v1.2.3-54-g00ecf From 86e9cbcaeda647eb4f1db77333f7069d5bf549f1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 00:58:26 +0100 Subject: resolved: reuse dns_trust_anchor_knows_domain() at another location --- src/resolve/resolved-dns-trust-anchor.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 0f34695696..64249edc7f 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -42,6 +42,17 @@ static const uint8_t root_digest[] = { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60, 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 }; +static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) { + assert(d); + + /* Returns true if there's an entry for the specified domain + * name in our trust anchor */ + + return + hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) || + hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)); +} + static int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; @@ -53,10 +64,11 @@ static int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) { if (r < 0) return r; - if (hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, "."))) - return 0; - - if (hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "."))) + /* Only add the built-in trust anchor if there's neither a DS + * nor a DNSKEY defined for the root domain. That way users + * have an easy way to override the root domain DS/DNSKEY + * data. */ + if (dns_trust_anchor_knows_domain_positive(d, ".")) return 0; /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */ @@ -522,17 +534,6 @@ static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceReco return 0; } -static bool dns_trust_anchor_knows_domain(DnsTrustAnchor *d, const char *name) { - assert(d); - - /* Returns true if there's an entry for the specified domain - * name in our trust anchor */ - - return - hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) || - hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)); -} - int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsAnswer *rrs, const DnsResourceKey *key) { DnsResourceRecord *dnskey; int r; @@ -562,7 +563,7 @@ int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsAnswer *rrs, const DnsR /* Could this be interesting to us at all? If not, * there's no point in looking for and verifying a * self-signed RRSIG. */ - if (!dns_trust_anchor_knows_domain(d, DNS_RESOURCE_KEY_NAME(dnskey->key))) + if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key))) continue; /* Look for a self-signed RRSIG */ -- cgit v1.2.3-54-g00ecf From b3331c3970fe4aa08eed1a6864080e57a3fbbbd8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 00:59:32 +0100 Subject: resolved: log all OOM errors --- src/resolve/resolved-dns-trust-anchor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 64249edc7f..0a28734e2c 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -248,7 +248,7 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops); if (r < 0) - return r; + return log_oom(); old_answer = hashmap_get(d->positive_by_key, rr->key); answer = dns_answer_ref(old_answer); @@ -291,7 +291,7 @@ static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, u r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops); if (r < 0) - return r; + return log_oom(); r = set_put(d->negative_by_name, domain); if (r < 0) -- cgit v1.2.3-54-g00ecf From 30c778094b90a637c6691c462a66df81eeb865b5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 00:59:51 +0100 Subject: resolved: populate negative trust anchor by default Let's increase compatibility with many private domains by default, and ship a default NTA list of wel-known private domains, where it is unlikely they will be deployed as official TLD anytime soon. --- man/dnssec-trust-anchors.d.xml | 4 ++ src/resolve/resolved-dns-trust-anchor.c | 99 ++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml index 9a7cf3c881..5f15d7cd59 100644 --- a/man/dnssec-trust-anchors.d.xml +++ b/man/dnssec-trust-anchors.d.xml @@ -175,6 +175,10 @@ RFC 7646 for details on negative trust anchors. + + 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. diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 0a28734e2c..928f7f7860 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -53,7 +53,7 @@ static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)); } -static int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) { +static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; int r; @@ -100,6 +100,95 @@ static int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) { return 0; } +static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) { + + static const char private_domains[] = + /* RFC 6761 says that .test is a special domain for + * testing and not to be installed in the root zone */ + "test\0" + + /* RFC 6761 says that these reverse IP lookup ranges + * are for private addresses, and hence should not + * show up in the root zone */ + "10.in-addr.arpa\0" + "16.172.in-addr.arpa\0" + "17.172.in-addr.arpa\0" + "18.172.in-addr.arpa\0" + "19.172.in-addr.arpa\0" + "20.172.in-addr.arpa\0" + "21.172.in-addr.arpa\0" + "22.172.in-addr.arpa\0" + "23.172.in-addr.arpa\0" + "24.172.in-addr.arpa\0" + "25.172.in-addr.arpa\0" + "26.172.in-addr.arpa\0" + "27.172.in-addr.arpa\0" + "28.172.in-addr.arpa\0" + "29.172.in-addr.arpa\0" + "30.172.in-addr.arpa\0" + "31.172.in-addr.arpa\0" + "168.192.in-addr.arpa\0" + + /* RFC 6762 reserves the .local domain for Multicast + * DNS, it hence cannot appear in the root zone. (Note + * that we by default do not route .local traffic to + * DNS anyway, except when a configured search domain + * suggests so.) */ + "local\0" + + /* These two are well known, popular private zone + * TLDs, that are blocked from delegation, according + * to: + * http://icannwiki.com/Name_Collision#NGPC_Resolution + * + * There's also ongoing work on making this official + * in an RRC: + * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */ + "home\0" + "corp\0" + + /* The following four TLDs are suggested for private + * zones in RFC 6762, Appendix G, and are hence very + * unlikely to be made official TLDs any day soon */ + "lan\0" + "intranet\0" + "internal\0" + "private\0"; + + const char *name; + int r; + + assert(d); + + /* Only add the built-in trust anchor if there's no negative + * trust anchor defined at all. This enables easy overriding + * of negative trust anchors. */ + + if (set_size(d->negative_by_name) > 0) + return 0; + + r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops); + if (r < 0) + return r; + + /* We add a couple of domains as default negative trust + * anchors, where it's very unlikely they will be installed in + * the root zone. If they exist they must be private, and thus + * unsigned. */ + + NULSTR_FOREACH(name, private_domains) { + + if (dns_trust_anchor_knows_domain_positive(d, name)) + continue; + + r = set_put_strdup(d->negative_by_name, name); + if (r < 0) + return r; + } + + return 0; +} + static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL; @@ -391,9 +480,13 @@ int dns_trust_anchor_load(DnsTrustAnchor *d) { (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative); /* However, if the built-in DS fails, then we have a problem. */ - r = dns_trust_anchor_add_builtin(d); + r = dns_trust_anchor_add_builtin_positive(d); + if (r < 0) + return log_error_errno(r, "Failed to add built-in positive trust anchor: %m"); + + r = dns_trust_anchor_add_builtin_negative(d); if (r < 0) - return log_error_errno(r, "Failed to add trust anchor built-in: %m"); + return log_error_errno(r, "Failed to add built-in negative trust anchor: %m"); dns_trust_anchor_dump(d); -- cgit v1.2.3-54-g00ecf From bec690501ed544199e72a292fbd6d28bc1e1727e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 01:01:00 +0100 Subject: resolved: when dumping the NTA database, sort output Now that we populate the trust database by default with a larger number of entires, we better make sure to output a more readable version. --- src/resolve/resolved-dns-trust-anchor.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 928f7f7860..9f8b76ebe2 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -441,7 +441,13 @@ static int dns_trust_anchor_load_files( return 0; } -static void dns_trust_anchor_dump(DnsTrustAnchor *d) { +static int domain_name_cmp(const void *a, const void *b) { + char **x = (char**) a, **y = (char**) b; + + return dns_name_compare_func(*x, *y); +} + +static int dns_trust_anchor_dump(DnsTrustAnchor *d) { DnsAnswer *a; Iterator i; @@ -462,12 +468,22 @@ static void dns_trust_anchor_dump(DnsTrustAnchor *d) { if (set_isempty(d->negative_by_name)) log_info("No negative trust anchors defined."); else { - char *n; - log_info("Negative trust anchors:"); + _cleanup_free_ char **l = NULL, *j = NULL; + + l = set_get_strv(d->negative_by_name); + if (!l) + return log_oom(); - SET_FOREACH(n, d->negative_by_name, i) - log_info("%s%s", n, endswith(n, ".") ? "" : "."); + qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp); + + j = strv_join(l, " "); + if (!j) + return log_oom(); + + log_info("Negative trust anchors: %s", j); } + + return 0; } int dns_trust_anchor_load(DnsTrustAnchor *d) { -- cgit v1.2.3-54-g00ecf From 8a516214c4412e8a40544bd725a6d499a30cbbbf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 18:36:32 +0100 Subject: resolved: introduce support for per-interface negative trust anchors --- man/dnssec-trust-anchors.d.xml | 9 +++- man/systemd.network.xml | 14 ++++++ src/libsystemd/sd-network/sd-network.c | 82 +++++++++++++++++--------------- src/network/networkd-link.c | 15 +++++- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 55 +++++++++++++++++++++ src/network/networkd-network.h | 2 + src/resolve/resolved-dns-transaction.c | 29 +++++++++-- src/resolve/resolved-link.c | 43 +++++++++++++++++ src/resolve/resolved-link.h | 1 + src/systemd/sd-network.h | 6 +++ 11 files changed, 211 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml index 5f15d7cd59..51271abc16 100644 --- a/man/dnssec-trust-anchors.d.xml +++ b/man/dnssec-trust-anchors.d.xml @@ -179,6 +179,12 @@ 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. + + It is also possibly to define per-interface negative trust + anchors using the DNSSECNegativeTrustAnchors= + setting in + systemd.network5 + files. @@ -186,7 +192,8 @@ systemd1, systemd-resolved.service8, - resolved.conf5 + resolved.conf5, + systemd.network5 diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 1dfa559c8b..5a6383cfc2 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -318,6 +318,20 @@ systemd-resolved.service8. + + DNSSECNegativeTrustAnchors= + 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 + systemd-resolved.service8. + + LLDP= diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index 517b48a04f..c1f5867ee4 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -123,6 +123,40 @@ static int network_link_get_string(int ifindex, const char *field, char **ret) { return 0; } +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; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, key, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) { + *ret = NULL; + return 0; + } + + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + + strv_uniq(a); + r = strv_length(a); + + *ret = a; + a = NULL; + + return r; +} + _public_ int sd_network_link_get_setup_state(int ifindex, char **state) { return network_link_get_string(ifindex, "ADMIN_STATE", state); } @@ -147,6 +181,10 @@ _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; @@ -176,58 +214,24 @@ 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) { - _cleanup_free_ char *p = NULL, *s = NULL; - _cleanup_strv_free_ char **a = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) - return -ENOMEM; - - r = parse_env_file(p, NEWLINE, key, &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) { - *ret = NULL; - return 0; - } - - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - - strv_uniq(a); - r = strv_length(a); - - *ret = a; - a = NULL; - - return r; -} - _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) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 18a82fdeb9..4a807bacc3 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 75a2620804..2f2a36ccca 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index c91531ca69..c11cb3dcb3 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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; +} diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 7817b13cc4..b07fa41abc 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 8631afadb2..f5171a940f 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -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) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 50d3f9096b..30838ef8cc 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -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); diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index d17b57253b..db0e51da04 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -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; diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 00aaa102ce..653c61a162 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -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. */ -- cgit v1.2.3-54-g00ecf From 28bf03b5265be30079630b5bc2c3dafc13fce27b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 Jan 2016 18:39:08 +0100 Subject: update DNSSEC TODO --- src/resolve/resolved-dns-dnssec.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 387355d8f8..51fe710795 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -41,6 +41,8 @@ * - nxdomain on qname * - workable hack for the .corp, .home, .box case * - bus calls to override DNSEC setting per interface + * - log all DNSSEC downgrades + * - enable by default * * */ -- cgit v1.2.3-54-g00ecf