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