[Spice-devel] RFC [spice-gtk] session: Allow to delay sending clipboard to the guest
Christophe Fergeau
cfergeau at redhat.com
Tue Jan 9 14:35:40 UTC 2018
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
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
More information about the Spice-devel
mailing list