1:255.13-alt1
[systemd_ALT.git] / src / resolve / resolved-dnstls-openssl.c
blobfbcee7fc634a80e35edbc1fe10589a3a0d243659
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_OPENSSL
4 #error This source file requires DNS-over-TLS to be enabled and OpenSSL to be available.
5 #endif
7 #include <openssl/bio.h>
8 #include <openssl/err.h>
9 #include <openssl/x509v3.h>
11 #include "io-util.h"
12 #include "openssl-util.h"
13 #include "resolved-dns-stream.h"
14 #include "resolved-dnstls.h"
15 #include "resolved-manager.h"
17 static char *dnstls_error_string(int ssl_error, char *buf, size_t count) {
18 assert(buf || count == 0);
19 if (ssl_error == SSL_ERROR_SSL)
20 ERR_error_string_n(ERR_get_error(), buf, count);
21 else
22 snprintf(buf, count, "SSL_get_error()=%d", ssl_error);
23 return buf;
26 #define DNSTLS_ERROR_BUFSIZE 256
27 #define DNSTLS_ERROR_STRING(error) \
28 dnstls_error_string((error), (char[DNSTLS_ERROR_BUFSIZE]){}, DNSTLS_ERROR_BUFSIZE)
30 static int dnstls_flush_write_buffer(DnsStream *stream) {
31 ssize_t ss;
33 assert(stream);
34 assert(stream->encrypted);
36 if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
37 assert(stream->dnstls_data.write_buffer->data);
39 struct iovec iov[1];
40 iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data + stream->dnstls_data.buffer_offset,
41 stream->dnstls_data.write_buffer->length - stream->dnstls_data.buffer_offset);
42 ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA);
43 if (ss < 0) {
44 if (ss == -EAGAIN)
45 stream->dnstls_events |= EPOLLOUT;
47 return ss;
48 } else {
49 stream->dnstls_data.buffer_offset += ss;
51 if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
52 stream->dnstls_events |= EPOLLOUT;
53 return -EAGAIN;
54 } else {
55 BIO_reset(SSL_get_wbio(stream->dnstls_data.ssl));
56 stream->dnstls_data.buffer_offset = 0;
61 return 0;
64 int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
65 _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
66 _cleanup_(SSL_freep) SSL *s = NULL;
67 int error, r;
69 assert(stream);
70 assert(stream->manager);
71 assert(server);
73 rb = BIO_new_socket(stream->fd, 0);
74 if (!rb)
75 return -ENOMEM;
77 wb = BIO_new(BIO_s_mem());
78 if (!wb)
79 return -ENOMEM;
81 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
82 stream->dnstls_data.buffer_offset = 0;
84 s = SSL_new(stream->manager->dnstls_data.ctx);
85 if (!s)
86 return -ENOMEM;
88 SSL_set_connect_state(s);
89 r = SSL_set_session(s, server->dnstls_data.session);
90 if (r == 0)
91 return -EIO;
92 SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
94 if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
95 X509_VERIFY_PARAM *v;
97 SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
98 v = SSL_get0_param(s);
99 if (server->server_name) {
100 X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
101 if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
102 return -ECONNREFUSED;
103 } else {
104 const unsigned char *ip;
105 ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
106 if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
107 return -ECONNREFUSED;
111 if (server->server_name) {
112 r = SSL_set_tlsext_host_name(s, server->server_name);
113 if (r <= 0)
114 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
115 "Failed to set server name: %s", DNSTLS_ERROR_STRING(SSL_ERROR_SSL));
118 ERR_clear_error();
119 stream->dnstls_data.handshake = SSL_do_handshake(s);
120 if (stream->dnstls_data.handshake <= 0) {
121 error = SSL_get_error(s, stream->dnstls_data.handshake);
122 if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE))
123 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
124 "Failed to invoke SSL_do_handshake: %s", DNSTLS_ERROR_STRING(error));
127 stream->encrypted = true;
128 stream->dnstls_data.ssl = TAKE_PTR(s);
130 r = dnstls_flush_write_buffer(stream);
131 if (r < 0 && r != -EAGAIN) {
132 SSL_free(TAKE_PTR(stream->dnstls_data.ssl));
133 return r;
136 return 0;
139 void dnstls_stream_free(DnsStream *stream) {
140 assert(stream);
141 assert(stream->encrypted);
143 if (stream->dnstls_data.ssl)
144 SSL_free(stream->dnstls_data.ssl);
147 int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
148 int error, r;
150 assert(stream);
151 assert(stream->encrypted);
152 assert(stream->dnstls_data.ssl);
154 /* Flush write buffer when requested by OpenSSL */
155 if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
156 r = dnstls_flush_write_buffer(stream);
157 if (r < 0)
158 return r;
161 if (stream->dnstls_data.shutdown) {
162 ERR_clear_error();
163 r = SSL_shutdown(stream->dnstls_data.ssl);
164 if (r == 0) {
165 stream->dnstls_events = 0;
167 r = dnstls_flush_write_buffer(stream);
168 if (r < 0)
169 return r;
171 return -EAGAIN;
172 } else if (r < 0) {
173 error = SSL_get_error(stream->dnstls_data.ssl, r);
174 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
175 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
177 r = dnstls_flush_write_buffer(stream);
178 if (r < 0)
179 return r;
181 return -EAGAIN;
182 } else if (error == SSL_ERROR_SYSCALL) {
183 if (errno > 0)
184 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
185 } else
186 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", DNSTLS_ERROR_STRING(error));
189 stream->dnstls_events = 0;
190 stream->dnstls_data.shutdown = false;
192 r = dnstls_flush_write_buffer(stream);
193 if (r < 0)
194 return r;
196 dns_stream_unref(stream);
197 return DNSTLS_STREAM_CLOSED;
198 } else if (stream->dnstls_data.handshake <= 0) {
199 ERR_clear_error();
200 stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
201 if (stream->dnstls_data.handshake <= 0) {
202 error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
203 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
204 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
205 r = dnstls_flush_write_buffer(stream);
206 if (r < 0)
207 return r;
209 return -EAGAIN;
210 } else
211 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
212 "Failed to invoke SSL_do_handshake: %s",
213 DNSTLS_ERROR_STRING(error));
216 stream->dnstls_events = 0;
217 r = dnstls_flush_write_buffer(stream);
218 if (r < 0)
219 return r;
222 return 0;
225 int dnstls_stream_shutdown(DnsStream *stream, int error) {
226 int ssl_error, r;
227 SSL_SESSION *s;
229 assert(stream);
230 assert(stream->encrypted);
231 assert(stream->dnstls_data.ssl);
233 if (stream->server) {
234 s = SSL_get1_session(stream->dnstls_data.ssl);
235 if (s) {
236 if (stream->server->dnstls_data.session)
237 SSL_SESSION_free(stream->server->dnstls_data.session);
239 stream->server->dnstls_data.session = s;
243 if (error == ETIMEDOUT) {
244 ERR_clear_error();
245 r = SSL_shutdown(stream->dnstls_data.ssl);
246 if (r == 0) {
247 if (!stream->dnstls_data.shutdown) {
248 stream->dnstls_data.shutdown = true;
249 dns_stream_ref(stream);
252 stream->dnstls_events = 0;
254 r = dnstls_flush_write_buffer(stream);
255 if (r < 0)
256 return r;
258 return -EAGAIN;
259 } else if (r < 0) {
260 ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
261 if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
262 stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
263 r = dnstls_flush_write_buffer(stream);
264 if (r < 0 && r != -EAGAIN)
265 return r;
267 if (!stream->dnstls_data.shutdown) {
268 stream->dnstls_data.shutdown = true;
269 dns_stream_ref(stream);
271 return -EAGAIN;
272 } else if (ssl_error == SSL_ERROR_SYSCALL) {
273 if (errno > 0)
274 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
275 } else
276 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", DNSTLS_ERROR_STRING(ssl_error));
279 stream->dnstls_events = 0;
280 r = dnstls_flush_write_buffer(stream);
281 if (r < 0)
282 return r;
285 return 0;
288 static ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
289 int error, r;
290 ssize_t ss;
292 ERR_clear_error();
293 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
294 if (r <= 0) {
295 error = SSL_get_error(stream->dnstls_data.ssl, r);
296 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
297 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
298 ss = -EAGAIN;
299 } else if (error == SSL_ERROR_ZERO_RETURN) {
300 stream->dnstls_events = 0;
301 ss = 0;
302 } else {
303 log_debug("Failed to invoke SSL_write: %s", DNSTLS_ERROR_STRING(error));
304 stream->dnstls_events = 0;
305 ss = -EPIPE;
307 } else
308 stream->dnstls_events = 0;
310 r = dnstls_flush_write_buffer(stream);
311 if (r < 0)
312 return r;
314 return ss;
317 ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt) {
318 _cleanup_free_ char *buf = NULL;
319 size_t count;
321 assert(stream);
322 assert(stream->encrypted);
323 assert(stream->dnstls_data.ssl);
324 assert(iov);
325 assert(iovec_total_size(iov, iovcnt) > 0);
327 if (iovcnt == 1)
328 return dnstls_stream_write(stream, iov[0].iov_base, iov[0].iov_len);
330 /* As of now, OpenSSL cannot accumulate multiple writes, so join into a
331 single buffer. Suboptimal, but better than multiple SSL_write calls. */
332 count = iovec_total_size(iov, iovcnt);
333 buf = new(char, count);
334 for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++)
335 memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len);
337 return dnstls_stream_write(stream, buf, count);
340 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
341 int error, r;
342 ssize_t ss;
344 assert(stream);
345 assert(stream->encrypted);
346 assert(stream->dnstls_data.ssl);
347 assert(buf);
349 ERR_clear_error();
350 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
351 if (r <= 0) {
352 error = SSL_get_error(stream->dnstls_data.ssl, r);
353 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
354 /* If we receive SSL_ERROR_WANT_READ here, there are two possible scenarios:
355 * OpenSSL needs to renegotiate (so we want to get an EPOLLIN event), or
356 * There is no more application data is available, so we can just return
357 And apparently there's no nice way to distinguish between the two.
358 To handle this, never set EPOLLIN and just continue as usual.
359 If OpenSSL really wants to read due to renegotiation, it will tell us
360 again on SSL_write (at which point we will request EPOLLIN force a read);
361 or we will just eventually read data anyway while we wait for a packet */
362 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? 0 : EPOLLOUT;
363 ss = -EAGAIN;
364 } else if (error == SSL_ERROR_ZERO_RETURN) {
365 stream->dnstls_events = 0;
366 ss = 0;
367 } else {
368 log_debug("Failed to invoke SSL_read: %s", DNSTLS_ERROR_STRING(error));
369 stream->dnstls_events = 0;
370 ss = -EPIPE;
372 } else
373 stream->dnstls_events = 0;
375 /* flush write buffer in cache of renegotiation */
376 r = dnstls_flush_write_buffer(stream);
377 if (r < 0)
378 return r;
380 return ss;
383 void dnstls_server_free(DnsServer *server) {
384 assert(server);
386 if (server->dnstls_data.session)
387 SSL_SESSION_free(server->dnstls_data.session);
390 int dnstls_manager_init(Manager *manager) {
391 int r;
393 assert(manager);
395 ERR_load_crypto_strings();
396 SSL_load_error_strings();
398 manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
399 if (!manager->dnstls_data.ctx)
400 return -ENOMEM;
402 r = SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
403 if (r == 0)
404 return -EIO;
406 (void) SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
408 r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
409 if (r == 0)
410 return log_warning_errno(SYNTHETIC_ERRNO(EIO),
411 "Failed to load system trust store: %s",
412 ERR_error_string(ERR_get_error(), NULL));
414 return 0;
417 void dnstls_manager_free(Manager *manager) {
418 assert(manager);
420 if (manager->dnstls_data.ctx)
421 SSL_CTX_free(manager->dnstls_data.ctx);