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