]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Implement SNI when using DNS-over-TLS
authorGuilhem Lettron <guilhem@barpilot.io>
Sat, 30 Nov 2019 02:51:40 +0000 (03:51 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 4 Dec 2019 14:24:06 +0000 (23:24 +0900)
Some DNS providers need SNI to identify client.

This can be used by adding #name to a DNS.
Example:
[Resolve]
DNS=192.168.1.1#example.com

12 files changed:
man/resolved.conf.xml
src/resolve/meson.build
src/resolve/resolved-conf.c
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-link.c
src/resolve/resolved-util.c [new file with mode: 0644]
src/resolve/resolved-util.h [new file with mode: 0644]
src/resolve/test-resolved-util.c [new file with mode: 0644]

index 818000145b93b4b1cdf149572042d4ecea4dcae3..0f70ced5b54f6aba824fee8d4f2cf54e7e95cff2 100644 (file)
         resolver is not capable of authenticating the server, so it is
         vulnerable to "man-in-the-middle" attacks.</para>
 
+        <para>Server Name Indication (SNI) can be used when opening a TLS connection.
+        Entries in <varname>DNS=</varname> should be in format <literal>address#server_name</literal>.</para>
+
         <para>In addition to this global DNSOverTLS setting
         <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         also maintains per-link DNSOverTLS settings. For system DNS
index 92b67b6333d764ce474aeb05a7085fcfebd178ae..c4d8d4e5d9a3bb309017553f7b6c813b22b6bfc8 100644 (file)
@@ -64,6 +64,8 @@ systemd_resolved_sources = files('''
         resolved-etc-hosts.h
         resolved-etc-hosts.c
         resolved-dnstls.h
+        resolved-util.c
+        resolved-util.h
 '''.split())
 
 resolvectl_sources = files('''
@@ -228,4 +230,10 @@ tests += [
          [],
          [],
          'ENABLE_RESOLVE', 'manual'],
+
+        [['src/resolve/test-resolved-util.c',
+          'src/resolve/resolved-util.c',
+          'src/resolve/resolved-util.h'],
+         [],
+         []],
 ]
index a46c45385b5a1f84227a169b11ae781091eaeeac..ca5b8e7918391c782eb2a3fd31c675f9943b3910 100644 (file)
@@ -8,6 +8,7 @@
 #include "parse-util.h"
 #include "resolved-conf.h"
 #include "resolved-dnssd.h"
+#include "resolved-util.h"
 #include "specifier.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -27,11 +28,12 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
         union in_addr_union address;
         int family, r, ifindex = 0;
         DnsServer *s;
+        _cleanup_free_ char *server_name = NULL;
 
         assert(m);
         assert(word);
 
-        r = in_addr_ifindex_from_string_auto(word, &family, &address, &ifindex);
+        r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
         if (r < 0)
                 return r;
 
@@ -52,7 +54,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
                 return 0;
         }
 
-        return dns_server_new(m, NULL, type, NULL, family, &address, ifindex);
+        return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name);
 }
 
 int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
index 9f2c97314fb475f8e6a40da572c2d7638b369feb..4b0599ab9cf3df9e1f3bb839b0e304df44b0397c 100644 (file)
@@ -25,8 +25,10 @@ int dns_server_new(
                 Link *l,
                 int family,
                 const union in_addr_union *in_addr,
-                int ifindex) {
+                int ifindex,
+                const char *server_name) {
 
+        _cleanup_free_ char *name = NULL;
         DnsServer *s;
 
         assert(m);
@@ -44,6 +46,12 @@ int dns_server_new(
                         return -E2BIG;
         }
 
+        if (server_name) {
+                name = strdup(server_name);
+                if (!name)
+                        return -ENOMEM;
+        }
+
         s = new(DnsServer, 1);
         if (!s)
                 return -ENOMEM;
@@ -55,6 +63,7 @@ int dns_server_new(
                 .family = family,
                 .address = *in_addr,
                 .ifindex = ifindex,
+                .server_name = TAKE_PTR(name),
         };
 
         dns_server_reset_features(s);
@@ -107,6 +116,7 @@ static DnsServer* dns_server_free(DnsServer *s)  {
 #endif
 
         free(s->server_string);
+        free(s->server_name);
         return mfree(s);
 }
 
index 54339355aa85367d73ae5c7ef2b14c97dd9c4354..889c80a2054bdaf488eb0632c8af0f6014796e46 100644 (file)
@@ -53,6 +53,8 @@ struct DnsServer {
 
         char *server_string;
 
+        char *server_name;
+
         /* The long-lived stream towards this server. */
         DnsStream *stream;
 
@@ -94,7 +96,8 @@ int dns_server_new(
                 Link *link,
                 int family,
                 const union in_addr_union *address,
-                int ifindex);
+                int ifindex,
+                const char *server_string);
 
 DnsServer* dns_server_ref(DnsServer *s);
 DnsServer* dns_server_unref(DnsServer *s);
index ed0a31e8bf39f51efb86a9ebffe2cdd57d223e64..aad3bb4481f5e2bf1b03416892b9db23db0843ef 100644 (file)
@@ -67,6 +67,12 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                 gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
         }
 
+        if (server->server_name) {
+                r = gnutls_server_name_set(gs, GNUTLS_NAME_DNS, server->server_name, strlen(server->server_name));
+                if (r < 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set server name: %s", gnutls_strerror(r));
+        }
+
         gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
 
         gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
index 85e202ff741d46a09911c1247577ef980f6a2952..ce0a4373715582e30e650450707ec4e48eb35a43 100644 (file)
@@ -87,6 +87,17 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                         return -ECONNREFUSED;
         }
 
+        if (server->server_name) {
+                r = SSL_set_tlsext_host_name(s, server->server_name);
+                if (r <= 0) {
+                        char errbuf[256];
+
+                        error = ERR_get_error();
+                        ERR_error_string_n(error, errbuf, sizeof(errbuf));
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set server name: %s", errbuf);
+                }
+        }
+
         ERR_clear_error();
         stream->dnstls_data.handshake = SSL_do_handshake(s);
         if (stream->dnstls_data.handshake <= 0) {
index 8a2768b1e2c8e60bf6e92a6d24cab0152ceffddc..dae8435b45bec93d5e784ba7142e06c51204cfc8 100644 (file)
@@ -284,7 +284,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
                 if (s)
                         dns_server_move_back_and_unmark(s);
                 else {
-                        r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0);
+                        r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0, NULL);
                         if (r < 0)
                                 goto clear;
                 }
index 96ebb4d23d360020a39eeb98d6f2a7a619d4c1a6..f19fc2f3aa12d480188faedea3048c5ff8b7b2b0 100644 (file)
@@ -269,7 +269,7 @@ static int link_update_dns_server_one(Link *l, const char *name) {
                 return 0;
         }
 
-        return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0);
+        return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0, NULL);
 }
 
 static int link_update_dns_servers(Link *l) {
diff --git a/src/resolve/resolved-util.c b/src/resolve/resolved-util.c
new file mode 100644 (file)
index 0000000..2f18f8c
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "resolved-util.h"
+
+int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
+        _cleanup_free_ char *buf = NULL, *name = NULL;
+        const char *m;
+        int r;
+
+        assert(s);
+
+        m = strchr(s, '#');
+        if (m) {
+                name = strdup(m+1);
+                if (!name)
+                        return -ENOMEM;
+
+                buf = strndup(s, m - s);
+                if (!buf)
+                        return -ENOMEM;
+
+                s = buf;
+        }
+
+        r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
+        if (r < 0)
+                return r;
+
+        if (server_name)
+                *server_name = TAKE_PTR(name);
+
+        return r;
+}
diff --git a/src/resolve/resolved-util.h b/src/resolve/resolved-util.h
new file mode 100644 (file)
index 0000000..10ebbc0
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "in-addr-util.h"
+
+int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name);
diff --git a/src/resolve/test-resolved-util.c b/src/resolve/test-resolved-util.c
new file mode 100644 (file)
index 0000000..35bd73c
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "log.h"
+#include "resolved-util.h"
+#include "string-util.h"
+#include "tests.h"
+
+
+static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) {
+        int family, ifindex;
+        union in_addr_union ua;
+        _cleanup_free_ char *server_name = NULL;
+
+        assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
+        assert_se(streq_ptr(server_name, expected));
+}
+
+static void test_in_addr_ifindex_name_from_string_auto(void) {
+        log_info("/* %s */", __func__);
+
+        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
+}
+
+int main(int argc, char **argv) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_in_addr_ifindex_name_from_string_auto();
+        return 0;
+}