1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_GNUTLS
4 #error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
7 #include <gnutls/socket.h>
9 #include "iovec-util.h"
10 #include "resolved-dns-stream.h"
11 #include "resolved-dnstls.h"
12 #include "resolved-manager.h"
14 #define TLS_PROTOCOL_PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2"
15 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gnutls_session_t
, gnutls_deinit
, NULL
);
17 static ssize_t
dnstls_stream_vec_push(gnutls_transport_ptr_t p
, const giovec_t
*iov
, int iovcnt
) {
22 r
= dns_stream_writev((DnsStream
*) p
, (const struct iovec
*) iov
, iovcnt
, DNS_STREAM_WRITE_TLS_DATA
);
31 int dnstls_stream_connect_tls(DnsStream
*stream
, DnsServer
*server
) {
32 _cleanup_(gnutls_deinitp
) gnutls_session_t gs
= NULL
;
38 r
= gnutls_init(&gs
, GNUTLS_CLIENT
| GNUTLS_ENABLE_FALSE_START
| GNUTLS_NONBLOCK
);
42 /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
43 r
= gnutls_priority_set_direct(gs
, TLS_PROTOCOL_PRIORITY
, NULL
);
47 r
= gnutls_credentials_set(gs
, GNUTLS_CRD_CERTIFICATE
, stream
->manager
->dnstls_data
.cert_cred
);
51 if (server
->dnstls_data
.session_data
.size
> 0) {
52 gnutls_session_set_data(gs
, server
->dnstls_data
.session_data
.data
, server
->dnstls_data
.session_data
.size
);
54 // Clear old session ticket
55 gnutls_free(server
->dnstls_data
.session_data
.data
);
56 server
->dnstls_data
.session_data
.data
= NULL
;
57 server
->dnstls_data
.session_data
.size
= 0;
60 if (server
->manager
->dns_over_tls_mode
== DNS_OVER_TLS_YES
) {
61 if (server
->server_name
)
62 gnutls_session_set_verify_cert(gs
, server
->server_name
, 0);
64 stream
->dnstls_data
.validation
.type
= GNUTLS_DT_IP_ADDRESS
;
65 if (server
->family
== AF_INET
) {
66 stream
->dnstls_data
.validation
.data
= (unsigned char*) &server
->address
.in
.s_addr
;
67 stream
->dnstls_data
.validation
.size
= 4;
69 stream
->dnstls_data
.validation
.data
= server
->address
.in6
.s6_addr
;
70 stream
->dnstls_data
.validation
.size
= 16;
72 gnutls_session_set_verify_cert2(gs
, &stream
->dnstls_data
.validation
, 1, 0);
76 if (server
->server_name
) {
77 r
= gnutls_server_name_set(gs
, GNUTLS_NAME_DNS
, server
->server_name
, strlen(server
->server_name
));
79 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to set server name: %s", gnutls_strerror(r
));
82 gnutls_handshake_set_timeout(gs
, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT
);
84 gnutls_transport_set_ptr2(gs
, (gnutls_transport_ptr_t
) (long) stream
->fd
, stream
);
85 gnutls_transport_set_vec_push_function(gs
, &dnstls_stream_vec_push
);
87 stream
->encrypted
= true;
88 stream
->dnstls_data
.handshake
= gnutls_handshake(gs
);
89 if (stream
->dnstls_data
.handshake
< 0 && gnutls_error_is_fatal(stream
->dnstls_data
.handshake
))
92 stream
->dnstls_data
.session
= TAKE_PTR(gs
);
97 void dnstls_stream_free(DnsStream
*stream
) {
99 assert(stream
->encrypted
);
101 if (stream
->dnstls_data
.session
)
102 gnutls_deinit(stream
->dnstls_data
.session
);
105 int dnstls_stream_on_io(DnsStream
*stream
, uint32_t revents
) {
109 assert(stream
->encrypted
);
110 assert(stream
->dnstls_data
.session
);
112 if (stream
->dnstls_data
.shutdown
) {
113 r
= gnutls_bye(stream
->dnstls_data
.session
, GNUTLS_SHUT_RDWR
);
114 if (r
== GNUTLS_E_AGAIN
) {
115 stream
->dnstls_events
= gnutls_record_get_direction(stream
->dnstls_data
.session
) == 1 ? EPOLLOUT
: EPOLLIN
;
118 log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r
));
120 stream
->dnstls_events
= 0;
121 stream
->dnstls_data
.shutdown
= false;
122 dns_stream_unref(stream
);
123 return DNSTLS_STREAM_CLOSED
;
124 } else if (stream
->dnstls_data
.handshake
< 0) {
125 stream
->dnstls_data
.handshake
= gnutls_handshake(stream
->dnstls_data
.session
);
126 if (stream
->dnstls_data
.handshake
== GNUTLS_E_AGAIN
) {
127 stream
->dnstls_events
= gnutls_record_get_direction(stream
->dnstls_data
.session
) == 1 ? EPOLLOUT
: EPOLLIN
;
129 } else if (stream
->dnstls_data
.handshake
< 0) {
130 log_debug("Failed to invoke gnutls_handshake: %s", gnutls_strerror(stream
->dnstls_data
.handshake
));
131 if (gnutls_error_is_fatal(stream
->dnstls_data
.handshake
))
132 return -ECONNREFUSED
;
135 stream
->dnstls_events
= 0;
141 int dnstls_stream_shutdown(DnsStream
*stream
, int error
) {
145 assert(stream
->encrypted
);
146 assert(stream
->dnstls_data
.session
);
148 /* Store TLS Ticket for faster successive TLS handshakes */
149 if (stream
->server
&& stream
->server
->dnstls_data
.session_data
.size
== 0 && stream
->dnstls_data
.handshake
== GNUTLS_E_SUCCESS
)
150 gnutls_session_get_data2(stream
->dnstls_data
.session
, &stream
->server
->dnstls_data
.session_data
);
152 if (IN_SET(error
, ETIMEDOUT
, 0)) {
153 r
= gnutls_bye(stream
->dnstls_data
.session
, GNUTLS_SHUT_RDWR
);
154 if (r
== GNUTLS_E_AGAIN
) {
155 if (!stream
->dnstls_data
.shutdown
) {
156 stream
->dnstls_data
.shutdown
= true;
157 dns_stream_ref(stream
);
161 log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r
));
167 ssize_t
dnstls_stream_writev(DnsStream
*stream
, const struct iovec
*iov
, size_t iovcnt
) {
171 assert(stream
->encrypted
);
172 assert(stream
->dnstls_data
.session
);
174 assert(iovec_total_size(iov
, iovcnt
) > 0);
176 gnutls_record_cork(stream
->dnstls_data
.session
);
178 for (size_t i
= 0; i
< iovcnt
; i
++) {
179 ss
= gnutls_record_send(
180 stream
->dnstls_data
.session
,
181 iov
[i
].iov_base
, iov
[i
].iov_len
);
186 ss
= gnutls_record_uncork(stream
->dnstls_data
.session
, 0);
189 case GNUTLS_E_INTERRUPTED
:
194 return log_debug_errno(SYNTHETIC_ERRNO(EPIPE
),
195 "Failed to invoke gnutls_record_send: %s",
196 gnutls_strerror(ss
));
202 ssize_t
dnstls_stream_read(DnsStream
*stream
, void *buf
, size_t count
) {
206 assert(stream
->encrypted
);
207 assert(stream
->dnstls_data
.session
);
210 ss
= gnutls_record_recv(stream
->dnstls_data
.session
, buf
, count
);
213 case GNUTLS_E_INTERRUPTED
:
218 return log_debug_errno(SYNTHETIC_ERRNO(EPIPE
),
219 "Failed to invoke gnutls_record_recv: %s",
220 gnutls_strerror(ss
));
226 void dnstls_server_free(DnsServer
*server
) {
229 if (server
->dnstls_data
.session_data
.data
)
230 gnutls_free(server
->dnstls_data
.session_data
.data
);
233 int dnstls_manager_init(Manager
*manager
) {
237 r
= gnutls_certificate_allocate_credentials(&manager
->dnstls_data
.cert_cred
);
241 r
= gnutls_certificate_set_x509_system_trust(manager
->dnstls_data
.cert_cred
);
243 log_warning("Failed to load system trust store: %s", gnutls_strerror(r
));
248 void dnstls_manager_free(Manager
*manager
) {
251 if (manager
->dnstls_data
.cert_cred
)
252 gnutls_certificate_free_credentials(manager
->dnstls_data
.cert_cred
);