]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dnstls-openssl.c
fix(journal-gatewayd): use relative urls (not starting with '/')
[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);
57 assert(server);
58
04c4d919
IT
59 rb = BIO_new_socket(stream->fd, 0);
60 if (!rb)
61 return -ENOMEM;
62
63 wb = BIO_new(BIO_s_mem());
64 if (!wb)
096cbdce
IT
65 return -ENOMEM;
66
04c4d919 67 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
ab8cd6c9 68 stream->dnstls_data.buffer_offset = 0;
04c4d919 69
096cbdce
IT
70 s = SSL_new(server->dnstls_data.ctx);
71 if (!s)
72 return -ENOMEM;
73
74 SSL_set_connect_state(s);
04c4d919
IT
75 SSL_set_session(s, server->dnstls_data.session);
76 SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
77
59c3fee2 78 ERR_clear_error();
04c4d919
IT
79 stream->dnstls_data.handshake = SSL_do_handshake(s);
80 if (stream->dnstls_data.handshake <= 0) {
81 error = SSL_get_error(s, stream->dnstls_data.handshake);
82 if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
83 char errbuf[256];
096cbdce 84
04c4d919
IT
85 ERR_error_string_n(error, errbuf, sizeof(errbuf));
86 log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
87 return -ECONNREFUSED;
88 }
89 }
096cbdce
IT
90
91 stream->encrypted = true;
ab8cd6c9 92 stream->dnstls_data.ssl = TAKE_PTR(s);
04c4d919
IT
93
94 r = dnstls_flush_write_buffer(stream);
ab8cd6c9
IT
95 if (r < 0 && r != -EAGAIN) {
96 SSL_free(TAKE_PTR(stream->dnstls_data.ssl));
04c4d919 97 return r;
ab8cd6c9 98 }
096cbdce
IT
99
100 return 0;
101}
102
103void dnstls_stream_free(DnsStream *stream) {
104 assert(stream);
105 assert(stream->encrypted);
106
107 if (stream->dnstls_data.ssl)
108 SSL_free(stream->dnstls_data.ssl);
109}
110
04c4d919 111int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
36f1946c 112 int error, r;
096cbdce
IT
113
114 assert(stream);
115 assert(stream->encrypted);
116 assert(stream->dnstls_data.ssl);
117
36f1946c 118 /* Flush write buffer when requested by OpenSSL */
04c4d919
IT
119 if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
120 r = dnstls_flush_write_buffer(stream);
121 if (r < 0)
122 return r;
123 }
124
096cbdce 125 if (stream->dnstls_data.shutdown) {
59c3fee2 126 ERR_clear_error();
096cbdce 127 r = SSL_shutdown(stream->dnstls_data.ssl);
8eadd291
YW
128 if (r == 0) {
129 stream->dnstls_events = 0;
130
131 r = dnstls_flush_write_buffer(stream);
132 if (r < 0)
133 return r;
134
135 return -EAGAIN;
136 } else if (r < 0) {
096cbdce 137 error = SSL_get_error(stream->dnstls_data.ssl, r);
8eadd291
YW
138 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
139 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
04c4d919
IT
140
141 r = dnstls_flush_write_buffer(stream);
142 if (r < 0)
143 return r;
144
096cbdce 145 return -EAGAIN;
8eadd291
YW
146 } else if (error == SSL_ERROR_SYSCALL) {
147 if (errno > 0)
148 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
096cbdce
IT
149 } else {
150 char errbuf[256];
151
152 ERR_error_string_n(error, errbuf, sizeof(errbuf));
8eadd291 153 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
096cbdce
IT
154 }
155 }
156
8eadd291
YW
157 stream->dnstls_events = 0;
158 stream->dnstls_data.shutdown = false;
159
04c4d919
IT
160 r = dnstls_flush_write_buffer(stream);
161 if (r < 0)
162 return r;
163
096cbdce
IT
164 dns_stream_unref(stream);
165 return DNSTLS_STREAM_CLOSED;
166 } else if (stream->dnstls_data.handshake <= 0) {
59c3fee2 167 ERR_clear_error();
096cbdce
IT
168 stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
169 if (stream->dnstls_data.handshake <= 0) {
170 error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
171 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
172 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
04c4d919
IT
173 r = dnstls_flush_write_buffer(stream);
174 if (r < 0)
175 return r;
176
096cbdce
IT
177 return -EAGAIN;
178 } else {
179 char errbuf[256];
180
181 ERR_error_string_n(error, errbuf, sizeof(errbuf));
baaa35ad
ZJS
182 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
183 "Failed to invoke SSL_do_handshake: %s",
184 errbuf);
096cbdce
IT
185 }
186 }
187
188 stream->dnstls_events = 0;
04c4d919
IT
189 r = dnstls_flush_write_buffer(stream);
190 if (r < 0)
191 return r;
096cbdce
IT
192 }
193
194 return 0;
195}
196
197int dnstls_stream_shutdown(DnsStream *stream, int error) {
36f1946c 198 int ssl_error, r;
096cbdce
IT
199 SSL_SESSION *s;
200
201 assert(stream);
202 assert(stream->encrypted);
203 assert(stream->dnstls_data.ssl);
204
04c4d919
IT
205 if (stream->server) {
206 s = SSL_get1_session(stream->dnstls_data.ssl);
207 if (s) {
208 if (stream->server->dnstls_data.session)
209 SSL_SESSION_free(stream->server->dnstls_data.session);
210
211 stream->server->dnstls_data.session = s;
212 }
213 }
214
096cbdce 215 if (error == ETIMEDOUT) {
59c3fee2 216 ERR_clear_error();
096cbdce
IT
217 r = SSL_shutdown(stream->dnstls_data.ssl);
218 if (r == 0) {
219 if (!stream->dnstls_data.shutdown) {
220 stream->dnstls_data.shutdown = true;
221 dns_stream_ref(stream);
222 }
04c4d919 223
8eadd291
YW
224 stream->dnstls_events = 0;
225
04c4d919
IT
226 r = dnstls_flush_write_buffer(stream);
227 if (r < 0)
228 return r;
229
096cbdce
IT
230 return -EAGAIN;
231 } else if (r < 0) {
232 ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
233 if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
234 stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
04c4d919
IT
235 r = dnstls_flush_write_buffer(stream);
236 if (r < 0 && r != -EAGAIN)
237 return r;
238
096cbdce
IT
239 if (!stream->dnstls_data.shutdown) {
240 stream->dnstls_data.shutdown = true;
241 dns_stream_ref(stream);
242 }
243 return -EAGAIN;
8eadd291
YW
244 } else if (ssl_error == SSL_ERROR_SYSCALL) {
245 if (errno > 0)
246 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
096cbdce
IT
247 } else {
248 char errbuf[256];
249
250 ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
8eadd291 251 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
096cbdce
IT
252 }
253 }
04c4d919
IT
254
255 stream->dnstls_events = 0;
256 r = dnstls_flush_write_buffer(stream);
257 if (r < 0)
258 return r;
096cbdce
IT
259 }
260
261 return 0;
262}
263
264ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
36f1946c 265 int error, r;
096cbdce
IT
266 ssize_t ss;
267
268 assert(stream);
269 assert(stream->encrypted);
270 assert(stream->dnstls_data.ssl);
271 assert(buf);
272
59c3fee2 273 ERR_clear_error();
096cbdce
IT
274 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
275 if (r <= 0) {
8e740110 276 error = SSL_get_error(stream->dnstls_data.ssl, r);
096cbdce
IT
277 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
278 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
279 ss = -EAGAIN;
8e740110
YW
280 } else if (error == SSL_ERROR_ZERO_RETURN) {
281 stream->dnstls_events = 0;
282 ss = 0;
096cbdce
IT
283 } else {
284 char errbuf[256];
285
286 ERR_error_string_n(error, errbuf, sizeof(errbuf));
36f1946c 287 log_debug("Failed to invoke SSL_write: %s", errbuf);
8e740110 288 stream->dnstls_events = 0;
096cbdce
IT
289 ss = -EPIPE;
290 }
8e740110
YW
291 } else
292 stream->dnstls_events = 0;
096cbdce 293
04c4d919
IT
294 r = dnstls_flush_write_buffer(stream);
295 if (r < 0)
296 return r;
297
096cbdce
IT
298 return ss;
299}
300
301ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
36f1946c 302 int error, r;
096cbdce
IT
303 ssize_t ss;
304
305 assert(stream);
306 assert(stream->encrypted);
307 assert(stream->dnstls_data.ssl);
308 assert(buf);
309
59c3fee2 310 ERR_clear_error();
096cbdce
IT
311 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
312 if (r <= 0) {
8e740110 313 error = SSL_get_error(stream->dnstls_data.ssl, r);
096cbdce
IT
314 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
315 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
316 ss = -EAGAIN;
8e740110
YW
317 } else if (error == SSL_ERROR_ZERO_RETURN) {
318 stream->dnstls_events = 0;
319 ss = 0;
096cbdce
IT
320 } else {
321 char errbuf[256];
322
323 ERR_error_string_n(error, errbuf, sizeof(errbuf));
324 log_debug("Failed to invoke SSL_read: %s", errbuf);
8e740110 325 stream->dnstls_events = 0;
096cbdce
IT
326 ss = -EPIPE;
327 }
8e740110
YW
328 } else
329 stream->dnstls_events = 0;
04c4d919
IT
330
331 /* flush write buffer in cache of renegotiation */
332 r = dnstls_flush_write_buffer(stream);
333 if (r < 0)
334 return r;
335
096cbdce
IT
336 return ss;
337}
338
339void dnstls_server_init(DnsServer *server) {
340 assert(server);
341
342 server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
343 if (server->dnstls_data.ctx) {
344 SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
345 SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
346 }
347}
348
349void dnstls_server_free(DnsServer *server) {
350 assert(server);
351
352 if (server->dnstls_data.ctx)
353 SSL_CTX_free(server->dnstls_data.ctx);
04c4d919
IT
354
355 if (server->dnstls_data.session)
356 SSL_SESSION_free(server->dnstls_data.session);
096cbdce 357}