Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright (C) 2008-2013 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: David Zeuthen <davidz@redhat.com>
21 : : */
22 : :
23 : : #include <locale.h>
24 : : #include <gio/gio.h>
25 : :
26 : : #include <string.h>
27 : : #include <unistd.h>
28 : :
29 : : #include "gdbus-tests.h"
30 : :
31 : : #ifdef G_OS_UNIX
32 : : #include <gio/gunixconnection.h>
33 : : #include <gio/gnetworkingprivate.h>
34 : : #include <gio/gunixsocketaddress.h>
35 : : #include <gio/gunixfdlist.h>
36 : : #endif
37 : :
38 : : /* ---------------------------------------------------------------------------------------------------- */
39 : :
40 : : static gboolean
41 : 33 : server_on_allow_mechanism (GDBusAuthObserver *observer,
42 : : const gchar *mechanism,
43 : : gpointer user_data)
44 : : {
45 : 33 : const gchar *allowed_mechanism = user_data;
46 : 33 : if (allowed_mechanism == NULL || g_strcmp0 (mechanism, allowed_mechanism) == 0)
47 : 23 : return TRUE;
48 : : else
49 : 10 : return FALSE;
50 : : }
51 : :
52 : : /* pass NULL to allow any mechanism */
53 : : static GDBusServer *
54 : 6 : server_new_for_mechanism (const gchar *allowed_mechanism)
55 : : {
56 : : gchar *addr;
57 : : gchar *guid;
58 : : GDBusServer *server;
59 : : GDBusAuthObserver *auth_observer;
60 : : GError *error;
61 : : GDBusServerFlags flags;
62 : :
63 : 6 : guid = g_dbus_generate_guid ();
64 : :
65 : : #ifdef G_OS_UNIX
66 : : gchar *tmpdir;
67 : 6 : tmpdir = g_dir_make_tmp ("gdbus-test-XXXXXX", NULL);
68 : 6 : addr = g_strdup_printf ("unix:tmpdir=%s", tmpdir);
69 : 6 : g_free (tmpdir);
70 : : #else
71 : : addr = g_strdup ("nonce-tcp:");
72 : : #endif
73 : :
74 : 6 : auth_observer = g_dbus_auth_observer_new ();
75 : :
76 : 6 : flags = G_DBUS_SERVER_FLAGS_NONE;
77 : 6 : if (g_strcmp0 (allowed_mechanism, "ANONYMOUS") == 0)
78 : 1 : flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
79 : :
80 : 6 : error = NULL;
81 : 6 : server = g_dbus_server_new_sync (addr,
82 : : flags,
83 : : guid,
84 : : auth_observer,
85 : : NULL, /* cancellable */
86 : : &error);
87 : 6 : g_assert_no_error (error);
88 : 6 : g_assert (server != NULL);
89 : :
90 : 6 : g_signal_connect (auth_observer,
91 : : "allow-mechanism",
92 : : G_CALLBACK (server_on_allow_mechanism),
93 : : (gpointer) allowed_mechanism);
94 : :
95 : 6 : g_free (addr);
96 : 6 : g_free (guid);
97 : 6 : g_object_unref (auth_observer);
98 : :
99 : 6 : return server;
100 : : }
101 : :
102 : : /* ---------------------------------------------------------------------------------------------------- */
103 : :
104 : : static gboolean
105 : 5 : test_auth_on_new_connection (GDBusServer *server,
106 : : GDBusConnection *connection,
107 : : gpointer user_data)
108 : : {
109 : 5 : GMainLoop *loop = user_data;
110 : 5 : g_main_loop_quit (loop);
111 : 5 : return FALSE;
112 : : }
113 : :
114 : : typedef struct
115 : : {
116 : : const gchar *address;
117 : : const gchar *allowed_client_mechanism;
118 : : const gchar *allowed_server_mechanism;
119 : : } TestAuthData;
120 : :
121 : : static gpointer
122 : 5 : test_auth_client_thread_func (gpointer user_data)
123 : : {
124 : 5 : TestAuthData *data = user_data;
125 : 5 : GDBusConnection *c = NULL;
126 : 5 : GError *error = NULL;
127 : 5 : GDBusAuthObserver *auth_observer = NULL;
128 : :
129 : 5 : auth_observer = g_dbus_auth_observer_new ();
130 : :
131 : 5 : g_signal_connect (auth_observer,
132 : : "allow-mechanism",
133 : : G_CALLBACK (server_on_allow_mechanism),
134 : : (gpointer) data->allowed_client_mechanism);
135 : :
136 : 5 : c = g_dbus_connection_new_for_address_sync (data->address,
137 : : G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
138 : : auth_observer,
139 : : NULL, /* GCancellable */
140 : : &error);
141 : 5 : g_assert_no_error (error);
142 : 5 : g_assert (c != NULL);
143 : 5 : g_clear_object (&c);
144 : 5 : g_clear_object (&auth_observer);
145 : 5 : return NULL;
146 : : }
147 : :
148 : : static void
149 : 5 : test_auth_mechanism (const gchar *allowed_client_mechanism,
150 : : const gchar *allowed_server_mechanism)
151 : : {
152 : : GDBusServer *server;
153 : : GMainLoop *loop;
154 : : GThread *client_thread;
155 : : TestAuthData data;
156 : :
157 : 5 : server = server_new_for_mechanism (allowed_server_mechanism);
158 : :
159 : 5 : loop = g_main_loop_new (NULL, FALSE);
160 : :
161 : 5 : g_signal_connect (server,
162 : : "new-connection",
163 : : G_CALLBACK (test_auth_on_new_connection),
164 : : loop);
165 : :
166 : 5 : data.allowed_client_mechanism = allowed_client_mechanism;
167 : 5 : data.allowed_server_mechanism = allowed_server_mechanism;
168 : 5 : data.address = g_dbus_server_get_client_address (server);
169 : :
170 : : /* Run the D-Bus client in a thread. If this hangs forever, the test harness
171 : : * (typically Meson) will eventually kill the test. */
172 : 5 : client_thread = g_thread_new ("gdbus-client-thread",
173 : : test_auth_client_thread_func,
174 : : &data);
175 : :
176 : 5 : g_dbus_server_start (server);
177 : :
178 : 5 : g_main_loop_run (loop);
179 : :
180 : 5 : g_dbus_server_stop (server);
181 : :
182 : 5 : g_thread_join (client_thread);
183 : :
184 : 15 : while (g_main_context_iteration (NULL, FALSE));
185 : 5 : g_main_loop_unref (loop);
186 : :
187 : 5 : g_object_unref (server);
188 : 5 : }
189 : :
190 : : /* ---------------------------------------------------------------------------------------------------- */
191 : :
192 : : static void
193 : 1 : auth_client_external (void)
194 : : {
195 : 1 : test_auth_mechanism ("EXTERNAL", NULL);
196 : 1 : }
197 : :
198 : : static void
199 : 1 : auth_client_dbus_cookie_sha1 (void)
200 : : {
201 : 1 : test_auth_mechanism ("DBUS_COOKIE_SHA1", NULL);
202 : 1 : }
203 : :
204 : : static void
205 : 1 : auth_server_anonymous (void)
206 : : {
207 : 1 : test_auth_mechanism (NULL, "ANONYMOUS");
208 : 1 : }
209 : :
210 : : static void
211 : 1 : auth_server_external (void)
212 : : {
213 : 1 : test_auth_mechanism (NULL, "EXTERNAL");
214 : 1 : }
215 : :
216 : : static void
217 : 1 : auth_server_dbus_cookie_sha1 (void)
218 : : {
219 : 1 : test_auth_mechanism (NULL, "DBUS_COOKIE_SHA1");
220 : 1 : }
221 : :
222 : : /* ---------------------------------------------------------------------------------------------------- */
223 : :
224 : : static gchar *temp_dbus_keyrings_dir = NULL;
225 : :
226 : : static void
227 : 1 : temp_dbus_keyrings_setup (void)
228 : : {
229 : 1 : GError *error = NULL;
230 : :
231 : 1 : g_assert (temp_dbus_keyrings_dir == NULL);
232 : 1 : temp_dbus_keyrings_dir = g_dir_make_tmp ("gdbus-test-dbus-keyrings-XXXXXX", &error);
233 : 1 : g_assert_no_error (error);
234 : 1 : g_assert (temp_dbus_keyrings_dir != NULL);
235 : 1 : g_setenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR", temp_dbus_keyrings_dir, TRUE);
236 : 1 : g_setenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION", "1", TRUE);
237 : 1 : }
238 : :
239 : : static void
240 : 1 : temp_dbus_keyrings_teardown (void)
241 : : {
242 : : GDir *dir;
243 : 1 : GError *error = NULL;
244 : : const gchar *name;
245 : :
246 : 1 : g_assert (temp_dbus_keyrings_dir != NULL);
247 : :
248 : 1 : dir = g_dir_open (temp_dbus_keyrings_dir, 0, &error);
249 : 1 : g_assert_no_error (error);
250 : 1 : g_assert (dir != NULL);
251 : 2 : while ((name = g_dir_read_name (dir)) != NULL)
252 : : {
253 : 1 : gchar *path = g_build_filename (temp_dbus_keyrings_dir, name, NULL);
254 : 1 : g_assert (unlink (path) == 0);
255 : 1 : g_free (path);
256 : : }
257 : 1 : g_dir_close (dir);
258 : 1 : g_assert (rmdir (temp_dbus_keyrings_dir) == 0);
259 : :
260 : 1 : g_free (temp_dbus_keyrings_dir);
261 : 1 : temp_dbus_keyrings_dir = NULL;
262 : 1 : g_unsetenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
263 : 1 : g_unsetenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION");
264 : 1 : }
265 : :
266 : : static void
267 : 2 : async_result_cb (GObject *obj,
268 : : GAsyncResult *result,
269 : : void *user_data)
270 : : {
271 : 2 : GAsyncResult **result_out = user_data;
272 : :
273 : 2 : g_assert (result_out != NULL);
274 : 2 : g_assert (*result_out == NULL);
275 : :
276 : 2 : *result_out = g_object_ref (result);
277 : 2 : g_main_context_wakeup (g_main_context_get_thread_default ());
278 : 2 : }
279 : :
280 : : static gboolean
281 : 0 : server_new_connection_unexpected_cb (GDBusServer *server,
282 : : GDBusConnection *connection,
283 : : void *user_data)
284 : : {
285 : : g_assert_not_reached ();
286 : : return FALSE;
287 : : }
288 : :
289 : : static void
290 : 1 : test_auth_server_read_limit (void)
291 : : {
292 : 1 : GDBusServer *server = NULL;
293 : 1 : unsigned long new_connection_id = 0;
294 : : const char *server_address;
295 : 1 : GIOStream *client_stream = NULL;
296 : : GOutputStream *client_output_stream;
297 : : GInputStream *client_input_stream;
298 : 1 : GAsyncResult *result = NULL;
299 : 1 : char *write_buffer = NULL;
300 : : char read_buffer[100];
301 : : ssize_t read_len;
302 : : size_t bytes_written;
303 : 1 : GError *local_error = NULL;
304 : :
305 : 1 : g_test_summary ("Test that GDBusServer limits the lengths of reads it does during auth from a client");
306 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3985");
307 : :
308 : 1 : server = server_new_for_mechanism (NULL);
309 : :
310 : 1 : new_connection_id = g_signal_connect (server,
311 : : "new-connection",
312 : : G_CALLBACK (server_new_connection_unexpected_cb),
313 : : NULL);
314 : 1 : server_address = g_dbus_server_get_client_address (server);
315 : 1 : g_dbus_server_start (server);
316 : :
317 : : /* Start connecting as a client */
318 : 1 : g_dbus_address_get_stream (server_address, NULL, async_result_cb, &result);
319 : :
320 : 3 : while (result == NULL)
321 : 2 : g_main_context_iteration (NULL, TRUE);
322 : :
323 : 1 : client_stream = g_dbus_address_get_stream_finish (result, NULL, &local_error);
324 : 1 : g_assert_no_error (local_error);
325 : 1 : g_clear_object (&result);
326 : :
327 : : /* Send an over-long AUTH line, maliciously */
328 : 1 : client_output_stream = g_io_stream_get_output_stream (client_stream);
329 : 1 : client_input_stream = g_io_stream_get_input_stream (client_stream);
330 : :
331 : 1 : write_buffer = g_strdup_printf ("AUTH DBUS_COOKIE_SHA1 context%0*d 123 456\r\n", 5000, 0);
332 : :
333 : 1 : g_output_stream_write_all_async (client_output_stream,
334 : : write_buffer,
335 : : strlen (write_buffer),
336 : : G_PRIORITY_DEFAULT,
337 : : NULL,
338 : : async_result_cb,
339 : : &result);
340 : :
341 : 2 : while (result == NULL)
342 : 1 : g_main_context_iteration (NULL, TRUE);
343 : :
344 : 1 : g_output_stream_write_all_finish (client_output_stream, result, &bytes_written, &local_error);
345 : 1 : g_assert_no_error (local_error);
346 : 1 : g_assert_cmpuint (bytes_written, ==, strlen (write_buffer));
347 : 1 : g_clear_object (&result);
348 : :
349 : 1 : g_clear_pointer (&write_buffer, g_free);
350 : :
351 : : /* Authentication should have been rejected, so reading or writing the stream
352 : : * should now fail. */
353 : 1 : read_len = g_input_stream_read (client_input_stream,
354 : : read_buffer,
355 : : sizeof (read_buffer),
356 : : NULL,
357 : : &local_error);
358 : 1 : if (read_len != 0)
359 : : {
360 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
361 : 1 : g_assert_cmpint (read_len, <, 0);
362 : : }
363 : 1 : g_clear_error (&local_error);
364 : :
365 : 1 : write_buffer = g_strdup_printf ("AUTH\r\n");
366 : :
367 : 1 : g_output_stream_write_all (client_output_stream,
368 : : write_buffer,
369 : : strlen (write_buffer),
370 : : &bytes_written,
371 : : NULL,
372 : : &local_error);
373 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
374 : 1 : g_assert_cmpuint (bytes_written, ==, 0);
375 : 1 : g_clear_error (&local_error);
376 : :
377 : 1 : g_clear_pointer (&write_buffer, g_free);
378 : :
379 : : /* Cleanup */
380 : 1 : g_clear_object (&client_stream);
381 : 1 : g_dbus_server_stop (server);
382 : :
383 : 1 : g_clear_signal_handler (&new_connection_id, server);
384 : 1 : g_clear_object (&server);
385 : 1 : }
386 : :
387 : : /* ---------------------------------------------------------------------------------------------------- */
388 : :
389 : : int
390 : 1 : main (int argc,
391 : : char *argv[])
392 : : {
393 : : gint ret;
394 : :
395 : 1 : setlocale (LC_ALL, "C");
396 : :
397 : 1 : temp_dbus_keyrings_setup ();
398 : :
399 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
400 : :
401 : 1 : g_test_add_func ("/gdbus/auth/client/EXTERNAL", auth_client_external);
402 : 1 : g_test_add_func ("/gdbus/auth/client/DBUS_COOKIE_SHA1", auth_client_dbus_cookie_sha1);
403 : 1 : g_test_add_func ("/gdbus/auth/server/ANONYMOUS", auth_server_anonymous);
404 : 1 : g_test_add_func ("/gdbus/auth/server/EXTERNAL", auth_server_external);
405 : 1 : g_test_add_func ("/gdbus/auth/server/DBUS_COOKIE_SHA1", auth_server_dbus_cookie_sha1);
406 : 1 : g_test_add_func ("/gdbus/auth/server/read-limit", test_auth_server_read_limit);
407 : :
408 : : /* TODO: we currently don't have tests for
409 : : *
410 : : * - DBUS_COOKIE_SHA1 timeouts (and clock changes etc)
411 : : * - interoperability with libdbus-1 implementations of authentication methods (both client and server)
412 : : */
413 : :
414 : 1 : ret = g_test_run();
415 : :
416 : 1 : temp_dbus_keyrings_teardown ();
417 : :
418 : 1 : return ret;
419 : : }
|