Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #if !ENABLE_DNS_OVER_TLS || !DNS_OVER_TLS_USE_GNUTLS
4 : : #error This source file requires DNS-over-TLS to be enabled and GnuTLS to be available.
5 : : #endif
6 : :
7 : : #include <gnutls/socket.h>
8 : :
9 : : #include "resolved-dns-stream.h"
10 : : #include "resolved-dnstls.h"
11 : :
12 : : #if GNUTLS_VERSION_NUMBER >= 0x030600
13 : : #define PRIORTY_STRING "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3"
14 : : #else
15 : : #define PRIORTY_STRING "NORMAL:-VERS-ALL:+VERS-TLS1.2"
16 : : #endif
17 [ # # ]: 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t, gnutls_deinit);
18 : :
19 : 0 : static ssize_t dnstls_stream_writev(gnutls_transport_ptr_t p, const giovec_t *iov, int iovcnt) {
20 : : int r;
21 : :
22 [ # # ]: 0 : assert(p);
23 : :
24 : 0 : r = dns_stream_writev((DnsStream*) p, (const struct iovec*) iov, iovcnt, DNS_STREAM_WRITE_TLS_DATA);
25 [ # # ]: 0 : if (r < 0) {
26 : 0 : errno = -r;
27 : 0 : return -1;
28 : : }
29 : :
30 : 0 : return r;
31 : : }
32 : :
33 : 0 : int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
34 : 0 : _cleanup_(gnutls_deinitp) gnutls_session_t gs;
35 : : int r;
36 : :
37 [ # # ]: 0 : assert(stream);
38 [ # # ]: 0 : assert(server);
39 : :
40 : 0 : r = gnutls_init(&gs, GNUTLS_CLIENT | GNUTLS_ENABLE_FALSE_START | GNUTLS_NONBLOCK);
41 [ # # ]: 0 : if (r < 0)
42 : 0 : return r;
43 : :
44 : : /* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
45 : 0 : r = gnutls_priority_set_direct(gs, PRIORTY_STRING, NULL);
46 [ # # ]: 0 : if (r < 0)
47 : 0 : return r;
48 : :
49 : 0 : r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, stream->manager->dnstls_data.cert_cred);
50 [ # # ]: 0 : if (r < 0)
51 : 0 : return r;
52 : :
53 [ # # ]: 0 : if (server->dnstls_data.session_data.size > 0) {
54 : 0 : gnutls_session_set_data(gs, server->dnstls_data.session_data.data, server->dnstls_data.session_data.size);
55 : :
56 : : // Clear old session ticket
57 : 0 : gnutls_free(server->dnstls_data.session_data.data);
58 : 0 : server->dnstls_data.session_data.data = NULL;
59 : 0 : server->dnstls_data.session_data.size = 0;
60 : : }
61 : :
62 [ # # ]: 0 : if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES)
63 : 0 : gnutls_session_set_verify_cert(gs, NULL, 0);
64 : :
65 : 0 : gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
66 : :
67 : 0 : gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
68 : 0 : gnutls_transport_set_vec_push_function(gs, &dnstls_stream_writev);
69 : :
70 : 0 : stream->encrypted = true;
71 : 0 : stream->dnstls_data.handshake = gnutls_handshake(gs);
72 [ # # # # ]: 0 : if (stream->dnstls_data.handshake < 0 && gnutls_error_is_fatal(stream->dnstls_data.handshake))
73 : 0 : return -ECONNREFUSED;
74 : :
75 : 0 : stream->dnstls_data.session = TAKE_PTR(gs);
76 : :
77 : 0 : return 0;
78 : : }
79 : :
80 : 0 : void dnstls_stream_free(DnsStream *stream) {
81 [ # # ]: 0 : assert(stream);
82 [ # # ]: 0 : assert(stream->encrypted);
83 : :
84 [ # # ]: 0 : if (stream->dnstls_data.session)
85 : 0 : gnutls_deinit(stream->dnstls_data.session);
86 : 0 : }
87 : :
88 : 0 : int dnstls_stream_on_io(DnsStream *stream, uint32_t revents) {
89 : : int r;
90 : :
91 [ # # ]: 0 : assert(stream);
92 [ # # ]: 0 : assert(stream->encrypted);
93 [ # # ]: 0 : assert(stream->dnstls_data.session);
94 : :
95 [ # # ]: 0 : if (stream->dnstls_data.shutdown) {
96 : 0 : r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
97 [ # # ]: 0 : if (r == GNUTLS_E_AGAIN) {
98 [ # # ]: 0 : stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
99 : 0 : return -EAGAIN;
100 [ # # ]: 0 : } else if (r < 0)
101 [ # # ]: 0 : log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
102 : :
103 : 0 : stream->dnstls_events = 0;
104 : 0 : stream->dnstls_data.shutdown = false;
105 : 0 : dns_stream_unref(stream);
106 : 0 : return DNSTLS_STREAM_CLOSED;
107 [ # # ]: 0 : } else if (stream->dnstls_data.handshake < 0) {
108 : 0 : stream->dnstls_data.handshake = gnutls_handshake(stream->dnstls_data.session);
109 [ # # ]: 0 : if (stream->dnstls_data.handshake == GNUTLS_E_AGAIN) {
110 [ # # ]: 0 : stream->dnstls_events = gnutls_record_get_direction(stream->dnstls_data.session) == 1 ? EPOLLOUT : EPOLLIN;
111 : 0 : return -EAGAIN;
112 [ # # ]: 0 : } else if (stream->dnstls_data.handshake < 0) {
113 [ # # ]: 0 : log_debug("Failed to invoke gnutls_handshake: %s", gnutls_strerror(stream->dnstls_data.handshake));
114 [ # # ]: 0 : if (gnutls_error_is_fatal(stream->dnstls_data.handshake))
115 : 0 : return -ECONNREFUSED;
116 : : }
117 : :
118 : 0 : stream->dnstls_events = 0;
119 : : }
120 : :
121 : 0 : return 0;
122 : : }
123 : :
124 : 0 : int dnstls_stream_shutdown(DnsStream *stream, int error) {
125 : : int r;
126 : :
127 [ # # ]: 0 : assert(stream);
128 [ # # ]: 0 : assert(stream->encrypted);
129 [ # # ]: 0 : assert(stream->dnstls_data.session);
130 : :
131 : : /* Store TLS Ticket for faster successive TLS handshakes */
132 [ # # # # : 0 : if (stream->server && stream->server->dnstls_data.session_data.size == 0 && stream->dnstls_data.handshake == GNUTLS_E_SUCCESS)
# # ]
133 : 0 : gnutls_session_get_data2(stream->dnstls_data.session, &stream->server->dnstls_data.session_data);
134 : :
135 [ # # # # ]: 0 : if (IN_SET(error, ETIMEDOUT, 0)) {
136 : 0 : r = gnutls_bye(stream->dnstls_data.session, GNUTLS_SHUT_RDWR);
137 [ # # ]: 0 : if (r == GNUTLS_E_AGAIN) {
138 [ # # ]: 0 : if (!stream->dnstls_data.shutdown) {
139 : 0 : stream->dnstls_data.shutdown = true;
140 : 0 : dns_stream_ref(stream);
141 : 0 : return -EAGAIN;
142 : : }
143 [ # # ]: 0 : } else if (r < 0)
144 [ # # ]: 0 : log_debug("Failed to invoke gnutls_bye: %s", gnutls_strerror(r));
145 : : }
146 : :
147 : 0 : return 0;
148 : : }
149 : :
150 : 0 : ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) {
151 : : ssize_t ss;
152 : :
153 [ # # ]: 0 : assert(stream);
154 [ # # ]: 0 : assert(stream->encrypted);
155 [ # # ]: 0 : assert(stream->dnstls_data.session);
156 [ # # ]: 0 : assert(buf);
157 : :
158 : 0 : ss = gnutls_record_send(stream->dnstls_data.session, buf, count);
159 [ # # ]: 0 : if (ss < 0)
160 [ # # # ]: 0 : switch(ss) {
161 : 0 : case GNUTLS_E_INTERRUPTED:
162 : 0 : return -EINTR;
163 : 0 : case GNUTLS_E_AGAIN:
164 : 0 : return -EAGAIN;
165 : 0 : default:
166 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EPIPE),
167 : : "Failed to invoke gnutls_record_send: %s",
168 : : gnutls_strerror(ss));
169 : : }
170 : :
171 : 0 : return ss;
172 : : }
173 : :
174 : 0 : ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
175 : : ssize_t ss;
176 : :
177 [ # # ]: 0 : assert(stream);
178 [ # # ]: 0 : assert(stream->encrypted);
179 [ # # ]: 0 : assert(stream->dnstls_data.session);
180 [ # # ]: 0 : assert(buf);
181 : :
182 : 0 : ss = gnutls_record_recv(stream->dnstls_data.session, buf, count);
183 [ # # ]: 0 : if (ss < 0)
184 [ # # # ]: 0 : switch(ss) {
185 : 0 : case GNUTLS_E_INTERRUPTED:
186 : 0 : return -EINTR;
187 : 0 : case GNUTLS_E_AGAIN:
188 : 0 : return -EAGAIN;
189 : 0 : default:
190 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EPIPE),
191 : : "Failed to invoke gnutls_record_recv: %s",
192 : : gnutls_strerror(ss));
193 : : }
194 : :
195 : 0 : return ss;
196 : : }
197 : :
198 : 0 : void dnstls_server_free(DnsServer *server) {
199 [ # # ]: 0 : assert(server);
200 : :
201 [ # # ]: 0 : if (server->dnstls_data.session_data.data)
202 : 0 : gnutls_free(server->dnstls_data.session_data.data);
203 : 0 : }
204 : :
205 : 0 : int dnstls_manager_init(Manager *manager) {
206 : : int r;
207 [ # # ]: 0 : assert(manager);
208 : :
209 : 0 : r = gnutls_certificate_allocate_credentials(&manager->dnstls_data.cert_cred);
210 [ # # ]: 0 : if (r < 0)
211 : 0 : return -ENOMEM;
212 : :
213 : 0 : r = gnutls_certificate_set_x509_system_trust(manager->dnstls_data.cert_cred);
214 [ # # ]: 0 : if (r < 0)
215 [ # # ]: 0 : log_warning("Failed to load system trust store: %s", gnutls_strerror(r));
216 : :
217 : 0 : return 0;
218 : : }
219 : :
220 : 0 : void dnstls_manager_free(Manager *manager) {
221 [ # # ]: 0 : assert(manager);
222 : :
223 [ # # ]: 0 : if (manager->dnstls_data.cert_cred)
224 : 0 : gnutls_certificate_free_credentials(manager->dnstls_data.cert_cred);
225 : 0 : }
|