Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-lan-utils"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <gio/gnetworking.h>
10 : : #include <valent.h>
11 : :
12 : : #include "valent-lan-utils.h"
13 : :
14 : :
15 : : /*
16 : : * The KDE Connect protocol follows a trust-on-first-use approach to TLS, so we
17 : : * use a dummy callback for `GTlsConnection`::accept-certificate that always
18 : : * returns %TRUE.
19 : : */
20 : : static gboolean
21 : 8 : valent_lan_accept_certificate_cb (GTlsConnection *connection,
22 : : GTlsCertificate *peer_cert,
23 : : GTlsCertificateFlags errors,
24 : : gpointer user_data)
25 : : {
26 : 8 : return TRUE;
27 : : }
28 : :
29 : : static gboolean
30 : 8 : valent_lan_accept_certificate (GTlsConnection *connection,
31 : : GCancellable *cancellable,
32 : : GError **error)
33 : : {
34 : 8 : unsigned long accept_id;
35 : 8 : gboolean ret;
36 : :
37 : 8 : accept_id = g_signal_connect (G_OBJECT (connection),
38 : : "accept-certificate",
39 : : G_CALLBACK (valent_lan_accept_certificate_cb),
40 : : NULL);
41 : :
42 : 8 : ret = g_tls_connection_handshake (connection, cancellable, error);
43 [ + - ]: 8 : g_clear_signal_handler (&accept_id, connection);
44 : :
45 : 8 : return ret;
46 : : }
47 : :
48 : : /* < private >
49 : : * valent_lan_handshake_certificate:
50 : : * @connection: a `GTlsConnection`
51 : : * @trusted: a `GTlsCertificate`
52 : : * @cancellable: (nullable): a `GCancellable`
53 : : * @error: (nullable): a `GError`
54 : : *
55 : : * Authenticate a connection for a known peer.
56 : : *
57 : : * This function is used to authenticate a TLS connection against a known and
58 : : * trusted TLS certificate. This should be used to authenticate auxiliary
59 : : * connections for authenticated channels.
60 : : *
61 : : * Returns: %TRUE, or %FALSE with @error set
62 : : */
63 : : static gboolean
64 : 2 : valent_lan_handshake_certificate (GTlsConnection *connection,
65 : : GTlsCertificate *trusted,
66 : : GCancellable *cancellable,
67 : : GError **error)
68 : : {
69 : 2 : GTlsCertificate *peer_cert;
70 : :
71 [ - + ]: 2 : if (!valent_lan_accept_certificate (connection, cancellable, error))
72 : : return FALSE;
73 : :
74 : 2 : peer_cert = g_tls_connection_get_peer_certificate (connection);
75 : :
76 [ - + ]: 2 : if (!g_tls_certificate_is_same (trusted, peer_cert))
77 : : {
78 : 0 : g_set_error (error,
79 : : G_TLS_ERROR,
80 : : G_TLS_ERROR_HANDSHAKE,
81 : : "Invalid certificate");
82 : 0 : return FALSE;
83 : : }
84 : :
85 : : return TRUE;
86 : : }
87 : :
88 : : /* < private >
89 : : * valent_lan_handshake_peer:
90 : : * @connection: a `GTlsConnection`
91 : : * @cancellable: (nullable): a `GCancellable`
92 : : * @error: (nullable): a `GError`
93 : : *
94 : : * Authenticate a connection for an unknown peer.
95 : : *
96 : : * This function is used to authenticate a TLS connection whether the remote
97 : : * device is paired or not. This should be used to authenticate new connections
98 : : * when negotiating a [class@Valent.LanChannel].
99 : : *
100 : : * If the TLS certificate is not known (i.e. previously authenticated), the
101 : : * device is assumed to be unpaired and %TRUE will be returned to
102 : : * trust-on-first-use. The certificate will become "known" when if and when the
103 : : * device is successfully paired.
104 : : *
105 : : * Returns: %TRUE, or %FALSE with @error set
106 : : */
107 : : static gboolean
108 : 6 : valent_lan_handshake_peer (GTlsConnection *connection,
109 : : GCancellable *cancellable,
110 : : GError **error)
111 : : {
112 : 12 : g_autoptr (GFile) file = NULL;
113 [ + - ]: 6 : g_autoptr (GTlsCertificate) peer_trusted = NULL;
114 : 6 : GTlsCertificate *peer_certificate;
115 : 6 : const char *peer_id;
116 : :
117 [ + - ]: 6 : if (!valent_lan_accept_certificate (connection, cancellable, error))
118 : : return FALSE;
119 : :
120 : 6 : peer_certificate = g_tls_connection_get_peer_certificate (connection);
121 : 6 : peer_id = valent_certificate_get_common_name (peer_certificate);
122 : :
123 : : /* If the certificate can not be found, assume that's because the device is
124 : : * unpaired and the certificate will be verified with user interaction
125 : : *
126 : : * TODO: this should be handled by centralized manager object
127 : : */
128 : 6 : file = g_file_new_build_filename (g_get_user_config_dir(), PACKAGE_NAME,
129 : : "device", peer_id,
130 : : "certificate.pem",
131 : : NULL);
132 : :
133 [ - + ]: 6 : if (!g_file_query_exists (file, NULL))
134 : : return TRUE;
135 : :
136 : 0 : peer_trusted = g_tls_certificate_new_from_file (g_file_peek_path (file),
137 : : error);
138 : :
139 : : // TODO: handle the case of a corrupted certificate
140 [ # # ]: 0 : if (peer_trusted == NULL)
141 : : return FALSE;
142 : :
143 [ # # ]: 0 : if (!g_tls_certificate_is_same (peer_trusted, peer_certificate))
144 : : {
145 : 0 : g_set_error (error,
146 : : G_TLS_ERROR,
147 : : G_TLS_ERROR_HANDSHAKE,
148 : : "Invalid certificate for \"%s\"",
149 : : peer_id);
150 : 0 : return FALSE;
151 : : }
152 : :
153 : : return TRUE;
154 : : }
155 : :
156 : : /**
157 : : * valent_lan_encrypt_client_connection:
158 : : * @connection: a `GSocketConnection`
159 : : * @certificate: a `GTlsCertificate`
160 : : * @cancellable: (nullable): a `GCancellable`
161 : : * @error: (nullable): a `GError`
162 : : *
163 : : * Authenticate and encrypt a client connection.
164 : : *
165 : : * This function sets the standard KDE Connect socket options on @connection,
166 : : * wraps it in a [class@Gio.TlsConnection] and returns the result.
167 : : *
168 : : * The common name is extracted from the peer's TLS certificate and used as the
169 : : * device ID to check for a trusted certificate. For auxiliary connections
170 : : * created from an existing channel, use [func@Valent.lan_encrypt_client].
171 : : *
172 : : * Returns: (transfer full) (nullable): a TLS encrypted `GIOStream`
173 : : */
174 : : GIOStream *
175 : 3 : valent_lan_encrypt_client_connection (GSocketConnection *connection,
176 : : GTlsCertificate *certificate,
177 : : GCancellable *cancellable,
178 : : GError **error)
179 : : {
180 : 6 : g_autoptr (GSocketAddress) address = NULL;
181 [ + - ]: 3 : g_autoptr (GIOStream) tls_stream = NULL;
182 : :
183 [ + - + - : 3 : g_assert (G_IS_SOCKET_CONNECTION (connection));
+ - - + ]
184 [ + - + - : 3 : g_assert (G_IS_TLS_CERTIFICATE (certificate));
+ - - + ]
185 [ + + + - : 3 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
186 [ + - - + ]: 3 : g_assert (error == NULL || *error == NULL);
187 : :
188 : : /* We're the client when accepting incoming connections
189 : : */
190 : 3 : g_socket_set_keepalive (g_socket_connection_get_socket (connection), TRUE);
191 : 3 : address = g_socket_connection_get_remote_address (connection, error);
192 [ - + ]: 3 : if (address == NULL)
193 : : return NULL;
194 : :
195 : 3 : tls_stream = g_tls_client_connection_new (G_IO_STREAM (connection),
196 : : G_SOCKET_CONNECTABLE (address),
197 : : error);
198 : :
199 [ - + ]: 3 : if (tls_stream == NULL)
200 : : return NULL;
201 : :
202 : 3 : g_tls_connection_set_certificate (G_TLS_CONNECTION (tls_stream), certificate);
203 : :
204 [ - + ]: 3 : if (!valent_lan_handshake_peer (G_TLS_CONNECTION (tls_stream),
205 : : cancellable,
206 : : error))
207 : : {
208 : 0 : g_io_stream_close (tls_stream, NULL, NULL);
209 : 0 : return NULL;
210 : : }
211 : :
212 : : return g_steal_pointer (&tls_stream);
213 : : }
214 : :
215 : : /**
216 : : * valent_lan_encrypt_client:
217 : : * @connection: a `GSocketConnection`
218 : : * @certificate: a `GTlsCertificate`
219 : : * @peer_certificate: a `GTlsCertificate`
220 : : * @cancellable: (nullable): a `GCancellable`
221 : : * @error: (nullable): a `GError`
222 : : *
223 : : * Authenticate and encrypt an auxiliary client connection.
224 : : *
225 : : * This function sets the standard KDE Connect socket options on @connection,
226 : : * wraps it in a [class@Gio.TlsConnection] and returns the result.
227 : : *
228 : : * Returns: (transfer full) (nullable): a TLS encrypted `GIOStream`
229 : : */
230 : : GIOStream *
231 : 1 : valent_lan_encrypt_client (GSocketConnection *connection,
232 : : GTlsCertificate *certificate,
233 : : GTlsCertificate *peer_certificate,
234 : : GCancellable *cancellable,
235 : : GError **error)
236 : : {
237 : 2 : g_autoptr (GSocketAddress) address = NULL;
238 [ + - ]: 1 : g_autoptr (GIOStream) tls_stream = NULL;
239 : :
240 [ + - + - : 1 : g_assert (G_IS_SOCKET_CONNECTION (connection));
+ - - + ]
241 [ + - + - : 1 : g_assert (G_IS_TLS_CERTIFICATE (certificate));
+ - - + ]
242 : : //g_assert (G_IS_TLS_CERTIFICATE (peer_certificate));
243 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
244 [ + - - + ]: 1 : g_assert (error == NULL || *error == NULL);
245 : :
246 : : // TODO: Occasionally we are not passed a certificate
247 [ + - + - : 1 : if G_UNLIKELY (!G_IS_TLS_CERTIFICATE (peer_certificate))
+ - + - ]
248 : : {
249 : 0 : g_set_error (error,
250 : : G_TLS_ERROR,
251 : : G_TLS_ERROR_CERTIFICATE_REQUIRED,
252 : : "No peer certificate");
253 : 0 : return NULL;
254 : : }
255 : :
256 : : /* We're the client when accepting auxiliary connections
257 : : */
258 : 1 : g_socket_set_keepalive (g_socket_connection_get_socket (connection), TRUE);
259 : 1 : address = g_socket_connection_get_remote_address (connection, error);
260 [ + - ]: 1 : if (address == NULL)
261 : : return NULL;
262 : :
263 : 1 : tls_stream = g_tls_client_connection_new (G_IO_STREAM (connection),
264 : : G_SOCKET_CONNECTABLE (address),
265 : : error);
266 : :
267 [ + - ]: 1 : if (tls_stream == NULL)
268 : : return NULL;
269 : :
270 : 1 : g_tls_connection_set_certificate (G_TLS_CONNECTION (tls_stream), certificate);
271 : :
272 [ - + ]: 1 : if (!valent_lan_handshake_certificate (G_TLS_CONNECTION (tls_stream),
273 : : peer_certificate,
274 : : cancellable,
275 : : error))
276 : : {
277 : 0 : g_io_stream_close (tls_stream, NULL, NULL);
278 : 0 : return NULL;
279 : : }
280 : :
281 : : return g_steal_pointer (&tls_stream);
282 : : }
283 : :
284 : : /**
285 : : * valent_lan_encrypt_server_connection:
286 : : * @connection: a `GSocketConnection`
287 : : * @certificate: a `GTlsConnection`
288 : : * @cancellable: (nullable): a `GCancellable`
289 : : * @error: (nullable): a `GError`
290 : : *
291 : : * Authenticate and encrypt a server connection.
292 : : *
293 : : * This function sets the standard KDE Connect socket options on @connection,
294 : : * wraps it in a [class@Gio.TlsConnection] and returns the result.
295 : : *
296 : : * The common name is extracted from the peer's TLS certificate and used as the
297 : : * device ID to check for a trusted certificate. For auxiliary connections
298 : : * created from an existing channel, use [func@Valent.lan_encrypt_server].
299 : : *
300 : : * Returns: (transfer full) (nullable): a TLS encrypted `GIOStream`
301 : : */
302 : : GIOStream *
303 : 3 : valent_lan_encrypt_server_connection (GSocketConnection *connection,
304 : : GTlsCertificate *certificate,
305 : : GCancellable *cancellable,
306 : : GError **error)
307 : : {
308 : 6 : g_autoptr (GIOStream) tls_stream = NULL;
309 : :
310 [ + - + - : 3 : g_assert (G_IS_SOCKET_CONNECTION (connection));
+ - - + ]
311 [ + - + - : 3 : g_assert (G_IS_TLS_CERTIFICATE (certificate));
+ - - + ]
312 [ + + + - : 3 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
313 [ + - - + ]: 3 : g_assert (error == NULL || *error == NULL);
314 : :
315 : : /* We're the server when opening outgoing connections
316 : : */
317 : 3 : g_socket_set_keepalive (g_socket_connection_get_socket (connection), TRUE);
318 : 3 : tls_stream = g_tls_server_connection_new (G_IO_STREAM (connection),
319 : : certificate,
320 : : error);
321 : :
322 [ - + ]: 3 : if (tls_stream == NULL)
323 : : return NULL;
324 : :
325 : 3 : g_object_set (G_TLS_SERVER_CONNECTION (tls_stream),
326 : : "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED,
327 : : NULL);
328 : :
329 [ - + ]: 3 : if (!valent_lan_handshake_peer (G_TLS_CONNECTION (tls_stream),
330 : : cancellable,
331 : : error))
332 : : {
333 : 0 : g_io_stream_close (tls_stream, NULL, NULL);
334 : 0 : return NULL;
335 : : }
336 : :
337 : : return g_steal_pointer (&tls_stream);
338 : : }
339 : :
340 : : /**
341 : : * valent_lan_encrypt_server:
342 : : * @connection: a `GSocketConnection`
343 : : * @certificate: a `GTlsCertificate`
344 : : * @peer_certificate: a `GTlsCertificate`
345 : : * @cancellable: (nullable): a `GCancellable`
346 : : * @error: (nullable): a `GError`
347 : : *
348 : : * Authenticate and encrypt an auxiliary server connection.
349 : : *
350 : : * This function sets the standard KDE Connect socket options on @connection,
351 : : * wraps it in a [class@Gio.TlsConnection] and returns the result.
352 : : *
353 : : * Returns: (transfer full) (nullable): a TLS encrypted `GIOStream`
354 : : */
355 : : GIOStream *
356 : 1 : valent_lan_encrypt_server (GSocketConnection *connection,
357 : : GTlsCertificate *certificate,
358 : : GTlsCertificate *peer_certificate,
359 : : GCancellable *cancellable,
360 : : GError **error)
361 : : {
362 : 2 : g_autoptr (GIOStream) tls_stream = NULL;
363 : :
364 [ + - + - : 1 : g_assert (G_IS_SOCKET_CONNECTION (connection));
+ - - + ]
365 [ + - + - : 1 : g_assert (G_IS_TLS_CERTIFICATE (certificate));
+ - - + ]
366 [ + - + - : 1 : g_assert (G_IS_TLS_CERTIFICATE (peer_certificate));
+ - - + ]
367 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
368 [ + - - + ]: 1 : g_assert (error == NULL || *error == NULL);
369 : :
370 : : /* We're the server when accepting auxiliary connections
371 : : */
372 : 1 : g_socket_set_keepalive (g_socket_connection_get_socket (connection), TRUE);
373 : 1 : tls_stream = g_tls_server_connection_new (G_IO_STREAM (connection),
374 : : certificate,
375 : : error);
376 : :
377 [ - + ]: 1 : if (tls_stream == NULL)
378 : : return NULL;
379 : :
380 : 1 : g_object_set (G_TLS_SERVER_CONNECTION (tls_stream),
381 : : "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED,
382 : : NULL);
383 : :
384 [ - + ]: 1 : if (!valent_lan_handshake_certificate (G_TLS_CONNECTION (tls_stream),
385 : : peer_certificate,
386 : : cancellable,
387 : : error))
388 : : {
389 : 0 : g_io_stream_close (tls_stream, NULL, NULL);
390 : 0 : return NULL;
391 : : }
392 : :
393 : : return g_steal_pointer (&tls_stream);
394 : : }
395 : :
|