resolved: add strict mode for DNS-over-TLS

Add strict mode for DNS-over-TLS, which will require TLS support from the server. Closes #10755
This commit is contained in:
Iwan Timmer 2019-02-18 20:41:46 +01:00 committed by Iwan Timmer
parent aedf00a2bd
commit 4310bfc20b
14 changed files with 46 additions and 15 deletions

View File

@ -193,8 +193,11 @@
<varlistentry> <varlistentry>
<term><varname>DNSOverTLS=</varname></term> <term><varname>DNSOverTLS=</varname></term>
<listitem> <listitem>
<para>Takes false or <para>Takes a boolean argument or <literal>opportunistic</literal>.
<literal>opportunistic</literal>. When set to <literal>opportunistic</literal> If true all connections to the server will be encrypted. Note that
this mode requires a DNS server that supports DNS-over-TLS and has
a valid certificate for it's IP. If the DNS server does not support
DNS-over-TLS all DNS requests will fail. When set to <literal>opportunistic</literal>
DNS request are attempted to send encrypted with DNS-over-TLS. DNS request are attempted to send encrypted with DNS-over-TLS.
If the DNS server does not support TLS, DNS-over-TLS is disabled. If the DNS server does not support TLS, DNS-over-TLS is disabled.
Note that this mode makes DNS-over-TLS vulnerable to "downgrade" Note that this mode makes DNS-over-TLS vulnerable to "downgrade"

View File

@ -395,12 +395,15 @@
<varlistentry> <varlistentry>
<term><varname>DNSOverTLS=</varname></term> <term><varname>DNSOverTLS=</varname></term>
<listitem> <listitem>
<para>Takes false or <para>Takes a boolean or <literal>opportunistic</literal>.
<literal>opportunistic</literal>. When set to <literal>opportunistic</literal>, enables When true, enables
<ulink <ulink
url="https://tools.ietf.org/html/rfc7858">DNS-over-TLS</ulink> url="https://tools.ietf.org/html/rfc7858">DNS-over-TLS</ulink>
support on the link. This option defines a support on the link.
per-interface setting for When set to <literal>opportunistic</literal>, compatibility with
non-DNS-over-TLS servers is increased, by automatically
turning off DNS-over-TLS servers in this case.
This option defines a per-interface setting for
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
global <varname>DNSOverTLS=</varname> option. Defaults to global <varname>DNSOverTLS=</varname> option. Defaults to
false. This setting is read by false. This setting is read by

View File

@ -1221,7 +1221,7 @@ if skip_deps
default_dns_over_tls = 'no' default_dns_over_tls = 'no'
endif endif
if default_dns_over_tls != 'no' and conf.get('ENABLE_DNS_OVER_TLS') == 0 if default_dns_over_tls != 'no' and conf.get('ENABLE_DNS_OVER_TLS') == 0
message('default-dns-over-tls cannot be set to opportunistic when DNS-over-TLS support is disabled. Setting default-dns-over-tls to no.') message('default-dns-over-tls cannot be enabled or set to opportunistic when DNS-over-TLS support is disabled. Setting default-dns-over-tls to no.')
default_dns_over_tls = 'no' default_dns_over_tls = 'no'
endif endif
conf.set('DEFAULT_DNS_OVER_TLS_MODE', conf.set('DEFAULT_DNS_OVER_TLS_MODE',

View File

@ -206,7 +206,7 @@ option('default-dnssec', type : 'combo',
value : 'allow-downgrade') value : 'allow-downgrade')
option('default-dns-over-tls', type : 'combo', option('default-dns-over-tls', type : 'combo',
description : 'default DNS-over-TLS mode', description : 'default DNS-over-TLS mode',
choices : ['opportunistic', 'no'], choices : ['yes', 'opportunistic', 'no'],
value : 'no') value : 'no')
option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'], option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
description : 'DNS-over-TLS support') description : 'DNS-over-TLS support')

View File

@ -53,7 +53,7 @@ _resolvectl() {
[FAMILY]='tcp udp sctp' [FAMILY]='tcp udp sctp'
[RESOLVE]='yes no resolve' [RESOLVE]='yes no resolve'
[DNSSEC]='yes no allow-downgrade' [DNSSEC]='yes no allow-downgrade'
[DNSOVERTLS]='no opportunistic' [DNSOVERTLS]='yes no opportunistic'
) )
local interfaces=$( __get_interfaces ) local interfaces=$( __get_interfaces )

View File

@ -64,7 +64,7 @@ _systemd-resolve() {
comps="yes no allow-downgrade" comps="yes no allow-downgrade"
;; ;;
--set-dnsovertls) --set-dnsovertls)
comps="no opportunistic" comps="yes no opportunistic"
;; ;;
esac esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) COMPREPLY=( $(compgen -W '$comps' -- "$cur") )

View File

@ -394,7 +394,7 @@ int manager_parse_config_file(Manager *m) {
#if ! ENABLE_DNS_OVER_TLS #if ! ENABLE_DNS_OVER_TLS
if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) { if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
log_warning("DNS-over-TLS option cannot be set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support."); log_warning("DNS-over-TLS option cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
m->dns_over_tls_mode = DNS_OVER_TLS_NO; m->dns_over_tls_mode = DNS_OVER_TLS_NO;
} }
#endif #endif

View File

@ -419,7 +419,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again..."); log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again...");
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP; s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
} else if (s->n_failed_tls > 0 && } else if (s->n_failed_tls > 0 &&
DNS_SERVER_FEATURE_LEVEL_IS_TLS(s->possible_feature_level)) { DNS_SERVER_FEATURE_LEVEL_IS_TLS(s->possible_feature_level) && dns_server_get_dns_over_tls_mode(s) != DNS_OVER_TLS_YES) {
/* We tried to connect using DNS-over-TLS, and it didn't work. Downgrade to plaintext UDP /* We tried to connect using DNS-over-TLS, and it didn't work. Downgrade to plaintext UDP
* if we don't require DNS-over-TLS */ * if we don't require DNS-over-TLS */

View File

@ -54,6 +54,9 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
server->dnstls_data.session_data.size = 0; server->dnstls_data.session_data.size = 0;
} }
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES)
gnutls_session_set_verify_cert(gs, NULL, 0);
gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream); gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
@ -202,6 +205,10 @@ int dnstls_manager_init(Manager *manager) {
if (r < 0) if (r < 0)
return -ENOMEM; return -ENOMEM;
r = gnutls_certificate_set_x509_system_trust(manager->dnstls_data.cert_cred);
if (r < 0)
log_warning("Failed to load system trust store: %s", gnutls_strerror(r));
return 0; return 0;
} }

View File

@ -76,6 +76,17 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
SSL_set_session(s, server->dnstls_data.session); SSL_set_session(s, server->dnstls_data.session);
SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb)); SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
X509_VERIFY_PARAM *v;
const unsigned char *ip;
SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
v = SSL_get0_param(s);
ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
if (!X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)))
return -ECONNREFUSED;
}
ERR_clear_error(); ERR_clear_error();
stream->dnstls_data.handshake = SSL_do_handshake(s); stream->dnstls_data.handshake = SSL_do_handshake(s);
if (stream->dnstls_data.handshake <= 0) { if (stream->dnstls_data.handshake <= 0) {
@ -357,6 +368,9 @@ int dnstls_manager_init(Manager *manager) {
SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION); SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION); SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
if (r < 0)
log_warning("Failed to load system trust store: %s", ERR_error_string(ERR_get_error(), NULL));
return 0; return 0;
} }

View File

@ -384,7 +384,7 @@ void link_set_dns_over_tls_mode(Link *l, DnsOverTlsMode mode) {
#if ! ENABLE_DNS_OVER_TLS #if ! ENABLE_DNS_OVER_TLS
if (mode != DNS_OVER_TLS_NO) if (mode != DNS_OVER_TLS_NO)
log_warning("DNS-over-TLS option for the link cannot be set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support."); log_warning("DNS-over-TLS option for the link cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
return; return;
#endif #endif

View File

@ -25,5 +25,6 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dnssec_mode, DnssecMode, DNSSEC_YES);
static const char* const dns_over_tls_mode_table[_DNS_OVER_TLS_MODE_MAX] = { static const char* const dns_over_tls_mode_table[_DNS_OVER_TLS_MODE_MAX] = {
[DNS_OVER_TLS_NO] = "no", [DNS_OVER_TLS_NO] = "no",
[DNS_OVER_TLS_OPPORTUNISTIC] = "opportunistic", [DNS_OVER_TLS_OPPORTUNISTIC] = "opportunistic",
[DNS_OVER_TLS_YES] = "yes",
}; };
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_tls_mode, DnsOverTlsMode, _DNS_OVER_TLS_MODE_INVALID); DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_tls_mode, DnsOverTlsMode, DNS_OVER_TLS_YES);

View File

@ -42,6 +42,9 @@ enum DnsOverTlsMode {
* fallback to using an unencrypted connection */ * fallback to using an unencrypted connection */
DNS_OVER_TLS_OPPORTUNISTIC, DNS_OVER_TLS_OPPORTUNISTIC,
/* Enforce DNS-over-TLS and require valid server certificates */
DNS_OVER_TLS_YES,
_DNS_OVER_TLS_MODE_MAX, _DNS_OVER_TLS_MODE_MAX,
_DNS_OVER_TLS_MODE_INVALID = -1 _DNS_OVER_TLS_MODE_INVALID = -1
}; };

View File

@ -127,7 +127,7 @@ int sd_network_link_get_mdns(int ifindex, char **mdns);
/* Indicates whether or not DNS-over-TLS should be enabled for the /* Indicates whether or not DNS-over-TLS should be enabled for the
* link. * link.
* Possible levels of support: strict, no, opportunistic * Possible levels of support: yes, no, opportunistic
* Possible return codes: * Possible return codes:
* -ENODATA: networkd is not aware of the link * -ENODATA: networkd is not aware of the link
*/ */