endif
conf.set10('HAVE_GNUTLS', have)
+want_openssl = get_option('openssl')
+if want_openssl != 'false' and not fuzzer_build
+ libopenssl = dependency('openssl',
+ version : '>= 1.1.0',
+ required : want_openssl == 'true')
+ have = libopenssl.found()
+else
+ have = false
+ libopenssl = []
+endif
+conf.set10('HAVE_OPENSSL', have)
+
want_elfutils = get_option('elfutils')
if want_elfutils != 'false' and not fuzzer_build
libdw = dependency('libdw',
dns_over_tls = get_option('dns-over-tls')
if dns_over_tls != 'false'
- have = (conf.get('HAVE_GNUTLS') == 1 and
- libgnutls.version().version_compare('>=3.5.3'))
- if dns_over_tls == 'true' and not have
- error('DNS-over-TLS support was requested, but dependencies are not available')
+ if dns_over_tls == 'openssl'
+ have_gnutls = false
+ else
+ have_gnutls = (conf.get('HAVE_GNUTLS') == 1 and libgnutls.version().version_compare('>= 3.5.3'))
+ if dns_over_tls == 'gnutls' and not have_gnutls
+ error('DNS-over-TLS support was requested with gnutls, but dependencies are not available')
+ endif
endif
+ if dns_over_tls == 'gnutls' or have_gnutls
+ have_openssl = false
+ else
+ have_openssl = conf.get('HAVE_OPENSSL') == 1
+ if dns_over_tls != 'auto' and not have_openssl
+ str = dns_over_tls == 'openssl' ? ' with openssl' : ''
+ error('DNS-over-TLS support was requested$0$, but dependencies are not available'.format(str))
+ endif
+ endif
+ have = have_gnutls or have_openssl
else
- have = false
+ have = have_gnutls = have_openssl = false
endif
conf.set10('ENABLE_DNS_OVER_TLS', have)
+conf.set10('DNS_OVER_TLS_USE_GNUTLS', have_gnutls)
+conf.set10('DNS_OVER_TLS_USE_OPENSSL', have_openssl)
default_dns_over_tls = get_option('default-dns-over-tls')
if fuzzer_build
['qrencode'],
['microhttpd'],
['gnutls'],
+ ['openssl'],
['libcurl'],
['idn'],
['libidn2'],
['localed'],
['networkd'],
['resolve'],
- ['DNS-over-TLS'],
+ ['DNS-over-TLS(gnutls)', conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1],
+ ['DNS-over-TLS(openssl)', conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1],
['coredump'],
['polkit'],
['legacy pkla', install_polkit_pkla],
description : 'default DNS-over-TLS mode',
choices : ['opportunistic', 'no'],
value : 'no')
-option('dns-over-tls', type : 'combo', choices : ['auto', 'true', 'false'],
+option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
description : 'DNS-over-TLS support')
option('dns-servers', type : 'string',
description : 'space-separated list of default DNS servers',
description : 'gcrypt support')
option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'gnutls support')
+option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'openssl support')
option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'elfutils support')
option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_OPENSSL
+#error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
+#endif
+
+#include "resolved-dnstls.h"
+#include "resolved-dns-stream.h"
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
+
+int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
+ _cleanup_(SSL_freep) SSL *s = NULL;
+ _cleanup_(BIO_freep) BIO *b = NULL;
+
+ assert(stream);
+ assert(server);
+
+ b = BIO_new_socket(stream->fd, 0);
+ if (!b)
+ return -ENOMEM;
+
+ s = SSL_new(server->dnstls_data.ctx);
+ if (!s)
+ return -ENOMEM;
+
+ SSL_set_connect_state(s);
+ SSL_set_bio(s, b, b);
+ b = NULL;
+
+ /* DNS-over-TLS using OpenSSL doesn't support TCP Fast Open yet */
+ connect(stream->fd, &stream->tfo_address.sa, stream->tfo_salen);
+ stream->tfo_salen = 0;
+
+ stream->encrypted = true;
+ stream->dnstls_events = EPOLLOUT;
+ stream->dnstls_data.ssl = TAKE_PTR(s);
+
+ return 0;
+}
+
+void dnstls_stream_free(DnsStream *stream) {
+ assert(stream);
+ assert(stream->encrypted);
+
+ if (stream->dnstls_data.ssl)
+ SSL_free(stream->dnstls_data.ssl);
+}
+
+int dnstls_stream_on_io(DnsStream *stream) {
+ int r;
+ int error;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+
+ if (stream->dnstls_data.shutdown) {
+ r = SSL_shutdown(stream->dnstls_data.ssl);
+ if (r == 0)
+ return -EAGAIN;
+ else if (r < 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, r);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ return -EAGAIN;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_shutdown: %s", errbuf);
+ }
+ }
+
+ stream->dnstls_events = 0;
+ stream->dnstls_data.shutdown = false;
+ dns_stream_unref(stream);
+ return DNSTLS_STREAM_CLOSED;
+ } else if (stream->dnstls_data.handshake <= 0) {
+ stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
+ if (stream->dnstls_data.handshake <= 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ return -EAGAIN;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
+ return -ECONNREFUSED;
+ }
+ }
+
+ stream->dnstls_events = 0;
+ }
+
+ return 0;
+}
+
+int dnstls_stream_shutdown(DnsStream *stream, int error) {
+ int r;
+ int ssl_error;
+ SSL_SESSION *s;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+
+ if (error == ETIMEDOUT) {
+ r = SSL_shutdown(stream->dnstls_data.ssl);
+ if (r == 0) {
+ if (!stream->dnstls_data.shutdown) {
+ stream->dnstls_data.shutdown = true;
+ dns_stream_ref(stream);
+ }
+ return -EAGAIN;
+ } else if (r < 0) {
+ ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
+ if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ if (!stream->dnstls_data.shutdown) {
+ stream->dnstls_data.shutdown = true;
+ dns_stream_ref(stream);
+ }
+ return -EAGAIN;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_shutdown: %s", errbuf);
+ }
+ }
+ }
+
+ return 0;
+}
+
+ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
+ int r;
+ int error;
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+ assert(buf);
+
+ ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
+ if (r <= 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, ss);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ ss = -EAGAIN;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_read: %s", errbuf);
+ ss = -EPIPE;
+ }
+ }
+
+ stream->dnstls_events = 0;
+ return ss;
+}
+
+ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
+ int r;
+ int error;
+ ssize_t ss;
+
+ assert(stream);
+ assert(stream->encrypted);
+ assert(stream->dnstls_data.ssl);
+ assert(buf);
+
+ ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
+ if (r <= 0) {
+ error = SSL_get_error(stream->dnstls_data.ssl, ss);
+ if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
+ stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
+ ss = -EAGAIN;
+ } else {
+ char errbuf[256];
+
+ ERR_error_string_n(error, errbuf, sizeof(errbuf));
+ log_debug("Failed to invoke SSL_read: %s", errbuf);
+ ss = -EPIPE;
+ }
+ }
+
+ stream->dnstls_events = 0;
+ return ss;
+}
+
+void dnstls_server_init(DnsServer *server) {
+ assert(server);
+
+ server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
+ if (server->dnstls_data.ctx) {
+ SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
+ SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+ }
+}
+
+void dnstls_server_free(DnsServer *server) {
+ assert(server);
+
+ if (server->dnstls_data.ctx)
+ SSL_CTX_free(server->dnstls_data.ctx);
+}