]>
Commit | Line | Data |
---|---|---|
096cbdce IT |
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 | int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) { | |
17 | _cleanup_(SSL_freep) SSL *s = NULL; | |
18 | _cleanup_(BIO_freep) BIO *b = NULL; | |
19 | ||
20 | assert(stream); | |
21 | assert(server); | |
22 | ||
23 | b = BIO_new_socket(stream->fd, 0); | |
24 | if (!b) | |
25 | return -ENOMEM; | |
26 | ||
27 | s = SSL_new(server->dnstls_data.ctx); | |
28 | if (!s) | |
29 | return -ENOMEM; | |
30 | ||
31 | SSL_set_connect_state(s); | |
32 | SSL_set_bio(s, b, b); | |
33 | b = NULL; | |
34 | ||
35 | /* DNS-over-TLS using OpenSSL doesn't support TCP Fast Open yet */ | |
36 | connect(stream->fd, &stream->tfo_address.sa, stream->tfo_salen); | |
37 | stream->tfo_salen = 0; | |
38 | ||
39 | stream->encrypted = true; | |
40 | stream->dnstls_events = EPOLLOUT; | |
41 | stream->dnstls_data.ssl = TAKE_PTR(s); | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | void dnstls_stream_free(DnsStream *stream) { | |
47 | assert(stream); | |
48 | assert(stream->encrypted); | |
49 | ||
50 | if (stream->dnstls_data.ssl) | |
51 | SSL_free(stream->dnstls_data.ssl); | |
52 | } | |
53 | ||
54 | int dnstls_stream_on_io(DnsStream *stream) { | |
55 | int r; | |
56 | int error; | |
57 | ||
58 | assert(stream); | |
59 | assert(stream->encrypted); | |
60 | assert(stream->dnstls_data.ssl); | |
61 | ||
62 | if (stream->dnstls_data.shutdown) { | |
63 | r = SSL_shutdown(stream->dnstls_data.ssl); | |
64 | if (r == 0) | |
65 | return -EAGAIN; | |
66 | else if (r < 0) { | |
67 | error = SSL_get_error(stream->dnstls_data.ssl, r); | |
68 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
69 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
70 | return -EAGAIN; | |
71 | } else { | |
72 | char errbuf[256]; | |
73 | ||
74 | ERR_error_string_n(error, errbuf, sizeof(errbuf)); | |
75 | log_debug("Failed to invoke SSL_shutdown: %s", errbuf); | |
76 | } | |
77 | } | |
78 | ||
79 | stream->dnstls_events = 0; | |
80 | stream->dnstls_data.shutdown = false; | |
81 | dns_stream_unref(stream); | |
82 | return DNSTLS_STREAM_CLOSED; | |
83 | } else if (stream->dnstls_data.handshake <= 0) { | |
84 | stream->dnstls_data.handshake = SSL_do_handshake(stream->dnstls_data.ssl); | |
85 | if (stream->dnstls_data.handshake <= 0) { | |
86 | error = SSL_get_error(stream->dnstls_data.ssl, stream->dnstls_data.handshake); | |
87 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
88 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
89 | return -EAGAIN; | |
90 | } else { | |
91 | char errbuf[256]; | |
92 | ||
93 | ERR_error_string_n(error, errbuf, sizeof(errbuf)); | |
94 | log_debug("Failed to invoke SSL_do_handshake: %s", errbuf); | |
95 | return -ECONNREFUSED; | |
96 | } | |
97 | } | |
98 | ||
99 | stream->dnstls_events = 0; | |
100 | } | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | int dnstls_stream_shutdown(DnsStream *stream, int error) { | |
106 | int r; | |
107 | int ssl_error; | |
108 | SSL_SESSION *s; | |
109 | ||
110 | assert(stream); | |
111 | assert(stream->encrypted); | |
112 | assert(stream->dnstls_data.ssl); | |
113 | ||
114 | if (error == ETIMEDOUT) { | |
115 | r = SSL_shutdown(stream->dnstls_data.ssl); | |
116 | if (r == 0) { | |
117 | if (!stream->dnstls_data.shutdown) { | |
118 | stream->dnstls_data.shutdown = true; | |
119 | dns_stream_ref(stream); | |
120 | } | |
121 | return -EAGAIN; | |
122 | } else if (r < 0) { | |
123 | ssl_error = SSL_get_error(stream->dnstls_data.ssl, r); | |
124 | if (IN_SET(ssl_error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
125 | stream->dnstls_events = ssl_error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
126 | if (!stream->dnstls_data.shutdown) { | |
127 | stream->dnstls_data.shutdown = true; | |
128 | dns_stream_ref(stream); | |
129 | } | |
130 | return -EAGAIN; | |
131 | } else { | |
132 | char errbuf[256]; | |
133 | ||
134 | ERR_error_string_n(ssl_error, errbuf, sizeof(errbuf)); | |
135 | log_debug("Failed to invoke SSL_shutdown: %s", errbuf); | |
136 | } | |
137 | } | |
138 | } | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) { | |
144 | int r; | |
145 | int error; | |
146 | ssize_t ss; | |
147 | ||
148 | assert(stream); | |
149 | assert(stream->encrypted); | |
150 | assert(stream->dnstls_data.ssl); | |
151 | assert(buf); | |
152 | ||
153 | ss = r = SSL_write(stream->dnstls_data.ssl, buf, count); | |
154 | if (r <= 0) { | |
155 | error = SSL_get_error(stream->dnstls_data.ssl, ss); | |
156 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
157 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
158 | ss = -EAGAIN; | |
159 | } else { | |
160 | char errbuf[256]; | |
161 | ||
162 | ERR_error_string_n(error, errbuf, sizeof(errbuf)); | |
163 | log_debug("Failed to invoke SSL_read: %s", errbuf); | |
164 | ss = -EPIPE; | |
165 | } | |
166 | } | |
167 | ||
168 | stream->dnstls_events = 0; | |
169 | return ss; | |
170 | } | |
171 | ||
172 | ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) { | |
173 | int r; | |
174 | int error; | |
175 | ssize_t ss; | |
176 | ||
177 | assert(stream); | |
178 | assert(stream->encrypted); | |
179 | assert(stream->dnstls_data.ssl); | |
180 | assert(buf); | |
181 | ||
182 | ss = r = SSL_read(stream->dnstls_data.ssl, buf, count); | |
183 | if (r <= 0) { | |
184 | error = SSL_get_error(stream->dnstls_data.ssl, ss); | |
185 | if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { | |
186 | stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; | |
187 | ss = -EAGAIN; | |
188 | } else { | |
189 | char errbuf[256]; | |
190 | ||
191 | ERR_error_string_n(error, errbuf, sizeof(errbuf)); | |
192 | log_debug("Failed to invoke SSL_read: %s", errbuf); | |
193 | ss = -EPIPE; | |
194 | } | |
195 | } | |
196 | ||
197 | stream->dnstls_events = 0; | |
198 | return ss; | |
199 | } | |
200 | ||
201 | void dnstls_server_init(DnsServer *server) { | |
202 | assert(server); | |
203 | ||
204 | server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method()); | |
205 | if (server->dnstls_data.ctx) { | |
206 | SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION); | |
207 | SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION); | |
208 | } | |
209 | } | |
210 | ||
211 | void dnstls_server_free(DnsServer *server) { | |
212 | assert(server); | |
213 | ||
214 | if (server->dnstls_data.ctx) | |
215 | SSL_CTX_free(server->dnstls_data.ctx); | |
216 | } |