]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dnstls-openssl.c
man: add missing drop-in directory
[thirdparty/systemd.git] / src / resolve / resolved-dnstls-openssl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
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
6
7 #include <openssl/bio.h>
8 #include <openssl/err.h>
9 #include <openssl/x509v3.h>
10
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"
16
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;
24 }
25
26 #define DNSTLS_ERROR_BUFSIZE 256
27 #define DNSTLS_ERROR_STRING(error) \
28 dnstls_error_string((error), (char[DNSTLS_ERROR_BUFSIZE]){}, DNSTLS_ERROR_BUFSIZE)
29
30 static int dnstls_flush_write_buffer(DnsStream *stream) {
31 ssize_t ss;
32
33 assert(stream);
34 assert(stream->encrypted);
35
36 if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
37 assert(stream->dnstls_data.write_buffer->data);
38
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;
46
47 return ss;
48 } else {
49 stream->dnstls_data.buffer_offset += ss;
50
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;
57 }
58 }
59 }
60
61 return 0;
62 }
63
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;
68
69 assert(stream);
70 assert(stream->manager);
71 assert(server);
72
73 rb = BIO_new_socket(stream->fd, 0);
74 if (!rb)
75 return -ENOMEM;
76
77 wb = BIO_new(BIO_s_mem());
78 if (!wb)
79 return -ENOMEM;
80
81 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
82 stream->dnstls_data.buffer_offset = 0;
83
84 s = SSL_new(stream->manager->dnstls_data.ctx);
85 if (!s)
86 return -ENOMEM;
87
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));
93
94 if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
95 X509_VERIFY_PARAM *v;
96
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;
108 }
109 }
110
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));
116 }
117
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));
125 }
126
127 stream->encrypted = true;
128 stream->dnstls_data.ssl = TAKE_PTR(s);
129
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;
134 }
135
136 return 0;
137 }
138
139 void dnstls_stream_free(DnsStream *stream) {
140 assert(stream);
141 assert(stream->encrypted);
142
143 if (stream->dnstls_data.ssl)
144 SSL_free(stream->dnstls_data.ssl);
145 }
146
147 int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
148 int error, r;
149
150 assert(stream);
151 assert(stream->encrypted);
152 assert(stream->dnstls_data.ssl);
153
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;
159 }
160
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;
166
167 r = dnstls_flush_write_buffer(stream);
168 if (r < 0)
169 return r;
170
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;
176
177 r = dnstls_flush_write_buffer(stream);
178 if (r < 0)
179 return r;
180
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));
187 }
188
189 stream->dnstls_events = 0;
190 stream->dnstls_data.shutdown = false;
191
192 r = dnstls_flush_write_buffer(stream);
193 if (r < 0)
194 return r;
195
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;
208
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));
214 }
215
216 stream->dnstls_events = 0;
217 r = dnstls_flush_write_buffer(stream);
218 if (r < 0)
219 return r;
220 }
221
222 return 0;
223 }
224
225 int dnstls_stream_shutdown(DnsStream *stream, int error) {
226 int ssl_error, r;
227 SSL_SESSION *s;
228
229 assert(stream);
230 assert(stream->encrypted);
231 assert(stream->dnstls_data.ssl);
232
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);
238
239 stream->server->dnstls_data.session = s;
240 }
241 }
242
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);
250 }
251
252 stream->dnstls_events = 0;
253
254 r = dnstls_flush_write_buffer(stream);
255 if (r < 0)
256 return r;
257
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;
266
267 if (!stream->dnstls_data.shutdown) {
268 stream->dnstls_data.shutdown = true;
269 dns_stream_ref(stream);
270 }
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));
277 }
278
279 stream->dnstls_events = 0;
280 r = dnstls_flush_write_buffer(stream);
281 if (r < 0)
282 return r;
283 }
284
285 return 0;
286 }
287
288 static ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
289 int error, r;
290 ssize_t ss;
291
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;
306 }
307 } else
308 stream->dnstls_events = 0;
309
310 r = dnstls_flush_write_buffer(stream);
311 if (r < 0)
312 return r;
313
314 return ss;
315 }
316
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;
320
321 assert(stream);
322 assert(stream->encrypted);
323 assert(stream->dnstls_data.ssl);
324 assert(iov);
325 assert(iovec_total_size(iov, iovcnt) > 0);
326
327 if (iovcnt == 1)
328 return dnstls_stream_write(stream, iov[0].iov_base, iov[0].iov_len);
329
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);
336
337 return dnstls_stream_write(stream, buf, count);
338 }
339
340 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
341 int error, r;
342 ssize_t ss;
343
344 assert(stream);
345 assert(stream->encrypted);
346 assert(stream->dnstls_data.ssl);
347 assert(buf);
348
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;
371 }
372 } else
373 stream->dnstls_events = 0;
374
375 /* flush write buffer in cache of renegotiation */
376 r = dnstls_flush_write_buffer(stream);
377 if (r < 0)
378 return r;
379
380 return ss;
381 }
382
383 void dnstls_server_free(DnsServer *server) {
384 assert(server);
385
386 if (server->dnstls_data.session)
387 SSL_SESSION_free(server->dnstls_data.session);
388 }
389
390 int dnstls_manager_init(Manager *manager) {
391 int r;
392
393 assert(manager);
394
395 ERR_load_crypto_strings();
396 SSL_load_error_strings();
397
398 manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
399 if (!manager->dnstls_data.ctx)
400 return -ENOMEM;
401
402 r = SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
403 if (r == 0)
404 return -EIO;
405
406 (void) SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
407
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));
413
414 return 0;
415 }
416
417 void dnstls_manager_free(Manager *manager) {
418 assert(manager);
419
420 if (manager->dnstls_data.ctx)
421 SSL_CTX_free(manager->dnstls_data.ctx);
422 }