commit 6e6f327aba8ccfa11120d865bf4690df6f1cbb00 Author: CentOS Sources Date: Tue Nov 3 06:46:41 2020 -0500 import gnome-remote-desktop-0.1.8-3.el8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4469da --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/gnome-remote-desktop-0.1.8.tar.xz diff --git a/.gnome-remote-desktop.metadata b/.gnome-remote-desktop.metadata new file mode 100644 index 0000000..e2dae6d --- /dev/null +++ b/.gnome-remote-desktop.metadata @@ -0,0 +1 @@ +3347257bd906cd0ca86887c692befd8d412afab1 SOURCES/gnome-remote-desktop-0.1.8.tar.xz diff --git a/SOURCES/0001-stream-log-a-warning-on-error.patch b/SOURCES/0001-stream-log-a-warning-on-error.patch new file mode 100644 index 0000000..a8b005e --- /dev/null +++ b/SOURCES/0001-stream-log-a-warning-on-error.patch @@ -0,0 +1,52 @@ +From 7670167e578eb5c6e032cff38112edf85df142ee Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Tue, 16 Jun 2020 11:44:52 +0200 +Subject: [PATCH 1/2] stream: log a warning on error + +When we get an invalid buffer or we can't mmap() it, log a warning +and exit instead of carying on with invalid pointers and segfault. +--- + src/grd-vnc-pipewire-stream.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 261292a..91fb0a1 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -323,14 +323,18 @@ process_buffer (GrdVncPipeWireStream *stream, + + if (buffer->datas[0].chunk->size == 0) + { +- size = 0; +- map = NULL; +- src_data = NULL; ++ g_warning ("Received empty buffer"); ++ return NULL; + } + else if (buffer->datas[0].type == SPA_DATA_MemFd) + { + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, buffer->datas[0].fd, 0); ++ if (map == MAP_FAILED) ++ { ++ g_warning ("Failed to mmap buffer: %s", g_strerror (errno)); ++ return NULL; ++ } + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); + } + else if (buffer->datas[0].type == SPA_DATA_DmaBuf) +@@ -341,6 +345,11 @@ process_buffer (GrdVncPipeWireStream *stream, + size = buffer->datas[0].maxsize + buffer->datas[0].mapoffset; + + map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); ++ if (map == MAP_FAILED) ++ { ++ g_warning ("Failed to mmap DMA buffer: %s", g_strerror (errno)); ++ return NULL; ++ } + sync_dma_buf (fd, DMA_BUF_SYNC_START); + + src_data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); +-- +2.26.2 + diff --git a/SOURCES/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch b/SOURCES/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch new file mode 100644 index 0000000..59bc460 --- /dev/null +++ b/SOURCES/0001-vnc-pipewire-stream-Handle-stride-mismatch.patch @@ -0,0 +1,73 @@ +From 78c5bcb181fe2b0b9fc17eea696feac8b504df54 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 7 May 2020 15:48:22 +0200 +Subject: [PATCH] vnc/pipewire-stream: Handle stride mismatch + +The VNC server framebuffer assumes a particular stride; but there is no +guarantee that we'll get the same from PipeWire. Handle this gracefully +by coping row by row instead of the whole buffer. +--- + src/grd-vnc-pipewire-stream.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 88c07be..261292a 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -187,8 +187,6 @@ on_stream_param_changed (void *user_data, + struct spa_pod_builder pod_builder; + int width; + int height; +- int stride; +- int size; + const struct spa_pod *params[3]; + + if (!format || id != SPA_PARAM_Format) +@@ -203,14 +201,9 @@ on_stream_param_changed (void *user_data, + + grd_session_vnc_queue_resize_framebuffer (stream->session, width, height); + +- stride = grd_session_vnc_get_framebuffer_stride (stream->session); +- size = stride * height; +- + params[0] = spa_pod_builder_add_object ( + &pod_builder, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, +- SPA_PARAM_BUFFERS_size, SPA_POD_Int (size), +- SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride), + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (8, 1, 8), + 0); + +@@ -319,6 +312,10 @@ process_buffer (GrdVncPipeWireStream *stream, + size_t size; + uint8_t *map; + void *src_data; ++ int src_stride; ++ int dst_stride; ++ int height; ++ int y; + struct spa_meta_cursor *spa_meta_cursor; + g_autofree GrdVncFrame *frame = NULL; + +@@ -359,7 +356,17 @@ process_buffer (GrdVncPipeWireStream *stream, + return NULL; + } + +- frame->data = g_memdup (src_data, buffer->datas[0].maxsize); ++ src_stride = buffer->datas[0].chunk->stride; ++ dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); ++ height = stream->spa_format.size.height; ++ ++ frame->data = g_malloc (height * dst_stride); ++ for (y = 0; y < height; y++) ++ { ++ memcpy (((uint8_t *) frame->data) + y * dst_stride, ++ ((uint8_t *) src_data) + y * src_stride, ++ dst_stride); ++ } + + if (map) + { +-- +2.26.2 + diff --git a/SOURCES/0001-vnc-pipewire-stream-Remove-assert.patch b/SOURCES/0001-vnc-pipewire-stream-Remove-assert.patch new file mode 100644 index 0000000..69e71b7 --- /dev/null +++ b/SOURCES/0001-vnc-pipewire-stream-Remove-assert.patch @@ -0,0 +1,25 @@ +From 240d8694fbcdeb020e7f9c0f8f292a4679b88b30 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 18 Jun 2020 13:14:04 +0200 +Subject: [PATCH] vnc/pipewire-stream: Remove assert + +Handle lack of frames gracefully. +--- + src/grd-vnc-pipewire-stream.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index ee8ad5d..a3f5fb6 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -463,7 +463,6 @@ on_stream_process (void *user_data) + + frame = process_buffer (stream, buffer->buffer); + +- g_assert (frame); + g_mutex_lock (&stream->frame_mutex); + if (stream->pending_frame) + { +-- +2.26.2 + diff --git a/SOURCES/0002-vnc-pipewire-stream-Only-try-to-copy-frame-pixels-if.patch b/SOURCES/0002-vnc-pipewire-stream-Only-try-to-copy-frame-pixels-if.patch new file mode 100644 index 0000000..ef46648 --- /dev/null +++ b/SOURCES/0002-vnc-pipewire-stream-Only-try-to-copy-frame-pixels-if.patch @@ -0,0 +1,62 @@ +From f3efe25a5cb173bc63b380619b8673cd5ba99f6f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 18 Jun 2020 11:35:44 +0200 +Subject: [PATCH 2/2] vnc/pipewire-stream: Only try to copy frame pixels if + there are any + +The producer might send empty frames with only cursor metadata, and in +this case we shouldn't try to copy any pixels, as there are no. +--- + src/grd-vnc-pipewire-stream.c | 28 ++++++++++++++++------------ + 1 file changed, 16 insertions(+), 12 deletions(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 91fb0a1..ee8ad5d 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -312,9 +312,6 @@ process_buffer (GrdVncPipeWireStream *stream, + size_t size; + uint8_t *map; + void *src_data; +- int src_stride; +- int dst_stride; +- int height; + int y; + struct spa_meta_cursor *spa_meta_cursor; + g_autofree GrdVncFrame *frame = NULL; +@@ -365,16 +362,23 @@ process_buffer (GrdVncPipeWireStream *stream, + return NULL; + } + +- src_stride = buffer->datas[0].chunk->stride; +- dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); +- height = stream->spa_format.size.height; +- +- frame->data = g_malloc (height * dst_stride); +- for (y = 0; y < height; y++) ++ if (src_data) + { +- memcpy (((uint8_t *) frame->data) + y * dst_stride, +- ((uint8_t *) src_data) + y * src_stride, +- dst_stride); ++ int src_stride; ++ int dst_stride; ++ int height; ++ ++ src_stride = buffer->datas[0].chunk->stride; ++ dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); ++ height = stream->spa_format.size.height; ++ ++ frame->data = g_malloc (height * dst_stride); ++ for (y = 0; y < height; y++) ++ { ++ memcpy (((uint8_t *) frame->data) + y * dst_stride, ++ ((uint8_t *) src_data) + y * src_stride, ++ dst_stride); ++ } + } + + if (map) +-- +2.26.2 + diff --git a/SOURCES/anon-tls-support.patch b/SOURCES/anon-tls-support.patch new file mode 100644 index 0000000..f165f69 --- /dev/null +++ b/SOURCES/anon-tls-support.patch @@ -0,0 +1,1532 @@ +From 10843a1f3edffbb475c01835451d39ebe6153e44 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 14 Jun 2018 12:21:37 +0200 +Subject: [PATCH 1/7] 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 | 15 + + src/grd-settings.c | 28 ++ + 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 +- + ...nome.desktop.remote-desktop.gschema.xml.in | 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 1c96849..a24acfd 100644 +--- a/meson.build ++++ b/meson.build +@@ -15,6 +15,7 @@ libvncserver_dep = dependency('libvncserver') + libvncclient_dep = dependency('libvncclient') + 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 1f3f0e2..0cc2ea2 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; +@@ -505,12 +507,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) + { +@@ -551,33 +571,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; +@@ -590,7 +651,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, +@@ -616,8 +680,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); +@@ -632,6 +698,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 14b5d12..46a8579 100644 +--- a/src/grd-session-vnc.h ++++ b/src/grd-session-vnc.h +@@ -36,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); + +@@ -53,6 +56,18 @@ void grd_session_vnc_move_cursor (GrdSessionVnc *session_vnc, + int x, + int y); + ++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 bdf8211..7324310 100644 +--- a/src/grd-settings.c ++++ b/src/grd-settings.c +@@ -48,6 +48,7 @@ struct _GrdSettings + gboolean view_only; + GrdVncAuthMethod auth_method; + int port; ++ GrdVncEncryption encryption; + } vnc; + }; + +@@ -120,6 +121,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) + { +@@ -134,6 +141,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, +@@ -149,6 +163,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 +@@ -172,6 +191,8 @@ grd_settings_init (GrdSettings *settings) + update_vnc_auth_method (settings); + + settings->vnc.port = GRD_VNC_SERVER_PORT; ++ ++ update_vnc_encryption (settings); + } + + static void +@@ -195,4 +216,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 e4e0c09..0575ec1 100644 +--- a/src/grd-settings.h ++++ b/src/grd-settings.h +@@ -45,4 +45,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 a6d95cb..f9c68db 100644 +--- a/src/grd-vnc-server.c ++++ b/src/grd-vnc-server.c +@@ -24,11 +24,13 @@ + + #include "grd-vnc-server.h" + ++#include + #include + #include + + #include "grd-context.h" + #include "grd-session-vnc.h" ++#include "grd-vnc-tls.h" + + + enum +@@ -130,6 +132,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) +@@ -220,12 +259,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..ec4758e +--- /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 ++#include ++#include ++#include ++ ++#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, 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 ++ ++rfbSecurityHandler * grd_vnc_tls_get_security_handler (void); ++ ++#endif /* GRD_VNC_TLS_H */ +diff --git a/src/meson.build b/src/meson.build +index 0f76fab..9d2f1ce 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -21,6 +21,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 = [] +@@ -51,7 +53,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.in b/src/org.gnome.desktop.remote-desktop.gschema.xml.in +index a5c2022..846e65b 100644 +--- a/src/org.gnome.desktop.remote-desktop.gschema.xml.in ++++ b/src/org.gnome.desktop.remote-desktop.gschema.xml.in +@@ -23,5 +23,15 @@ + * password - by requiring the remote client to provide a known password + + ++ ++ ['tls-anon'] ++ Allowed encryption method to use ++ ++ Allowed encryption methods. Includes the following: ++ ++ * none - no encryption ++ * tls-anon - anonymous (unauthenticated) TLS ++ ++ + + +-- +2.26.2 + + +From aa54aeb43938250a4d27a99e62eb5628d3b55076 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 11:02:09 +0100 +Subject: [PATCH 2/7] session-vnc: Add paused/resumed signals + +Paused is when the socket sourec is detached, and resumed when attached. +Meant to be used by the TLS channel security to a attach/detach +out-of-socket source. +--- + src/grd-session-vnc.c | 72 ++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 65 insertions(+), 7 deletions(-) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index 0cc2ea2..076e25f 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -40,14 +40,27 @@ + #define BGRX_SAMPLES_PER_PIXEL 3 + #define BGRX_BYTES_PER_PIXEL 4 + ++enum ++{ ++ PAUSED, ++ RESUMED, ++ ++ N_SIGNALS ++}; ++ ++static guint signals[N_SIGNALS]; ++ + struct _GrdSessionVnc + { + GrdSession parent; + + GrdVncServer *vnc_server; + GSocketConnection *connection; ++ + GList *socket_grabs; + GSource *source; ++ gboolean is_paused; ++ + rfbScreenInfoPtr rfb_screen; + rfbClientPtr rfb_client; + +@@ -73,7 +86,7 @@ struct _GrdSessionVnc + G_DEFINE_TYPE (GrdSessionVnc, grd_session_vnc, GRD_TYPE_SESSION); + + static void +-grd_session_vnc_detach_source (GrdSessionVnc *session_vnc); ++grd_session_vnc_pause (GrdSessionVnc *session_vnc); + + static gboolean + close_session_idle (gpointer user_data); +@@ -212,7 +225,8 @@ handle_client_gone (rfbClientPtr rfb_client) + + g_debug ("VNC client gone"); + +- grd_session_vnc_detach_source (session_vnc); ++ grd_session_vnc_pause (session_vnc); ++ + maybe_queue_close_session_idle (session_vnc); + } + +@@ -280,7 +294,7 @@ handle_new_client (rfbClientPtr rfb_client) + session_vnc->prompt_cancellable, + prompt_response_callback, + session_vnc); +- grd_session_vnc_detach_source (session_vnc); ++ grd_session_vnc_pause (session_vnc); + return RFB_CLIENT_ON_HOLD; + case GRD_VNC_AUTH_METHOD_PASSWORD: + session_vnc->rfb_screen->passwordCheck = check_rfb_password; +@@ -498,7 +512,7 @@ check_rfb_password (rfbClientPtr rfb_client, + if (memcmp (challenge_encrypted, response_encrypted, len) == 0) + { + grd_session_start (GRD_SESSION (session_vnc)); +- grd_session_vnc_detach_source (session_vnc); ++ grd_session_vnc_pause (session_vnc); + return TRUE; + } + else +@@ -668,6 +682,36 @@ grd_session_vnc_detach_source (GrdSessionVnc *session_vnc) + g_clear_pointer (&session_vnc->source, g_source_destroy); + } + ++gboolean ++grd_session_vnc_is_paused (GrdSessionVnc *session_vnc) ++{ ++ return session_vnc->is_paused; ++} ++ ++static void ++grd_session_vnc_pause (GrdSessionVnc *session_vnc) ++{ ++ if (grd_session_vnc_is_paused (session_vnc)) ++ return; ++ ++ session_vnc->is_paused = TRUE; ++ ++ grd_session_vnc_detach_source (session_vnc); ++ g_signal_emit (session_vnc, signals[PAUSED], 0); ++} ++ ++static void ++grd_session_vnc_resume (GrdSessionVnc *session_vnc) ++{ ++ if (!grd_session_vnc_is_paused (session_vnc)) ++ return; ++ ++ session_vnc->is_paused = FALSE; ++ ++ grd_session_vnc_attach_source (session_vnc); ++ g_signal_emit (session_vnc, signals[RESUMED], 0); ++} ++ + GrdSessionVnc * + grd_session_vnc_new (GrdVncServer *vnc_server, + GSocketConnection *connection) +@@ -685,6 +729,7 @@ grd_session_vnc_new (GrdVncServer *vnc_server, + + grd_session_vnc_grab_socket (session_vnc, vnc_socket_grab_func); + grd_session_vnc_attach_source (session_vnc); ++ session_vnc->is_paused = FALSE; + + init_vnc_session (session_vnc); + +@@ -714,7 +759,7 @@ grd_session_vnc_stop (GrdSession *session) + + g_clear_object (&session_vnc->pipewire_stream); + +- grd_session_vnc_detach_source (session_vnc); ++ grd_session_vnc_pause (session_vnc); + + g_clear_object (&session_vnc->connection); + g_clear_pointer (&session_vnc->rfb_screen->frameBuffer, g_free); +@@ -770,8 +815,8 @@ grd_session_vnc_stream_ready (GrdSession *session, + G_CALLBACK (on_pipwire_stream_closed), + session_vnc); + +- if (!session_vnc->source) +- grd_session_vnc_attach_source (session_vnc); ++ if (grd_session_vnc_is_paused (session_vnc)) ++ grd_session_vnc_resume (session_vnc); + } + + static void +@@ -790,4 +835,17 @@ grd_session_vnc_class_init (GrdSessionVncClass *klass) + + session_class->stop = grd_session_vnc_stop; + session_class->stream_ready = grd_session_vnc_stream_ready; ++ ++ signals[PAUSED] = g_signal_new ("paused", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++ signals[RESUMED] = g_signal_new ("resumed", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); + } +-- +2.26.2 + + +From ed3d72cb8d08192831397903f0ba92f439751988 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 11:03:46 +0100 +Subject: [PATCH 3/7] session-vnc: Add grd_session_vnc_dispatch() helper + +To be used by the TLS channel security to dispatch when there is data +available that is not visible to the socket source. +--- + src/grd-session-vnc.c | 26 ++++++++++++++++---------- + src/grd-session-vnc.h | 2 ++ + 2 files changed, 18 insertions(+), 10 deletions(-) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index 076e25f..8b8ce1b 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -622,6 +622,21 @@ vnc_socket_grab_func (GrdSessionVnc *session_vnc, + return TRUE; + } + ++void ++grd_session_vnc_dispatch (GrdSessionVnc *session_vnc) ++{ ++ GrdVncSocketGrabFunc grab_func; ++ g_autoptr (GError) error = NULL; ++ ++ grab_func = g_list_first (session_vnc->socket_grabs)->data; ++ if (!grab_func (session_vnc, &error)) ++ { ++ g_warning ("Error when reading socket: %s", error->message); ++ ++ grd_session_stop (GRD_SESSION (session_vnc)); ++ } ++} ++ + static gboolean + handle_socket_data (GSocket *socket, + GIOCondition condition, +@@ -638,16 +653,7 @@ handle_socket_data (GSocket *socket, + } + else if (condition & G_IO_IN) + { +- GrdVncSocketGrabFunc grab_func; +- g_autoptr (GError) error = NULL; +- +- grab_func = g_list_first (session_vnc->socket_grabs)->data; +- if (!grab_func (session_vnc, &error)) +- { +- g_warning ("Error when reading socket: %s", error->message); +- +- grd_session_stop (session); +- } ++ grd_session_vnc_dispatch (session_vnc); + } + else + { +diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h +index 46a8579..910b00c 100644 +--- a/src/grd-session-vnc.h ++++ b/src/grd-session-vnc.h +@@ -68,6 +68,8 @@ void grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc, + void grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc, + GrdVncSocketGrabFunc grab_func); + ++void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc); ++ + GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc); + + #endif /* GRD_SESSION_VNC_H */ +-- +2.26.2 + + +From 44e6bec84a86064a7b3abbcbbcd07ebb525aca9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 11:05:13 +0100 +Subject: [PATCH 4/7] vnc/tls: Add some logging + +Uses the log utility from libvncserver as it is related to the RFB +protocol rather than the session itself. +--- + src/grd-vnc-tls.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c +index ec4758e..ac6c35f 100644 +--- a/src/grd-vnc-tls.c ++++ b/src/grd-vnc-tls.c +@@ -67,6 +67,7 @@ grd_vnc_tls_context_new (void) + + tls_context = g_new0 (GrdVncTlsContext, 1); + ++ rfbLog ("TLS: Initializing gnutls context\n"); + gnutls_global_init (); + + gnutls_anon_allocate_server_credentials (&tls_context->anon_credentials); +@@ -127,6 +128,7 @@ perform_anon_tls_handshake (GrdVncTlsSession *tls_session, + ret = gnutls_handshake (tls_session->tls_session); + if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret)) + { ++ rfbLog ("TLS: More handshake pending\n"); + tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_DURING; + return TRUE; + } +@@ -140,6 +142,8 @@ perform_anon_tls_handshake (GrdVncTlsSession *tls_session, + return FALSE; + } + ++ rfbLog ("TLS: Handshake finished"); ++ + tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED; + return TRUE; + } +@@ -373,6 +377,7 @@ perform_handshake (GrdSessionVnc *session_vnc, + break; + case GRD_TLS_HANDSHAKE_STATE_FINISHED: + grd_session_vnc_ungrab_socket (session_vnc, tls_handshake_grab_func); ++ rfbLog ("TLS: Sending post-channel security security list\n"); + rfbSendSecurityTypeList (grd_session_vnc_get_rfb_client (session_vnc), + RFB_SECURITY_TAG_CHANNEL); + break; +@@ -387,6 +392,7 @@ tls_handshake_grab_func (GrdSessionVnc *session_vnc, + { + g_autoptr (GError) handshake_error = NULL; + ++ rfbLog ("TLS: Continuing handshake\n"); + if (!perform_handshake (session_vnc, &handshake_error)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +@@ -404,6 +410,8 @@ rfb_tls_security_handler (rfbClientPtr rfb_client) + GrdVncTlsSession *tls_session; + g_autoptr(GError) error = NULL; + ++ rfbLog ("TLS: Setting up rfbClient for gnutls encrypted traffic\n"); ++ + tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc); + if (!tls_session) + { +@@ -424,6 +432,7 @@ rfb_tls_security_handler (rfbClientPtr rfb_client) + grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func); + } + ++ rfbLog ("TLS: Performing handshake\n"); + if (!perform_handshake (session_vnc, &error)) + { + g_warning ("TLS handshake failed: %s", error->message); +-- +2.26.2 + + +From fc07db3b6fafec47e02ff81f0f893dcaf64ba988 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 11:07:40 +0100 +Subject: [PATCH 5/7] vnc/tls: Dispatch also when data is pending outside of + the socket + +gnutls may have data available in its buffers, and we have our own peek +buffer temporarly storing data later to be processed. This would missed +by the socket source, as it wouldn't get any notification about it from +epoll(). Deal with this by adding a custom source that dispatches as +long as there is data to read in those buffers. +--- + src/grd-session-vnc.h | 2 + + src/grd-vnc-tls.c | 92 ++++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 88 insertions(+), 6 deletions(-) + +diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h +index 910b00c..294860e 100644 +--- a/src/grd-session-vnc.h ++++ b/src/grd-session-vnc.h +@@ -68,6 +68,8 @@ void grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc, + void grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc, + GrdVncSocketGrabFunc grab_func); + ++gboolean grd_session_vnc_is_paused (GrdSessionVnc *session_vnc); ++ + void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc); + + GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc); +diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c +index ac6c35f..8f65225 100644 +--- a/src/grd-vnc-tls.c ++++ b/src/grd-vnc-tls.c +@@ -41,6 +41,12 @@ typedef enum _GrdTlsHandshakeState + GRD_TLS_HANDSHAKE_STATE_FINISHED + } GrdTlsHandshakeState; + ++typedef struct _PeekBufferSource ++{ ++ GSource parent; ++ GrdSessionVnc *session_vnc; ++} PeekBufferSource; ++ + typedef struct _GrdVncTlsSession + { + GrdVncTlsContext *tls_context; +@@ -53,6 +59,8 @@ typedef struct _GrdVncTlsSession + char *peek_buffer; + int peek_buffer_size; + int peek_buffer_len; ++ ++ GSource *peek_buffer_source; + } GrdVncTlsSession; + + static gboolean +@@ -296,16 +304,14 @@ grd_vnc_tls_peek_at_socket (rfbClientPtr rfb_client, + peekable_len = MIN (len, tls_session->peek_buffer_len); + memcpy (buf, tls_session->peek_buffer, peekable_len); + ++ fprintf(stderr, ":::: %s:%d %s() - peeked %d bytes, can peek %d bytes\n", __FILE__, __LINE__, __func__, ++ peekable_len, tls_session->peek_buffer_len); + return peekable_len; + } + +-static rfbBool +-grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client) ++static gboolean ++grd_vnc_tls_session_has_pending_data (GrdVncTlsSession *tls_session) + { +- 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; + +@@ -315,6 +321,16 @@ grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client) + return FALSE; + } + ++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); ++ ++ return grd_vnc_tls_session_has_pending_data (tls_session); ++} ++ + static int + grd_vnc_tls_write_to_socket (rfbClientPtr rfb_client, + const char *buf, +@@ -403,6 +419,62 @@ tls_handshake_grab_func (GrdSessionVnc *session_vnc, + return TRUE; + } + ++static gboolean ++peek_buffer_source_prepare (GSource *source, ++ int *timeout) ++{ ++ PeekBufferSource *psource = (PeekBufferSource *) source; ++ GrdSessionVnc *session_vnc = psource->session_vnc; ++ GrdVncTlsSession *tls_session = ++ grd_vnc_tls_session_from_vnc_session (session_vnc); ++ ++ return grd_vnc_tls_session_has_pending_data (tls_session); ++} ++ ++static gboolean ++peek_buffer_source_dispatch (GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ PeekBufferSource *psource = (PeekBufferSource *) source; ++ GrdSessionVnc *session_vnc = psource->session_vnc; ++ ++ grd_session_vnc_dispatch (session_vnc); ++ ++ return G_SOURCE_CONTINUE; ++} ++ ++static GSourceFuncs peek_buffer_source_funcs = { ++ .prepare = peek_buffer_source_prepare, ++ .dispatch = peek_buffer_source_dispatch, ++}; ++ ++static void ++attach_peek_buffer_source (GrdSessionVnc *session_vnc) ++{ ++ GrdVncTlsSession *tls_session; ++ ++ tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc); ++ tls_session->peek_buffer_source = g_source_new (&peek_buffer_source_funcs, ++ sizeof (PeekBufferSource)); ++ ((PeekBufferSource *) tls_session->peek_buffer_source)->session_vnc = ++ session_vnc; ++ g_source_set_priority (tls_session->peek_buffer_source, ++ G_PRIORITY_DEFAULT + 1); ++ ++ g_source_attach (tls_session->peek_buffer_source, NULL); ++} ++ ++static void ++detach_peek_buffer_source (GrdSessionVnc *session_vnc) ++{ ++ GrdVncTlsSession *tls_session; ++ ++ tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc); ++ ++ g_clear_pointer (&tls_session->peek_buffer_source, g_source_destroy); ++} ++ + static void + rfb_tls_security_handler (rfbClientPtr rfb_client) + { +@@ -429,6 +501,14 @@ rfb_tls_security_handler (rfbClientPtr rfb_client) + rfb_client->hasPendingOnSocket = grd_vnc_tls_has_pending_on_socket; + rfb_client->writeToSocket = grd_vnc_tls_write_to_socket; + ++ if (!grd_session_vnc_is_paused (session_vnc)) ++ attach_peek_buffer_source (session_vnc); ++ ++ g_signal_connect (session_vnc, "paused", ++ G_CALLBACK (detach_peek_buffer_source), NULL); ++ g_signal_connect (session_vnc, "resumed", ++ G_CALLBACK (attach_peek_buffer_source), NULL); ++ + grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func); + } + +-- +2.26.2 + + +From c582baab12c1e2dd2b512329da42880c40993df6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 16:48:00 +0100 +Subject: [PATCH 6/7] session-vnc: Set our own password handling function up + front + +libvncserver decides whether to register a auth security handler +depending on whether the password data is set or not. When we use the +prompt auth method, we don't want to ask for password, so set the +password data to NULL. + +Also, to be a bit more in control of the password mechanism, always set +the password function up front, instead of just when the client uses the +password prompt. +--- + src/grd-session-vnc.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index 8b8ce1b..a93a2e3 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -91,11 +91,6 @@ grd_session_vnc_pause (GrdSessionVnc *session_vnc); + static gboolean + close_session_idle (gpointer user_data); + +-static rfbBool +-check_rfb_password (rfbClientPtr rfb_client, +- const char *response_encrypted, +- int len); +- + static void + swap_uint8 (uint8_t *a, + uint8_t *b) +@@ -297,7 +292,6 @@ handle_new_client (rfbClientPtr rfb_client) + grd_session_vnc_pause (session_vnc); + return RFB_CLIENT_ON_HOLD; + case GRD_VNC_AUTH_METHOD_PASSWORD: +- session_vnc->rfb_screen->passwordCheck = check_rfb_password; + /* + * authPasswdData needs to be non NULL in libvncserver to trigger + * password authentication. +@@ -581,6 +575,8 @@ 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); + ++ rfb_screen->passwordCheck = check_rfb_password; ++ + rfbInitServer (rfb_screen); + rfbProcessEvents (rfb_screen, 0); + } +-- +2.26.2 + + +From b7fc232ee5272b430f28c33ebaacd501ff63a4dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 27 Nov 2019 16:53:42 +0100 +Subject: [PATCH 7/7] vnc: Unregister previously set security handlers on init + +When we're starting a session, we're going to handle a new client +connection. However, any previous client that was ever run on in a +previous session would still have their "security handler" registered, +as such is a global permanent change in libvncserver right now. + +To work around this, unregister all primary security handler (i.e. +'none' and 'password') when initializing the RFB screen. We'll set up +the preferred one when handling the new client. +--- + src/grd-session-vnc.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index a93a2e3..9fcbb69 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -555,6 +555,12 @@ init_vnc_session (GrdSessionVnc *session_vnc) + 8, 3, 4); + session_vnc->rfb_screen = rfb_screen; + ++ /* ++ * Unregister whatever security handler was used the last time; we'll set ++ * up new ones when authorizing the new client anyway. ++ */ ++ rfbUnregisterPrimarySecurityHandlers (); ++ + update_server_format (session_vnc); + + socket = g_socket_connection_get_socket (session_vnc->connection); +-- +2.26.2 + diff --git a/SOURCES/cursor-only-frame-fixes.patch b/SOURCES/cursor-only-frame-fixes.patch new file mode 100644 index 0000000..228ff15 --- /dev/null +++ b/SOURCES/cursor-only-frame-fixes.patch @@ -0,0 +1,127 @@ +From f97b689c5c67cee36025a7b0a9210deb8b373b03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 17:03:52 +0200 +Subject: [PATCH 1/3] session-vnc: Add API to flush + +When no damage is to be reported, but e.g. cursor moved, we need to +flush, so add API to make this possible. + +(cherry picked from commit 25e61a7ed3631687aed4310824e7810088e63b37) +--- + src/grd-session-vnc.c | 6 ++++++ + src/grd-session-vnc.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c +index 9fcbb69..7950d1e 100644 +--- a/src/grd-session-vnc.c ++++ b/src/grd-session-vnc.c +@@ -179,6 +179,12 @@ grd_session_vnc_take_buffer (GrdSessionVnc *session_vnc, + rfbProcessEvents (session_vnc->rfb_screen, 0); + } + ++void ++grd_session_vnc_flush (GrdSessionVnc *session_vnc) ++{ ++ rfbProcessEvents (session_vnc->rfb_screen, 0); ++} ++ + void + grd_session_vnc_set_cursor (GrdSessionVnc *session_vnc, + rfbCursorPtr rfb_cursor) +diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h +index 294860e..a065857 100644 +--- a/src/grd-session-vnc.h ++++ b/src/grd-session-vnc.h +@@ -49,6 +49,8 @@ void grd_session_vnc_queue_resize_framebuffer (GrdSessionVnc *session_vnc, + void grd_session_vnc_take_buffer (GrdSessionVnc *session_vnc, + void *data); + ++void grd_session_vnc_flush (GrdSessionVnc *session_vnc); ++ + void grd_session_vnc_set_cursor (GrdSessionVnc *session_vnc, + rfbCursorPtr rfb_cursor); + +-- +2.26.2 + + +From 8a050b66be76d73725ac7665295160ab6c40b0f5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 17:12:58 +0200 +Subject: [PATCH 2/3] vnc-pipewire-stream: Properly process cursor-change-only + frames + +Such frames will have the buffer data size set to 0, as it is empty, but +may contain metadata carrying the cursor update. + +(cherry picked from commit c04762a450ea9a21730db26c296c1283e121dc08) +--- + src/grd-vnc-pipewire-stream.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index a3f5fb6..7519377 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -312,7 +312,6 @@ process_buffer (GrdVncPipeWireStream *stream, + size_t size; + uint8_t *map; + void *src_data; +- int y; + struct spa_meta_cursor *spa_meta_cursor; + g_autofree GrdVncFrame *frame = NULL; + +@@ -320,8 +319,8 @@ process_buffer (GrdVncPipeWireStream *stream, + + if (buffer->datas[0].chunk->size == 0) + { +- g_warning ("Received empty buffer"); +- return NULL; ++ map = NULL; ++ src_data = NULL; + } + else if (buffer->datas[0].type == SPA_DATA_MemFd) + { +@@ -367,6 +366,7 @@ process_buffer (GrdVncPipeWireStream *stream, + int src_stride; + int dst_stride; + int height; ++ int y; + + src_stride = buffer->datas[0].chunk->stride; + dst_stride = grd_session_vnc_get_framebuffer_stride (stream->session); +-- +2.26.2 + + +From eac6368d8411c586007df8b1a2d85df3da1b55c5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Jul 2020 17:13:58 +0200 +Subject: [PATCH 3/3] vnc-pipewire-stream: Flush connection if no new pixel + buffer + +Otherwise we'll wait on input until we flush out our new cursor move +only output. + +(cherry picked from commit 3394e34c3c502d63636bb852c062855c46736a6f) +--- + src/grd-vnc-pipewire-stream.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c +index 7519377..791b71d 100644 +--- a/src/grd-vnc-pipewire-stream.c ++++ b/src/grd-vnc-pipewire-stream.c +@@ -299,6 +299,8 @@ do_render (struct spa_loop *loop, + + if (frame->data) + grd_session_vnc_take_buffer (stream->session, frame->data); ++ else ++ grd_session_vnc_flush (stream->session); + + g_free (frame); + +-- +2.26.2 + diff --git a/SPECS/gnome-remote-desktop.spec b/SPECS/gnome-remote-desktop.spec new file mode 100644 index 0000000..3590d90 --- /dev/null +++ b/SPECS/gnome-remote-desktop.spec @@ -0,0 +1,141 @@ +%global systemd_unit gnome-remote-desktop.service + +Name: gnome-remote-desktop +Version: 0.1.8 +Release: 3%{?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/20e4965351cdbd8dc32ff9801e884b91/gnome-remote-desktop-0.1.8.tar.xz + +# Fix black screen on Wayland +Patch1: 0001-vnc-pipewire-stream-Handle-stride-mismatch.patch + +# Anon TLS encryption support +Patch2: anon-tls-support.patch + +# Don't crash on metadata only buffers (#1847062) +Patch3: 0001-stream-log-a-warning-on-error.patch +Patch4: 0002-vnc-pipewire-stream-Only-try-to-copy-frame-pixels-if.patch +Patch5: 0001-vnc-pipewire-stream-Remove-assert.patch + +# Cursor only frame fixes (#1837406) +Patch6: cursor-only-frame-fixes.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.3) >= 0.3.4 +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.3.4 + +%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 +* Wed Jul 15 2020 Jonas Ådahl - 0.1.8-3 +- Backport cursor only frame fixes + Related: #1837406 + +* Thu Jun 18 2020 Jonas Ådahl - 0.1.8-2 +- Don't crash on metadata only buffers + Resolves: #1847062 + +* Wed May 20 2020 Jonas Ådahl - 0.1.8-1 +- Rebase to 0.1.8 + Resolves: #1837406 + +* Wed Nov 27 2019 Jonas Ådahl - 0.1.6-8 +- Update patch to handle older libvncserver at build time + Resolves: #1684729 + +* Wed Nov 27 2019 Jonas Ådahl - 0.1.6-7 +- Handle auth settings changes + Resolves: #1684729 + +* Wed Nov 27 2019 Jonas Ådahl - 0.1.6-6 +- Fix initial black content issue + Resolves: #1765448 + +* Thu May 30 2019 Tomáš Popela - 0.1.6-5 +- Bump the version to make gating happy - that's bug 1681618 +- Resolves: rhbz#1713330 + +* Fri May 24 2019 Jonas Ådahl - 0.1.6-4 +- Backport password override test helper (rhbz#1713330) + +* Thu Jan 3 2019 Jonas Ådahl - 0.1.6-3 +- Backport various fixes (rhbz#1659118) + +* Mon Oct 1 2018 Jonas Ådahl - 0.1.6-2 +- Don't crash when PipeWire disconnects (rhbz#1627469) + +* Tue Aug 7 2018 Jonas Ådahl - 0.1.6 +- Update to 0.1.6 +- Apply ANON-TLS patch +- Depend on pipewire 0.2.2 + +* Tue Aug 29 2017 Jonas Ådahl - 0.1.2-3 +- Use %%autosetup +- Install licence file + +* Tue Aug 22 2017 Jonas Ådahl - 0.1.2-2 +- Remove gschema compilation step as that had been deprecated + +* Mon Aug 21 2017 Jonas Ådahl - 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 - 0.1.1-1 +- First packaged version