]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
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 <openssl/x509v3.h> | |
10 | ||
11 | #include "io-util.h" | |
12 | #include "openssl-util.h" | |
13 | #include "resolved-dns-stream.h" | |
14 | #include "resolved-dnstls.h" | |
15 | #include "resolved-manager.h" | |
16 | ||
17 | static char *dnstls_error_string(int ssl_error, char *buf, size_t count) { | |
18 | assert(buf || count == 0); | |
19 | if (ssl_error == SSL_ERROR_SSL) | |
20 | ERR_error_string_n(ERR_get_error(), buf, count); | |
21 | else | |
22 | snprintf(buf, count, "SSL_get_error()=%d", ssl_error); | |
23 | return buf; | |
24 | } | |
25 | ||
26 | #define DNSTLS_ERROR_BUFSIZE 256 | |
27 | #define DNSTLS_ERROR_STRING(error) \ | |
28 | dnstls_error_string((error), (char[DNSTLS_ERROR_BUFSIZE]){}, DNSTLS_ERROR_BUFSIZE) | |
29 | ||
30 | static int dnstls_flush_write_buffer(DnsStream *stream) { | |
31 | ssize_t ss; | |
32 | ||
33 | assert(stream); | |
34 | assert(stream->encrypted); | |
35 | ||
36 | if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) { | |
37 | assert(stream->dnstls_data.write_buffer->data); | |
38 | ||
39 | struct iovec iov[1]; | |
40 | iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data + stream->dnstls_data.buffer_offset, | |
41 | stream->dnstls_data.write_buffer->length - stream->dnstls_data.buffer_offset); | |
42 | ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA); | |
43 | if (ss < 0) { | |
44 | if (ss == -EAGAIN) | |
45 | stream->dnstls_events |= EPOLLOUT; | |
46 | ||
47 | return ss; | |
48 | } else { | |
49 | stream->dnstls_data.buffer_offset += ss; | |
50 | ||
51 | if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) { | |
52 | stream->dnstls_events |= EPOLLOUT; | |
53 | return -EAGAIN; | |
54 | } else { | |
55 | BIO_reset(SSL_get_wbio(stream->dnstls_data.ssl)); | |
56 | stream->dnstls_data.buffer_offset = 0; | |
57 | } | |
58 | } | |
59 | } | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) { | |
65 | _cleanup_(BIO_freep) BIO *rb = NULL, *wb = NULL; | |
66 | _cleanup_(SSL_freep) SSL *s = NULL; | |
67 | int error, r; | |
68 | ||
69 | assert(stream); | |
70 | assert(stream->manager); | |
71 | assert(server); | |
72 | ||
73 | rb = BIO_new_socket(stream->fd, 0); | |
74 | if (!rb) | |
75 | return -ENOMEM; | |
76 | ||
77 | wb = BIO_new(BIO_s_mem()); | |
78 | if (!wb) | |
79 | return -ENOMEM; | |
80 | ||
81 | BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer); | |
82 | stream->dnstls_data.buffer_offset = 0; | |
83 | ||
84 | s = SSL_new(stream->manager->dnstls_data.ctx); | |
85 | if (!s) | |
86 | return -ENOMEM; | |
87 | ||
88 | SSL_set_connect_state(s); | |
89 | r = SSL_set_session(s, server->dnstls_data.session); | |
90 | if (r == 0) | |
91 | return -EIO; | |
92 | SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb)); | |
93 | ||
94 | if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) { | |
95 | X509_VERIFY_PARAM *v; | |
96 | ||
97 | SSL_set_verify(s, SSL_VERIFY_PEER, NULL); | |
98 | v = SSL_get0_param(s); | |
99 | if (server->server_name) { | |
100 | X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); | |
101 | if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0) | |
102 | return -ECONNREFUSED; | |
103 | } else { | |
104 | const unsigned char *ip; | |
105 | ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr; | |
106 | if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0) | |
107 | return -ECONNREFUSED; | |
108 | } | |
109 | } | |
110 | ||
111 | if (server->server_name) { | |
112 | r = SSL_set_tlsext_host_name(s, server->server_name); | |
113 | if (r <= 0) | |
114 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
115 | "Failed to set server name: %s", DNSTLS_ERROR_STRING(SSL_ERROR_SSL)); | |
116 | } | |
117 | ||
118 | ERR_clear_error(); | |
119 | stream->dnstls_data.handshake = SSL_do_handshake(s); | |
120 | if (stream->dnstls_data.handshake <= 0) { | |
121 | error = SSL_get_error(s, stream->dnstls_data.handshake); | |
122 | if (!IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) | |
123 | return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED), | |
124 | "Failed to invoke SSL_do_handshake: %s", DNSTLS_ERROR_STRING(error)); | |
125 | } | |
126 | ||
127 | stream->encrypted = true; | |
128 | stream->dnstls_data.ssl = TAKE_PTR(s); | |
129 | ||
130 | r = dnstls_flush_write_buffer(stream); | |
131 | if (r < 0 && r != -EAGAIN) { | |
132 | SSL_free(TAKE_PTR(stream->dnstls_data.ssl)); | |
133 | return r; | |
134 | } | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | void dnstls_stream_free(DnsStream *stream) { | |
140 | assert(stream); | |
141 | assert(stream->encrypted); | |
142 | ||
143 | if (stream->dnstls_data.ssl) | |
144 | SSL_free(stream->dnstls_data.ssl); | |
145 | } | |
146 | ||
147 | int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) { | |
148 | int error, r; | |
149 | ||
150 | assert(stream); | |
151 | assert(stream->encrypted); | |
152 | assert(stream->dnstls_data.ssl); | |
153 | ||
154 | /* Flush write buffer when requested by OpenSSL */ | |
155 | if ((revents & EPOLLOUT) && (stream->dnstls_events & EPOLLOUT)) { | |
156 | r = dnstls_flush_write_buffer(stream); | |
157 | if (r < 0) | |
158 | return r; | |
159 | } | |
160 | ||
161 | if (stream->dnstls_data.shutdown) { | |
162 | ERR_clear_error(); | |
163 | r = SSL_shutdown(stream->dnstls_data.ssl); | |
164 | if (r == 0) { | |
165 | stream->dnstls_events = 0; | |
166 | ||
167 | r = dnstls_flush_write_buffer(stream); | |
168 | if (r < 0) | |
169 | return r; | |
170 | ||
171 | return -EAGAIN; | |
172 | } else if (r < 0) { | |
173 | error = SSL_get_error(stream->dnstls_data.ssl, r); | |
174 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
175 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
176 | ||
177 | r = dnstls_flush_write_buffer(stream); | |
178 | if (r < 0) | |
179 | return r; | |
180 | ||
181 | return -EAGAIN; | |
182 | } else if (error == SSL_ERROR_SYSCALL) { | |
183 | if (errno > 0) | |
184 | log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m"); | |
185 | } else | |
186 | log_debug("Failed to invoke SSL_shutdown, ignoring: %s", DNSTLS_ERROR_STRING(error)); | |
187 | } | |
188 | ||
189 | stream->dnstls_events = 0; | |
190 | stream->dnstls_data.shutdown = false; | |
191 | ||
192 | r = dnstls_flush_write_buffer(stream); | |
193 | if (r < 0) | |
194 | return r; | |
195 | ||
196 | dns_stream_unref(stream); | |
197 | return DNSTLS_STREAM_CLOSED; | |
198 | } else if (stream->dnstls_data.handshake <= 0) { | |
199 | ERR_clear_error(); | |
200 | stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl); | |
201 | if (stream->dnstls_data.handshake <= 0) { | |
202 | error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake); | |
203 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
204 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
205 | r = dnstls_flush_write_buffer(stream); | |
206 | if (r < 0) | |
207 | return r; | |
208 | ||
209 | return -EAGAIN; | |
210 | } else | |
211 | return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED), | |
212 | "Failed to invoke SSL_do_handshake: %s", | |
213 | DNSTLS_ERROR_STRING(error)); | |
214 | } | |
215 | ||
216 | stream->dnstls_events = 0; | |
217 | r = dnstls_flush_write_buffer(stream); | |
218 | if (r < 0) | |
219 | return r; | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
225 | int dnstls_stream_shutdown(DnsStream *stream, int error) { | |
226 | int ssl_error, r; | |
227 | SSL_SESSION *s; | |
228 | ||
229 | assert(stream); | |
230 | assert(stream->encrypted); | |
231 | assert(stream->dnstls_data.ssl); | |
232 | ||
233 | if (stream->server) { | |
234 | s = SSL_get1_session(stream->dnstls_data.ssl); | |
235 | if (s) { | |
236 | if (stream->server->dnstls_data.session) | |
237 | SSL_SESSION_free(stream->server->dnstls_data.session); | |
238 | ||
239 | stream->server->dnstls_data.session = s; | |
240 | } | |
241 | } | |
242 | ||
243 | if (error == ETIMEDOUT) { | |
244 | ERR_clear_error(); | |
245 | r = SSL_shutdown(stream->dnstls_data.ssl); | |
246 | if (r == 0) { | |
247 | if (!stream->dnstls_data.shutdown) { | |
248 | stream->dnstls_data.shutdown = true; | |
249 | dns_stream_ref(stream); | |
250 | } | |
251 | ||
252 | stream->dnstls_events = 0; | |
253 | ||
254 | r = dnstls_flush_write_buffer(stream); | |
255 | if (r < 0) | |
256 | return r; | |
257 | ||
258 | return -EAGAIN; | |
259 | } else if (r < 0) { | |
260 | ssl_error = SSL_get_error(stream->dnstls_data.ssl, r); | |
261 | if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
262 | stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
263 | r = dnstls_flush_write_buffer(stream); | |
264 | if (r < 0 && r != -EAGAIN) | |
265 | return r; | |
266 | ||
267 | if (!stream->dnstls_data.shutdown) { | |
268 | stream->dnstls_data.shutdown = true; | |
269 | dns_stream_ref(stream); | |
270 | } | |
271 | return -EAGAIN; | |
272 | } else if (ssl_error == SSL_ERROR_SYSCALL) { | |
273 | if (errno > 0) | |
274 | log_debug_errno(errno, "Failed to invoke SSL_shutdown, ignoring: %m"); | |
275 | } else | |
276 | log_debug("Failed to invoke SSL_shutdown, ignoring: %s", DNSTLS_ERROR_STRING(ssl_error)); | |
277 | } | |
278 | ||
279 | stream->dnstls_events = 0; | |
280 | r = dnstls_flush_write_buffer(stream); | |
281 | if (r < 0) | |
282 | return r; | |
283 | } | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
288 | static ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) { | |
289 | int error, r; | |
290 | ssize_t ss; | |
291 | ||
292 | ERR_clear_error(); | |
293 | ss = r = SSL_write(stream->dnstls_data.ssl, buf, count); | |
294 | if (r <= 0) { | |
295 | error = SSL_get_error(stream->dnstls_data.ssl, r); | |
296 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
297 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
298 | ss = -EAGAIN; | |
299 | } else if (error == SSL_ERROR_ZERO_RETURN) { | |
300 | stream->dnstls_events = 0; | |
301 | ss = 0; | |
302 | } else { | |
303 | log_debug("Failed to invoke SSL_write: %s", DNSTLS_ERROR_STRING(error)); | |
304 | stream->dnstls_events = 0; | |
305 | ss = -EPIPE; | |
306 | } | |
307 | } else | |
308 | stream->dnstls_events = 0; | |
309 | ||
310 | r = dnstls_flush_write_buffer(stream); | |
311 | if (r < 0) | |
312 | return r; | |
313 | ||
314 | return ss; | |
315 | } | |
316 | ||
317 | ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt) { | |
318 | _cleanup_free_ char *buf = NULL; | |
319 | size_t count; | |
320 | ||
321 | assert(stream); | |
322 | assert(stream->encrypted); | |
323 | assert(stream->dnstls_data.ssl); | |
324 | assert(iov); | |
325 | assert(iovec_total_size(iov, iovcnt) > 0); | |
326 | ||
327 | if (iovcnt == 1) | |
328 | return dnstls_stream_write(stream, iov[0].iov_base, iov[0].iov_len); | |
329 | ||
330 | /* As of now, OpenSSL cannot accumulate multiple writes, so join into a | |
331 | single buffer. Suboptimal, but better than multiple SSL_write calls. */ | |
332 | count = iovec_total_size(iov, iovcnt); | |
333 | buf = new(char, count); | |
334 | for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++) | |
335 | memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len); | |
336 | ||
337 | return dnstls_stream_write(stream, buf, count); | |
338 | } | |
339 | ||
340 | ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) { | |
341 | int error, r; | |
342 | ssize_t ss; | |
343 | ||
344 | assert(stream); | |
345 | assert(stream->encrypted); | |
346 | assert(stream->dnstls_data.ssl); | |
347 | assert(buf); | |
348 | ||
349 | ERR_clear_error(); | |
350 | ss = r = SSL_read(stream->dnstls_data.ssl, buf, count); | |
351 | if (r <= 0) { | |
352 | error = SSL_get_error(stream->dnstls_data.ssl, r); | |
353 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
354 | /* If we receive SSL_ERROR_WANT_READ here, there are two possible scenarios: | |
355 | * OpenSSL needs to renegotiate (so we want to get an EPOLLIN event), or | |
356 | * There is no more application data is available, so we can just return | |
357 | And apparently there's no nice way to distinguish between the two. | |
358 | To handle this, never set EPOLLIN and just continue as usual. | |
359 | If OpenSSL really wants to read due to renegotiation, it will tell us | |
360 | again on SSL_write (at which point we will request EPOLLIN force a read); | |
361 | or we will just eventually read data anyway while we wait for a packet */ | |
362 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? 0 : EPOLLOUT; | |
363 | ss = -EAGAIN; | |
364 | } else if (error == SSL_ERROR_ZERO_RETURN) { | |
365 | stream->dnstls_events = 0; | |
366 | ss = 0; | |
367 | } else { | |
368 | log_debug("Failed to invoke SSL_read: %s", DNSTLS_ERROR_STRING(error)); | |
369 | stream->dnstls_events = 0; | |
370 | ss = -EPIPE; | |
371 | } | |
372 | } else | |
373 | stream->dnstls_events = 0; | |
374 | ||
375 | /* flush write buffer in cache of renegotiation */ | |
376 | r = dnstls_flush_write_buffer(stream); | |
377 | if (r < 0) | |
378 | return r; | |
379 | ||
380 | return ss; | |
381 | } | |
382 | ||
383 | void dnstls_server_free(DnsServer *server) { | |
384 | assert(server); | |
385 | ||
386 | if (server->dnstls_data.session) | |
387 | SSL_SESSION_free(server->dnstls_data.session); | |
388 | } | |
389 | ||
390 | int dnstls_manager_init(Manager *manager) { | |
391 | int r; | |
392 | ||
393 | assert(manager); | |
394 | ||
395 | manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method()); | |
396 | if (!manager->dnstls_data.ctx) | |
397 | return -ENOMEM; | |
398 | ||
399 | r = SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION); | |
400 | if (r == 0) | |
401 | return -EIO; | |
402 | ||
403 | (void) SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION); | |
404 | ||
405 | r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx); | |
406 | if (r == 0) | |
407 | return log_warning_errno(SYNTHETIC_ERRNO(EIO), | |
408 | "Failed to load system trust store: %s", | |
409 | ERR_error_string(ERR_get_error(), NULL)); | |
410 | ||
411 | return 0; | |
412 | } | |
413 | ||
414 | void dnstls_manager_free(Manager *manager) { | |
415 | assert(manager); | |
416 | ||
417 | if (manager->dnstls_data.ctx) | |
418 | SSL_CTX_free(manager->dnstls_data.ctx); | |
419 | } |