From 08d9c7e41882e5e4821de2c9bc2035043f2ca1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Nov 2019 11:02:09 +0100 Subject: [PATCH 1/4] session-vnc: Add paused/resumed signals Paused is when the socket sourec is detached, and resumed when attached. Meant to be used by the TLS channel security to a attach/detach out-of-socket source. --- src/grd-session-vnc.c | 72 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c index dcd8599..6be8330 100644 --- a/src/grd-session-vnc.c +++ b/src/grd-session-vnc.c @@ -40,14 +40,27 @@ #define BGRX_SAMPLES_PER_PIXEL 3 #define BGRX_BYTES_PER_PIXEL 4 +enum +{ + PAUSED, + RESUMED, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + struct _GrdSessionVnc { GrdSession parent; GrdVncServer *vnc_server; GSocketConnection *connection; + GList *socket_grabs; GSource *source; + gboolean is_paused; + rfbScreenInfoPtr rfb_screen; rfbClientPtr rfb_client; @@ -73,7 +86,7 @@ struct _GrdSessionVnc G_DEFINE_TYPE (GrdSessionVnc, grd_session_vnc, GRD_TYPE_SESSION); static void -grd_session_vnc_detach_source (GrdSessionVnc *session_vnc); +grd_session_vnc_pause (GrdSessionVnc *session_vnc); static gboolean close_session_idle (gpointer user_data); @@ -215,7 +228,8 @@ handle_client_gone (rfbClientPtr rfb_client) g_debug ("VNC client gone"); - grd_session_vnc_detach_source (session_vnc); + grd_session_vnc_pause (session_vnc); + maybe_queue_close_session_idle (session_vnc); } @@ -283,7 +297,7 @@ handle_new_client (rfbClientPtr rfb_client) session_vnc->prompt_cancellable, prompt_response_callback, session_vnc); - grd_session_vnc_detach_source (session_vnc); + grd_session_vnc_pause (session_vnc); return RFB_CLIENT_ON_HOLD; case GRD_VNC_AUTH_METHOD_PASSWORD: session_vnc->rfb_screen->passwordCheck = check_rfb_password; @@ -501,7 +515,7 @@ check_rfb_password (rfbClientPtr rfb_client, if (memcmp (challenge_encrypted, response_encrypted, len) == 0) { grd_session_start (GRD_SESSION (session_vnc)); - grd_session_vnc_detach_source (session_vnc); + grd_session_vnc_pause (session_vnc); return TRUE; } else @@ -670,6 +684,36 @@ grd_session_vnc_detach_source (GrdSessionVnc *session_vnc) g_clear_pointer (&session_vnc->source, g_source_destroy); } +gboolean +grd_session_vnc_is_paused (GrdSessionVnc *session_vnc) +{ + return session_vnc->is_paused; +} + +static void +grd_session_vnc_pause (GrdSessionVnc *session_vnc) +{ + if (grd_session_vnc_is_paused (session_vnc)) + return; + + session_vnc->is_paused = TRUE; + + grd_session_vnc_detach_source (session_vnc); + g_signal_emit (session_vnc, signals[PAUSED], 0); +} + +static void +grd_session_vnc_resume (GrdSessionVnc *session_vnc) +{ + if (!grd_session_vnc_is_paused (session_vnc)) + return; + + session_vnc->is_paused = FALSE; + + grd_session_vnc_attach_source (session_vnc); + g_signal_emit (session_vnc, signals[RESUMED], 0); +} + GrdSessionVnc * grd_session_vnc_new (GrdVncServer *vnc_server, GSocketConnection *connection) @@ -687,6 +731,7 @@ grd_session_vnc_new (GrdVncServer *vnc_server, grd_session_vnc_grab_socket (session_vnc, vnc_socket_grab_func); grd_session_vnc_attach_source (session_vnc); + session_vnc->is_paused = FALSE; init_vnc_session (session_vnc); @@ -716,7 +761,7 @@ grd_session_vnc_stop (GrdSession *session) g_clear_object (&session_vnc->pipewire_stream); - grd_session_vnc_detach_source (session_vnc); + grd_session_vnc_pause (session_vnc); g_clear_object (&session_vnc->connection); g_clear_pointer (&session_vnc->rfb_screen->frameBuffer, g_free); @@ -772,8 +817,8 @@ grd_session_vnc_stream_ready (GrdSession *session, G_CALLBACK (on_pipwire_stream_closed), session_vnc); - if (!session_vnc->source) - grd_session_vnc_attach_source (session_vnc); + if (grd_session_vnc_is_paused (session_vnc)) + grd_session_vnc_resume (session_vnc); } static void @@ -792,4 +837,17 @@ grd_session_vnc_class_init (GrdSessionVncClass *klass) session_class->stop = grd_session_vnc_stop; session_class->stream_ready = grd_session_vnc_stream_ready; + + signals[PAUSED] = g_signal_new ("paused", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + signals[RESUMED] = g_signal_new ("resumed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } -- 2.23.0 From b27fe979adf4910f4173370091a06b6a945f83ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Nov 2019 11:03:46 +0100 Subject: [PATCH 2/4] session-vnc: Add grd_session_vnc_dispatch() helper To be used by the TLS channel security to dispatch when there is data available that is not visible to the socket source. --- src/grd-session-vnc.c | 26 ++++++++++++++++---------- src/grd-session-vnc.h | 2 ++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/grd-session-vnc.c b/src/grd-session-vnc.c index 6be8330..c5f83d8 100644 --- a/src/grd-session-vnc.c +++ b/src/grd-session-vnc.c @@ -624,6 +624,21 @@ vnc_socket_grab_func (GrdSessionVnc *session_vnc, return TRUE; } +void +grd_session_vnc_dispatch (GrdSessionVnc *session_vnc) +{ + GrdVncSocketGrabFunc grab_func; + g_autoptr (GError) error = NULL; + + grab_func = g_list_first (session_vnc->socket_grabs)->data; + if (!grab_func (session_vnc, &error)) + { + g_warning ("Error when reading socket: %s", error->message); + + grd_session_stop (GRD_SESSION (session_vnc)); + } +} + static gboolean handle_socket_data (GSocket *socket, GIOCondition condition, @@ -640,16 +655,7 @@ handle_socket_data (GSocket *socket, } else if (condition & G_IO_IN) { - GrdVncSocketGrabFunc grab_func; - g_autoptr (GError) error = NULL; - - grab_func = g_list_first (session_vnc->socket_grabs)->data; - if (!grab_func (session_vnc, &error)) - { - g_warning ("Error when reading socket: %s", error->message); - - grd_session_stop (session); - } + grd_session_vnc_dispatch (session_vnc); } else { diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h index 789693e..e699d64 100644 --- a/src/grd-session-vnc.h +++ b/src/grd-session-vnc.h @@ -68,6 +68,8 @@ void grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc, void grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc, GrdVncSocketGrabFunc grab_func); +void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc); + GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc); #endif /* GRD_SESSION_VNC_H */ -- 2.23.0 From bb4e67869e9226c7e10907d59fee3247b3a7fa8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Nov 2019 11:05:13 +0100 Subject: [PATCH 3/4] vnc/tls: Add some logging Uses the log utility from libvncserver as it is related to the RFB protocol rather than the session itself. --- src/grd-vnc-tls.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c index 8fc0fc2..fb24b34 100644 --- a/src/grd-vnc-tls.c +++ b/src/grd-vnc-tls.c @@ -67,6 +67,7 @@ grd_vnc_tls_context_new (void) tls_context = g_new0 (GrdVncTlsContext, 1); + rfbLog ("TLS: Initializing gnutls context\n"); gnutls_global_init (); gnutls_anon_allocate_server_credentials (&tls_context->anon_credentials); @@ -127,6 +128,7 @@ perform_anon_tls_handshake (GrdVncTlsSession *tls_session, ret = gnutls_handshake (tls_session->tls_session); if (ret != GNUTLS_E_SUCCESS && !gnutls_error_is_fatal (ret)) { + rfbLog ("TLS: More handshake pending\n"); tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_DURING; return TRUE; } @@ -140,6 +142,8 @@ perform_anon_tls_handshake (GrdVncTlsSession *tls_session, return FALSE; } + rfbLog ("TLS: Handshake finished"); + tls_session->handshake_state = GRD_TLS_HANDSHAKE_STATE_FINISHED; return TRUE; } @@ -373,6 +377,7 @@ perform_handshake (GrdSessionVnc *session_vnc, break; case GRD_TLS_HANDSHAKE_STATE_FINISHED: grd_session_vnc_ungrab_socket (session_vnc, tls_handshake_grab_func); + rfbLog ("TLS: Sending post-channel security security list\n"); rfbSendSecurityTypeList (grd_session_vnc_get_rfb_client (session_vnc), RFB_SECURITY_TAG_CHANNEL); break; @@ -387,6 +392,7 @@ tls_handshake_grab_func (GrdSessionVnc *session_vnc, { g_autoptr (GError) handshake_error = NULL; + rfbLog ("TLS: Continuing handshake\n"); if (!perform_handshake (session_vnc, &handshake_error)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -404,6 +410,8 @@ rfb_tls_security_handler (rfbClientPtr rfb_client) GrdVncTlsSession *tls_session; g_autoptr(GError) error = NULL; + rfbLog ("TLS: Setting up rfbClient for gnutls encrypted traffic\n"); + tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc); if (!tls_session) { @@ -424,6 +432,7 @@ rfb_tls_security_handler (rfbClientPtr rfb_client) grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func); } + rfbLog ("TLS: Performing handshake\n"); if (!perform_handshake (session_vnc, &error)) { g_warning ("TLS handshake failed: %s", error->message); -- 2.23.0 From 707425d19861295bb64e5558d1f81175d0327429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Nov 2019 11:07:40 +0100 Subject: [PATCH 4/4] vnc/tls: Dispatch also when data is pending outside of the socket gnutls may have data available in its buffers, and we have our own peek buffer temporarly storing data later to be processed. This would missed by the socket source, as it wouldn't get any notification about it from epoll(). Deal with this by adding a custom source that dispatches as long as there is data to read in those buffers. --- src/grd-session-vnc.h | 2 + src/grd-vnc-tls.c | 92 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/grd-session-vnc.h b/src/grd-session-vnc.h index e699d64..5a74b5f 100644 --- a/src/grd-session-vnc.h +++ b/src/grd-session-vnc.h @@ -68,6 +68,8 @@ void grd_session_vnc_grab_socket (GrdSessionVnc *session_vnc, void grd_session_vnc_ungrab_socket (GrdSessionVnc *session_vnc, GrdVncSocketGrabFunc grab_func); +gboolean grd_session_vnc_is_paused (GrdSessionVnc *session_vnc); + void grd_session_vnc_dispatch (GrdSessionVnc *session_vnc); GrdVncServer * grd_session_vnc_get_vnc_server (GrdSessionVnc *session_vnc); diff --git a/src/grd-vnc-tls.c b/src/grd-vnc-tls.c index fb24b34..815b40b 100644 --- a/src/grd-vnc-tls.c +++ b/src/grd-vnc-tls.c @@ -41,6 +41,12 @@ typedef enum _GrdTlsHandshakeState GRD_TLS_HANDSHAKE_STATE_FINISHED } GrdTlsHandshakeState; +typedef struct _PeekBufferSource +{ + GSource parent; + GrdSessionVnc *session_vnc; +} PeekBufferSource; + typedef struct _GrdVncTlsSession { GrdVncTlsContext *tls_context; @@ -53,6 +59,8 @@ typedef struct _GrdVncTlsSession char *peek_buffer; int peek_buffer_size; int peek_buffer_len; + + GSource *peek_buffer_source; } GrdVncTlsSession; static gboolean @@ -296,16 +304,14 @@ grd_vnc_tls_peek_at_socket (rfbClientPtr rfb_client, peekable_len = MIN (len, tls_session->peek_buffer_len); memcpy (buf, tls_session->peek_buffer, peekable_len); + fprintf(stderr, ":::: %s:%d %s() - peeked %d bytes, can peek %d bytes\n", __FILE__, __LINE__, __func__, + peekable_len, tls_session->peek_buffer_len); return peekable_len; } -static rfbBool -grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client) +static gboolean +grd_vnc_tls_session_has_pending_data (GrdVncTlsSession *tls_session) { - GrdSessionVnc *session_vnc = rfb_client->screen->screenData; - GrdVncTlsSession *tls_session = - grd_vnc_tls_session_from_vnc_session (session_vnc); - if (tls_session->peek_buffer_len > 0) return TRUE; @@ -315,6 +321,16 @@ grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client) return FALSE; } +static rfbBool +grd_vnc_tls_has_pending_on_socket (rfbClientPtr rfb_client) +{ + GrdSessionVnc *session_vnc = rfb_client->screen->screenData; + GrdVncTlsSession *tls_session = + grd_vnc_tls_session_from_vnc_session (session_vnc); + + return grd_vnc_tls_session_has_pending_data (tls_session); +} + static int grd_vnc_tls_write_to_socket (rfbClientPtr rfb_client, const char *buf, @@ -403,6 +419,62 @@ tls_handshake_grab_func (GrdSessionVnc *session_vnc, return TRUE; } +static gboolean +peek_buffer_source_prepare (GSource *source, + int *timeout) +{ + PeekBufferSource *psource = (PeekBufferSource *) source; + GrdSessionVnc *session_vnc = psource->session_vnc; + GrdVncTlsSession *tls_session = + grd_vnc_tls_session_from_vnc_session (session_vnc); + + return grd_vnc_tls_session_has_pending_data (tls_session); +} + +static gboolean +peek_buffer_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + PeekBufferSource *psource = (PeekBufferSource *) source; + GrdSessionVnc *session_vnc = psource->session_vnc; + + grd_session_vnc_dispatch (session_vnc); + + return G_SOURCE_CONTINUE; +} + +static GSourceFuncs peek_buffer_source_funcs = { + .prepare = peek_buffer_source_prepare, + .dispatch = peek_buffer_source_dispatch, +}; + +static void +attach_peek_buffer_source (GrdSessionVnc *session_vnc) +{ + GrdVncTlsSession *tls_session; + + tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc); + tls_session->peek_buffer_source = g_source_new (&peek_buffer_source_funcs, + sizeof (PeekBufferSource)); + ((PeekBufferSource *) tls_session->peek_buffer_source)->session_vnc = + session_vnc; + g_source_set_priority (tls_session->peek_buffer_source, + G_PRIORITY_DEFAULT + 1); + + g_source_attach (tls_session->peek_buffer_source, NULL); +} + +static void +detach_peek_buffer_source (GrdSessionVnc *session_vnc) +{ + GrdVncTlsSession *tls_session; + + tls_session = grd_vnc_tls_session_from_vnc_session (session_vnc); + + g_clear_pointer (&tls_session->peek_buffer_source, g_source_destroy); +} + static void rfb_tls_security_handler (rfbClientPtr rfb_client) { @@ -429,6 +501,14 @@ rfb_tls_security_handler (rfbClientPtr rfb_client) rfb_client->hasPendingOnSocket = grd_vnc_tls_has_pending_on_socket; rfb_client->writeToSocket = grd_vnc_tls_write_to_socket; + if (!grd_session_vnc_is_paused (session_vnc)) + attach_peek_buffer_source (session_vnc); + + g_signal_connect (session_vnc, "paused", + G_CALLBACK (detach_peek_buffer_source), NULL); + g_signal_connect (session_vnc, "resumed", + G_CALLBACK (attach_peek_buffer_source), NULL); + grd_session_vnc_grab_socket (session_vnc, tls_handshake_grab_func); } -- 2.23.0