systemd-resolved: use hostname for certificate validation in DoT

Widely accepted certificates for IP addresses are expensive and only
affordable for larger organizations. Therefore if the user provides
the hostname in the DNS= option, we should use it instead of the IP
address.
This commit is contained in:
Jörg Thalheim 2020-03-03 23:31:25 +00:00 committed by Yu Watanabe
parent 0d14eefb0d
commit eec394f10b
3 changed files with 34 additions and 20 deletions

View File

@ -193,11 +193,17 @@
<varlistentry>
<term><varname>DNSOverTLS=</varname></term>
<listitem>
<para>Takes a boolean argument or <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>
<para>Takes a boolean argument or <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. If the hostname was specified in <varname>DNS=</varname>
by using the format format <literal>address#server_name</literal> it
is used to validate its certificate and also to enable Server Name
Indication (SNI) when opening a TLS connection. Otherwise
the certificate is checked against the server's IP.
If the DNS server does not support DNS-over-TLS all DNS requests will fail.</para>
<para>When set to <literal>opportunistic</literal>
DNS request are attempted to send encrypted with DNS-over-TLS.
If the DNS server does not support TLS, DNS-over-TLS is disabled.
Note that this mode makes DNS-over-TLS vulnerable to "downgrade"
@ -214,9 +220,6 @@
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

View File

@ -56,15 +56,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
}
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
if (server->family == AF_INET) {
stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
stream->dnstls_data.validation.size = 4;
} else {
stream->dnstls_data.validation.data = server->address.in6.s6_addr;
stream->dnstls_data.validation.size = 16;
if (server->server_name)
gnutls_session_set_verify_cert(gs, server->server_name, 0);
else {
stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
if (server->family == AF_INET) {
stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
stream->dnstls_data.validation.size = 4;
} else {
stream->dnstls_data.validation.data = server->address.in6.s6_addr;
stream->dnstls_data.validation.size = 16;
}
gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
}
gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
}
if (server->server_name) {

View File

@ -6,6 +6,7 @@
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#include "io-util.h"
#include "resolved-dns-stream.h"
@ -80,13 +81,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
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)) == 0)
return -ECONNREFUSED;
if (server->server_name) {
X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
return -ECONNREFUSED;
} else {
const unsigned char *ip;
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)) == 0)
return -ECONNREFUSED;
}
}
if (server->server_name) {