]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dnstls-openssl.c
resolved: basic OpenSSL support for DNS-over-TLS
[thirdparty/systemd.git] / src / resolve / resolved-dnstls-openssl.c
CommitLineData
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
13DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
14DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
15
16int 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
46void 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
54int 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
105int 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
143ssize_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
172ssize_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
201void 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
211void dnstls_server_free(DnsServer *server) {
212 assert(server);
213
214 if (server->dnstls_data.ctx)
215 SSL_CTX_free(server->dnstls_data.ctx);
216}