Blame SOURCES/anon-tls-support.patch

e34316
From 10843a1f3edffbb475c01835451d39ebe6153e44 Mon Sep 17 00:00:00 2001
e142da
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e142da
Date: Thu, 14 Jun 2018 12:21:37 +0200
e34316
Subject: [PATCH 1/7] vnc: Add anonymous TLS encryption support
e142da
e142da
Add support for encrypting the VNC connection using anonymous TLS. In
e142da
effect this means that the channel is encrypted using TLS but that no
e142da
authentication of the peers are done. This means the connection is still
e142da
vulnerable to man-in-the-middle attacks where an attacker proxies the
e142da
VNC connection.
e142da
---
e142da
 meson.build                                   |   1 +
e142da
 src/grd-enums.h                               |   6 +
e142da
 src/grd-session-vnc.c                         |  98 +++-
e34316
 src/grd-session-vnc.h                         |  15 +
e34316
 src/grd-settings.c                            |  28 ++
e142da
 src/grd-settings.h                            |   2 +
e142da
 src/grd-vnc-server.c                          |  45 ++
e142da
 src/grd-vnc-tls.c                             | 444 ++++++++++++++++++
e142da
 src/grd-vnc-tls.h                             |  28 ++
e142da
 src/meson.build                               |   5 +-
e34316
 ...nome.desktop.remote-desktop.gschema.xml.in |  10 +
e142da
 11 files changed, 666 insertions(+), 16 deletions(-)
e142da
 create mode 100644 src/grd-vnc-tls.c
e142da
 create mode 100644 src/grd-vnc-tls.h
e142da
e142da
diff --git a/meson.build b/meson.build
e34316
index 1c96849..a24acfd 100644
e142da
--- a/meson.build
e142da
+++ b/meson.build
e34316
@@ -15,6 +15,7 @@ libvncserver_dep = dependency('libvncserver')
e34316
 libvncclient_dep = dependency('libvncclient')
e142da
 libsecret_dep = dependency('libsecret-1')
e142da
 libnotify_dep = dependency('libnotify')
e142da
+gnutls_dep = dependency('gnutls')
e142da
 
e142da
 cdata = configuration_data()
e142da
 cdata.set_quoted('GETTEXT_PACKAGE', 'gnome-remote-desktop')
e142da
diff --git a/src/grd-enums.h b/src/grd-enums.h
e142da
index ffab821..4333863 100644
e142da
--- a/src/grd-enums.h
e142da
+++ b/src/grd-enums.h
e142da
@@ -27,4 +27,10 @@ typedef enum
e142da
   GRD_VNC_AUTH_METHOD_PASSWORD
e142da
 } GrdVncAuthMethod;
e142da
 
e142da
+typedef enum
e142da
+{
e142da
+  GRD_VNC_ENCRYPTION_NONE = 1 << 0,
e142da
+  GRD_VNC_ENCRYPTION_TLS_ANON = 1 << 1,
e142da
+} GrdVncEncryption;
e142da
+
e142da
 #endif /* GRD_ENUMS_H */
e142da
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
e34316
index 1f3f0e2..0cc2ea2 100644
e142da
--- a/src/grd-session-vnc.c
e142da
+++ b/src/grd-session-vnc.c
e142da
@@ -44,7 +44,9 @@ struct _GrdSessionVnc
e142da
 {
e142da
   GrdSession parent;
e142da
 
e142da
+  GrdVncServer *vnc_server;
e142da
   GSocketConnection *connection;
e142da
+  GList *socket_grabs;
e142da
   GSource *source;
e142da
   rfbScreenInfoPtr rfb_screen;
e142da
   rfbClientPtr rfb_client;
e34316
@@ -505,12 +507,30 @@ check_rfb_password (rfbClientPtr  rfb_client,
e142da
     }
e142da
 }
e142da
 
e142da
+int
e142da
+grd_session_vnc_get_fd (GrdSessionVnc *session_vnc)
e142da
+{
e142da
+  return session_vnc->rfb_screen->inetdSock;
e142da
+}
e142da
+
e142da
 int
e142da
 grd_session_vnc_get_framebuffer_stride (GrdSessionVnc *session_vnc)
e142da
 {
e142da
   return session_vnc->rfb_screen->paddedWidthInBytes;
e142da
 }
e142da
 
e142da
+rfbClientPtr
e142da
+grd_session_vnc_get_rfb_client (GrdSessionVnc *session_vnc)
e142da
+{
e142da
+  return session_vnc->rfb_client;
e142da
+}
e142da
+
e142da
+GrdVncServer *
e142da
+grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc)
e142da
+{
e142da
+  return session_vnc->vnc_server;
e142da
+}
e142da
+
e142da
 static void
e142da
 init_vnc_session (GrdSessionVnc *session_vnc)
e142da
 {
e34316
@@ -551,33 +571,74 @@ init_vnc_session (GrdSessionVnc *session_vnc)
e142da
   rfbProcessEvents (rfb_screen, 0);
e142da
 }
e142da
 
e142da
+void
e142da
+grd_session_vnc_grab_socket (GrdSessionVnc        *session_vnc,
e142da
+                             GrdVncSocketGrabFunc  grab_func)
e142da
+{
e142da
+  session_vnc->socket_grabs = g_list_prepend (session_vnc->socket_grabs,
e142da
+                                              grab_func);
e142da
+}
e142da
+
e142da
+void
e142da
+grd_session_vnc_ungrab_socket (GrdSessionVnc        *session_vnc,
e142da
+                               GrdVncSocketGrabFunc  grab_func)
e142da
+{
e142da
+  session_vnc->socket_grabs = g_list_remove (session_vnc->socket_grabs,
e142da
+                                             grab_func);
e142da
+}
e142da
+
e142da
+static gboolean
e142da
+vnc_socket_grab_func (GrdSessionVnc  *session_vnc,
e142da
+                      GError        **error)
e142da
+{
e142da
+  if (rfbIsActive (session_vnc->rfb_screen))
e142da
+    {
e142da
+      rfbProcessEvents (session_vnc->rfb_screen, 0);
e142da
+
e142da
+      if (session_vnc->pending_framebuffer_resize &&
e142da
+          session_vnc->rfb_client->preferredEncoding != -1)
e142da
+        {
e142da
+          resize_vnc_framebuffer (session_vnc,
e142da
+                                  session_vnc->pending_framebuffer_width,
e142da
+                                  session_vnc->pending_framebuffer_height);
e142da
+          session_vnc->pending_framebuffer_resize = FALSE;
e142da
+        }
e142da
+    }
e142da
+
e142da
+  return TRUE;
e142da
+}
e142da
+
e142da
 static gboolean
e142da
 handle_socket_data (GSocket *socket,
e142da
                     GIOCondition condition,
e142da
                     gpointer user_data)
e142da
 {
e142da
-  GrdSessionVnc *session_vnc = user_data;
e142da
+  GrdSessionVnc *session_vnc = GRD_SESSION_VNC (user_data);
e142da
+  GrdSession *session = GRD_SESSION (session_vnc);
e142da
 
e142da
-  if (condition & G_IO_IN)
e142da
+  if (condition & (G_IO_ERR | G_IO_HUP))
e142da
+    {
e142da
+      g_warning ("Client disconnected");
e142da
+
e142da
+      grd_session_stop (session);
e142da
+    }
e142da
+  else if (condition & G_IO_IN)
e142da
     {
e142da
-      if (rfbIsActive (session_vnc->rfb_screen))
e142da
+      GrdVncSocketGrabFunc grab_func;
e142da
+      g_autoptr (GError) error = NULL;
e142da
+
e142da
+      grab_func = g_list_first (session_vnc->socket_grabs)->data;
e142da
+      if (!grab_func (session_vnc, &error))
e142da
         {
e142da
-          rfbProcessEvents (session_vnc->rfb_screen, 0);
e142da
+          g_warning ("Error when reading socket: %s", error->message);
e142da
 
e142da
-          if (session_vnc->pending_framebuffer_resize &&
e142da
-              session_vnc->rfb_client->preferredEncoding != -1)
e142da
-            {
e142da
-              resize_vnc_framebuffer (session_vnc,
e142da
-                                      session_vnc->pending_framebuffer_width,
e142da
-                                      session_vnc->pending_framebuffer_height);
e142da
-              session_vnc->pending_framebuffer_resize = FALSE;
e142da
-            }
e142da
+          grd_session_stop (session);
e142da
         }
e142da
     }
e142da
   else
e142da
     {
e142da
-      g_debug ("Unhandled socket condition %d\n", condition);
e142da
-      return G_SOURCE_REMOVE;
e142da
+      g_warning ("Unhandled socket condition %d\n", condition);
e142da
+      g_assert_not_reached ();
e142da
     }
e142da
 
e142da
   return G_SOURCE_CONTINUE;
e34316
@@ -590,7 +651,10 @@ grd_session_vnc_attach_source (GrdSessionVnc *session_vnc)
e142da
 
e142da
   socket = g_socket_connection_get_socket (session_vnc->connection);
e142da
   session_vnc->source = g_socket_create_source (socket,
e142da
-                                                G_IO_IN | G_IO_PRI,
e142da
+                                                (G_IO_IN |
e142da
+                                                 G_IO_PRI |
e142da
+                                                 G_IO_ERR |
e142da
+                                                 G_IO_HUP),
e142da
                                                 NULL);
e142da
   g_source_set_callback (session_vnc->source,
e142da
                          (GSourceFunc) handle_socket_data,
e34316
@@ -616,8 +680,10 @@ grd_session_vnc_new (GrdVncServer      *vnc_server,
e142da
                               "context", context,
e142da
                               NULL);
e142da
 
e142da
+  session_vnc->vnc_server = vnc_server;
e142da
   session_vnc->connection = g_object_ref (connection);
e142da
 
e142da
+  grd_session_vnc_grab_socket (session_vnc, vnc_socket_grab_func);
e142da
   grd_session_vnc_attach_source (session_vnc);
e142da
 
e142da
   init_vnc_session (session_vnc);
e34316
@@ -632,6 +698,8 @@ grd_session_vnc_dispose (GObject *object)
e142da
 
e142da
   g_assert (!session_vnc->rfb_screen);
e142da
 
e142da
+  g_clear_pointer (&session_vnc->socket_grabs, g_list_free);
e142da
+
e142da
   g_clear_pointer (&session_vnc->pressed_keys, g_hash_table_unref);
e142da
 
e142da
   G_OBJECT_CLASS (grd_session_vnc_parent_class)->dispose (object);
e142da
diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h
e34316
index 14b5d12..46a8579 100644
e142da
--- a/src/grd-session-vnc.h
e142da
+++ b/src/grd-session-vnc.h
e34316
@@ -36,6 +36,9 @@ G_DECLARE_FINAL_TYPE (GrdSessionVnc,
e142da
                       GRD, SESSION_VNC,
e142da
                       GrdSession);
e142da
 
e142da
+typedef gboolean (* GrdVncSocketGrabFunc) (GrdSessionVnc  *session_vnc,
e142da
+                                           GError        **error);
e142da
+
e142da
 GrdSessionVnc *grd_session_vnc_new (GrdVncServer      *vnc_server,
e142da
                                     GSocketConnection *connection);
e142da
 
e34316
@@ -53,6 +56,18 @@ void grd_session_vnc_move_cursor (GrdSessionVnc *session_vnc,
e34316
                                   int            x,
e34316
                                   int            y);
e142da
 
e142da
+int grd_session_vnc_get_fd (GrdSessionVnc *session_vnc);
e142da
+
e142da
 int grd_session_vnc_get_framebuffer_stride (GrdSessionVnc *session_vnc);
e142da
 
e142da
+rfbClientPtr grd_session_vnc_get_rfb_client (GrdSessionVnc *session_vnc);
e142da
+
e142da
+void grd_session_vnc_grab_socket (GrdSessionVnc        *session_vnc,
e142da
+                                  GrdVncSocketGrabFunc  grab_func);
e142da
+
e142da
+void grd_session_vnc_ungrab_socket (GrdSessionVnc        *session_vnc,
e142da
+                                    GrdVncSocketGrabFunc  grab_func);
e142da
+
e142da
+GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc);
e142da
+
e142da
 #endif /* GRD_SESSION_VNC_H */
e142da
diff --git a/src/grd-settings.c b/src/grd-settings.c
e34316
index bdf8211..7324310 100644
e142da
--- a/src/grd-settings.c
e142da
+++ b/src/grd-settings.c
e34316
@@ -48,6 +48,7 @@ struct _GrdSettings
e142da
     gboolean view_only;
e142da
     GrdVncAuthMethod auth_method;
e34316
     int port;
e142da
+    GrdVncEncryption encryption;
e142da
   } vnc;
e142da
 };
e142da
 
e34316
@@ -120,6 +121,12 @@ grd_settings_get_vnc_auth_method (GrdSettings *settings)
e34316
     return settings->vnc.auth_method;
e142da
 }
e142da
 
e142da
+GrdVncEncryption
e142da
+grd_settings_get_vnc_encryption (GrdSettings *settings)
e142da
+{
e142da
+  return settings->vnc.encryption;
e142da
+}
e142da
+
e142da
 static void
e142da
 update_vnc_view_only (GrdSettings *settings)
e142da
 {
e34316
@@ -134,6 +141,13 @@ update_vnc_auth_method (GrdSettings *settings)
e142da
                                                    "auth-method");
e142da
 }
e142da
 
e142da
+static void
e142da
+update_vnc_encryption (GrdSettings *settings)
e142da
+{
e142da
+  settings->vnc.encryption = g_settings_get_flags (settings->vnc.settings,
e142da
+                                                   "encryption");
e142da
+}
e142da
+
e142da
 static void
e142da
 on_vnc_settings_changed (GSettings   *vnc_settings,
e142da
                          const char  *key,
e34316
@@ -149,6 +163,11 @@ on_vnc_settings_changed (GSettings   *vnc_settings,
e142da
       update_vnc_auth_method (settings);
e142da
       g_signal_emit (settings, signals[VNC_AUTH_METHOD_CHANGED], 0);
e142da
     }
e142da
+  else if (strcmp (key, "encryption") == 0)
e142da
+    {
e142da
+      update_vnc_encryption (settings);
e142da
+      g_signal_emit (settings, signals[VNC_ENCRYPTION_CHANGED], 0);
e142da
+    }
e142da
 }
e142da
 
e142da
 static void
e34316
@@ -172,6 +191,8 @@ grd_settings_init (GrdSettings *settings)
e142da
   update_vnc_auth_method (settings);
e34316
 
e34316
   settings->vnc.port = GRD_VNC_SERVER_PORT;
e34316
+
e142da
+  update_vnc_encryption (settings);
e142da
 }
e142da
 
e142da
 static void
e34316
@@ -195,4 +216,11 @@ grd_settings_class_init (GrdSettingsClass *klass)
e142da
                   0,
e142da
                   NULL, NULL, NULL,
e142da
                   G_TYPE_NONE, 0);
e142da
+  signals[VNC_ENCRYPTION_CHANGED] =
e142da
+    g_signal_new ("vnc-encryption-changed",
e142da
+                  G_TYPE_FROM_CLASS (klass),
e142da
+                  G_SIGNAL_RUN_LAST,
e142da
+                  0,
e142da
+                  NULL, NULL, NULL,
e142da
+                  G_TYPE_NONE, 0);
e142da
 }
e142da
diff --git a/src/grd-settings.h b/src/grd-settings.h
e34316
index e4e0c09..0575ec1 100644
e142da
--- a/src/grd-settings.h
e142da
+++ b/src/grd-settings.h
e34316
@@ -45,4 +45,6 @@ gboolean grd_settings_get_vnc_view_only (GrdSettings *settings);
e142da
 
e142da
 GrdVncAuthMethod grd_settings_get_vnc_auth_method (GrdSettings *settings);
e142da
 
e142da
+GrdVncEncryption grd_settings_get_vnc_encryption (GrdSettings *settings);
e142da
+
e142da
 #endif /* GRD_SETTINGS_H */
e142da
diff --git a/src/grd-vnc-server.c b/src/grd-vnc-server.c
e34316
index a6d95cb..f9c68db 100644
e142da
--- a/src/grd-vnc-server.c
e142da
+++ b/src/grd-vnc-server.c
e142da
@@ -24,11 +24,13 @@
e142da
 
e142da
 #include "grd-vnc-server.h"
e142da
 
e142da
+#include <rfb/rfb.h>
e142da
 #include <gio/gio.h>
e142da
 #include <rfb/rfb.h>
e142da
 
e142da
 #include "grd-context.h"
e142da
 #include "grd-session-vnc.h"
e142da
+#include "grd-vnc-tls.h"
e142da
 
e142da
 
e34316
 enum
e34316
@@ -130,6 +132,43 @@ on_incoming (GSocketService    *service,
e142da
   return TRUE;
e142da
 }
e142da
 
e142da
+static void
e142da
+sync_encryption_settings (GrdVncServer *vnc_server)
e142da
+{
e142da
+  GrdSettings *settings = grd_context_get_settings (vnc_server->context);
e142da
+  rfbSecurityHandler *tls_security_handler;
e142da
+  GrdVncEncryption encryption;
e142da
+
e142da
+  tls_security_handler = grd_vnc_tls_get_security_handler ();
e142da
+  encryption = grd_settings_get_vnc_encryption (settings);
e142da
+
e142da
+  if (encryption == (GRD_VNC_ENCRYPTION_NONE | GRD_VNC_ENCRYPTION_TLS_ANON))
e142da
+    {
e142da
+      rfbRegisterSecurityHandler (tls_security_handler);
e142da
+      rfbUnregisterChannelSecurityHandler (tls_security_handler);
e142da
+    }
e142da
+  else if (encryption == GRD_VNC_ENCRYPTION_NONE)
e142da
+    {
e142da
+      rfbUnregisterSecurityHandler (tls_security_handler);
e142da
+      rfbUnregisterChannelSecurityHandler (tls_security_handler);
e142da
+    }
e142da
+  else
e142da
+    {
e142da
+      if (encryption != GRD_VNC_ENCRYPTION_TLS_ANON)
e142da
+        g_warning ("Invalid VNC encryption setting, falling back to TLS-ANON");
e142da
+
e142da
+      rfbRegisterChannelSecurityHandler (tls_security_handler);
e142da
+      rfbUnregisterSecurityHandler (tls_security_handler);
e142da
+    }
e142da
+}
e142da
+
e142da
+static void
e142da
+on_vnc_encryption_changed (GrdSettings  *settings,
e142da
+                           GrdVncServer *vnc_server)
e142da
+{
e142da
+  sync_encryption_settings (vnc_server);
e142da
+}
e142da
+
e142da
 gboolean
e142da
 grd_vnc_server_start (GrdVncServer  *vnc_server,
e142da
                       GError       **error)
e34316
@@ -220,12 +259,18 @@ static void
e142da
 grd_vnc_server_constructed (GObject *object)
e142da
 {
e142da
   GrdVncServer *vnc_server = GRD_VNC_SERVER (object);
e142da
+  GrdSettings *settings = grd_context_get_settings (vnc_server->context);
e142da
 
e142da
   if (grd_context_get_debug_flags (vnc_server->context) & GRD_DEBUG_VNC)
e142da
     rfbLogEnable (1);
e142da
   else
e142da
     rfbLogEnable (0);
e142da
 
e142da
+  g_signal_connect (settings, "vnc-encryption-changed",
e142da
+                    G_CALLBACK (on_vnc_encryption_changed),
e142da
+                    vnc_server);
e142da
+  sync_encryption_settings (vnc_server);
e142da
+
e142da
   G_OBJECT_CLASS (grd_vnc_server_parent_class)->constructed (object);
e142da
 }
e142da
 
e142da
diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c
e142da
new file mode 100644
e34316
index 0000000..ec4758e
e142da
--- /dev/null
e142da
+++ b/src/grd-vnc-tls.c
e142da
@@ -0,0 +1,444 @@
e142da
+/*
e142da
+ * Copyright (C) 2018 Red Hat Inc.
e142da
+ *
e142da
+ * This program is free software; you can redistribute it and/or
e142da
+ * modify it under the terms of the GNU General Public License as
e142da
+ * published by the Free Software Foundation; either version 2 of the
e142da
+ * License, or (at your option) any later version.
e142da
+ *
e142da
+ * This program is distributed in the hope that it will be useful, but
e142da
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
e142da
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e142da
+ * General Public License for more details.
e142da
+ *
e142da
+ * You should have received a copy of the GNU General Public License
e142da
+ * along with this program; if not, write to the Free Software
e142da
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
e142da
+ * 02111-1307, USA.
e142da
+ *
e142da
+ */
e142da
+
e142da
+#include "grd-vnc-tls.h"
e142da
+
e142da
+#include <errno.h>
e142da
+#include <glib.h>
e142da
+#include <gnutls/gnutls.h>
e142da
+#include <rfb/rfb.h>
e142da
+
e142da
+#include "grd-session-vnc.h"
e142da
+#include "grd-vnc-server.h"
e142da
+
e142da
+typedef struct _GrdVncTlsContext
e142da
+{
e142da
+  gnutls_anon_server_credentials_t anon_credentials;
e142da
+  gnutls_dh_params_t dh_params;
e142da
+} GrdVncTlsContext;
e142da
+
e142da
+typedef enum _GrdTlsHandshakeState
e142da
+{
e142da
+  GRD_TLS_HANDSHAKE_STATE_INIT,
e142da
+  GRD_TLS_HANDSHAKE_STATE_DURING,
e142da
+  GRD_TLS_HANDSHAKE_STATE_FINISHED
e142da
+} GrdTlsHandshakeState;
e142da
+
e142da
+typedef struct _GrdVncTlsSession
e142da
+{
e142da
+  GrdVncTlsContext *tls_context;
e142da
+
e142da
+  int fd;
e142da
+
e142da
+  gnutls_session_t tls_session;
e142da
+  GrdTlsHandshakeState handshake_state;
e142da
+
e142da
+  char *peek_buffer;
e142da
+  int peek_buffer_size;
e142da
+  int peek_buffer_len;
e142da
+} GrdVncTlsSession;
e142da
+
e142da
+static gboolean
e142da
+tls_handshake_grab_func (GrdSessionVnc  *session_vnc,
e142da
+                         GError        **error);
e142da
+
e142da
+static GrdVncTlsContext *
e142da
+grd_vnc_tls_context_new (void)
e142da
+{
e142da
+  GrdVncTlsContext *tls_context;
e142da
+  const unsigned int dh_bits = 1024;
e142da
+
e142da
+  tls_context = g_new0 (GrdVncTlsContext, 1);
e142da
+
e142da
+  gnutls_global_init ();
e142da
+
e142da
+  gnutls_anon_allocate_server_credentials (&tls_context->anon_credentials);
e142da
+
e142da
+  gnutls_dh_params_init (&tls_context->dh_params);
e142da
+  gnutls_dh_params_generate2 (tls_context->dh_params, dh_bits);
e142da
+
e142da
+  gnutls_anon_set_server_dh_params (tls_context->anon_credentials,
e142da
+                                    tls_context->dh_params);
e142da
+
e142da
+  return tls_context;
e142da
+}
e142da
+
e142da
+static void
e142da
+grd_vnc_tls_context_free (GrdVncTlsContext *tls_context)
e142da
+{
e142da
+  gnutls_dh_params_deinit (tls_context->dh_params);
e142da
+  gnutls_anon_free_server_credentials (tls_context->anon_credentials);
e142da
+  gnutls_global_deinit ();
e142da
+}
e142da
+
e142da
+GrdVncTlsContext *
e142da
+ensure_tls_context (GrdVncServer *vnc_server)
e142da
+{
e142da
+  GrdVncTlsContext *tls_context;
e142da
+
e142da
+  tls_context = g_object_get_data (G_OBJECT (vnc_server), "vnc-tls-context");
e142da
+  if (!tls_context)
e142da
+    {
e142da
+      tls_context = grd_vnc_tls_context_new ();
e142da
+      g_object_set_data_full (G_OBJECT (vnc_server), "vnc-tls-context",
e142da
+                              tls_context,
e142da
+                              (GDestroyNotify) grd_vnc_tls_context_free);
e142da
+    }
e142da
+
e142da
+  return tls_context;
e142da
+}
e142da
+
e142da
+static gboolean
e142da
+perform_anon_tls_handshake (GrdVncTlsSession  *tls_session,
e142da
+                            GError           **error)
e142da
+{
e142da
+  GrdVncTlsContext *tls_context = tls_session->tls_context;
e142da
+  const char kx_priority[] = "NORMAL:+ANON-DH";
e142da
+  int ret;
e142da
+
e142da
+  gnutls_init (&tls_session->tls_session, GNUTLS_SERVER | GNUTLS_NO_SIGNAL);
e142da
+
e142da
+  gnutls_set_default_priority (tls_session->tls_session);
e142da
+  gnutls_priority_set_direct (tls_session->tls_session, kx_priority, NULL);
e142da
+
e142da
+  gnutls_credentials_set (tls_session->tls_session,
e142da
+                          GNUTLS_CRD_ANON,
e142da
+                          tls_context->anon_credentials);
e142da
+  gnutls_transport_set_ptr (tls_session->tls_session,
e142da
+                            GINT_TO_POINTER (tls_session->fd));
e142da
+
e142da
+  ret = gnutls_handshake (tls_session->tls_session);
e142da
+  if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret))
e142da
+    {
e142da
+      tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_DURING;
e142da
+      return TRUE;
e142da
+    }
e142da
+
e142da
+  if (ret != GNUTLS_E_SUCCESS)
e142da
+    {
e142da
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
e142da
+                   "%s", gnutls_strerror (ret));
e142da
+      gnutls_deinit (tls_session->tls_session);
e142da
+      tls_session->tls_session = NULL;
e142da
+      return FALSE;
e142da
+    }
e142da
+
e142da
+  tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED;
e142da
+  return TRUE;
e142da
+}
e142da
+
e142da
+static gboolean
e142da
+continue_tls_handshake (GrdVncTlsSession  *tls_session,
e142da
+                        GError           **error)
e142da
+{
e142da
+  int ret;
e142da
+
e142da
+  ret = gnutls_handshake (tls_session->tls_session);
e142da
+  if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret))
e142da
+    return TRUE;
e142da
+
e142da
+  if (ret != GNUTLS_E_SUCCESS)
e142da
+    {
e142da
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
e142da
+                   "%s", gnutls_strerror (ret));
e142da
+      gnutls_deinit (tls_session->tls_session);
e142da
+      tls_session->tls_session = NULL;
e142da
+      return FALSE;
e142da
+    }
e142da
+
e142da
+  tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED;
e142da
+  return TRUE;
e142da
+}
e142da
+
e142da
+static void
e142da
+grd_vnc_tls_session_free (GrdVncTlsSession *tls_session)
e142da
+{
e142da
+  g_clear_pointer (&tls_session->peek_buffer, g_free);
e34316
+  g_clear_pointer (&tls_session->tls_session, gnutls_deinit);
e142da
+  g_free (tls_session);
e142da
+}
e142da
+
e142da
+static GrdVncTlsSession *
e142da
+grd_vnc_tls_session_from_vnc_session (GrdSessionVnc *session_vnc)
e142da
+{
e142da
+  return g_object_get_data (G_OBJECT (session_vnc), "vnc-tls-session");
e142da
+}
e142da
+
e142da
+static int
e142da
+do_read (GrdVncTlsSession *tls_session,
e142da
+         char             *buf,
e142da
+         int               len)
e142da
+{
e142da
+  do
e142da
+    {
e142da
+      int ret;
e142da
+
e142da
+      ret = gnutls_record_recv (tls_session->tls_session, buf, len);
e142da
+      if (ret == GNUTLS_E_AGAIN ||
e142da
+          ret == GNUTLS_E_INTERRUPTED)
e142da
+        {
e142da
+          continue;
e142da
+        }
e142da
+      else if (ret < 0)
e142da
+        {
e142da
+          g_debug ("gnutls_record_recv failed: %s", gnutls_strerror (ret));
e142da
+          errno = EIO;
e142da
+          return -1;
e142da
+        }
e142da
+      else
e142da
+        {
e142da
+          return ret;
e142da
+        }
e142da
+    }
e142da
+  while (TRUE);
e142da
+}
e142da
+
e142da
+static int
e142da
+grd_vnc_tls_read_from_socket (rfbClientPtr  rfb_client,
e142da
+                              char         *buf,
e142da
+                              int           len)
e142da
+{
e142da
+  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e142da
+  GrdVncTlsSession *tls_session =
e142da
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e142da
+  int to_read = len;
e142da
+  int len_read = 0;
e142da
+
e142da
+  if (to_read < tls_session->peek_buffer_len)
e142da
+    {
e142da
+      memcpy (buf, tls_session->peek_buffer, to_read);
e142da
+      memmove (buf,
e142da
+               tls_session->peek_buffer + to_read,
e142da
+               tls_session->peek_buffer_len - to_read);
e142da
+      len_read = to_read;
e142da
+      to_read = 0;
e142da
+    }
e142da
+  else
e142da
+    {
e142da
+      memcpy (buf,
e142da
+              tls_session->peek_buffer,
e142da
+              tls_session->peek_buffer_len);
e142da
+      to_read -= tls_session->peek_buffer_len;
e142da
+      len_read = tls_session->peek_buffer_len;
e142da
+
e142da
+      g_clear_pointer (&tls_session->peek_buffer,
e142da
+                       g_free);
e142da
+      tls_session->peek_buffer_len = 0;
e142da
+      tls_session->peek_buffer_size = 0;
e142da
+    }
e142da
+
e142da
+  if (to_read > 0)
e142da
+    {
e142da
+      int ret;
e142da
+
e142da
+      ret = do_read (tls_session, buf + len_read, to_read);
e142da
+      if (ret == -1)
e142da
+        return -1;
e142da
+
e142da
+      len_read += ret;
e142da
+    }
e142da
+
e142da
+  return len_read;
e142da
+}
e142da
+
e142da
+static int
e142da
+grd_vnc_tls_peek_at_socket (rfbClientPtr  rfb_client,
e142da
+                            char         *buf,
e142da
+                            int           len)
e142da
+{
e142da
+  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e142da
+  GrdVncTlsSession *tls_session =
e142da
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e142da
+  int peekable_len;
e142da
+
e142da
+  if (tls_session->peek_buffer_len < len)
e142da
+    {
e142da
+      int ret;
e142da
+
e142da
+      if (len > tls_session->peek_buffer_size)
e142da
+        {
e142da
+          tls_session->peek_buffer = g_renew (char,
e142da
+                                              tls_session->peek_buffer,
e142da
+                                              len);
e142da
+          tls_session->peek_buffer_size = len;
e142da
+        }
e142da
+
e142da
+      ret = do_read (tls_session,
e142da
+                     tls_session->peek_buffer + tls_session->peek_buffer_len,
e142da
+                     len - tls_session->peek_buffer_len);
e142da
+      if (ret == -1)
e142da
+        return -1;
e142da
+
e142da
+      tls_session->peek_buffer_len += ret;
e142da
+    }
e142da
+
e142da
+  peekable_len = MIN (len, tls_session->peek_buffer_len);
e142da
+  memcpy (buf, tls_session->peek_buffer, peekable_len);
e142da
+
e142da
+  return peekable_len;
e142da
+}
e142da
+
e142da
+static rfbBool
e142da
+grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client)
e142da
+{
e142da
+  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e142da
+  GrdVncTlsSession *tls_session =
e142da
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e142da
+
e142da
+  if (tls_session->peek_buffer_len > 0)
e142da
+    return TRUE;
e142da
+
e142da
+  if (gnutls_record_check_pending (tls_session->tls_session) > 0)
e142da
+    return TRUE;
e142da
+
e142da
+  return FALSE;
e142da
+}
e142da
+
e142da
+static int
e142da
+grd_vnc_tls_write_to_socket (rfbClientPtr  rfb_client,
e142da
+                             const char   *buf,
e142da
+                             int           len)
e142da
+{
e142da
+  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e142da
+  GrdVncTlsSession *tls_session =
e142da
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e142da
+
e142da
+  do
e142da
+    {
e142da
+      int ret;
e142da
+
e142da
+      ret = gnutls_record_send (tls_session->tls_session, buf, len);
e142da
+      if (ret == GNUTLS_E_AGAIN ||
e142da
+          ret == GNUTLS_E_INTERRUPTED)
e142da
+        {
e142da
+          continue;
e142da
+        }
e142da
+      else if (ret < 0)
e142da
+        {
e142da
+          g_debug ("gnutls_record_send failed: %s", gnutls_strerror (ret));
e142da
+          errno = EIO;
e142da
+          return -1;
e142da
+        }
e142da
+      else
e142da
+        {
e142da
+          return ret;
e142da
+        }
e142da
+    }
e142da
+  while (TRUE);
e142da
+}
e142da
+
e142da
+static gboolean
e142da
+perform_handshake (GrdSessionVnc  *session_vnc,
e142da
+                   GError        **error)
e142da
+{
e142da
+  GrdVncTlsSession *tls_session =
e142da
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e142da
+
e142da
+  switch (tls_session->handshake_state)
e142da
+    {
e142da
+    case GRD_TLS_HANDSHAKE_STATE_INIT:
e142da
+      if (!perform_anon_tls_handshake (tls_session, error))
e142da
+        return FALSE;
e142da
+      break;
e142da
+    case GRD_TLS_HANDSHAKE_STATE_DURING:
e142da
+      if (!continue_tls_handshake (tls_session, error))
e142da
+        return FALSE;
e142da
+      break;
e142da
+    case GRD_TLS_HANDSHAKE_STATE_FINISHED:
e142da
+      break;
e142da
+    }
e142da
+
e142da
+  switch (tls_session->handshake_state)
e142da
+    {
e142da
+    case GRD_TLS_HANDSHAKE_STATE_INIT:
e142da
+      break;
e142da
+    case GRD_TLS_HANDSHAKE_STATE_DURING:
e142da
+      break;
e142da
+    case GRD_TLS_HANDSHAKE_STATE_FINISHED:
e142da
+      grd_session_vnc_ungrab_socket (session_vnc, tls_handshake_grab_func);
e142da
+      rfbSendSecurityTypeList (grd_session_vnc_get_rfb_client (session_vnc),
e142da
+                               RFB_SECURITY_TAG_CHANNEL);
e142da
+      break;
e142da
+    }
e142da
+
e142da
+  return TRUE;
e142da
+}
e142da
+
e142da
+static gboolean
e142da
+tls_handshake_grab_func (GrdSessionVnc  *session_vnc,
e142da
+                         GError        **error)
e142da
+{
e142da
+  g_autoptr (GError) handshake_error = NULL;
e142da
+
e142da
+  if (!perform_handshake (session_vnc, &handshake_error))
e142da
+    {
e142da
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
e142da
+                   "TLS handshake failed: %s", handshake_error->message);
e142da
+      return FALSE;
e142da
+    }
e142da
+
e142da
+  return TRUE;
e142da
+}
e142da
+
e142da
+static void
e142da
+rfb_tls_security_handler (rfbClientPtr rfb_client)
e142da
+{
e142da
+  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e142da
+  GrdVncTlsSession *tls_session;
e142da
+  g_autoptr(GError) error = NULL;
e142da
+
e142da
+  tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc);
e142da
+  if (!tls_session)
e142da
+    {
e142da
+      GrdVncServer *vnc_server = grd_session_vnc_get_vnc_server (session_vnc);
e142da
+
e142da
+      tls_session = g_new0 (GrdVncTlsSession, 1);
e142da
+      tls_session->fd = grd_session_vnc_get_fd (session_vnc);
e142da
+      tls_session->tls_context = ensure_tls_context (vnc_server);
e142da
+      g_object_set_data_full (G_OBJECT (session_vnc), "vnc-tls-session",
e142da
+                              tls_session,
e142da
+                              (GDestroyNotify) grd_vnc_tls_session_free);
e142da
+
e142da
+      rfb_client->readFromSocket = grd_vnc_tls_read_from_socket;
e142da
+      rfb_client->peekAtSocket = grd_vnc_tls_peek_at_socket;
e142da
+      rfb_client->hasPendingOnSocket = grd_vnc_tls_has_pending_on_socket;
e142da
+      rfb_client->writeToSocket = grd_vnc_tls_write_to_socket;
e142da
+
e142da
+      grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func);
e142da
+    }
e142da
+
e142da
+  if (!perform_handshake (session_vnc, &error))
e142da
+    {
e142da
+      g_warning ("TLS handshake failed: %s", error->message);
e142da
+      rfbCloseClient (rfb_client);
e142da
+    }
e142da
+}
e142da
+
e142da
+static rfbSecurityHandler anon_tls_security_handler = {
e142da
+  .type = rfbTLS,
e142da
+  .handler = rfb_tls_security_handler,
e142da
+  .securityTags = RFB_SECURITY_TAG_CHANNEL,
e142da
+};
e142da
+
e142da
+rfbSecurityHandler *
e142da
+grd_vnc_tls_get_security_handler (void)
e142da
+{
e142da
+  return &anon_tls_security_handler;
e142da
+}
e142da
diff --git a/src/grd-vnc-tls.h b/src/grd-vnc-tls.h
e142da
new file mode 100644
e142da
index 0000000..135ef8c
e142da
--- /dev/null
e142da
+++ b/src/grd-vnc-tls.h
e142da
@@ -0,0 +1,28 @@
e142da
+/*
e142da
+ * Copyright (C) 2018 Red Hat Inc.
e142da
+ *
e142da
+ * This program is free software; you can redistribute it and/or
e142da
+ * modify it under the terms of the GNU General Public License as
e142da
+ * published by the Free Software Foundation; either version 2 of the
e142da
+ * License, or (at your option) any later version.
e142da
+ *
e142da
+ * This program is distributed in the hope that it will be useful, but
e142da
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
e142da
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e142da
+ * General Public License for more details.
e142da
+ *
e142da
+ * You should have received a copy of the GNU General Public License
e142da
+ * along with this program; if not, write to the Free Software
e142da
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
e142da
+ * 02111-1307, USA.
e142da
+ *
e142da
+ */
e142da
+
e142da
+#ifndef GRD_VNC_TLS_H
e142da
+#define GRD_VNC_TLS_H
e142da
+
e142da
+#include <rfb/rfb.h>
e142da
+
e142da
+rfbSecurityHandler * grd_vnc_tls_get_security_handler (void);
e142da
+
e142da
+#endif /* GRD_VNC_TLS_H */
e142da
diff --git a/src/meson.build b/src/meson.build
e34316
index 0f76fab..9d2f1ce 100644
e142da
--- a/src/meson.build
e142da
+++ b/src/meson.build
e34316
@@ -21,6 +21,8 @@ daemon_sources = files([
e142da
   'grd-vnc-pipewire-stream.h',
e142da
   'grd-vnc-server.c',
e142da
   'grd-vnc-server.h',
e142da
+  'grd-vnc-tls.c',
e142da
+  'grd-vnc-tls.h',
e142da
 ])
e142da
 
e142da
 gen_daemon_sources = []
e34316
@@ -51,7 +53,8 @@ executable('gnome-remote-desktop-daemon',
e142da
                           pipewire_dep,
e142da
                           libvncserver_dep,
e142da
                           libsecret_dep,
e142da
-                          libnotify_dep],
e142da
+                          libnotify_dep,
e142da
+                          gnutls_dep],
e142da
            include_directories: [configinc],
e142da
            install: true,
e142da
            install_dir: libexecdir)
e34316
diff --git a/src/org.gnome.desktop.remote-desktop.gschema.xml.in b/src/org.gnome.desktop.remote-desktop.gschema.xml.in
e142da
index a5c2022..846e65b 100644
e34316
--- a/src/org.gnome.desktop.remote-desktop.gschema.xml.in
e34316
+++ b/src/org.gnome.desktop.remote-desktop.gschema.xml.in
e142da
@@ -23,5 +23,15 @@
e142da
 	 * password - by requiring the remote client to provide a known password
e142da
       </description>
e142da
     </key>
e142da
+    <key name='encryption' flags='org.gnome.desktop.remote-desktop.GrdVncEncryption'>
e142da
+      <default>['tls-anon']</default>
e142da
+      <summary>Allowed encryption method to use</summary>
e142da
+      <description>
e142da
+	Allowed encryption methods. Includes the following:
e142da
+
e142da
+	 * none     - no encryption
e142da
+	 * tls-anon - anonymous (unauthenticated) TLS
e142da
+      </description>
e142da
+    </key>
e142da
   </schema>
e142da
 </schemalist>
e142da
-- 
e34316
2.26.2
e34316
e34316
e34316
From aa54aeb43938250a4d27a99e62eb5628d3b55076 Mon Sep 17 00:00:00 2001
e34316
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e34316
Date: Wed, 27 Nov 2019 11:02:09 +0100
e34316
Subject: [PATCH 2/7] session-vnc: Add paused/resumed signals
e34316
e34316
Paused is when the socket sourec is detached, and resumed when attached.
e34316
Meant to be used by the TLS channel security to a attach/detach
e34316
out-of-socket source.
e34316
---
e34316
 src/grd-session-vnc.c | 72 ++++++++++++++++++++++++++++++++++++++-----
e34316
 1 file changed, 65 insertions(+), 7 deletions(-)
e34316
e34316
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
e34316
index 0cc2ea2..076e25f 100644
e34316
--- a/src/grd-session-vnc.c
e34316
+++ b/src/grd-session-vnc.c
e34316
@@ -40,14 +40,27 @@
e34316
 #define BGRX_SAMPLES_PER_PIXEL 3
e34316
 #define BGRX_BYTES_PER_PIXEL 4
e34316
 
e34316
+enum
e34316
+{
e34316
+  PAUSED,
e34316
+  RESUMED,
e34316
+
e34316
+  N_SIGNALS
e34316
+};
e34316
+
e34316
+static guint signals[N_SIGNALS];
e34316
+
e34316
 struct _GrdSessionVnc
e34316
 {
e34316
   GrdSession parent;
e34316
 
e34316
   GrdVncServer *vnc_server;
e34316
   GSocketConnection *connection;
e34316
+
e34316
   GList *socket_grabs;
e34316
   GSource *source;
e34316
+  gboolean is_paused;
e34316
+
e34316
   rfbScreenInfoPtr rfb_screen;
e34316
   rfbClientPtr rfb_client;
e34316
 
e34316
@@ -73,7 +86,7 @@ struct _GrdSessionVnc
e34316
 G_DEFINE_TYPE (GrdSessionVnc, grd_session_vnc, GRD_TYPE_SESSION);
e34316
 
e34316
 static void
e34316
-grd_session_vnc_detach_source (GrdSessionVnc *session_vnc);
e34316
+grd_session_vnc_pause (GrdSessionVnc *session_vnc);
e34316
 
e34316
 static gboolean
e34316
 close_session_idle (gpointer user_data);
e34316
@@ -212,7 +225,8 @@ handle_client_gone (rfbClientPtr rfb_client)
e34316
 
e34316
   g_debug ("VNC client gone");
e34316
 
e34316
-  grd_session_vnc_detach_source (session_vnc);
e34316
+  grd_session_vnc_pause (session_vnc);
e34316
+
e34316
   maybe_queue_close_session_idle (session_vnc);
e34316
 }
e34316
 
e34316
@@ -280,7 +294,7 @@ handle_new_client (rfbClientPtr rfb_client)
e34316
                               session_vnc->prompt_cancellable,
e34316
                               prompt_response_callback,
e34316
                               session_vnc);
e34316
-      grd_session_vnc_detach_source (session_vnc);
e34316
+      grd_session_vnc_pause (session_vnc);
e34316
       return RFB_CLIENT_ON_HOLD;
e34316
     case GRD_VNC_AUTH_METHOD_PASSWORD:
e34316
       session_vnc->rfb_screen->passwordCheck = check_rfb_password;
e34316
@@ -498,7 +512,7 @@ check_rfb_password (rfbClientPtr  rfb_client,
e34316
   if (memcmp (challenge_encrypted, response_encrypted, len) == 0)
e34316
     {
e34316
       grd_session_start (GRD_SESSION (session_vnc));
e34316
-      grd_session_vnc_detach_source (session_vnc);
e34316
+      grd_session_vnc_pause (session_vnc);
e34316
       return TRUE;
e34316
     }
e34316
   else
e34316
@@ -668,6 +682,36 @@ grd_session_vnc_detach_source (GrdSessionVnc *session_vnc)
e34316
   g_clear_pointer (&session_vnc->source, g_source_destroy);
e34316
 }
e34316
 
e34316
+gboolean
e34316
+grd_session_vnc_is_paused (GrdSessionVnc *session_vnc)
e34316
+{
e34316
+  return session_vnc->is_paused;
e34316
+}
e34316
+
e34316
+static void
e34316
+grd_session_vnc_pause (GrdSessionVnc *session_vnc)
e34316
+{
e34316
+  if (grd_session_vnc_is_paused (session_vnc))
e34316
+    return;
e34316
+
e34316
+  session_vnc->is_paused = TRUE;
e34316
+
e34316
+  grd_session_vnc_detach_source (session_vnc);
e34316
+  g_signal_emit (session_vnc, signals[PAUSED], 0);
e34316
+}
e34316
+
e34316
+static void
e34316
+grd_session_vnc_resume (GrdSessionVnc *session_vnc)
e34316
+{
e34316
+  if (!grd_session_vnc_is_paused (session_vnc))
e34316
+    return;
e34316
+
e34316
+  session_vnc->is_paused = FALSE;
e34316
+
e34316
+  grd_session_vnc_attach_source (session_vnc);
e34316
+  g_signal_emit (session_vnc, signals[RESUMED], 0);
e34316
+}
e34316
+
e34316
 GrdSessionVnc *
e34316
 grd_session_vnc_new (GrdVncServer      *vnc_server,
e34316
                      GSocketConnection *connection)
e34316
@@ -685,6 +729,7 @@ grd_session_vnc_new (GrdVncServer      *vnc_server,
e34316
 
e34316
   grd_session_vnc_grab_socket (session_vnc, vnc_socket_grab_func);
e34316
   grd_session_vnc_attach_source (session_vnc);
e34316
+  session_vnc->is_paused = FALSE;
e34316
 
e34316
   init_vnc_session (session_vnc);
e34316
 
e34316
@@ -714,7 +759,7 @@ grd_session_vnc_stop (GrdSession *session)
e34316
 
e34316
   g_clear_object (&session_vnc->pipewire_stream);
e34316
 
e34316
-  grd_session_vnc_detach_source (session_vnc);
e34316
+  grd_session_vnc_pause (session_vnc);
e34316
 
e34316
   g_clear_object (&session_vnc->connection);
e34316
   g_clear_pointer (&session_vnc->rfb_screen->frameBuffer, g_free);
e34316
@@ -770,8 +815,8 @@ grd_session_vnc_stream_ready (GrdSession *session,
e34316
                     G_CALLBACK (on_pipwire_stream_closed),
e34316
                     session_vnc);
e34316
 
e34316
-  if (!session_vnc->source)
e34316
-    grd_session_vnc_attach_source (session_vnc);
e34316
+  if (grd_session_vnc_is_paused (session_vnc))
e34316
+    grd_session_vnc_resume (session_vnc);
e34316
 }
e34316
 
e34316
 static void
e34316
@@ -790,4 +835,17 @@ grd_session_vnc_class_init (GrdSessionVncClass *klass)
e34316
 
e34316
   session_class->stop = grd_session_vnc_stop;
e34316
   session_class->stream_ready = grd_session_vnc_stream_ready;
e34316
+
e34316
+  signals[PAUSED] = g_signal_new ("paused",
e34316
+                                  G_TYPE_FROM_CLASS (klass),
e34316
+                                  G_SIGNAL_RUN_LAST,
e34316
+                                  0,
e34316
+                                  NULL, NULL, NULL,
e34316
+                                  G_TYPE_NONE, 0);
e34316
+  signals[RESUMED] = g_signal_new ("resumed",
e34316
+                                   G_TYPE_FROM_CLASS (klass),
e34316
+                                   G_SIGNAL_RUN_LAST,
e34316
+                                   0,
e34316
+                                   NULL, NULL, NULL,
e34316
+                                   G_TYPE_NONE, 0);
e34316
 }
e34316
-- 
e34316
2.26.2
e34316
e34316
e34316
From ed3d72cb8d08192831397903f0ba92f439751988 Mon Sep 17 00:00:00 2001
e34316
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e34316
Date: Wed, 27 Nov 2019 11:03:46 +0100
e34316
Subject: [PATCH 3/7] session-vnc: Add grd_session_vnc_dispatch() helper
e34316
e34316
To be used by the TLS channel security to dispatch when there is data
e34316
available that is not visible to the socket source.
e34316
---
e34316
 src/grd-session-vnc.c | 26 ++++++++++++++++----------
e34316
 src/grd-session-vnc.h |  2 ++
e34316
 2 files changed, 18 insertions(+), 10 deletions(-)
e34316
e34316
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
e34316
index 076e25f..8b8ce1b 100644
e34316
--- a/src/grd-session-vnc.c
e34316
+++ b/src/grd-session-vnc.c
e34316
@@ -622,6 +622,21 @@ vnc_socket_grab_func (GrdSessionVnc  *session_vnc,
e34316
   return TRUE;
e34316
 }
e34316
 
e34316
+void
e34316
+grd_session_vnc_dispatch (GrdSessionVnc *session_vnc)
e34316
+{
e34316
+  GrdVncSocketGrabFunc grab_func;
e34316
+  g_autoptr (GError) error = NULL;
e34316
+
e34316
+  grab_func = g_list_first (session_vnc->socket_grabs)->data;
e34316
+  if (!grab_func (session_vnc, &error))
e34316
+    {
e34316
+      g_warning ("Error when reading socket: %s", error->message);
e34316
+
e34316
+      grd_session_stop (GRD_SESSION (session_vnc));
e34316
+    }
e34316
+}
e34316
+
e34316
 static gboolean
e34316
 handle_socket_data (GSocket *socket,
e34316
                     GIOCondition condition,
e34316
@@ -638,16 +653,7 @@ handle_socket_data (GSocket *socket,
e34316
     }
e34316
   else if (condition & G_IO_IN)
e34316
     {
e34316
-      GrdVncSocketGrabFunc grab_func;
e34316
-      g_autoptr (GError) error = NULL;
e34316
-
e34316
-      grab_func = g_list_first (session_vnc->socket_grabs)->data;
e34316
-      if (!grab_func (session_vnc, &error))
e34316
-        {
e34316
-          g_warning ("Error when reading socket: %s", error->message);
e34316
-
e34316
-          grd_session_stop (session);
e34316
-        }
e34316
+      grd_session_vnc_dispatch (session_vnc);
e34316
     }
e34316
   else
e34316
     {
e34316
diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h
e34316
index 46a8579..910b00c 100644
e34316
--- a/src/grd-session-vnc.h
e34316
+++ b/src/grd-session-vnc.h
e34316
@@ -68,6 +68,8 @@ void grd_session_vnc_grab_socket (GrdSessionVnc        *session_vnc,
e34316
 void grd_session_vnc_ungrab_socket (GrdSessionVnc        *session_vnc,
e34316
                                     GrdVncSocketGrabFunc  grab_func);
e34316
 
e34316
+void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc);
e34316
+
e34316
 GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc);
e34316
 
e34316
 #endif /* GRD_SESSION_VNC_H */
e34316
-- 
e34316
2.26.2
e34316
e34316
e34316
From 44e6bec84a86064a7b3abbcbbcd07ebb525aca9f Mon Sep 17 00:00:00 2001
e34316
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e34316
Date: Wed, 27 Nov 2019 11:05:13 +0100
e34316
Subject: [PATCH 4/7] vnc/tls: Add some logging
e34316
e34316
Uses the log utility from libvncserver as it is related to the RFB
e34316
protocol rather than the session itself.
e34316
---
e34316
 src/grd-vnc-tls.c | 9 +++++++++
e34316
 1 file changed, 9 insertions(+)
e34316
e34316
diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c
e34316
index ec4758e..ac6c35f 100644
e34316
--- a/src/grd-vnc-tls.c
e34316
+++ b/src/grd-vnc-tls.c
e34316
@@ -67,6 +67,7 @@ grd_vnc_tls_context_new (void)
e34316
 
e34316
   tls_context = g_new0 (GrdVncTlsContext, 1);
e34316
 
e34316
+  rfbLog ("TLS: Initializing gnutls context\n");
e34316
   gnutls_global_init ();
e34316
 
e34316
   gnutls_anon_allocate_server_credentials (&tls_context->anon_credentials);
e34316
@@ -127,6 +128,7 @@ perform_anon_tls_handshake (GrdVncTlsSession  *tls_session,
e34316
   ret = gnutls_handshake (tls_session->tls_session);
e34316
   if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret))
e34316
     {
e34316
+      rfbLog ("TLS: More handshake pending\n");
e34316
       tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_DURING;
e34316
       return TRUE;
e34316
     }
e34316
@@ -140,6 +142,8 @@ perform_anon_tls_handshake (GrdVncTlsSession  *tls_session,
e34316
       return FALSE;
e34316
     }
e34316
 
e34316
+  rfbLog ("TLS: Handshake finished");
e34316
+
e34316
   tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED;
e34316
   return TRUE;
e34316
 }
e34316
@@ -373,6 +377,7 @@ perform_handshake (GrdSessionVnc  *session_vnc,
e34316
       break;
e34316
     case GRD_TLS_HANDSHAKE_STATE_FINISHED:
e34316
       grd_session_vnc_ungrab_socket (session_vnc, tls_handshake_grab_func);
e34316
+      rfbLog ("TLS: Sending post-channel security security list\n");
e34316
       rfbSendSecurityTypeList (grd_session_vnc_get_rfb_client (session_vnc),
e34316
                                RFB_SECURITY_TAG_CHANNEL);
e34316
       break;
e34316
@@ -387,6 +392,7 @@ tls_handshake_grab_func (GrdSessionVnc  *session_vnc,
e34316
 {
e34316
   g_autoptr (GError) handshake_error = NULL;
e34316
 
e34316
+  rfbLog ("TLS: Continuing handshake\n");
e34316
   if (!perform_handshake (session_vnc, &handshake_error))
e34316
     {
e34316
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
e34316
@@ -404,6 +410,8 @@ rfb_tls_security_handler (rfbClientPtr rfb_client)
e34316
   GrdVncTlsSession *tls_session;
e34316
   g_autoptr(GError) error = NULL;
e34316
 
e34316
+  rfbLog ("TLS: Setting up rfbClient for gnutls encrypted traffic\n");
e34316
+
e34316
   tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc);
e34316
   if (!tls_session)
e34316
     {
e34316
@@ -424,6 +432,7 @@ rfb_tls_security_handler (rfbClientPtr rfb_client)
e34316
       grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func);
e34316
     }
e34316
 
e34316
+  rfbLog ("TLS: Performing handshake\n");
e34316
   if (!perform_handshake (session_vnc, &error))
e34316
     {
e34316
       g_warning ("TLS handshake failed: %s", error->message);
e34316
-- 
e34316
2.26.2
e34316
e34316
e34316
From fc07db3b6fafec47e02ff81f0f893dcaf64ba988 Mon Sep 17 00:00:00 2001
e34316
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e34316
Date: Wed, 27 Nov 2019 11:07:40 +0100
e34316
Subject: [PATCH 5/7] vnc/tls: Dispatch also when data is pending outside of
e34316
 the socket
e34316
e34316
gnutls may have data available in its buffers, and we have our own peek
e34316
buffer temporarly storing data later to be processed. This would missed
e34316
by the socket source, as it wouldn't get any notification about it from
e34316
epoll(). Deal with this by adding a custom source that dispatches as
e34316
long as there is data to read in those buffers.
e34316
---
e34316
 src/grd-session-vnc.h |  2 +
e34316
 src/grd-vnc-tls.c     | 92 ++++++++++++++++++++++++++++++++++++++++---
e34316
 2 files changed, 88 insertions(+), 6 deletions(-)
e34316
e34316
diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h
e34316
index 910b00c..294860e 100644
e34316
--- a/src/grd-session-vnc.h
e34316
+++ b/src/grd-session-vnc.h
e34316
@@ -68,6 +68,8 @@ void grd_session_vnc_grab_socket (GrdSessionVnc        *session_vnc,
e34316
 void grd_session_vnc_ungrab_socket (GrdSessionVnc        *session_vnc,
e34316
                                     GrdVncSocketGrabFunc  grab_func);
e34316
 
e34316
+gboolean grd_session_vnc_is_paused (GrdSessionVnc *session_vnc);
e34316
+
e34316
 void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc);
e34316
 
e34316
 GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc);
e34316
diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c
e34316
index ac6c35f..8f65225 100644
e34316
--- a/src/grd-vnc-tls.c
e34316
+++ b/src/grd-vnc-tls.c
e34316
@@ -41,6 +41,12 @@ typedef enum _GrdTlsHandshakeState
e34316
   GRD_TLS_HANDSHAKE_STATE_FINISHED
e34316
 } GrdTlsHandshakeState;
e34316
 
e34316
+typedef struct _PeekBufferSource
e34316
+{
e34316
+  GSource parent;
e34316
+  GrdSessionVnc *session_vnc;
e34316
+} PeekBufferSource;
e34316
+
e34316
 typedef struct _GrdVncTlsSession
e34316
 {
e34316
   GrdVncTlsContext *tls_context;
e34316
@@ -53,6 +59,8 @@ typedef struct _GrdVncTlsSession
e34316
   char *peek_buffer;
e34316
   int peek_buffer_size;
e34316
   int peek_buffer_len;
e34316
+
e34316
+  GSource *peek_buffer_source;
e34316
 } GrdVncTlsSession;
e34316
 
e34316
 static gboolean
e34316
@@ -296,16 +304,14 @@ grd_vnc_tls_peek_at_socket (rfbClientPtr  rfb_client,
e34316
   peekable_len = MIN (len, tls_session->peek_buffer_len);
e34316
   memcpy (buf, tls_session->peek_buffer, peekable_len);
e34316
 
e34316
+  fprintf(stderr, ":::: %s:%d %s() - peeked %d bytes, can peek %d bytes\n", __FILE__, __LINE__, __func__,
e34316
+          peekable_len, tls_session->peek_buffer_len);
e34316
   return peekable_len;
e34316
 }
e34316
 
e34316
-static rfbBool
e34316
-grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client)
e34316
+static gboolean
e34316
+grd_vnc_tls_session_has_pending_data (GrdVncTlsSession *tls_session)
e34316
 {
e34316
-  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e34316
-  GrdVncTlsSession *tls_session =
e34316
-    grd_vnc_tls_session_from_vnc_session (session_vnc);
e34316
-
e34316
   if (tls_session->peek_buffer_len > 0)
e34316
     return TRUE;
e34316
 
e34316
@@ -315,6 +321,16 @@ grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client)
e34316
   return FALSE;
e34316
 }
e34316
 
e34316
+static rfbBool
e34316
+grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client)
e34316
+{
e34316
+  GrdSessionVnc *session_vnc = rfb_client->screen->screenData;
e34316
+  GrdVncTlsSession *tls_session =
e34316
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e34316
+
e34316
+  return grd_vnc_tls_session_has_pending_data (tls_session);
e34316
+}
e34316
+
e34316
 static int
e34316
 grd_vnc_tls_write_to_socket (rfbClientPtr  rfb_client,
e34316
                              const char   *buf,
e34316
@@ -403,6 +419,62 @@ tls_handshake_grab_func (GrdSessionVnc  *session_vnc,
e34316
   return TRUE;
e34316
 }
e34316
 
e34316
+static gboolean
e34316
+peek_buffer_source_prepare (GSource *source,
e34316
+                            int     *timeout)
e34316
+{
e34316
+  PeekBufferSource *psource = (PeekBufferSource *) source;
e34316
+  GrdSessionVnc *session_vnc = psource->session_vnc;
e34316
+  GrdVncTlsSession *tls_session =
e34316
+    grd_vnc_tls_session_from_vnc_session (session_vnc);
e34316
+
e34316
+  return grd_vnc_tls_session_has_pending_data (tls_session);
e34316
+}
e34316
+
e34316
+static gboolean
e34316
+peek_buffer_source_dispatch (GSource     *source,
e34316
+                             GSourceFunc  callback,
e34316
+                             gpointer     user_data)
e34316
+{
e34316
+  PeekBufferSource *psource = (PeekBufferSource *) source;
e34316
+  GrdSessionVnc *session_vnc = psource->session_vnc;
e34316
+
e34316
+  grd_session_vnc_dispatch (session_vnc);
e34316
+
e34316
+  return G_SOURCE_CONTINUE;
e34316
+}
e34316
+
e34316
+static GSourceFuncs peek_buffer_source_funcs = {
e34316
+  .prepare = peek_buffer_source_prepare,
e34316
+  .dispatch = peek_buffer_source_dispatch,
e34316
+};
e34316
+
e34316
+static void
e34316
+attach_peek_buffer_source (GrdSessionVnc *session_vnc)
e34316
+{
e34316
+  GrdVncTlsSession *tls_session;
e34316
+
e34316
+  tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc);
e34316
+  tls_session->peek_buffer_source = g_source_new (&peek_buffer_source_funcs,
e34316
+                                                  sizeof (PeekBufferSource));
e34316
+  ((PeekBufferSource *) tls_session->peek_buffer_source)->session_vnc =
e34316
+    session_vnc;
e34316
+  g_source_set_priority (tls_session->peek_buffer_source,
e34316
+                         G_PRIORITY_DEFAULT + 1);
e34316
+
e34316
+  g_source_attach (tls_session->peek_buffer_source, NULL);
e34316
+}
e34316
+
e34316
+static void
e34316
+detach_peek_buffer_source (GrdSessionVnc *session_vnc)
e34316
+{
e34316
+  GrdVncTlsSession *tls_session;
e34316
+
e34316
+  tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc);
e34316
+
e34316
+  g_clear_pointer (&tls_session->peek_buffer_source, g_source_destroy);
e34316
+}
e34316
+
e34316
 static void
e34316
 rfb_tls_security_handler (rfbClientPtr rfb_client)
e34316
 {
e34316
@@ -429,6 +501,14 @@ rfb_tls_security_handler (rfbClientPtr rfb_client)
e34316
       rfb_client->hasPendingOnSocket = grd_vnc_tls_has_pending_on_socket;
e34316
       rfb_client->writeToSocket = grd_vnc_tls_write_to_socket;
e34316
 
e34316
+      if (!grd_session_vnc_is_paused (session_vnc))
e34316
+        attach_peek_buffer_source (session_vnc);
e34316
+
e34316
+      g_signal_connect (session_vnc, "paused",
e34316
+                        G_CALLBACK (detach_peek_buffer_source), NULL);
e34316
+      g_signal_connect (session_vnc, "resumed",
e34316
+                        G_CALLBACK (attach_peek_buffer_source), NULL);
e34316
+
e34316
       grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func);
e34316
     }
e34316
 
e34316
-- 
e34316
2.26.2
e34316
e34316
e34316
From c582baab12c1e2dd2b512329da42880c40993df6 Mon Sep 17 00:00:00 2001
e34316
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e34316
Date: Wed, 27 Nov 2019 16:48:00 +0100
e34316
Subject: [PATCH 6/7] session-vnc: Set our own password handling function up
e34316
 front
e34316
e34316
libvncserver decides whether to register a auth security handler
e34316
depending on whether the password data is set or not. When we use the
e34316
prompt auth method, we don't want to ask for password, so set the
e34316
password data to NULL.
e34316
e34316
Also, to be a bit more in control of the password mechanism, always set
e34316
the password function up front, instead of just when the client uses the
e34316
password prompt.
e34316
---
e34316
 src/grd-session-vnc.c | 8 ++------
e34316
 1 file changed, 2 insertions(+), 6 deletions(-)
e34316
e34316
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
e34316
index 8b8ce1b..a93a2e3 100644
e34316
--- a/src/grd-session-vnc.c
e34316
+++ b/src/grd-session-vnc.c
e34316
@@ -91,11 +91,6 @@ grd_session_vnc_pause (GrdSessionVnc *session_vnc);
e34316
 static gboolean
e34316
 close_session_idle (gpointer user_data);
e34316
 
e34316
-static rfbBool
e34316
-check_rfb_password (rfbClientPtr  rfb_client,
e34316
-                    const char   *response_encrypted,
e34316
-                    int           len);
e34316
-
e34316
 static void
e34316
 swap_uint8 (uint8_t *a,
e34316
             uint8_t *b)
e34316
@@ -297,7 +292,6 @@ handle_new_client (rfbClientPtr rfb_client)
e34316
       grd_session_vnc_pause (session_vnc);
e34316
       return RFB_CLIENT_ON_HOLD;
e34316
     case GRD_VNC_AUTH_METHOD_PASSWORD:
e34316
-      session_vnc->rfb_screen->passwordCheck = check_rfb_password;
e34316
       /*
e34316
        * authPasswdData needs to be non NULL in libvncserver to trigger
e34316
        * password authentication.
e34316
@@ -581,6 +575,8 @@ init_vnc_session (GrdSessionVnc *session_vnc)
e34316
   rfb_screen->frameBuffer = g_malloc0 (screen_width * screen_height * 4);
e34316
   memset (rfb_screen->frameBuffer, 0x1f, screen_width * screen_height * 4);
e34316
 
e34316
+  rfb_screen->passwordCheck = check_rfb_password;
e34316
+
e34316
   rfbInitServer (rfb_screen);
e34316
   rfbProcessEvents (rfb_screen, 0);
e34316
 }
e34316
-- 
e34316
2.26.2
e34316
e34316
e34316
From b7fc232ee5272b430f28c33ebaacd501ff63a4dc Mon Sep 17 00:00:00 2001
e34316
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
e34316
Date: Wed, 27 Nov 2019 16:53:42 +0100
e34316
Subject: [PATCH 7/7] vnc: Unregister previously set security handlers on init
e34316
e34316
When we're starting a session, we're going to handle a new client
e34316
connection. However, any previous client that was ever run on in a
e34316
previous session would still have their "security handler" registered,
e34316
as such is a global permanent change in libvncserver right now.
e34316
e34316
To work around this, unregister all primary security handler (i.e.
e34316
'none' and 'password') when initializing the RFB screen. We'll set up
e34316
the preferred one when handling the new client.
e34316
---
e34316
 src/grd-session-vnc.c | 6 ++++++
e34316
 1 file changed, 6 insertions(+)
e34316
e34316
diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c
e34316
index a93a2e3..9fcbb69 100644
e34316
--- a/src/grd-session-vnc.c
e34316
+++ b/src/grd-session-vnc.c
e34316
@@ -555,6 +555,12 @@ init_vnc_session (GrdSessionVnc *session_vnc)
e34316
                              8, 3, 4);
e34316
   session_vnc->rfb_screen = rfb_screen;
e34316
 
e34316
+  /*
e34316
+   * Unregister whatever security handler was used the last time; we'll set
e34316
+   * up new ones when authorizing the new client anyway.
e34316
+   */
e34316
+  rfbUnregisterPrimarySecurityHandlers ();
e34316
+
e34316
   update_server_format (session_vnc);
e34316
 
e34316
   socket = g_socket_connection_get_socket (session_vnc->connection);
e34316
-- 
e34316
2.26.2
e142da