]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dnstls-openssl.c
resolved: make no changes to OpenSSL BUF_MEM struct
[thirdparty/systemd.git] / src / resolve / resolved-dnstls-openssl.c
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 <openssl/bio.h>
8 #include <openssl/err.h>
9
10 #include "io-util.h"
11 #include "resolved-dns-stream.h"
12 #include "resolved-dnstls.h"
13
14 DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
15 DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
16
17 static 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.buffer_offset < stream->dnstls_data.write_buffer->length) {
24 assert(stream->dnstls_data.write_buffer->data);
25
26 struct iovec iov[1];
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);
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.buffer_offset += ss;
37
38 if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
39 stream->dnstls_events |= EPOLLOUT;
40 return -EAGAIN;
41 } else {
42 BIO_reset(SSL_get_wbio(stream->dnstls_data.ssl));
43 stream->dnstls_data.buffer_offset = 0;
44 }
45 }
46 }
47
48 return 0;
49 }
50
51 int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
52 _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
53 _cleanup_(SSL_freep) SSL *s = NULL;
54 int error, r;
55
56 assert(stream);
57 assert(server);
58
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)
65 return -ENOMEM;
66
67 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
68 stream->dnstls_data.buffer_offset = 0;
69
70 s = SSL_new(server->dnstls_data.ctx);
71 if (!s)
72 return -ENOMEM;
73
74 SSL_set_connect_state(s);
75 SSL_set_session(s, server->dnstls_data.session);
76 SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
77
78 ERR_clear_error();
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];
84
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 }
90
91 stream->encrypted = true;
92 stream->dnstls_data.ssl = TAKE_PTR(s);
93
94 r = dnstls_flush_write_buffer(stream);
95 if (r < 0 && r != -EAGAIN) {
96 SSL_free(TAKE_PTR(stream->dnstls_data.ssl));
97 return r;
98 }
99
100 return 0;
101 }
102
103 void 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
111 int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
112 int error, r;
113
114 assert(stream);
115 assert(stream->encrypted);
116 assert(stream->dnstls_data.ssl);
117
118 /* Flush write buffer when requested by OpenSSL */
119 if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
120 r = dnstls_flush_write_buffer(stream);
121 if (r < 0)
122 return r;
123 }
124
125 if (stream->dnstls_data.shutdown) {
126 ERR_clear_error();
127 r = SSL_shutdown(stream->dnstls_data.ssl);
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) {
137 error = SSL_get_error(stream->dnstls_data.ssl, r);
138 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
139 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
140
141 r = dnstls_flush_write_buffer(stream);
142 if (r < 0)
143 return r;
144
145 return -EAGAIN;
146 } else if (error == SSL_ERROR_SYSCALL) {
147 if (errno > 0)
148 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
149 } else {
150 char errbuf[256];
151
152 ERR_error_string_n(error, errbuf, sizeof(errbuf));
153 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
154 }
155 }
156
157 stream->dnstls_events = 0;
158 stream->dnstls_data.shutdown = false;
159
160 r = dnstls_flush_write_buffer(stream);
161 if (r < 0)
162 return r;
163
164 dns_stream_unref(stream);
165 return DNSTLS_STREAM_CLOSED;
166 } else if (stream->dnstls_data.handshake <= 0) {
167 ERR_clear_error();
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;
173 r = dnstls_flush_write_buffer(stream);
174 if (r < 0)
175 return r;
176
177 return -EAGAIN;
178 } else {
179 char errbuf[256];
180
181 ERR_error_string_n(error, errbuf, sizeof(errbuf));
182 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
183 "Failed to invoke SSL_do_handshake: %s",
184 errbuf);
185 }
186 }
187
188 stream->dnstls_events = 0;
189 r = dnstls_flush_write_buffer(stream);
190 if (r < 0)
191 return r;
192 }
193
194 return 0;
195 }
196
197 int dnstls_stream_shutdown(DnsStream *stream, int error) {
198 int ssl_error, r;
199 SSL_SESSION *s;
200
201 assert(stream);
202 assert(stream->encrypted);
203 assert(stream->dnstls_data.ssl);
204
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
215 if (error == ETIMEDOUT) {
216 ERR_clear_error();
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 }
223
224 stream->dnstls_events = 0;
225
226 r = dnstls_flush_write_buffer(stream);
227 if (r < 0)
228 return r;
229
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;
235 r = dnstls_flush_write_buffer(stream);
236 if (r < 0 && r != -EAGAIN)
237 return r;
238
239 if (!stream->dnstls_data.shutdown) {
240 stream->dnstls_data.shutdown = true;
241 dns_stream_ref(stream);
242 }
243 return -EAGAIN;
244 } else if (ssl_error == SSL_ERROR_SYSCALL) {
245 if (errno > 0)
246 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
247 } else {
248 char errbuf[256];
249
250 ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
251 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
252 }
253 }
254
255 stream->dnstls_events = 0;
256 r = dnstls_flush_write_buffer(stream);
257 if (r < 0)
258 return r;
259 }
260
261 return 0;
262 }
263
264 ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
265 int error, r;
266 ssize_t ss;
267
268 assert(stream);
269 assert(stream->encrypted);
270 assert(stream->dnstls_data.ssl);
271 assert(buf);
272
273 ERR_clear_error();
274 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
275 if (r <= 0) {
276 error = SSL_get_error(stream->dnstls_data.ssl, r);
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;
280 } else if (error == SSL_ERROR_ZERO_RETURN) {
281 stream->dnstls_events = 0;
282 ss = 0;
283 } else {
284 char errbuf[256];
285
286 ERR_error_string_n(error, errbuf, sizeof(errbuf));
287 log_debug("Failed to invoke SSL_write: %s", errbuf);
288 stream->dnstls_events = 0;
289 ss = -EPIPE;
290 }
291 } else
292 stream->dnstls_events = 0;
293
294 r = dnstls_flush_write_buffer(stream);
295 if (r < 0)
296 return r;
297
298 return ss;
299 }
300
301 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
302 int error, r;
303 ssize_t ss;
304
305 assert(stream);
306 assert(stream->encrypted);
307 assert(stream->dnstls_data.ssl);
308 assert(buf);
309
310 ERR_clear_error();
311 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
312 if (r <= 0) {
313 error = SSL_get_error(stream->dnstls_data.ssl, r);
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;
317 } else if (error == SSL_ERROR_ZERO_RETURN) {
318 stream->dnstls_events = 0;
319 ss = 0;
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);
325 stream->dnstls_events = 0;
326 ss = -EPIPE;
327 }
328 } else
329 stream->dnstls_events = 0;
330
331 /* flush write buffer in cache of renegotiation */
332 r = dnstls_flush_write_buffer(stream);
333 if (r < 0)
334 return r;
335
336 return ss;
337 }
338
339 void 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
349 void dnstls_server_free(DnsServer *server) {
350 assert(server);
351
352 if (server->dnstls_data.ctx)
353 SSL_CTX_free(server->dnstls_data.ctx);
354
355 if (server->dnstls_data.session)
356 SSL_SESSION_free(server->dnstls_data.session);
357 }