[Spice-devel] RFC [spice-gtk] session: Allow to delay sending clipboard to the guest
Marc-André Lureau
marcandre.lureau at redhat.com
Tue Jan 9 14:46:48 UTC 2018
Hi
----- Original Message -----
> This is used to prevent unfocused guests from sniffing the clipboard
> content without the host or other guests noticing. This can be a
> security issue if any VM can track the clipboard activity in the
> session.
> This commit sets a boolean in SpiceGtkSession on focus in/out events.
> The client -> guest sending of clipboard data is then delayed until the
> window is focused again. This behaviour matches the behaviour we get on
> Wayland.
>
> This mostly solves https://bugzilla.redhat.com/show_bug.cgi?id=1320263
As Hans corrected in the bug, the data isn't actually transferred until the guest actually requested it.
Now, a malicious guest could try to get the clipboard content in a loop, even without previous notification of clipboard content.
However, isn't this true for any application running in the client desktop? What makes Spice guest different here? And by that I mean that the problem shouldn't probably be solved at the spice/spice-gtk level.
I am not that familiar with Wayland clipboard behaviour, could you explained what changed? That could help me to understand this patch better.
thanks
>
> Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
> ---
> Not overly happy with the added complexity, maybe someone will have
> suggestions for a better approach ;)
>
>
> src/spice-gtk-session-priv.h | 1 +
> src/spice-gtk-session.c | 97
> +++++++++++++++++++++++++++++++++++++++++---
> src/spice-widget.c | 5 +++
> 3 files changed, 97 insertions(+), 6 deletions(-)
>
> diff --git a/src/spice-gtk-session-priv.h b/src/spice-gtk-session-priv.h
> index 0dbc9e96..efd36806 100644
> --- a/src/spice-gtk-session-priv.h
> +++ b/src/spice-gtk-session-priv.h
> @@ -44,6 +44,7 @@ void
> spice_gtk_session_set_keyboard_has_focus(SpiceGtkSession *self, gboolean ke
> void spice_gtk_session_set_mouse_has_pointer(SpiceGtkSession *self, gboolean
> mouse_has_pointer);
> gboolean spice_gtk_session_get_keyboard_has_focus(SpiceGtkSession *self);
> gboolean spice_gtk_session_get_mouse_has_pointer(SpiceGtkSession *self);
> +void spice_gtk_session_set_clipboard_delayed(SpiceGtkSession *self, gboolean
> clipboard_delayed);
>
> G_END_DECLS
>
> diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c
> index 31f60dc4..15f2b531 100644
> --- a/src/spice-gtk-session.c
> +++ b/src/spice-gtk-session.c
> @@ -59,6 +59,17 @@ struct _SpiceGtkSessionPrivate {
> gboolean clip_hasdata[CLIPBOARD_LAST];
> gboolean clip_grabbed[CLIPBOARD_LAST];
> gboolean clipboard_by_guest[CLIPBOARD_LAST];
> +
> + /* Used to prevent sending host->guest clipboard data when the guest
> + * display is not focused
> + */
> + gboolean clipboard_delayed;
> + gboolean
> clipboard_pending_selection_release[CLIPBOARD_LAST];
> + gboolean clipboard_pending_selection[CLIPBOARD_LAST];
> + guint32 clipboard_pending_type[CLIPBOARD_LAST];
> + guchar *clipboard_pending_data[CLIPBOARD_LAST];
> + gsize clipboard_pending_len[CLIPBOARD_LAST];
> +
> /* auto-usbredir related */
> gboolean auto_usbredir_enable;
> int auto_usbredir_reqs;
> @@ -666,6 +677,24 @@ static void clipboard_get_targets(GtkClipboard
> *clipboard,
> s->nclip_targets[selection] = 0;
> }
>
> +static void
> +clipboard_selection_release(SpiceGtkSession *self, int selection)
> +{
> + g_return_if_fail(selection >= 0);
> + g_return_if_fail(selection < CLIPBOARD_LAST);
> +
> + g_warning("selection_release");
> +
> + if (!self->priv->clipboard_delayed) {
> + spice_main_channel_clipboard_selection_release(self->priv->main,
> selection);
> + } else {
> + self->priv->clipboard_pending_selection_release[selection] = TRUE;
> + g_clear_pointer(&self->priv->clipboard_pending_data[selection],
> g_free);
> + self->priv->clipboard_pending_len[selection] = 0;
> + self->priv->clipboard_pending_selection[selection] = FALSE;
> + }
> +}
> +
> static void clipboard_owner_change(GtkClipboard *clipboard,
> GdkEventOwnerChange *event,
> gpointer user_data)
> @@ -685,7 +714,7 @@ static void clipboard_owner_change(GtkClipboard
> *clipboard,
> if (s->clip_grabbed[selection]) {
> s->clip_grabbed[selection] = FALSE;
> if (spice_main_channel_agent_test_capability(s->main,
> VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
> - spice_main_channel_clipboard_selection_release(s->main,
> selection);
> + clipboard_selection_release(self, selection);
> }
>
> switch (event->reason) {
> @@ -714,6 +743,18 @@ typedef struct
> guint selection;
> } RunInfo;
>
> +static void
> +clipboard_delayed_selection_notify(SpiceGtkSession *self);
> +
> +G_GNUC_INTERNAL void spice_gtk_session_set_clipboard_delayed(SpiceGtkSession
> *self,
> + gboolean
> clipboard_delayed)
> +{
> + self->priv->clipboard_delayed = clipboard_delayed;
> + if (!self->priv->clipboard_delayed) {
> + clipboard_delayed_selection_notify(self);
> + }
> +}
> +
> static void clipboard_got_from_guest(SpiceMainChannel *main, guint
> selection,
> guint type, const guchar *data, guint
> size,
> gpointer user_data)
> @@ -927,6 +968,52 @@ static char *fixup_clipboard_text(SpiceGtkSession *self,
> const char *text, int *
> return conv;
> }
>
> +static void
> +clipboard_delayed_selection_notify(SpiceGtkSession *self)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < CLIPBOARD_LAST; i++) {
> + if (self->priv->clipboard_pending_selection_release[i]) {
> + g_warn_if_fail(!self->priv->clipboard_pending_selection[i]);
> + spice_main_channel_clipboard_selection_release(self->priv->main,
> i);
> + self->priv->clipboard_pending_selection_release[i] = FALSE;
> + }
> + if (!self->priv->clipboard_pending_selection[i]) {
> + continue;
> + }
> + spice_main_channel_clipboard_selection_notify(self->priv->main,
> + i,
> +
> self->priv->clipboard_pending_type[i],
> +
> self->priv->clipboard_pending_data[i],
> +
> self->priv->clipboard_pending_len[i]);
> + g_clear_pointer(&self->priv->clipboard_pending_data[i], g_free);
> + self->priv->clipboard_pending_len[i] = 0;
> + self->priv->clipboard_pending_selection[i] = FALSE;
> + }
> +}
> +
> +static void
> +clipboard_selection_notify(SpiceGtkSession *self, int selection, guint32
> type,
> + const guchar *data, gsize len)
> +{
> + g_return_if_fail(selection >= 0);
> + g_return_if_fail(selection < CLIPBOARD_LAST);
> +
> + if (!self->priv->clipboard_delayed) {
> + spice_main_channel_clipboard_selection_notify(self->priv->main,
> + selection, type,
> + data, len);
> + } else {
> + self->priv->clipboard_pending_selection[selection] = TRUE;
> + self->priv->clipboard_pending_type[selection] = type;
> + g_clear_pointer(&self->priv->clipboard_pending_data[selection],
> g_free);
> + self->priv->clipboard_pending_data[selection] = g_memdup(data, len);
> + self->priv->clipboard_pending_len[selection] = len;
> + self->priv->clipboard_pending_selection_release[selection] = FALSE;
> + }
> +}
> +
> static void clipboard_received_text_cb(GtkClipboard *clipboard,
> const gchar *text,
> gpointer user_data)
> @@ -965,10 +1052,8 @@ static void clipboard_received_text_cb(GtkClipboard
> *clipboard,
>
> data = (const guchar *) (conv != NULL ? conv : text);
> notify_agent:
> - spice_main_channel_clipboard_selection_notify(self->priv->main,
> selection,
> -
> VD_AGENT_CLIPBOARD_UTF8_TEXT,
> - data,
> - (data != NULL) ? len : 0);
> + clipboard_selection_notify(self, selection,
> VD_AGENT_CLIPBOARD_UTF8_TEXT,
> + data, (data != NULL) ? len : 0);
> g_free(conv);
> }
>
> @@ -1021,7 +1106,7 @@ static void clipboard_received_cb(GtkClipboard
> *clipboard,
> */
> g_warn_if_fail(type != VD_AGENT_CLIPBOARD_UTF8_TEXT);
>
> - spice_main_channel_clipboard_selection_notify(s->main, selection, type,
> data, len);
> + clipboard_selection_notify(self, selection, type, data, len);
> }
>
> static gboolean clipboard_request(SpiceMainChannel *main, guint selection,
> diff --git a/src/spice-widget.c b/src/spice-widget.c
> index 316043a9..7fd8349b 100644
> --- a/src/spice-widget.c
> +++ b/src/spice-widget.c
> @@ -1833,6 +1833,8 @@ static gboolean focus_in_event(GtkWidget *widget,
> GdkEventFocus *focus G_GNUC_UN
>
> DISPLAY_DEBUG(display, "%s", __FUNCTION__);
>
> + spice_gtk_session_set_clipboard_delayed(d->gtk_session, FALSE);
> +
> /*
> * Ignore focus in when we already have the focus
> * (this happens when doing an ungrab from the leave_event callback).
> @@ -1868,6 +1870,9 @@ static gboolean focus_out_event(GtkWidget *widget,
> GdkEventFocus *focus G_GNUC_U
> SpiceDisplay *display = SPICE_DISPLAY(widget);
>
> DISPLAY_DEBUG(display, "%s", __FUNCTION__);
> +
> + spice_gtk_session_set_clipboard_delayed(display->priv->gtk_session,
> TRUE);
> +
> update_display(NULL);
>
> /*
> --
> 2.14.3
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
>
More information about the Spice-devel
mailing list