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