gnome-remote-desktop/SOURCES/0001-vnc-Add-anonymous-TLS-encryption-support.patch

954 lines
28 KiB
Diff
Raw Normal View History

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