From dbddf59e3a90b4f13b5a23b40462d9c84a9a1a96 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 11 Oct 2023 11:07:21 +0200 Subject: [PATCH 01/27] daemon: Acquire mutter dbus proxies always from session bus From the previous design of the handover, mutter dbus proxies could be at system session bus, but now not anymore. --- src/grd-daemon-user.c | 4 +--- src/grd-daemon.c | 27 +++++++++++++-------------- src/grd-daemon.h | 3 +-- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/grd-daemon-user.c b/src/grd-daemon-user.c index 5cc6e60f..9fce89a2 100644 --- a/src/grd-daemon-user.c +++ b/src/grd-daemon-user.c @@ -73,10 +73,8 @@ static void grd_daemon_user_startup (GApplication *app) { GrdDaemon *daemon = GRD_DAEMON (app); - g_autoptr (GDBusConnection) connection = NULL; - connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - grd_daemon_acquire_mutter_dbus_proxies (daemon, connection); + grd_daemon_acquire_mutter_dbus_proxies (daemon); g_signal_connect (daemon, "mutter-proxy-acquired", G_CALLBACK (grd_daemon_maybe_enable_services), NULL); diff --git a/src/grd-daemon.c b/src/grd-daemon.c index 79e808ba..e72727a8 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -512,28 +512,27 @@ on_mutter_screen_cast_name_vanished (GDBusConnection *connection, } void -grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon, - GDBusConnection *connection) +grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon) { GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); g_clear_handle_id (&priv->mutter_remote_desktop_watch_name_id, g_bus_unwatch_name); priv->mutter_remote_desktop_watch_name_id = - g_bus_watch_name_on_connection (connection, - MUTTER_REMOTE_DESKTOP_BUS_NAME, - G_BUS_NAME_WATCHER_FLAGS_NONE, - on_mutter_remote_desktop_name_appeared, - on_mutter_remote_desktop_name_vanished, - daemon, NULL); + g_bus_watch_name (G_BUS_TYPE_SESSION, + MUTTER_REMOTE_DESKTOP_BUS_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_mutter_remote_desktop_name_appeared, + on_mutter_remote_desktop_name_vanished, + daemon, NULL); g_clear_handle_id (&priv->mutter_screen_cast_watch_name_id, g_bus_unwatch_name); priv->mutter_screen_cast_watch_name_id = - g_bus_watch_name_on_connection (connection, - MUTTER_SCREEN_CAST_BUS_NAME, - G_BUS_NAME_WATCHER_FLAGS_NONE, - on_mutter_screen_cast_name_appeared, - on_mutter_screen_cast_name_vanished, - daemon, NULL); + g_bus_watch_name (G_BUS_TYPE_SESSION, + MUTTER_SCREEN_CAST_BUS_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_mutter_screen_cast_name_appeared, + on_mutter_screen_cast_name_vanished, + daemon, NULL); } #ifdef HAVE_RDP diff --git a/src/grd-daemon.h b/src/grd-daemon.h index 814cf950..a2b81995 100644 --- a/src/grd-daemon.h +++ b/src/grd-daemon.h @@ -43,7 +43,6 @@ GrdContext *grd_daemon_get_context (GrdDaemon *daemon); void grd_daemon_maybe_enable_services (GrdDaemon *daemon); -void grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon, - GDBusConnection *connection); +void grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon); #endif /* GRD_DAEMON_H */ -- GitLab From a8cb6c6f31c35a753382e1f004669213d80fe08b Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Tue, 27 Sep 2022 20:45:13 +0200 Subject: [PATCH 02/27] Introduce one time credentials These credentials are used only when an RDP client is reconnecting on the handover process to the new daemon-handover. Using them, we are not exposing the configured ones, which are not necessary in the handover process. --- src/grd-credentials-one-time.c | 193 +++++++++++++++++++++++++++++++++ src/grd-credentials-one-time.h | 34 ++++++ src/meson.build | 2 + 3 files changed, 229 insertions(+) create mode 100644 src/grd-credentials-one-time.c create mode 100644 src/grd-credentials-one-time.h diff --git a/src/grd-credentials-one-time.c b/src/grd-credentials-one-time.c new file mode 100644 index 00000000..f6c5a67f --- /dev/null +++ b/src/grd-credentials-one-time.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2022 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#include "config.h" + +#include +#include + +#include "grd-credentials-one-time.h" + +struct _GrdCredentialsOneTime +{ + GrdCredentials parent; + + struct + { + char *username; + char *password; + } rdp; +}; + +G_DEFINE_TYPE (GrdCredentialsOneTime, + grd_credentials_one_time, + GRD_TYPE_CREDENTIALS) + +static char * +grd_generate_random_bytes (size_t size, + GError **error) +{ + int fd; + int ret; + char *bytes; + + errno = 0; + fd = open ("/dev/urandom", O_RDONLY); + if (fd < 0) + { + g_set_error_literal (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + g_strerror (errno)); + return NULL; + } + + bytes = g_malloc (size); + do + ret = read (fd, bytes, size); + while ((ret == -1 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) || + ret < size); + + close (fd); + + return bytes; +} + +static char * +generate_random_utf8_bytes (size_t len, + GError **error) +{ + char *random_bytes; + int i; + + random_bytes = grd_generate_random_bytes (len + 1, error); + if (!random_bytes) + return NULL; + + /* UTF-8 chars defined with 1 byte always have the MSB to 0. + * Do not use ASCII control characters (0 - 32, 127). */ + for (i = 0; i < len; ++i) + { + random_bytes[i] &= 127; + random_bytes[i] = (random_bytes[i] % 94) + 33; + } + random_bytes[len] = '\0'; + + return random_bytes; +} + +static char * +generate_random_username (size_t len, + GError **error) +{ + char *username; + + username = generate_random_utf8_bytes (len, error); + if (!username) + return NULL; + + /* The use of # at the beggining or : at any position, + * makes an error when looking up the user on the SAM file. */ + return g_strdelimit (username, "#:", '_'); +} + +static GVariant * +grd_credentials_one_time_lookup (GrdCredentials *credentials, + GrdCredentialsType type, + GError **error) +{ + GrdCredentialsOneTime *credentials_one_time = + GRD_CREDENTIALS_ONE_TIME (credentials); + const char *rdp_username = credentials_one_time->rdp.username; + const char *rdp_password = credentials_one_time->rdp.password; + GVariantBuilder builder; + + switch (type) + { + case GRD_CREDENTIALS_TYPE_RDP: + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add (&builder, "{sv}", + "username", g_variant_new_string (rdp_username)); + g_variant_builder_add (&builder, "{sv}", + "password", g_variant_new_string (rdp_password)); + + return g_variant_builder_end (&builder); + default: + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Credentials type not found"); + return NULL; + } +} + +GrdCredentialsOneTime * +grd_credentials_one_time_new (void) +{ + g_autoptr (GrdCredentialsOneTime) credentials_one_time = NULL; + g_autoptr (GError) error = NULL; + + credentials_one_time = g_object_new (GRD_TYPE_CREDENTIALS_ONE_TIME, NULL); + + credentials_one_time->rdp.username = generate_random_username (16, &error); + if (!credentials_one_time->rdp.username) + { + g_warning ("Error generating one time rdp.username %s", error->message); + return NULL; + } + + credentials_one_time->rdp.password = generate_random_utf8_bytes (16, &error); + if (!credentials_one_time->rdp.password) + { + g_warning ("Error generating one time rdp.password %s", error->message); + return NULL; + } + + return g_steal_pointer (&credentials_one_time); +} + +static void +grd_credentials_one_time_finalize (GObject *object) +{ + GrdCredentialsOneTime *credentials_one_time = GRD_CREDENTIALS_ONE_TIME (object); + + g_clear_pointer (&credentials_one_time->rdp.username, g_free); + g_clear_pointer (&credentials_one_time->rdp.password, g_free); + + G_OBJECT_CLASS (grd_credentials_one_time_parent_class)->finalize (object); +} + +static void +grd_credentials_one_time_init (GrdCredentialsOneTime *credentials_one_time) +{ +} + +static void +grd_credentials_one_time_class_init (GrdCredentialsOneTimeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GrdCredentialsClass *credentials_class = GRD_CREDENTIALS_CLASS (klass); + + object_class->finalize = grd_credentials_one_time_finalize; + + credentials_class->lookup = grd_credentials_one_time_lookup; +} diff --git a/src/grd-credentials-one-time.h b/src/grd-credentials-one-time.h new file mode 100644 index 00000000..0a435b48 --- /dev/null +++ b/src/grd-credentials-one-time.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#ifndef GRD_CREDENTIALS_ONE_TIME_H +#define GRD_CREDENTIALS_ONE_TIME_H + +#include "grd-credentials.h" + +#define GRD_TYPE_CREDENTIALS_ONE_TIME (grd_credentials_one_time_get_type ()) +G_DECLARE_FINAL_TYPE (GrdCredentialsOneTime, grd_credentials_one_time, + GRD, CREDENTIALS_ONE_TIME, GrdCredentials) + +GrdCredentialsOneTime *grd_credentials_one_time_new (void); + +#endif /* GRD_CREDENTIALS_ONE_TIME_H */ diff --git a/src/meson.build b/src/meson.build index be972cd0..a0a310a5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -29,6 +29,8 @@ credentials_sources = files([ 'grd-credentials-file.h', 'grd-credentials-libsecret.c', 'grd-credentials-libsecret.h', + 'grd-credentials-one-time.c', + 'grd-credentials-one-time.h', 'grd-credentials-tpm.c', 'grd-credentials-tpm.h', 'grd-tpm.c', -- GitLab From 48e4d0734260d4f983cfdaa7b02d1ec4117d7b16 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 9 Aug 2023 17:13:09 +0200 Subject: [PATCH 03/27] settings: Modify how the server-certificate and private-key are stored Instead of just storing the path of the server certificate and the private key, also store their content. The content of the server certificate and the private key will in future commits not just be used by the RDP server itself, but also for the handover process. While the path of the server certificate and the private key is primarily used by the different settings backends, the content of both files will in future commits be fetched by the handover daemon from the system daemon to use the same server certificate and private key, that the system daemon uses. --- src/grd-ctl.c | 8 ++--- src/grd-daemon.c | 6 ++-- src/grd-session-rdp.c | 4 +-- src/grd-settings-user.c | 4 +-- src/grd-settings.c | 70 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/src/grd-ctl.c b/src/grd-ctl.c index da628367..8fd1fe8c 100644 --- a/src/grd-ctl.c +++ b/src/grd-ctl.c @@ -147,7 +147,7 @@ rdp_set_tls_cert (GrdSettings *settings, return FALSE; } - g_object_set (G_OBJECT (settings), "rdp-server-cert", argv[0], NULL); + g_object_set (G_OBJECT (settings), "rdp-server-cert-path", argv[0], NULL); return TRUE; } @@ -166,7 +166,7 @@ rdp_set_tls_key (GrdSettings *settings, return FALSE; } - g_object_set (G_OBJECT (settings), "rdp-server-key", argv[0], NULL); + g_object_set (G_OBJECT (settings), "rdp-server-key-path", argv[0], NULL); return TRUE; } @@ -544,8 +544,8 @@ print_rdp_status (GrdSettings *settings, g_object_get (G_OBJECT (settings), "rdp-port", &port, "rdp-enabled", &enabled, - "rdp-server-key", &tls_key, - "rdp-server-cert", &tls_cert, + "rdp-server-key-path", &tls_key, + "rdp-server-cert-path", &tls_cert, "rdp-view-only", &view_only, "rdp-negotiate-port", &negotiate_port, NULL); diff --git a/src/grd-daemon.c b/src/grd-daemon.c index e72727a8..20116b81 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -148,10 +148,10 @@ export_rdp_server_interface (GrdDaemon *daemon) g_object_bind_property (settings, "rdp-negotiate-port", rdp_server_interface, "negotiate-port", G_BINDING_SYNC_CREATE); - g_object_bind_property (settings, "rdp-server-cert", + g_object_bind_property (settings, "rdp-server-cert-path", rdp_server_interface, "tls-cert", G_BINDING_SYNC_CREATE); - g_object_bind_property (settings, "rdp-server-key", + g_object_bind_property (settings, "rdp-server-key-path", rdp_server_interface, "tls-key", G_BINDING_SYNC_CREATE); g_object_bind_property (settings, "rdp-view-only", @@ -211,7 +211,7 @@ start_rdp_server (GrdDaemon *daemon) "rdp-server-key", &key, NULL); - if (!g_access (certificate, F_OK) && !g_access (key, F_OK)) + if (certificate && key) { priv->rdp_server = grd_rdp_server_new (priv->context); if (!grd_rdp_server_start (priv->rdp_server, &error)) diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c index 937e5cbd..14b48de9 100644 --- a/src/grd-session-rdp.c +++ b/src/grd-session-rdp.c @@ -1844,7 +1844,7 @@ init_rdp_session (GrdSessionRdp *session_rdp, "rdp-server-key", &server_key, NULL); - rdp_certificate = freerdp_certificate_new_from_file (server_cert); + rdp_certificate = freerdp_certificate_new_from_pem (server_cert); if (!rdp_certificate) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -1856,7 +1856,7 @@ init_rdp_session (GrdSessionRdp *session_rdp, rdp_certificate, 1)) g_assert_not_reached (); - rdp_private_key = freerdp_key_new_from_file (server_key); + rdp_private_key = freerdp_key_new_from_pem (server_key); if (!rdp_private_key) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, diff --git a/src/grd-settings-user.c b/src/grd-settings-user.c index 1401dd2c..c430a889 100644 --- a/src/grd-settings-user.c +++ b/src/grd-settings-user.c @@ -62,10 +62,10 @@ grd_settings_user_constructed (GObject *object) settings, "rdp-enabled", G_SETTINGS_BIND_DEFAULT); g_settings_bind (settings->rdp_settings, "tls-cert", - settings, "rdp-server-cert", + settings, "rdp-server-cert-path", G_SETTINGS_BIND_DEFAULT); g_settings_bind (settings->rdp_settings, "tls-key", - settings, "rdp-server-key", + settings, "rdp-server-key-path", G_SETTINGS_BIND_DEFAULT); g_settings_bind (settings->vnc_settings, "port", settings, "vnc-port", diff --git a/src/grd-settings.c b/src/grd-settings.c index 17049f68..36618e24 100644 --- a/src/grd-settings.c +++ b/src/grd-settings.c @@ -49,6 +49,8 @@ enum PROP_VNC_SCREEN_SHARE_MODE, PROP_RDP_SERVER_CERT, PROP_RDP_SERVER_KEY, + PROP_RDP_SERVER_CERT_PATH, + PROP_RDP_SERVER_KEY_PATH, PROP_VNC_AUTH_METHOD, }; @@ -65,6 +67,8 @@ typedef struct _GrdSettingsPrivate GrdRdpScreenShareMode screen_share_mode; char *server_cert; char *server_key; + char *server_cert_path; + char *server_key_path; } rdp; struct { int port; @@ -301,6 +305,8 @@ grd_settings_finalize (GObject *object) g_clear_pointer (&priv->rdp.server_cert, g_free); g_clear_pointer (&priv->rdp.server_key, g_free); + g_clear_pointer (&priv->rdp.server_cert_path, g_free); + g_clear_pointer (&priv->rdp.server_key_path, g_free); g_clear_object (&priv->credentials); G_OBJECT_CLASS (grd_settings_parent_class)->finalize (object); @@ -356,6 +362,12 @@ grd_settings_get_property (GObject *object, case PROP_RDP_SERVER_KEY: g_value_set_string (value, priv->rdp.server_key); break; + case PROP_RDP_SERVER_CERT_PATH: + g_value_set_string (value, priv->rdp.server_cert_path); + break; + case PROP_RDP_SERVER_KEY_PATH: + g_value_set_string (value, priv->rdp.server_key_path); + break; case PROP_VNC_AUTH_METHOD: if (g_getenv ("GNOME_REMOTE_DESKTOP_TEST_VNC_PASSWORD")) g_value_set_enum (value, GRD_VNC_AUTH_METHOD_PASSWORD); @@ -367,6 +379,36 @@ grd_settings_get_property (GObject *object, } } +static void +update_rdp_server_cert (GrdSettings *settings) +{ + GrdSettingsPrivate *priv = grd_settings_get_instance_private (settings); + g_autofree char *server_cert = NULL; + + if (!priv->rdp.server_cert_path) + return; + + g_file_get_contents (priv->rdp.server_cert_path, + &server_cert, + NULL, NULL); + g_object_set (G_OBJECT (settings), "rdp-server-cert", server_cert, NULL); +} + +static void +update_rdp_server_key (GrdSettings *settings) +{ + GrdSettingsPrivate *priv = grd_settings_get_instance_private (settings); + g_autofree char *server_key = NULL; + + if (!priv->rdp.server_key_path) + return; + + g_file_get_contents (priv->rdp.server_key_path, + &server_key, + NULL, NULL); + g_object_set (G_OBJECT (settings), "rdp-server-key", server_key, NULL); +} + static void grd_settings_set_property (GObject *object, guint prop_id, @@ -419,6 +461,16 @@ grd_settings_set_property (GObject *object, g_clear_pointer (&priv->rdp.server_key, g_free); priv->rdp.server_key = g_strdup (g_value_get_string (value)); break; + case PROP_RDP_SERVER_CERT_PATH: + g_clear_pointer (&priv->rdp.server_cert_path, g_free); + priv->rdp.server_cert_path = g_strdup (g_value_get_string (value)); + update_rdp_server_cert (settings); + break; + case PROP_RDP_SERVER_KEY_PATH: + g_clear_pointer (&priv->rdp.server_key_path, g_free); + priv->rdp.server_key_path = g_strdup (g_value_get_string (value)); + update_rdp_server_key (settings); + break; case PROP_VNC_AUTH_METHOD: priv->vnc.auth_method = g_value_get_enum (value); break; @@ -566,6 +618,24 @@ grd_settings_class_init (GrdSettingsClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_RDP_SERVER_CERT_PATH, + g_param_spec_string ("rdp-server-cert-path", + "rdp server cert path", + "rdp server cert path", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_RDP_SERVER_KEY_PATH, + g_param_spec_string ("rdp-server-key-path", + "rdp server key path", + "rdp server key path", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_VNC_AUTH_METHOD, g_param_spec_enum ("vnc-auth-method", -- GitLab From 57cbb3e67584ae78b4f44fb86306a5769ddf4edf Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 3 May 2023 11:59:08 +0200 Subject: [PATCH 04/27] settings: Add handover backend This settings backend will be used by the handover daemon. It just hardcodes enabled, view-only and screen-share-mode settings. The RDP server-certificate and private key will be received from the system daemon. Now that there is a new settings backend, check if settings-user is being used and only in that case connect to its "notify::rdp-enabled-changed" signal. --- src/grd-daemon.c | 19 +++++++++----- src/grd-settings-handover.c | 50 +++++++++++++++++++++++++++++++++++++ src/grd-settings-handover.h | 32 ++++++++++++++++++++++++ src/meson.build | 2 ++ 4 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 src/grd-settings-handover.c create mode 100644 src/grd-settings-handover.h diff --git a/src/grd-daemon.c b/src/grd-daemon.c index 20116b81..5229fac1 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -38,6 +38,7 @@ #include "grd-private.h" #include "grd-rdp-server.h" #include "grd-session.h" +#include "grd-settings-user.h" #include "grd-vnc-server.h" enum @@ -603,14 +604,20 @@ grd_daemon_startup (GApplication *app) export_services_status (daemon); #ifdef HAVE_RDP - g_signal_connect (settings, "notify::rdp-enabled", - G_CALLBACK (on_rdp_enabled_changed), - daemon); + if (GRD_IS_SETTINGS_USER (settings)) + { + g_signal_connect (settings, "notify::rdp-enabled", + G_CALLBACK (on_rdp_enabled_changed), + daemon); + } #endif #ifdef HAVE_VNC - g_signal_connect (settings, "notify::vnc-enabled", - G_CALLBACK (on_vnc_enabled_changed), - daemon); + if (GRD_IS_SETTINGS_USER (settings)) + { + g_signal_connect (settings, "notify::vnc-enabled", + G_CALLBACK (on_vnc_enabled_changed), + daemon); + } #endif /* Run indefinitely, until told to exit. */ diff --git a/src/grd-settings-handover.c b/src/grd-settings-handover.c new file mode 100644 index 00000000..8f300067 --- /dev/null +++ b/src/grd-settings-handover.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "grd-settings-handover.h" + +struct _GrdSettingsHandover +{ + GrdSettings parent; +}; + +G_DEFINE_TYPE (GrdSettingsHandover, grd_settings_handover, GRD_TYPE_SETTINGS) + +GrdSettingsHandover * +grd_settings_handover_new (void) +{ + return g_object_new (GRD_TYPE_SETTINGS_HANDOVER, + "rdp-enabled", TRUE, + "rdp-view-only", FALSE, + "rdp-screen-share-mode", GRD_RDP_SCREEN_SHARE_MODE_EXTEND, + NULL); +} + +static void +grd_settings_handover_init (GrdSettingsHandover *settings_handover) +{ +} + +static void +grd_settings_handover_class_init (GrdSettingsHandoverClass *klass) +{ +} diff --git a/src/grd-settings-handover.h b/src/grd-settings-handover.h new file mode 100644 index 00000000..ef936df0 --- /dev/null +++ b/src/grd-settings-handover.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef GRD_SETTINGS_HANDOVER_H +#define GRD_SETTINGS_HANDOVER_H + +#include "grd-settings.h" + +#define GRD_TYPE_SETTINGS_HANDOVER (grd_settings_handover_get_type ()) +G_DECLARE_FINAL_TYPE (GrdSettingsHandover, grd_settings_handover, + GRD, SETTINGS_HANDOVER, GrdSettings) + +GrdSettingsHandover *grd_settings_handover_new (void); + +#endif /* GRD_SETTINGS_HANDOVER_H */ diff --git a/src/meson.build b/src/meson.build index a0a310a5..1eaa3d3b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -63,6 +63,8 @@ daemon_sources = files([ 'grd-session.h', 'grd-settings.c', 'grd-settings.h', + 'grd-settings-handover.c', + 'grd-settings-handover.h', 'grd-settings-user.c', 'grd-settings-user.h', 'grd-stream.c', -- GitLab From 9ff6cbca7f723a2c036f43c7c67bafed2ea35b9b Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 8 Nov 2023 15:53:42 +0100 Subject: [PATCH 05/27] settings: Add system backend This setting backend will be used by the system daemon. It hardcodes the enabled, view-only and screen-share-mode settings. The RDP server-certificate- and private-key- settings are stored in a keyfile called grd.conf. This settings backend will write the key-file when the respective settings are set. It will also read the key-file again, when it is written/saved. To avoid ending up in an infinite loop with reading and writing, use the g_signal_handlers_block_by_func and g_signal_handlers_unblock_by_func functions. --- config.h.meson | 3 + data/grd.conf | 5 + data/meson.build | 5 + meson.build | 9 ++ meson_options.txt | 10 ++ src/grd-settings-system.c | 324 ++++++++++++++++++++++++++++++++++++++ src/grd-settings-system.h | 32 ++++ src/meson.build | 2 + 8 files changed, 390 insertions(+) create mode 100644 data/grd.conf create mode 100644 src/grd-settings-system.c create mode 100644 src/grd-settings-system.h diff --git a/config.h.meson b/config.h.meson index e9f9ea85..909e7cdd 100644 --- a/config.h.meson +++ b/config.h.meson @@ -14,3 +14,6 @@ /* Path of the data dir */ #mesondefine GRD_DATA_DIR + +/* Path of the system daemon settings */ +#mesondefine GRD_CONF diff --git a/data/grd.conf b/data/grd.conf new file mode 100644 index 00000000..ac1e2df5 --- /dev/null +++ b/data/grd.conf @@ -0,0 +1,5 @@ +# GNOME Remote Desktop configuration file for the system daemon + +[RDP] +tls-key= +tls-cert= diff --git a/data/meson.build b/data/meson.build index 4077c091..feb8d2b7 100644 --- a/data/meson.build +++ b/data/meson.build @@ -5,4 +5,9 @@ if have_rdp install_data(['grd-cuda-avc-utils_30.ptx'], install_dir: grd_datadir, ) + + install_data('grd.conf', + install_mode: ['rw-r--r--', grd_username, grd_username], + install_dir: grd_confdir, + ) endif diff --git a/meson.build b/meson.build index 6c3a5cdc..eada64c1 100644 --- a/meson.build +++ b/meson.build @@ -69,6 +69,14 @@ schemadir = join_paths(datadir, 'glib-2.0', 'schemas') grd_datadir = join_paths(datadir, 'gnome-remote-desktop') +grd_confdir = get_option('conf_dir') +if grd_confdir == '' + grd_confdir = join_paths(get_option('sysconfdir'), 'gnome-remote-desktop') +endif +grd_conf = join_paths(grd_confdir, 'grd.conf') + +grd_username = get_option('user') + cdata = configuration_data() cdata.set_quoted('GETTEXT_PACKAGE', 'gnome-remote-desktop') cdata.set_quoted('VERSION', meson.project_version()) @@ -77,6 +85,7 @@ cdata.set('HAVE_RDP', have_rdp) cdata.set('HAVE_VNC', have_vnc) cdata.set_quoted('GRD_DATA_DIR', grd_datadir) +cdata.set_quoted('GRD_CONF', grd_conf) configure_file(input: 'config.h.meson', output: 'config.h', diff --git a/meson_options.txt b/meson_options.txt index fe2af25d..973df28b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -32,3 +32,13 @@ option('systemd_user_unit_dir', type: 'string', value: '', description: 'systemd user service directory') + +option('conf_dir', + type: 'string', + value: '', + description: 'Config directory for the system daemon') + +option('user', + type: 'string', + value: 'gnome-remote-desktop', + description: 'Username for the GNOME Remote Desktop system service') diff --git a/src/grd-settings-system.c b/src/grd-settings-system.c new file mode 100644 index 00000000..2bee31ba --- /dev/null +++ b/src/grd-settings-system.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "grd-settings-system.h" + +#include + +#define GRD_SETTINGS_SYSTEM_GROUP_RDP "RDP" + +typedef struct +{ + const char *file_key; + const char *settings_name; + void (* read_settings_value) (GrdSettingsSystem *settings_system, + GKeyFile *key_file, + const char *group, + const char *key, + const char *settings_name); + void (* write_settings_value) (GrdSettingsSystem *settings_system, + GKeyFile *key_file, + const char *group, + const char *key, + const char *settings_name); +} FileSetting; + +struct _GrdSettingsSystem +{ + GrdSettings parent; + + GFileMonitor *file_monitor; +}; + +G_DEFINE_TYPE (GrdSettingsSystem, grd_settings_system, GRD_TYPE_SETTINGS) + +GrdSettingsSystem * +grd_settings_system_new (void) +{ + return g_object_new (GRD_TYPE_SETTINGS_SYSTEM, + "rdp-enabled", TRUE, + "rdp-view-only", FALSE, + "rdp-screen-share-mode", GRD_RDP_SCREEN_SHARE_MODE_EXTEND, + NULL); +} + +static void +read_string (GrdSettingsSystem *settings_system, + GKeyFile *key_file, + const char *group, + const char *key, + const char *settings_name) +{ + g_autofree char *value = NULL; + g_autoptr (GError) error = NULL; + + value = g_key_file_get_string (key_file, + group, + key, + &error); + if (error) + return; + + g_object_set (G_OBJECT (settings_system), settings_name, value, NULL); +} + +static void +write_string (GrdSettingsSystem *settings_system, + GKeyFile *key_file, + const char *group, + const char *key, + const char *settings_name) +{ + g_autofree char *value = NULL; + + g_object_get (G_OBJECT (settings_system), settings_name, &value, NULL); + + g_key_file_set_string (key_file, + group, + key, + value); +} + +static void +read_int (GrdSettingsSystem *settings_system, + GKeyFile *key_file, + const char *group, + const char *key, + const char *settings_name) +{ + int value; + g_autoptr (GError) error = NULL; + + value = g_key_file_get_integer (key_file, + group, + key, + &error); + if (error) + return; + + g_object_set (G_OBJECT (settings_system), settings_name, value, NULL); +} + +static void +write_int (GrdSettingsSystem *settings_system, + GKeyFile *key_file, + const char *group, + const char *key, + const char *settings_name) +{ + int value = 0; + + g_object_get (G_OBJECT (settings_system), settings_name, &value, NULL); + + g_key_file_set_integer (key_file, + group, + key, + value); +} + +static const FileSetting rdp_file_settings[] = +{ + { "tls-cert", "rdp-server-cert-path", read_string, write_string }, + { "tls-key", "rdp-server-key-path", read_string, write_string }, + { "port", "rdp-port", read_int, write_int }, +}; + +GKeyFile * +load_key_file (void) +{ + g_autoptr (GKeyFile) key_file = NULL; + g_autoptr (GError) error = NULL; + + if (!g_file_test (GRD_CONF, G_FILE_TEST_IS_REGULAR)) + { + g_warning ("Couldn't find system settings file"); + return NULL; + } + + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, + GRD_CONF, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, + &error)) + { + g_warning ("Coulnd't load system settings file: %s", error->message); + return NULL; + } + + return g_steal_pointer (&key_file); +} + +static void +on_rdp_setting_changed (GrdSettingsSystem *settings_system, + GParamSpec *pspec, + FileSetting *file_setting) +{ + g_autoptr (GKeyFile) key_file = NULL; + g_autoptr (GError) error = NULL; + + key_file = load_key_file (); + if (!key_file) + return; + + file_setting->write_settings_value (settings_system, + key_file, + GRD_SETTINGS_SYSTEM_GROUP_RDP, + file_setting->file_key, + file_setting->settings_name); + + if (!g_key_file_save_to_file (key_file, GRD_CONF, &error)) + g_warning ("Failed writing %s: %s", file_setting->file_key, error->message); +} + +static void +read_rdp_file_settings (GrdSettingsSystem *settings_system) +{ + int i; + g_autoptr (GKeyFile) key_file = NULL; + + key_file = load_key_file (); + if (!key_file) + return; + + for (i = 0; i < G_N_ELEMENTS (rdp_file_settings); i++) + { + g_signal_handlers_block_by_func (G_OBJECT (settings_system), + G_CALLBACK (on_rdp_setting_changed), + (gpointer) &rdp_file_settings[i]); + + rdp_file_settings[i].read_settings_value ( + settings_system, + key_file, + GRD_SETTINGS_SYSTEM_GROUP_RDP, + rdp_file_settings[i].file_key, + rdp_file_settings[i].settings_name); + + g_signal_handlers_unblock_by_func (G_OBJECT (settings_system), + G_CALLBACK (on_rdp_setting_changed), + (gpointer) &rdp_file_settings[i]); + } +} + +static void +on_file_changed (GFileMonitor *file_monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + GrdSettingsSystem *settings_system = GRD_SETTINGS_SYSTEM (user_data); + + if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) + read_rdp_file_settings (settings_system); +} + +static void +register_read_rdp_file_settings (GrdSettingsSystem *settings_system) +{ + if (!settings_system->file_monitor) + return; + + g_signal_connect (settings_system->file_monitor, + "changed", G_CALLBACK (on_file_changed), + settings_system); +} + +static void +register_write_rdp_file_settings (GrdSettingsSystem *settings_system) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (rdp_file_settings); i++) + { + g_autofree char *signal_name = NULL; + + signal_name = g_strdup_printf ("notify::%s", + rdp_file_settings[i].settings_name); + g_signal_connect (G_OBJECT (settings_system), + signal_name, + G_CALLBACK (on_rdp_setting_changed), + (gpointer) &rdp_file_settings[i]); + } +} + +static void +grd_settings_system_constructed (GObject *object) +{ + GrdSettingsSystem *settings_system = GRD_SETTINGS_SYSTEM (object); + + read_rdp_file_settings (settings_system); + + register_read_rdp_file_settings (settings_system); + register_write_rdp_file_settings (settings_system); + + G_OBJECT_CLASS (grd_settings_system_parent_class)->constructed (object); +} + +static void +grd_settings_system_finalize (GObject *object) +{ + GrdSettingsSystem *settings = GRD_SETTINGS_SYSTEM (object); + + g_clear_object (&settings->file_monitor); + + G_OBJECT_CLASS (grd_settings_system_parent_class)->finalize (object); +} + +static GFileMonitor * +init_file_monitor (void) +{ + g_autoptr (GFile) g_file = NULL; + g_autoptr (GFileMonitor) file_monitor = NULL; + g_autoptr (GError) error = NULL; + + g_file = g_file_new_for_path (GRD_CONF); + if (!g_file) + return NULL; + + file_monitor = g_file_monitor_file (g_file, + G_FILE_MONITOR_NONE, + NULL, + &error); + if (!file_monitor) + { + g_warning ("Failed monitoring %s: %s", GRD_CONF, error->message); + return NULL; + } + + return g_steal_pointer (&file_monitor); +} + +static void +grd_settings_system_init (GrdSettingsSystem *settings_system) +{ + settings_system->file_monitor = init_file_monitor (); +} + +static void +grd_settings_system_class_init (GrdSettingsSystemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = grd_settings_system_constructed; + object_class->finalize = grd_settings_system_finalize; +} diff --git a/src/grd-settings-system.h b/src/grd-settings-system.h new file mode 100644 index 00000000..8cb2f855 --- /dev/null +++ b/src/grd-settings-system.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef GRD_SETTINGS_SYSTEM_H +#define GRD_SETTINGS_SYSTEM_H + +#include "grd-settings.h" + +#define GRD_TYPE_SETTINGS_SYSTEM (grd_settings_system_get_type ()) +G_DECLARE_FINAL_TYPE (GrdSettingsSystem, grd_settings_system, + GRD, SETTINGS_SYSTEM, GrdSettings) + +GrdSettingsSystem *grd_settings_system_new (void); + +#endif /* GRD_SETTINGS_SYSTEM_H */ diff --git a/src/meson.build b/src/meson.build index 1eaa3d3b..ddf34865 100644 --- a/src/meson.build +++ b/src/meson.build @@ -65,6 +65,8 @@ daemon_sources = files([ 'grd-settings.h', 'grd-settings-handover.c', 'grd-settings-handover.h', + 'grd-settings-system.c', + 'grd-settings-system.h', 'grd-settings-user.c', 'grd-settings-user.h', 'grd-stream.c', -- GitLab From fbe1daaf30ac3846cff07842b7a39e71c9e6e878 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Tue, 27 Sep 2022 20:19:33 +0200 Subject: [PATCH 06/27] rdp: Introduce routing-token It allows peeking the routing token from the X.224 Connection Request PDU. It will be used by the system daemon in the RDP server. --- src/grd-rdp-routing-token.c | 323 ++++++++++++++++++++++++++++++++++++ src/grd-rdp-routing-token.h | 41 +++++ src/meson.build | 2 + 3 files changed, 366 insertions(+) create mode 100644 src/grd-rdp-routing-token.c create mode 100644 src/grd-rdp-routing-token.h diff --git a/src/grd-rdp-routing-token.c b/src/grd-rdp-routing-token.c new file mode 100644 index 00000000..6cb9d228 --- /dev/null +++ b/src/grd-rdp-routing-token.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2022 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#include "config.h" + +#include "grd-rdp-routing-token.h" + +#include +#include + +#define MAX_PEEK_TIME_MS 2000 + +typedef struct _RoutingTokenContext +{ + GrdRdpServer *rdp_server; + GSocketConnection *connection; + + GCancellable *cancellable; + unsigned int abort_peek_source_id; + + GCancellable *server_cancellable; +} RoutingTokenContext; + +static int +find_cr_lf (const char *buffer, + int length) +{ + int i; + + for (i = 0; i < length; ++i) + { + if (buffer[i] == 0x0D && buffer[i + 1] == 0x0A) + return i; + } + + return -1; +} + +static gboolean +peek_bytes (int fd, + uint8_t *buffer, + int length, + GCancellable *cancellable, + GError **error) +{ + GPollFD poll_fds[2] = {}; + int n_fds = 0; + int ret; + + poll_fds[n_fds].fd = fd; + poll_fds[n_fds].events = G_IO_IN; + n_fds++; + + if (!g_cancellable_make_pollfd (cancellable, &poll_fds[n_fds])) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failure preparing the cancellable for pollfd"); + return FALSE; + } + + n_fds++; + + do + { + do + ret = g_poll (poll_fds, n_fds, MAX_PEEK_TIME_MS); + while (ret == -1 && errno == EINTR); + + if (ret == -1) + { + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errno), + "On poll command: %s", strerror (errno)); + return FALSE; + } + + if (g_cancellable_is_cancelled (cancellable)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, + "Cancelled"); + return FALSE; + } + + do + ret = recv (fd, (void *) buffer, (size_t) length, MSG_PEEK); + while (ret == -1 && errno == EINTR); + + if (ret == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errno), + "On recv command: %s", strerror (errno)); + return FALSE; + } + + } + while (ret < length); + + g_cancellable_release_fd (cancellable); + + return TRUE; +} + +static char * +get_routing_token_without_prefix (char *buffer, + size_t buffer_length) +{ + g_autofree char *peeked_prefix = NULL; + g_autofree char *prefix = NULL; + size_t prefix_length; + size_t routing_token_length; + + prefix = g_strdup ("Cookie: msts="); + prefix_length = strlen (prefix); + + if (buffer_length < prefix_length) + return NULL; + + peeked_prefix = g_strndup (buffer, prefix_length); + if (g_strcmp0 (peeked_prefix, prefix) != 0) + return NULL; + + routing_token_length = find_cr_lf (buffer, buffer_length); + if (routing_token_length == -1) + return NULL; + + return g_strndup (buffer + prefix_length, + routing_token_length - prefix_length); +} + +static gboolean +peek_routing_token (int fd, + char **routing_token, + GCancellable *cancellable, + GError **error) +{ + BOOL success = FALSE; + wStream *s = NULL; + + /* TPKT values */ + uint8_t version; + uint16_t tpkt_length; + + /* x224Crq values */ + uint8_t length_indicator; + uint8_t cr_cdt; + uint16_t dst_ref; + uint8_t class_opt; + + /* Peek TPKT Header */ + s = Stream_New (NULL, 4); + g_assert (s); + + if (!peek_bytes (fd, Stream_Buffer (s), 4, cancellable, error)) + goto out; + + Stream_Read_UINT8 (s, version); + Stream_Seek (s, 1); + Stream_Read_UINT16_BE (s, tpkt_length); + + if (version != 3) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "The TPKT Header doesn't have version 3"); + goto out; + } + + /* Peek full PDU */ + Stream_Free (s, TRUE); + s = Stream_New (NULL, tpkt_length); + g_assert (s); + + if (!peek_bytes (fd, Stream_Buffer (s), tpkt_length, cancellable, error)) + goto out; + + Stream_Seek (s, 4); + + /* Check x224Crq */ + Stream_Read_UINT8 (s, length_indicator); + Stream_Read_UINT8 (s, cr_cdt); + Stream_Read_UINT16 (s, dst_ref); + Stream_Seek (s, 2); + Stream_Read_UINT8 (s, class_opt); + if (tpkt_length - 5 != length_indicator || + cr_cdt != 0xE0 || + dst_ref != 0 || + (class_opt & 0xFC) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Wrong info on x224Crq"); + goto out; + } + + /* Check routingToken */ + *routing_token = + get_routing_token_without_prefix ((char *) Stream_Pointer (s), + Stream_GetRemainingLength (s)); + + success = TRUE; + +out: + Stream_Free (s, TRUE); + return success; +} + +static void +peek_routing_token_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + RoutingTokenContext *routing_token_context = task_data; + GSocket *socket; + int fd; + char *routing_token; + GError *error = NULL; + + socket = g_socket_connection_get_socket (routing_token_context->connection); + fd = g_socket_get_fd (socket); + + if (!peek_routing_token (fd, &routing_token, + routing_token_context->cancellable, + &error)) + g_task_return_error (task, error); + else + g_task_return_pointer (task, routing_token, g_free); +} + +static gboolean +abort_peek_routing_token (gpointer user_data) +{ + RoutingTokenContext *routing_token_context = user_data; + + g_assert (routing_token_context->abort_peek_source_id); + + g_debug ("RoutingToken: Aborting current peek operation " + "(Timeout reached)"); + + g_cancellable_cancel (routing_token_context->cancellable); + + routing_token_context->abort_peek_source_id = 0; + + return G_SOURCE_REMOVE; +} + +static void +clear_routing_token_context (gpointer data) +{ + RoutingTokenContext *routing_token_context = data; + + g_clear_object (&routing_token_context->connection); + g_clear_object (&routing_token_context->server_cancellable); + g_clear_object (&routing_token_context->cancellable); + g_clear_handle_id (&routing_token_context->abort_peek_source_id, + g_source_remove); + + g_free (routing_token_context); +} + +void +grd_routing_token_peek_async (GrdRdpServer *rdp_server, + GSocketConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback on_finished_callback) +{ + RoutingTokenContext *routing_token_context; + GTask *task; + + routing_token_context = g_new0 (RoutingTokenContext, 1); + routing_token_context->rdp_server = rdp_server; + routing_token_context->connection = g_object_ref (connection); + routing_token_context->cancellable = g_cancellable_new (); + routing_token_context->server_cancellable = g_object_ref (cancellable); + + task = g_task_new (NULL, NULL, on_finished_callback, NULL); + g_task_set_task_data (task, routing_token_context, clear_routing_token_context); + g_task_run_in_thread (task, peek_routing_token_in_thread); + g_object_unref (task); + + routing_token_context->abort_peek_source_id = + g_timeout_add (MAX_PEEK_TIME_MS, + abort_peek_routing_token, + routing_token_context); +} + +char * +grd_routing_token_peek_finish (GAsyncResult *result, + GrdRdpServer **rdp_server, + GSocketConnection **connection, + GCancellable **server_cancellable, + GError **error) +{ + RoutingTokenContext *routing_token_context = + g_task_get_task_data (G_TASK (result)); + + *rdp_server = routing_token_context->rdp_server; + *connection = routing_token_context->connection; + *server_cancellable = routing_token_context->server_cancellable; + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/grd-rdp-routing-token.h b/src/grd-rdp-routing-token.h new file mode 100644 index 00000000..599e0e24 --- /dev/null +++ b/src/grd-rdp-routing-token.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#ifndef GRD_RDP_ROUTING_TOKEN_H +#define GRD_RDP_ROUTING_TOKEN_H + +#include + +#include "grd-types.h" + +void grd_routing_token_peek_async (GrdRdpServer *rdp_server, + GSocketConnection *connection, + GCancellable *cancellable, + GAsyncReadyCallback callback); + +char *grd_routing_token_peek_finish (GAsyncResult *result, + GrdRdpServer **rdp_server, + GSocketConnection **connection, + GCancellable **server_cancellable, + GError **error); + +#endif /* GRD_RDP_ROUTING_TOKEN_H */ diff --git a/src/meson.build b/src/meson.build index ddf34865..04fc209b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -130,6 +130,8 @@ if have_rdp 'grd-rdp-pipewire-stream.c', 'grd-rdp-pipewire-stream.h', 'grd-rdp-private.h', + 'grd-rdp-routing-token.c', + 'grd-rdp-routing-token.h', 'grd-rdp-sam.c', 'grd-rdp-sam.h', 'grd-rdp-server.c', -- GitLab From 2e512006acbb03914d3cac2641191062149d3675 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Tue, 27 Sep 2022 18:56:55 +0200 Subject: [PATCH 07/27] rdp-server: Add signals to expose incoming connections These signals will be used by the system daemon to get notified of new connections. On an RDP connection, only one of two following signals are emitted: - incoming-new-connection: If a routing token isn't found when peeking the first PDU, then this signal will be emitted only after successful authentication, when the RDP session emits post-connected signal (next commit). - incoming-redirected-connection: If a routing token is found when peeking the first PDU, then this signal will be emitted and the connection won't be initialized. --- src/grd-rdp-server.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c index 33a3faf6..39a83d61 100644 --- a/src/grd-rdp-server.c +++ b/src/grd-rdp-server.c @@ -39,6 +39,16 @@ enum PROP_CONTEXT, }; +enum +{ + INCOMING_NEW_CONNECTION, + INCOMING_REDIRECTED_CONNECTION, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + struct _GrdRdpServer { GSocketService parent; @@ -288,4 +298,17 @@ grd_rdp_server_class_init (GrdRdpServerClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + signals[INCOMING_NEW_CONNECTION] = g_signal_new ("incoming-new-connection", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, GRD_TYPE_SESSION); + signals[INCOMING_REDIRECTED_CONNECTION] = g_signal_new ("incoming-redirected-connection", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, + G_TYPE_SOCKET_CONNECTION); } -- GitLab From cc2bc42e3230cefe74cc0f1c4dc67f43c99bd1f0 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Tue, 24 Oct 2023 13:58:31 +0200 Subject: [PATCH 08/27] session-rdp: Emit signal when PostConnect callback is reached This signal will be used in future commits by the RDP server to notify to notify the future system daemon, when a new incoming connection happens. The PostConnect callback of the RDP peer object is called from a different thread. Use g_idle_add_once() to let the post-connected signal to be handled within the main thread to ensure no memory corruptions happen. Also move into this idle callback the call grd_session_start() and the assignment 'session_metrics->rd_session_start_init_us = g_get_monotonic_time ();' to avoid potential race conditions. --- src/grd-session-rdp.c | 50 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c index 14b48de9..5ca1d2fe 100644 --- a/src/grd-session-rdp.c +++ b/src/grd-session-rdp.c @@ -54,6 +54,15 @@ #define MAX_MONITOR_COUNT_SCREEN_SHARE 1 #define DISCRETE_SCROLL_STEP 10.0 +enum +{ + POST_CONNECTED, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + typedef enum _RdpPeerFlag { RDP_PEER_ACTIVATED = 1 << 0, @@ -157,6 +166,9 @@ struct _GrdSessionRdp GMutex close_session_mutex; unsigned int close_session_idle_id; + GMutex notify_post_connected_mutex; + unsigned int notify_post_connected_source_id; + uint32_t next_stream_id; }; @@ -1590,12 +1602,27 @@ rdp_peer_capabilities (freerdp_peer *peer) return TRUE; } +static void +notify_post_connected (gpointer user_data) +{ + GrdSessionRdp *session_rdp = user_data; + SessionMetrics *session_metrics = &session_rdp->session_metrics; + + g_mutex_lock (&session_rdp->notify_post_connected_mutex); + session_rdp->notify_post_connected_source_id = 0; + g_mutex_unlock (&session_rdp->notify_post_connected_mutex); + + session_metrics->rd_session_start_init_us = g_get_monotonic_time (); + grd_session_start (GRD_SESSION (session_rdp)); + + g_signal_emit (session_rdp, signals[POST_CONNECTED], 0); +} + static BOOL rdp_peer_post_connect (freerdp_peer *peer) { RdpPeerContext *rdp_peer_context = (RdpPeerContext *) peer->context; GrdSessionRdp *session_rdp = rdp_peer_context->session_rdp; - SessionMetrics *session_metrics = &session_rdp->session_metrics; rdpSettings *rdp_settings = peer->context->settings; uint32_t multifrag_max_request_size = freerdp_settings_get_uint32 (rdp_settings, FreeRDP_MultifragMaxRequestSize); @@ -1673,9 +1700,6 @@ rdp_peer_post_connect (freerdp_peer *peer) if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline)) set_rdp_peer_flag (session_rdp, RDP_PEER_PENDING_GFX_INIT); - session_metrics->rd_session_start_init_us = g_get_monotonic_time (); - grd_session_start (GRD_SESSION (session_rdp)); - g_clear_pointer (&session_rdp->sam_file, grd_rdp_sam_free_sam_file); if (freerdp_settings_get_bool (rdp_settings, FreeRDP_SupportGraphicsPipeline) && @@ -1689,6 +1713,11 @@ rdp_peer_post_connect (freerdp_peer *peer) set_rdp_peer_flag (session_rdp, RDP_PEER_OUTPUT_ENABLED); set_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED); + g_mutex_lock (&session_rdp->notify_post_connected_mutex); + session_rdp->notify_post_connected_source_id = + g_idle_add_once (notify_post_connected, session_rdp); + g_mutex_unlock (&session_rdp->notify_post_connected_mutex); + return TRUE; } @@ -2237,6 +2266,9 @@ grd_session_rdp_stop (GrdSession *session) g_mutex_unlock (&rdp_peer_context->channel_mutex); g_clear_pointer (&session_rdp->socket_thread, g_thread_join); + g_clear_handle_id (&session_rdp->notify_post_connected_source_id, + g_source_remove); + g_clear_object (&session_rdp->layout_manager); g_clear_object (&session_rdp->cursor_renderer); @@ -2428,6 +2460,7 @@ grd_session_rdp_dispose (GObject *object) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (object); + g_assert (!session_rdp->notify_post_connected_source_id); g_assert (!session_rdp->cursor_renderer); g_clear_object (&session_rdp->layout_manager); @@ -2452,6 +2485,7 @@ grd_session_rdp_finalize (GObject *object) { GrdSessionRdp *session_rdp = GRD_SESSION_RDP (object); + g_mutex_clear (&session_rdp->notify_post_connected_mutex); g_mutex_clear (&session_rdp->close_session_mutex); g_mutex_clear (&session_rdp->rdp_flags_mutex); g_mutex_clear (&session_rdp->pending_jobs_mutex); @@ -2473,6 +2507,7 @@ grd_session_rdp_init (GrdSessionRdp *session_rdp) g_mutex_init (&session_rdp->pending_jobs_mutex); g_mutex_init (&session_rdp->rdp_flags_mutex); g_mutex_init (&session_rdp->close_session_mutex); + g_mutex_init (&session_rdp->notify_post_connected_mutex); session_rdp->rdp_event_queue = grd_rdp_event_queue_new (session_rdp); @@ -2498,4 +2533,11 @@ grd_session_rdp_class_init (GrdSessionRdpClass *klass) grd_session_rdp_on_caps_lock_state_changed; session_class->on_num_lock_state_changed = grd_session_rdp_on_num_lock_state_changed; + + signals[POST_CONNECTED] = g_signal_new ("post-connected", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } -- GitLab From ad89df4c8035ccd1e4ef149ae0558ecc219f5dcc Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Tue, 27 Sep 2022 20:28:08 +0200 Subject: [PATCH 09/27] session-rdp: Add method to send Server Redirection PDU This method will be used in later commits by the system daemon to send a Server Redirection PDU to the RDP client during the handover process. --- src/grd-session-rdp.c | 173 ++++++++++++++++++++++++++++++++++++++++++ src/grd-session-rdp.h | 6 ++ 2 files changed, 179 insertions(+) diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c index 5ca1d2fe..b3a80b73 100644 --- a/src/grd-session-rdp.c +++ b/src/grd-session-rdp.c @@ -22,6 +22,7 @@ #include "grd-session-rdp.h" #include +#include #include #include #include @@ -53,6 +54,7 @@ #define MAX_MONITOR_COUNT_HEADLESS 16 #define MAX_MONITOR_COUNT_SCREEN_SHARE 1 #define DISCRETE_SCROLL_STEP 10.0 +#define ELEMENT_TYPE_CERTIFICATE 32 enum { @@ -352,6 +354,177 @@ grd_session_rdp_maybe_encode_pending_frame (GrdSessionRdp *session_rdp, rdp_peer_refresh_region (session_rdp, rdp_surface, buffer); } +static WCHAR * +get_utf16_string (const char *str, + size_t *size) +{ + WCHAR *utf16_string; + + *size = 0; + + utf16_string = ConvertUtf8ToWCharAlloc (str, size); + if (!utf16_string) + return NULL; + + *size = (*size + 1) * sizeof (WCHAR); + + return utf16_string; +} + +static WCHAR * +generate_encoded_redirection_guid (size_t *size) +{ + BYTE redirection_guid[16] = {}; + g_autofree char *redirection_guid_base_64 = NULL; + WCHAR *encoded_redirection_guid; + + *size = 0; + + if (winpr_RAND (redirection_guid, 16) == -1) + return NULL; + + redirection_guid_base_64 = crypto_base64_encode (redirection_guid, 16); + if (!redirection_guid_base_64) + return NULL; + + encoded_redirection_guid = get_utf16_string (redirection_guid_base_64, size); + if (!encoded_redirection_guid) + return NULL; + + return encoded_redirection_guid; +} + +static BYTE * +get_certificate_container (const char *certificate, + size_t *size) +{ + BOOL free_buffer = TRUE; + g_autofree BYTE *der_certificate = NULL; + rdpCertificate *rdp_certificate = NULL; + BYTE *certificate_container = NULL; + size_t der_certificate_len = 0; + wStream *s; + + s = Stream_New (NULL, 2048); + g_assert (s); + + *size = 0; + + rdp_certificate = freerdp_certificate_new_from_pem (certificate); + if (!rdp_certificate) + goto out; + + der_certificate = freerdp_certificate_get_der (rdp_certificate, + &der_certificate_len); + if (!der_certificate) + goto out; + + if (!Stream_EnsureRemainingCapacity (s, 12)) + g_assert_not_reached (); + + Stream_Write_UINT32 (s, ELEMENT_TYPE_CERTIFICATE); + Stream_Write_UINT32 (s, ENCODING_TYPE_ASN1_DER); + Stream_Write_UINT32 (s, der_certificate_len); + + if (!Stream_EnsureRemainingCapacity (s, der_certificate_len)) + g_assert_not_reached (); + + Stream_Write (s, der_certificate, der_certificate_len); + + *size = Stream_GetPosition (s); + certificate_container = Stream_Buffer (s); + free_buffer = FALSE; + +out: + freerdp_certificate_free (rdp_certificate); + Stream_Free (s, free_buffer); + return certificate_container; +} + +gboolean +grd_session_rdp_send_server_redirection (GrdSessionRdp *session_rdp, + const char *routing_token, + const char *user_name, + const char *password, + const char *certificate) +{ + freerdp_peer *peer = session_rdp->peer; + rdpRedirection *redirection; + g_autofree BYTE *certificate_container = NULL; + g_autofree WCHAR *utf16_password = NULL; + g_autofree WCHAR *utf16_encoded_redirection_guid = NULL; + gboolean success = FALSE; + size_t size = 0; + uint32_t redirection_flags = 0; + uint32_t redirection_incorrect_flags = 0; + + g_assert (routing_token); + g_assert (user_name); + g_assert (password); + g_assert (certificate); + + redirection = redirection_new (); + g_assert (redirection); + + /* Load Balance Info */ + redirection_flags |= LB_LOAD_BALANCE_INFO; + redirection_set_byte_option (redirection, LB_LOAD_BALANCE_INFO, + (BYTE *) routing_token, + strlen (routing_token)); + + /* Username */ + redirection_flags |= LB_USERNAME; + redirection_set_string_option (redirection, LB_USERNAME, + user_name); + + /* Password */ + redirection_flags |= LB_PASSWORD; + utf16_password = get_utf16_string (password, &size); + g_assert (utf16_password); + redirection_set_byte_option (redirection, LB_PASSWORD, + (BYTE *) utf16_password, + size); + + /* Redirection GUID */ + redirection_flags |= LB_REDIRECTION_GUID; + utf16_encoded_redirection_guid = generate_encoded_redirection_guid (&size); + g_assert (utf16_encoded_redirection_guid); + redirection_set_byte_option (redirection, LB_REDIRECTION_GUID, + (BYTE *) utf16_encoded_redirection_guid, + size); + + /* Target Certificate */ + redirection_flags |= LB_TARGET_CERTIFICATE; + certificate_container = get_certificate_container (certificate, &size); + g_assert (certificate_container); + redirection_set_byte_option (redirection, + LB_TARGET_CERTIFICATE, + certificate_container, + size); + + redirection_set_flags (redirection, redirection_flags); + + if (!redirection_settings_are_valid (redirection, &redirection_incorrect_flags)) + { + g_warning ("[RDP] Something went wrong sending Server Redirection PDU. " + "Incorrect flag/s: 0x%08x", redirection_incorrect_flags); + goto out; + } + + g_message ("[RDP] Sending server redirection"); + if (!peer->SendServerRedirection (peer, redirection)) + { + g_warning ("[RDP] Error sending server Redirection"); + goto out; + } + + success = TRUE; + +out: + redirection_free (redirection); + return success; +} + static void maybe_queue_close_session_idle (GrdSessionRdp *session_rdp) { diff --git a/src/grd-session-rdp.h b/src/grd-session-rdp.h index 49a0be22..6ea8cecf 100644 --- a/src/grd-session-rdp.h +++ b/src/grd-session-rdp.h @@ -83,4 +83,10 @@ int grd_session_rdp_get_stride_for_width (GrdSessionRdp *session_rdp, void grd_session_rdp_maybe_encode_pending_frame (GrdSessionRdp *session_rdp, GrdRdpSurface *rdp_surface); +gboolean grd_session_rdp_send_server_redirection (GrdSessionRdp *session_rdp, + const char *routing_token, + const char *user_name, + const char *password, + const char *certificate); + #endif /* GRD_SESSION_RDP_H */ -- GitLab From 0899145c8b7712829e1ef9f88048d3bd5299c711 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Thu, 10 Aug 2023 19:15:09 +0200 Subject: [PATCH 10/27] daemon: Add getter for the RDP server instance and daemon-cancellable These getters will be used by the future system daemon and by the future handover daemon. --- src/grd-daemon.c | 18 ++++++++++++++++++ src/grd-daemon.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/src/grd-daemon.c b/src/grd-daemon.c index 5229fac1..fbbb5d1f 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -82,6 +82,14 @@ typedef struct _GrdDaemonPrivate G_DEFINE_TYPE_WITH_PRIVATE (GrdDaemon, grd_daemon, G_TYPE_APPLICATION) +GCancellable * +grd_daemon_get_cancellable (GrdDaemon *daemon) +{ + GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); + + return priv->cancellable; +} + GrdContext * grd_daemon_get_context (GrdDaemon *daemon) { @@ -90,6 +98,16 @@ grd_daemon_get_context (GrdDaemon *daemon) return priv->context; } +#ifdef HAVE_RDP +GrdRdpServer * +grd_daemon_get_rdp_server (GrdDaemon *daemon) +{ + GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); + + return priv->rdp_server; +} +#endif /* HAVE_RDP */ + static void export_remote_desktop_interface (GrdDaemon *daemon) { diff --git a/src/grd-daemon.h b/src/grd-daemon.h index a2b81995..1266cf7d 100644 --- a/src/grd-daemon.h +++ b/src/grd-daemon.h @@ -39,8 +39,12 @@ struct _GrdDaemonClass gboolean (*is_daemon_ready) (GrdDaemon *daemon); }; +GCancellable *grd_daemon_get_cancellable (GrdDaemon *daemon); + GrdContext *grd_daemon_get_context (GrdDaemon *daemon); +GrdRdpServer *grd_daemon_get_rdp_server (GrdDaemon *daemon); + void grd_daemon_maybe_enable_services (GrdDaemon *daemon); void grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon); -- GitLab From 80199fd4edcbc7a861d48322160ca974a3269473 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Thu, 10 Aug 2023 19:29:10 +0200 Subject: [PATCH 11/27] Introduce daemon-system This daemon is running as a system service. It is not in charge of displaying any session. It only listens to the RDP port and on a new connection it creates a RemoteClient and uses the gdm interface RemoteDisplayFactory to create a RemoteDisplay giving the id of this RemoteClient (aka remote_id). There's one RemoteClient per one RDP client. Each RemoteDisplay is "tagged" with one RemoteClient id (remote_id). --- src/grd-context.c | 7 +- src/grd-ctl.c | 2 + src/grd-daemon-system.c | 325 +++++++++++++++++++++++++++++++ src/grd-daemon-system.h | 34 ++++ src/grd-daemon-user.c | 2 + src/grd-daemon.c | 45 ++++- src/grd-daemon.h | 2 + src/grd-enums.h | 1 + src/grd-private.h | 4 + src/grd-rdp-server.c | 95 ++++++++- src/grd-session-rdp.c | 8 +- src/grd-session-rdp.h | 1 + src/grd-settings-system.c | 1 + src/grd-settings-user.c | 2 + src/grd-settings.c | 1 + src/meson.build | 9 + src/org.gnome.DisplayManager.xml | 8 + 17 files changed, 539 insertions(+), 8 deletions(-) create mode 100644 src/grd-daemon-system.c create mode 100644 src/grd-daemon-system.h create mode 100644 src/org.gnome.DisplayManager.xml diff --git a/src/grd-context.c b/src/grd-context.c index c3fe31ef..f1320f14 100644 --- a/src/grd-context.c +++ b/src/grd-context.c @@ -25,6 +25,7 @@ #include "grd-context.h" #include "grd-egl-thread.h" +#include "grd-settings-system.h" #include "grd-settings-user.h" #include "grd-dbus-mutter-remote-desktop.h" @@ -130,7 +131,8 @@ grd_context_notify_daemon_ready (GrdContext *context) { g_autoptr (GError) error = NULL; - if (context->egl_thread) + if (context->egl_thread || + context->runtime_mode == GRD_RUNTIME_MODE_SYSTEM) return; context->egl_thread = grd_egl_thread_new (&error); @@ -153,6 +155,9 @@ grd_context_new (GrdRuntimeMode runtime_mode, case GRD_RUNTIME_MODE_HEADLESS: context->settings = GRD_SETTINGS (grd_settings_user_new (runtime_mode)); break; + case GRD_RUNTIME_MODE_SYSTEM: + context->settings = GRD_SETTINGS (grd_settings_system_new ()); + break; } if (!context->settings) diff --git a/src/grd-ctl.c b/src/grd-ctl.c index 8fd1fe8c..89114970 100644 --- a/src/grd-ctl.c +++ b/src/grd-ctl.c @@ -500,6 +500,8 @@ create_settings (GrdRuntimeMode runtime_mode) case GRD_RUNTIME_MODE_SCREEN_SHARE: case GRD_RUNTIME_MODE_HEADLESS: return GRD_SETTINGS (grd_settings_user_new (runtime_mode)); + case GRD_RUNTIME_MODE_SYSTEM: + g_assert_not_reached (); } g_assert_not_reached (); diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c new file mode 100644 index 00000000..72a337cb --- /dev/null +++ b/src/grd-daemon-system.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#include "config.h" + +#include "grd-daemon-system.h" + +#include "grd-context.h" +#include "grd-daemon.h" +#include "grd-dbus-gdm.h" +#include "grd-private.h" +#include "grd-rdp-server.h" +#include "grd-session-rdp.h" + +typedef struct +{ + GrdDaemonSystem *daemon_system; + + char *id; + + GrdSession *session; +} GrdRemoteClient; + +struct _GrdDaemonSystem +{ + GrdDaemon parent; + + GrdDBusGdmRemoteDisplayFactory *remote_display_factory_proxy; + + GHashTable *remote_clients; +}; + +G_DEFINE_TYPE (GrdDaemonSystem, grd_daemon_system, GRD_TYPE_DAEMON) + +static gboolean +grd_daemon_system_is_ready (GrdDaemon *daemon) +{ + GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (daemon); + g_autofree const char *gdm_remote_display_factory_name_owner = NULL; + GDBusProxy *remote_display_factory_proxy = + G_DBUS_PROXY (daemon_system->remote_display_factory_proxy); + + if (!daemon_system->remote_display_factory_proxy) + return FALSE; + + gdm_remote_display_factory_name_owner = g_dbus_proxy_get_name_owner ( + remote_display_factory_proxy); + + if (!gdm_remote_display_factory_name_owner) + return FALSE; + + return TRUE; +} + +static char * +get_id_from_routing_token (uint32_t routing_token) +{ + return g_strdup_printf (REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/%u", + routing_token); +} + +static void +grd_remote_client_free (GrdRemoteClient *remote_client) +{ + g_clear_pointer (&remote_client->id, g_free); + + g_free (remote_client); +} + +static char * +get_next_available_id (GrdDaemonSystem *daemon_system) +{ + uint32_t routing_token = 0; + + while (routing_token == 0) + { + g_autofree char *id = NULL; + + routing_token = g_random_int (); + id = get_id_from_routing_token (routing_token); + + if (!g_hash_table_contains (daemon_system->remote_clients, id)) + break; + + routing_token = 0; + } + + return get_id_from_routing_token (routing_token); +} + +static void +session_disposed (GrdRemoteClient *remote_client) +{ + remote_client->session = NULL; +} + +static GrdRemoteClient * +remote_client_new (GrdDaemonSystem *daemon_system, + GrdSession *session) +{ + GrdRemoteClient *remote_client; + + remote_client = g_new0 (GrdRemoteClient, 1); + remote_client->id = get_next_available_id (daemon_system); + remote_client->daemon_system = daemon_system; + remote_client->session = session; + g_object_weak_ref (G_OBJECT (session), + (GWeakNotify) session_disposed, + remote_client); + + return remote_client; +} + +static void +on_create_remote_display_finished (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDBusGdmRemoteDisplayFactory *proxy = + GRD_DBUS_GDM_REMOTE_DISPLAY_FACTORY (object); + GrdRemoteClient *remote_client = user_data; + g_autoptr (GError) error = NULL; + + if (!grd_dbus_gdm_remote_display_factory_call_create_remote_display_finish ( + proxy, result, &error)) + { + g_warning ("[DaemonSystem] Error while calling CreateRemoteDisplay on " + "DisplayMananger: %s", error->message); + if (remote_client->session) + { + grd_session_rdp_notify_error ( + GRD_SESSION_RDP (remote_client->session), + GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION); + } + g_hash_table_remove (remote_client->daemon_system->remote_clients, + remote_client->id); + } +} + +static void +on_incoming_new_connection (GrdRdpServer *rdp_server, + GrdSession *session, + GrdDaemonSystem *daemon_system) +{ + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_system)); + GrdRemoteClient *remote_client; + + g_debug ("[DaemonSystem] Incoming connection without routing token"); + + remote_client = remote_client_new (daemon_system, session); + + g_hash_table_insert (daemon_system->remote_clients, + remote_client->id, + remote_client); + + g_debug ("[DaemonSystem] Creating remote display with remote id: %s", + remote_client->id); + + grd_dbus_gdm_remote_display_factory_call_create_remote_display ( + daemon_system->remote_display_factory_proxy, + remote_client->id, + cancellable, + on_create_remote_display_finished, + remote_client); +} + +static void +on_rdp_server_started (GrdDaemonSystem *daemon_system) +{ + GrdRdpServer *rdp_server = + grd_daemon_get_rdp_server (GRD_DAEMON (daemon_system)); + + g_signal_connect (rdp_server, "incoming-new-connection", + G_CALLBACK (on_incoming_new_connection), + daemon_system); +} + +static void +on_rdp_server_stopped (GrdDaemonSystem *daemon_system) +{ + GrdRdpServer *rdp_server = + grd_daemon_get_rdp_server (GRD_DAEMON (daemon_system)); + + g_signal_handlers_disconnect_by_func (rdp_server, + G_CALLBACK (on_incoming_new_connection), + daemon_system); +} + +static void +on_remote_display_factory_name_owner_changed (GrdDBusGdmRemoteDisplayFactory *remote_display_factory_proxy, + GParamSpec *pspec, + GrdDaemonSystem *daemon_system) +{ + g_autofree const char *name_owner = NULL; + + g_object_get (G_OBJECT (remote_display_factory_proxy), + "g-name-owner", &name_owner, + NULL); + if (!name_owner) + { + grd_daemon_disable_services (GRD_DAEMON (daemon_system)); + return; + } + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); +} + +static void +on_remote_display_factory_proxy_acquired (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonSystem *daemon_system = user_data; + g_autoptr (GError) error = NULL; + + daemon_system->remote_display_factory_proxy = + grd_dbus_gdm_remote_display_factory_proxy_new_for_bus_finish (result, + &error); + if (!daemon_system->remote_display_factory_proxy) + { + g_warning ("[DaemonSystem] Failed to acquire GDM remote display " + "factory proxy: %s", error->message); + return; + } + + g_signal_connect (G_OBJECT (daemon_system->remote_display_factory_proxy), + "notify::g-name-owner", + G_CALLBACK (on_remote_display_factory_name_owner_changed), + daemon_system); + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); +} + +GrdDaemonSystem * +grd_daemon_system_new (GError **error) +{ + GrdContext *context; + + context = grd_context_new (GRD_RUNTIME_MODE_SYSTEM, error); + if (!context) + return NULL; + + return g_object_new (GRD_TYPE_DAEMON_SYSTEM, + "application-id", GRD_DAEMON_SYSTEM_APPLICATION_ID, + "flags", G_APPLICATION_IS_SERVICE, + "context", context, + NULL); +} + +static void +grd_daemon_system_init (GrdDaemonSystem *daemon_system) +{ + daemon_system->remote_clients = + g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) grd_remote_client_free); +} + +static void +grd_daemon_system_startup (GApplication *app) +{ + GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (app); + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_system)); + + grd_dbus_gdm_remote_display_factory_proxy_new_for_bus ( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + GDM_BUS_NAME, + GDM_REMOTE_DISPLAY_FACTORY_OBJECT_PATH, + cancellable, + on_remote_display_factory_proxy_acquired, + daemon_system); + + g_signal_connect (daemon_system, "rdp-server-started", + G_CALLBACK (on_rdp_server_started), NULL); + + g_signal_connect (daemon_system, "rdp-server-stopped", + G_CALLBACK (on_rdp_server_stopped), NULL); + + G_APPLICATION_CLASS (grd_daemon_system_parent_class)->startup (app); +} + +static void +grd_daemon_system_shutdown (GApplication *app) +{ + GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (app); + + g_clear_pointer (&daemon_system->remote_clients, g_hash_table_unref); + + g_clear_object (&daemon_system->remote_display_factory_proxy); + + G_APPLICATION_CLASS (grd_daemon_system_parent_class)->shutdown (app); +} + +static void +grd_daemon_system_class_init (GrdDaemonSystemClass *klass) +{ + GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass); + GrdDaemonClass *daemon_class = GRD_DAEMON_CLASS (klass); + + g_application_class->startup = grd_daemon_system_startup; + g_application_class->shutdown = grd_daemon_system_shutdown; + + daemon_class->is_daemon_ready = grd_daemon_system_is_ready; +} diff --git a/src/grd-daemon-system.h b/src/grd-daemon-system.h new file mode 100644 index 00000000..3eca3986 --- /dev/null +++ b/src/grd-daemon-system.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#ifndef GRD_DAEMON_SYSTEM_H +#define GRD_DAEMON_SYSTEM_H + +#include "grd-daemon.h" + +#define GRD_TYPE_DAEMON_SYSTEM (grd_daemon_system_get_type ()) +G_DECLARE_FINAL_TYPE (GrdDaemonSystem, grd_daemon_system, + GRD, DAEMON_SYSTEM, GrdDaemon) + +GrdDaemonSystem *grd_daemon_system_new (GError **error); + +#endif /* GRD_DAEMON_SYSTEM_H */ diff --git a/src/grd-daemon-user.c b/src/grd-daemon-user.c index 9fce89a2..9b1e1ecc 100644 --- a/src/grd-daemon-user.c +++ b/src/grd-daemon-user.c @@ -53,6 +53,8 @@ grd_daemon_user_new (GrdRuntimeMode runtime_mode, case GRD_RUNTIME_MODE_HEADLESS: application_id = GRD_DAEMON_HEADLESS_APPLICATION_ID; break; + case GRD_RUNTIME_MODE_SYSTEM: + g_assert_not_reached (); } daemon_user = g_object_new (GRD_TYPE_DAEMON_USER, diff --git a/src/grd-daemon.c b/src/grd-daemon.c index fbbb5d1f..46bf7ba7 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -32,6 +32,7 @@ #include #include "grd-context.h" +#include "grd-daemon-system.h" #include "grd-daemon-user.h" #include "grd-dbus-mutter-remote-desktop.h" #include "grd-dbus-remote-desktop.h" @@ -129,6 +130,10 @@ export_remote_desktop_interface (GrdDaemon *daemon) grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode ( priv->remote_desktop_interface, "headless"); break; + case GRD_RUNTIME_MODE_SYSTEM: + grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode ( + priv->remote_desktop_interface, "system"); + break; } g_dbus_interface_skeleton_export ( @@ -411,8 +416,8 @@ grd_daemon_maybe_enable_services (GrdDaemon *daemon) #endif } -static void -disable_services (GrdDaemon *daemon) +void +grd_daemon_disable_services (GrdDaemon *daemon) { #ifdef HAVE_RDP stop_rdp_server (daemon); @@ -494,7 +499,7 @@ on_mutter_remote_desktop_name_vanished (GDBusConnection *connection, GrdDaemon *daemon = user_data; GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); - disable_services (daemon); + grd_daemon_disable_services (daemon); grd_context_set_mutter_remote_desktop_proxy (priv->context, NULL); } @@ -525,7 +530,7 @@ on_mutter_screen_cast_name_vanished (GDBusConnection *connection, GrdDaemon *daemon = user_data; GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); - disable_services (daemon); + grd_daemon_disable_services (daemon); grd_context_set_mutter_screen_cast_proxy (priv->context, NULL); } @@ -653,7 +658,7 @@ grd_daemon_shutdown (GApplication *app) g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); - disable_services (daemon); + grd_daemon_disable_services (daemon); unexport_services_status (daemon); @@ -819,6 +824,20 @@ register_signals (GrdDaemon *daemon) g_source_attach (priv->sigterm_source, NULL); } +static int +count_trues (int n_args, ...) +{ + va_list booleans; + int booleans_count = 0; + + va_start (booleans, n_args); + + for (int i = 0; i < n_args; ++i) + booleans_count += va_arg (booleans, gboolean) ? 1 : 0; + + return booleans_count; +} + int main (int argc, char **argv) { @@ -826,6 +845,7 @@ main (int argc, char **argv) GrdSettings *settings; gboolean print_version = FALSE; gboolean headless = FALSE; + gboolean system = FALSE; int rdp_port = -1; int vnc_port = -1; @@ -834,6 +854,10 @@ main (int argc, char **argv) "Print version", NULL }, { "headless", 0, 0, G_OPTION_ARG_NONE, &headless, "Run in headless mode", NULL }, +#ifdef HAVE_RDP + { "system", 0, 0, G_OPTION_ARG_NONE, &system, + "Run in headless mode as a system g-r-d service", NULL }, +#endif { "rdp-port", 0, 0, G_OPTION_ARG_INT, &rdp_port, "RDP port", NULL }, { "vnc-port", 0, 0, G_OPTION_ARG_INT, &vnc_port, @@ -862,8 +886,16 @@ main (int argc, char **argv) return EXIT_SUCCESS; } + if (count_trues (2, headless, system) > 1) + { + g_printerr ("Invalid option: More than one runtime mode specified"); + return EXIT_FAILURE; + } + if (headless) runtime_mode = GRD_RUNTIME_MODE_HEADLESS; + else if (system) + runtime_mode = GRD_RUNTIME_MODE_SYSTEM; else runtime_mode = GRD_RUNTIME_MODE_SCREEN_SHARE; @@ -873,6 +905,9 @@ main (int argc, char **argv) case GRD_RUNTIME_MODE_HEADLESS: daemon = GRD_DAEMON (grd_daemon_user_new (runtime_mode, &error)); break; + case GRD_RUNTIME_MODE_SYSTEM: + daemon = GRD_DAEMON (grd_daemon_system_new (&error)); + break; } if (!daemon) diff --git a/src/grd-daemon.h b/src/grd-daemon.h index 1266cf7d..03c9659d 100644 --- a/src/grd-daemon.h +++ b/src/grd-daemon.h @@ -47,6 +47,8 @@ GrdRdpServer *grd_daemon_get_rdp_server (GrdDaemon *daemon); void grd_daemon_maybe_enable_services (GrdDaemon *daemon); +void grd_daemon_disable_services (GrdDaemon *daemon); + void grd_daemon_acquire_mutter_dbus_proxies (GrdDaemon *daemon); #endif /* GRD_DAEMON_H */ diff --git a/src/grd-enums.h b/src/grd-enums.h index 2c786aeb..c0185d25 100644 --- a/src/grd-enums.h +++ b/src/grd-enums.h @@ -43,6 +43,7 @@ typedef enum { GRD_RUNTIME_MODE_SCREEN_SHARE, GRD_RUNTIME_MODE_HEADLESS, + GRD_RUNTIME_MODE_SYSTEM, } GrdRuntimeMode; #endif /* GRD_ENUMS_H */ diff --git a/src/grd-private.h b/src/grd-private.h index 2320a901..ba614f67 100644 --- a/src/grd-private.h +++ b/src/grd-private.h @@ -25,12 +25,16 @@ #define GRD_DAEMON_USER_APPLICATION_ID "org.gnome.RemoteDesktop.User" #define GRD_DAEMON_HEADLESS_APPLICATION_ID "org.gnome.RemoteDesktop.Headless" +#define GRD_DAEMON_SYSTEM_APPLICATION_ID "org.gnome.RemoteDesktop" #define REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/RemoteDesktop" +#define REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/org/gnome/RemoteDesktop/Client" #define GRD_RDP_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Server" #define GRD_VNC_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Vnc/Server" #define MUTTER_REMOTE_DESKTOP_BUS_NAME "org.gnome.Mutter.RemoteDesktop" #define MUTTER_REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/Mutter/RemoteDesktop" #define MUTTER_SCREEN_CAST_BUS_NAME "org.gnome.Mutter.ScreenCast" #define MUTTER_SCREEN_CAST_OBJECT_PATH "/org/gnome/Mutter/ScreenCast" +#define GDM_BUS_NAME "org.gnome.DisplayManager" +#define GDM_REMOTE_DISPLAY_FACTORY_OBJECT_PATH "/org/gnome/DisplayManager/RemoteDisplayFactory" #endif /* GRD_PRIVATE_H */ diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c index 39a83d61..9950b4f6 100644 --- a/src/grd-rdp-server.c +++ b/src/grd-rdp-server.c @@ -19,6 +19,7 @@ #include "config.h" +#include "grd-rdp-routing-token.h" #include "grd-rdp-server.h" #include @@ -58,6 +59,8 @@ struct _GrdRdpServer GList *stopped_sessions; guint cleanup_sessions_idle_id; + GCancellable *cancellable; + GrdContext *context; GrdHwAccelNvidia *hwaccel_nvidia; }; @@ -128,6 +131,76 @@ on_session_stopped (GrdSession *session, } } +static void +on_session_post_connect (GrdSessionRdp *session_rdp, + GrdRdpServer *rdp_server) +{ + g_signal_emit (rdp_server, signals[INCOMING_NEW_CONNECTION], 0, session_rdp); +} + +static void +on_routing_token_peeked (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GrdRdpServer *rdp_server; + GSocketConnection *connection; + GrdSessionRdp *session_rdp; + GCancellable *server_cancellable; + g_autofree char *routing_token = NULL; + g_autoptr (GError) error = NULL; + + routing_token = grd_routing_token_peek_finish (result, + &rdp_server, + &connection, + &server_cancellable, + &error); + if (g_cancellable_is_cancelled (server_cancellable)) + return; + + if (error) + { + g_warning ("Error peeking routing token: %s", error->message); + return; + } + + if (routing_token) + { + g_signal_emit (rdp_server, signals[INCOMING_REDIRECTED_CONNECTION], + 0, routing_token, connection); + } + else + { + if (!(session_rdp = grd_session_rdp_new (rdp_server, connection, + rdp_server->hwaccel_nvidia))) + return; + + rdp_server->sessions = g_list_append (rdp_server->sessions, session_rdp); + + g_signal_connect (session_rdp, "stopped", + G_CALLBACK (on_session_stopped), + rdp_server); + + g_signal_connect (session_rdp, "post-connected", + G_CALLBACK (on_session_post_connect), + rdp_server); + } +} + +static gboolean +on_incoming_as_system_headless (GSocketService *service, + GSocketConnection *connection) +{ + GrdRdpServer *rdp_server = GRD_RDP_SERVER (service); + + grd_routing_token_peek_async (rdp_server, + connection, + rdp_server->cancellable, + on_routing_token_peeked); + + return TRUE; +} + static gboolean on_incoming (GSocketService *service, GSocketConnection *connection) @@ -155,6 +228,7 @@ grd_rdp_server_start (GrdRdpServer *rdp_server, GError **error) { GrdSettings *settings = grd_context_get_settings (rdp_server->context); + GrdRuntimeMode runtime_mode = grd_context_get_runtime_mode (rdp_server->context); uint16_t rdp_port; uint16_t selected_rdp_port = 0; gboolean negotiate_port; @@ -174,7 +248,20 @@ grd_rdp_server_start (GrdRdpServer *rdp_server, g_assert (selected_rdp_port != 0); - g_signal_connect (rdp_server, "incoming", G_CALLBACK (on_incoming), NULL); + switch (runtime_mode) + { + case GRD_RUNTIME_MODE_SCREEN_SHARE: + case GRD_RUNTIME_MODE_HEADLESS: + g_signal_connect (rdp_server, "incoming", G_CALLBACK (on_incoming), NULL); + break; + case GRD_RUNTIME_MODE_SYSTEM: + g_signal_connect (rdp_server, "incoming", + G_CALLBACK (on_incoming_as_system_headless), NULL); + + g_assert (!rdp_server->cancellable); + rdp_server->cancellable = g_cancellable_new (); + break; + } rdp_server_iface = grd_context_get_rdp_server_interface (rdp_server->context); grd_dbus_remote_desktop_rdp_server_set_enabled (rdp_server_iface, TRUE); @@ -206,6 +293,12 @@ grd_rdp_server_stop (GrdRdpServer *rdp_server) g_clear_handle_id (&rdp_server->cleanup_sessions_idle_id, g_source_remove); grd_rdp_server_cleanup_stopped_sessions (rdp_server); + if (rdp_server->cancellable) + { + g_cancellable_cancel (rdp_server->cancellable); + g_clear_object (&rdp_server->cancellable); + } + g_clear_object (&rdp_server->hwaccel_nvidia); } diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c index b3a80b73..087615cd 100644 --- a/src/grd-session-rdp.c +++ b/src/grd-session-rdp.c @@ -564,6 +564,9 @@ grd_session_rdp_notify_error (GrdSessionRdp *session_rdp, case GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED: session_rdp->rdp_error_info = ERRINFO_GRAPHICS_SUBSYSTEM_FAILED; break; + case GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION: + session_rdp->rdp_error_info = ERRINFO_CB_CONNECTION_CANCELLED; + break; } unset_rdp_peer_flag (session_rdp, RDP_PEER_ACTIVATED); @@ -1645,6 +1648,7 @@ get_max_monitor_count (GrdSessionRdp *session_rdp) switch (grd_context_get_runtime_mode (context)) { case GRD_RUNTIME_MODE_HEADLESS: + case GRD_RUNTIME_MODE_SYSTEM: return MAX_MONITOR_COUNT_HEADLESS; case GRD_RUNTIME_MODE_SCREEN_SHARE: return MAX_MONITOR_COUNT_SCREEN_SHARE; @@ -1780,13 +1784,15 @@ notify_post_connected (gpointer user_data) { GrdSessionRdp *session_rdp = user_data; SessionMetrics *session_metrics = &session_rdp->session_metrics; + GrdContext *grd_context = grd_session_get_context (GRD_SESSION (session_rdp)); g_mutex_lock (&session_rdp->notify_post_connected_mutex); session_rdp->notify_post_connected_source_id = 0; g_mutex_unlock (&session_rdp->notify_post_connected_mutex); session_metrics->rd_session_start_init_us = g_get_monotonic_time (); - grd_session_start (GRD_SESSION (session_rdp)); + if (grd_context_get_runtime_mode (grd_context) != GRD_RUNTIME_MODE_SYSTEM) + grd_session_start (GRD_SESSION (session_rdp)); g_signal_emit (session_rdp, signals[POST_CONNECTED], 0); } diff --git a/src/grd-session-rdp.h b/src/grd-session-rdp.h index 6ea8cecf..f7615bed 100644 --- a/src/grd-session-rdp.h +++ b/src/grd-session-rdp.h @@ -39,6 +39,7 @@ typedef enum _GrdSessionRdpError GRD_SESSION_RDP_ERROR_BAD_MONITOR_DATA, GRD_SESSION_RDP_ERROR_CLOSE_STACK_ON_DRIVER_FAILURE, GRD_SESSION_RDP_ERROR_GRAPHICS_SUBSYSTEM_FAILED, + GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION, } GrdSessionRdpError; typedef enum _GrdRdpChannel diff --git a/src/grd-settings-system.c b/src/grd-settings-system.c index 2bee31ba..f471a654 100644 --- a/src/grd-settings-system.c +++ b/src/grd-settings-system.c @@ -55,6 +55,7 @@ GrdSettingsSystem * grd_settings_system_new (void) { return g_object_new (GRD_TYPE_SETTINGS_SYSTEM, + "runtime-mode", GRD_RUNTIME_MODE_SYSTEM, "rdp-enabled", TRUE, "rdp-view-only", FALSE, "rdp-screen-share-mode", GRD_RDP_SCREEN_SHARE_MODE_EXTEND, diff --git a/src/grd-settings-user.c b/src/grd-settings-user.c index c430a889..7ca3b3c8 100644 --- a/src/grd-settings-user.c +++ b/src/grd-settings-user.c @@ -104,6 +104,8 @@ grd_settings_user_constructed (GObject *object) settings, "vnc-screen-share-mode", G_SETTINGS_BIND_DEFAULT); break; + case GRD_RUNTIME_MODE_SYSTEM: + g_assert_not_reached (); } G_OBJECT_CLASS (grd_settings_user_parent_class)->constructed (object); diff --git a/src/grd-settings.c b/src/grd-settings.c index 36618e24..31e927ac 100644 --- a/src/grd-settings.c +++ b/src/grd-settings.c @@ -279,6 +279,7 @@ create_credentials (GrdRuntimeMode runtime_mode) switch (runtime_mode) { case GRD_RUNTIME_MODE_HEADLESS: + case GRD_RUNTIME_MODE_SYSTEM: return create_headless_credentials (); case GRD_RUNTIME_MODE_SCREEN_SHARE: return GRD_CREDENTIALS (grd_credentials_libsecret_new ()); diff --git a/src/meson.build b/src/meson.build index 04fc209b..27f44e54 100644 --- a/src/meson.build +++ b/src/meson.build @@ -80,6 +80,8 @@ if have_rdp daemon_sources += files([ 'grd-clipboard-rdp.c', 'grd-clipboard-rdp.h', + 'grd-daemon-system.c', + 'grd-daemon-system.h', 'grd-hwaccel-nvidia.c', 'grd-hwaccel-nvidia.h', 'grd-rdp-audio-input.c', @@ -195,6 +197,13 @@ gen_daemon_sources += gnome.gdbus_codegen('grd-dbus-remote-desktop', 'org.gnome.RemoteDesktop.xml', interface_prefix: 'org.gnome.RemoteDesktop.', namespace: 'GrdDBusRemoteDesktop') +if have_rdp + gen_daemon_sources += gnome.gdbus_codegen('grd-dbus-gdm', + 'org.gnome.DisplayManager.xml', + object_manager: true, + interface_prefix: 'org.gnome.DisplayManager.', + namespace: 'GrdDBusGdm') +endif gen_enum_types = gnome.mkenums('grd-enum-types', h_template: 'grd-enum-types.h.in', diff --git a/src/org.gnome.DisplayManager.xml b/src/org.gnome.DisplayManager.xml new file mode 100644 index 00000000..65cbd49b --- /dev/null +++ b/src/org.gnome.DisplayManager.xml @@ -0,0 +1,8 @@ + + + + + + + + -- GitLab From f394c24acaaaf0c5f9530e9f2445cc29b396dc17 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Fri, 24 Nov 2023 10:58:24 +0100 Subject: [PATCH 12/27] daemon: Expose RDP-server and daemon ifaces at system dbus too Now that the system daemon exists, expose its settings too. GApplication doesn't use its dbus backend when running as a system service, so get the dbus connection directly instead of relying on GApplication. Also, don't expose the VNC-server iface on non user-daemons. --- src/grd-daemon.c | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/grd-daemon.c b/src/grd-daemon.c index 46bf7ba7..79bf0370 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -71,6 +71,7 @@ typedef struct _GrdDaemonPrivate GrdContext *context; + GDBusConnection *connection; GrdDBusRemoteDesktopOrgGnomeRemoteDesktop *remote_desktop_interface; #ifdef HAVE_RDP @@ -114,8 +115,6 @@ export_remote_desktop_interface (GrdDaemon *daemon) { GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); GrdRuntimeMode runtime_mode = grd_context_get_runtime_mode (priv->context); - GDBusConnection *connection = g_application_get_dbus_connection ( - G_APPLICATION (daemon)); priv->remote_desktop_interface = grd_dbus_remote_desktop_org_gnome_remote_desktop_skeleton_new (); @@ -138,7 +137,7 @@ export_remote_desktop_interface (GrdDaemon *daemon) g_dbus_interface_skeleton_export ( G_DBUS_INTERFACE_SKELETON (priv->remote_desktop_interface), - connection, + priv->connection, REMOTE_DESKTOP_OBJECT_PATH, NULL); } @@ -161,8 +160,6 @@ export_rdp_server_interface (GrdDaemon *daemon) GrdDBusRemoteDesktopRdpServer *rdp_server_interface; GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); GrdSettings *settings = grd_context_get_settings (priv->context); - GDBusConnection *connection = g_application_get_dbus_connection ( - G_APPLICATION (daemon)); rdp_server_interface = grd_dbus_remote_desktop_rdp_server_skeleton_new (); @@ -184,7 +181,7 @@ export_rdp_server_interface (GrdDaemon *daemon) g_dbus_interface_skeleton_export ( G_DBUS_INTERFACE_SKELETON (rdp_server_interface), - connection, + priv->connection, GRD_RDP_SERVER_OBJECT_PATH, NULL); @@ -284,8 +281,6 @@ export_vnc_server_interface (GrdDaemon *daemon) GrdDBusRemoteDesktopVncServer *vnc_server_interface; GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); GrdSettings *settings = grd_context_get_settings (priv->context); - GDBusConnection *connection = g_application_get_dbus_connection ( - G_APPLICATION (daemon)); vnc_server_interface = grd_dbus_remote_desktop_vnc_server_skeleton_new (); @@ -306,7 +301,7 @@ export_vnc_server_interface (GrdDaemon *daemon) g_dbus_interface_skeleton_export ( G_DBUS_INTERFACE_SKELETON (vnc_server_interface), - connection, + priv->connection, GRD_VNC_SERVER_OBJECT_PATH, NULL); @@ -370,7 +365,8 @@ export_services_status (GrdDaemon *daemon) export_rdp_server_interface (daemon); #endif #ifdef HAVE_VNC - export_vnc_server_interface (daemon); + if (GRD_IS_DAEMON_USER (daemon)) + export_vnc_server_interface (daemon); #endif } @@ -383,7 +379,8 @@ unexport_services_status (GrdDaemon *daemon) unexport_rdp_server_interface (daemon); #endif #ifdef HAVE_VNC - unexport_vnc_server_interface (daemon); + if (GRD_IS_DAEMON_USER (daemon)) + unexport_vnc_server_interface (daemon); #endif } @@ -617,6 +614,26 @@ grd_daemon_init (GrdDaemon *daemon) priv->cancellable = g_cancellable_new (); } +static GDBusConnection * +get_daemon_dbus_connection (GrdDaemon *daemon) +{ + g_autoptr (GDBusConnection) connection = NULL; + g_autoptr (GError) error = NULL; + + if (GRD_IS_DAEMON_SYSTEM (daemon)) + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + else + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (!connection) + { + g_warning ("Failing acquiring dbus connection: %s", error->message); + return NULL; + } + + return g_steal_pointer (&connection); +} + static void grd_daemon_startup (GApplication *app) { @@ -624,6 +641,7 @@ grd_daemon_startup (GApplication *app) GrdDaemonPrivate *priv = grd_daemon_get_instance_private (daemon); GrdSettings *settings = grd_context_get_settings (priv->context); + priv->connection = get_daemon_dbus_connection (daemon); export_services_status (daemon); #ifdef HAVE_RDP @@ -661,6 +679,7 @@ grd_daemon_shutdown (GApplication *app) grd_daemon_disable_services (daemon); unexport_services_status (daemon); + g_clear_object (&priv->connection); grd_context_set_mutter_remote_desktop_proxy (priv->context, NULL); g_clear_handle_id (&priv->mutter_remote_desktop_watch_name_id, g_bus_unwatch_name); -- GitLab From e5d41faef5b64e23353779dfeb54f2c55851da09 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Fri, 11 Aug 2023 12:13:40 +0200 Subject: [PATCH 13/27] daemon-system: Register handover iface when a new RemoteDisplay is added The system daemon connects to the ObjectManagerServer of gdm to know when a new display object is added. When a display interface added, it checks: - If it is a RemoteDisplay - If its remote_id corresponds to an id of any of the stored RemoteClients If both conditions are true, a handover interface is registered with the session_id of the RemoteDisplay (i.e. /org/gnome/RemoteDesktop/Rdp/Handovers/session) and that interface proxy is stored on the RemoteClient, which has the looked up remote_id. The handover interface will be implemented in next commits. --- data/meson.build | 7 + data/org.gnome.RemoteDesktop.conf.in | 24 ++ meson.build | 2 + meson_options.txt | 5 + src/grd-daemon-system.c | 331 ++++++++++++++++++++++++++- src/grd-private.h | 3 + src/org.gnome.DisplayManager.xml | 4 + src/org.gnome.RemoteDesktop.xml | 11 + 8 files changed, 383 insertions(+), 4 deletions(-) create mode 100644 data/org.gnome.RemoteDesktop.conf.in diff --git a/data/meson.build b/data/meson.build index feb8d2b7..a93b547e 100644 --- a/data/meson.build +++ b/data/meson.build @@ -10,4 +10,11 @@ if have_rdp install_mode: ['rw-r--r--', grd_username, grd_username], install_dir: grd_confdir, ) + + configure_file(input: 'org.gnome.RemoteDesktop.conf.in', + output: 'org.gnome.RemoteDesktop.conf', + configuration: { + 'GRD_USERNAME': grd_username, + }, + install_dir: dbus_sys_dir) endif diff --git a/data/org.gnome.RemoteDesktop.conf.in b/data/org.gnome.RemoteDesktop.conf.in new file mode 100644 index 00000000..c9de73f7 --- /dev/null +++ b/data/org.gnome.RemoteDesktop.conf.in @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/meson.build b/meson.build index eada64c1..97b0c96a 100644 --- a/meson.build +++ b/meson.build @@ -66,6 +66,7 @@ libexecdir = join_paths(prefix, get_option('libexecdir')) datadir = join_paths(prefix, get_option('datadir')) mandir = join_paths(prefix, get_option('mandir')) schemadir = join_paths(datadir, 'glib-2.0', 'schemas') +dbus_sys_dir = (get_option('dbus_sys_dir') != '') ? get_option('dbus_sys_dir') : join_paths(datadir, 'dbus-1', 'system.d') grd_datadir = join_paths(datadir, 'gnome-remote-desktop') @@ -134,6 +135,7 @@ output = [ ' datadir...................... ' + datadir, ' systemd user unit dir........ ' + servicedir, ' GSettings schema dir......... ' + schemadir, + ' System DBus dir.............. ' + dbus_sys_dir, '', ' Backends:', '', diff --git a/meson_options.txt b/meson_options.txt index 973df28b..e48d5f52 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -42,3 +42,8 @@ option('user', type: 'string', value: 'gnome-remote-desktop', description: 'Username for the GNOME Remote Desktop system service') + +option('dbus_sys_dir', + type: 'string', + value: '', + description: 'System D-Bus conf directory') diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c index 72a337cb..59801d68 100644 --- a/src/grd-daemon-system.c +++ b/src/grd-daemon-system.c @@ -27,6 +27,7 @@ #include "grd-context.h" #include "grd-daemon.h" #include "grd-dbus-gdm.h" +#include "grd-dbus-remote-desktop.h" #include "grd-private.h" #include "grd-rdp-server.h" #include "grd-session-rdp.h" @@ -38,6 +39,12 @@ typedef struct char *id; GrdSession *session; + + GDBusObjectSkeleton *handover_skeleton; + GrdDBusRemoteDesktopRdpHandover *handover_interface; + char *dbus_object_path; + + gboolean is_registered; } GrdRemoteClient; struct _GrdDaemonSystem @@ -45,6 +52,10 @@ struct _GrdDaemonSystem GrdDaemon parent; GrdDBusGdmRemoteDisplayFactory *remote_display_factory_proxy; + GDBusObjectManager *display_objects; + + unsigned int system_grd_name_id; + GDBusObjectManagerServer *handover_manager_server; GHashTable *remote_clients; }; @@ -55,17 +66,29 @@ static gboolean grd_daemon_system_is_ready (GrdDaemon *daemon) { GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (daemon); - g_autofree const char *gdm_remote_display_factory_name_owner = NULL; GDBusProxy *remote_display_factory_proxy = G_DBUS_PROXY (daemon_system->remote_display_factory_proxy); + GDBusObjectManagerClient *display_objects_manager = + G_DBUS_OBJECT_MANAGER_CLIENT (daemon_system->display_objects); + g_autofree const char *gdm_remote_display_factory_name_owner = NULL; + g_autofree const char *gdm_display_objects_name_owner = NULL; + g_autoptr (GDBusConnection) manager_connection = NULL; - if (!daemon_system->remote_display_factory_proxy) + if (!daemon_system->remote_display_factory_proxy || + !daemon_system->display_objects || + !daemon_system->handover_manager_server) return FALSE; gdm_remote_display_factory_name_owner = g_dbus_proxy_get_name_owner ( remote_display_factory_proxy); - - if (!gdm_remote_display_factory_name_owner) + gdm_display_objects_name_owner = g_dbus_object_manager_client_get_name_owner ( + display_objects_manager); + manager_connection = g_dbus_object_manager_server_get_connection ( + daemon_system->handover_manager_server); + + if (!gdm_remote_display_factory_name_owner || + !gdm_display_objects_name_owner || + !manager_connection) return FALSE; return TRUE; @@ -78,10 +101,67 @@ get_id_from_routing_token (uint32_t routing_token) routing_token); } +static void +register_handover_iface (GrdRemoteClient *remote_client, + const char *session_id) +{ + GrdDaemonSystem *daemon_system = remote_client->daemon_system; + + if (remote_client->is_registered) + return; + + remote_client->dbus_object_path = + g_strdup_printf ("%s/session%s", + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, + session_id); + + remote_client->handover_skeleton = + g_dbus_object_skeleton_new (remote_client->dbus_object_path); + + g_dbus_object_manager_server_export (daemon_system->handover_manager_server, + remote_client->handover_skeleton); + + g_dbus_object_skeleton_add_interface ( + remote_client->handover_skeleton, + G_DBUS_INTERFACE_SKELETON (remote_client->handover_interface)); + + remote_client->is_registered = TRUE; + + g_debug ("[DaemonSystem] Registered handover on path %s", + remote_client->dbus_object_path); +} + +static void +unregister_handover_iface (GrdRemoteClient *remote_client) +{ + GrdDaemonSystem *daemon_system = remote_client->daemon_system; + + if (!remote_client->is_registered) + return; + + g_dbus_object_skeleton_remove_interface ( + remote_client->handover_skeleton, + G_DBUS_INTERFACE_SKELETON (remote_client->handover_interface)); + + g_dbus_object_manager_server_unexport ( + daemon_system->handover_manager_server, + remote_client->dbus_object_path); + + g_clear_object (&remote_client->handover_skeleton); + g_clear_pointer (&remote_client->dbus_object_path, g_free); + + remote_client->is_registered = FALSE; +} + static void grd_remote_client_free (GrdRemoteClient *remote_client) { + unregister_handover_iface (remote_client); + g_clear_pointer (&remote_client->id, g_free); + g_clear_object (&remote_client->handover_skeleton); + g_clear_object (&remote_client->handover_interface); + g_clear_pointer (&remote_client->dbus_object_path, g_free); g_free (remote_client); } @@ -122,11 +202,15 @@ remote_client_new (GrdDaemonSystem *daemon_system, remote_client = g_new0 (GrdRemoteClient, 1); remote_client->id = get_next_available_id (daemon_system); remote_client->daemon_system = daemon_system; + remote_client->is_registered = FALSE; remote_client->session = session; g_object_weak_ref (G_OBJECT (session), (GWeakNotify) session_disposed, remote_client); + remote_client->handover_interface = + grd_dbus_remote_desktop_rdp_handover_skeleton_new (); + return remote_client; } @@ -206,6 +290,38 @@ on_rdp_server_stopped (GrdDaemonSystem *daemon_system) daemon_system); } +static void +on_system_grd_bus_acquired (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + GrdDaemonSystem *daemon_system = user_data; + + g_debug ("[DaemonSystem] Now on system bus"); + + g_dbus_object_manager_server_set_connection ( + daemon_system->handover_manager_server, + connection); + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); +} + +static void +on_system_grd_name_acquired (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + g_debug ("[DaemonSystem] Owned %s name", name); +} + +static void +on_system_grd_name_lost (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + g_debug ("[DaemonSystem] Lost owned %s name", name); +} + static void on_remote_display_factory_name_owner_changed (GrdDBusGdmRemoteDisplayFactory *remote_display_factory_proxy, GParamSpec *pspec, @@ -251,6 +367,187 @@ on_remote_display_factory_proxy_acquired (GObject *object, grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); } +static void +on_gdm_remote_display_session_id_changed (GrdDBusGdmRemoteDisplay *remote_display, + GParamSpec *pspec, + GrdRemoteClient *remote_client) +{ + const char *session_id; + + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); + + g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " + "and session: %s", + remote_client->id, + session_id); + + register_handover_iface (remote_client, session_id); + + g_signal_handlers_disconnect_by_func ( + remote_display, + G_CALLBACK (on_gdm_remote_display_session_id_changed), + remote_client); +} + +static void +register_handover_for_display (GrdDaemonSystem *daemon_system, + GrdDBusGdmRemoteDisplay *remote_display) +{ + GrdRemoteClient *remote_client; + const char *session_id; + const char *remote_id; + + remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); + if (!g_hash_table_lookup_extended (daemon_system->remote_clients, + remote_id, NULL, + (gpointer *) &remote_client)) + { + g_debug ("[DaemonSystem] GDM added a new remote display with a remote " + "id %s we didn't know about", remote_id); + return; + } + + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); + if (!session_id || strcmp (session_id, "") == 0) + { + g_signal_connect (G_OBJECT (remote_display), + "notify::session-id", + G_CALLBACK (on_gdm_remote_display_session_id_changed), + remote_client); + return; + } + + g_debug ("[DaemonSystem] GDM added a new remote display with remote id: %s " + "and session: %s", + remote_client->id, + session_id); + + register_handover_iface (remote_client, session_id); +} + +static void +unregister_handover_for_display (GrdDaemonSystem *daemon_system, + GrdDBusGdmRemoteDisplay *remote_display) +{ + GrdRemoteClient *remote_client; + const char *remote_id; + + remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); + if (!g_hash_table_lookup_extended (daemon_system->remote_clients, + remote_id, NULL, + (gpointer *) &remote_client)) + { + g_debug ("[DaemonSystem] GDM removed a remote display with remote id " + "%s we didn't know about", remote_id); + return; + } + + unregister_handover_iface (remote_client); +} + +static void +on_gdm_object_remote_display_interface_changed (GrdDaemonSystem *daemon_system, + GParamSpec *param_spec, + GrdDBusGdmObject *object) +{ + GrdDBusGdmRemoteDisplay *remote_display; + + remote_display = grd_dbus_gdm_object_peek_remote_display (object); + if (remote_display) + register_handover_for_display (daemon_system, remote_display); +} + +static void +on_gdm_object_added (GrdDaemonSystem *daemon_system, + GrdDBusGdmObject *object) +{ + GrdDBusGdmRemoteDisplay *remote_display; + + remote_display = grd_dbus_gdm_object_peek_remote_display (object); + if (remote_display) + { + register_handover_for_display (daemon_system, remote_display); + return; + } + + g_signal_connect_object ( + G_OBJECT (object), + "notify::remote-display", + G_CALLBACK (on_gdm_object_remote_display_interface_changed), + daemon_system, + G_CONNECT_SWAPPED); +} + +static void +on_gdm_object_removed (GrdDaemonSystem *daemon_system, + GrdDBusGdmObject *object) +{ + GrdDBusGdmRemoteDisplay *remote_display; + + remote_display = grd_dbus_gdm_object_peek_remote_display (object); + if (remote_display) + unregister_handover_for_display (daemon_system, remote_display); + + g_signal_handlers_disconnect_by_func ( + G_OBJECT (object), + G_CALLBACK (on_gdm_object_remote_display_interface_changed), + daemon_system); +} + +static void +on_gdm_display_objects_name_owner_changed (GDBusObjectManager *display_objects, + GParamSpec *pspec, + GrdDaemonSystem *daemon_system) +{ + g_autofree const char *name_owner = NULL; + + g_object_get (G_OBJECT (display_objects), "name-owner", &name_owner, NULL); + if (!name_owner) + { + grd_daemon_disable_services (GRD_DAEMON (daemon_system)); + return; + } + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); +} + +static void +on_gdm_object_manager_client_acquired (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonSystem *daemon_system = user_data; + g_autoptr (GError) error = NULL; + + daemon_system->display_objects = + grd_dbus_gdm_object_manager_client_new_for_bus_finish (result, &error); + if (!daemon_system->display_objects) + { + g_warning ("[DaemonSystem] Error connecting to display manager: %s", + error->message); + return; + } + + g_signal_connect_object (daemon_system->display_objects, + "object-added", + G_CALLBACK (on_gdm_object_added), + daemon_system, + G_CONNECT_SWAPPED); + + g_signal_connect_object (daemon_system->display_objects, + "object-removed", + G_CALLBACK (on_gdm_object_removed), + daemon_system, + G_CONNECT_SWAPPED); + + g_signal_connect (G_OBJECT (daemon_system->display_objects), + "notify::name-owner", + G_CALLBACK (on_gdm_display_objects_name_owner_changed), + daemon_system); + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); +} + GrdDaemonSystem * grd_daemon_system_new (GError **error) { @@ -282,6 +579,18 @@ grd_daemon_system_startup (GApplication *app) GCancellable *cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_system)); + daemon_system->handover_manager_server = + g_dbus_object_manager_server_new (REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH); + + daemon_system->system_grd_name_id = + g_bus_own_name (G_BUS_TYPE_SYSTEM, + REMOTE_DESKTOP_BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_system_grd_bus_acquired, + on_system_grd_name_acquired, + on_system_grd_name_lost, + daemon_system, NULL); + grd_dbus_gdm_remote_display_factory_proxy_new_for_bus ( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, @@ -291,6 +600,15 @@ grd_daemon_system_startup (GApplication *app) on_remote_display_factory_proxy_acquired, daemon_system); + grd_dbus_gdm_object_manager_client_new_for_bus ( + G_BUS_TYPE_SYSTEM, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + GDM_BUS_NAME, + GDM_OBJECT_MANAGER_OBJECT_PATH, + cancellable, + on_gdm_object_manager_client_acquired, + daemon_system); + g_signal_connect (daemon_system, "rdp-server-started", G_CALLBACK (on_rdp_server_started), NULL); @@ -307,8 +625,13 @@ grd_daemon_system_shutdown (GApplication *app) g_clear_pointer (&daemon_system->remote_clients, g_hash_table_unref); + g_clear_object (&daemon_system->display_objects); g_clear_object (&daemon_system->remote_display_factory_proxy); + g_clear_object (&daemon_system->handover_manager_server); + g_clear_handle_id (&daemon_system->system_grd_name_id, + g_bus_unown_name); + G_APPLICATION_CLASS (grd_daemon_system_parent_class)->shutdown (app); } diff --git a/src/grd-private.h b/src/grd-private.h index ba614f67..8bc76765 100644 --- a/src/grd-private.h +++ b/src/grd-private.h @@ -26,8 +26,10 @@ #define GRD_DAEMON_USER_APPLICATION_ID "org.gnome.RemoteDesktop.User" #define GRD_DAEMON_HEADLESS_APPLICATION_ID "org.gnome.RemoteDesktop.Headless" #define GRD_DAEMON_SYSTEM_APPLICATION_ID "org.gnome.RemoteDesktop" +#define REMOTE_DESKTOP_BUS_NAME "org.gnome.RemoteDesktop" #define REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/RemoteDesktop" #define REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/org/gnome/RemoteDesktop/Client" +#define REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Handovers" #define GRD_RDP_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Server" #define GRD_VNC_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Vnc/Server" #define MUTTER_REMOTE_DESKTOP_BUS_NAME "org.gnome.Mutter.RemoteDesktop" @@ -36,5 +38,6 @@ #define MUTTER_SCREEN_CAST_OBJECT_PATH "/org/gnome/Mutter/ScreenCast" #define GDM_BUS_NAME "org.gnome.DisplayManager" #define GDM_REMOTE_DISPLAY_FACTORY_OBJECT_PATH "/org/gnome/DisplayManager/RemoteDisplayFactory" +#define GDM_OBJECT_MANAGER_OBJECT_PATH "/org/gnome/DisplayManager/Displays" #endif /* GRD_PRIVATE_H */ diff --git a/src/org.gnome.DisplayManager.xml b/src/org.gnome.DisplayManager.xml index 65cbd49b..73e073c2 100644 --- a/src/org.gnome.DisplayManager.xml +++ b/src/org.gnome.DisplayManager.xml @@ -1,5 +1,9 @@ + + + + diff --git a/src/org.gnome.RemoteDesktop.xml b/src/org.gnome.RemoteDesktop.xml index ed8c4077..ac0874ec 100644 --- a/src/org.gnome.RemoteDesktop.xml +++ b/src/org.gnome.RemoteDesktop.xml @@ -52,4 +52,15 @@ + + + + -- GitLab From 6fb6c8d350abf6f60a1c18a8852dd1a2d153a606 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Fri, 11 Aug 2023 14:10:36 +0200 Subject: [PATCH 14/27] daemon-system: Add dispatcher interface to find the handover object All handover-daemons will request from this dispatcher the dbus object path in order to be able to get their correspondent handover interface. The handover interface is registered based on the session_id as mentioned in the previous commit, so just knowing the session_id of the daemon-handover is enough. --- data/org.gnome.RemoteDesktop.conf.in | 2 + meson.build | 1 + src/grd-daemon-system.c | 194 ++++++++++++++++++++++++++- src/grd-daemon-utils.c | 163 ++++++++++++++++++++++ src/grd-daemon-utils.h | 41 ++++++ src/grd-private.h | 1 + src/meson.build | 3 + src/org.gnome.RemoteDesktop.xml | 23 ++++ 8 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 src/grd-daemon-utils.c create mode 100644 src/grd-daemon-utils.h diff --git a/data/org.gnome.RemoteDesktop.conf.in b/data/org.gnome.RemoteDesktop.conf.in index c9de73f7..0e642dcf 100644 --- a/data/org.gnome.RemoteDesktop.conf.in +++ b/data/org.gnome.RemoteDesktop.conf.in @@ -11,6 +11,8 @@ + = 0.3.49') systemd_dep = dependency('systemd', required: get_option('systemd')) tss2_esys_dep = dependency('tss2-esys') diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c index 59801d68..2a40594f 100644 --- a/src/grd-daemon-system.c +++ b/src/grd-daemon-system.c @@ -24,8 +24,11 @@ #include "grd-daemon-system.h" +#include + #include "grd-context.h" #include "grd-daemon.h" +#include "grd-daemon-utils.h" #include "grd-dbus-gdm.h" #include "grd-dbus-remote-desktop.h" #include "grd-private.h" @@ -55,6 +58,7 @@ struct _GrdDaemonSystem GDBusObjectManager *display_objects; unsigned int system_grd_name_id; + GrdDBusRemoteDesktopRdpDispatcher *dispatcher_skeleton; GDBusObjectManagerServer *handover_manager_server; GHashTable *remote_clients; @@ -70,13 +74,17 @@ grd_daemon_system_is_ready (GrdDaemon *daemon) G_DBUS_PROXY (daemon_system->remote_display_factory_proxy); GDBusObjectManagerClient *display_objects_manager = G_DBUS_OBJECT_MANAGER_CLIENT (daemon_system->display_objects); + GDBusInterfaceSkeleton *dispatcher_skeleton = + G_DBUS_INTERFACE_SKELETON (daemon_system->dispatcher_skeleton); g_autofree const char *gdm_remote_display_factory_name_owner = NULL; g_autofree const char *gdm_display_objects_name_owner = NULL; g_autoptr (GDBusConnection) manager_connection = NULL; + GDBusConnection *dispatcher_connection = NULL; if (!daemon_system->remote_display_factory_proxy || !daemon_system->display_objects || - !daemon_system->handover_manager_server) + !daemon_system->handover_manager_server || + !daemon_system->dispatcher_skeleton) return FALSE; gdm_remote_display_factory_name_owner = g_dbus_proxy_get_name_owner ( @@ -85,10 +93,13 @@ grd_daemon_system_is_ready (GrdDaemon *daemon) display_objects_manager); manager_connection = g_dbus_object_manager_server_get_connection ( daemon_system->handover_manager_server); + dispatcher_connection = g_dbus_interface_skeleton_get_connection ( + dispatcher_skeleton); if (!gdm_remote_display_factory_name_owner || !gdm_display_objects_name_owner || - !manager_connection) + !manager_connection || + !dispatcher_connection) return FALSE; return TRUE; @@ -290,6 +301,168 @@ on_rdp_server_stopped (GrdDaemonSystem *daemon_system) daemon_system); } +static char * +get_session_id_of_sender (GDBusConnection *connection, + const char *name, + GCancellable *cancellable, + GError **error) +{ + pid_t pid = 0; + uid_t uid = 0; + gboolean success; + char *session_id = NULL; + + success = grd_get_pid_of_sender_sync (connection, + name, + &pid, + cancellable, + error); + if (!success) + return NULL; + + session_id = grd_get_session_id_from_pid (pid); + if (session_id) + return session_id; + + success = grd_get_uid_of_sender_sync (connection, + name, + &uid, + cancellable, + error); + if (!success) + return NULL; + + session_id = grd_get_session_id_from_uid (uid); + if (!session_id) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "Could not find a session for user %d", + (int) uid); + } + + return session_id; +} + +static char * +get_handover_object_path_for_call (GrdDaemonSystem *daemon_system, + GDBusMethodInvocation *invocation, + GError **error) +{ + g_autofree char *session_id = NULL; + g_autofree char *object_path = NULL; + g_autoptr (GDBusObject) object = NULL; + GDBusConnection *connection = NULL; + const char *sender = NULL; + GCancellable *cancellable; + + connection = g_dbus_method_invocation_get_connection (invocation); + sender = g_dbus_method_invocation_get_sender (invocation); + cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_system)); + + session_id = get_session_id_of_sender (connection, + sender, + cancellable, + error); + if (!session_id) + return NULL; + + object_path = g_strdup_printf ("%s/session%s", + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, + session_id); + + object = g_dbus_object_manager_get_object ( + G_DBUS_OBJECT_MANAGER (daemon_system->handover_manager_server), + object_path); + if (!object) + { + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_OBJECT, + "No connection waiting for handover"); + return NULL; + } + + return g_steal_pointer (&object_path); +} + +static void +get_handover_object_path_for_call_in_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GDBusMethodInvocation *invocation = task_data; + GrdDaemonSystem *daemon_system = source_object; + GError *error = NULL; + char *object_path; + + object_path = get_handover_object_path_for_call (daemon_system, + invocation, + &error); + if (error) + { + g_task_return_error (task, error); + return; + } + + g_task_return_pointer (task, object_path, g_free); +} + +static void +get_handover_object_path_for_call_async (gpointer source_object, + gpointer user_data, + GAsyncReadyCallback on_finished_callback) +{ + GrdDaemonSystem *daemon_system = source_object; + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_system)); + GTask *task; + + task = g_task_new (daemon_system, cancellable, on_finished_callback, user_data); + g_task_set_task_data (task, user_data, NULL); + g_task_run_in_thread (task, get_handover_object_path_for_call_in_thread); + g_object_unref (task); +} + +static void +on_get_handover_object_path_finished (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonSystem *daemon_system = GRD_DAEMON_SYSTEM (source_object); + GDBusMethodInvocation *invocation = user_data; + g_autofree char *object_path = NULL; + g_autoptr (GError) error = NULL; + + object_path = g_task_propagate_pointer (G_TASK (result), &error); + if (error) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } + + grd_dbus_remote_desktop_rdp_dispatcher_complete_request_handover ( + daemon_system->dispatcher_skeleton, + invocation, + object_path); +} + +static gboolean +on_handle_request_handover (GrdDBusRemoteDesktopRdpDispatcher *skeleton, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GrdDaemonSystem *daemon_system = user_data; + + get_handover_object_path_for_call_async (daemon_system, + invocation, + on_get_handover_object_path_finished); + + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + static void on_system_grd_bus_acquired (GDBusConnection *connection, const char *name, @@ -303,6 +476,12 @@ on_system_grd_bus_acquired (GDBusConnection *connection, daemon_system->handover_manager_server, connection); + g_dbus_interface_skeleton_export ( + G_DBUS_INTERFACE_SKELETON (daemon_system->dispatcher_skeleton), + connection, + REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH, + NULL); + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_system)); } @@ -579,6 +758,13 @@ grd_daemon_system_startup (GApplication *app) GCancellable *cancellable = grd_daemon_get_cancellable (GRD_DAEMON (daemon_system)); + daemon_system->dispatcher_skeleton = + grd_dbus_remote_desktop_rdp_dispatcher_skeleton_new (); + g_signal_connect (daemon_system->dispatcher_skeleton, + "handle-request-handover", + G_CALLBACK (on_handle_request_handover), + daemon_system); + daemon_system->handover_manager_server = g_dbus_object_manager_server_new (REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH); @@ -628,6 +814,10 @@ grd_daemon_system_shutdown (GApplication *app) g_clear_object (&daemon_system->display_objects); g_clear_object (&daemon_system->remote_display_factory_proxy); + g_dbus_interface_skeleton_unexport ( + G_DBUS_INTERFACE_SKELETON (daemon_system->dispatcher_skeleton)); + g_clear_object (&daemon_system->dispatcher_skeleton); + g_clear_object (&daemon_system->handover_manager_server); g_clear_handle_id (&daemon_system->system_grd_name_id, g_bus_unown_name); diff --git a/src/grd-daemon-utils.c b/src/grd-daemon-utils.c new file mode 100644 index 00000000..a2299d17 --- /dev/null +++ b/src/grd-daemon-utils.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "config.h" + +#include "grd-daemon-utils.h" + +#include + +gboolean +grd_get_pid_of_sender_sync (GDBusConnection *connection, + const char *name, + pid_t *out_pid, + GCancellable *cancellable, + GError **error) +{ + g_autoptr (GVariant) result = NULL; + uint32_t pid; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + g_assert (out_pid); + + result = g_dbus_connection_call_sync (connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixProcessID", + g_variant_new ("(s)", name), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, error); + if (!result) + return FALSE; + + g_variant_get (result, "(u)", &pid); + + *out_pid = (pid_t) pid; + + return TRUE; +} + +gboolean +grd_get_uid_of_sender_sync (GDBusConnection *connection, + const char *name, + uid_t *out_uid, + GCancellable *cancellable, + GError **error) +{ + g_autoptr (GVariant) result = NULL; + uint32_t uid; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + g_assert (out_uid); + + result = g_dbus_connection_call_sync (connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixUser", + g_variant_new ("(s)", name), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, error); + if (!result) + return FALSE; + + g_variant_get (result, "(u)", &uid); + + *out_uid = (uid_t) uid; + + return TRUE; +} + +char * +grd_get_session_id_from_pid (pid_t pid) +{ + char *session_id = NULL; + int res; + + res = sd_pid_get_session (pid, &session_id); + if (res < 0 && res != -ENODATA) + { + g_warning ("Failed to retrieve session information for " + "pid %d: %s", (int) pid, strerror (-res)); + } + + return g_steal_pointer (&session_id); +} + +static gboolean +grd_sd_session_is_graphical (const char *session_id) +{ + const char * const graphical_session_types[] = { "wayland", "x11", NULL }; + int res; + g_autofree char *type = NULL; + + res = sd_session_get_type (session_id, &type); + if (res < 0) + return FALSE; + + return g_strv_contains (graphical_session_types, type); +} + +static gboolean +grd_sd_session_is_active (const char *session_id) +{ + const char * const active_states[] = { "active", "online", NULL }; + int res; + g_autofree char *state = NULL; + + res = sd_session_get_state (session_id, &state); + if (res < 0) + return FALSE; + + return g_strv_contains (active_states, state); +} + +char * +grd_get_session_id_from_uid (uid_t uid) +{ + g_auto (GStrv) sessions = NULL; + char *session_id = NULL; + int n_sessions; + int i; + + n_sessions = sd_uid_get_sessions (uid, 0, &sessions); + + for (i = n_sessions; i >= 0; i--) + { + if (!grd_sd_session_is_graphical (sessions[i])) + continue; + + if (!grd_sd_session_is_active (sessions[i])) + continue; + + session_id = sessions[i]; + break; + } + + return g_strdup (session_id); +} diff --git a/src/grd-daemon-utils.h b/src/grd-daemon-utils.h new file mode 100644 index 00000000..96a3a458 --- /dev/null +++ b/src/grd-daemon-utils.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef GRD_DAEMON_UTILS_H +#define GRD_DAEMON_UTILS_H + +#include + +gboolean grd_get_pid_of_sender_sync (GDBusConnection *connection, + const char *name, + pid_t *out_pid, + GCancellable *cancellable, + GError **error); + +gboolean grd_get_uid_of_sender_sync (GDBusConnection *connection, + const char *name, + uid_t *out_uid, + GCancellable *cancellable, + GError **error); + +char *grd_get_session_id_from_pid (pid_t pid); + +char *grd_get_session_id_from_uid (uid_t uid); + +#endif /* GRD_DAEMON_UTILS_H */ diff --git a/src/grd-private.h b/src/grd-private.h index 8bc76765..8b5218a3 100644 --- a/src/grd-private.h +++ b/src/grd-private.h @@ -29,6 +29,7 @@ #define REMOTE_DESKTOP_BUS_NAME "org.gnome.RemoteDesktop" #define REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/RemoteDesktop" #define REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/org/gnome/RemoteDesktop/Client" +#define REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Dispatcher" #define REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Handovers" #define GRD_RDP_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Rdp/Server" #define GRD_VNC_SERVER_OBJECT_PATH "/org/gnome/RemoteDesktop/Vnc/Server" diff --git a/src/meson.build b/src/meson.build index 27f44e54..4b7d34e0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,6 +16,7 @@ deps = [ pipewire_dep, libei_dep, libnotify_dep, + libsystemd_dep, epoxy_dep, drm_dep, @@ -82,6 +83,8 @@ if have_rdp 'grd-clipboard-rdp.h', 'grd-daemon-system.c', 'grd-daemon-system.h', + 'grd-daemon-utils.c', + 'grd-daemon-utils.h', 'grd-hwaccel-nvidia.c', 'grd-hwaccel-nvidia.h', 'grd-rdp-audio-input.c', diff --git a/src/org.gnome.RemoteDesktop.xml b/src/org.gnome.RemoteDesktop.xml index ac0874ec..10c1d8a5 100644 --- a/src/org.gnome.RemoteDesktop.xml +++ b/src/org.gnome.RemoteDesktop.xml @@ -52,6 +52,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From d2efa11c60df79b0c7f450bddd1a95cdbccbcf1c Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 14 Aug 2023 11:41:16 +0200 Subject: [PATCH 16/27] Introduce daemon-handover This daemon is running as a user session service. It doesn't listen on any port but will get the RDP client from the daemon-system using the handover interface. Right now it's only using the dispatcher interface to request to the daemon-system what handover object path should use. --- src/grd-context.c | 4 + src/grd-ctl.c | 1 + src/grd-daemon-handover.c | 212 ++++++++++++++++++++++++++++++++++++ src/grd-daemon-handover.h | 34 ++++++ src/grd-daemon-user.c | 1 + src/grd-daemon.c | 18 ++- src/grd-enums.h | 1 + src/grd-private.h | 1 + src/grd-rdp-server.c | 12 +- src/grd-session-rdp.c | 1 + src/grd-settings-handover.c | 1 + src/grd-settings-user.c | 1 + src/grd-settings.c | 3 + src/meson.build | 2 + 14 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 src/grd-daemon-handover.c create mode 100644 src/grd-daemon-handover.h diff --git a/src/grd-context.c b/src/grd-context.c index f1320f14..5413d38b 100644 --- a/src/grd-context.c +++ b/src/grd-context.c @@ -25,6 +25,7 @@ #include "grd-context.h" #include "grd-egl-thread.h" +#include "grd-settings-handover.h" #include "grd-settings-system.h" #include "grd-settings-user.h" @@ -158,6 +159,9 @@ grd_context_new (GrdRuntimeMode runtime_mode, case GRD_RUNTIME_MODE_SYSTEM: context->settings = GRD_SETTINGS (grd_settings_system_new ()); break; + case GRD_RUNTIME_MODE_HANDOVER: + context->settings = GRD_SETTINGS (grd_settings_handover_new ()); + break; } if (!context->settings) diff --git a/src/grd-ctl.c b/src/grd-ctl.c index 89114970..b9296c26 100644 --- a/src/grd-ctl.c +++ b/src/grd-ctl.c @@ -501,6 +501,7 @@ create_settings (GrdRuntimeMode runtime_mode) case GRD_RUNTIME_MODE_HEADLESS: return GRD_SETTINGS (grd_settings_user_new (runtime_mode)); case GRD_RUNTIME_MODE_SYSTEM: + case GRD_RUNTIME_MODE_HANDOVER: g_assert_not_reached (); } diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c new file mode 100644 index 00000000..3995399a --- /dev/null +++ b/src/grd-daemon-handover.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#include "config.h" + +#include "grd-daemon-handover.h" + +#include "grd-context.h" +#include "grd-dbus-remote-desktop.h" +#include "grd-private.h" + +struct _GrdDaemonHandover +{ + GrdDaemon parent; + + GrdDBusRemoteDesktopRdpDispatcher *remote_desktop_dispatcher; + GrdDBusRemoteDesktopRdpHandover *remote_desktop_handover; +}; + +G_DEFINE_TYPE (GrdDaemonHandover, grd_daemon_handover, GRD_TYPE_DAEMON) + +static gboolean +grd_daemon_handover_is_ready (GrdDaemon *daemon) +{ + GrdContext *context = grd_daemon_get_context (daemon); + + if (!grd_context_get_mutter_remote_desktop_proxy (context) || + !grd_context_get_mutter_screen_cast_proxy (context) || + !GRD_DAEMON_HANDOVER (daemon)->remote_desktop_handover) + return FALSE; + + return TRUE; +} + +static void +on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + g_autoptr (GrdDBusRemoteDesktopRdpHandover) proxy = NULL; + g_autoptr (GError) error = NULL; + + proxy = + grd_dbus_remote_desktop_rdp_handover_proxy_new_for_bus_finish (result, + &error); + if (!proxy) + { + g_warning ("[DaemonHandover] Failed to create remote desktop handover " + "proxy: %s", error->message); + return; + } + + daemon_handover->remote_desktop_handover = g_steal_pointer (&proxy); + + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_handover)); +} + +static void +on_remote_desktop_rdp_dispatcher_handover_requested (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + g_autofree char *object_path = NULL; + g_autoptr (GError) error = NULL; + gboolean success; + + success = + grd_dbus_remote_desktop_rdp_dispatcher_call_request_handover_finish ( + daemon_handover->remote_desktop_dispatcher, + &object_path, + result, + &error); + if (!success) + { + g_warning ("[DaemonHandover] Failed requesting remote desktop " + "handover: %s", error->message); + return; + } + + g_debug ("[DaemonHandover] Using: %s, from dispatcher request", + object_path); + + grd_dbus_remote_desktop_rdp_handover_proxy_new_for_bus ( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + REMOTE_DESKTOP_BUS_NAME, + object_path, + cancellable, + on_remote_desktop_rdp_handover_proxy_acquired, + daemon_handover); +} + +static void +on_remote_desktop_rdp_dispatcher_proxy_acquired (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + g_autoptr (GrdDBusRemoteDesktopRdpDispatcher) proxy = NULL; + g_autoptr (GError) error = NULL; + + proxy = + grd_dbus_remote_desktop_rdp_dispatcher_proxy_new_for_bus_finish (result, + &error); + if (!proxy) + { + g_warning ("[DaemonHandover] Failed to create remote desktop " + "dispatcher proxy: %s", error->message); + return; + } + + daemon_handover->remote_desktop_dispatcher = g_steal_pointer (&proxy); + + grd_dbus_remote_desktop_rdp_dispatcher_call_request_handover ( + daemon_handover->remote_desktop_dispatcher, + cancellable, + on_remote_desktop_rdp_dispatcher_handover_requested, + daemon_handover); +} + +GrdDaemonHandover * +grd_daemon_handover_new (GError **error) +{ + GrdContext *context; + + context = grd_context_new (GRD_RUNTIME_MODE_HANDOVER, error); + if (!context) + return NULL; + + return g_object_new (GRD_TYPE_DAEMON_HANDOVER, + "application-id", GRD_DAEMON_HANDOVER_APPLICATION_ID, + "flags", G_APPLICATION_IS_SERVICE, + "context", context, + NULL); +} + +static void +grd_daemon_handover_init (GrdDaemonHandover *daemon_handover) +{ +} + +static void +grd_daemon_handover_startup (GApplication *app) +{ + GrdDaemonHandover *daemon_handover = GRD_DAEMON_HANDOVER (app); + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + + grd_daemon_acquire_mutter_dbus_proxies (GRD_DAEMON (daemon_handover)); + + g_signal_connect (daemon_handover, "mutter-proxy-acquired", + G_CALLBACK (grd_daemon_maybe_enable_services), NULL); + + grd_dbus_remote_desktop_rdp_dispatcher_proxy_new_for_bus ( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + REMOTE_DESKTOP_BUS_NAME, + REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH, + cancellable, + on_remote_desktop_rdp_dispatcher_proxy_acquired, + daemon_handover); + + G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->startup (app); +} + +static void +grd_daemon_handover_shutdown (GApplication *app) +{ + GrdDaemonHandover *daemon_handover = GRD_DAEMON_HANDOVER (app); + + g_clear_object (&daemon_handover->remote_desktop_handover); + g_clear_object (&daemon_handover->remote_desktop_dispatcher); + + G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->shutdown (app); +} + +static void +grd_daemon_handover_class_init (GrdDaemonHandoverClass *klass) +{ + GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass); + GrdDaemonClass *daemon_class = GRD_DAEMON_CLASS (klass); + + g_application_class->startup = grd_daemon_handover_startup; + g_application_class->shutdown = grd_daemon_handover_shutdown; + + daemon_class->is_daemon_ready = grd_daemon_handover_is_ready; +} diff --git a/src/grd-daemon-handover.h b/src/grd-daemon-handover.h new file mode 100644 index 00000000..aad3c3a6 --- /dev/null +++ b/src/grd-daemon-handover.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 SUSE Software Solutions Germany GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Joan Torres + */ + +#ifndef GRD_DAEMON_HANDOVER_H +#define GRD_DAEMON_HANDOVER_H + +#include "grd-daemon.h" + +#define GRD_TYPE_DAEMON_HANDOVER (grd_daemon_handover_get_type ()) +G_DECLARE_FINAL_TYPE (GrdDaemonHandover, grd_daemon_handover, + GRD, DAEMON_HANDOVER, GrdDaemon) + +GrdDaemonHandover *grd_daemon_handover_new (GError **error); + +#endif /* GRD_DAEMON_HANDOVER */ diff --git a/src/grd-daemon-user.c b/src/grd-daemon-user.c index 9b1e1ecc..3013b45c 100644 --- a/src/grd-daemon-user.c +++ b/src/grd-daemon-user.c @@ -54,6 +54,7 @@ grd_daemon_user_new (GrdRuntimeMode runtime_mode, application_id = GRD_DAEMON_HEADLESS_APPLICATION_ID; break; case GRD_RUNTIME_MODE_SYSTEM: + case GRD_RUNTIME_MODE_HANDOVER: g_assert_not_reached (); } diff --git a/src/grd-daemon.c b/src/grd-daemon.c index 79bf0370..b1cf2f09 100644 --- a/src/grd-daemon.c +++ b/src/grd-daemon.c @@ -32,6 +32,7 @@ #include #include "grd-context.h" +#include "grd-daemon-handover.h" #include "grd-daemon-system.h" #include "grd-daemon-user.h" #include "grd-dbus-mutter-remote-desktop.h" @@ -133,6 +134,10 @@ export_remote_desktop_interface (GrdDaemon *daemon) grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode ( priv->remote_desktop_interface, "system"); break; + case GRD_RUNTIME_MODE_HANDOVER: + grd_dbus_remote_desktop_org_gnome_remote_desktop_set_runtime_mode ( + priv->remote_desktop_interface, "handover"); + break; } g_dbus_interface_skeleton_export ( @@ -232,7 +237,8 @@ start_rdp_server (GrdDaemon *daemon) "rdp-server-key", &key, NULL); - if (certificate && key) + if ((certificate && key) || + grd_context_get_runtime_mode (priv->context) == GRD_RUNTIME_MODE_HANDOVER) { priv->rdp_server = grd_rdp_server_new (priv->context); if (!grd_rdp_server_start (priv->rdp_server, &error)) @@ -865,6 +871,7 @@ main (int argc, char **argv) gboolean print_version = FALSE; gboolean headless = FALSE; gboolean system = FALSE; + gboolean handover = FALSE; int rdp_port = -1; int vnc_port = -1; @@ -876,6 +883,8 @@ main (int argc, char **argv) #ifdef HAVE_RDP { "system", 0, 0, G_OPTION_ARG_NONE, &system, "Run in headless mode as a system g-r-d service", NULL }, + { "handover", 0, 0, G_OPTION_ARG_NONE, &handover, + "Run in headless mode taking a connection from system g-r-d service", NULL }, #endif { "rdp-port", 0, 0, G_OPTION_ARG_INT, &rdp_port, "RDP port", NULL }, @@ -905,7 +914,7 @@ main (int argc, char **argv) return EXIT_SUCCESS; } - if (count_trues (2, headless, system) > 1) + if (count_trues (3, headless, system, handover) > 1) { g_printerr ("Invalid option: More than one runtime mode specified"); return EXIT_FAILURE; @@ -915,6 +924,8 @@ main (int argc, char **argv) runtime_mode = GRD_RUNTIME_MODE_HEADLESS; else if (system) runtime_mode = GRD_RUNTIME_MODE_SYSTEM; + else if (handover) + runtime_mode = GRD_RUNTIME_MODE_HANDOVER; else runtime_mode = GRD_RUNTIME_MODE_SCREEN_SHARE; @@ -927,6 +938,9 @@ main (int argc, char **argv) case GRD_RUNTIME_MODE_SYSTEM: daemon = GRD_DAEMON (grd_daemon_system_new (&error)); break; + case GRD_RUNTIME_MODE_HANDOVER: + daemon = GRD_DAEMON (grd_daemon_handover_new (&error)); + break; } if (!daemon) diff --git a/src/grd-enums.h b/src/grd-enums.h index c0185d25..028bdf9a 100644 --- a/src/grd-enums.h +++ b/src/grd-enums.h @@ -44,6 +44,7 @@ typedef enum GRD_RUNTIME_MODE_SCREEN_SHARE, GRD_RUNTIME_MODE_HEADLESS, GRD_RUNTIME_MODE_SYSTEM, + GRD_RUNTIME_MODE_HANDOVER, } GrdRuntimeMode; #endif /* GRD_ENUMS_H */ diff --git a/src/grd-private.h b/src/grd-private.h index 8b5218a3..04352d17 100644 --- a/src/grd-private.h +++ b/src/grd-private.h @@ -26,6 +26,7 @@ #define GRD_DAEMON_USER_APPLICATION_ID "org.gnome.RemoteDesktop.User" #define GRD_DAEMON_HEADLESS_APPLICATION_ID "org.gnome.RemoteDesktop.Headless" #define GRD_DAEMON_SYSTEM_APPLICATION_ID "org.gnome.RemoteDesktop" +#define GRD_DAEMON_HANDOVER_APPLICATION_ID "org.gnome.RemoteDesktop.Handover" #define REMOTE_DESKTOP_BUS_NAME "org.gnome.RemoteDesktop" #define REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/RemoteDesktop" #define REMOTE_DESKTOP_CLIENT_OBJECT_PATH "/org/gnome/RemoteDesktop/Client" diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c index 9950b4f6..4f47868e 100644 --- a/src/grd-rdp-server.c +++ b/src/grd-rdp-server.c @@ -229,10 +229,17 @@ grd_rdp_server_start (GrdRdpServer *rdp_server, { GrdSettings *settings = grd_context_get_settings (rdp_server->context); GrdRuntimeMode runtime_mode = grd_context_get_runtime_mode (rdp_server->context); + GrdDBusRemoteDesktopRdpServer *rdp_server_iface = + grd_context_get_rdp_server_interface (rdp_server->context); uint16_t rdp_port; uint16_t selected_rdp_port = 0; gboolean negotiate_port; - GrdDBusRemoteDesktopRdpServer *rdp_server_iface; + + if (runtime_mode == GRD_RUNTIME_MODE_HANDOVER) + { + grd_dbus_remote_desktop_rdp_server_set_enabled (rdp_server_iface, TRUE); + return TRUE; + } g_object_get (G_OBJECT (settings), "rdp-port", &rdp_port, @@ -261,9 +268,10 @@ grd_rdp_server_start (GrdRdpServer *rdp_server, g_assert (!rdp_server->cancellable); rdp_server->cancellable = g_cancellable_new (); break; + case GRD_RUNTIME_MODE_HANDOVER: + g_assert_not_reached (); } - rdp_server_iface = grd_context_get_rdp_server_interface (rdp_server->context); grd_dbus_remote_desktop_rdp_server_set_enabled (rdp_server_iface, TRUE); grd_dbus_remote_desktop_rdp_server_set_port (rdp_server_iface, selected_rdp_port); diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c index 087615cd..b5d70092 100644 --- a/src/grd-session-rdp.c +++ b/src/grd-session-rdp.c @@ -1649,6 +1649,7 @@ get_max_monitor_count (GrdSessionRdp *session_rdp) { case GRD_RUNTIME_MODE_HEADLESS: case GRD_RUNTIME_MODE_SYSTEM: + case GRD_RUNTIME_MODE_HANDOVER: return MAX_MONITOR_COUNT_HEADLESS; case GRD_RUNTIME_MODE_SCREEN_SHARE: return MAX_MONITOR_COUNT_SCREEN_SHARE; diff --git a/src/grd-settings-handover.c b/src/grd-settings-handover.c index 8f300067..2fa8bc1e 100644 --- a/src/grd-settings-handover.c +++ b/src/grd-settings-handover.c @@ -33,6 +33,7 @@ GrdSettingsHandover * grd_settings_handover_new (void) { return g_object_new (GRD_TYPE_SETTINGS_HANDOVER, + "runtime-mode", GRD_RUNTIME_MODE_HANDOVER, "rdp-enabled", TRUE, "rdp-view-only", FALSE, "rdp-screen-share-mode", GRD_RDP_SCREEN_SHARE_MODE_EXTEND, diff --git a/src/grd-settings-user.c b/src/grd-settings-user.c index 7ca3b3c8..20b81a94 100644 --- a/src/grd-settings-user.c +++ b/src/grd-settings-user.c @@ -105,6 +105,7 @@ grd_settings_user_constructed (GObject *object) G_SETTINGS_BIND_DEFAULT); break; case GRD_RUNTIME_MODE_SYSTEM: + case GRD_RUNTIME_MODE_HANDOVER: g_assert_not_reached (); } diff --git a/src/grd-settings.c b/src/grd-settings.c index 31e927ac..9edd9989 100644 --- a/src/grd-settings.c +++ b/src/grd-settings.c @@ -26,6 +26,7 @@ #include "grd-credentials-file.h" #include "grd-credentials-libsecret.h" +#include "grd-credentials-one-time.h" #include "grd-credentials-tpm.h" #include "grd-enum-types.h" @@ -283,6 +284,8 @@ create_credentials (GrdRuntimeMode runtime_mode) return create_headless_credentials (); case GRD_RUNTIME_MODE_SCREEN_SHARE: return GRD_CREDENTIALS (grd_credentials_libsecret_new ()); + case GRD_RUNTIME_MODE_HANDOVER: + return GRD_CREDENTIALS (grd_credentials_one_time_new ()); } g_assert_not_reached (); diff --git a/src/meson.build b/src/meson.build index 4b7d34e0..7ff3988f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -81,6 +81,8 @@ if have_rdp daemon_sources += files([ 'grd-clipboard-rdp.c', 'grd-clipboard-rdp.h', + 'grd-daemon-handover.c', + 'grd-daemon-handover.h', 'grd-daemon-system.c', 'grd-daemon-system.h', 'grd-daemon-utils.c', -- GitLab From 3900272fc0a997b9c7040037d54e079a63a84b2c Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 14 Aug 2023 11:47:01 +0200 Subject: [PATCH 17/27] daemon-handover: Add handover implementation 1. Once the daemon-handover is ready, the handover process will be started using the "StartHandover" method of the handover interface. Onetime username/password will be sent and the certificate/private-key used by the daemon-system will be received. 2. When the RDP client is reconnected again to the daemon-system, the "TakeClientReady" signal will be emitted. 3. The RDP client socket will be acquired using the "TakeClient" method of the handover interface. 4. The RDP server of the daemon-handover will use that socket to initialize and start the connection. --- src/grd-daemon-handover.c | 166 ++++++++++++++++++++++++++++++++++++++ src/grd-rdp-server.c | 7 ++ src/grd-rdp-server.h | 3 + 3 files changed, 176 insertions(+) diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c index 3995399a..c412408f 100644 --- a/src/grd-daemon-handover.c +++ b/src/grd-daemon-handover.c @@ -24,9 +24,15 @@ #include "grd-daemon-handover.h" +#include +#include + #include "grd-context.h" +#include "grd-daemon.h" #include "grd-dbus-remote-desktop.h" #include "grd-private.h" +#include "grd-rdp-server.h" +#include "grd-settings.h" struct _GrdDaemonHandover { @@ -51,6 +57,160 @@ grd_daemon_handover_is_ready (GrdDaemon *daemon) return TRUE; } +static void +on_take_client_finished (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + int fd; + int fd_idx; + g_autoptr (GError) error = NULL; + g_autoptr (GSocket) socket = NULL; + g_autoptr (GVariant) fd_variant = NULL; + g_autoptr (GUnixFDList) fd_list = NULL; + GrdDBusRemoteDesktopRdpHandover *proxy; + GSocketConnection *socket_connection; + GrdRdpServer *rdp_server; + + proxy = GRD_DBUS_REMOTE_DESKTOP_RDP_HANDOVER (object); + if (!grd_dbus_remote_desktop_rdp_handover_call_take_client_finish ( + proxy, &fd_variant, &fd_list, result, &error)) + { + g_warning ("[DaemonHandover] Error calling TakeClient dbus method: %s", + error->message); + return; + } + + g_variant_get (fd_variant, "h", &fd_idx); + fd = g_unix_fd_list_get (fd_list, fd_idx, &error); + if (fd == -1) + { + g_warning ("[DaemonHandover] Failed to acquire file descriptor: %s", + error->message); + return; + } + + socket = g_socket_new_from_fd (fd, &error); + if (!socket) + { + g_warning ("[DaemonHandover] Error creating socket: %s", + error->message); + return; + } + + socket_connection = g_socket_connection_factory_create_connection (socket); + + rdp_server = grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover)); + grd_rdp_server_notify_incoming (G_SOCKET_SERVICE (rdp_server), + socket_connection); +} + +static void +on_take_client_ready (GrdDBusRemoteDesktopRdpHandover *interface, + GrdDaemonHandover *daemon_handover) +{ + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + const char* object_path; + + object_path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (interface)); + + g_debug ("[DaemonHandover] At: %s, received TakeClientReady signal and " + "calling TakeClient", object_path); + + grd_dbus_remote_desktop_rdp_handover_call_take_client ( + interface, + NULL, + cancellable, + on_take_client_finished, + daemon_handover); +} + +static void +on_start_handover_finished (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_handover)); + GrdSettings *settings = grd_context_get_settings (context); + GrdDBusRemoteDesktopRdpHandover *proxy; + g_autofree char *certificate = NULL; + g_autofree char *key = NULL; + g_autoptr (GError) error = NULL; + + proxy = GRD_DBUS_REMOTE_DESKTOP_RDP_HANDOVER (object); + if (!grd_dbus_remote_desktop_rdp_handover_call_start_handover_finish ( + proxy, &certificate, &key, result, &error)) + { + g_warning ("[DaemonHandover] Failed to start handover: %s", + error->message); + return; + } + + g_object_set (G_OBJECT (settings), + "rdp-server-cert", certificate, + "rdp-server-key", key, + NULL); +} + +static void +start_handover (GrdDaemonHandover *daemon_handover, + const char *user_name, + const char *password) +{ + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + const char *object_path; + + object_path = g_dbus_proxy_get_object_path ( + G_DBUS_PROXY (daemon_handover->remote_desktop_handover)); + + g_debug ("[DaemonHandover] At: %s, calling StartHandover", object_path); + + grd_dbus_remote_desktop_rdp_handover_call_start_handover ( + daemon_handover->remote_desktop_handover, + user_name, + password, + cancellable, + on_start_handover_finished, + daemon_handover); +} + +static void +on_rdp_server_started (GrdDaemonHandover *daemon_handover) +{ + GrdDaemon *daemon = GRD_DAEMON (daemon_handover); + GrdContext *context = grd_daemon_get_context (daemon); + GrdSettings *settings = grd_context_get_settings (context); + g_autofree char *user_name = NULL; + g_autofree char *password = NULL; + + if (!grd_settings_get_rdp_credentials (settings, + &user_name, &password, + NULL)) + g_assert_not_reached (); + + g_signal_connect (daemon_handover->remote_desktop_handover, + "take-client-ready", G_CALLBACK (on_take_client_ready), + daemon_handover); + + start_handover (daemon_handover, user_name, password); +} + +static void +on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) +{ + if (daemon_handover->remote_desktop_handover) + { + g_signal_handlers_disconnect_by_func ( + daemon_handover->remote_desktop_handover, + G_CALLBACK (on_take_client_ready), + daemon_handover); + } +} + static void on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, GAsyncResult *result, @@ -185,6 +345,12 @@ grd_daemon_handover_startup (GApplication *app) on_remote_desktop_rdp_dispatcher_proxy_acquired, daemon_handover); + g_signal_connect (daemon_handover, "rdp-server-started", + G_CALLBACK (on_rdp_server_started), NULL); + + g_signal_connect (daemon_handover, "rdp-server-stopped", + G_CALLBACK (on_rdp_server_stopped), NULL); + G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->startup (app); } diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c index 4f47868e..dfb0d5ee 100644 --- a/src/grd-rdp-server.c +++ b/src/grd-rdp-server.c @@ -223,6 +223,13 @@ on_incoming (GSocketService *service, return TRUE; } +void +grd_rdp_server_notify_incoming (GSocketService *service, + GSocketConnection *connection) +{ + on_incoming (service, connection); +} + gboolean grd_rdp_server_start (GrdRdpServer *rdp_server, GError **error) diff --git a/src/grd-rdp-server.h b/src/grd-rdp-server.h index 8b3667a0..8a5fd5e7 100644 --- a/src/grd-rdp-server.h +++ b/src/grd-rdp-server.h @@ -40,4 +40,7 @@ void grd_rdp_server_stop (GrdRdpServer *rdp_server); GrdRdpServer *grd_rdp_server_new (GrdContext *context); +void grd_rdp_server_notify_incoming (GSocketService *service, + GSocketConnection *connection); + #endif /* GRD_RDP_SERVER_H */ -- GitLab From aa1d16d0cee6cda125479e1b7415c251654f3f92 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 14 Aug 2023 13:21:07 +0200 Subject: [PATCH 18/27] daemon-handover: Logout when RDP client disconnects or handover fails There are three possible situations, where a logout should happen: 1. The RDP client disconnected. 2. Calling "StartHandover" method returned an error from daemon-system. 3. The "StartHandover" method is called successfully, but after a while, the "TakeClientReady" signal isn't received. The RDP client mstsc disconnects and reconnects a second time on redirection, when using RDSTLS as security method. So use the "incoming-new-connection" signal to make sure to not logout on the first disconnection, but to logout when the client finished authenticating ("incoming-new-connection" is emitted on the PostConnect callback). In the future, user sessions won't be logged out, making it possible to reuse them in a new connection. --- src/grd-daemon-handover.c | 55 +++++++++++++++++++++++++++++++++++++++ src/grd-daemon-utils.c | 35 +++++++++++++++++++++++++ src/grd-daemon-utils.h | 2 ++ src/grd-rdp-server.c | 4 +++ 4 files changed, 96 insertions(+) diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c index c412408f..0c7725f8 100644 --- a/src/grd-daemon-handover.c +++ b/src/grd-daemon-handover.c @@ -29,17 +29,22 @@ #include "grd-context.h" #include "grd-daemon.h" +#include "grd-daemon-utils.h" #include "grd-dbus-remote-desktop.h" #include "grd-private.h" #include "grd-rdp-server.h" #include "grd-settings.h" +#define MAX_HANDOVER_WAIT_TIME_MS (20 * 1000) + struct _GrdDaemonHandover { GrdDaemon parent; GrdDBusRemoteDesktopRdpDispatcher *remote_desktop_dispatcher; GrdDBusRemoteDesktopRdpHandover *remote_desktop_handover; + + unsigned int logout_source_id; }; G_DEFINE_TYPE (GrdDaemonHandover, grd_daemon_handover, GRD_TYPE_DAEMON) @@ -104,6 +109,8 @@ on_take_client_finished (GObject *object, rdp_server = grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover)); grd_rdp_server_notify_incoming (G_SOCKET_SERVICE (rdp_server), socket_connection); + + g_clear_handle_id (&daemon_handover->logout_source_id, g_source_remove); } static void @@ -146,6 +153,7 @@ on_start_handover_finished (GObject *object, { g_warning ("[DaemonHandover] Failed to start handover: %s", error->message); + grd_session_manager_call_logout_sync (); return; } @@ -155,6 +163,19 @@ on_start_handover_finished (GObject *object, NULL); } +static gboolean +logout (gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + + g_warning ("[DaemonHandover] Logging out, handover timeout reached"); + + daemon_handover->logout_source_id = 0; + grd_session_manager_call_logout_sync (); + + return G_SOURCE_REMOVE; +} + static void start_handover (GrdDaemonHandover *daemon_handover, const char *user_name, @@ -164,6 +185,8 @@ start_handover (GrdDaemonHandover *daemon_handover, grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); const char *object_path; + g_assert (!daemon_handover->logout_source_id); + object_path = g_dbus_proxy_get_object_path ( G_DBUS_PROXY (daemon_handover->remote_desktop_handover)); @@ -176,6 +199,24 @@ start_handover (GrdDaemonHandover *daemon_handover, cancellable, on_start_handover_finished, daemon_handover); + + daemon_handover->logout_source_id = + g_timeout_add (MAX_HANDOVER_WAIT_TIME_MS, logout, daemon_handover); +} + +static void +on_session_stopped (GrdSession *session) +{ + grd_session_manager_call_logout_sync (); +} + +static void +on_incoming_new_connection (GrdRdpServer *rdp_server, + GrdSession *session) +{ + g_signal_connect (session, "stopped", + G_CALLBACK (on_session_stopped), + NULL); } static void @@ -184,6 +225,7 @@ on_rdp_server_started (GrdDaemonHandover *daemon_handover) GrdDaemon *daemon = GRD_DAEMON (daemon_handover); GrdContext *context = grd_daemon_get_context (daemon); GrdSettings *settings = grd_context_get_settings (context); + GrdRdpServer *rdp_server = grd_daemon_get_rdp_server (daemon); g_autofree char *user_name = NULL; g_autofree char *password = NULL; @@ -197,11 +239,18 @@ on_rdp_server_started (GrdDaemonHandover *daemon_handover) daemon_handover); start_handover (daemon_handover, user_name, password); + + g_signal_connect (rdp_server, "incoming-new-connection", + G_CALLBACK (on_incoming_new_connection), + NULL); } static void on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) { + GrdRdpServer *rdp_server = + grd_daemon_get_rdp_server (GRD_DAEMON (daemon_handover)); + if (daemon_handover->remote_desktop_handover) { g_signal_handlers_disconnect_by_func ( @@ -209,6 +258,10 @@ on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) G_CALLBACK (on_take_client_ready), daemon_handover); } + + g_signal_handlers_disconnect_by_func (rdp_server, + G_CALLBACK (on_incoming_new_connection), + NULL); } static void @@ -362,6 +415,8 @@ grd_daemon_handover_shutdown (GApplication *app) g_clear_object (&daemon_handover->remote_desktop_handover); g_clear_object (&daemon_handover->remote_desktop_dispatcher); + g_clear_handle_id (&daemon_handover->logout_source_id, g_source_remove); + G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->shutdown (app); } diff --git a/src/grd-daemon-utils.c b/src/grd-daemon-utils.c index a2299d17..3113912c 100644 --- a/src/grd-daemon-utils.c +++ b/src/grd-daemon-utils.c @@ -23,6 +23,13 @@ #include +enum +{ + GSM_LOGOUT_MODE_NORMAL = 0, + GSM_LOGOUT_MODE_NO_CONFIRMATION, + GSM_LOGOUT_MODE_FORCE, +}; + gboolean grd_get_pid_of_sender_sync (GDBusConnection *connection, const char *name, @@ -161,3 +168,31 @@ grd_get_session_id_from_uid (uid_t uid) return g_strdup (session_id); } + +void +grd_session_manager_call_logout_sync (void) +{ + g_autoptr (GDBusConnection) connection = NULL; + g_autoptr (GError) error = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + if (!connection) + { + g_warning ("Couldn't get session bus to logout: %s", error->message); + return; + } + + g_dbus_connection_call_sync (connection, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + "Logout", + g_variant_new ("(u)", + GSM_LOGOUT_MODE_NO_CONFIRMATION), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &error); + if (error) + g_warning ("Couldn't logout of session: %s", error->message); +} diff --git a/src/grd-daemon-utils.h b/src/grd-daemon-utils.h index 96a3a458..7d4065b0 100644 --- a/src/grd-daemon-utils.h +++ b/src/grd-daemon-utils.h @@ -38,4 +38,6 @@ char *grd_get_session_id_from_pid (pid_t pid); char *grd_get_session_id_from_uid (uid_t uid); +void grd_session_manager_call_logout_sync (void); + #endif /* GRD_DAEMON_UTILS_H */ diff --git a/src/grd-rdp-server.c b/src/grd-rdp-server.c index dfb0d5ee..eccca867 100644 --- a/src/grd-rdp-server.c +++ b/src/grd-rdp-server.c @@ -220,6 +220,10 @@ on_incoming (GSocketService *service, G_CALLBACK (on_session_stopped), rdp_server); + g_signal_connect (session_rdp, "post-connected", + G_CALLBACK (on_session_post_connect), + rdp_server); + return TRUE; } -- GitLab From c7aeea0271f5ffada616c9253f4a858520a9ed35 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 30 Oct 2023 12:34:02 +0100 Subject: [PATCH 19/27] daemon-system: Handover an RDP client between two daemon-handovers The RemoteClient now has a handover_src and a handover_dst to do that. From the situation of a successful first handover, where the RDP client (RemoteClient) is connected to a daemon-handover at a login session and the daemon-system is communicating with it with a handover iface stored at handover_dst: 1. The user authenticates, and GDM starts the user session and creates a new RemoteDisplay with its session_id and with the remote_id of the RemoteClient. 2. The daemon-system receives the "interface-added" event from GDM, checks if it is a RemoteDisplay iface and if its remote_id corresponds with any of its RemoteClients. 3. If a RemoteClient is found, it registers a new handover interface with the session_id of that RemoteDisplay. And on that RemoteClient, it stores that iface at handover_dst, setting the old handover_dst as handover_src. 4. On that new session, a daemon-handover is started, and requests if it has a handover iface by calling the "RequestHandover" method on the daemon-system dispatcher iface. 5. The daemon-system checks and responds with the handover_dst object path. 6. The daemon-handover connects to that handover iface and calls the "StartHandover" method. 7. When the daemon-system handles this call, it will emit the "RedirectClient" signal at handover_src in unicast mode, where there is the other daemon-handover at the login session with the RDP client connected. It also stores the unique name of the daemon-handover connected at handover_dst for future unicast signal emissions. 8. That daemon-handover connected at handover_src receives the signal and sends to the RDP client the Server Redirection PDU, and the RDP client disconnects and reconnects again. 9. The daemon-system detects the new connecting RDP client has as routing token the remote_id of one of its RemoteClients. 10. The daemon-system emits the "TakeClientReady" signal on the handover_dst iface of that RemoteClient. 11. The daemon-handover at the user session will receive that signal and call "TakeClient" method to get the RDP client. --- src/grd-daemon-system.c | 256 ++++++++++++++++++++------------ src/org.gnome.RemoteDesktop.xml | 18 ++- 2 files changed, 176 insertions(+), 98 deletions(-) diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c index 6ab02825..85713c4b 100644 --- a/src/grd-daemon-system.c +++ b/src/grd-daemon-system.c @@ -39,6 +39,14 @@ #define MAX_HANDOVER_WAIT_TIME_MS (30 * 1000) +typedef struct +{ + GrdDBusRemoteDesktopRdpHandover *interface; + GDBusObjectSkeleton *skeleton; + char *object_path; + char *sender_name; +} HandoverInterface; + typedef struct { GrdDaemonSystem *daemon_system; @@ -48,12 +56,8 @@ typedef struct GrdSession *session; GSocketConnection *socket_connection; - GDBusObjectSkeleton *handover_skeleton; - GrdDBusRemoteDesktopRdpHandover *handover_interface; - char *dbus_object_path; - - gboolean is_registered; - gboolean is_being_redirected; + HandoverInterface *handover_src; + HandoverInterface *handover_dst; unsigned int abort_handover_source_id; } GrdRemoteClient; @@ -125,6 +129,11 @@ on_handle_take_client (GrdDBusRemoteDesktopRdpHandover *interface, int fd; int fd_idx; + g_assert (interface == remote_client->handover_dst->interface); + + g_debug ("[DaemonSystem] At: %s, received TakeClient call", + remote_client->handover_dst->object_path); + socket = g_socket_connection_get_socket (remote_client->socket_connection); fd = g_socket_get_fd (socket); @@ -136,7 +145,6 @@ on_handle_take_client (GrdDBusRemoteDesktopRdpHandover *interface, invocation, fd_list, fd_variant); - remote_client->is_being_redirected = FALSE; g_clear_object (&remote_client->socket_connection); g_clear_handle_id (&remote_client->abort_handover_source_id, g_source_remove); @@ -144,6 +152,26 @@ on_handle_take_client (GrdDBusRemoteDesktopRdpHandover *interface, return G_DBUS_METHOD_INVOCATION_HANDLED; } +static gboolean +abort_handover (gpointer user_data) +{ + GrdRemoteClient *remote_client = user_data; + GrdDaemonSystem *daemon_system = remote_client->daemon_system; + + g_warning ("[DaemonSystem] Aborting handover, removing remote client with " + "remote id %s", remote_client->id); + + if (remote_client->session) + { + grd_session_rdp_notify_error (GRD_SESSION_RDP (remote_client->session), + GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION); + } + + g_hash_table_remove (daemon_system->remote_clients, remote_client->id); + + return G_SOURCE_REMOVE; +} + static char * get_id_from_routing_token (uint32_t routing_token) { @@ -172,10 +200,14 @@ on_handle_start_handover (GrdDBusRemoteDesktopRdpHandover *interface, g_autofree char *key = NULL; g_autofree char *certificate = NULL; g_autofree char *routing_token = NULL; + g_autoptr (GVariant) redirect_variant = NULL; + GDBusConnection *connection; + const char *sender; - g_debug ("[DaemonSystem] Received StartHandover call"); + g_assert (interface == remote_client->handover_dst->interface); - remote_client->is_being_redirected = TRUE; + g_debug ("[DaemonSystem] At: %s, received StartHandover call", + remote_client->handover_dst->object_path ); routing_token = get_routing_token_from_id (remote_client->id); @@ -184,18 +216,49 @@ on_handle_start_handover (GrdDBusRemoteDesktopRdpHandover *interface, "rdp-server-key", &key, NULL); - if (!grd_session_rdp_send_server_redirection ( - GRD_SESSION_RDP (remote_client->session), - routing_token, - user_name, - password, - certificate)) - goto err; + /* The remote client is at daemon-system */ + if (remote_client->session) + { + if (!grd_session_rdp_send_server_redirection ( + GRD_SESSION_RDP (remote_client->session), + routing_token, + user_name, + password, + certificate)) + goto err; + } + else + { + connection = g_dbus_interface_skeleton_get_connection ( + G_DBUS_INTERFACE_SKELETON ( + remote_client->handover_src->interface)); + redirect_variant = g_variant_new ("(sss)", + routing_token, + user_name, + password); + g_dbus_connection_emit_signal (connection, + remote_client->handover_src->sender_name, + remote_client->handover_src->object_path, + "org.gnome.RemoteDesktop.Rdp.Handover", + "RedirectClient", + redirect_variant, + NULL); + } - grd_dbus_remote_desktop_rdp_handover_complete_start_handover (interface, - invocation, - certificate, - key); + sender = g_dbus_method_invocation_get_sender (invocation); + remote_client->handover_dst->sender_name = g_strdup (sender); + + grd_dbus_remote_desktop_rdp_handover_complete_start_handover ( + remote_client->handover_dst->interface, + invocation, + certificate, + key); + + if (remote_client->abort_handover_source_id == 0) + { + remote_client->abort_handover_source_id = + g_timeout_add (MAX_HANDOVER_WAIT_TIME_MS, abort_handover, remote_client); + } return G_DBUS_METHOD_INVOCATION_HANDLED; @@ -208,89 +271,91 @@ err: } static void -register_handover_iface (GrdRemoteClient *remote_client, - const char *session_id) +handover_iface_free (HandoverInterface *handover) { - GrdDaemonSystem *daemon_system = remote_client->daemon_system; + g_clear_object (&handover->skeleton); + g_clear_object (&handover->interface); + g_clear_pointer (&handover->object_path, g_free); + g_clear_pointer (&handover->sender_name, g_free); + g_free (handover); +} - if (remote_client->is_registered) - return; +static HandoverInterface * +handover_iface_new (const char *session_id, + GrdRemoteClient *remote_client) +{ + HandoverInterface *handover; - remote_client->dbus_object_path = - g_strdup_printf ("%s/session%s", - REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, - session_id); + handover = g_new0 (HandoverInterface, 1); - remote_client->handover_skeleton = - g_dbus_object_skeleton_new (remote_client->dbus_object_path); + handover->object_path = g_strdup_printf ("%s/session%s", + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, + session_id); - g_dbus_object_manager_server_export (daemon_system->handover_manager_server, - remote_client->handover_skeleton); + handover->skeleton = g_dbus_object_skeleton_new (handover->object_path); - g_dbus_object_skeleton_add_interface ( - remote_client->handover_skeleton, - G_DBUS_INTERFACE_SKELETON (remote_client->handover_interface)); - - remote_client->is_registered = TRUE; + handover->interface = grd_dbus_remote_desktop_rdp_handover_skeleton_new (); + g_signal_connect (handover->interface, "handle-start-handover", + G_CALLBACK (on_handle_start_handover), remote_client); + g_signal_connect (handover->interface, "handle-take-client", + G_CALLBACK (on_handle_take_client), remote_client); - g_debug ("[DaemonSystem] Registered handover on path %s", - remote_client->dbus_object_path); + return handover; } static void -unregister_handover_iface (GrdRemoteClient *remote_client) +register_handover_iface (GrdRemoteClient *remote_client, + const char *session_id) { GrdDaemonSystem *daemon_system = remote_client->daemon_system; + HandoverInterface *handover; - if (!remote_client->is_registered) - return; + handover = handover_iface_new (session_id, remote_client); - g_dbus_object_skeleton_remove_interface ( - remote_client->handover_skeleton, - G_DBUS_INTERFACE_SKELETON (remote_client->handover_interface)); + g_debug ("[DaemonSystem] Registering handover at: %s", + handover->object_path); - g_dbus_object_manager_server_unexport ( - daemon_system->handover_manager_server, - remote_client->dbus_object_path); + g_dbus_object_manager_server_export (daemon_system->handover_manager_server, + handover->skeleton); - g_clear_object (&remote_client->handover_skeleton); - g_clear_pointer (&remote_client->dbus_object_path, g_free); + g_dbus_object_skeleton_add_interface ( + handover->skeleton, + G_DBUS_INTERFACE_SKELETON (handover->interface)); - remote_client->is_registered = FALSE; + g_clear_pointer (&remote_client->handover_src, handover_iface_free); + remote_client->handover_src = remote_client->handover_dst; + remote_client->handover_dst = handover; } -static gboolean -abort_handover (gpointer user_data) +static void +unregister_handover_iface (GrdRemoteClient *remote_client, + HandoverInterface *handover) { - GrdRemoteClient *remote_client = user_data; GrdDaemonSystem *daemon_system = remote_client->daemon_system; - g_warning ("[DaemonSystem] Aborting handover, RDP client wasn't handovered " - "(Timeout reached)"); - - remote_client->abort_handover_source_id = 0; + if (!handover) + return; - if (!remote_client->is_being_redirected) - { - grd_session_rdp_notify_error (GRD_SESSION_RDP (remote_client->session), - GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION); - } + g_debug ("[DaemonSystem] Unregistering handover at: %s", + handover->object_path); - g_hash_table_remove (daemon_system->remote_clients, remote_client->id); + g_dbus_object_skeleton_remove_interface ( + handover->skeleton, + G_DBUS_INTERFACE_SKELETON (handover->interface)); - return G_SOURCE_REMOVE; + g_dbus_object_manager_server_unexport (daemon_system->handover_manager_server, + handover->object_path); } static void grd_remote_client_free (GrdRemoteClient *remote_client) { - unregister_handover_iface (remote_client); - g_clear_pointer (&remote_client->id, g_free); g_clear_object (&remote_client->socket_connection); - g_clear_object (&remote_client->handover_skeleton); - g_clear_object (&remote_client->handover_interface); - g_clear_pointer (&remote_client->dbus_object_path, g_free); + unregister_handover_iface (remote_client, remote_client->handover_src); + unregister_handover_iface (remote_client, remote_client->handover_dst); + g_clear_pointer (&remote_client->handover_src, handover_iface_free); + g_clear_pointer (&remote_client->handover_dst, handover_iface_free); g_clear_handle_id (&remote_client->abort_handover_source_id, g_source_remove); g_free (remote_client); @@ -332,8 +397,6 @@ remote_client_new (GrdDaemonSystem *daemon_system, remote_client = g_new0 (GrdRemoteClient, 1); remote_client->id = get_next_available_id (daemon_system); remote_client->daemon_system = daemon_system; - remote_client->is_registered = FALSE; - remote_client->is_being_redirected = FALSE; remote_client->session = session; g_object_weak_ref (G_OBJECT (session), (GWeakNotify) session_disposed, @@ -342,13 +405,6 @@ remote_client_new (GrdDaemonSystem *daemon_system, remote_client->abort_handover_source_id = g_timeout_add (MAX_HANDOVER_WAIT_TIME_MS, abort_handover, remote_client); - remote_client->handover_interface = - grd_dbus_remote_desktop_rdp_handover_skeleton_new (); - g_signal_connect (remote_client->handover_interface, "handle-start-handover", - G_CALLBACK (on_handle_start_handover), remote_client); - g_signal_connect (remote_client->handover_interface, "handle-take-client", - G_CALLBACK (on_handle_take_client), remote_client); - return remote_client; } @@ -367,14 +423,7 @@ on_create_remote_display_finished (GObject *object, { g_warning ("[DaemonSystem] Error while calling CreateRemoteDisplay on " "DisplayMananger: %s", error->message); - if (remote_client->session) - { - grd_session_rdp_notify_error ( - GRD_SESSION_RDP (remote_client->session), - GRD_SESSION_RDP_ERROR_SERVER_REDIRECTION); - } - g_hash_table_remove (remote_client->daemon_system->remote_clients, - remote_client->id); + abort_handover (remote_client); } } @@ -404,11 +453,11 @@ on_incoming_redirected_connection (GrdRdpServer *rdp_server, remote_client->socket_connection = g_object_ref (connection); - g_debug ("[DaemonSystem] Found routing token on remote_clients list, " - "emitting signal TakeClientReady"); + g_debug ("[DaemonSystem] At: %s, emitting TakeClientReady signal", + remote_client->handover_dst->object_path); grd_dbus_remote_desktop_rdp_handover_emit_take_client_ready ( - remote_client->handover_interface); + remote_client->handover_dst->interface); } static void @@ -774,7 +823,9 @@ static void unregister_handover_for_display (GrdDaemonSystem *daemon_system, GrdDBusGdmRemoteDisplay *remote_display) { + g_autofree const char *object_path = NULL; GrdRemoteClient *remote_client; + const char *session_id; const char *remote_id; remote_id = grd_dbus_gdm_remote_display_get_remote_id (remote_display); @@ -787,13 +838,28 @@ unregister_handover_for_display (GrdDaemonSystem *daemon_system, return; } - if (!remote_client->is_being_redirected) + session_id = grd_dbus_gdm_remote_display_get_session_id (remote_display); + object_path = g_strdup_printf ("%s/session%s", + REMOTE_DESKTOP_HANDOVERS_OBJECT_PATH, + session_id); + + g_debug ("[DaemonSystem] GDM removed a remote display with remote id: %s " + "and session: %s", remote_client->id, session_id); + + if (remote_client->handover_src) { - g_hash_table_remove (daemon_system->remote_clients, remote_id); - return; + if (strcmp (object_path, remote_client->handover_src->object_path) == 0) + { + unregister_handover_iface (remote_client, remote_client->handover_src); + g_clear_pointer (&remote_client->handover_src, handover_iface_free); + } } - unregister_handover_iface (remote_client); + if (remote_client->handover_dst) + { + if (strcmp (object_path, remote_client->handover_dst->object_path) == 0) + g_hash_table_remove (daemon_system->remote_clients, remote_id); + } } static void diff --git a/src/org.gnome.RemoteDesktop.xml b/src/org.gnome.RemoteDesktop.xml index 295faa6d..d33fe31c 100644 --- a/src/org.gnome.RemoteDesktop.xml +++ b/src/org.gnome.RemoteDesktop.xml @@ -88,7 +88,7 @@ @@ -99,10 +99,22 @@ + + + + + + + @@ -110,7 +122,7 @@ -- GitLab From e01a6b8eba57c54e09267124d6d868d935f6bad2 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 4 Sep 2023 16:47:53 +0200 Subject: [PATCH 20/27] daemon-handover: Store the session to send the Server Redirection PDU This is needed in order to be able to hand over an RDP client between two daemon-handovers connected to the daemon-system. See the previous commit for more info. --- src/grd-daemon-handover.c | 51 ++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c index 0c7725f8..0e7e4574 100644 --- a/src/grd-daemon-handover.c +++ b/src/grd-daemon-handover.c @@ -33,6 +33,7 @@ #include "grd-dbus-remote-desktop.h" #include "grd-private.h" #include "grd-rdp-server.h" +#include "grd-session-rdp.h" #include "grd-settings.h" #define MAX_HANDOVER_WAIT_TIME_MS (20 * 1000) @@ -44,6 +45,8 @@ struct _GrdDaemonHandover GrdDBusRemoteDesktopRdpDispatcher *remote_desktop_dispatcher; GrdDBusRemoteDesktopRdpHandover *remote_desktop_handover; + GrdSession *session; + unsigned int logout_source_id; }; @@ -205,18 +208,24 @@ start_handover (GrdDaemonHandover *daemon_handover, } static void -on_session_stopped (GrdSession *session) +on_session_stopped (GrdSession *session, + GrdDaemonHandover *daemon_handover) { grd_session_manager_call_logout_sync (); + + daemon_handover->session = NULL; } static void -on_incoming_new_connection (GrdRdpServer *rdp_server, - GrdSession *session) +on_incoming_new_connection (GrdRdpServer *rdp_server, + GrdSession *session, + GrdDaemonHandover *daemon_handover) { g_signal_connect (session, "stopped", G_CALLBACK (on_session_stopped), - NULL); + daemon_handover); + + daemon_handover->session = session; } static void @@ -242,7 +251,7 @@ on_rdp_server_started (GrdDaemonHandover *daemon_handover) g_signal_connect (rdp_server, "incoming-new-connection", G_CALLBACK (on_incoming_new_connection), - NULL); + daemon_handover); } static void @@ -261,7 +270,34 @@ on_rdp_server_stopped (GrdDaemonHandover *daemon_handover) g_signal_handlers_disconnect_by_func (rdp_server, G_CALLBACK (on_incoming_new_connection), - NULL); + daemon_handover); +} + +static void +on_redirect_client (GrdDBusRemoteDesktopRdpHandover *interface, + const char *routing_token, + const char *user_name, + const char *password, + GrdDaemonHandover *daemon_handover) +{ + const char *object_path = + g_dbus_proxy_get_object_path (G_DBUS_PROXY (interface)); + GrdContext *context = grd_daemon_get_context (GRD_DAEMON (daemon_handover)); + GrdSettings *settings = grd_context_get_settings (context); + GrdSessionRdp *session_rdp = GRD_SESSION_RDP (daemon_handover->session); + g_autofree char *certificate = NULL; + + g_debug ("[DaemonHandover] At: %s, received RedirectClient signal", + object_path); + + g_object_get (G_OBJECT (settings), + "rdp-server-cert", &certificate, + NULL); + + if (!grd_session_rdp_send_server_redirection (session_rdp, routing_token, + user_name, password, + certificate)) + grd_session_manager_call_logout_sync (); } static void @@ -285,6 +321,9 @@ on_remote_desktop_rdp_handover_proxy_acquired (GObject *object, daemon_handover->remote_desktop_handover = g_steal_pointer (&proxy); + g_signal_connect (daemon_handover->remote_desktop_handover, "redirect-client", + G_CALLBACK (on_redirect_client), daemon_handover); + grd_daemon_maybe_enable_services (GRD_DAEMON (daemon_handover)); } -- GitLab From 3ecf3f8feb217075d695484fc610dc94c0449109 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 25 Oct 2023 15:23:01 +0200 Subject: [PATCH 21/27] daemon-system: Abort handover when RDP client disconnects The client can disconnect during the handover in two situations: - daemon-system to daemon-handover: This is the first handover. The RDP client is handed over from the daemon-system to a daemon-handover at a login session. In this case, when the client disconnects, the session property of remote_client is set to NULL; also the handover_src iface remains unset because it's the first handover. - daemon-handover to daemon-handover: In this case, the session property of the remote_client was previously set to NULL after the first handover; and the RDP client has disconnected from the daemon-handover connected to handover_src. Currently when the RDP client disconnects from a daemon-handover, a session logout is triggered, which makes gdm remove the RemoteDisplay which makes daemon-system unregister handover_src. --- src/grd-daemon-system.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/grd-daemon-system.c b/src/grd-daemon-system.c index 85713c4b..ffb94d61 100644 --- a/src/grd-daemon-system.c +++ b/src/grd-daemon-system.c @@ -209,6 +209,12 @@ on_handle_start_handover (GrdDBusRemoteDesktopRdpHandover *interface, g_debug ("[DaemonSystem] At: %s, received StartHandover call", remote_client->handover_dst->object_path ); + if (!remote_client->session && !remote_client->handover_src) + { + g_warning ("[DaemonSystem] RDP client disconnected during the handover"); + goto err; + } + routing_token = get_routing_token_from_id (remote_client->id); g_object_get (G_OBJECT (settings), @@ -263,6 +269,7 @@ on_handle_start_handover (GrdDBusRemoteDesktopRdpHandover *interface, return G_DBUS_METHOD_INVOCATION_HANDLED; err: + abort_handover (remote_client); g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_OBJECT, -- GitLab From 311e2dc2f85f423c24f6a74d18cce8e31a1143a2 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 24 Aug 2022 21:04:57 +0200 Subject: [PATCH 22/27] meson: Add systemd units for the daemon-system and the daemon-handover - gnome-remote-desktop.service is a system level service while - gnome-remote-desktop-handover.service is a session level service that will be started in headless user sessions. Also, add a .desktop file to start the daemon-handover in headless login sessions: - org.gnome.RemoteDesktop.Handover.desktop --- data/gnome-remote-desktop-handover.service.in | 11 +++++ .../gnome-remote-desktop-headless.service.in | 0 data/gnome-remote-desktop-system.service.in | 12 ++++++ {src => data}/gnome-remote-desktop.service.in | 0 data/meson.build | 42 +++++++++++++++++++ ...rg.gnome.RemoteDesktop.Handover.desktop.in | 10 +++++ meson.build | 22 +++++++--- meson_options.txt | 5 +++ src/meson.build | 12 ------ 9 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 data/gnome-remote-desktop-handover.service.in rename {src => data}/gnome-remote-desktop-headless.service.in (100%) create mode 100644 data/gnome-remote-desktop-system.service.in rename {src => data}/gnome-remote-desktop.service.in (100%) create mode 100644 data/org.gnome.RemoteDesktop.Handover.desktop.in diff --git a/data/gnome-remote-desktop-handover.service.in b/data/gnome-remote-desktop-handover.service.in new file mode 100644 index 00000000..d93828d3 --- /dev/null +++ b/data/gnome-remote-desktop-handover.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=GNOME Remote Desktop Handover + +[Service] +Type=dbus +BusName=org.gnome.RemoteDesktop.Handover +ExecStart=@libexecdir@/gnome-remote-desktop-daemon --handover +Restart=on-failure + +[Install] +WantedBy=gnome-session.target diff --git a/src/gnome-remote-desktop-headless.service.in b/data/gnome-remote-desktop-headless.service.in similarity index 100% rename from src/gnome-remote-desktop-headless.service.in rename to data/gnome-remote-desktop-headless.service.in diff --git a/data/gnome-remote-desktop-system.service.in b/data/gnome-remote-desktop-system.service.in new file mode 100644 index 00000000..2abf562d --- /dev/null +++ b/data/gnome-remote-desktop-system.service.in @@ -0,0 +1,12 @@ +[Unit] +Description=GNOME Remote Desktop + +[Service] +Type=dbus +User=@GRD_USERNAME@ +BusName=org.gnome.RemoteDesktop +ExecStart=@libexecdir@/gnome-remote-desktop-daemon --system +Restart=on-failure + +[Install] +WantedBy=graphical.target diff --git a/src/gnome-remote-desktop.service.in b/data/gnome-remote-desktop.service.in similarity index 100% rename from src/gnome-remote-desktop.service.in rename to data/gnome-remote-desktop.service.in diff --git a/data/meson.build b/data/meson.build index a93b547e..ff25941a 100644 --- a/data/meson.build +++ b/data/meson.build @@ -17,4 +17,46 @@ if have_rdp 'GRD_USERNAME': grd_username, }, install_dir: dbus_sys_dir) + + configure_file(input: 'org.gnome.RemoteDesktop.Handover.desktop.in', + output: 'org.gnome.RemoteDesktop.Handover.desktop', + configuration: { + 'libexecdir': libexecdir, + }, + install_dir: desktopdir) +endif + +if get_option('systemd') + configure_file(input: 'gnome-remote-desktop.service.in', + output: 'gnome-remote-desktop.service', + configuration: { + 'libexecdir': libexecdir, + }, + install_dir: userunitdir) + configure_file(input: 'gnome-remote-desktop-headless.service.in', + output: 'gnome-remote-desktop-headless.service', + configuration: { + 'libexecdir': libexecdir, + }, + install_dir: userunitdir) + + if have_rdp + grd_system_service = configure_file(input: 'gnome-remote-desktop-system.service.in', + output: 'gnome-remote-desktop-system.service', + configuration: { + 'libexecdir': libexecdir, + 'GRD_USERNAME': grd_username, + }) + install_data(grd_system_service, + rename: 'gnome-remote-desktop.service', + install_dir: systemunitdir + ) + + configure_file(input: 'gnome-remote-desktop-handover.service.in', + output: 'gnome-remote-desktop-handover.service', + configuration: { + 'libexecdir': libexecdir, + }, + install_dir: userunitdir) + endif endif diff --git a/data/org.gnome.RemoteDesktop.Handover.desktop.in b/data/org.gnome.RemoteDesktop.Handover.desktop.in new file mode 100644 index 00000000..a355af18 --- /dev/null +++ b/data/org.gnome.RemoteDesktop.Handover.desktop.in @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=Gnome Remote Desktop Handover Daemon +Exec=@libexecdir@/gnome-remote-desktop-daemon --handover +Terminal=false +StartupNotify=true +NoDisplay=true +X-GNOME-Autostart-Notify=true +X-GNOME-AutoRestart=true +X-GNOME-HiddenUnderSystemd=true diff --git a/meson.build b/meson.build index 0a94281b..87993b3e 100644 --- a/meson.build +++ b/meson.build @@ -67,6 +67,7 @@ libexecdir = join_paths(prefix, get_option('libexecdir')) datadir = join_paths(prefix, get_option('datadir')) mandir = join_paths(prefix, get_option('mandir')) schemadir = join_paths(datadir, 'glib-2.0', 'schemas') +desktopdir = join_paths(datadir, 'applications') dbus_sys_dir = (get_option('dbus_sys_dir') != '') ? get_option('dbus_sys_dir') : join_paths(datadir, 'dbus-1', 'system.d') grd_datadir = join_paths(datadir, 'gnome-remote-desktop') @@ -95,15 +96,24 @@ configure_file(input: 'config.h.meson', configinc = include_directories('.') -servicedir = get_option('systemd_user_unit_dir') +userunitdir = get_option('systemd_user_unit_dir') +systemunitdir = get_option('systemd_system_unit_dir') if systemd_dep.found() - if servicedir == '' - servicedir = systemd_dep.get_variable(pkgconfig: 'systemduserunitdir') + if userunitdir == '' + userunitdir = systemd_dep.get_variable(pkgconfig: 'systemduserunitdir') endif - if servicedir == '' + if userunitdir == '' error('Couldn\'t determine systemd user unit service directory') endif + + if systemunitdir == '' + systemunitdir = systemd_dep.get_variable(pkgconfig: 'systemdsystemunitdir') + endif + + if systemunitdir == '' + error('Couldn\'t determine systemd system unit service directory') + endif endif top_srcdir = meson.current_source_dir() @@ -134,7 +144,9 @@ output = [ ' Prefix....................... ' + prefix, ' libexecdir................... ' + libexecdir, ' datadir...................... ' + datadir, - ' systemd user unit dir........ ' + servicedir, + ' desktopdir................... ' + desktopdir, + ' systemd user unit dir........ ' + userunitdir, + ' systemd system unit dir...... ' + systemunitdir, ' GSettings schema dir......... ' + schemadir, ' System DBus dir.............. ' + dbus_sys_dir, '', diff --git a/meson_options.txt b/meson_options.txt index e48d5f52..bd6f22fd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -33,6 +33,11 @@ option('systemd_user_unit_dir', value: '', description: 'systemd user service directory') +option('systemd_system_unit_dir', + type: 'string', + value: '', + description: 'systemd system service directory') + option('conf_dir', type: 'string', value: '', diff --git a/src/meson.build b/src/meson.build index 7ff3988f..33edb769 100644 --- a/src/meson.build +++ b/src/meson.build @@ -259,18 +259,6 @@ executable('grdctl', include_directories: [configinc], install: true) -service_config = configuration_data() -service_config.set('libexecdir', libexecdir) - -configure_file(input: 'gnome-remote-desktop.service.in', - output: 'gnome-remote-desktop.service', - configuration: service_config, - install_dir: servicedir) -configure_file(input: 'gnome-remote-desktop-headless.service.in', - output: 'gnome-remote-desktop-headless.service', - configuration: service_config, - install_dir: servicedir) - generated_enums_schema = custom_target('gsettings-enums', input: 'grd-enums.h', output: 'org.gnome.desktop.remote-desktop.enums.xml', -- GitLab From 25b9655107367669c09d64602ebe3b37dcdf5c5d Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Wed, 3 May 2023 12:03:00 +0200 Subject: [PATCH 23/27] ctl: Add --system option This option is used to set the daemon-system settings. The settings that the daemon-system uses are the RDP credentials and RDP cert/key. It relies on pkexec to be used as gnome-remote-desktop user and requires admin authentication to write to the daemon-system configuration key file. It can start and stop the systemd service too. In that case, it won't be used pkexec because managing unit files already has a polkit action which will require admin authentication. --- config.h.meson | 3 + data/meson.build | 7 + ...edesktop.configure-system-daemon.policy.in | 21 ++ meson.build | 3 + src/grd-ctl.c | 314 ++++++++++++++++-- src/meson.build | 2 + 6 files changed, 321 insertions(+), 29 deletions(-) create mode 100644 data/org.gnome.remotedesktop.configure-system-daemon.policy.in diff --git a/config.h.meson b/config.h.meson index 909e7cdd..89e1ddc7 100644 --- a/config.h.meson +++ b/config.h.meson @@ -17,3 +17,6 @@ /* Path of the system daemon settings */ #mesondefine GRD_CONF + +/* Username of system grd daemon */ +#mesondefine GRD_USERNAME diff --git a/data/meson.build b/data/meson.build index ff25941a..770a6a03 100644 --- a/data/meson.build +++ b/data/meson.build @@ -60,3 +60,10 @@ if get_option('systemd') install_dir: userunitdir) endif endif + +configure_file(input: 'org.gnome.remotedesktop.configure-system-daemon.policy.in', + output: 'org.gnome.remotedesktop.configure-system-daemon.policy', + configuration: { + 'bindir': bindir, + }, + install_dir: join_paths(datadir, 'polkit-1', 'actions')) diff --git a/data/org.gnome.remotedesktop.configure-system-daemon.policy.in b/data/org.gnome.remotedesktop.configure-system-daemon.policy.in new file mode 100644 index 00000000..9ea548a7 --- /dev/null +++ b/data/org.gnome.remotedesktop.configure-system-daemon.policy.in @@ -0,0 +1,21 @@ + + + + + The GNOME Project + http://www.gnome.org/ + + + Allow to configure gnome-remote-desktop's system daemon settings. + Authentication is required to configure gnome-remote-desktop's system daemon settings. + + auth_admin + auth_admin + auth_admin_keep + + @bindir@/grdctl + + + diff --git a/meson.build b/meson.build index 87993b3e..e8a9a933 100644 --- a/meson.build +++ b/meson.build @@ -64,6 +64,7 @@ endif prefix = get_option('prefix') libexecdir = join_paths(prefix, get_option('libexecdir')) +bindir = join_paths(prefix, get_option('bindir')) datadir = join_paths(prefix, get_option('datadir')) mandir = join_paths(prefix, get_option('mandir')) schemadir = join_paths(datadir, 'glib-2.0', 'schemas') @@ -89,6 +90,7 @@ cdata.set('HAVE_VNC', have_vnc) cdata.set_quoted('GRD_DATA_DIR', grd_datadir) cdata.set_quoted('GRD_CONF', grd_conf) +cdata.set_quoted('GRD_USERNAME', grd_username) configure_file(input: 'config.h.meson', output: 'config.h', @@ -143,6 +145,7 @@ output = [ '', ' Prefix....................... ' + prefix, ' libexecdir................... ' + libexecdir, + ' bindir....................... ' + bindir, ' datadir...................... ' + datadir, ' desktopdir................... ' + desktopdir, ' systemd user unit dir........ ' + userunitdir, diff --git a/src/grd-ctl.c b/src/grd-ctl.c index b9296c26..f9329694 100644 --- a/src/grd-ctl.c +++ b/src/grd-ctl.c @@ -22,12 +22,17 @@ #include #include +#include #include #include +#include #include "grd-enums.h" +#include "grd-settings-system.h" #include "grd-settings-user.h" +#define GRD_SYSTEMD_SERVICE "gnome-remote-desktop.service" + typedef struct _SubCommand { const char *subcommand; @@ -64,10 +69,7 @@ process_options (GrdSettings *settings, int i; if (argc <= 0) - { - print_usage (); - return EXIT_FAILURE; - } + return EX_USAGE; for (i = 0; i < n_subcommands; i++) { @@ -80,7 +82,7 @@ process_options (GrdSettings *settings, { g_printerr ("Wrong number of arguments for subcommand '%s'\n", argv[0]); - return EXIT_FAILURE; + return EX_USAGE; } if (!subcommands[i].process (settings, @@ -90,12 +92,127 @@ process_options (GrdSettings *settings, return EXIT_FAILURE; } - g_settings_sync (); + if (!GRD_IS_SETTINGS_SYSTEM (settings)) + g_settings_sync (); + return EXIT_SUCCESS; } g_printerr ("Unknown subcommand '%s'\n", argv[0]); - return EXIT_FAILURE; + return EX_USAGE; +} + +gboolean +start_systemd_unit (GBusType bus_type, + const char *unit, + GError **error) +{ + g_autoptr (GDBusConnection) connection = NULL; + g_autoptr (GVariant) reply = NULL; + const char *mode = "replace"; + + connection = g_bus_get_sync (bus_type, NULL, error); + if (!connection) + return FALSE; + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + g_variant_new ("(ss)", + unit, mode), + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, error); + + return reply != NULL; +} + +gboolean +stop_systemd_unit (GBusType bus_type, + const char *unit, + GError **error) +{ + g_autoptr (GDBusConnection) connection = NULL; + g_autoptr (GVariant) reply = NULL; + const char *mode = "replace"; + + connection = g_bus_get_sync (bus_type, NULL, error); + if (!connection) + return FALSE; + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + g_variant_new ("(ss)", + unit, mode), + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, error); + + return reply != NULL; +} + +static gboolean +systemd_unit_is_active (GBusType bus_type, + const char *unit, + GError **error) +{ + g_autoptr (GDBusProxy) proxy = NULL; + g_autoptr (GVariant) result = NULL; + g_autofree char *object_path = NULL; + g_autofree char *active_state = NULL; + g_autoptr (GDBusProxy) unit_proxy = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync (bus_type, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + NULL, + error); + if (!proxy) + return FALSE; + + result = g_dbus_proxy_call_sync (proxy, + "GetUnit", + g_variant_new ("(s)", unit), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + error); + if (!result) + return FALSE; + + g_variant_get (result, "(o)", &object_path); + g_clear_pointer (&result, g_variant_unref); + + unit_proxy = g_dbus_proxy_new_for_bus_sync (bus_type, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.systemd1", + object_path, + "org.freedesktop.systemd1.Unit", + NULL, + error); + if (!unit_proxy) + return FALSE; + + result = g_dbus_proxy_get_cached_property (unit_proxy, "ActiveState"); + if (!result) + { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, + "Error getting ActiveState property"); + return FALSE; + } + + g_variant_get (result, "s", &active_state); + + return g_str_equal (active_state, "active"); } #ifdef HAVE_RDP @@ -118,6 +235,13 @@ rdp_enable (GrdSettings *settings, char **argv, GError **error) { + if (GRD_IS_SETTINGS_SYSTEM (settings)) + { + return start_systemd_unit (G_BUS_TYPE_SYSTEM, + GRD_SYSTEMD_SERVICE, + error); + } + g_object_set (G_OBJECT (settings), "rdp-enabled", TRUE, NULL); return TRUE; } @@ -128,6 +252,13 @@ rdp_disable (GrdSettings *settings, char **argv, GError **error) { + if (GRD_IS_SETTINGS_SYSTEM (settings)) + { + return stop_systemd_unit (G_BUS_TYPE_SYSTEM, + GRD_SYSTEMD_SERVICE, + error); + } + g_object_set (G_OBJECT (settings), "rdp-enabled", FALSE, NULL); return TRUE; } @@ -471,6 +602,7 @@ print_help (void) "\n" "Options:\n" " --headless - Use headless credentials storage\n" + " --system - Configure system daemon\n" " --help - Print this help text\n"); print_usage (); @@ -501,6 +633,7 @@ create_settings (GrdRuntimeMode runtime_mode) case GRD_RUNTIME_MODE_HEADLESS: return GRD_SETTINGS (grd_settings_user_new (runtime_mode)); case GRD_RUNTIME_MODE_SYSTEM: + return GRD_SETTINGS (grd_settings_system_new ()); case GRD_RUNTIME_MODE_HANDOVER: g_assert_not_reached (); } @@ -554,13 +687,17 @@ print_rdp_status (GrdSettings *settings, NULL); printf ("RDP:\n"); - printf ("\tStatus: %s\n", status_to_string (enabled, use_colors)); + if (!GRD_IS_SETTINGS_SYSTEM (settings)) + printf ("\tStatus: %s\n", status_to_string (enabled, use_colors)); printf ("\tPort: %u\n", port); printf ("\tTLS certificate: %s\n", tls_cert); printf ("\tTLS key: %s\n", tls_key); - printf ("\tView-only: %s\n", view_only ? "yes" : "no"); - printf ("\tNegotiate port: %s\n", negotiate_port ? "yes" : "no"); + if (!GRD_IS_SETTINGS_SYSTEM (settings)) + { + printf ("\tView-only: %s\n", view_only ? "yes" : "no"); + printf ("\tNegotiate port: %s\n", negotiate_port ? "yes" : "no"); + } grd_settings_get_rdp_credentials (settings, &username, &password, @@ -637,6 +774,50 @@ print_vnc_status (GrdSettings *settings, } #endif /* HAVE_VNC */ +static const char * +unit_status_to_string (gboolean running, + gboolean use_colors) +{ + if (use_colors) + { + if (running) + return "\x1b[1mactive\033[m"; + else + return "\x1b[1minactive\033[m"; + } + else + { + if (running) + return "active"; + else + return "inactive"; + } +} + +static void +print_service_status (GrdSettings *settings, + gboolean use_colors) +{ + GBusType bus_type; + gboolean service_running; + g_autoptr (GError) error = NULL; + + if (GRD_IS_SETTINGS_SYSTEM (settings)) + bus_type = G_BUS_TYPE_SYSTEM; + else + bus_type = G_BUS_TYPE_SESSION; + + service_running = systemd_unit_is_active (bus_type, + GRD_SYSTEMD_SERVICE, + &error); + if (error) + return; + + printf ("Overall:\n"); + printf ("\tUnit status: %s\n", unit_status_to_string (service_running, + use_colors)); +} + static int print_status (GrdSettings *settings, int argc, @@ -647,8 +828,7 @@ print_status (GrdSettings *settings, if (argc > 1) { - print_usage (); - return EXIT_FAILURE; + return EX_USAGE; } else if (argc == 1) { @@ -658,18 +838,20 @@ print_status (GrdSettings *settings, } else { - print_usage (); - return EXIT_FAILURE; + return EX_USAGE; } } use_colors = isatty (fileno (stdout)); + print_service_status (settings, use_colors); + #ifdef HAVE_RDP print_rdp_status (settings, use_colors, show_credentials); #endif /* HAVE_RDP */ #ifdef HAVE_VNC - print_vnc_status (settings, use_colors, show_credentials); + if (!GRD_IS_SETTINGS_SYSTEM (settings)) + print_vnc_status (settings, use_colors, show_credentials); #endif /* HAVE_VNC */ return EXIT_SUCCESS; @@ -683,20 +865,21 @@ main (int argc, GrdRuntimeMode runtime_mode; int i; int arg_shift; + int exit_code = EX_USAGE; + gboolean is_switching_rdp = FALSE; + struct passwd *pw; g_set_prgname (argv[0]); g_log_set_handler (NULL, G_LOG_LEVEL_WARNING, log_handler, NULL); if (argc < 2) - { - print_usage (); - return EXIT_FAILURE; - } + goto done; if (argc == 2 && strcmp (argv[1], "--help") == 0) { print_help (); - return EXIT_SUCCESS; + exit_code = EXIT_SUCCESS; + goto done; } if (argc > 2 && strcmp (argv[1], "--headless") == 0) @@ -704,6 +887,11 @@ main (int argc, runtime_mode = GRD_RUNTIME_MODE_HEADLESS; i = 2; } + else if (argc > 1 && strcmp (argv[1], "--system") == 0) + { + runtime_mode = GRD_RUNTIME_MODE_SYSTEM; + i = 2; + } else { runtime_mode = GRD_RUNTIME_MODE_SCREEN_SHARE; @@ -712,34 +900,102 @@ main (int argc, arg_shift = i + 1; + if (argc > i + 1) + { + is_switching_rdp = (strcmp (argv[i], "rdp") == 0 && + strcmp (argv[i + 1], "enable") == 0) || + (strcmp (argv[i], "rdp") == 0 && + strcmp (argv[i + 1], "disable") == 0); + } + + if (runtime_mode == GRD_RUNTIME_MODE_SYSTEM && !is_switching_rdp) + { + g_autoptr (GStrvBuilder) builder = NULL; + g_auto (GStrv) new_argv = NULL; + g_autoptr (GError) error = NULL; + int wait_status; + int j; + + builder = g_strv_builder_new (); + + g_strv_builder_add (builder, "pkexec"); + g_strv_builder_add (builder, "--user"); + g_strv_builder_add (builder, GRD_USERNAME); + g_strv_builder_add (builder, argv[0]); + + for (j = 2; j < argc; j++) + g_strv_builder_add (builder, argv[j]); + + new_argv = g_strv_builder_end (builder); + + g_spawn_sync (NULL, + new_argv, + NULL, + G_SPAWN_SEARCH_PATH + | G_SPAWN_CHILD_INHERITS_STDOUT, + NULL, + NULL, + NULL, + NULL, + &wait_status, + &error); + if (error) + { + fprintf (stderr, "Failed to start the configuration of the system daemon: %s\n", + error->message); + + if (WIFEXITED (wait_status)) + exit_code = WEXITSTATUS (wait_status); + else + exit_code = EX_SOFTWARE; + } + else + { + exit_code = EXIT_SUCCESS; + } + + goto done; + } + + pw = getpwnam (GRD_USERNAME); + if (geteuid () == pw->pw_uid) + runtime_mode = GRD_RUNTIME_MODE_SYSTEM; + settings = create_settings (runtime_mode); if (!settings) { fprintf (stderr, "Failed to initialize settings.\n"); - return EXIT_FAILURE; + exit_code = EXIT_FAILURE; + goto done; } #ifdef HAVE_RDP if (strcmp (argv[i], "rdp") == 0) { - return process_rdp_options (settings, - argc - arg_shift, argv + arg_shift); + exit_code = process_rdp_options (settings, + argc - arg_shift, argv + arg_shift); + goto done; } #endif #ifdef HAVE_VNC if (strcmp (argv[i], "vnc") == 0) { - return process_vnc_options (settings, - argc - arg_shift, argv + arg_shift); + exit_code = process_vnc_options (settings, + argc - arg_shift, argv + arg_shift); + goto done; } #endif if (strcmp (argv[i], "status") == 0) { - return print_status (settings, - argc - arg_shift, argv + arg_shift); + exit_code = print_status (settings, + argc - arg_shift, argv + arg_shift); + goto done; } - print_usage (); - return EXIT_FAILURE; +done: + if (exit_code == EX_USAGE) + print_usage (); + + return exit_code; } diff --git a/src/meson.build b/src/meson.build index 33edb769..2e7e48d9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -230,6 +230,8 @@ ctl_sources = ([ 'grd-debug.h', 'grd-settings.c', 'grd-settings.h', + 'grd-settings-system.c', + 'grd-settings-system.h', 'grd-settings-user.c', 'grd-settings-user.h', credentials_sources, -- GitLab From d260b572173548f64d80643c3b173af3b04e76cc Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Fri, 24 Nov 2023 10:21:45 +0100 Subject: [PATCH 24/27] ctl: On RDP/VNC 'set-port' command, check if value is numeric --- src/grd-ctl.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/grd-ctl.c b/src/grd-ctl.c index f9329694..585ba1e0 100644 --- a/src/grd-ctl.c +++ b/src/grd-ctl.c @@ -20,6 +20,7 @@ #include "config.h" +#include #include #include #include @@ -215,6 +216,18 @@ systemd_unit_is_active (GBusType bus_type, return g_str_equal (active_state, "active"); } +static gboolean +is_numeric (const char *s) +{ + while (*s) + { + if (isdigit (*s++) == 0) + return FALSE; + } + + return TRUE; +} + #ifdef HAVE_RDP static gboolean rdp_set_port (GrdSettings *settings, @@ -224,6 +237,13 @@ rdp_set_port (GrdSettings *settings, { int port; + if (!is_numeric (argv[0])) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "The port must be an integer"); + return FALSE; + } + port = strtol (argv[0], NULL, 10); g_object_set (G_OBJECT (settings), "rdp-port", port, NULL); return TRUE; @@ -394,6 +414,13 @@ vnc_set_port (GrdSettings *settings, { int port; + if (!is_numeric (argv[0])) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "The port must be an integer"); + return FALSE; + } + port = strtol (argv[0], NULL, 10); g_object_set (G_OBJECT (settings), "vnc-port", port, NULL); return TRUE; -- GitLab From b114f9185f69d8cc904f36eaf9766a6f033a92ac Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 27 Nov 2023 15:46:33 +0100 Subject: [PATCH 25/27] daemon-handover: Shutdown when daemon-system service disappears --- src/grd-daemon-handover.c | 61 +++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/grd-daemon-handover.c b/src/grd-daemon-handover.c index 0e7e4574..89cb37ab 100644 --- a/src/grd-daemon-handover.c +++ b/src/grd-daemon-handover.c @@ -47,6 +47,8 @@ struct _GrdDaemonHandover GrdSession *session; + unsigned int gnome_remote_desktop_watch_name_id; + unsigned int logout_source_id; }; @@ -376,9 +378,11 @@ on_remote_desktop_rdp_dispatcher_proxy_acquired (GObject *object, g_autoptr (GrdDBusRemoteDesktopRdpDispatcher) proxy = NULL; g_autoptr (GError) error = NULL; + if (daemon_handover->remote_desktop_dispatcher) + return; + proxy = - grd_dbus_remote_desktop_rdp_dispatcher_proxy_new_for_bus_finish (result, - &error); + grd_dbus_remote_desktop_rdp_dispatcher_proxy_new_finish (result, &error); if (!proxy) { g_warning ("[DaemonHandover] Failed to create remote desktop " @@ -395,6 +399,39 @@ on_remote_desktop_rdp_dispatcher_proxy_acquired (GObject *object, daemon_handover); } +static void +on_gnome_remote_desktop_name_appeared (GDBusConnection *connection, + const char *name, + const char *name_owner, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + GCancellable *cancellable = + grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); + + grd_dbus_remote_desktop_rdp_dispatcher_proxy_new ( + connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + REMOTE_DESKTOP_BUS_NAME, + REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH, + cancellable, + on_remote_desktop_rdp_dispatcher_proxy_acquired, + daemon_handover); +} + +static void +on_gnome_remote_desktop_name_vanished (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + GrdDaemonHandover *daemon_handover = user_data; + + g_warning ("[DaemonHandover] %s name vanished, shutting down daemon", + REMOTE_DESKTOP_BUS_NAME); + + g_application_release (G_APPLICATION (daemon_handover)); +} + GrdDaemonHandover * grd_daemon_handover_new (GError **error) { @@ -420,22 +457,19 @@ static void grd_daemon_handover_startup (GApplication *app) { GrdDaemonHandover *daemon_handover = GRD_DAEMON_HANDOVER (app); - GCancellable *cancellable = - grd_daemon_get_cancellable (GRD_DAEMON (daemon_handover)); grd_daemon_acquire_mutter_dbus_proxies (GRD_DAEMON (daemon_handover)); g_signal_connect (daemon_handover, "mutter-proxy-acquired", G_CALLBACK (grd_daemon_maybe_enable_services), NULL); - grd_dbus_remote_desktop_rdp_dispatcher_proxy_new_for_bus ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - REMOTE_DESKTOP_BUS_NAME, - REMOTE_DESKTOP_DISPATCHER_OBJECT_PATH, - cancellable, - on_remote_desktop_rdp_dispatcher_proxy_acquired, - daemon_handover); + daemon_handover->gnome_remote_desktop_watch_name_id = + g_bus_watch_name (G_BUS_TYPE_SYSTEM, + REMOTE_DESKTOP_BUS_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_gnome_remote_desktop_name_appeared, + on_gnome_remote_desktop_name_vanished, + daemon_handover, NULL); g_signal_connect (daemon_handover, "rdp-server-started", G_CALLBACK (on_rdp_server_started), NULL); @@ -454,6 +488,9 @@ grd_daemon_handover_shutdown (GApplication *app) g_clear_object (&daemon_handover->remote_desktop_handover); g_clear_object (&daemon_handover->remote_desktop_dispatcher); + g_clear_handle_id (&daemon_handover->gnome_remote_desktop_watch_name_id, + g_bus_unwatch_name); + g_clear_handle_id (&daemon_handover->logout_source_id, g_source_remove); G_APPLICATION_CLASS (grd_daemon_handover_parent_class)->shutdown (app); -- GitLab From ff5a85e6d959ec81f4842f0d326e8ebd6eb1f92f Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Mon, 27 Nov 2023 18:03:34 +0100 Subject: [PATCH 26/27] session-rdp: Use RDSTLS security on daemon-handover When the runtime mode is handover, all RDP clients are connected through the RDP redirection process. That process allows the use of a different security method called RDSTLS. Only with this security method enabled, the mstsc client can do a redirection and successfully authenticate. Also, at this moment FreeRDP3-based clients fail authenticating with NLA security on redirection. Enabling RDSTLS allows them to do the redirection and authenticate at least with RDSTLS. --- src/grd-session-rdp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/grd-session-rdp.c b/src/grd-session-rdp.c index b5d70092..30ecaeed 100644 --- a/src/grd-session-rdp.c +++ b/src/grd-session-rdp.c @@ -449,6 +449,9 @@ grd_session_rdp_send_server_redirection (GrdSessionRdp *session_rdp, const char *certificate) { freerdp_peer *peer = session_rdp->peer; + rdpSettings *rdp_settings = peer->context->settings; + uint32_t os_major_type = + freerdp_settings_get_uint32 (rdp_settings, FreeRDP_OsMajorType); rdpRedirection *redirection; g_autofree BYTE *certificate_container = NULL; g_autofree WCHAR *utf16_password = NULL; @@ -479,6 +482,8 @@ grd_session_rdp_send_server_redirection (GrdSessionRdp *session_rdp, /* Password */ redirection_flags |= LB_PASSWORD; + if (os_major_type != OSMAJORTYPE_IOS && os_major_type != OSMAJORTYPE_ANDROID) + redirection_flags |= LB_PASSWORD_IS_PK_ENCRYPTED; utf16_password = get_utf16_string (password, &size); g_assert (utf16_password); redirection_set_byte_option (redirection, LB_PASSWORD, @@ -2081,6 +2086,13 @@ init_rdp_session (GrdSessionRdp *session_rdp, freerdp_settings_set_bool (rdp_settings, FreeRDP_TlsSecurity, FALSE); freerdp_settings_set_bool (rdp_settings, FreeRDP_NlaSecurity, TRUE); + if (grd_context_get_runtime_mode (context) == GRD_RUNTIME_MODE_HANDOVER) + { + freerdp_settings_set_string (rdp_settings, FreeRDP_Username, username); + freerdp_settings_set_string (rdp_settings, FreeRDP_Password, password); + freerdp_settings_set_bool (rdp_settings, FreeRDP_RdstlsSecurity, TRUE); + } + freerdp_settings_set_uint32 (rdp_settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX); freerdp_settings_set_uint32 (rdp_settings, FreeRDP_OsMinorType, -- GitLab From a7121f9f14e1891bd2d86d673ded1ca59d5d2cc3 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Tue, 2 Jan 2024 20:19:15 +0100 Subject: [PATCH 27/27] meson: Install a sysusers conf file to automatically create the grd user The daemon-system is run as gnome-remote-desktop user. Use the systemd-sysusers utility to create that user. Also use the systemd-tmpfiles to set the ownership of the conf file. A home directory is also needed when the GKeyFile backend is used to store the credentials. So use the systemd-tmpfiles utility to create it. --- data/gnome-remote-desktop-sysusers.conf.in | 2 ++ data/gnome-remote-desktop-tmpfiles.conf.in | 6 ++++++ data/meson.build | 17 +++++++++++++++++ meson.build | 19 +++++++++++++++++++ meson_options.txt | 15 +++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 data/gnome-remote-desktop-sysusers.conf.in create mode 100644 data/gnome-remote-desktop-tmpfiles.conf.in diff --git a/data/gnome-remote-desktop-sysusers.conf.in b/data/gnome-remote-desktop-sysusers.conf.in new file mode 100644 index 00000000..70234ecf --- /dev/null +++ b/data/gnome-remote-desktop-sysusers.conf.in @@ -0,0 +1,2 @@ +# sysusers.d file to ensure the existence of the gnome-remote-desktop user +u @GRD_USERNAME@ - "GNOME Remote Desktop" @GRD_HOMEDIR@ diff --git a/data/gnome-remote-desktop-tmpfiles.conf.in b/data/gnome-remote-desktop-tmpfiles.conf.in new file mode 100644 index 00000000..33e04dfd --- /dev/null +++ b/data/gnome-remote-desktop-tmpfiles.conf.in @@ -0,0 +1,6 @@ +# tmpfiles.d file to ensure the existence of the home directory for gnome-remote-desktop user +d @GRD_HOMEDIR@ 0700 @GRD_USERNAME@ @GRD_USERNAME@ +z @GRD_HOMEDIR@ 0700 @GRD_USERNAME@ @GRD_USERNAME@ +Z @GRD_CONFDIR@ - @GRD_USERNAME@ @GRD_USERNAME@ +z @GRD_CONFDIR@ 0755 +z @GRD_CONFDIR@/grd.conf 0644 diff --git a/data/meson.build b/data/meson.build index 770a6a03..71b7daa2 100644 --- a/data/meson.build +++ b/data/meson.build @@ -11,6 +11,23 @@ if have_rdp install_dir: grd_confdir, ) + configure_file(input: 'gnome-remote-desktop-sysusers.conf.in', + output: 'gnome-remote-desktop-sysusers.conf', + configuration: { + 'GRD_USERNAME': grd_username, + 'GRD_HOMEDIR': grd_homedir, + }, + install_dir: sysusersdir) + + configure_file(input: 'gnome-remote-desktop-tmpfiles.conf.in', + output: 'gnome-remote-desktop-tmpfiles.conf', + configuration: { + 'GRD_USERNAME': grd_username, + 'GRD_HOMEDIR': grd_homedir, + 'GRD_CONFDIR': grd_confdir, + }, + install_dir: tmpfilesdir) + configure_file(input: 'org.gnome.RemoteDesktop.conf.in', output: 'org.gnome.RemoteDesktop.conf', configuration: { diff --git a/meson.build b/meson.build index e8a9a933..9179db4f 100644 --- a/meson.build +++ b/meson.build @@ -80,6 +80,7 @@ endif grd_conf = join_paths(grd_confdir, 'grd.conf') grd_username = get_option('user') +grd_homedir = get_option('home_dir') cdata = configuration_data() cdata.set_quoted('GETTEXT_PACKAGE', 'gnome-remote-desktop') @@ -100,6 +101,8 @@ configinc = include_directories('.') userunitdir = get_option('systemd_user_unit_dir') systemunitdir = get_option('systemd_system_unit_dir') +sysusersdir = get_option('systemd_sysusers_dir') +tmpfilesdir = get_option('systemd_tmpfiles_dir') if systemd_dep.found() if userunitdir == '' userunitdir = systemd_dep.get_variable(pkgconfig: 'systemduserunitdir') @@ -116,6 +119,22 @@ if systemd_dep.found() if systemunitdir == '' error('Couldn\'t determine systemd system unit service directory') endif + + if sysusersdir == '' + sysusersdir = systemd_dep.get_variable(pkgconfig: 'sysusersdir') + endif + + if sysusersdir == '' + error('Couldn\'t determine systemd sysusers directory') + endif + + if tmpfilesdir == '' + tmpfilesdir = systemd_dep.get_variable(pkgconfig: 'tmpfilesdir') + endif + + if tmpfilesdir == '' + error('Couldn\'t determine systemd tmpfilesdir directory') + endif endif top_srcdir = meson.current_source_dir() diff --git a/meson_options.txt b/meson_options.txt index bd6f22fd..b0b279da 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -38,6 +38,16 @@ option('systemd_system_unit_dir', value: '', description: 'systemd system service directory') +option('systemd_sysusers_dir', + type: 'string', + value: '', + description: 'systemd sysusers directory') + +option('systemd_tmpfiles_dir', + type: 'string', + value: '', + description: 'systemd tmpfiles directory') + option('conf_dir', type: 'string', value: '', @@ -48,6 +58,11 @@ option('user', value: 'gnome-remote-desktop', description: 'Username for the GNOME Remote Desktop system service') +option('home_dir', + type: 'string', + value: '/var/lib/gnome-remote-desktop', + description: 'gnome-remote-desktop user directory') + option('dbus_sys_dir', type: 'string', value: '', -- GitLab