]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dnstls-openssl.c
resolved: add strict mode for DNS-over-TLS
[thirdparty/systemd.git] / src / resolve / resolved-dnstls-openssl.c
CommitLineData
096cbdce
IT
1/* SPDX-License-Identifier: LGPL-2.1+ */
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
096cbdce
IT
7#include <openssl/bio.h>
8#include <openssl/err.h>
9
29e719ce 10#include "io-util.h"
72938b93
YW
11#include "resolved-dns-stream.h"
12#include "resolved-dnstls.h"
13
096cbdce
IT
14DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
15DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
16
04c4d919
IT
17static int dnstls_flush_write_buffer(DnsStream *stream) {
18 ssize_t ss;
19
20 assert(stream);
21 assert(stream->encrypted);
22
ab8cd6c9 23 if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
04c4d919
IT
24 assert(stream->dnstls_data.write_buffer->data);
25
26 struct iovec iov[1];
ab8cd6c9
IT
27 iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data + stream->dnstls_data.buffer_offset,
28 stream->dnstls_data.write_buffer->length - stream->dnstls_data.buffer_offset);
04c4d919
IT
29 ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA);
30 if (ss < 0) {
31 if (ss == -EAGAIN)
32 stream->dnstls_events |= EPOLLOUT;
33
34 return ss;
35 } else {
ab8cd6c9 36 stream->dnstls_data.buffer_offset += ss;
04c4d919 37
ab8cd6c9 38 if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
04c4d919
IT
39 stream->dnstls_events |= EPOLLOUT;
40 return -EAGAIN;
ab8cd6c9
IT
41 } else {
42 BIO_reset(SSL_get_wbio(stream->dnstls_data.ssl));
43 stream->dnstls_data.buffer_offset = 0;
04c4d919
IT
44 }
45 }
46 }
47
48 return 0;
49}
50
096cbdce 51int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
36f1946c 52 _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
096cbdce 53 _cleanup_(SSL_freep) SSL *s = NULL;
36f1946c 54 int error, r;
096cbdce
IT
55
56 assert(stream);
e22c5b20 57 assert(stream->manager);
096cbdce
IT
58 assert(server);
59
04c4d919
IT
60 rb = BIO_new_socket(stream->fd, 0);
61 if (!rb)
62 return -ENOMEM;
63
64 wb = BIO_new(BIO_s_mem());
65 if (!wb)
096cbdce
IT
66 return -ENOMEM;
67
04c4d919 68 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
ab8cd6c9 69 stream->dnstls_data.buffer_offset = 0;
04c4d919 70
e22c5b20 71 s = SSL_new(stream->manager->dnstls_data.ctx);
096cbdce
IT
72 if (!s)
73 return -ENOMEM;
74
75 SSL_set_connect_state(s);
04c4d919
IT
76 SSL_set_session(s, server->dnstls_data.session);
77 SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
78
4310bfc2
IT
79 if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
80 X509_VERIFY_PARAM *v;
81 const unsigned char *ip;
82
83 SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
84 v = SSL_get0_param(s);
85 ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
86 if (!X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)))
87 return -ECONNREFUSED;
88 }
89
59c3fee2 90 ERR_clear_error();
04c4d919
IT
91 stream->dnstls_data.handshake = SSL_do_handshake(s);
92 if (stream->dnstls_data.handshake <= 0) {
93 error = SSL_get_error(s, stream->dnstls_data.handshake);
94 if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
95 char errbuf[256];
096cbdce 96
04c4d919
IT
97 ERR_error_string_n(error, errbuf, sizeof(errbuf));
98 log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
99 return -ECONNREFUSED;
100 }
101 }
096cbdce
IT
102
103 stream->encrypted = true;
ab8cd6c9 104 stream->dnstls_data.ssl = TAKE_PTR(s);
04c4d919
IT
105
106 r = dnstls_flush_write_buffer(stream);
ab8cd6c9
IT
107 if (r < 0 && r != -EAGAIN) {
108 SSL_free(TAKE_PTR(stream->dnstls_data.ssl));
04c4d919 109 return r;
ab8cd6c9 110 }
096cbdce
IT
111
112 return 0;
113}
114
115void dnstls_stream_free(DnsStream *stream) {
116 assert(stream);
117 assert(stream->encrypted);
118
119 if (stream->dnstls_data.ssl)
120 SSL_free(stream->dnstls_data.ssl);
121}
122
04c4d919 123int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
36f1946c 124 int error, r;
096cbdce
IT
125
126 assert(stream);
127 assert(stream->encrypted);
128 assert(stream->dnstls_data.ssl);
129
36f1946c 130 /* Flush write buffer when requested by OpenSSL */
04c4d919
IT
131 if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
132 r = dnstls_flush_write_buffer(stream);
133 if (r < 0)
134 return r;
135 }
136
096cbdce 137 if (stream->dnstls_data.shutdown) {
59c3fee2 138 ERR_clear_error();
096cbdce 139 r = SSL_shutdown(stream->dnstls_data.ssl);
8eadd291
YW
140 if (r == 0) {
141 stream->dnstls_events = 0;
142
143 r = dnstls_flush_write_buffer(stream);
144 if (r < 0)
145 return r;
146
147 return -EAGAIN;
148 } else if (r < 0) {
096cbdce 149 error = SSL_get_error(stream->dnstls_data.ssl, r);
8eadd291
YW
150 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
151 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
04c4d919
IT
152
153 r = dnstls_flush_write_buffer(stream);
154 if (r < 0)
155 return r;
156
096cbdce 157 return -EAGAIN;
8eadd291
YW
158 } else if (error == SSL_ERROR_SYSCALL) {
159 if (errno > 0)
160 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
096cbdce
IT
161 } else {
162 char errbuf[256];
163
164 ERR_error_string_n(error, errbuf, sizeof(errbuf));
8eadd291 165 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
096cbdce
IT
166 }
167 }
168
8eadd291
YW
169 stream->dnstls_events = 0;
170 stream->dnstls_data.shutdown = false;
171
04c4d919
IT
172 r = dnstls_flush_write_buffer(stream);
173 if (r < 0)
174 return r;
175
096cbdce
IT
176 dns_stream_unref(stream);
177 return DNSTLS_STREAM_CLOSED;
178 } else if (stream->dnstls_data.handshake <= 0) {
59c3fee2 179 ERR_clear_error();
096cbdce
IT
180 stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
181 if (stream->dnstls_data.handshake <= 0) {
182 error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
183 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
184 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
04c4d919
IT
185 r = dnstls_flush_write_buffer(stream);
186 if (r < 0)
187 return r;
188
096cbdce
IT
189 return -EAGAIN;
190 } else {
191 char errbuf[256];
192
193 ERR_error_string_n(error, errbuf, sizeof(errbuf));
baaa35ad
ZJS
194 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
195 "Failed to invoke SSL_do_handshake: %s",
196 errbuf);
096cbdce
IT
197 }
198 }
199
200 stream->dnstls_events = 0;
04c4d919
IT
201 r = dnstls_flush_write_buffer(stream);
202 if (r < 0)
203 return r;
096cbdce
IT
204 }
205
206 return 0;
207}
208
209int dnstls_stream_shutdown(DnsStream *stream, int error) {
36f1946c 210 int ssl_error, r;
096cbdce
IT
211 SSL_SESSION *s;
212
213 assert(stream);
214 assert(stream->encrypted);
215 assert(stream->dnstls_data.ssl);
216
04c4d919
IT
217 if (stream->server) {
218 s = SSL_get1_session(stream->dnstls_data.ssl);
219 if (s) {
220 if (stream->server->dnstls_data.session)
221 SSL_SESSION_free(stream->server->dnstls_data.session);
222
223 stream->server->dnstls_data.session = s;
224 }
225 }
226
096cbdce 227 if (error == ETIMEDOUT) {
59c3fee2 228 ERR_clear_error();
096cbdce
IT
229 r = SSL_shutdown(stream->dnstls_data.ssl);
230 if (r == 0) {
231 if (!stream->dnstls_data.shutdown) {
232 stream->dnstls_data.shutdown = true;
233 dns_stream_ref(stream);
234 }
04c4d919 235
8eadd291
YW
236 stream->dnstls_events = 0;
237
04c4d919
IT
238 r = dnstls_flush_write_buffer(stream);
239 if (r < 0)
240 return r;
241
096cbdce
IT
242 return -EAGAIN;
243 } else if (r < 0) {
244 ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
245 if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
246 stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
04c4d919
IT
247 r = dnstls_flush_write_buffer(stream);
248 if (r < 0 && r != -EAGAIN)
249 return r;
250
096cbdce
IT
251 if (!stream->dnstls_data.shutdown) {
252 stream->dnstls_data.shutdown = true;
253 dns_stream_ref(stream);
254 }
255 return -EAGAIN;
8eadd291
YW
256 } else if (ssl_error == SSL_ERROR_SYSCALL) {
257 if (errno > 0)
258 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
096cbdce
IT
259 } else {
260 char errbuf[256];
261
262 ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
8eadd291 263 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
096cbdce
IT
264 }
265 }
04c4d919
IT
266
267 stream->dnstls_events = 0;
268 r = dnstls_flush_write_buffer(stream);
269 if (r < 0)
270 return r;
096cbdce
IT
271 }
272
273 return 0;
274}
275
276ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
36f1946c 277 int error, r;
096cbdce
IT
278 ssize_t ss;
279
280 assert(stream);
281 assert(stream->encrypted);
282 assert(stream->dnstls_data.ssl);
283 assert(buf);
284
59c3fee2 285 ERR_clear_error();
096cbdce
IT
286 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
287 if (r <= 0) {
8e740110 288 error = SSL_get_error(stream->dnstls_data.ssl, r);
096cbdce
IT
289 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
290 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
291 ss = -EAGAIN;
8e740110
YW
292 } else if (error == SSL_ERROR_ZERO_RETURN) {
293 stream->dnstls_events = 0;
294 ss = 0;
096cbdce
IT
295 } else {
296 char errbuf[256];
297
298 ERR_error_string_n(error, errbuf, sizeof(errbuf));
36f1946c 299 log_debug("Failed to invoke SSL_write: %s", errbuf);
8e740110 300 stream->dnstls_events = 0;
096cbdce
IT
301 ss = -EPIPE;
302 }
8e740110
YW
303 } else
304 stream->dnstls_events = 0;
096cbdce 305
04c4d919
IT
306 r = dnstls_flush_write_buffer(stream);
307 if (r < 0)
308 return r;
309
096cbdce
IT
310 return ss;
311}
312
313ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
36f1946c 314 int error, r;
096cbdce
IT
315 ssize_t ss;
316
317 assert(stream);
318 assert(stream->encrypted);
319 assert(stream->dnstls_data.ssl);
320 assert(buf);
321
59c3fee2 322 ERR_clear_error();
096cbdce
IT
323 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
324 if (r <= 0) {
8e740110 325 error = SSL_get_error(stream->dnstls_data.ssl, r);
096cbdce
IT
326 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
327 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
328 ss = -EAGAIN;
8e740110
YW
329 } else if (error == SSL_ERROR_ZERO_RETURN) {
330 stream->dnstls_events = 0;
331 ss = 0;
096cbdce
IT
332 } else {
333 char errbuf[256];
334
335 ERR_error_string_n(error, errbuf, sizeof(errbuf));
336 log_debug("Failed to invoke SSL_read: %s", errbuf);
8e740110 337 stream->dnstls_events = 0;
096cbdce
IT
338 ss = -EPIPE;
339 }
8e740110
YW
340 } else
341 stream->dnstls_events = 0;
04c4d919
IT
342
343 /* flush write buffer in cache of renegotiation */
344 r = dnstls_flush_write_buffer(stream);
345 if (r < 0)
346 return r;
347
096cbdce
IT
348 return ss;
349}
350
e22c5b20 351void dnstls_server_free(DnsServer *server) {
096cbdce
IT
352 assert(server);
353
e22c5b20
IT
354 if (server->dnstls_data.session)
355 SSL_SESSION_free(server->dnstls_data.session);
096cbdce
IT
356}
357
71a681ae 358int dnstls_manager_init(Manager *manager) {
e22c5b20
IT
359 int r;
360 assert(manager);
096cbdce 361
e22c5b20
IT
362 ERR_load_crypto_strings();
363 SSL_load_error_strings();
364 manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
71a681ae
IT
365
366 if (!manager->dnstls_data.ctx)
367 return -ENOMEM;
368
369 SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
370 SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
4310bfc2
IT
371 r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
372 if (r < 0)
373 log_warning("Failed to load system trust store: %s", ERR_error_string(ERR_get_error(), NULL));
71a681ae
IT
374
375 return 0;
e22c5b20 376}
04c4d919 377
e22c5b20
IT
378void dnstls_manager_free(Manager *manager) {
379 assert(manager);
380
381 if (manager->dnstls_data.ctx)
382 SSL_CTX_free(manager->dnstls_data.ctx);
096cbdce 383}