Blob Blame History Raw
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Victor Toso <victortoso@redhat.com>
Date: Sat, 9 Apr 2016 12:00:08 +0200
Subject: [PATCH] session-info: check for a locked session

Each session back-end can return this information to vdagentd when
requested.

The agent should use this on situations that should not work when
session is locked such as file-transfer-start which is fixed by this
patch.

systemd-login is the only back-end implementing this function at the
moment and I'll address console-kit back-end in a later patch.

Also, this patch makes spice-vdagent depend on dbus for getting the
lock information.

Resolve: https://bugzilla.redhat.com/show_bug.cgi?id=1323623

Acked-by: Jonathon Jongsma <jjongsma@redhat.com>
(cherry picked from commit 364b6bba068bd694d7c4355b6275f88482d9f3f8)
---
 configure.ac             |  17 ++-----
 src/console-kit.c        |   7 +++
 src/dummy-session-info.c |   5 ++
 src/session-info.h       |   3 ++
 src/systemd-login.c      | 127 +++++++++++++++++++++++++++++++++++++++++++++++
 src/vdagentd.c           |   7 +++
 6 files changed, 153 insertions(+), 13 deletions(-)

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