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