summaryrefslogtreecommitdiffstats
path: root/plugins/usb-protection/gsd-usb-protection-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/usb-protection/gsd-usb-protection-manager.c')
-rw-r--r--plugins/usb-protection/gsd-usb-protection-manager.c1199
1 files changed, 1199 insertions, 0 deletions
diff --git a/plugins/usb-protection/gsd-usb-protection-manager.c b/plugins/usb-protection/gsd-usb-protection-manager.c
new file mode 100644
index 0000000..1139696
--- /dev/null
+++ b/plugins/usb-protection/gsd-usb-protection-manager.c
@@ -0,0 +1,1199 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2018 Ludovico de Nittis <denittis@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <libnotify/notify.h>
+#include <locale.h>
+#include <string.h>
+
+#include <gdesktop-enums.h>
+
+#include "gnome-settings-bus.h"
+#include "gnome-settings-profile.h"
+#include "gsd-enums.h"
+#include "gsd-usb-protection-manager.h"
+
+#define PRIVACY_SETTINGS "org.gnome.desktop.privacy"
+#define USB_PROTECTION "usb-protection"
+#define USB_PROTECTION_LEVEL "usb-protection-level"
+
+#define DBUS_VERSION "1"
+
+#define USBGUARD_DBUS_NAME "org.usbguard" DBUS_VERSION
+#define USBGUARD_DBUS_PATH "/org/usbguard" DBUS_VERSION
+#define USBGUARD_DBUS_INTERFACE "org.usbguard"
+#define USBGUARD_DBUS_INTERFACE_VERSIONED USBGUARD_DBUS_INTERFACE DBUS_VERSION
+
+#define USBGUARD_DBUS_PATH_POLICY USBGUARD_DBUS_PATH "/Policy"
+#define USBGUARD_DBUS_INTERFACE_POLICY USBGUARD_DBUS_INTERFACE ".Policy" DBUS_VERSION
+
+#define USBGUARD_DBUS_PATH_DEVICES USBGUARD_DBUS_PATH "/Devices"
+#define USBGUARD_DBUS_INTERFACE_DEVICES USBGUARD_DBUS_INTERFACE ".Devices" DBUS_VERSION
+
+#define APPLY_POLICY "apply-policy"
+#define BLOCK "block"
+#define REJECT "reject"
+
+#define APPLY_DEVICE_POLICY "applyDevicePolicy"
+#define LIST_DEVICES "listDevices"
+#define LIST_RULES "listRules"
+#define ALLOW "allow"
+#define DEVICE_POLICY_CHANGED "DevicePolicyChanged"
+#define DEVICE_PRESENCE_CHANGED "DevicePresenceChanged"
+#define INSERTED_DEVICE_POLICY "InsertedDevicePolicy"
+#define APPEND_RULE "appendRule"
+#define ALLOW_ALL "allow id *:* label \"GNOME_SETTINGS_DAEMON_RULE\""
+#define WITH_CONNECT_TYPE "with-connect-type"
+#define WITH_INTERFACE "with-interface"
+#define NAME "name"
+
+struct _GsdUsbProtectionManager
+{
+ GObject parent;
+ guint start_idle_id;
+ GDBusNodeInfo *introspection_data;
+ GSettings *settings;
+ guint name_id;
+ GDBusConnection *connection;
+ gboolean available;
+ GDBusProxy *usb_protection;
+ GDBusProxy *usb_protection_devices;
+ GDBusProxy *usb_protection_policy;
+ GCancellable *cancellable;
+ GsdScreenSaver *screensaver_proxy;
+ gboolean screensaver_active;
+ guint last_device_id;
+ NotifyNotification *notification;
+};
+
+typedef enum {
+ EVENT_PRESENT,
+ EVENT_INSERT,
+ EVENT_UPDATE,
+ EVENT_REMOVE
+} UsbGuardEvent;
+
+
+typedef enum {
+ TARGET_ALLOW,
+ TARGET_BLOCK,
+ TARGET_REJECT
+} UsbGuardTarget;
+
+typedef enum {
+ POLICY_DEVICE_ID,
+ POLICY_TARGET_OLD,
+ /* This is the rule that has been applied */
+ POLICY_TARGET_NEW,
+ POLICY_DEV_RULE,
+ /* The ID of the rule that has been applied.
+ * uint32 - 1 is one of the implicit rules,
+ * e.g. ImplicitPolicyTarget or InsertedDevicePolicy.
+ */
+ POLICY_RULE_ID,
+ POLICY_ATTRIBUTES
+} UsbGuardPolicyChanged;
+
+typedef enum {
+ PRESENCE_DEVICE_ID,
+ PRESENCE_EVENT,
+ /* That does not reflect what USBGuard intends to do with the device :( */
+ PRESENCE_TARGET,
+ PRESENCE_DEV_RULE,
+ PRESENCE_ATTRIBUTES
+} UsbGuardPresenceChanged;
+
+static void gsd_usb_protection_manager_finalize (GObject *object);
+
+G_DEFINE_TYPE (GsdUsbProtectionManager, gsd_usb_protection_manager, G_TYPE_OBJECT)
+
+static gpointer manager_object = NULL;
+
+#define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
+#define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
+#define GSD_DBUS_BASE_INTERFACE "org.gnome.SettingsDaemon"
+
+#define GSD_USB_PROTECTION_DBUS_NAME GSD_DBUS_NAME ".UsbProtection"
+#define GSD_USB_PROTECTION_DBUS_PATH GSD_DBUS_PATH "/UsbProtection"
+
+static const gchar introspection_xml[] =
+"<node>"
+" <interface name='org.gnome.SettingsDaemon.UsbProtection'>"
+" <property name='Available' type='b' access='read'/>"
+" </interface>"
+"</node>";
+
+static void
+dbus_call_log_error (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GVariant) result = NULL;
+ g_autoptr(GError) error = NULL;
+ const gchar *msg = user_data;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+ res,
+ &error);
+ if (result == NULL &&
+ !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
+ g_warning ("%s: %s", msg, error->message);
+}
+
+static void
+add_usbguard_allow_rule (GsdUsbProtectionManager *manager)
+{
+ /* This appends a "allow all" rule.
+ * It has the purpose of ensuring the authorization of new devices when
+ * the lockscreen is off while respecting existing rules.
+ * We make it temporary, so that we are stateless and don't alter the
+ * existing (persistent) configuration.
+ */
+
+ GVariant *params;
+ GDBusProxy *policy_proxy = manager->usb_protection_policy;
+
+ if (policy_proxy == NULL) {
+ g_warning ("Cannot add allow rule, because dbus proxy is missing");
+ } else {
+ gboolean temporary = TRUE;
+ /* This is USBGuard's Rule::LastID */
+ const guint32 last_rule_id = G_MAXUINT32 - 2;
+ g_debug ("Adding rule %u", last_rule_id);
+ params = g_variant_new ("(sub)", ALLOW_ALL, last_rule_id, temporary);
+ g_dbus_proxy_call (policy_proxy,
+ APPEND_RULE,
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ dbus_call_log_error,
+ "Error appending USBGuard rule");
+ }
+}
+
+static gboolean
+is_usbguard_allow_rule_present (GVariant *rules)
+{
+ g_autoptr(GVariantIter) iter = NULL;
+ g_autofree gchar *value = NULL;
+ guint number = 0;
+
+ g_debug ("Detecting rule...");
+
+ g_variant_get (rules, "a(us)", &iter);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ while (g_variant_iter_loop (iter, "(us)", &number, &value)) {
+ if (g_strcmp0 (value, ALLOW_ALL) == 0) {
+ g_debug ("Detected rule!");
+ return TRUE;
+ }
+ }
+ g_debug ("Rule not present");
+ return FALSE;
+}
+
+static void
+usbguard_listrules_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVariant *result, *rules;
+ g_autoptr(GError) error = NULL;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+ res,
+ &error);
+
+ if (!result) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("Failed to fetch USBGuard rules list: %s", error->message);
+ }
+ return;
+ }
+
+ rules = g_variant_get_child_value (result, 0);
+ g_variant_unref (result);
+ g_return_if_fail (rules != NULL);
+ if (!is_usbguard_allow_rule_present (rules))
+ add_usbguard_allow_rule (user_data);
+
+}
+
+static void
+usbguard_ensure_allow_rule (GsdUsbProtectionManager *manager)
+{
+ GVariant *params;
+ GDBusProxy *policy_proxy = manager->usb_protection_policy;
+
+ if (policy_proxy == NULL) {
+ g_warning ("Cannot list rules, because dbus proxy is missing");
+ } else {
+ /* listRules parameter is a label for matching rules.
+ * Currently we are using an empty label to get all the
+ * rules instead of just using "GNOME_SETTINGS_DAEMON_RULE"
+ * until this bug gets solved:
+ * https://github.com/USBGuard/usbguard/issues/328 */
+ params = g_variant_new ("(s)", "");
+ g_dbus_proxy_call (policy_proxy,
+ LIST_RULES,
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ usbguard_listrules_cb,
+ manager);
+ }
+}
+
+static void
+settings_changed_callback (GSettings *settings,
+ const char *key,
+ GsdUsbProtectionManager *manager)
+{
+ gchar *value_usbguard;
+ gboolean usbguard_controlled;
+ GVariant *params;
+ GDesktopUsbProtection protection_level;
+
+ /* We react only if one of the two USB related properties has been changed */
+ if (g_strcmp0 (key, USB_PROTECTION) != 0 && g_strcmp0 (key, USB_PROTECTION_LEVEL) != 0)
+ return;
+
+ usbguard_controlled = g_settings_get_boolean (settings, USB_PROTECTION);
+ protection_level = g_settings_get_enum (settings, USB_PROTECTION_LEVEL);
+ g_debug ("USBGuard control is currently %i with a protection level of %i",
+ usbguard_controlled, protection_level);
+
+ /* If previously we were controlling USBGuard and now we are not,
+ * we leave the USBGuard configuration in a clean state. I.e. we set
+ * "InsertedDevicePolicy" to "apply-policy" and we ensure that
+ * there is an always allow rule. In this way even if USBGuard daemon
+ * is running every USB devices will be automatically authorized. */
+ if (g_strcmp0 (key, USB_PROTECTION) == 0 && !usbguard_controlled) {
+ g_debug ("let's clean usbguard config state");
+ params = g_variant_new ("(ss)",
+ INSERTED_DEVICE_POLICY,
+ APPLY_POLICY);
+
+ if (manager->usb_protection != NULL) {
+ g_dbus_proxy_call (manager->usb_protection,
+ "setParameter",
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ dbus_call_log_error,
+ "Error calling USBGuard DBus to set a clean configuration state");
+ }
+
+ usbguard_ensure_allow_rule (manager);
+ }
+
+ /* Only if we are entitled to handle USBGuard */
+ if (usbguard_controlled && manager->usb_protection != NULL) {
+ value_usbguard = (protection_level == G_DESKTOP_USB_PROTECTION_ALWAYS) ? BLOCK : APPLY_POLICY;
+ params = g_variant_new ("(ss)",
+ INSERTED_DEVICE_POLICY,
+ value_usbguard);
+
+ g_dbus_proxy_call (manager->usb_protection,
+ "setParameter",
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ dbus_call_log_error,
+ "Error calling USBGuard DBus to set the desidered protection level");
+
+ /* If we are in "When lockscreen is active" we also check if the
+ * always allow rule is present. */
+ if (protection_level == G_DESKTOP_USB_PROTECTION_LOCKSCREEN)
+ usbguard_ensure_allow_rule (manager);
+ }
+}
+
+static void update_usb_protection_store (GsdUsbProtectionManager *manager,
+ GVariant *parameter)
+{
+ const gchar *key;
+ gboolean usbguard_controlled;
+ GDesktopUsbProtection protection_level;
+ GSettings *settings = manager->settings;
+
+ usbguard_controlled = g_settings_get_boolean (settings, USB_PROTECTION);
+ /* If we are not handling USBGuard configuration (e.g. the user is using
+ * a third party program) we do nothing when the config changes. */
+ if (usbguard_controlled) {
+ key = g_variant_get_string (parameter, NULL);
+ protection_level = g_settings_get_enum (settings, USB_PROTECTION_LEVEL);
+ /* If the USBGuard configuration has been changed and doesn't match
+ * our internal state, most likely means that the user externally
+ * changed it. When this happens we set to false the control value. */
+ if ((g_strcmp0 (key, APPLY_POLICY) == 0 && protection_level == G_DESKTOP_USB_PROTECTION_ALWAYS)) {
+ g_settings_set (settings, USB_PROTECTION, "b", FALSE);
+ g_warning ("We don't control anymore USBGuard because the configuration changed externally.");
+ }
+ }
+}
+
+static gboolean
+is_protection_active (GsdUsbProtectionManager *manager)
+{
+ GSettings *settings = manager->settings;
+
+ return g_settings_get_boolean (settings, USB_PROTECTION);
+}
+
+static void
+on_notification_closed (NotifyNotification *n,
+ GsdUsbProtectionManager *manager)
+{
+ g_debug ("Clearing notification");
+ g_clear_object (&manager->notification);
+}
+
+static void
+show_notification (GsdUsbProtectionManager *manager,
+ const char *summary,
+ const char *body)
+{
+ /* Don't show a notice if one is already displayed */
+ if (manager->notification != NULL) {
+ g_debug ("A notification already exists, we do not show a new one");
+ return;
+ }
+
+ manager->notification = notify_notification_new (summary, body, "drive-removable-media-symbolic");
+ notify_notification_set_app_name (manager->notification, _("USB Protection"));
+ notify_notification_set_hint (manager->notification, "transient", g_variant_new_boolean (TRUE));
+ notify_notification_set_hint_string (manager->notification, "x-gnome-privacy-scope", "system");
+ notify_notification_set_timeout (manager->notification, NOTIFY_EXPIRES_DEFAULT);
+ notify_notification_set_urgency (manager->notification, NOTIFY_URGENCY_CRITICAL);
+ g_signal_connect_object (manager->notification,
+ "closed",
+ G_CALLBACK (on_notification_closed),
+ manager,
+ 0);
+ g_debug ("Showing notification for %s: %s", summary, body);
+ if (!notify_notification_show (manager->notification, NULL)) {
+ g_warning ("Failed to send USB protection notification");
+ g_clear_object (&manager->notification);
+ }
+}
+
+static void call_usbguard_dbus (GDBusProxy *proxy,
+ GsdUsbProtectionManager *manager,
+ guint device_id,
+ guint target,
+ gboolean permanent)
+{
+ if (manager->usb_protection_devices == NULL) {
+ g_warning("Could not call USBGuard, because DBus is missing");
+ } else {
+ g_debug ("Calling applyDevicePolicy with device_id %u, target %u and permanent: %i", device_id, target, permanent);
+ GVariant *params = g_variant_new ("(uub)", device_id, target, permanent);
+ g_dbus_proxy_call (manager->usb_protection_devices,
+ APPLY_DEVICE_POLICY,
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ dbus_call_log_error,
+ "Error calling USBGuard DBus to authorize a device");
+ }
+}
+
+static gboolean
+is_hid_or_hub (GVariant *device,
+ gboolean *has_other_classes)
+{
+ g_autoptr(GVariantIter) iter = NULL;
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *value = NULL;
+ guint i;
+ gboolean is_hid_or_hub = FALSE;
+
+ if (has_other_classes != NULL) {
+ *has_other_classes = FALSE;
+ }
+
+ g_variant_get_child (device, PRESENCE_ATTRIBUTES, "a{ss}", &iter);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ while (g_variant_iter_loop (iter, "{ss}", &name, &value)) {
+ if (g_strcmp0 (name, WITH_INTERFACE) == 0) {
+ g_auto(GStrv) interfaces_splitted = NULL;
+ interfaces_splitted = g_strsplit (value, " ", -1);
+ for (i = 0; i < g_strv_length (interfaces_splitted); i++) {
+ if (g_str_has_prefix (interfaces_splitted[i], "03:")
+ || g_str_has_prefix (interfaces_splitted[i], "09:")) {
+ is_hid_or_hub = TRUE;
+ }
+ else if (has_other_classes != NULL) {
+ *has_other_classes = TRUE;
+ }
+ }
+ }
+ }
+ return is_hid_or_hub;
+}
+
+static gboolean
+is_hardwired (GVariant *device)
+{
+ g_autoptr(GVariantIter) iter = NULL;
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *value = NULL;
+
+ g_variant_get_child (device, PRESENCE_ATTRIBUTES, "a{ss}", &iter);
+ g_return_val_if_fail (iter != NULL, FALSE);
+ while (g_variant_iter_loop (iter, "{ss}", &name, &value)) {
+ if (g_strcmp0 (name, WITH_CONNECT_TYPE) == 0) {
+ return g_strcmp0 (value, "hardwired") == 0;
+ }
+ }
+ return FALSE;
+}
+
+static void
+authorize_device (GsdUsbProtectionManager *manager,
+ guint device_id)
+{
+ g_return_if_fail (manager->usb_protection_devices != NULL);
+
+ g_debug ("Authorizing device %u", device_id);
+ call_usbguard_dbus(manager->usb_protection_devices,
+ manager,
+ device_id,
+ TARGET_ALLOW,
+ FALSE);
+}
+
+static void
+on_screen_locked (GsdScreenSaver *screen_saver,
+ GAsyncResult *result,
+ GsdUsbProtectionManager *manager)
+{
+ guint device_id;
+ g_autoptr(GError) error = NULL;
+
+ gsd_screen_saver_call_lock_finish (screen_saver, result, &error);
+
+ if (error) {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ g_warning ("Couldn't lock screen: %s", error->message);
+ }
+
+ device_id = manager->last_device_id;
+ authorize_device (manager, device_id);
+ manager->last_device_id = G_MAXUINT;
+ show_notification (manager,
+ _("New USB device"),
+ _("New device has been detected while the session was not locked. "
+ "If you did not plug anything, check your system for any suspicious device."));
+
+}
+
+static void
+on_usbguard_signal (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ UsbGuardTarget target = TARGET_BLOCK;
+ UsbGuardEvent device_event;
+ GDesktopUsbProtection protection_level;
+ GsdUsbProtectionManager *manager = user_data;
+ g_autoptr(GVariantIter) iter = NULL;
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *device_name = NULL;
+ gboolean hid_or_hub = FALSE;
+ gboolean has_other_classes = FALSE;
+
+ g_debug ("USBGuard signal: %s", signal_name);
+
+ /* We act only if we receive a signal indicating that a device has been inserted */
+ if (g_strcmp0 (signal_name, DEVICE_PRESENCE_CHANGED) != 0) {
+ return;
+ }
+
+ g_return_if_fail (g_variant_n_children (parameters) >= PRESENCE_EVENT);
+ g_variant_get_child (parameters, PRESENCE_EVENT, "u", &device_event);
+ if (device_event != EVENT_INSERT) {
+ g_debug ("Device hat not been inserted (%d); ignoring", device_event);
+ return;
+ }
+
+ /* We would like to show a notification for an inserted device that
+ * *has not been blocked*. But USBGuard is not providing that information.
+ * So we have to work around that limitation and assume that any device plugged in
+ * during screensaver shall be blocked.
+ * https://github.com/USBGuard/usbguard/issues/353
+
+ g_variant_get_child (parameters, POLICY_TARGET_NEW, "u", &target);
+ */
+
+ /* If the device is already authorized we do nothing */
+ if (target == TARGET_ALLOW) {
+ g_debug ("Device will be allowed, we return");
+ return;
+ }
+
+ /* If the USB protection is disabled we do nothing */
+ if (!is_protection_active (manager)) {
+ g_debug ("Protection is not active. Not acting on the device");
+ return;
+ }
+
+ g_variant_get_child (parameters, PRESENCE_ATTRIBUTES, "a{ss}", &iter);
+ g_return_if_fail (iter != NULL);
+ while (g_variant_iter_loop (iter, "{ss}", &name, &device_name)) {
+ if (g_strcmp0 (name, NAME) == 0)
+ g_debug ("A new USB device has been connected: %s", device_name);
+ }
+
+ if (is_hardwired (parameters)) {
+ guint device_id;
+ g_debug ("Device is hardwired, allowing it to be connected");
+ g_variant_get_child (parameters, POLICY_DEVICE_ID, "u", &device_id);
+ authorize_device (manager, device_id);
+ return;
+ }
+
+ protection_level = g_settings_get_enum (manager->settings, USB_PROTECTION_LEVEL);
+
+ g_debug ("Screensaver active: %d", manager->screensaver_active);
+ hid_or_hub = is_hid_or_hub (parameters, &has_other_classes);
+ if (manager->screensaver_active) {
+ /* If the session is locked we check if the inserted device is a HID,
+ * e.g. a keyboard or a mouse, or an HUB.
+ * If that is the case we authorize the newly inserted device as an
+ * antilockout policy.
+ *
+ * If this device advertises also interfaces outside the HID class, or the
+ * HUB class, it is suspect. It could be a false positive because this could
+ * be a "smart" keyboard for example, but at this stage is better be safe. */
+ if (hid_or_hub && !has_other_classes) {
+ guint device_id;
+ show_notification (manager,
+ _("New device detected"),
+ _("Either one of your existing devices has been reconnected or a new one has been plugged in. "
+ "If you did not do it, check your system for any suspicious device."));
+ g_variant_get_child (parameters, POLICY_DEVICE_ID, "u", &device_id);
+ authorize_device (manager, device_id);
+ } else {
+ if (protection_level == G_DESKTOP_USB_PROTECTION_LOCKSCREEN) {
+ show_notification (manager,
+ _("Reconnect USB device"),
+ _("New device has been detected while you were away. "
+ "Please disconnect and reconnect the device to start using it."));
+ } else {
+ const char* name_for_notification = device_name ? device_name : "unknown name";
+ g_debug ("Showing notification for %s", name_for_notification);
+ show_notification (manager,
+ _("USB device blocked"),
+ _("New device has been detected while you were away. "
+ "It has been blocked because the USB protection is active."));
+ }
+ }
+ } else {
+ /* If the protection level is "lockscreen" the device will be automatically
+ * authorized by usbguard. */
+ if (protection_level == G_DESKTOP_USB_PROTECTION_ALWAYS) {
+ /* We authorize the device if this is a HID,
+ * e.g. a keyboard or a mouse, or an HUB.
+ * We also lock the screen to prevent an attacker to plug malicious
+ * devices if the legitimate user forgot to lock his session.
+ *
+ * If this device advertises also interfaces outside the HID class, or the
+ * HUB class, it is suspect. It could be a false positive because this could
+ * be a "smart" keyboard for example, but at this stage is better be safe. */
+ if (hid_or_hub && !has_other_classes) {
+ g_variant_get_child (parameters, POLICY_DEVICE_ID, "u", &(manager->last_device_id));
+ gsd_screen_saver_call_lock (manager->screensaver_proxy,
+ manager->cancellable,
+ (GAsyncReadyCallback) on_screen_locked,
+ manager);
+ } else {
+ show_notification (manager,
+ _("USB device blocked"),
+ _("The new inserted device has been blocked because the USB protection is active."));
+ }
+ }
+ }
+}
+
+static void
+on_usb_protection_signal (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ g_autoptr(GVariant) parameter = NULL;
+ g_autofree gchar *policy_name = NULL;
+
+ if (g_strcmp0 (signal_name, "PropertyParameterChanged") != 0)
+ return;
+
+ g_variant_get_child (parameters, 0, "s", &policy_name);
+
+ /* Right now we just care about the InsertedDevicePolicy value */
+ if (g_strcmp0 (policy_name, INSERTED_DEVICE_POLICY) != 0)
+ return;
+
+ parameter = g_variant_get_child_value (parameters, 2);
+ g_return_if_fail (parameter != NULL);
+ update_usb_protection_store (user_data, parameter);
+
+}
+
+static void
+get_parameter_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVariant *result;
+ GVariant *params = NULL;
+ g_autofree gchar *key = NULL;
+ GDesktopUsbProtection protection_level;
+ GsdUsbProtectionManager *manager;
+ GSettings *settings;
+ g_autoptr(GError) error = NULL;
+
+ result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
+ res,
+ &error);
+ if (result == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("Failed to fetch USBGuard parameters: %s", error->message);
+ }
+ return;
+ }
+
+ manager = GSD_USB_PROTECTION_MANAGER (user_data);
+ settings = manager->settings;
+
+ g_variant_get_child (result, 0, "s", &key);
+ g_variant_unref (result);
+ protection_level = g_settings_get_enum (settings, USB_PROTECTION_LEVEL);
+
+ g_debug ("InsertedDevicePolicy is: %s", key);
+
+ if (protection_level == G_DESKTOP_USB_PROTECTION_LOCKSCREEN) {
+ if (g_strcmp0 (key, APPLY_POLICY) != 0) {
+ /* We are out of sync. */
+ params = g_variant_new ("(ss)",
+ INSERTED_DEVICE_POLICY,
+ APPLY_POLICY);
+ }
+ } else if (protection_level == G_DESKTOP_USB_PROTECTION_ALWAYS) {
+ if (g_strcmp0 (key, BLOCK) != 0) {
+ /* We are out of sync. */
+ params = g_variant_new ("(ss)",
+ INSERTED_DEVICE_POLICY,
+ BLOCK);
+ }
+ }
+
+ if (params != NULL) {
+ /* We are out of sync. We need to call setParameter to update USBGuard state */
+ if (manager->usb_protection != NULL) {
+ g_debug ("Setting InsertedDevicePolicy");
+ g_dbus_proxy_call (manager->usb_protection,
+ "setParameter",
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ dbus_call_log_error,
+ "Error calling USBGuard DBus while we were out of sync");
+ }
+
+ }
+
+ /* If we are in "When lockscreen is active" we also check
+ * if the "always allow" rule is present. */
+ if (protection_level == G_DESKTOP_USB_PROTECTION_LOCKSCREEN) {
+ g_debug ("Ensuring allow all");
+ usbguard_ensure_allow_rule (manager);
+ }
+}
+
+static void
+sync_usb_protection (GDBusProxy *proxy,
+ GsdUsbProtectionManager *manager)
+{
+ GVariant *params;
+ gboolean usbguard_controlled;
+ GSettings *settings = manager->settings;
+
+ usbguard_controlled = g_settings_get_boolean (settings, USB_PROTECTION);
+
+ g_debug ("Attempting to sync USB parameters: %d %p %p",
+ usbguard_controlled, proxy, manager->usb_protection);
+
+ if (!usbguard_controlled || manager->usb_protection == NULL)
+ return;
+
+ params = g_variant_new ("(s)", INSERTED_DEVICE_POLICY);
+ g_dbus_proxy_call (manager->usb_protection,
+ "getParameter",
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ get_parameter_cb,
+ manager);
+}
+
+static void
+usb_protection_properties_changed (GsdUsbProtectionManager *manager)
+{
+ GVariantBuilder props_builder;
+ GVariant *props_changed = NULL;
+
+ /* not yet connected to the session bus */
+ if (manager->connection == NULL)
+ return;
+
+ g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add (&props_builder, "{sv}", "Available",
+ g_variant_new_boolean (manager->available));
+
+ props_changed = g_variant_new ("(s@a{sv}@as)", GSD_USB_PROTECTION_DBUS_NAME,
+ g_variant_builder_end (&props_builder),
+ g_variant_new_strv (NULL, 0));
+
+ g_dbus_connection_emit_signal (manager->connection,
+ NULL,
+ GSD_USB_PROTECTION_DBUS_PATH,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ props_changed, NULL);
+}
+
+static void
+on_usb_protection_owner_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GsdUsbProtectionManager *manager = user_data;
+ GDBusProxy *proxy = G_DBUS_PROXY(object);
+ g_autofree gchar *name_owner = NULL;
+
+ name_owner = g_dbus_proxy_get_name_owner (proxy);
+ g_debug ("Got owner change: %s", name_owner);
+
+ if (name_owner) {
+ manager->available = TRUE;
+ } else {
+ manager->available = FALSE;
+ }
+
+ usb_protection_properties_changed (manager);
+}
+
+static void
+handle_screensaver_active (GsdUsbProtectionManager *manager,
+ GVariant *parameters)
+{
+ gboolean active;
+ gchar *value_usbguard;
+ gboolean usbguard_controlled;
+ GVariant *params;
+ GDesktopUsbProtection protection_level;
+ GSettings *settings = manager->settings;
+
+ usbguard_controlled = g_settings_get_boolean (settings, USB_PROTECTION);
+ protection_level = g_settings_get_enum (settings, USB_PROTECTION_LEVEL);
+
+ g_variant_get (parameters, "(b)", &active);
+ g_debug ("Received screensaver ActiveChanged signal: %d (old: %d)", active, manager->screensaver_active);
+ if (manager->screensaver_active != active) {
+ manager->screensaver_active = active;
+ if (usbguard_controlled && protection_level == G_DESKTOP_USB_PROTECTION_LOCKSCREEN) {
+ /* If we are in the "lockscreen protection" level we change
+ * the usbguard config with apply-policy or block if the session
+ * is unlocked or locked, respectively. */
+ value_usbguard = active ? BLOCK : APPLY_POLICY;
+ params = g_variant_new ("(ss)",
+ INSERTED_DEVICE_POLICY,
+ value_usbguard);
+ if (manager->usb_protection != NULL) {
+ g_dbus_proxy_call (manager->usb_protection,
+ "setParameter",
+ params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ dbus_call_log_error,
+ "Error calling USBGuard DBus to change the protection after a screensaver event");
+ }
+ }
+ }
+}
+
+static void
+screensaver_signal_cb (GDBusProxy *proxy,
+ const gchar *sender_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ g_debug ("ScreenSaver Signal: %s", signal_name);
+ if (g_strcmp0 (signal_name, "ActiveChanged") == 0)
+ handle_screensaver_active (GSD_USB_PROTECTION_MANAGER (user_data), parameters);
+}
+
+static void
+usb_protection_policy_proxy_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsdUsbProtectionManager *manager;
+ GDBusProxy *proxy;
+ g_autoptr(GError) error = NULL;
+ g_debug ("usb_protection_policy_proxy_ready");
+
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (!proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to contact USBGuard: %s", error->message);
+ return;
+ } else {
+ manager = GSD_USB_PROTECTION_MANAGER (user_data);
+ manager->usb_protection_policy = proxy;
+ g_debug ("Set protection policy proxy to %p", proxy);
+ sync_usb_protection (proxy, manager);
+ }
+}
+
+static void
+usb_protection_devices_proxy_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsdUsbProtectionManager *manager;
+ GDBusProxy *proxy;
+ g_autoptr(GError) error = NULL;
+
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (!proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to contact USBGuard: %s", error->message);
+ return;
+ }
+ manager = GSD_USB_PROTECTION_MANAGER (user_data);
+ manager->usb_protection_devices = proxy;
+
+ /* We don't care about already plugged in devices because they'll be
+ * already autorized by the "allow all" rule in USBGuard. */
+ g_debug ("Listening to signals");
+ g_signal_connect_object (source_object,
+ "g-signal",
+ G_CALLBACK (on_usbguard_signal),
+ user_data,
+ 0);
+}
+
+static void
+get_current_screen_saver_status (GsdUsbProtectionManager *manager)
+{
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->screensaver_proxy),
+ "GetActive",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ manager->cancellable,
+ &error);
+ if (ret == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to get screen saver status: %s", error->message);
+ return;
+ }
+ handle_screensaver_active (manager, ret);
+}
+
+static void
+usb_protection_proxy_ready (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsdUsbProtectionManager *manager;
+ GDBusProxy *proxy;
+ g_autofree gchar *name_owner = NULL;
+ g_autoptr(GError) error = NULL;
+
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (!proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to contact USBGuard: %s", error->message);
+ return;
+ }
+ manager = GSD_USB_PROTECTION_MANAGER (user_data);
+ manager->usb_protection = proxy;
+
+ g_signal_connect (G_OBJECT (manager->settings), "changed",
+ G_CALLBACK (settings_changed_callback), manager);
+
+ manager->screensaver_proxy = gnome_settings_bus_get_screen_saver_proxy ();
+ if (!manager->screensaver_proxy) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Failed to connect to screensaver service: %s", error->message);
+ g_clear_object (&manager->usb_protection);
+ return;
+ }
+
+ get_current_screen_saver_status (manager);
+
+ g_signal_connect (manager->screensaver_proxy, "g-signal",
+ G_CALLBACK (screensaver_signal_cb), manager);
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (proxy));
+
+ if (name_owner == NULL) {
+ g_debug("Probably USBGuard >= 0.7.5 is not currently installed.");
+ manager->available = FALSE;
+ } else {
+ manager->available = TRUE;
+ }
+
+ g_signal_connect_object (source_object,
+ "notify::g-name-owner",
+ G_CALLBACK (on_usb_protection_owner_changed_cb),
+ user_data,
+ 0);
+
+ g_signal_connect_object (source_object,
+ "g-signal",
+ G_CALLBACK (on_usb_protection_signal),
+ user_data,
+ 0);
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ USBGUARD_DBUS_NAME,
+ USBGUARD_DBUS_PATH_DEVICES,
+ USBGUARD_DBUS_INTERFACE_DEVICES,
+ manager->cancellable,
+ usb_protection_devices_proxy_ready,
+ manager);
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ USBGUARD_DBUS_NAME,
+ USBGUARD_DBUS_PATH_POLICY,
+ USBGUARD_DBUS_INTERFACE_POLICY,
+ manager->cancellable,
+ usb_protection_policy_proxy_ready,
+ manager);
+}
+
+static GVariant *
+handle_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ GsdUsbProtectionManager *manager = GSD_USB_PROTECTION_MANAGER (user_data);
+
+ /* Check session pointer as a proxy for whether the manager is in the
+ start or stop state */
+ if (manager->connection == NULL)
+ return NULL;
+
+ if (g_strcmp0 (property_name, "Available") == 0)
+ return g_variant_new_boolean (manager->available);
+
+ return NULL;
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+ NULL,
+ handle_get_property,
+ NULL
+};
+
+static void
+on_bus_gotten (GObject *source_object,
+ GAsyncResult *res,
+ GsdUsbProtectionManager *manager)
+{
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ connection = g_bus_get_finish (res, &error);
+ if (connection == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not get session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ manager->connection = connection;
+
+ g_dbus_connection_register_object (connection,
+ GSD_USB_PROTECTION_DBUS_PATH,
+ manager->introspection_data->interfaces[0],
+ &interface_vtable,
+ manager,
+ NULL,
+ NULL);
+
+ manager->name_id = g_bus_own_name_on_connection (connection,
+ GSD_USB_PROTECTION_DBUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+}
+
+static gboolean
+start_usb_protection_idle_cb (GsdUsbProtectionManager *manager)
+{
+ g_debug ("Starting USB protection manager");
+
+ manager->settings = g_settings_new (PRIVACY_SETTINGS);
+ manager->cancellable = g_cancellable_new ();
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ USBGUARD_DBUS_NAME,
+ USBGUARD_DBUS_PATH,
+ USBGUARD_DBUS_INTERFACE_VERSIONED,
+ manager->cancellable,
+ usb_protection_proxy_ready,
+ manager);
+
+ notify_init ("gnome-settings-daemon");
+
+ manager->start_idle_id = 0;
+
+ return FALSE;
+}
+
+gboolean
+gsd_usb_protection_manager_start (GsdUsbProtectionManager *manager,
+ GError **error)
+{
+ gnome_settings_profile_start (NULL);
+
+ manager->start_idle_id = g_idle_add ((GSourceFunc) start_usb_protection_idle_cb, manager);
+ g_source_set_name_by_id (manager->start_idle_id, "[gnome-settings-daemon] start_usbguard_idle_cb");
+
+ manager->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (manager->introspection_data != NULL);
+
+ /* Start process of owning a D-Bus name */
+ g_bus_get (G_BUS_TYPE_SESSION,
+ manager->cancellable,
+ (GAsyncReadyCallback) on_bus_gotten,
+ manager);
+
+ gnome_settings_profile_end (NULL);
+ return TRUE;
+}
+
+void
+gsd_usb_protection_manager_stop (GsdUsbProtectionManager *manager)
+{
+ g_debug ("Stopping USB protection manager");
+
+ if (manager->cancellable != NULL) {
+ g_cancellable_cancel (manager->cancellable);
+ g_clear_object (&manager->cancellable);
+ }
+
+ g_clear_object (&manager->notification);
+
+ if (manager->start_idle_id != 0) {
+ g_source_remove (manager->start_idle_id);
+ manager->start_idle_id = 0;
+ }
+
+ if (manager->name_id != 0) {
+ g_bus_unown_name (manager->name_id);
+ manager->name_id = 0;
+ }
+
+ g_clear_pointer (&manager->introspection_data, g_dbus_node_info_unref);
+ g_clear_object (&manager->connection);
+ g_clear_object (&manager->settings);
+ g_clear_object (&manager->usb_protection);
+ g_clear_object (&manager->usb_protection_devices);
+ g_clear_object (&manager->usb_protection_policy);
+ g_clear_object (&manager->screensaver_proxy);
+}
+
+static void
+gsd_usb_protection_manager_class_init (GsdUsbProtectionManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gsd_usb_protection_manager_finalize;
+}
+
+static void
+gsd_usb_protection_manager_init (GsdUsbProtectionManager *manager)
+{
+}
+
+static void
+gsd_usb_protection_manager_finalize (GObject *object)
+{
+ GsdUsbProtectionManager *usb_protection_manager;
+
+ usb_protection_manager = GSD_USB_PROTECTION_MANAGER (object);
+ gsd_usb_protection_manager_stop (usb_protection_manager);
+
+ G_OBJECT_CLASS (gsd_usb_protection_manager_parent_class)->finalize (object);
+}
+
+GsdUsbProtectionManager *
+gsd_usb_protection_manager_new (void)
+{
+ g_debug ("Starting USB Protection");
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ manager_object = g_object_new (GSD_TYPE_USB_PROTECTION_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ }
+
+ return GSD_USB_PROTECTION_MANAGER (manager_object);
+}