]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dnstls-openssl.c
Merge pull request #10944 from poettering/redirect-file-fix
[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 "resolved-dns-stream.h"
11 #include "resolved-dnstls.h"
12
13 DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
14 DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
15
16 static 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] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data,
27 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
48 int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
49 _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
50 _cleanup_(SSL_freep) SSL *s = NULL;
51 int error, r;
52
53 assert(stream);
54 assert(server);
55
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)
62 return -ENOMEM;
63
64 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
65
66 s = SSL_new(server->dnstls_data.ctx);
67 if (!s)
68 return -ENOMEM;
69
70 SSL_set_connect_state(s);
71 SSL_set_session(s, server->dnstls_data.session);
72 SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
73
74 ERR_clear_error();
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];
80
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 }
86
87 stream->encrypted = true;
88
89 r = dnstls_flush_write_buffer(stream);
90 if (r < 0 && r != -EAGAIN)
91 return r;
92
93 stream->dnstls_data.ssl = TAKE_PTR(s);
94
95 return 0;
96 }
97
98 void 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
106 int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
107 int error, r;
108
109 assert(stream);
110 assert(stream->encrypted);
111 assert(stream->dnstls_data.ssl);
112
113 /* Flush write buffer when requested by OpenSSL */
114 if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
115 r = dnstls_flush_write_buffer(stream);
116 if (r < 0)
117 return r;
118 }
119
120 if (stream->dnstls_data.shutdown) {
121 ERR_clear_error();
122 r = SSL_shutdown(stream->dnstls_data.ssl);
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) {
132 error = SSL_get_error(stream->dnstls_data.ssl, r);
133 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
134 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
135
136 r = dnstls_flush_write_buffer(stream);
137 if (r < 0)
138 return r;
139
140 return -EAGAIN;
141 } else if (error == SSL_ERROR_SYSCALL) {
142 if (errno > 0)
143 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
144 } else {
145 char errbuf[256];
146
147 ERR_error_string_n(error, errbuf, sizeof(errbuf));
148 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
149 }
150 }
151
152 stream->dnstls_events = 0;
153 stream->dnstls_data.shutdown = false;
154
155 r = dnstls_flush_write_buffer(stream);
156 if (r < 0)
157 return r;
158
159 dns_stream_unref(stream);
160 return DNSTLS_STREAM_CLOSED;
161 } else if (stream->dnstls_data.handshake <= 0) {
162 ERR_clear_error();
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;
168 r = dnstls_flush_write_buffer(stream);
169 if (r < 0)
170 return r;
171
172 return -EAGAIN;
173 } else {
174 char errbuf[256];
175
176 ERR_error_string_n(error, errbuf, sizeof(errbuf));
177 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
178 "Failed to invoke SSL_do_handshake: %s",
179 errbuf);
180 }
181 }
182
183 stream->dnstls_events = 0;
184 r = dnstls_flush_write_buffer(stream);
185 if (r < 0)
186 return r;
187 }
188
189 return 0;
190 }
191
192 int dnstls_stream_shutdown(DnsStream *stream, int error) {
193 int ssl_error, r;
194 SSL_SESSION *s;
195
196 assert(stream);
197 assert(stream->encrypted);
198 assert(stream->dnstls_data.ssl);
199
200 if (stream->server) {
201 s = SSL_get1_session(stream->dnstls_data.ssl);
202 if (s) {
203 if (stream->server->dnstls_data.session)
204 SSL_SESSION_free(stream->server->dnstls_data.session);
205
206 stream->server->dnstls_data.session = s;
207 }
208 }
209
210 if (error == ETIMEDOUT) {
211 ERR_clear_error();
212 r = SSL_shutdown(stream->dnstls_data.ssl);
213 if (r == 0) {
214 if (!stream->dnstls_data.shutdown) {
215 stream->dnstls_data.shutdown = true;
216 dns_stream_ref(stream);
217 }
218
219 stream->dnstls_events = 0;
220
221 r = dnstls_flush_write_buffer(stream);
222 if (r < 0)
223 return r;
224
225 return -EAGAIN;
226 } else if (r < 0) {
227 ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
228 if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
229 stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
230 r = dnstls_flush_write_buffer(stream);
231 if (r < 0 && r != -EAGAIN)
232 return r;
233
234 if (!stream->dnstls_data.shutdown) {
235 stream->dnstls_data.shutdown = true;
236 dns_stream_ref(stream);
237 }
238 return -EAGAIN;
239 } else if (ssl_error == SSL_ERROR_SYSCALL) {
240 if (errno > 0)
241 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
242 } else {
243 char errbuf[256];
244
245 ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
246 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
247 }
248 }
249
250 stream->dnstls_events = 0;
251 r = dnstls_flush_write_buffer(stream);
252 if (r < 0)
253 return r;
254 }
255
256 return 0;
257 }
258
259 ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
260 int error, r;
261 ssize_t ss;
262
263 assert(stream);
264 assert(stream->encrypted);
265 assert(stream->dnstls_data.ssl);
266 assert(buf);
267
268 ERR_clear_error();
269 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
270 if (r <= 0) {
271 error = SSL_get_error(stream->dnstls_data.ssl, r);
272 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
273 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
274 ss = -EAGAIN;
275 } else if (error == SSL_ERROR_ZERO_RETURN) {
276 stream->dnstls_events = 0;
277 ss = 0;
278 } else {
279 char errbuf[256];
280
281 ERR_error_string_n(error, errbuf, sizeof(errbuf));
282 log_debug("Failed to invoke SSL_write: %s", errbuf);
283 stream->dnstls_events = 0;
284 ss = -EPIPE;
285 }
286 } else
287 stream->dnstls_events = 0;
288
289 r = dnstls_flush_write_buffer(stream);
290 if (r < 0)
291 return r;
292
293 return ss;
294 }
295
296 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
297 int error, r;
298 ssize_t ss;
299
300 assert(stream);
301 assert(stream->encrypted);
302 assert(stream->dnstls_data.ssl);
303 assert(buf);
304
305 ERR_clear_error();
306 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
307 if (r <= 0) {
308 error = SSL_get_error(stream->dnstls_data.ssl, r);
309 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
310 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
311 ss = -EAGAIN;
312 } else if (error == SSL_ERROR_ZERO_RETURN) {
313 stream->dnstls_events = 0;
314 ss = 0;
315 } else {
316 char errbuf[256];
317
318 ERR_error_string_n(error, errbuf, sizeof(errbuf));
319 log_debug("Failed to invoke SSL_read: %s", errbuf);
320 stream->dnstls_events = 0;
321 ss = -EPIPE;
322 }
323 } else
324 stream->dnstls_events = 0;
325
326 /* flush write buffer in cache of renegotiation */
327 r = dnstls_flush_write_buffer(stream);
328 if (r < 0)
329 return r;
330
331 return ss;
332 }
333
334 void dnstls_server_init(DnsServer *server) {
335 assert(server);
336
337 server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
338 if (server->dnstls_data.ctx) {
339 SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
340 SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
341 }
342 }
343
344 void dnstls_server_free(DnsServer *server) {
345 assert(server);
346
347 if (server->dnstls_data.ctx)
348 SSL_CTX_free(server->dnstls_data.ctx);
349
350 if (server->dnstls_data.session)
351 SSL_SESSION_free(server->dnstls_data.session);
352 }