]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dnstls-openssl.c
resolved: sort headers again
[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.write_buffer->length > 0) {
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,
28 stream->dnstls_data.write_buffer->length);
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;
37 stream->dnstls_data.write_buffer->data += ss;
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
49 int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
50 _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL;
51 _cleanup_(SSL_freep) SSL *s = NULL;
52 int error, r;
53
54 assert(stream);
55 assert(server);
56
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)
63 return -ENOMEM;
64
65 BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
66
67 s = SSL_new(server->dnstls_data.ctx);
68 if (!s)
69 return -ENOMEM;
70
71 SSL_set_connect_state(s);
72 SSL_set_session(s, server->dnstls_data.session);
73 SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
74
75 ERR_clear_error();
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];
81
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 }
87
88 stream->encrypted = true;
89
90 r = dnstls_flush_write_buffer(stream);
91 if (r < 0 && r != -EAGAIN)
92 return r;
93
94 stream->dnstls_data.ssl = TAKE_PTR(s);
95
96 return 0;
97 }
98
99 void 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
107 int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
108 int error, r;
109
110 assert(stream);
111 assert(stream->encrypted);
112 assert(stream->dnstls_data.ssl);
113
114 /* Flush write buffer when requested by OpenSSL */
115 if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) {
116 r = dnstls_flush_write_buffer(stream);
117 if (r < 0)
118 return r;
119 }
120
121 if (stream->dnstls_data.shutdown) {
122 ERR_clear_error();
123 r = SSL_shutdown(stream->dnstls_data.ssl);
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) {
133 error = SSL_get_error(stream->dnstls_data.ssl, r);
134 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
135 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
136
137 r = dnstls_flush_write_buffer(stream);
138 if (r < 0)
139 return r;
140
141 return -EAGAIN;
142 } else if (error == SSL_ERROR_SYSCALL) {
143 if (errno > 0)
144 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
145 } else {
146 char errbuf[256];
147
148 ERR_error_string_n(error, errbuf, sizeof(errbuf));
149 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
150 }
151 }
152
153 stream->dnstls_events = 0;
154 stream->dnstls_data.shutdown = false;
155
156 r = dnstls_flush_write_buffer(stream);
157 if (r < 0)
158 return r;
159
160 dns_stream_unref(stream);
161 return DNSTLS_STREAM_CLOSED;
162 } else if (stream->dnstls_data.handshake <= 0) {
163 ERR_clear_error();
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;
169 r = dnstls_flush_write_buffer(stream);
170 if (r < 0)
171 return r;
172
173 return -EAGAIN;
174 } else {
175 char errbuf[256];
176
177 ERR_error_string_n(error, errbuf, sizeof(errbuf));
178 return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
179 "Failed to invoke SSL_do_handshake: %s",
180 errbuf);
181 }
182 }
183
184 stream->dnstls_events = 0;
185 r = dnstls_flush_write_buffer(stream);
186 if (r < 0)
187 return r;
188 }
189
190 return 0;
191 }
192
193 int dnstls_stream_shutdown(DnsStream *stream, int error) {
194 int ssl_error, r;
195 SSL_SESSION *s;
196
197 assert(stream);
198 assert(stream->encrypted);
199 assert(stream->dnstls_data.ssl);
200
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
211 if (error == ETIMEDOUT) {
212 ERR_clear_error();
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 }
219
220 stream->dnstls_events = 0;
221
222 r = dnstls_flush_write_buffer(stream);
223 if (r < 0)
224 return r;
225
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;
231 r = dnstls_flush_write_buffer(stream);
232 if (r < 0 && r != -EAGAIN)
233 return r;
234
235 if (!stream->dnstls_data.shutdown) {
236 stream->dnstls_data.shutdown = true;
237 dns_stream_ref(stream);
238 }
239 return -EAGAIN;
240 } else if (ssl_error == SSL_ERROR_SYSCALL) {
241 if (errno > 0)
242 log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m");
243 } else {
244 char errbuf[256];
245
246 ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
247 log_debug("Failed to invoke SSL_shutdown, ignoring: %s", errbuf);
248 }
249 }
250
251 stream->dnstls_events = 0;
252 r = dnstls_flush_write_buffer(stream);
253 if (r < 0)
254 return r;
255 }
256
257 return 0;
258 }
259
260 ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
261 int error, r;
262 ssize_t ss;
263
264 assert(stream);
265 assert(stream->encrypted);
266 assert(stream->dnstls_data.ssl);
267 assert(buf);
268
269 ERR_clear_error();
270 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
271 if (r <= 0) {
272 error = SSL_get_error(stream->dnstls_data.ssl, r);
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;
276 } else if (error == SSL_ERROR_ZERO_RETURN) {
277 stream->dnstls_events = 0;
278 ss = 0;
279 } else {
280 char errbuf[256];
281
282 ERR_error_string_n(error, errbuf, sizeof(errbuf));
283 log_debug("Failed to invoke SSL_write: %s", errbuf);
284 stream->dnstls_events = 0;
285 ss = -EPIPE;
286 }
287 } else
288 stream->dnstls_events = 0;
289
290 r = dnstls_flush_write_buffer(stream);
291 if (r < 0)
292 return r;
293
294 return ss;
295 }
296
297 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
298 int error, r;
299 ssize_t ss;
300
301 assert(stream);
302 assert(stream->encrypted);
303 assert(stream->dnstls_data.ssl);
304 assert(buf);
305
306 ERR_clear_error();
307 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
308 if (r <= 0) {
309 error = SSL_get_error(stream->dnstls_data.ssl, r);
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;
313 } else if (error == SSL_ERROR_ZERO_RETURN) {
314 stream->dnstls_events = 0;
315 ss = 0;
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);
321 stream->dnstls_events = 0;
322 ss = -EPIPE;
323 }
324 } else
325 stream->dnstls_events = 0;
326
327 /* flush write buffer in cache of renegotiation */
328 r = dnstls_flush_write_buffer(stream);
329 if (r < 0)
330 return r;
331
332 return ss;
333 }
334
335 void 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
345 void dnstls_server_free(DnsServer *server) {
346 assert(server);
347
348 if (server->dnstls_data.ctx)
349 SSL_CTX_free(server->dnstls_data.ctx);
350
351 if (server->dnstls_data.session)
352 SSL_SESSION_free(server->dnstls_data.session);
353 }