Blame SOURCES/0025-session-info-check-for-a-locked-session.patch

46eaa5
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
46eaa5
From: Victor Toso <victortoso@redhat.com>
46eaa5
Date: Sat, 9 Apr 2016 12:00:08 +0200
46eaa5
Subject: [PATCH] session-info: check for a locked session
46eaa5
46eaa5
Each session back-end can return this information to vdagentd when
46eaa5
requested.
46eaa5
46eaa5
The agent should use this on situations that should not work when
46eaa5
session is locked such as file-transfer-start which is fixed by this
46eaa5
patch.
46eaa5
46eaa5
systemd-login is the only back-end implementing this function at the
46eaa5
moment and I'll address console-kit back-end in a later patch.
46eaa5
46eaa5
Also, this patch makes spice-vdagent depend on dbus for getting the
46eaa5
lock information.
46eaa5
46eaa5
Resolve: https://bugzilla.redhat.com/show_bug.cgi?id=1323623
46eaa5
46eaa5
Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
46eaa5
(cherry picked from commit 364b6bba068bd694d7c4355b6275f88482d9f3f8)
46eaa5
---
46eaa5
 configure.ac             |  17 ++-----
46eaa5
 src/console-kit.c        |   7 +++
46eaa5
 src/dummy-session-info.c |   5 ++
46eaa5
 src/session-info.h       |   3 ++
46eaa5
 src/systemd-login.c      | 127 +++++++++++++++++++++++++++++++++++++++++++++++
46eaa5
 src/vdagentd.c           |   7 +++
46eaa5
 6 files changed, 153 insertions(+), 13 deletions(-)
46eaa5
46eaa5
diff --git a/configure.ac b/configure.ac
46eaa5
index 9f9d34c..9903ea9 100644
46eaa5
--- a/configure.ac
46eaa5
+++ b/configure.ac
46eaa5
@@ -81,6 +81,7 @@ PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.12])
46eaa5
 PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11])
46eaa5
 PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.5])
46eaa5
 PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.22])
46eaa5
+PKG_CHECK_MODULES([DBUS], [dbus-1])
46eaa5
 
46eaa5
 if test "$with_session_info" = "auto" || test "$with_session_info" = "systemd"; then
46eaa5
     PKG_CHECK_MODULES([LIBSYSTEMD_LOGIN],
46eaa5
@@ -100,19 +101,9 @@ fi
46eaa5
 AM_CONDITIONAL(HAVE_LIBSYSTEMD_LOGIN, test x"$have_libsystemd_login" = "xyes")
46eaa5
 
46eaa5
 if test "$with_session_info" = "auto" || test "$with_session_info" = "console-kit"; then
46eaa5
-    PKG_CHECK_MODULES([DBUS],
46eaa5
-                      [dbus-1],
46eaa5
-                      [have_console_kit="yes"],
46eaa5
-                      [have_console_kit="no"])
46eaa5
-    if test x"$have_console_kit" = "xno" && test "$with_session_info" = "console-kit"; then
46eaa5
-        AC_MSG_ERROR([console-kit support explicitly requested, but some required packages are not available])
46eaa5
-    fi
46eaa5
-    if test x"$have_console_kit" = "xyes"; then
46eaa5
-        AC_DEFINE([HAVE_CONSOLE_KIT], [1], [If defined, vdagentd will be compiled with ConsoleKit support])
46eaa5
-        with_session_info="console-kit"
46eaa5
-    else
46eaa5
-        with_session_info="none"
46eaa5
-    fi
46eaa5
+    AC_DEFINE([HAVE_CONSOLE_KIT], [1], [If defined, vdagentd will be compiled with ConsoleKit support])
46eaa5
+    have_console_kit="yes"
46eaa5
+    with_session_info="console-kit"
46eaa5
 else
46eaa5
     have_console_kit="no"
46eaa5
 fi
46eaa5
diff --git a/src/console-kit.c b/src/console-kit.c
46eaa5
index 759a81e..260fcc7 100644
46eaa5
--- a/src/console-kit.c
46eaa5
+++ b/src/console-kit.c
46eaa5
@@ -352,3 +352,10 @@ static char *console_kit_check_active_session_change(struct session_info *ck)
46eaa5
 
46eaa5
     return ck->active_session;
46eaa5
 }
46eaa5
+
46eaa5
+gboolean session_info_session_is_locked(struct session_info *info)
46eaa5
+{
46eaa5
+    /* TODO: It could be implemented based on Lock/Unlock signals from Session
46eaa5
+     * interface. */
46eaa5
+    return FALSE;
46eaa5
+}
46eaa5
diff --git a/src/dummy-session-info.c b/src/dummy-session-info.c
46eaa5
index e188ddc..c09643b 100644
46eaa5
--- a/src/dummy-session-info.c
46eaa5
+++ b/src/dummy-session-info.c
46eaa5
@@ -44,3 +44,8 @@ char *session_info_session_for_pid(struct session_info *si, uint32_t pid)
46eaa5
 {
46eaa5
     return NULL;
46eaa5
 }
46eaa5
+
46eaa5
+gboolean session_is_locked(struct session_info *ck)
46eaa5
+{
46eaa5
+    return FALSE;
46eaa5
+}
46eaa5
diff --git a/src/session-info.h b/src/session-info.h
46eaa5
index 67099de..d660fcf 100644
46eaa5
--- a/src/session-info.h
46eaa5
+++ b/src/session-info.h
46eaa5
@@ -24,6 +24,7 @@
46eaa5
 
46eaa5
 #include <stdio.h>
46eaa5
 #include <stdint.h>
46eaa5
+#include <glib.h>
46eaa5
 
46eaa5
 struct session_info;
46eaa5
 
46eaa5
@@ -36,4 +37,6 @@ const char *session_info_get_active_session(struct session_info *ck);
46eaa5
 /* Note result must be free()-ed by caller */
46eaa5
 char *session_info_session_for_pid(struct session_info *ck, uint32_t pid);
46eaa5
 
46eaa5
+gboolean session_info_session_is_locked(struct session_info *si);
46eaa5
+
46eaa5
 #endif
46eaa5
diff --git a/src/systemd-login.c b/src/systemd-login.c
46eaa5
index 73db37f..4a365c0 100644
46eaa5
--- a/src/systemd-login.c
46eaa5
+++ b/src/systemd-login.c
46eaa5
@@ -25,13 +25,121 @@
46eaa5
 #include <string.h>
46eaa5
 #include <syslog.h>
46eaa5
 #include <systemd/sd-login.h>
46eaa5
+#include <dbus/dbus.h>
46eaa5
 
46eaa5
 struct session_info {
46eaa5
     int verbose;
46eaa5
     sd_login_monitor *mon;
46eaa5
     char *session;
46eaa5
+    struct {
46eaa5
+        DBusConnection *system_connection;
46eaa5
+        char *match_session_signals;
46eaa5
+    } dbus;
46eaa5
+    gboolean session_is_locked;
46eaa5
 };
46eaa5
 
46eaa5
+#define LOGIND_SESSION_INTERFACE    "org.freedesktop.login1.Session"
46eaa5
+#define LOGIND_SESSION_OBJ_TEMPLATE "'/org/freedesktop/login1/session/_3%s'"
46eaa5
+
46eaa5
+#define SESSION_SIGNAL_LOCK         "Lock"
46eaa5
+#define SESSION_SIGNAL_UNLOCK       "Unlock"
46eaa5
+
46eaa5
+/* dbus related */
46eaa5
+static DBusConnection *si_dbus_get_system_bus(void)
46eaa5
+{
46eaa5
+    DBusConnection *connection;
46eaa5
+    DBusError error;
46eaa5
+
46eaa5
+    dbus_error_init(&error);
46eaa5
+    connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
46eaa5
+    if (connection == NULL || dbus_error_is_set(&error)) {
46eaa5
+        if (dbus_error_is_set(&error)) {
46eaa5
+            syslog(LOG_WARNING, "Unable to connect to system bus: %s",
46eaa5
+                   error.message);
46eaa5
+            dbus_error_free(&error);
46eaa5
+        } else {
46eaa5
+            syslog(LOG_WARNING, "Unable to connect to system bus");
46eaa5
+        }
46eaa5
+        return NULL;
46eaa5
+    }
46eaa5
+    return connection;
46eaa5
+}
46eaa5
+
46eaa5
+static void si_dbus_match_remove(struct session_info *si)
46eaa5
+{
46eaa5
+    DBusError error;
46eaa5
+    if (si->dbus.match_session_signals == NULL)
46eaa5
+        return;
46eaa5
+
46eaa5
+    dbus_error_init(&error);
46eaa5
+    dbus_bus_remove_match(si->dbus.system_connection,
46eaa5
+                          si->dbus.match_session_signals,
46eaa5
+                          &error);
46eaa5
+
46eaa5
+    g_free(si->dbus.match_session_signals);
46eaa5
+    si->dbus.match_session_signals = NULL;
46eaa5
+}
46eaa5
+
46eaa5
+static void si_dbus_match_rule_update(struct session_info *si)
46eaa5
+{
46eaa5
+    DBusError error;
46eaa5
+
46eaa5
+    if (si->dbus.system_connection == NULL ||
46eaa5
+            si->session == NULL)
46eaa5
+        return;
46eaa5
+
46eaa5
+    si_dbus_match_remove(si);
46eaa5
+
46eaa5
+    si->dbus.match_session_signals =
46eaa5
+        g_strdup_printf ("type='signal',interface='%s',path="
46eaa5
+                         LOGIND_SESSION_OBJ_TEMPLATE,
46eaa5
+                         LOGIND_SESSION_INTERFACE,
46eaa5
+                         si->session);
46eaa5
+    if (si->verbose)
46eaa5
+        syslog(LOG_DEBUG, "logind match: %s", si->dbus.match_session_signals);
46eaa5
+
46eaa5
+    dbus_error_init(&error);
46eaa5
+    dbus_bus_add_match(si->dbus.system_connection,
46eaa5
+                       si->dbus.match_session_signals,
46eaa5
+                       &error);
46eaa5
+    if (dbus_error_is_set(&error)) {
46eaa5
+        syslog(LOG_WARNING, "Unable to add dbus rule match: %s",
46eaa5
+               error.message);
46eaa5
+        dbus_error_free(&error);
46eaa5
+        g_free(si->dbus.match_session_signals);
46eaa5
+        si->dbus.match_session_signals = NULL;
46eaa5
+    }
46eaa5
+}
46eaa5
+
46eaa5
+static void
46eaa5
+si_dbus_read_signals(struct session_info *si)
46eaa5
+{
46eaa5
+    DBusMessage *message = NULL;
46eaa5
+
46eaa5
+    dbus_connection_read_write(si->dbus.system_connection, 0);
46eaa5
+    message = dbus_connection_pop_message(si->dbus.system_connection);
46eaa5
+    while (message != NULL) {
46eaa5
+        const char *member;
46eaa5
+
46eaa5
+        member = dbus_message_get_member (message);
46eaa5
+        if (g_strcmp0(member, SESSION_SIGNAL_LOCK) == 0) {
46eaa5
+            si->session_is_locked = TRUE;
46eaa5
+        } else if (g_strcmp0(member, SESSION_SIGNAL_UNLOCK) == 0) {
46eaa5
+            si->session_is_locked = FALSE;
46eaa5
+        } else {
46eaa5
+            if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {
46eaa5
+                syslog(LOG_WARNING, "(systemd-login) received non signal message");
46eaa5
+            } else if (si->verbose) {
46eaa5
+                syslog(LOG_DEBUG, "(systemd-login) Signal not handled: %s", member);
46eaa5
+            }
46eaa5
+        }
46eaa5
+
46eaa5
+        dbus_message_unref(message);
46eaa5
+        dbus_connection_read_write(si->dbus.system_connection, 0);
46eaa5
+        message = dbus_connection_pop_message(si->dbus.system_connection);
46eaa5
+    }
46eaa5
+}
46eaa5
+
46eaa5
 struct session_info *session_info_create(int verbose)
46eaa5
 {
46eaa5
     struct session_info *si;
46eaa5
@@ -42,6 +150,7 @@ struct session_info *session_info_create(int verbose)
46eaa5
         return NULL;
46eaa5
 
46eaa5
     si->verbose = verbose;
46eaa5
+    si->session_is_locked = FALSE;
46eaa5
 
46eaa5
     r = sd_login_monitor_new("session", &si->mon);
46eaa5
     if (r < 0) {
46eaa5
@@ -50,6 +159,7 @@ struct session_info *session_info_create(int verbose)
46eaa5
         return NULL;
46eaa5
     }
46eaa5
 
46eaa5
+    si->dbus.system_connection = si_dbus_get_system_bus();
46eaa5
     return si;
46eaa5
 }
46eaa5
 
46eaa5
@@ -58,6 +168,8 @@ void session_info_destroy(struct session_info *si)
46eaa5
     if (!si)
46eaa5
         return;
46eaa5
 
46eaa5
+    si_dbus_match_remove(si);
46eaa5
+    dbus_connection_close(si->dbus.system_connection);
46eaa5
     sd_login_monitor_unref(si->mon);
46eaa5
     free(si->session);
46eaa5
     free(si);
46eaa5
@@ -87,6 +199,7 @@ const char *session_info_get_active_session(struct session_info *si)
46eaa5
     sd_login_monitor_flush(si->mon);
46eaa5
     free(old_session);
46eaa5
 
46eaa5
+    si_dbus_match_rule_update(si);
46eaa5
     return si->session;
46eaa5
 }
46eaa5
 
46eaa5
@@ -104,3 +217,17 @@ char *session_info_session_for_pid(struct session_info *si, uint32_t pid)
46eaa5
 
46eaa5
     return session;
46eaa5
 }
46eaa5
+
46eaa5
+gboolean session_info_session_is_locked(struct session_info *si)
46eaa5
+{
46eaa5
+    g_return_val_if_fail (si != NULL, FALSE);
46eaa5
+
46eaa5
+    /* We could also rely on IdleHint property from Session which seems to work
46eaa5
+     * well in rhel7 but it wasn't working well in my own system (F23). I'm
46eaa5
+     * convinced for now that Lock/Unlock signals should be enough but that
46eaa5
+     * means Lock/Unlock being done by logind. That might take a while.
46eaa5
+     * Check: https://bugzilla.gnome.org/show_bug.cgi?id=764773 */
46eaa5
+
46eaa5
+    si_dbus_read_signals(si);
46eaa5
+    return si->session_is_locked;
46eaa5
+}
46eaa5
diff --git a/src/vdagentd.c b/src/vdagentd.c
46eaa5
index 2c8e973..2f77773 100644
46eaa5
--- a/src/vdagentd.c
46eaa5
+++ b/src/vdagentd.c
46eaa5
@@ -276,6 +276,13 @@ static void do_client_file_xfer(struct vdagent_virtio_port *vport,
46eaa5
                "active session, cancelling client file-xfer request %u",
46eaa5
                s->id, VD_AGENT_FILE_XFER_STATUS_CANCELLED);
46eaa5
             return;
46eaa5
+        } else if (session_info_session_is_locked(session_info)) {
46eaa5
+            syslog(LOG_DEBUG, "Session is locked, skipping file-xfer-start");
46eaa5
+            send_file_xfer_status(vport,
46eaa5
+               "User's session is locked and cannot start file transfer. "
46eaa5
+               "Cancelling client file-xfer request %u",
46eaa5
+               s->id, VD_AGENT_FILE_XFER_STATUS_ERROR);
46eaa5
+            return;
46eaa5
         }
46eaa5
         udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
46eaa5
                     data, message_header->size);