import gnome-remote-desktop-0.1.6-5.el8

This commit is contained in:
CentOS Sources 2019-08-01 10:43:11 -04:00 committed by Stepan Oksanichenko
commit 65e586f1e7
9 changed files with 1972 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
SOURCES/gnome-remote-desktop-0.1.6.tar.xz

View File

@ -0,0 +1 @@
25504e6190dbfae00c7a648d1a4dd37c4ecc92b2 SOURCES/gnome-remote-desktop-0.1.6.tar.xz

View File

@ -0,0 +1,25 @@
From 8f760d73df6011330cd09da7ca7b8a3f40c9a3ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 7 Aug 2018 13:35:43 +0200
Subject: [PATCH] meson.build: Bump pipewire requirement to 0.2.2
---
meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
index 6951b89..34ec5ea 100644
--- a/meson.build
+++ b/meson.build
@@ -10,7 +10,7 @@ gnome = import('gnome')
glib_dep = dependency('glib-2.0')
gio_dep = dependency('gio-2.0')
gio_unix_dep = dependency('gio-unix-2.0')
-pipewire_dep = dependency('libpipewire-0.1')
+pipewire_dep = dependency('libpipewire-0.2', version: '>= 0.2.2')
systemd_dep = dependency('systemd')
libvncserver_dep = dependency('libvncserver')
libsecret_dep = dependency('libsecret-1')
--
2.17.1

View File

@ -0,0 +1,84 @@
From add0ea34fd1d6835c99aebeb4e56b805b38e53ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 1 Oct 2018 18:02:39 +0200
Subject: [PATCH 1/2] session/vnc: Don't requeue close session idle
If being closed due to a PipeWire error, RFB will still process state
and invoke callbacks when cleaning up the RFB screen, meaning we'd
requeue the close session idle handler. Avoid this by avoiding
requeueing if there is already one queued, and don't mark is as unqueued
until after actually stopping the session.
---
src/grd-session-vnc.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index ce4dd29..3c98eeb 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -165,6 +165,16 @@ grd_session_vnc_draw_buffer (GrdSessionVnc *session_vnc,
rfbProcessEvents (session_vnc->rfb_screen, 0);
}
+static void
+maybe_queue_close_session_idle (GrdSessionVnc *session_vnc)
+{
+ if (session_vnc->close_session_idle_id)
+ return;
+
+ session_vnc->close_session_idle_id =
+ g_idle_add (close_session_idle, session_vnc);
+}
+
static void
handle_client_gone (rfbClientPtr rfb_client)
{
@@ -172,8 +182,7 @@ handle_client_gone (rfbClientPtr rfb_client)
g_debug ("VNC client gone");
- session_vnc->close_session_idle_id =
- g_idle_add (close_session_idle, session_vnc);
+ maybe_queue_close_session_idle (session_vnc);
}
static void
@@ -670,12 +679,6 @@ grd_session_vnc_stop (GrdSession *session)
g_debug ("Stopping VNC session");
- if (session_vnc->close_session_idle_id)
- {
- g_source_remove (session_vnc->close_session_idle_id);
- session_vnc->close_session_idle_id = 0;
- }
-
g_clear_object (&session_vnc->pipewire_stream);
grd_session_vnc_detach_source (session_vnc);
@@ -683,6 +686,12 @@ grd_session_vnc_stop (GrdSession *session)
g_clear_object (&session_vnc->connection);
g_clear_pointer (&session_vnc->rfb_screen->frameBuffer, g_free);
g_clear_pointer (&session_vnc->rfb_screen, (GDestroyNotify) rfbScreenCleanup);
+
+ if (session_vnc->close_session_idle_id)
+ {
+ g_source_remove (session_vnc->close_session_idle_id);
+ session_vnc->close_session_idle_id = 0;
+ }
}
static gboolean
@@ -703,8 +712,7 @@ on_pipwire_stream_closed (GrdVncPipeWireStream *stream,
{
g_warning ("PipeWire stream closed, closing client");
- session_vnc->close_session_idle_id =
- g_idle_add (close_session_idle, session_vnc);
+ maybe_queue_close_session_idle (session_vnc);
}
static void
--
2.17.1

View File

@ -0,0 +1,953 @@
From fcfef86768d3dc63a2e7da799beb011800dff2ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Thu, 14 Jun 2018 12:21:37 +0200
Subject: [PATCH] vnc: Add anonymous TLS encryption support
Add support for encrypting the VNC connection using anonymous TLS. In
effect this means that the channel is encrypted using TLS but that no
authentication of the peers are done. This means the connection is still
vulnerable to man-in-the-middle attacks where an attacker proxies the
VNC connection.
---
meson.build | 1 +
src/grd-enums.h | 6 +
src/grd-session-vnc.c | 98 +++-
src/grd-session-vnc.h | 16 +
src/grd-settings.c | 27 ++
src/grd-settings.h | 2 +
src/grd-vnc-server.c | 45 ++
src/grd-vnc-tls.c | 444 ++++++++++++++++++
src/grd-vnc-tls.h | 28 ++
src/meson.build | 5 +-
...g.gnome.desktop.remote-desktop.gschema.xml | 10 +
11 files changed, 666 insertions(+), 16 deletions(-)
create mode 100644 src/grd-vnc-tls.c
create mode 100644 src/grd-vnc-tls.h
diff --git a/meson.build b/meson.build
index d8e20d2..f8c8cee 100644
--- a/meson.build
+++ b/meson.build
@@ -15,6 +15,7 @@ systemd_dep = dependency('systemd')
libvncserver_dep = dependency('libvncserver')
libsecret_dep = dependency('libsecret-1')
libnotify_dep = dependency('libnotify')
+gnutls_dep = dependency('gnutls')
cdata = configuration_data()
cdata.set_quoted('GETTEXT_PACKAGE', 'gnome-remote-desktop')
diff --git a/src/grd-enums.h b/src/grd-enums.h
index ffab821..4333863 100644
--- a/src/grd-enums.h
+++ b/src/grd-enums.h
@@ -27,4 +27,10 @@ typedef enum
GRD_VNC_AUTH_METHOD_PASSWORD
} GrdVncAuthMethod;
+typedef enum
+{
+ GRD_VNC_ENCRYPTION_NONE = 1 << 0,
+ GRD_VNC_ENCRYPTION_TLS_ANON = 1 << 1,
+} GrdVncEncryption;
+
#endif /* GRD_ENUMS_H */
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index 5d40971..ce4dd29 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -44,7 +44,9 @@ struct _GrdSessionVnc
{
GrdSession parent;
+ GrdVncServer *vnc_server;
GSocketConnection *connection;
+ GList *socket_grabs;
GSource *source;
rfbScreenInfoPtr rfb_screen;
rfbClientPtr rfb_client;
@@ -465,12 +467,30 @@ check_rfb_password (rfbClientPtr rfb_client,
}
}
+int
+grd_session_vnc_get_fd (GrdSessionVnc *session_vnc)
+{
+ return session_vnc->rfb_screen->inetdSock;
+}
+
int
grd_session_vnc_get_framebuffer_stride (GrdSessionVnc *session_vnc)
{
return session_vnc->rfb_screen->paddedWidthInBytes;
}
+rfbClientPtr
+grd_session_vnc_get_rfb_client (GrdSessionVnc *session_vnc)
+{
+ return session_vnc->rfb_client;
+}
+
+GrdVncServer *
+grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc)
+{
+ return session_vnc->vnc_server;
+}
+
static void
init_vnc_session (GrdSessionVnc *session_vnc)
{
@@ -509,33 +529,74 @@ init_vnc_session (GrdSessionVnc *session_vnc)
rfbProcessEvents (rfb_screen, 0);
}
+void
+grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc,
+ GrdVncSocketGrabFunc grab_func)
+{
+ session_vnc->socket_grabs = g_list_prepend (session_vnc->socket_grabs,
+ grab_func);
+}
+
+void
+grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc,
+ GrdVncSocketGrabFunc grab_func)
+{
+ session_vnc->socket_grabs = g_list_remove (session_vnc->socket_grabs,
+ grab_func);
+}
+
+static gboolean
+vnc_socket_grab_func (GrdSessionVnc *session_vnc,
+ GError **error)
+{
+ if (rfbIsActive (session_vnc->rfb_screen))
+ {
+ rfbProcessEvents (session_vnc->rfb_screen, 0);
+
+ if (session_vnc->pending_framebuffer_resize &&
+ session_vnc->rfb_client->preferredEncoding != -1)
+ {
+ resize_vnc_framebuffer (session_vnc,
+ session_vnc->pending_framebuffer_width,
+ session_vnc->pending_framebuffer_height);
+ session_vnc->pending_framebuffer_resize = FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
static gboolean
handle_socket_data (GSocket *socket,
GIOCondition condition,
gpointer user_data)
{
- GrdSessionVnc *session_vnc = user_data;
+ GrdSessionVnc *session_vnc = GRD_SESSION_VNC (user_data);
+ GrdSession *session = GRD_SESSION (session_vnc);
- if (condition & G_IO_IN)
+ if (condition & (G_IO_ERR | G_IO_HUP))
+ {
+ g_warning ("Client disconnected");
+
+ grd_session_stop (session);
+ }
+ else if (condition & G_IO_IN)
{
- if (rfbIsActive (session_vnc->rfb_screen))
+ GrdVncSocketGrabFunc grab_func;
+ g_autoptr (GError) error = NULL;
+
+ grab_func = g_list_first (session_vnc->socket_grabs)->data;
+ if (!grab_func (session_vnc, &error))
{
- rfbProcessEvents (session_vnc->rfb_screen, 0);
+ g_warning ("Error when reading socket: %s", error->message);
- if (session_vnc->pending_framebuffer_resize &&
- session_vnc->rfb_client->preferredEncoding != -1)
- {
- resize_vnc_framebuffer (session_vnc,
- session_vnc->pending_framebuffer_width,
- session_vnc->pending_framebuffer_height);
- session_vnc->pending_framebuffer_resize = FALSE;
- }
+ grd_session_stop (session);
}
}
else
{
- g_debug ("Unhandled socket condition %d\n", condition);
- return G_SOURCE_REMOVE;
+ g_warning ("Unhandled socket condition %d\n", condition);
+ g_assert_not_reached ();
}
return G_SOURCE_CONTINUE;
@@ -548,7 +609,10 @@ grd_session_vnc_attach_source (GrdSessionVnc *session_vnc)
socket = g_socket_connection_get_socket (session_vnc->connection);
session_vnc->source = g_socket_create_source (socket,
- G_IO_IN | G_IO_PRI,
+ (G_IO_IN |
+ G_IO_PRI |
+ G_IO_ERR |
+ G_IO_HUP),
NULL);
g_source_set_callback (session_vnc->source,
(GSourceFunc) handle_socket_data,
@@ -574,8 +638,10 @@ grd_session_vnc_new (GrdVncServer *vnc_server,
"context", context,
NULL);
+ session_vnc->vnc_server = vnc_server;
session_vnc->connection = g_object_ref (connection);
+ grd_session_vnc_grab_socket (session_vnc, vnc_socket_grab_func);
grd_session_vnc_attach_source (session_vnc);
init_vnc_session (session_vnc);
@@ -590,6 +656,8 @@ grd_session_vnc_dispose (GObject *object)
g_assert (!session_vnc->rfb_screen);
+ g_clear_pointer (&session_vnc->socket_grabs, g_list_free);
+
g_clear_pointer (&session_vnc->pressed_keys, g_hash_table_unref);
G_OBJECT_CLASS (grd_session_vnc_parent_class)->dispose (object);
diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h
index 6bd067a..33245bc 100644
--- a/src/grd-session-vnc.h
+++ b/src/grd-session-vnc.h
@@ -25,6 +25,7 @@
#include <gio/gio.h>
#include <glib-object.h>
+#include <rfb/rfb.h>
#include "grd-session.h"
#include "grd-types.h"
@@ -35,6 +36,9 @@ G_DECLARE_FINAL_TYPE (GrdSessionVnc,
GRD, SESSION_VNC,
GrdSession);
+typedef gboolean (* GrdVncSocketGrabFunc) (GrdSessionVnc *session_vnc,
+ GError **error);
+
GrdSessionVnc *grd_session_vnc_new (GrdVncServer *vnc_server,
GSocketConnection *connection);
@@ -45,6 +49,18 @@ void grd_session_vnc_queue_resize_framebuffer (GrdSessionVnc *session_vnc,
void grd_session_vnc_draw_buffer (GrdSessionVnc *session_vnc,
void *data);
+int grd_session_vnc_get_fd (GrdSessionVnc *session_vnc);
+
int grd_session_vnc_get_framebuffer_stride (GrdSessionVnc *session_vnc);
+rfbClientPtr grd_session_vnc_get_rfb_client (GrdSessionVnc *session_vnc);
+
+void grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc,
+ GrdVncSocketGrabFunc grab_func);
+
+void grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc,
+ GrdVncSocketGrabFunc grab_func);
+
+GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc);
+
#endif /* GRD_SESSION_VNC_H */
diff --git a/src/grd-settings.c b/src/grd-settings.c
index a3a2afa..c886b7e 100644
--- a/src/grd-settings.c
+++ b/src/grd-settings.c
@@ -46,6 +46,7 @@ struct _GrdSettings
GSettings *settings;
gboolean view_only;
GrdVncAuthMethod auth_method;
+ GrdVncEncryption encryption;
} vnc;
};
@@ -87,6 +88,12 @@ grd_settings_get_vnc_auth_method (GrdSettings *settings)
return settings->vnc.auth_method;
}
+GrdVncEncryption
+grd_settings_get_vnc_encryption (GrdSettings *settings)
+{
+ return settings->vnc.encryption;
+}
+
static void
update_vnc_view_only (GrdSettings *settings)
{
@@ -101,6 +108,13 @@ update_vnc_auth_method (GrdSettings *settings)
"auth-method");
}
+static void
+update_vnc_encryption (GrdSettings *settings)
+{
+ settings->vnc.encryption = g_settings_get_flags (settings->vnc.settings,
+ "encryption");
+}
+
static void
on_vnc_settings_changed (GSettings *vnc_settings,
const char *key,
@@ -116,6 +130,11 @@ on_vnc_settings_changed (GSettings *vnc_settings,
update_vnc_auth_method (settings);
g_signal_emit (settings, signals[VNC_AUTH_METHOD_CHANGED], 0);
}
+ else if (strcmp (key, "encryption") == 0)
+ {
+ update_vnc_encryption (settings);
+ g_signal_emit (settings, signals[VNC_ENCRYPTION_CHANGED], 0);
+ }
}
static void
@@ -137,6 +156,7 @@ grd_settings_init (GrdSettings *settings)
update_vnc_view_only (settings);
update_vnc_auth_method (settings);
+ update_vnc_encryption (settings);
}
static void
@@ -160,4 +180,11 @@ grd_settings_class_init (GrdSettingsClass *klass)
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
+ signals[VNC_ENCRYPTION_CHANGED] =
+ g_signal_new ("vnc-encryption-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
}
diff --git a/src/grd-settings.h b/src/grd-settings.h
index 9b23b09..4bca403 100644
--- a/src/grd-settings.h
+++ b/src/grd-settings.h
@@ -40,4 +40,6 @@ gboolean grd_settings_get_vnc_view_only (GrdSettings *settings);
GrdVncAuthMethod grd_settings_get_vnc_auth_method (GrdSettings *settings);
+GrdVncEncryption grd_settings_get_vnc_encryption (GrdSettings *settings);
+
#endif /* GRD_SETTINGS_H */
diff --git a/src/grd-vnc-server.c b/src/grd-vnc-server.c
index a8fed02..769b7ec 100644
--- a/src/grd-vnc-server.c
+++ b/src/grd-vnc-server.c
@@ -24,11 +24,13 @@
#include "grd-vnc-server.h"
+#include <rfb/rfb.h>
#include <gio/gio.h>
#include <rfb/rfb.h>
#include "grd-context.h"
#include "grd-session-vnc.h"
+#include "grd-vnc-tls.h"
#define GRD_VNC_SERVER_PORT 5900
@@ -131,6 +133,43 @@ on_incoming (GSocketService *service,
return TRUE;
}
+static void
+sync_encryption_settings (GrdVncServer *vnc_server)
+{
+ GrdSettings *settings = grd_context_get_settings (vnc_server->context);
+ rfbSecurityHandler *tls_security_handler;
+ GrdVncEncryption encryption;
+
+ tls_security_handler = grd_vnc_tls_get_security_handler ();
+ encryption = grd_settings_get_vnc_encryption (settings);
+
+ if (encryption == (GRD_VNC_ENCRYPTION_NONE | GRD_VNC_ENCRYPTION_TLS_ANON))
+ {
+ rfbRegisterSecurityHandler (tls_security_handler);
+ rfbUnregisterChannelSecurityHandler (tls_security_handler);
+ }
+ else if (encryption == GRD_VNC_ENCRYPTION_NONE)
+ {
+ rfbUnregisterSecurityHandler (tls_security_handler);
+ rfbUnregisterChannelSecurityHandler (tls_security_handler);
+ }
+ else
+ {
+ if (encryption != GRD_VNC_ENCRYPTION_TLS_ANON)
+ g_warning ("Invalid VNC encryption setting, falling back to TLS-ANON");
+
+ rfbRegisterChannelSecurityHandler (tls_security_handler);
+ rfbUnregisterSecurityHandler (tls_security_handler);
+ }
+}
+
+static void
+on_vnc_encryption_changed (GrdSettings *settings,
+ GrdVncServer *vnc_server)
+{
+ sync_encryption_settings (vnc_server);
+}
+
gboolean
grd_vnc_server_start (GrdVncServer *vnc_server,
GError **error)
@@ -219,12 +258,18 @@ static void
grd_vnc_server_constructed (GObject *object)
{
GrdVncServer *vnc_server = GRD_VNC_SERVER (object);
+ GrdSettings *settings = grd_context_get_settings (vnc_server->context);
if (grd_context_get_debug_flags (vnc_server->context) & GRD_DEBUG_VNC)
rfbLogEnable (1);
else
rfbLogEnable (0);
+ g_signal_connect (settings, "vnc-encryption-changed",
+ G_CALLBACK (on_vnc_encryption_changed),
+ vnc_server);
+ sync_encryption_settings (vnc_server);
+
G_OBJECT_CLASS (grd_vnc_server_parent_class)->constructed (object);
}
diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c
new file mode 100644
index 0000000..8fc0fc2
--- /dev/null
+++ b/src/grd-vnc-tls.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2018 Red Hat Inc.
+ *
+ * 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 "grd-vnc-tls.h"
+
+#include <errno.h>
+#include <glib.h>
+#include <gnutls/gnutls.h>
+#include <rfb/rfb.h>
+
+#include "grd-session-vnc.h"
+#include "grd-vnc-server.h"
+
+typedef struct _GrdVncTlsContext
+{
+ gnutls_anon_server_credentials_t anon_credentials;
+ gnutls_dh_params_t dh_params;
+} GrdVncTlsContext;
+
+typedef enum _GrdTlsHandshakeState
+{
+ GRD_TLS_HANDSHAKE_STATE_INIT,
+ GRD_TLS_HANDSHAKE_STATE_DURING,
+ GRD_TLS_HANDSHAKE_STATE_FINISHED
+} GrdTlsHandshakeState;
+
+typedef struct _GrdVncTlsSession
+{
+ GrdVncTlsContext *tls_context;
+
+ int fd;
+
+ gnutls_session_t tls_session;
+ GrdTlsHandshakeState handshake_state;
+
+ char *peek_buffer;
+ int peek_buffer_size;
+ int peek_buffer_len;
+} GrdVncTlsSession;
+
+static gboolean
+tls_handshake_grab_func (GrdSessionVnc *session_vnc,
+ GError **error);
+
+static GrdVncTlsContext *
+grd_vnc_tls_context_new (void)
+{
+ GrdVncTlsContext *tls_context;
+ const unsigned int dh_bits = 1024;
+
+ tls_context = g_new0 (GrdVncTlsContext, 1);
+
+ gnutls_global_init ();
+
+ gnutls_anon_allocate_server_credentials (&tls_context->anon_credentials);
+
+ gnutls_dh_params_init (&tls_context->dh_params);
+ gnutls_dh_params_generate2 (tls_context->dh_params, dh_bits);
+
+ gnutls_anon_set_server_dh_params (tls_context->anon_credentials,
+ tls_context->dh_params);
+
+ return tls_context;
+}
+
+static void
+grd_vnc_tls_context_free (GrdVncTlsContext *tls_context)
+{
+ gnutls_dh_params_deinit (tls_context->dh_params);
+ gnutls_anon_free_server_credentials (tls_context->anon_credentials);
+ gnutls_global_deinit ();
+}
+
+GrdVncTlsContext *
+ensure_tls_context (GrdVncServer *vnc_server)
+{
+ GrdVncTlsContext *tls_context;
+
+ tls_context = g_object_get_data (G_OBJECT (vnc_server), "vnc-tls-context");
+ if (!tls_context)
+ {
+ tls_context = grd_vnc_tls_context_new ();
+ g_object_set_data_full (G_OBJECT (vnc_server), "vnc-tls-context",
+ tls_context,
+ (GDestroyNotify) grd_vnc_tls_context_free);
+ }
+
+ return tls_context;
+}
+
+static gboolean
+perform_anon_tls_handshake (GrdVncTlsSession *tls_session,
+ GError **error)
+{
+ GrdVncTlsContext *tls_context = tls_session->tls_context;
+ const char kx_priority[] = "NORMAL:+ANON-DH";
+ int ret;
+
+ gnutls_init (&tls_session->tls_session, GNUTLS_SERVER | GNUTLS_NO_SIGNAL);
+
+ gnutls_set_default_priority (tls_session->tls_session);
+ gnutls_priority_set_direct (tls_session->tls_session, kx_priority, NULL);
+
+ gnutls_credentials_set (tls_session->tls_session,
+ GNUTLS_CRD_ANON,
+ tls_context->anon_credentials);
+ gnutls_transport_set_ptr (tls_session->tls_session,
+ GINT_TO_POINTER (tls_session->fd));
+
+ ret = gnutls_handshake (tls_session->tls_session);
+ if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret))
+ {
+ tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_DURING;
+ return TRUE;
+ }
+
+ if (ret != GNUTLS_E_SUCCESS)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "%s", gnutls_strerror (ret));
+ gnutls_deinit (tls_session->tls_session);
+ tls_session->tls_session = NULL;
+ return FALSE;
+ }
+
+ tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED;
+ return TRUE;
+}
+
+static gboolean
+continue_tls_handshake (GrdVncTlsSession *tls_session,
+ GError **error)
+{
+ int ret;
+
+ ret = gnutls_handshake (tls_session->tls_session);
+ if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret))
+ return TRUE;
+
+ if (ret != GNUTLS_E_SUCCESS)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "%s", gnutls_strerror (ret));
+ gnutls_deinit (tls_session->tls_session);
+ tls_session->tls_session = NULL;
+ return FALSE;
+ }
+
+ tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED;
+ return TRUE;
+}
+
+static void
+grd_vnc_tls_session_free (GrdVncTlsSession *tls_session)
+{
+ g_clear_pointer (&tls_session->peek_buffer, g_free);
+ g_clear_pointer (&tls_session->tls_session, (GDestroyNotify) gnutls_deinit);
+ g_free (tls_session);
+}
+
+static GrdVncTlsSession *
+grd_vnc_tls_session_from_vnc_session (GrdSessionVnc *session_vnc)
+{
+ return g_object_get_data (G_OBJECT (session_vnc), "vnc-tls-session");
+}
+
+static int
+do_read (GrdVncTlsSession *tls_session,
+ char *buf,
+ int len)
+{
+ do
+ {
+ int ret;
+
+ ret = gnutls_record_recv (tls_session->tls_session, buf, len);
+ if (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_INTERRUPTED)
+ {
+ continue;
+ }
+ else if (ret < 0)
+ {
+ g_debug ("gnutls_record_recv failed: %s", gnutls_strerror (ret));
+ errno = EIO;
+ return -1;
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ while (TRUE);
+}
+
+static int
+grd_vnc_tls_read_from_socket (rfbClientPtr rfb_client,
+ char *buf,
+ int len)
+{
+ GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
+ GrdVncTlsSession *tls_session =
+ grd_vnc_tls_session_from_vnc_session (session_vnc);
+ int to_read = len;
+ int len_read = 0;
+
+ if (to_read < tls_session->peek_buffer_len)
+ {
+ memcpy (buf, tls_session->peek_buffer, to_read);
+ memmove (buf,
+ tls_session->peek_buffer + to_read,
+ tls_session->peek_buffer_len - to_read);
+ len_read = to_read;
+ to_read = 0;
+ }
+ else
+ {
+ memcpy (buf,
+ tls_session->peek_buffer,
+ tls_session->peek_buffer_len);
+ to_read -= tls_session->peek_buffer_len;
+ len_read = tls_session->peek_buffer_len;
+
+ g_clear_pointer (&tls_session->peek_buffer,
+ g_free);
+ tls_session->peek_buffer_len = 0;
+ tls_session->peek_buffer_size = 0;
+ }
+
+ if (to_read > 0)
+ {
+ int ret;
+
+ ret = do_read (tls_session, buf + len_read, to_read);
+ if (ret == -1)
+ return -1;
+
+ len_read += ret;
+ }
+
+ return len_read;
+}
+
+static int
+grd_vnc_tls_peek_at_socket (rfbClientPtr rfb_client,
+ char *buf,
+ int len)
+{
+ GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
+ GrdVncTlsSession *tls_session =
+ grd_vnc_tls_session_from_vnc_session (session_vnc);
+ int peekable_len;
+
+ if (tls_session->peek_buffer_len < len)
+ {
+ int ret;
+
+ if (len > tls_session->peek_buffer_size)
+ {
+ tls_session->peek_buffer = g_renew (char,
+ tls_session->peek_buffer,
+ len);
+ tls_session->peek_buffer_size = len;
+ }
+
+ ret = do_read (tls_session,
+ tls_session->peek_buffer + tls_session->peek_buffer_len,
+ len - tls_session->peek_buffer_len);
+ if (ret == -1)
+ return -1;
+
+ tls_session->peek_buffer_len += ret;
+ }
+
+ peekable_len = MIN (len, tls_session->peek_buffer_len);
+ memcpy (buf, tls_session->peek_buffer, peekable_len);
+
+ return peekable_len;
+}
+
+static rfbBool
+grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client)
+{
+ GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
+ GrdVncTlsSession *tls_session =
+ grd_vnc_tls_session_from_vnc_session (session_vnc);
+
+ if (tls_session->peek_buffer_len > 0)
+ return TRUE;
+
+ if (gnutls_record_check_pending (tls_session->tls_session) > 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static int
+grd_vnc_tls_write_to_socket (rfbClientPtr rfb_client,
+ const char *buf,
+ int len)
+{
+ GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
+ GrdVncTlsSession *tls_session =
+ grd_vnc_tls_session_from_vnc_session (session_vnc);
+
+ do
+ {
+ int ret;
+
+ ret = gnutls_record_send (tls_session->tls_session, buf, len);
+ if (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_INTERRUPTED)
+ {
+ continue;
+ }
+ else if (ret < 0)
+ {
+ g_debug ("gnutls_record_send failed: %s", gnutls_strerror (ret));
+ errno = EIO;
+ return -1;
+ }
+ else
+ {
+ return ret;
+ }
+ }
+ while (TRUE);
+}
+
+static gboolean
+perform_handshake (GrdSessionVnc *session_vnc,
+ GError **error)
+{
+ GrdVncTlsSession *tls_session =
+ grd_vnc_tls_session_from_vnc_session (session_vnc);
+
+ switch (tls_session->handshake_state)
+ {
+ case GRD_TLS_HANDSHAKE_STATE_INIT:
+ if (!perform_anon_tls_handshake (tls_session, error))
+ return FALSE;
+ break;
+ case GRD_TLS_HANDSHAKE_STATE_DURING:
+ if (!continue_tls_handshake (tls_session, error))
+ return FALSE;
+ break;
+ case GRD_TLS_HANDSHAKE_STATE_FINISHED:
+ break;
+ }
+
+ switch (tls_session->handshake_state)
+ {
+ case GRD_TLS_HANDSHAKE_STATE_INIT:
+ break;
+ case GRD_TLS_HANDSHAKE_STATE_DURING:
+ break;
+ case GRD_TLS_HANDSHAKE_STATE_FINISHED:
+ grd_session_vnc_ungrab_socket (session_vnc, tls_handshake_grab_func);
+ rfbSendSecurityTypeList (grd_session_vnc_get_rfb_client (session_vnc),
+ RFB_SECURITY_TAG_CHANNEL);
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+tls_handshake_grab_func (GrdSessionVnc *session_vnc,
+ GError **error)
+{
+ g_autoptr (GError) handshake_error = NULL;
+
+ if (!perform_handshake (session_vnc, &handshake_error))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "TLS handshake failed: %s", handshake_error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+rfb_tls_security_handler (rfbClientPtr rfb_client)
+{
+ GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
+ GrdVncTlsSession *tls_session;
+ g_autoptr(GError) error = NULL;
+
+ tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc);
+ if (!tls_session)
+ {
+ GrdVncServer *vnc_server = grd_session_vnc_get_vnc_server (session_vnc);
+
+ tls_session = g_new0 (GrdVncTlsSession, 1);
+ tls_session->fd = grd_session_vnc_get_fd (session_vnc);
+ tls_session->tls_context = ensure_tls_context (vnc_server);
+ g_object_set_data_full (G_OBJECT (session_vnc), "vnc-tls-session",
+ tls_session,
+ (GDestroyNotify) grd_vnc_tls_session_free);
+
+ rfb_client->readFromSocket = grd_vnc_tls_read_from_socket;
+ rfb_client->peekAtSocket = grd_vnc_tls_peek_at_socket;
+ rfb_client->hasPendingOnSocket = grd_vnc_tls_has_pending_on_socket;
+ rfb_client->writeToSocket = grd_vnc_tls_write_to_socket;
+
+ grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func);
+ }
+
+ if (!perform_handshake (session_vnc, &error))
+ {
+ g_warning ("TLS handshake failed: %s", error->message);
+ rfbCloseClient (rfb_client);
+ }
+}
+
+static rfbSecurityHandler anon_tls_security_handler = {
+ .type = rfbTLS,
+ .handler = rfb_tls_security_handler,
+ .securityTags = RFB_SECURITY_TAG_CHANNEL,
+};
+
+rfbSecurityHandler *
+grd_vnc_tls_get_security_handler (void)
+{
+ return &anon_tls_security_handler;
+}
diff --git a/src/grd-vnc-tls.h b/src/grd-vnc-tls.h
new file mode 100644
index 0000000..135ef8c
--- /dev/null
+++ b/src/grd-vnc-tls.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 Red Hat Inc.
+ *
+ * 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_VNC_TLS_H
+#define GRD_VNC_TLS_H
+
+#include <rfb/rfb.h>
+
+rfbSecurityHandler * grd_vnc_tls_get_security_handler (void);
+
+#endif /* GRD_VNC_TLS_H */
diff --git a/src/meson.build b/src/meson.build
index 70e2102..b633ad7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -19,6 +19,8 @@ daemon_sources = files([
'grd-vnc-pipewire-stream.h',
'grd-vnc-server.c',
'grd-vnc-server.h',
+ 'grd-vnc-tls.c',
+ 'grd-vnc-tls.h',
])
gen_daemon_sources = []
@@ -49,7 +51,8 @@ executable('gnome-remote-desktop-daemon',
pipewire_dep,
libvncserver_dep,
libsecret_dep,
- libnotify_dep],
+ libnotify_dep,
+ gnutls_dep],
include_directories: [configinc],
install: true,
install_dir: libexecdir)
diff --git a/src/org.gnome.desktop.remote-desktop.gschema.xml b/src/org.gnome.desktop.remote-desktop.gschema.xml
index a5c2022..846e65b 100644
--- a/src/org.gnome.desktop.remote-desktop.gschema.xml
+++ b/src/org.gnome.desktop.remote-desktop.gschema.xml
@@ -23,5 +23,15 @@
* password - by requiring the remote client to provide a known password
</description>
</key>
+ <key name='encryption' flags='org.gnome.desktop.remote-desktop.GrdVncEncryption'>
+ <default>['tls-anon']</default>
+ <summary>Allowed encryption method to use</summary>
+ <description>
+ Allowed encryption methods. Includes the following:
+
+ * none - no encryption
+ * tls-anon - anonymous (unauthenticated) TLS
+ </description>
+ </key>
</schema>
</schemalist>
--
2.17.1

View File

@ -0,0 +1,42 @@
From 1467e4c26f47ad3747903392a026698a169870aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 10 Apr 2019 15:59:54 +0200
Subject: [PATCH] vnc: Allow overriding password with env var
For testing purposes. Also overrides VNC auth method setting.
---
src/grd-settings.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/grd-settings.c b/src/grd-settings.c
index c886b7e..d6c4a25 100644
--- a/src/grd-settings.c
+++ b/src/grd-settings.c
@@ -71,6 +71,12 @@ char *
grd_settings_get_vnc_password (GrdSettings *settings,
GError **error)
{
+ const char *test_password_override;
+
+ test_password_override = g_getenv ("GNOME_REMOTE_DESKTOP_TEST_VNC_PASSWORD");
+ if (test_password_override)
+ return g_strdup (test_password_override);
+
return secret_password_lookup_sync (GRD_VNC_PASSWORD_SCHEMA,
NULL, error,
NULL);
@@ -85,7 +91,10 @@ grd_settings_get_vnc_view_only (GrdSettings *settings)
GrdVncAuthMethod
grd_settings_get_vnc_auth_method (GrdSettings *settings)
{
- return settings->vnc.auth_method;
+ if (g_getenv ("GNOME_REMOTE_DESKTOP_TEST_VNC_PASSWORD"))
+ return GRD_VNC_AUTH_METHOD_PASSWORD;
+ else
+ return settings->vnc.auth_method;
}
GrdVncEncryption
--
2.21.0

View File

@ -0,0 +1,28 @@
From 59188d81cf8936cd9f5400df040d875427251bf2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 1 Oct 2018 18:05:07 +0200
Subject: [PATCH 2/2] vnc-pipewire-stream: Close session when disconnected
When there is an active stream, and we're disconnected from PipeWire
(e.g. because it terminated), close the session.
---
src/grd-vnc-pipewire-stream.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c
index 66d66a0..d6454b8 100644
--- a/src/grd-vnc-pipewire-stream.c
+++ b/src/grd-vnc-pipewire-stream.c
@@ -392,6 +392,9 @@ on_state_changed (void *user_data,
}
break;
case PW_REMOTE_STATE_UNCONNECTED:
+ if (stream->pipewire_stream)
+ g_signal_emit (stream, signals[CLOSED], 0);
+ break;
case PW_REMOTE_STATE_CONNECTING:
break;
}
--
2.17.1

View File

@ -0,0 +1,719 @@
From b9221b1efcfe205409628bf365008e0b248725f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Fri, 23 Nov 2018 16:55:20 +0100
Subject: [PATCH 1/6] vnc: Detach source when client is gone
LibVNCServer will close the socket for us, so don't keep the socket
source attached after a client is gone. It's not fast enough to close it
in the idle function, as that means we'd get a G_IO_NVAL error when the
source is processed.
Fixes: https://gitlab.gnome.org/jadahl/gnome-remote-desktop/issues/23
---
src/grd-session-vnc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index 3c98eeb..fbf41e2 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -182,6 +182,7 @@ handle_client_gone (rfbClientPtr rfb_client)
g_debug ("VNC client gone");
+ grd_session_vnc_detach_source (session_vnc);
maybe_queue_close_session_idle (session_vnc);
}
--
2.19.1
From 5ce2a1a6f80581d7d5b79b3a86f6707d22ad94ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 26 Nov 2018 16:37:12 +0100
Subject: [PATCH 2/6] session/vnc: Always set pixel format translate functions
Internally LibVNCServer uses RGBX, but we provide BGRX. Currently the
LibVNCServer API doesn't allow to override this, but internally, it
supports any order of the color components. We relied on this already,
to avoid an extra pixel conversion, but we failed to update the
conversion table.
Fixes: https://gitlab.gnome.org/jadahl/gnome-remote-desktop/issues/20
---
src/grd-session-vnc.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index fbf41e2..c6be742 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -115,11 +115,13 @@ resize_vnc_framebuffer (GrdSessionVnc *session_vnc,
BGRX_BYTES_PER_PIXEL);
/*
- * Our format is hard coded to BGRX but LibVNCServer asusumes it's RGBX;
+ * Our format is hard coded to BGRX but LibVNCServer assumes it's RGBX;
* lets override that.
*/
+
swap_uint8 (&session_vnc->rfb_screen->serverFormat.redShift,
&session_vnc->rfb_screen->serverFormat.blueShift);
+ rfb_screen->setTranslateFunction (session_vnc->rfb_client);
}
void
--
2.19.1
From b3bb9ebb47a9883c1022a1ad3dc90aa9bb9c2d27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 17 Dec 2018 13:12:38 +0100
Subject: [PATCH 3/6] session/vnc: Update server format earlier too
We update the server protocol to swap the blue and red shift levels, as
LibVNCServer only RGBX by default, but we pass BGRX. Doing this just
after resizing the framebuffer where the format is updated is too late
though, as for some reason LibVNCServer's tight encoding still gets it
wrong when encoding JPEG rects.
Fixes: https://gitlab.gnome.org/jadahl/gnome-remote-desktop/issues/20
---
src/grd-session-vnc.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index c6be742..b86c3a7 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -94,6 +94,19 @@ swap_uint8 (uint8_t *a,
*b = tmp;
}
+static void
+update_server_format (GrdSessionVnc *session_vnc)
+{
+ rfbScreenInfoPtr rfb_screen = session_vnc->rfb_screen;
+
+ /*
+ * Our format is hard coded to BGRX but LibVNCServer assumes it's RGBX;
+ * lets override that.
+ */
+ swap_uint8 (&rfb_screen->serverFormat.redShift,
+ &rfb_screen->serverFormat.blueShift);
+}
+
static void
resize_vnc_framebuffer (GrdSessionVnc *session_vnc,
int width,
@@ -114,13 +127,7 @@ resize_vnc_framebuffer (GrdSessionVnc *session_vnc,
BGRX_SAMPLES_PER_PIXEL,
BGRX_BYTES_PER_PIXEL);
- /*
- * Our format is hard coded to BGRX but LibVNCServer assumes it's RGBX;
- * lets override that.
- */
-
- swap_uint8 (&session_vnc->rfb_screen->serverFormat.redShift,
- &session_vnc->rfb_screen->serverFormat.blueShift);
+ update_server_format (session_vnc);
rfb_screen->setTranslateFunction (session_vnc->rfb_client);
}
@@ -517,6 +524,7 @@ init_vnc_session (GrdSessionVnc *session_vnc)
rfb_screen = rfbGetScreen (0, NULL,
screen_width, screen_height,
8, 3, 4);
+ update_server_format (session_vnc);
socket = g_socket_connection_get_socket (session_vnc->connection);
rfb_screen->inetdSock = g_socket_get_fd (socket);
--
2.19.1
From 930d1c127e4a2252e3288cd3c752debe88d938c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 17 Dec 2018 14:35:17 +0100
Subject: [PATCH 4/6] vnc: Request cursor sprite as PipeWire metadata
This allows sending cursor updates, including position and cursor
sprite, without having to embed it into the framebuffer. In effect, when
there is a hardware cursor in the compositor, moving the cursor will not
result in any actual frames being copied.
The actual cursor sprite sent over VNC is derived from the cursor sprite
used in the compositor. The conversion is lossy, as the VNC cursor
sprite is a 3 bits per pixel (transparen, white or black), and the cursor
sprite from the compositor is 32 bits per pixel.
This bumps the PipeWire requirement to 0.2.5.
---
meson.build | 2 +-
src/grd-session-vnc.c | 24 +++++++
src/grd-session-vnc.h | 7 ++
src/grd-session.c | 10 +++
src/grd-types.h | 5 ++
src/grd-vnc-cursor.c | 125 ++++++++++++++++++++++++++++++++++
src/grd-vnc-cursor.h | 37 ++++++++++
src/grd-vnc-pipewire-stream.c | 94 +++++++++++++++++++++++--
src/meson.build | 2 +
9 files changed, 299 insertions(+), 7 deletions(-)
create mode 100644 src/grd-vnc-cursor.c
create mode 100644 src/grd-vnc-cursor.h
diff --git a/meson.build b/meson.build
index cb0d5fe..b6bfb6f 100644
--- a/meson.build
+++ b/meson.build
@@ -10,7 +10,7 @@ gnome = import('gnome')
glib_dep = dependency('glib-2.0')
gio_dep = dependency('gio-2.0')
gio_unix_dep = dependency('gio-unix-2.0')
-pipewire_dep = dependency('libpipewire-0.2', version: '>= 0.2.2')
+pipewire_dep = dependency('libpipewire-0.2', version: '>= 0.2.5')
systemd_dep = dependency('systemd')
libvncserver_dep = dependency('libvncserver')
libsecret_dep = dependency('libsecret-1')
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index b86c3a7..247c130 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -174,6 +174,30 @@ grd_session_vnc_draw_buffer (GrdSessionVnc *session_vnc,
rfbProcessEvents (session_vnc->rfb_screen, 0);
}
+void
+grd_session_vnc_set_cursor (GrdSessionVnc *session_vnc,
+ rfbCursorPtr rfb_cursor)
+{
+ rfbSetCursor (session_vnc->rfb_screen, rfb_cursor);
+}
+
+void
+grd_session_vnc_move_cursor (GrdSessionVnc *session_vnc,
+ int x,
+ int y)
+{
+ if (session_vnc->rfb_screen->cursorX == x ||
+ session_vnc->rfb_screen->cursorY == y)
+ return;
+
+ LOCK (session_vnc->rfb_screen->cursorMutex);
+ session_vnc->rfb_screen->cursorX = x;
+ session_vnc->rfb_screen->cursorY = y;
+ UNLOCK (session_vnc->rfb_screen->cursorMutex);
+
+ session_vnc->rfb_client->cursorWasMoved = TRUE;
+}
+
static void
maybe_queue_close_session_idle (GrdSessionVnc *session_vnc)
{
diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h
index 33245bc..789693e 100644
--- a/src/grd-session-vnc.h
+++ b/src/grd-session-vnc.h
@@ -51,6 +51,13 @@ void grd_session_vnc_draw_buffer (GrdSessionVnc *session_vnc,
int grd_session_vnc_get_fd (GrdSessionVnc *session_vnc);
+void grd_session_vnc_set_cursor (GrdSessionVnc *session_vnc,
+ rfbCursorPtr rfb_cursor);
+
+void grd_session_vnc_move_cursor (GrdSessionVnc *session_vnc,
+ int x,
+ int y);
+
int grd_session_vnc_get_framebuffer_stride (GrdSessionVnc *session_vnc);
rfbClientPtr grd_session_vnc_get_rfb_client (GrdSessionVnc *session_vnc);
diff --git a/src/grd-session.c b/src/grd-session.c
index 212770b..3697584 100644
--- a/src/grd-session.c
+++ b/src/grd-session.c
@@ -47,6 +47,13 @@ enum
static guint signals[LAST_SIGNAL];
+typedef enum _GrdScreenCastCursorMode
+{
+ GRD_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0,
+ GRD_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1,
+ GRD_SCREEN_CAST_CURSOR_MODE_METADATA = 2,
+} GrdScreenCastCursorMode;
+
typedef struct _GrdSessionPrivate
{
GrdContext *context;
@@ -296,6 +303,9 @@ on_screen_cast_session_proxy_acquired (GObject *object,
priv->screen_cast_session = session_proxy;
g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&properties_builder, "{sv}",
+ "cursor-mode",
+ g_variant_new_uint32 (GRD_SCREEN_CAST_CURSOR_MODE_METADATA));
/* TODO: Support something other than primary monitor */
grd_dbus_screen_cast_session_call_record_monitor (session_proxy,
diff --git a/src/grd-types.h b/src/grd-types.h
index af92c88..89dd22f 100644
--- a/src/grd-types.h
+++ b/src/grd-types.h
@@ -31,4 +31,9 @@ typedef struct _GrdPipeWireStream GrdPipeWireStream;
typedef struct _GrdPipeWireStreamMonitor GrdPipeWireStreamMonitor;
typedef struct _GrdVncServer GrdVncServer;
+typedef enum _GrdPixelFormat
+{
+ GRD_PIXEL_FORMAT_RGBA8888,
+} GrdPixelFormat;
+
#endif /* GRD_TYPES_H */
diff --git a/src/grd-vnc-cursor.c b/src/grd-vnc-cursor.c
new file mode 100644
index 0000000..c0cb1aa
--- /dev/null
+++ b/src/grd-vnc-cursor.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 Red Hat Inc.
+ *
+ * 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-vnc-cursor.h"
+
+#include <glib.h>
+#include <stdint.h>
+
+static void
+get_pixel_components (uint32_t pixel,
+ GrdPixelFormat format,
+ uint8_t *a,
+ uint8_t *r,
+ uint8_t *g,
+ uint8_t *b)
+{
+ g_assert (format == GRD_PIXEL_FORMAT_RGBA8888);
+
+ *a = (pixel & 0xff000000) >> 24;
+ *b = (pixel & 0xff0000) >> 16;
+ *g = (pixel & 0xff00) >> 8;
+ *r = pixel & 0xff;
+}
+
+static gboolean
+is_practically_black (uint8_t r,
+ uint8_t g,
+ uint8_t b)
+{
+ if (r <= 0x62 &&
+ g <= 0x62 &&
+ b <= 0x62)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+is_practically_opaque (uint8_t a)
+{
+ return a > 0xe0;
+}
+
+rfbCursorPtr
+grd_vnc_create_cursor (int width,
+ int height,
+ int stride,
+ GrdPixelFormat format,
+ uint8_t *buf)
+{
+ g_autofree char *cursor = NULL;
+ g_autofree char *mask = NULL;
+ int y;
+
+ g_return_val_if_fail (format == GRD_PIXEL_FORMAT_RGBA8888, NULL);
+
+ cursor = g_new0 (char, width * height);
+ mask = g_new0 (char, width * height);
+
+ for (y = 0; y < height; y++)
+ {
+ uint32_t *pixel_row;
+ int x;
+
+ pixel_row = (uint32_t *) &buf[y * stride];
+
+ for (x = 0; x < width; x++)
+ {
+ uint32_t pixel = pixel_row[x];
+ uint8_t a, r, g, b;
+
+ get_pixel_components (pixel,
+ format,
+ &a, &r, &g, &b);
+
+ if (is_practically_opaque (a))
+ {
+ if (is_practically_black (r, g, b))
+ cursor[y * width + x] = ' ';
+ else
+ cursor[y * width + x] = 'x';
+
+ mask[y * width + x] = 'x';
+ }
+ else
+ {
+ cursor[y * width + x] = ' ';
+ mask[y * width + x] = ' ';
+ }
+ }
+ }
+
+ return rfbMakeXCursor (width, height, cursor, mask);
+}
+
+rfbCursorPtr
+grd_vnc_create_empty_cursor (int width,
+ int height)
+{
+ g_autofree char *cursor = NULL;
+ cursor = g_new0 (char, width * height);
+
+ memset (cursor, ' ', width * height);
+
+ return rfbMakeXCursor (width, height, cursor, cursor);
+}
diff --git a/src/grd-vnc-cursor.h b/src/grd-vnc-cursor.h
new file mode 100644
index 0000000..36bd0fb
--- /dev/null
+++ b/src/grd-vnc-cursor.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 Red Hat Inc.
+ *
+ * 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_VNC_CURSOR_H
+#define GRD_VNC_CURSOR_H
+
+#include <rfb/rfb.h>
+
+#include "grd-types.h"
+
+rfbCursorPtr grd_vnc_create_cursor (int width,
+ int height,
+ int stride,
+ GrdPixelFormat format,
+ uint8_t *buf);
+
+rfbCursorPtr grd_vnc_create_empty_cursor (int width,
+ int height);
+
+#endif /* GRD_VNC_CURSOR_H */
diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c
index d6454b8..c341887 100644
--- a/src/grd-vnc-pipewire-stream.c
+++ b/src/grd-vnc-pipewire-stream.c
@@ -28,6 +28,8 @@
#include <spa/param/video/format-utils.h>
#include <sys/mman.h>
+#include "grd-vnc-cursor.h"
+
enum
{
CLOSED,
@@ -43,6 +45,7 @@ typedef struct _GrdSpaType
struct spa_type_media_subtype media_subtype;
struct spa_type_format_video format_video;
struct spa_type_video_format video_format;
+ uint32_t meta_cursor;
} GrdSpaType;
typedef struct _GrdPipeWireSource
@@ -79,6 +82,11 @@ struct _GrdVncPipeWireStream
G_DEFINE_TYPE (GrdVncPipeWireStream, grd_vnc_pipewire_stream,
G_TYPE_OBJECT)
+#define CURSOR_META_SIZE(width, height) \
+ (sizeof(struct spa_meta_cursor) + \
+ sizeof(struct spa_meta_bitmap) + width * height * 4)
+
+
static void
init_spa_type (GrdSpaType *type,
struct spa_type_map *map)
@@ -87,6 +95,7 @@ init_spa_type (GrdSpaType *type,
spa_type_media_subtype_map (map, &type->media_subtype);
spa_type_format_video_map (map, &type->format_video);
spa_type_video_format_map (map, &type->video_format);
+ type->meta_cursor = spa_type_map_get_id (map, SPA_TYPE_META__Cursor);
}
static gboolean
@@ -196,7 +205,7 @@ on_stream_format_changed (void *user_data,
int height;
int stride;
int size;
- const struct spa_pod *params[2];
+ const struct spa_pod *params[3];
if (!format)
{
@@ -231,7 +240,29 @@ on_stream_format_changed (void *user_data,
":", pipewire_type->param_meta.type, "I", pipewire_type->meta.Header,
":", pipewire_type->param_meta.size, "i", sizeof(struct spa_meta_header));
- pw_stream_finish_format (stream->pipewire_stream, 0, params, 2);
+ params[2] = spa_pod_builder_object(
+ &pod_builder,
+ pipewire_type->param.idMeta, pipewire_type->param_meta.Meta,
+ ":", pipewire_type->param_meta.type, "I", stream->spa_type.meta_cursor,
+ ":", pipewire_type->param_meta.size, "iru", CURSOR_META_SIZE (64,64),
+ SPA_POD_PROP_MIN_MAX (CURSOR_META_SIZE (1,1),
+ CURSOR_META_SIZE (256,256)));
+
+ pw_stream_finish_format (stream->pipewire_stream, 0,
+ params, G_N_ELEMENTS (params));
+}
+
+static gboolean
+spa_pixel_format_to_grd_pixel_format (GrdSpaType *spa_type,
+ uint32_t spa_format,
+ GrdPixelFormat *out_format)
+{
+ if (spa_format == spa_type->video_format.RGBA)
+ *out_format = GRD_PIXEL_FORMAT_RGBA8888;
+ else
+ return FALSE;
+
+ return TRUE;
}
static int
@@ -243,12 +274,19 @@ do_render (struct spa_loop *loop,
void *user_data)
{
GrdVncPipeWireStream *stream = GRD_VNC_PIPEWIRE_STREAM (user_data);
+ GrdSpaType *spa_type = &stream->spa_type;
struct spa_buffer *buffer = ((struct spa_buffer **) data)[0];
uint8_t *map;
void *src_data;
+ struct spa_meta_cursor *spa_meta_cursor;
- if (buffer->datas[0].type == stream->pipewire_type->data.MemFd ||
- buffer->datas[0].type == stream->pipewire_type->data.DmaBuf)
+ if (buffer->datas[0].chunk->size == 0)
+ {
+ map = NULL;
+ src_data = NULL;
+ }
+ else if (buffer->datas[0].type == stream->pipewire_type->data.MemFd ||
+ buffer->datas[0].type == stream->pipewire_type->data.DmaBuf)
{
map = mmap (NULL, buffer->datas[0].maxsize + buffer->datas[0].mapoffset,
PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0);
@@ -264,7 +302,51 @@ do_render (struct spa_loop *loop,
return -EINVAL;
}
- grd_session_vnc_draw_buffer (stream->session, src_data);
+ spa_meta_cursor = spa_buffer_find_meta (buffer, spa_type->meta_cursor);
+ if (spa_meta_cursor && spa_meta_cursor_is_valid (spa_meta_cursor))
+ {
+ struct spa_meta_bitmap *spa_meta_bitmap;
+ GrdPixelFormat format;
+
+ spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
+ spa_meta_cursor->bitmap_offset,
+ struct spa_meta_bitmap);
+
+ if (spa_meta_bitmap->size.width > 0 &&
+ spa_meta_bitmap->size.height > 0 &&
+ spa_pixel_format_to_grd_pixel_format (spa_type,
+ spa_meta_bitmap->format,
+ &format))
+ {
+ uint8_t *buf;
+ rfbCursorPtr rfb_cursor;
+
+ buf = SPA_MEMBER (spa_meta_bitmap, spa_meta_bitmap->offset, uint8_t);
+ rfb_cursor = grd_vnc_create_cursor (spa_meta_bitmap->size.width,
+ spa_meta_bitmap->size.height,
+ spa_meta_bitmap->stride,
+ format,
+ buf);
+ rfb_cursor->xhot = spa_meta_cursor->hotspot.x;
+ rfb_cursor->yhot = spa_meta_cursor->hotspot.y;
+
+ grd_session_vnc_set_cursor (stream->session, rfb_cursor);
+ }
+ else
+ {
+ rfbCursorPtr empty_cursor;
+
+ empty_cursor = grd_vnc_create_empty_cursor (1, 1);
+ grd_session_vnc_set_cursor (stream->session, empty_cursor);
+ }
+
+ grd_session_vnc_move_cursor (stream->session,
+ spa_meta_cursor->position.x,
+ spa_meta_cursor->position.y);
+ }
+
+ if (src_data)
+ grd_session_vnc_draw_buffer (stream->session, src_data);
if (map)
munmap (map, buffer->datas[0].maxsize + buffer->datas[0].mapoffset);
@@ -306,7 +388,7 @@ connect_to_stream (GrdVncPipeWireStream *stream,
struct spa_rectangle max_rect;
struct spa_fraction min_framerate;
struct spa_fraction max_framerate;
- const struct spa_pod *params[1];
+ const struct spa_pod *params[2];
int ret;
pipewire_stream = pw_stream_new (stream->pipewire_remote,
diff --git a/src/meson.build b/src/meson.build
index b633ad7..31c7221 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -15,6 +15,8 @@ daemon_sources = files([
'grd-stream.c',
'grd-stream.h',
'grd-types.h',
+ 'grd-vnc-cursor.c',
+ 'grd-vnc-cursor.h',
'grd-vnc-pipewire-stream.c',
'grd-vnc-pipewire-stream.h',
'grd-vnc-server.c',
--
2.19.1
From 6aff333ef474abfe18e0da964032e08e6cda2cd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Thu, 3 Jan 2019 18:07:33 +0100
Subject: [PATCH 5/6] session/vnc: Set rfbScreenPtr pointer earlier
Otherwise the format update_server_format() function doesn't find it.
---
src/grd-session-vnc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
index 247c130..dcd8599 100644
--- a/src/grd-session-vnc.c
+++ b/src/grd-session-vnc.c
@@ -548,6 +548,8 @@ init_vnc_session (GrdSessionVnc *session_vnc)
rfb_screen = rfbGetScreen (0, NULL,
screen_width, screen_height,
8, 3, 4);
+ session_vnc->rfb_screen = rfb_screen;
+
update_server_format (session_vnc);
socket = g_socket_connection_get_socket (session_vnc->connection);
@@ -567,8 +569,6 @@ init_vnc_session (GrdSessionVnc *session_vnc)
rfb_screen->frameBuffer = g_malloc0 (screen_width * screen_height * 4);
memset (rfb_screen->frameBuffer, 0x1f, screen_width * screen_height * 4);
- session_vnc->rfb_screen = rfb_screen;
-
rfbInitServer (rfb_screen);
rfbProcessEvents (rfb_screen, 0);
}
--
2.19.1
From 5d9ffd0efe8b32d95c1a566f431e7d14fa95f3fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Thu, 3 Jan 2019 18:08:52 +0100
Subject: [PATCH 6/6] vnc-pipewire-stream: Assume no bitmap offset means no
bitmap change
This is according to the agreed semantics:
https://github.com/PipeWire/pipewire/commit/8984c6f48d2106f143d3f6d5df976f788ecfde30
---
src/grd-vnc-pipewire-stream.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c
index c341887..cee569a 100644
--- a/src/grd-vnc-pipewire-stream.c
+++ b/src/grd-vnc-pipewire-stream.c
@@ -308,11 +308,19 @@ do_render (struct spa_loop *loop,
struct spa_meta_bitmap *spa_meta_bitmap;
GrdPixelFormat format;
- spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
- spa_meta_cursor->bitmap_offset,
- struct spa_meta_bitmap);
+ if (spa_meta_cursor->bitmap_offset)
+ {
+ spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor,
+ spa_meta_cursor->bitmap_offset,
+ struct spa_meta_bitmap);
+ }
+ else
+ {
+ spa_meta_bitmap = NULL;
+ }
- if (spa_meta_bitmap->size.width > 0 &&
+ if (spa_meta_bitmap &&
+ spa_meta_bitmap->size.width > 0 &&
spa_meta_bitmap->size.height > 0 &&
spa_pixel_format_to_grd_pixel_format (spa_type,
spa_meta_bitmap->format,
@@ -332,7 +340,7 @@ do_render (struct spa_loop *loop,
grd_session_vnc_set_cursor (stream->session, rfb_cursor);
}
- else
+ else if (spa_meta_bitmap)
{
rfbCursorPtr empty_cursor;
--
2.19.1

View File

@ -0,0 +1,119 @@
%global systemd_unit gnome-remote-desktop.service
Name: gnome-remote-desktop
Version: 0.1.6
Release: 5%{?dist}
Summary: GNOME Remote Desktop screen share service
License: GPLv2+
URL: https://gitlab.gnome.org/jadahl/gnome-remote-desktop
Source0: https://gitlab.gnome.org/jadahl/gnome-remote-desktop/uploads/c6862c12f0b741714d5a27e0693322fe/gnome-remote-desktop-0.1.6.tar.xz
# Adds encryption support (requires patched LibVNCServer)
Patch0: 0001-vnc-Add-anonymous-TLS-encryption-support.patch
# Align pipewire requirement with Fedora
Patch1: 0001-meson.build-Bump-pipewire-requirement-to-0.2.2.patch
# Crash fix (rhbz#1627469)
Patch2: 0001-session-vnc-Don-t-requeue-close-session-idle.patch
Patch3: 0002-vnc-pipewire-stream-Close-session-when-disconnected.patch
# Backport various fixes (rhbz#1659118)
Patch4: rhel8.0.0-backports.patch
# Backport password override, for testing (rhbz#1713330)
Patch5: 0001-vnc-Allow-overriding-password-with-env-var.patch
BuildRequires: git
BuildRequires: gcc
BuildRequires: meson >= 0.36.0
BuildRequires: pkgconfig
BuildRequires: pkgconfig(glib-2.0) >= 2.32
BuildRequires: pkgconfig(gio-unix-2.0) >= 2.32
BuildRequires: pkgconfig(libpipewire-0.2) >= 0.2.5
BuildRequires: pkgconfig(libvncserver) >= 0.9.11-7
BuildRequires: pkgconfig(libsecret-1)
BuildRequires: pkgconfig(libnotify)
BuildRequires: pkgconfig(gnutls)
BuildRequires: python3-devel
%{?systemd_requires}
BuildRequires: systemd
Requires: pipewire >= 0.2.5
%description
GNOME Remote Desktop is a remote desktop and screen sharing service for the
GNOME desktop environment.
%prep
%autosetup -S git
%build
%meson
%meson_build
%install
%meson_install
%post
%systemd_user_post %{systemd_unit}
%preun
%systemd_user_preun %{systemd_unit}
%postun
%systemd_user_postun_with_restart %{systemd_unit}
%files
%license COPYING
%doc README
%{_libexecdir}/gnome-remote-desktop-daemon
%{_userunitdir}/gnome-remote-desktop.service
%{_datadir}/glib-2.0/schemas/org.gnome.desktop.remote-desktop.gschema.xml
%{_datadir}/glib-2.0/schemas/org.gnome.desktop.remote-desktop.enums.xml
%changelog
* Thu May 30 2019 Tomáš Popela <tpopela@redhat.com> - 0.1.6-5
- Bump the version to make gating happy - that's bug 1681618
- Resolves: rhbz#1713330
* Fri May 24 2019 Jonas Ådahl <jadahl@redhat.com> - 0.1.6-4
- Backport password override test helper (rhbz#1713330)
* Thu Jan 3 2019 Jonas Ådahl <jadahl@redhat.com> - 0.1.6-3
- Backport various fixes (rhbz#1659118)
* Mon Oct 1 2018 Jonas Ådahl <jadahl@redhat.com> - 0.1.6-2
- Don't crash when PipeWire disconnects (rhbz#1627469)
* Tue Aug 7 2018 Jonas Ådahl <jadahl@redhat.com> - 0.1.6
- Update to 0.1.6
- Apply ANON-TLS patch
- Depend on pipewire 0.2.2
* Tue Aug 29 2017 Jonas Ådahl <jadahl@redhat.com> - 0.1.2-3
- Use %%autosetup
- Install licence file
* Tue Aug 22 2017 Jonas Ådahl <jadahl@redhat.com> - 0.1.2-2
- Remove gschema compilation step as that had been deprecated
* Mon Aug 21 2017 Jonas Ådahl <jadahl@redhat.com> - 0.1.2-1
- Update to 0.1.2
- Changed tabs to spaces
- Added systemd user macros
- Install to correct systemd user unit directory
- Compile gsettings schemas after install and uninstall
* Mon Aug 21 2017 Jonas Ådahl <jadahl@redhat.com> - 0.1.1-1
- First packaged version