1
+/*
2
+ * D-Bus VNC server (qemu-vnc) end-to-end test
3
+ *
4
+ * Copyright (c) 2026 Red Hat, Inc.
5
+ *
6
+ * SPDX-License-Identifier: GPL-2.0-or-later
7
+ */
8
+
9
+#include "qemu/osdep.h"
10
+#include <gio/gio.h>
11
+#include <gvnc.h>
12
+#include <sys/un.h>
13
+#include "qemu/sockets.h"
14
+#include "libqtest.h"
15
+#include "qemu-vnc1.h"
16
+#ifdef CONFIG_TASN1
17
+#include "tests/unit/crypto-tls-x509-helpers.h"
18
+#endif
19
+
20
+#define VNC_TEST_TIMEOUT_MS 10000
21
+
22
+typedef struct DbusTest {
23
+ QTestState *qts;
24
+ GSubprocess *vnc_subprocess;
25
+ GTestDBus *bus;
26
+ GDBusConnection *bus_conn;
27
+ GMainLoop *loop;
28
+ char *vnc_sock_path;
29
+ char *tmp_dir;
30
+ char *bus_addr;
31
+} DbusTest;
32
+
33
+typedef struct LifecycleData {
34
+ DbusTest *dt;
35
+ QemuVnc1Server *server_proxy;
36
+ VncConnection *conn;
37
+ char *client_path;
38
+ gboolean got_connected;
39
+ gboolean got_initialized;
40
+ gboolean got_disconnected;
41
+} LifecycleData;
42
+
43
+static QemuVnc1Server *
44
+create_server_proxy(GDBusConnection *bus_conn, GError **errp)
45
+{
46
+ return qemu_vnc1_server_proxy_new_sync(
47
+ bus_conn,
48
+ G_DBUS_PROXY_FLAGS_NONE,
49
+ "org.qemu.vnc",
50
+ "/org/qemu/Vnc1/Server",
51
+ NULL, errp);
52
+}
53
+
54
+static void
55
+on_vnc_error(VncConnection *self, const char *msg)
56
+{
57
+ g_error("vnc-error: %s", msg);
58
+}
59
+
60
+static void
61
+on_vnc_auth_failure(VncConnection *self, const char *msg)
62
+{
63
+ g_error("vnc-auth-failure: %s", msg);
64
+}
65
+
66
+static void
67
+on_vnc_initialized(VncConnection *self, GMainLoop *loop)
68
+{
69
+ const char *name = vnc_connection_get_name(self);
70
+
71
+ g_assert_cmpstr(name, ==, "QEMU (dbus-vnc-test)");
72
+ g_main_loop_quit(loop);
73
+}
74
+
75
+static gboolean
76
+timeout_cb(gpointer data)
77
+{
78
+ g_error("test timed out");
79
+ return G_SOURCE_REMOVE;
80
+}
81
+
82
+static int
83
+connect_unix_socket(const char *path)
84
+{
85
+ int fd;
86
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
87
+
88
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
89
+ g_assert(fd >= 0);
90
+
91
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
92
+
93
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
94
+ close(fd);
95
+ return -1;
96
+ }
97
+ return fd;
98
+}
99
+
100
+static int
101
+wait_for_vnc_socket(const char *path, int timeout_ms)
102
+{
103
+ int elapsed = 0;
104
+ const int interval = 50;
105
+
106
+ while (elapsed < timeout_ms) {
107
+ int fd = connect_unix_socket(path);
108
+
109
+ if (fd >= 0) {
110
+ return fd;
111
+ }
112
+
113
+ g_usleep(interval * 1000);
114
+ elapsed += interval;
115
+ }
116
+ return -1;
117
+}
118
+
119
+static GSubprocess *
120
+spawn_qemu_vnc(int dbus_fd, const char *sock_path)
121
+{
122
+ const char *binary;
123
+ g_autoptr(GError) err = NULL;
124
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
125
+ GSubprocess *proc;
126
+ g_autofree char *fd_str = NULL;
127
+ g_autofree char *vnc_addr = NULL;
128
+
129
+ binary = g_getenv("QTEST_QEMU_VNC_BINARY");
130
+ g_assert(binary != NULL);
131
+
132
+ fd_str = g_strdup_printf("%d", dbus_fd);
133
+ vnc_addr = g_strdup_printf("unix:%s", sock_path);
134
+
135
+ launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE);
136
+ g_subprocess_launcher_take_fd(launcher, dbus_fd, dbus_fd);
137
+
138
+ proc = g_subprocess_launcher_spawn(launcher, &err,
139
+ binary,
140
+ "--dbus-p2p-fd", fd_str,
141
+ "--vnc-addr", vnc_addr,
142
+ NULL);
143
+ g_assert_no_error(err);
144
+ g_assert(proc != NULL);
145
+
146
+ return proc;
147
+}
148
+
149
+static GSubprocess *
150
+spawn_qemu_vnc_bus_full(const char *dbus_addr, const char *sock_path,
151
+ const char *const *extra_args)
152
+{
153
+ const char *binary;
154
+ g_autoptr(GError) err = NULL;
155
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
156
+ g_autoptr(GPtrArray) argv = NULL;
157
+ GSubprocess *proc;
158
+ g_autofree char *vnc_addr = NULL;
159
+
160
+ binary = g_getenv("QTEST_QEMU_VNC_BINARY");
161
+ g_assert(binary != NULL);
162
+
163
+ vnc_addr = g_strdup_printf("unix:%s", sock_path);
164
+
165
+ argv = g_ptr_array_new();
166
+ g_ptr_array_add(argv, (gpointer)binary);
167
+ g_ptr_array_add(argv, (gpointer)"--dbus-address");
168
+ g_ptr_array_add(argv, (gpointer)dbus_addr);
169
+ g_ptr_array_add(argv, (gpointer)"--bus-name");
170
+ g_ptr_array_add(argv, (gpointer)"org.qemu");
171
+ g_ptr_array_add(argv, (gpointer)"--vnc-addr");
172
+ g_ptr_array_add(argv, (gpointer)vnc_addr);
173
+
174
+ if (extra_args) {
175
+ for (int i = 0; extra_args[i]; i++) {
176
+ g_ptr_array_add(argv, (gpointer)extra_args[i]);
177
+ }
178
+ }
179
+
180
+ g_ptr_array_add(argv, NULL);
181
+
182
+ launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE);
183
+ proc = g_subprocess_launcher_spawnv(launcher,
184
+ (const char *const *)argv->pdata, &err);
185
+ g_assert_no_error(err);
186
+ g_assert(proc != NULL);
187
+
188
+ return proc;
189
+}
190
+
191
+
192
+static void
193
+name_appeared_cb(GDBusConnection *connection,
194
+ const gchar *name,
195
+ const gchar *name_owner,
196
+ gpointer user_data)
197
+{
198
+ gboolean *appeared = user_data;
199
+ *appeared = TRUE;
200
+}
201
+
202
+static bool
203
+setup_dbus_test_full(DbusTest *dt, const char *const *vnc_extra_args)
204
+{
205
+ g_autoptr(GError) err = NULL;
206
+ g_auto(GStrv) addr_parts = NULL;
207
+ g_autofree char *qemu_args = NULL;
208
+
209
+ if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
210
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
211
+ return false;
212
+ }
213
+
214
+ dt->bus = g_test_dbus_new(G_TEST_DBUS_NONE);
215
+ g_test_dbus_up(dt->bus);
216
+
217
+ /* remove ,guid=foo part */
218
+ addr_parts = g_strsplit(g_test_dbus_get_bus_address(dt->bus), ",", 2);
219
+ dt->bus_addr = g_strdup(addr_parts[0]);
220
+
221
+ dt->bus_conn = g_dbus_connection_new_for_address_sync(
222
+ g_test_dbus_get_bus_address(dt->bus),
223
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
224
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
225
+ NULL, NULL, &err);
226
+ g_assert_no_error(err);
227
+
228
+ qemu_args = g_strdup_printf("-display dbus,addr=%s "
229
+ "-name dbus-vnc-test", dt->bus_addr);
230
+ dt->qts = qtest_init(qemu_args);
231
+
232
+ dt->tmp_dir = g_dir_make_tmp("dbus-vnc-test-XXXXXX", NULL);
233
+ g_assert(dt->tmp_dir != NULL);
234
+ dt->vnc_sock_path = g_build_filename(dt->tmp_dir, "vnc.sock", NULL);
235
+ dt->vnc_subprocess = spawn_qemu_vnc_bus_full(dt->bus_addr,
236
+ dt->vnc_sock_path,
237
+ vnc_extra_args);
238
+
239
+ /*
240
+ * Wait for the org.qemu.vnc bus name to appear, which indicates
241
+ * qemu-vnc has fully initialized (connected to QEMU, set up the
242
+ * display, exported its D-Bus interfaces, and opened the VNC
243
+ * socket).
244
+ */
245
+ {
246
+ guint watch_id, timeout_id;
247
+ gboolean appeared = FALSE;
248
+
249
+ watch_id = g_bus_watch_name_on_connection(
250
+ dt->bus_conn, "org.qemu.vnc",
251
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
252
+ name_appeared_cb, NULL, &appeared, NULL);
253
+ timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
254
+
255
+ while (!appeared) {
256
+ if (!g_main_context_iteration(NULL, TRUE)) {
257
+ break;
258
+ }
259
+ }
260
+
261
+ g_bus_unwatch_name(watch_id);
262
+ g_source_remove(timeout_id);
263
+
264
+ if (!appeared) {
265
+ g_test_fail();
266
+ g_test_message("Timed out waiting for org.qemu.vnc bus name");
267
+ return false;
268
+ }
269
+ }
270
+
271
+ return true;
272
+}
273
+
274
+static bool
275
+setup_dbus_test(DbusTest *dt)
276
+{
277
+ return setup_dbus_test_full(dt, NULL);
278
+}
279
+
280
+static void
281
+cleanup_dbus_test(DbusTest *dt)
282
+{
283
+ if (dt->bus_conn) {
284
+ g_dbus_connection_close_sync(dt->bus_conn, NULL, NULL);
285
+ g_object_unref(dt->bus_conn);
286
+ }
287
+ if (dt->vnc_subprocess) {
288
+ g_subprocess_force_exit(dt->vnc_subprocess);
289
+ g_subprocess_wait(dt->vnc_subprocess, NULL, NULL);
290
+ g_object_unref(dt->vnc_subprocess);
291
+ }
292
+ if (dt->vnc_sock_path) {
293
+ unlink(dt->vnc_sock_path);
294
+ g_free(dt->vnc_sock_path);
295
+ }
296
+ if (dt->tmp_dir) {
297
+ rmdir(dt->tmp_dir);
298
+ g_free(dt->tmp_dir);
299
+ }
300
+ if (dt->qts) {
301
+ qtest_quit(dt->qts);
302
+ }
303
+ if (dt->bus) {
304
+ g_test_dbus_down(dt->bus);
305
+ g_object_unref(dt->bus);
306
+ }
307
+ g_free(dt->bus_addr);
308
+}
309
+
310
+static void
311
+test_dbus_vnc_basic(void)
312
+{
313
+ DbusTest dt = { 0 };
314
+ VncConnection *conn = NULL;
315
+ GMainLoop *loop = NULL;
316
+ int pair[2];
317
+ int vnc_fd;
318
+ guint timeout_id;
319
+
320
+ if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
321
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
322
+ return;
323
+ }
324
+
325
+ dt.qts = qtest_init("-display dbus,p2p=yes -name dbus-vnc-test");
326
+
327
+ g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
328
+ qtest_qmp_add_client(dt.qts, "@dbus-display", pair[1]);
329
+ close(pair[1]);
330
+
331
+ dt.tmp_dir = g_dir_make_tmp("dbus-vnc-test-XXXXXX", NULL);
332
+ g_assert(dt.tmp_dir != NULL);
333
+ dt.vnc_sock_path = g_build_filename(dt.tmp_dir, "vnc.sock", NULL);
334
+
335
+ dt.vnc_subprocess = spawn_qemu_vnc(pair[0], dt.vnc_sock_path);
336
+
337
+ vnc_fd = wait_for_vnc_socket(dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
338
+ g_assert(vnc_fd >= 0);
339
+
340
+ loop = g_main_loop_new(NULL, FALSE);
341
+
342
+ conn = vnc_connection_new();
343
+ g_signal_connect(conn, "vnc-error",
344
+ G_CALLBACK(on_vnc_error), NULL);
345
+ g_signal_connect(conn, "vnc-auth-failure",
346
+ G_CALLBACK(on_vnc_auth_failure), NULL);
347
+ g_signal_connect(conn, "vnc-initialized",
348
+ G_CALLBACK(on_vnc_initialized), loop);
349
+ vnc_connection_set_auth_type(conn, VNC_CONNECTION_AUTH_NONE);
350
+ vnc_connection_open_fd(conn, vnc_fd);
351
+
352
+ timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
353
+ g_main_loop_run(loop);
354
+ g_source_remove(timeout_id);
355
+
356
+ if (conn) {
357
+ vnc_connection_shutdown(conn);
358
+ g_object_unref(conn);
359
+ }
360
+ g_clear_pointer(&loop, g_main_loop_unref);
361
+ cleanup_dbus_test(&dt);
362
+}
363
+
364
+static void
365
+test_dbus_vnc_server_props(void)
366
+{
367
+ DbusTest dt = { 0 };
368
+ QemuVnc1Server *proxy = NULL;
369
+ g_autoptr(GError) err = NULL;
370
+ const gchar *const *clients;
371
+ GVariant *listeners;
372
+
373
+ if (!setup_dbus_test(&dt)) {
374
+ goto cleanup;
375
+ }
376
+
377
+ proxy = create_server_proxy(dt.bus_conn, &err);
378
+ g_assert_no_error(err);
379
+ g_assert_nonnull(proxy);
380
+
381
+ g_assert_cmpstr(qemu_vnc1_server_get_name(proxy), ==,
382
+ "dbus-vnc-test");
383
+ g_assert_cmpstr(qemu_vnc1_server_get_auth(proxy), ==,
384
+ "none");
385
+ g_assert_cmpstr(qemu_vnc1_server_get_vencrypt_sub_auth(proxy), ==,
386
+ "");
387
+
388
+ clients = qemu_vnc1_server_get_clients(proxy);
389
+ g_assert_nonnull(clients);
390
+ g_assert_cmpint(g_strv_length((gchar **)clients), ==, 0);
391
+
392
+ listeners = qemu_vnc1_server_get_listeners(proxy);
393
+ g_assert_nonnull(listeners);
394
+ g_assert_cmpint(g_variant_n_children(listeners), >, 0);
395
+
396
+cleanup:
397
+ g_clear_object(&proxy);
398
+ cleanup_dbus_test(&dt);
399
+}
400
+
401
+static void
402
+on_client_connected(QemuVnc1Server *proxy,
403
+ const gchar *client_path,
404
+ LifecycleData *data)
405
+{
406
+ data->got_connected = TRUE;
407
+ data->client_path = g_strdup(client_path);
408
+}
409
+
410
+static void
411
+on_client_initialized(QemuVnc1Server *proxy,
412
+ const gchar *client_path,
413
+ LifecycleData *data)
414
+{
415
+ data->got_initialized = TRUE;
416
+ g_main_loop_quit(data->dt->loop);
417
+}
418
+
419
+static void
420
+on_client_disconnected(QemuVnc1Server *proxy,
421
+ const gchar *client_path,
422
+ LifecycleData *data)
423
+{
424
+ data->got_disconnected = TRUE;
425
+ g_main_loop_quit(data->dt->loop);
426
+}
427
+
428
+static void
429
+test_dbus_vnc_client_lifecycle(void)
430
+{
431
+ DbusTest dt = { 0 };
432
+ QemuVnc1Server *server_proxy = NULL;
433
+ QemuVnc1Client *client_proxy = NULL;
434
+ g_autoptr(GError) err = NULL;
435
+ LifecycleData ldata = { 0 };
436
+ int vnc_fd;
437
+ guint timeout_id;
438
+
439
+ if (!setup_dbus_test(&dt)) {
440
+ goto cleanup;
441
+ }
442
+
443
+ server_proxy = create_server_proxy(dt.bus_conn, &err);
444
+ g_assert_no_error(err);
445
+
446
+ ldata.dt = &dt;
447
+ ldata.server_proxy = server_proxy;
448
+
449
+ g_signal_connect(server_proxy, "client-connected",
450
+ G_CALLBACK(on_client_connected), &ldata);
451
+ g_signal_connect(server_proxy, "client-initialized",
452
+ G_CALLBACK(on_client_initialized), &ldata);
453
+ g_signal_connect(server_proxy, "client-disconnected",
454
+ G_CALLBACK(on_client_disconnected), &ldata);
455
+
456
+ vnc_fd = wait_for_vnc_socket(dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
457
+ g_assert(vnc_fd >= 0);
458
+
459
+ ldata.conn = vnc_connection_new();
460
+ g_signal_connect(ldata.conn, "vnc-error",
461
+ G_CALLBACK(on_vnc_error), NULL);
462
+ g_signal_connect(ldata.conn, "vnc-auth-failure",
463
+ G_CALLBACK(on_vnc_auth_failure), NULL);
464
+ vnc_connection_set_auth_type(ldata.conn, VNC_CONNECTION_AUTH_NONE);
465
+ vnc_connection_open_fd(ldata.conn, vnc_fd);
466
+
467
+ /* wait for ClientInitialized */
468
+ dt.loop = g_main_loop_new(NULL, FALSE);
469
+ timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
470
+ g_main_loop_run(dt.loop);
471
+ g_source_remove(timeout_id);
472
+
473
+ g_assert_true(ldata.got_connected);
474
+ g_assert_true(ldata.got_initialized);
475
+ g_assert_nonnull(ldata.client_path);
476
+
477
+ /* Check client properties while still connected */
478
+ client_proxy = qemu_vnc1_client_proxy_new_sync(
479
+ dt.bus_conn,
480
+ G_DBUS_PROXY_FLAGS_NONE,
481
+ "org.qemu.vnc",
482
+ ldata.client_path,
483
+ NULL, &err);
484
+ g_assert_no_error(err);
485
+
486
+ g_assert_cmpstr(qemu_vnc1_client_get_family(client_proxy), ==,
487
+ "unix");
488
+ g_assert_false(qemu_vnc1_client_get_web_socket(client_proxy));
489
+ g_assert_cmpstr(qemu_vnc1_client_get_x509_dname(client_proxy), ==,
490
+ "");
491
+ g_assert_cmpstr(qemu_vnc1_client_get_sasl_username(client_proxy),
492
+ ==, "");
493
+
494
+ /* disconnect and wait for ClientDisconnected */
495
+ vnc_connection_shutdown(ldata.conn);
496
+ timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
497
+ g_main_loop_run(dt.loop);
498
+ g_source_remove(timeout_id);
499
+
500
+ g_assert_true(ldata.got_disconnected);
501
+
502
+ g_object_unref(ldata.conn);
503
+ g_main_loop_unref(dt.loop);
504
+ dt.loop = NULL;
505
+ g_free(ldata.client_path);
506
+
507
+cleanup:
508
+ g_clear_object(&server_proxy);
509
+ g_clear_object(&client_proxy);
510
+ cleanup_dbus_test(&dt);
511
+}
512
+
513
+static void
514
+test_dbus_vnc_no_password(void)
515
+{
516
+ DbusTest dt = { 0 };
517
+ QemuVnc1Server *proxy = NULL;
518
+ g_autoptr(GError) err = NULL;
519
+ gboolean ret;
520
+
521
+ if (!setup_dbus_test(&dt)) {
522
+ goto cleanup;
523
+ }
524
+
525
+ proxy = create_server_proxy(dt.bus_conn, &err);
526
+ g_assert_no_error(err);
527
+
528
+ /*
529
+ * With default auth=none, SetPassword should return an error
530
+ * because VNC password authentication is not enabled.
531
+ */
532
+ ret = qemu_vnc1_server_call_set_password_sync(
533
+ proxy, "secret",
534
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
535
+ g_assert_false(ret);
536
+ g_assert_error(err, G_DBUS_ERROR, G_DBUS_ERROR_FAILED);
537
+ g_clear_error(&err);
538
+
539
+ ret = qemu_vnc1_server_call_expire_password_sync(
540
+ proxy, "never",
541
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
542
+ g_assert_no_error(err);
543
+ g_assert_true(ret);
544
+
545
+ ret = qemu_vnc1_server_call_expire_password_sync(
546
+ proxy, "+3600",
547
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
548
+ g_assert_no_error(err);
549
+ g_assert_true(ret);
550
+
551
+cleanup:
552
+ g_clear_object(&proxy);
553
+ cleanup_dbus_test(&dt);
554
+}
555
+
556
+typedef struct PasswordData {
557
+ DbusTest *dt;
558
+ VncConnection *conn;
559
+ const char *password;
560
+ gboolean auth_succeeded;
561
+ gboolean auth_failed;
562
+} PasswordData;
563
+
564
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
565
+static void
566
+on_pw_vnc_auth_credential(VncConnection *conn, GValueArray *creds,
567
+ PasswordData *data)
568
+{
569
+ for (guint i = 0; i < creds->n_values; i++) {
570
+ int type = g_value_get_enum(g_value_array_get_nth(creds, i));
571
+
572
+ if (type == VNC_CONNECTION_CREDENTIAL_PASSWORD) {
573
+ vnc_connection_set_credential(conn, type, data->password);
574
+ }
575
+ }
576
+}
577
+G_GNUC_END_IGNORE_DEPRECATIONS
578
+
579
+static void
580
+on_pw_vnc_initialized(VncConnection *conn, PasswordData *data)
581
+{
582
+ data->auth_succeeded = TRUE;
583
+ g_main_loop_quit(data->dt->loop);
584
+}
585
+
586
+static void
587
+on_pw_vnc_auth_failure(VncConnection *conn, const char *msg,
588
+ PasswordData *data)
589
+{
590
+ data->auth_failed = TRUE;
591
+ g_main_loop_quit(data->dt->loop);
592
+}
593
+
594
+static void
595
+on_pw_vnc_error(VncConnection *conn, const char *msg,
596
+ PasswordData *data)
597
+{
598
+ data->auth_failed = TRUE;
599
+ g_main_loop_quit(data->dt->loop);
600
+}
601
+
602
+static void
603
+test_dbus_vnc_password_auth(void)
604
+{
605
+ DbusTest dt = { 0 };
606
+ QemuVnc1Server *proxy = NULL;
607
+ g_autoptr(GError) err = NULL;
608
+ PasswordData pdata = { 0 };
609
+ const char *extra_args[] = { "--password", NULL };
610
+ int vnc_fd;
611
+ guint timeout_id;
612
+ gboolean ret;
613
+
614
+ if (!setup_dbus_test_full(&dt, extra_args)) {
615
+ goto cleanup;
616
+ }
617
+
618
+ proxy = create_server_proxy(dt.bus_conn, &err);
619
+ g_assert_no_error(err);
620
+
621
+ g_assert_cmpstr(qemu_vnc1_server_get_auth(proxy), ==, "vnc");
622
+
623
+ ret = qemu_vnc1_server_call_set_password_sync(
624
+ proxy, "testpass123",
625
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err);
626
+ g_assert_no_error(err);
627
+ g_assert_true(ret);
628
+
629
+ vnc_fd = wait_for_vnc_socket(dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
630
+ g_assert(vnc_fd >= 0);
631
+
632
+ pdata.dt = &dt;
633
+ pdata.password = "testpass123";
634
+ pdata.conn = vnc_connection_new();
635
+
636
+ g_signal_connect(pdata.conn, "vnc-error",
637
+ G_CALLBACK(on_pw_vnc_error), &pdata);
638
+ g_signal_connect(pdata.conn, "vnc-auth-failure",
639
+ G_CALLBACK(on_pw_vnc_auth_failure), &pdata);
640
+ g_signal_connect(pdata.conn, "vnc-auth-credential",
641
+ G_CALLBACK(on_pw_vnc_auth_credential), &pdata);
642
+ g_signal_connect(pdata.conn, "vnc-initialized",
643
+ G_CALLBACK(on_pw_vnc_initialized), &pdata);
644
+ vnc_connection_set_auth_type(pdata.conn, VNC_CONNECTION_AUTH_VNC);
645
+ vnc_connection_open_fd(pdata.conn, vnc_fd);
646
+
647
+ dt.loop = g_main_loop_new(NULL, FALSE);
648
+ timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
649
+ g_main_loop_run(dt.loop);
650
+ g_source_remove(timeout_id);
651
+
652
+ g_assert_true(pdata.auth_succeeded);
653
+ g_assert_false(pdata.auth_failed);
654
+
655
+ vnc_connection_shutdown(pdata.conn);
656
+ g_object_unref(pdata.conn);
657
+ g_main_loop_unref(dt.loop);
658
+ dt.loop = NULL;
659
+
660
+cleanup:
661
+ g_clear_object(&proxy);
662
+ cleanup_dbus_test(&dt);
663
+}
664
+
665
+static void
666
+test_dbus_vnc_sasl_authz_no_sasl(void)
667
+{
668
+ const char *binary;
669
+ g_autoptr(GError) err = NULL;
670
+ g_autoptr(GSubprocess) proc = NULL;
671
+ gboolean ok;
672
+
673
+ binary = g_getenv("QTEST_QEMU_VNC_BINARY");
674
+ if (!binary) {
675
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
676
+ return;
677
+ }
678
+
679
+ proc = g_subprocess_new(G_SUBPROCESS_FLAGS_STDERR_SILENCE,
680
+ &err,
681
+ binary,
682
+ "--sasl-authz", "authz0",
683
+ NULL);
684
+ g_assert_no_error(err);
685
+ g_assert_nonnull(proc);
686
+
687
+ ok = g_subprocess_wait(proc, NULL, &err);
688
+ g_assert_no_error(err);
689
+ g_assert_true(ok);
690
+ g_assert_false(g_subprocess_get_successful(proc));
691
+}
692
+
693
+#ifdef CONFIG_VNC_SASL
694
+static void
695
+test_dbus_vnc_sasl_server_props(void)
696
+{
697
+ DbusTest dt = { 0 };
698
+ QemuVnc1Server *proxy = NULL;
699
+ g_autoptr(GError) err = NULL;
700
+ const char *extra_args[] = { "--sasl", NULL };
701
+
702
+ if (!setup_dbus_test_full(&dt, extra_args)) {
703
+ goto cleanup;
704
+ }
705
+
706
+ proxy = create_server_proxy(dt.bus_conn, &err);
707
+ g_assert_no_error(err);
708
+ g_assert_nonnull(proxy);
709
+
710
+ g_assert_cmpstr(qemu_vnc1_server_get_auth(proxy), ==, "sasl");
711
+
712
+cleanup:
713
+ g_clear_object(&proxy);
714
+ cleanup_dbus_test(&dt);
715
+}
716
+
717
+#define SASL_TEST_USER "testuser"
718
+#define SASL_TEST_PASS "testpass123"
719
+
720
+typedef struct SaslAuthData {
721
+ DbusTest *dt;
722
+ const char *username;
723
+ const char *password;
724
+ gboolean auth_succeeded;
725
+ gboolean auth_failed;
726
+} SaslAuthData;
727
+
728
+typedef struct SaslTestData {
729
+ DbusTest dt;
730
+ SaslAuthData sdata;
731
+ char *sasl_dir;
732
+ char *db_path;
733
+} SaslTestData;
734
+
735
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
736
+static void
737
+on_sasl_vnc_auth_credential(VncConnection *conn, GValueArray *creds,
738
+ SaslAuthData *data)
739
+{
740
+ for (guint i = 0; i < creds->n_values; i++) {
741
+ int type = g_value_get_enum(g_value_array_get_nth(creds, i));
742
+
743
+ switch (type) {
744
+ case VNC_CONNECTION_CREDENTIAL_USERNAME:
745
+ vnc_connection_set_credential(conn, type, data->username);
746
+ break;
747
+ case VNC_CONNECTION_CREDENTIAL_PASSWORD:
748
+ vnc_connection_set_credential(conn, type, data->password);
749
+ break;
750
+ }
751
+ }
752
+}
753
+G_GNUC_END_IGNORE_DEPRECATIONS
754
+
755
+static void
756
+on_sasl_vnc_initialized(VncConnection *conn, SaslAuthData *data)
757
+{
758
+ data->auth_succeeded = TRUE;
759
+ g_main_loop_quit(data->dt->loop);
760
+}
761
+
762
+static void
763
+on_sasl_vnc_auth_failure(VncConnection *conn, const char *msg,
764
+ SaslAuthData *data)
765
+{
766
+ data->auth_failed = TRUE;
767
+ g_main_loop_quit(data->dt->loop);
768
+}
769
+
770
+static void
771
+on_sasl_vnc_error(VncConnection *conn, const char *msg,
772
+ SaslAuthData *data)
773
+{
774
+ data->auth_failed = TRUE;
775
+ g_main_loop_quit(data->dt->loop);
776
+}
777
+
778
+/*
779
+ * Create a SASL configuration directory with a qemu.conf and a
780
+ * sasldb2 user database. Returns the path to the sasldb file,
781
+ * or NULL if saslpasswd2 is not available.
782
+ */
783
+static char *
784
+create_sasl_config(const char *dir, const char *username,
785
+ const char *password)
786
+{
787
+ g_autofree char *conf_path = g_strdup_printf("%s/qemu.conf", dir);
788
+ g_autofree char *db_path = g_strdup_printf("%s/sasldb2", dir);
789
+ g_autoptr(GError) err = NULL;
790
+ g_autoptr(GSubprocess) proc = NULL;
791
+ g_autofree char *conf = NULL;
792
+ GOutputStream *stdin_stream;
793
+ gboolean ok;
794
+
795
+ /* use PLAIN, and local auxprop sasldb plugin */
796
+ conf = g_strdup_printf(
797
+ "mech_list: plain\n"
798
+ "pwcheck_method: auxprop\n"
799
+ "auxprop_plugin: sasldb\n"
800
+ "sasldb_path: %s\n", db_path);
801
+ g_assert_true(g_file_set_contents(conf_path, conf, -1, NULL));
802
+
803
+ proc = g_subprocess_new(
804
+ G_SUBPROCESS_FLAGS_STDIN_PIPE |
805
+ G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
806
+ G_SUBPROCESS_FLAGS_STDERR_SILENCE,
807
+ &err,
808
+ "saslpasswd2", "-f", db_path, "-a", "qemu", "-p", "-c",
809
+ username, NULL);
810
+ if (!proc) {
811
+ return NULL;
812
+ }
813
+
814
+ stdin_stream = g_subprocess_get_stdin_pipe(proc);
815
+ g_output_stream_write_all(stdin_stream, password,
816
+ strlen(password), NULL, NULL, NULL);
817
+ g_output_stream_close(stdin_stream, NULL, NULL);
818
+
819
+ ok = g_subprocess_wait_check(proc, NULL, &err);
820
+ if (!ok) {
821
+ return NULL;
822
+ }
823
+
824
+ return g_strdup(db_path);
825
+}
826
+
827
+static void
828
+cleanup_sasl_config(const char *dir, const char *db_path)
829
+{
830
+ g_autofree char *conf = g_strdup_printf("%s/qemu.conf", dir);
831
+
832
+ unlink(conf);
833
+ if (db_path) {
834
+ unlink(db_path);
835
+ }
836
+ rmdir(dir);
837
+}
838
+
839
+/*
840
+ * Set up SASL environment: create temp config dir, sasldb, and
841
+ * start qemu-vnc with the given extra_args. Returns FALSE if the
842
+ * test should be skipped.
843
+ */
844
+static gboolean
845
+setup_sasl_test(SaslTestData *st, const char **extra_args)
846
+{
847
+ if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
848
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
849
+ return FALSE;
850
+ }
851
+
852
+ st->sasl_dir = g_dir_make_tmp("dbus-vnc-sasl-XXXXXX", NULL);
853
+ g_assert_nonnull(st->sasl_dir);
854
+
855
+ st->db_path = create_sasl_config(st->sasl_dir, SASL_TEST_USER,
856
+ SASL_TEST_PASS);
857
+ if (!st->db_path) {
858
+ g_test_skip("saslpasswd2 not available or failed");
859
+ cleanup_sasl_config(st->sasl_dir, NULL);
860
+ return FALSE;
861
+ }
862
+
863
+ g_setenv("SASL_CONF_PATH", st->sasl_dir, TRUE);
864
+
865
+ if (!setup_dbus_test_full(&st->dt, extra_args)) {
866
+ return FALSE;
867
+ }
868
+
869
+ return TRUE;
870
+}
871
+
872
+/*
873
+ * Connect to the VNC server using SASL and run the main loop
874
+ * until authentication completes or times out.
875
+ */
876
+static void
877
+run_sasl_auth(SaslTestData *st, const char *username,
878
+ const char *password)
879
+{
880
+ VncConnection *conn;
881
+ guint timeout_id;
882
+ int vnc_fd;
883
+
884
+ st->sdata.dt = &st->dt;
885
+ st->sdata.username = username;
886
+ st->sdata.password = password;
887
+
888
+ vnc_fd = wait_for_vnc_socket(st->dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
889
+ g_assert(vnc_fd >= 0);
890
+
891
+ conn = vnc_connection_new();
892
+ g_signal_connect(conn, "vnc-error",
893
+ G_CALLBACK(on_sasl_vnc_error), &st->sdata);
894
+ g_signal_connect(conn, "vnc-auth-failure",
895
+ G_CALLBACK(on_sasl_vnc_auth_failure), &st->sdata);
896
+ g_signal_connect(conn, "vnc-auth-credential",
897
+ G_CALLBACK(on_sasl_vnc_auth_credential),
898
+ &st->sdata);
899
+ g_signal_connect(conn, "vnc-initialized",
900
+ G_CALLBACK(on_sasl_vnc_initialized), &st->sdata);
901
+ vnc_connection_set_auth_type(conn, VNC_CONNECTION_AUTH_SASL);
902
+ vnc_connection_open_fd(conn, vnc_fd);
903
+
904
+ st->dt.loop = g_main_loop_new(NULL, FALSE);
905
+ timeout_id = g_timeout_add_seconds(10, timeout_cb, NULL);
906
+ g_main_loop_run(st->dt.loop);
907
+ g_source_remove(timeout_id);
908
+
909
+ g_signal_handlers_disconnect_by_data(conn, &st->sdata);
910
+ vnc_connection_shutdown(conn);
911
+ g_object_unref(conn);
912
+ g_main_loop_unref(st->dt.loop);
913
+ st->dt.loop = NULL;
914
+}
915
+
916
+static void
917
+cleanup_sasl_test(SaslTestData *st)
918
+{
919
+ cleanup_dbus_test(&st->dt);
920
+ g_unsetenv("SASL_CONF_PATH");
921
+ cleanup_sasl_config(st->sasl_dir, st->db_path);
922
+ g_free(st->sasl_dir);
923
+ g_free(st->db_path);
924
+}
925
+
926
+static void
927
+test_dbus_vnc_sasl_auth(void)
928
+{
929
+ SaslTestData st = { 0 };
930
+ const char *extra_args[] = { "--sasl", NULL };
931
+
932
+ if (!setup_sasl_test(&st, extra_args)) {
933
+ return;
934
+ }
935
+
936
+ run_sasl_auth(&st, SASL_TEST_USER, SASL_TEST_PASS);
937
+
938
+ g_assert_true(st.sdata.auth_succeeded);
939
+ g_assert_false(st.sdata.auth_failed);
940
+
941
+ cleanup_sasl_test(&st);
942
+}
943
+
944
+static void
945
+test_dbus_vnc_sasl_auth_bad_password(void)
946
+{
947
+ SaslTestData st = { 0 };
948
+ const char *extra_args[] = { "--sasl", NULL };
949
+
950
+ if (!setup_sasl_test(&st, extra_args)) {
951
+ return;
952
+ }
953
+
954
+ run_sasl_auth(&st, SASL_TEST_USER, "wrongpassword");
955
+
956
+ g_assert_false(st.sdata.auth_succeeded);
957
+ g_assert_true(st.sdata.auth_failed);
958
+
959
+ cleanup_sasl_test(&st);
960
+}
961
+
962
+static void
963
+test_dbus_vnc_sasl_authz_denied(void)
964
+{
965
+ SaslTestData st = { 0 };
966
+ const char *extra_args[] = {
967
+ "--sasl",
968
+ "--object",
969
+ "authz-simple,id=authz0,identity=otheruser",
970
+ "--sasl-authz", "authz0",
971
+ NULL
972
+ };
973
+
974
+ if (!setup_sasl_test(&st, extra_args)) {
975
+ return;
976
+ }
977
+
978
+ run_sasl_auth(&st, SASL_TEST_USER, SASL_TEST_PASS);
979
+
980
+ g_assert_false(st.sdata.auth_succeeded);
981
+ g_assert_true(st.sdata.auth_failed);
982
+
983
+ cleanup_sasl_test(&st);
984
+}
985
+#endif /* CONFIG_VNC_SASL */
986
+
987
+static void
988
+test_dbus_vnc_tls_authz_no_creds(void)
989
+{
990
+ const char *binary;
991
+ g_autoptr(GError) err = NULL;
992
+ g_autoptr(GSubprocess) proc = NULL;
993
+ gboolean ok;
994
+
995
+ binary = g_getenv("QTEST_QEMU_VNC_BINARY");
996
+ if (!binary) {
997
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
998
+ return;
999
+ }
1000
+
1001
+ proc = g_subprocess_new(G_SUBPROCESS_FLAGS_STDERR_SILENCE,
1002
+ &err,
1003
+ binary,
1004
+ "--tls-authz", "authz0",
1005
+ NULL);
1006
+ g_assert_no_error(err);
1007
+ g_assert_nonnull(proc);
1008
+
1009
+ ok = g_subprocess_wait(proc, NULL, &err);
1010
+ g_assert_no_error(err);
1011
+ g_assert_true(ok);
1012
+ g_assert_false(g_subprocess_get_successful(proc));
1013
+}
1014
+
1015
+#ifdef CONFIG_TASN1
1016
+#define CLIENT_CERT_CN "qemu-vnc-test"
1017
+
1018
+static char *
1019
+create_tls_certs(const char *dir)
1020
+{
1021
+ char *keyfile = g_strdup_printf("%s/key.pem", dir);
1022
+ char *cacert = g_strdup_printf("%s/ca-cert.pem", dir);
1023
+ char *servercert = g_strdup_printf("%s/server-cert.pem", dir);
1024
+ char *serverkey = g_strdup_printf("%s/server-key.pem", dir);
1025
+ char *clientcert = g_strdup_printf("%s/client-cert.pem", dir);
1026
+
1027
+ test_tls_init(keyfile);
1028
+ g_assert(link(keyfile, serverkey) == 0);
1029
+
1030
+ TLS_ROOT_REQ_SIMPLE(cacertreq, cacert);
1031
+ TLS_CERT_REQ_SIMPLE_SERVER(servercertreq, cacertreq,
1032
+ servercert, "localhost", NULL);
1033
+ TLS_CERT_REQ_SIMPLE_CLIENT(clientcertreq, cacertreq,
1034
+ CLIENT_CERT_CN, clientcert);
1035
+
1036
+ test_tls_deinit_cert(&clientcertreq);
1037
+ test_tls_deinit_cert(&servercertreq);
1038
+ test_tls_deinit_cert(&cacertreq);
1039
+
1040
+ g_free(cacert);
1041
+ g_free(servercert);
1042
+ g_free(serverkey);
1043
+ g_free(clientcert);
1044
+ return keyfile;
1045
+}
1046
+
1047
+static void
1048
+cleanup_tls_certs(const char *dir, const char *keyfile)
1049
+{
1050
+ g_autofree char *cacert = g_strdup_printf("%s/ca-cert.pem", dir);
1051
+ g_autofree char *servercert = g_strdup_printf("%s/server-cert.pem", dir);
1052
+ g_autofree char *serverkey = g_strdup_printf("%s/server-key.pem", dir);
1053
+ g_autofree char *clientcert = g_strdup_printf("%s/client-cert.pem", dir);
1054
+
1055
+ unlink(cacert);
1056
+ unlink(servercert);
1057
+ unlink(serverkey);
1058
+ unlink(clientcert);
1059
+ unlink(keyfile);
1060
+ test_tls_cleanup(keyfile);
1061
+ rmdir(dir);
1062
+}
1063
+
1064
+/*
1065
+ * Do a minimal VNC/VeNCrypt negotiation on @fd up to the point where
1066
+ * the TLS handshake should begin, then perform a GnuTLS handshake
1067
+ * using the given credentials.
1068
+ */
1069
+static bool
1070
+try_raw_tls_connect(int fd, gnutls_certificate_credentials_t cred)
1071
+{
1072
+ char buf[13];
1073
+ uint8_t num_types, type;
1074
+ uint8_t vencrypt_ver[2], ack;
1075
+ uint8_t num_sub;
1076
+ uint32_t subtype;
1077
+ gnutls_session_t session;
1078
+ int ret;
1079
+ bool success;
1080
+
1081
+ /* RFB version exchange */
1082
+ g_assert_cmpint(read(fd, buf, 12), ==, 12);
1083
+ g_assert_cmpint(write(fd, "RFB 003.008\n", 12), ==, 12);
1084
+
1085
+ /* Select VeNCrypt (type 19) from the auth list */
1086
+ g_assert_cmpint(read(fd, &num_types, 1), ==, 1);
1087
+ for (int i = 0; i < num_types; i++) {
1088
+ g_assert_cmpint(read(fd, &type, 1), ==, 1);
1089
+ }
1090
+ type = 19;
1091
+ g_assert_cmpint(write(fd, &type, 1), ==, 1);
1092
+
1093
+ /* VeNCrypt version exchange */
1094
+ g_assert_cmpint(read(fd, vencrypt_ver, 2), ==, 2);
1095
+ g_assert_cmpint(write(fd, vencrypt_ver, 2), ==, 2);
1096
+ g_assert_cmpint(read(fd, &ack, 1), ==, 1);
1097
+ g_assert_cmpint(ack, ==, 0);
1098
+
1099
+ /* Select x509-none (260) sub-auth */
1100
+ g_assert_cmpint(read(fd, &num_sub, 1), ==, 1);
1101
+ for (int i = 0; i < num_sub; i++) {
1102
+ g_assert_cmpint(read(fd, &subtype, 4), ==, 4);
1103
+ }
1104
+ subtype = htonl(260);
1105
+ g_assert_cmpint(write(fd, &subtype, 4), ==, 4);
1106
+
1107
+ /* Server sends 1-byte ack (1 = accepted) before TLS starts */
1108
+ g_assert_cmpint(read(fd, &ack, 1), ==, 1);
1109
+ g_assert_cmpint(ack, ==, 1);
1110
+
1111
+ /* TLS handshake */
1112
+ g_assert_cmpint(gnutls_init(&session, GNUTLS_CLIENT), >=, 0);
1113
+ g_assert_cmpint(
1114
+ gnutls_set_default_priority(session), >=, 0);
1115
+ g_assert_cmpint(
1116
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred),
1117
+ >=, 0);
1118
+ gnutls_transport_set_int(session, fd);
1119
+
1120
+ do {
1121
+ ret = gnutls_handshake(session);
1122
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
1123
+
1124
+ if (ret < 0) {
1125
+ success = false;
1126
+ } else {
1127
+ /*
1128
+ * Try reading the VNC security-result (4 bytes) — if the
1129
+ * server rejected us it will have closed the connection.
1130
+ */
1131
+ char tmp[4];
1132
+ do {
1133
+ ret = gnutls_record_recv(session, tmp, sizeof(tmp));
1134
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
1135
+ success = (ret > 0);
1136
+ }
1137
+
1138
+ gnutls_deinit(session);
1139
+ return success;
1140
+}
1141
+
1142
+static void
1143
+test_dbus_vnc_tls_server_props(void)
1144
+{
1145
+ DbusTest dt = { 0 };
1146
+ QemuVnc1Server *proxy = NULL;
1147
+ g_autoptr(GError) err = NULL;
1148
+ g_autofree char *tls_dir = NULL;
1149
+ g_autofree char *keyfile = NULL;
1150
+
1151
+ if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
1152
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
1153
+ return;
1154
+ }
1155
+
1156
+ tls_dir = g_dir_make_tmp("dbus-vnc-tls-XXXXXX", NULL);
1157
+ g_assert_nonnull(tls_dir);
1158
+ keyfile = create_tls_certs(tls_dir);
1159
+
1160
+ {
1161
+ const char *extra_args[] = {
1162
+ "--tls-creds", tls_dir, NULL
1163
+ };
1164
+ if (!setup_dbus_test_full(&dt, extra_args)) {
1165
+ goto cleanup;
1166
+ }
1167
+ }
1168
+
1169
+ proxy = create_server_proxy(dt.bus_conn, &err);
1170
+ g_assert_no_error(err);
1171
+ g_assert_nonnull(proxy);
1172
+
1173
+ g_assert_cmpstr(qemu_vnc1_server_get_auth(proxy), ==, "vencrypt");
1174
+ g_assert_cmpstr(qemu_vnc1_server_get_vencrypt_sub_auth(proxy), ==,
1175
+ "x509-none");
1176
+
1177
+ /*
1178
+ * With verify-peer=no, a client without a certificate should
1179
+ * be able to connect successfully through TLS.
1180
+ */
1181
+ {
1182
+ g_autofree char *ca_path =
1183
+ g_strdup_printf("%s/ca-cert.pem", tls_dir);
1184
+ gnutls_certificate_credentials_t cred;
1185
+ int fd;
1186
+
1187
+ g_assert_cmpint(
1188
+ gnutls_certificate_allocate_credentials(&cred), >=, 0);
1189
+ g_assert_cmpint(
1190
+ gnutls_certificate_set_x509_trust_file(
1191
+ cred, ca_path, GNUTLS_X509_FMT_PEM), >=, 0);
1192
+
1193
+ fd = wait_for_vnc_socket(dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
1194
+ g_assert(fd >= 0);
1195
+ g_assert_true(try_raw_tls_connect(fd, cred));
1196
+ close(fd);
1197
+
1198
+ gnutls_certificate_free_credentials(cred);
1199
+ }
1200
+
1201
+cleanup:
1202
+ g_clear_object(&proxy);
1203
+ cleanup_dbus_test(&dt);
1204
+ cleanup_tls_certs(tls_dir, keyfile);
1205
+}
1206
+
1207
+static void
1208
+test_dbus_vnc_tls_authz(void)
1209
+{
1210
+ DbusTest dt = { 0 };
1211
+ g_autofree char *tls_dir = NULL;
1212
+ g_autofree char *keyfile = NULL;
1213
+ g_autofree char *ca_path = NULL;
1214
+
1215
+ if (!g_getenv("QTEST_QEMU_VNC_BINARY")) {
1216
+ g_test_skip("QTEST_QEMU_VNC_BINARY not set");
1217
+ return;
1218
+ }
1219
+
1220
+ tls_dir = g_dir_make_tmp("dbus-vnc-tls-XXXXXX", NULL);
1221
+ g_assert_nonnull(tls_dir);
1222
+ keyfile = create_tls_certs(tls_dir);
1223
+
1224
+ /*
1225
+ * The client cert has CN=qemu-vnc-test, so the DN string
1226
+ * reported by GnuTLS is "CN=qemu-vnc-test". Configure
1227
+ * authz-simple to accept exactly that identity.
1228
+ */
1229
+ {
1230
+ g_autofree char *identity =
1231
+ g_strdup_printf("CN=%s", CLIENT_CERT_CN);
1232
+ const char *extra_args[] = {
1233
+ "--tls-creds", tls_dir,
1234
+ "--object",
1235
+ NULL, /* filled below */
1236
+ "--tls-authz", "authz0",
1237
+ NULL
1238
+ };
1239
+ g_autofree char *object_arg =
1240
+ g_strdup_printf("authz-simple,id=authz0,identity=%s", identity);
1241
+ extra_args[3] = object_arg;
1242
+
1243
+ if (!setup_dbus_test_full(&dt, extra_args)) {
1244
+ goto cleanup;
1245
+ }
1246
+ }
1247
+
1248
+ ca_path = g_strdup_printf("%s/ca-cert.pem", tls_dir);
1249
+
1250
+ /*
1251
+ * Connect without a client certificate.
1252
+ * With verify-peer=yes the TLS handshake must fail.
1253
+ */
1254
+ {
1255
+ gnutls_certificate_credentials_t cred;
1256
+ int fd;
1257
+
1258
+ g_assert_cmpint(
1259
+ gnutls_certificate_allocate_credentials(&cred), >=, 0);
1260
+ g_assert_cmpint(
1261
+ gnutls_certificate_set_x509_trust_file(
1262
+ cred, ca_path, GNUTLS_X509_FMT_PEM), >=, 0);
1263
+
1264
+ fd = wait_for_vnc_socket(dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
1265
+ g_assert(fd >= 0);
1266
+ g_assert_false(try_raw_tls_connect(fd, cred));
1267
+ close(fd);
1268
+
1269
+ gnutls_certificate_free_credentials(cred);
1270
+ }
1271
+
1272
+ /*
1273
+ * Connect with a valid client certificate whose DN
1274
+ * matches the authz-simple identity. This must succeed.
1275
+ */
1276
+ {
1277
+ g_autofree char *cert_path =
1278
+ g_strdup_printf("%s/client-cert.pem", tls_dir);
1279
+ g_autofree char *key_path =
1280
+ g_strdup_printf("%s/key.pem", tls_dir);
1281
+ gnutls_certificate_credentials_t cred;
1282
+ int fd;
1283
+
1284
+ g_assert_cmpint(
1285
+ gnutls_certificate_allocate_credentials(&cred), >=, 0);
1286
+ g_assert_cmpint(
1287
+ gnutls_certificate_set_x509_trust_file(
1288
+ cred, ca_path, GNUTLS_X509_FMT_PEM), >=, 0);
1289
+ g_assert_cmpint(
1290
+ gnutls_certificate_set_x509_key_file(
1291
+ cred, cert_path, key_path, GNUTLS_X509_FMT_PEM), >=, 0);
1292
+
1293
+ fd = wait_for_vnc_socket(dt.vnc_sock_path, VNC_TEST_TIMEOUT_MS);
1294
+ g_assert(fd >= 0);
1295
+ g_assert_true(try_raw_tls_connect(fd, cred));
1296
+ close(fd);
1297
+
1298
+ gnutls_certificate_free_credentials(cred);
1299
+ }
1300
+
1301
+cleanup:
1302
+ cleanup_dbus_test(&dt);
1303
+ cleanup_tls_certs(tls_dir, keyfile);
1304
+}
1305
+#endif /* CONFIG_TASN1 */
1306
+
1307
+int
1308
+main(int argc, char **argv)
1309
+{
1310
+ g_log_set_always_fatal(G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
1311
+
1312
+ if (getenv("GTK_VNC_DEBUG")) {
1313
+ vnc_util_set_debug(true);
1314
+ }
1315
+
1316
+ g_test_init(&argc, &argv, NULL);
1317
+
1318
+ qtest_add_func("/dbus-vnc/basic", test_dbus_vnc_basic);
1319
+ qtest_add_func("/dbus-vnc/server-props", test_dbus_vnc_server_props);
1320
+ qtest_add_func("/dbus-vnc/client-lifecycle",
1321
+ test_dbus_vnc_client_lifecycle);
1322
+ qtest_add_func("/dbus-vnc/no-password", test_dbus_vnc_no_password);
1323
+ qtest_add_func("/dbus-vnc/password-auth", test_dbus_vnc_password_auth);
1324
+ qtest_add_func("/dbus-vnc/sasl-authz-no-sasl",
1325
+ test_dbus_vnc_sasl_authz_no_sasl);
1326
+#ifdef CONFIG_VNC_SASL
1327
+ qtest_add_func("/dbus-vnc/sasl-server-props",
1328
+ test_dbus_vnc_sasl_server_props);
1329
+ qtest_add_func("/dbus-vnc/sasl-auth",
1330
+ test_dbus_vnc_sasl_auth);
1331
+ qtest_add_func("/dbus-vnc/sasl-auth-bad-password",
1332
+ test_dbus_vnc_sasl_auth_bad_password);
1333
+ qtest_add_func("/dbus-vnc/sasl-authz-denied",
1334
+ test_dbus_vnc_sasl_authz_denied);
1335
+#endif
1336
+ qtest_add_func("/dbus-vnc/tls-authz-no-creds",
1337
+ test_dbus_vnc_tls_authz_no_creds);
1338
+#ifdef CONFIG_TASN1
1339
+ qtest_add_func("/dbus-vnc/tls-server-props",
1340
+ test_dbus_vnc_tls_server_props);
1341
+ qtest_add_func("/dbus-vnc/tls-authz",
1342
+ test_dbus_vnc_tls_authz);
1343
+#endif
1344
+
1345
+ return g_test_run();
1346
+}