From fcfef86768d3dc63a2e7da799beb011800dff2ad 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] 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 af423a4..813c97f 100644 --- a/meson.build +++ b/meson.build @@ -20,6 +20,7 @@ libvncclient_dep = dependency('libvncclient') libsecret_dep = dependency('libsecret-1') libnotify_dep = dependency('libnotify') winpr_dep = dependency('winpr2', version: freerdp_req) +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 813838a..4bfb46e 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; @@ -511,12 +513,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) { @@ -557,33 +577,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; @@ -596,7 +657,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, @@ -622,8 +686,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); @@ -638,6 +704,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 579a12a..912dfef 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); @@ -55,6 +58,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 3af87be..1c7dc4b 100644 --- a/src/grd-settings.c +++ b/src/grd-settings.c @@ -59,6 +59,7 @@ struct _GrdSettings GSettings *settings; gboolean view_only; GrdVncAuthMethod auth_method; + GrdVncEncryption encryption; int port; } vnc; }; @@ -253,6 +254,12 @@ update_rdp_view_only (GrdSettings *settings) "view-only"); } +GrdVncEncryption +grd_settings_get_vnc_encryption (GrdSettings *settings) +{ + return settings->vnc.encryption; +} + static void update_vnc_view_only (GrdSettings *settings) { @@ -289,6 +296,13 @@ on_rdp_settings_changed (GSettings *rdp_settings, } } +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, @@ -304,6 +318,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 @@ -332,6 +351,7 @@ grd_settings_init (GrdSettings *settings) update_rdp_view_only (settings); update_vnc_view_only (settings); update_vnc_auth_method (settings); + update_vnc_encryption (settings); settings->rdp.port = GRD_RDP_SERVER_PORT; settings->vnc.port = GRD_VNC_SERVER_PORT; @@ -379,4 +399,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 e12e47e..b940fdb 100644 --- a/src/grd-settings.h +++ b/src/grd-settings.h @@ -64,4 +64,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..98b23c9 100644 --- a/src/grd-vnc-server.c +++ b/src/grd-vnc-server.c @@ -29,6 +29,7 @@ #include "grd-context.h" #include "grd-session-vnc.h" +#include "grd-vnc-tls.h" enum @@ -130,6 +131,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 +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..a77b91f --- /dev/null +++ b/src/grd-vnc-tls.c @@ -0,0 +1,445 @@ +/* + * 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, (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 + +rfbSecurityHandler * grd_vnc_tls_get_security_handler (void); + +#endif /* GRD_VNC_TLS_H */ diff --git a/src/meson.build b/src/meson.build index 1b6425d..2b8aeea 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,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 = [] @@ -65,7 +67,8 @@ executable('gnome-remote-desktop-daemon', pipewire_dep, libvncserver_dep, libsecret_dep, - libnotify_dep, + libnotify_dep, + gnutls_dep, winpr_dep], include_directories: [configinc], install: true, -- 2.17.1