]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dnstls-openssl.c
resolved: TCP Fast Open and TLS Session Tickets for OpenSSL
[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 "resolved-dnstls.h"
8 #include "resolved-dns-stream.h"
9
10 #include <openssl/bio.h>
11 #include <openssl/err.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].iov_base = stream->dnstls_data.write_buffer->data;
27 iov[0].iov_len = 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_(SSL_freep) SSL *s = NULL;
50 _cleanup_(BIO_freep) BIO *rb = NULL;
51 _cleanup_(BIO_freep) BIO *wb = NULL;
52 int r;
53 int error;
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 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 r;
109 int error;
110
111 assert(stream);
112 assert(stream->encrypted);
113 assert(stream->dnstls_data.ssl);
114
115 /* Flush write buffer when requested by OpenSSL ss*/
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 r = SSL_shutdown(stream->dnstls_data.ssl);
124 if (r <= 0) {
125 error = SSL_get_error(stream->dnstls_data.ssl, r);
126 if (r == 0 || IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
127 if (r < 0)
128 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
129
130 r = dnstls_flush_write_buffer(stream);
131 if (r < 0)
132 return r;
133
134 return -EAGAIN;
135 } else {
136 char errbuf[256];
137
138 ERR_error_string_n(error, errbuf, sizeof(errbuf));
139 log_debug("Failed to invoke SSL_shutdown: %s", errbuf);
140 }
141 }
142
143 r = dnstls_flush_write_buffer(stream);
144 if (r < 0)
145 return r;
146
147 stream->dnstls_events = 0;
148 stream->dnstls_data.shutdown = false;
149 dns_stream_unref(stream);
150 return DNSTLS_STREAM_CLOSED;
151 } else if (stream->dnstls_data.handshake <= 0) {
152 stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl);
153 if (stream->dnstls_data.handshake <= 0) {
154 error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake);
155 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
156 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
157 r = dnstls_flush_write_buffer(stream);
158 if (r < 0)
159 return r;
160
161 return -EAGAIN;
162 } else {
163 char errbuf[256];
164
165 ERR_error_string_n(error, errbuf, sizeof(errbuf));
166 log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
167 return -ECONNREFUSED;
168 }
169 }
170
171 stream->dnstls_events = 0;
172 r = dnstls_flush_write_buffer(stream);
173 if (r < 0)
174 return r;
175 }
176
177 return 0;
178 }
179
180 int dnstls_stream_shutdown(DnsStream *stream, int error) {
181 int r;
182 int ssl_error;
183 SSL_SESSION *s;
184
185 assert(stream);
186 assert(stream->encrypted);
187 assert(stream->dnstls_data.ssl);
188
189 if (stream->server) {
190 s = SSL_get1_session(stream->dnstls_data.ssl);
191 if (s) {
192 if (stream->server->dnstls_data.session)
193 SSL_SESSION_free(stream->server->dnstls_data.session);
194
195 stream->server->dnstls_data.session = s;
196 }
197 }
198
199 if (error == ETIMEDOUT) {
200 r = SSL_shutdown(stream->dnstls_data.ssl);
201 if (r == 0) {
202 if (!stream->dnstls_data.shutdown) {
203 stream->dnstls_data.shutdown = true;
204 dns_stream_ref(stream);
205 }
206
207 r = dnstls_flush_write_buffer(stream);
208 if (r < 0)
209 return r;
210
211 return -EAGAIN;
212 } else if (r < 0) {
213 ssl_error = SSL_get_error(stream->dnstls_data.ssl, r);
214 if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
215 stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
216 r = dnstls_flush_write_buffer(stream);
217 if (r < 0 && r != -EAGAIN)
218 return r;
219
220 if (!stream->dnstls_data.shutdown) {
221 stream->dnstls_data.shutdown = true;
222 dns_stream_ref(stream);
223 }
224 return -EAGAIN;
225 } else {
226 char errbuf[256];
227
228 ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf));
229 log_debug("Failed to invoke SSL_shutdown: %s", errbuf);
230 }
231 }
232
233 stream->dnstls_events = 0;
234 r = dnstls_flush_write_buffer(stream);
235 if (r < 0)
236 return r;
237 }
238
239 return 0;
240 }
241
242 ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
243 int r;
244 int error;
245 ssize_t ss;
246
247 assert(stream);
248 assert(stream->encrypted);
249 assert(stream->dnstls_data.ssl);
250 assert(buf);
251
252 ss = r = SSL_write(stream->dnstls_data.ssl, buf, count);
253 if (r <= 0) {
254 error = SSL_get_error(stream->dnstls_data.ssl, ss);
255 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
256 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
257 r = dnstls_flush_write_buffer(stream);
258 if (r < 0)
259 return r;
260
261 ss = -EAGAIN;
262 } else {
263 char errbuf[256];
264
265 ERR_error_string_n(error, errbuf, sizeof(errbuf));
266 log_debug("Failed to invoke SSL_read: %s", errbuf);
267 ss = -EPIPE;
268 }
269 }
270
271 stream->dnstls_events = 0;
272 r = dnstls_flush_write_buffer(stream);
273 if (r < 0)
274 return r;
275
276 return ss;
277 }
278
279 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
280 int r;
281 int error;
282 ssize_t ss;
283
284 assert(stream);
285 assert(stream->encrypted);
286 assert(stream->dnstls_data.ssl);
287 assert(buf);
288
289 ss = r = SSL_read(stream->dnstls_data.ssl, buf, count);
290 if (r <= 0) {
291 error = SSL_get_error(stream->dnstls_data.ssl, ss);
292 if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) {
293 stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT;
294
295 /* flush write buffer in cache of renegotiation */
296 r = dnstls_flush_write_buffer(stream);
297 if (r < 0)
298 return r;
299
300 ss = -EAGAIN;
301 } else {
302 char errbuf[256];
303
304 ERR_error_string_n(error, errbuf, sizeof(errbuf));
305 log_debug("Failed to invoke SSL_read: %s", errbuf);
306 ss = -EPIPE;
307 }
308 }
309
310 stream->dnstls_events = 0;
311
312 /* flush write buffer in cache of renegotiation */
313 r = dnstls_flush_write_buffer(stream);
314 if (r < 0)
315 return r;
316
317 return ss;
318 }
319
320 void dnstls_server_init(DnsServer *server) {
321 assert(server);
322
323 server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
324 if (server->dnstls_data.ctx) {
325 SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
326 SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
327 }
328 }
329
330 void dnstls_server_free(DnsServer *server) {
331 assert(server);
332
333 if (server->dnstls_data.ctx)
334 SSL_CTX_free(server->dnstls_data.ctx);
335
336 if (server->dnstls_data.session)
337 SSL_SESSION_free(server->dnstls_data.session);
338 }