resolved,networkd: add a per-interface DNSSEC setting
authorLennart Poettering <lennart@poettering.net>
Tue, 5 Jan 2016 18:57:33 +0000 (19:57 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 5 Jan 2016 19:10:31 +0000 (20:10 +0100)
This adds a DNSSEC= setting to .network files, and makes resolved honour
them.

20 files changed:
man/resolved.conf.xml
man/systemd.network.xml
src/libsystemd/sd-network/sd-network.c
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/resolve/resolved-conf.c
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-dnssec.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-gperf.gperf
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved.c
src/shared/resolve-util.c
src/shared/resolve-util.h
src/systemd/sd-network.h

index c2c277b..3209f73 100644 (file)
         happen regularly. On other systems it is recommended to set
         <varname>DNSSEC=</varname> to
         <literal>allow-downgrade</literal>.</para>
+
+        <para>In addition to this global DNSSEC setting
+        <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        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.</para>
+
+        <para>Defaults to off.</para>
         </listitem>
       </varlistentry>
 
index 36172ae..1dfa559 100644 (file)
           </listitem>
         </varlistentry>
         <varlistentry>
+          <term><varname>DNSSEC=</varname></term>
+          <listitem>
+            <para>A boolean or
+            <literal>allow-downgrade</literal>. When true, enables
+            <ulink
+            url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
+            DNS validation support on the link. When set to
+            <literal>allow-downgrade</literal>, compatibility with
+            non-DNSSEC capable networks is increased, by automatically
+            turning off DNSEC in this case. This option defines a
+            per-interface setting for
+            <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
+            global <varname>DNSSEC=</varname> option. Defaults to
+            false. This setting is read by
+            <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
           <term><varname>LLDP=</varname></term>
           <listitem>
             <para>A boolean. When true, enables LLDP link receive support.
index a63ff46..517b48a 100644 (file)
@@ -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;
index 2013a8e..18a82fd 100644 (file)
@@ -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) {
index c66ca3c..75a2620 100644 (file)
@@ -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)
index 7544280..c91531c 100644 (file)
@@ -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;
 
index 91e90e6..7817b13 100644 (file)
@@ -146,6 +146,7 @@ struct Network {
 
         ResolveSupport llmnr;
         ResolveSupport mdns;
+        DnssecMode dnssec_mode;
 
         LIST_FIELDS(Network, networks);
 };
index 6afa61f..88df753 100644 (file)
@@ -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;
 
index ff57198..edbd83c 100644 (file)
@@ -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",
index d818d1a..6977fac 100644 (file)
@@ -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_;
index 13be2a3..c96bed0 100644 (file)
@@ -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);
index fb3fe9c..c5ad04a 100644 (file)
@@ -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)
index 8785079..50d3f90 100644 (file)
@@ -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;
index bb3fe8e..d17b572 100644 (file)
@@ -69,6 +69,7 @@ struct Link {
 
         ResolveSupport llmnr_support;
         ResolveSupport mdns_support;
+        DnssecMode dnssec_mode;
 
         DnsScope *unicast_scope;
         DnsScope *llmnr_ipv4_scope;
index ed1ac82..b32bad4 100644 (file)
@@ -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;
index d61804f..1907d2e 100644 (file)
@@ -47,6 +47,7 @@ struct Manager {
 
         ResolveSupport llmnr_support;
         ResolveSupport mdns_support;
+        DnssecMode dnssec_mode;
 
         /* Network */
         Hashmap *links;
index be406b7..472bb32 100644 (file)
@@ -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");
index 487de3e..bf6fc26 100644 (file)
@@ -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);
index e585c50..fd93a13 100644 (file)
@@ -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_;
index 6765be0..00aaa10 100644 (file)
@@ -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. */