From df5f01c6ed0c20d269f7239901d21883cc871bbb Mon Sep 17 00:00:00 2001
From: Sebastian Pipping <sebastian@pipping.org>
Date: Wed, 9 Feb 2022 02:10:40 +0100
Subject: [PATCH] dbus: Add missing checks for authorization using Polkit

---
 configure.ac            |   2 +-
 src/DBus/DBusBridge.cpp | 137 ++++++++++++++++++++++++++++++++++++++++
 src/DBus/DBusBridge.hpp |   2 +
 3 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index f565b87..a45174a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -405,7 +405,7 @@ if test "x$with_dbus" = xyes; then
   #
   # Check for required D-Bus modules 
   #
-  PKG_CHECK_MODULES([dbus], [dbus-1 dbus-glib-1 >= 0.100 gio-2.0],
+  PKG_CHECK_MODULES([dbus], [dbus-1 gio-2.0 polkit-gobject-1],
   [AC_DEFINE([HAVE_DBUS], [1], [Required GLib DBus API available])
   dbus_summary="system-wide; $dbus_CFLAGS $dbus_LIBS"],
   [AC_MSG_FAILURE([Required D-Bus modules (dbus-1, dbus-glib-1, gio-2.0) not found!])]
diff --git a/src/DBus/DBusBridge.cpp b/src/DBus/DBusBridge.cpp
index f9209f7..696d906 100644
--- a/src/DBus/DBusBridge.cpp
+++ b/src/DBus/DBusBridge.cpp
@@ -15,12 +15,14 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 // Authors: Daniel Kopecek <dkopecek@redhat.com>
+// Authors: Sebastian Pipping <sebastian@pipping.org>
 //
 #ifdef HAVE_BUILD_CONFIG_H
   #include <build-config.h>
 #endif
 
 #include "DBusBridge.hpp"
+#include <polkit/polkit.h>
 
 namespace usbguard
 {
@@ -78,6 +80,10 @@ namespace usbguard
   void DBusBridge::handleRootMethodCall(const std::string& method_name, GVariant* parameters, GDBusMethodInvocation* invocation)
   {
     if (method_name == "getParameter") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       const char* name_cstr = nullptr;
       g_variant_get(parameters, "(&s)", &name_cstr);
       std::string name(name_cstr);
@@ -87,6 +93,10 @@ namespace usbguard
     }
 
     if (method_name == "setParameter") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       const char* name_cstr = nullptr;
       const char* value_cstr = nullptr;
       g_variant_get(parameters, "(&s&s)", &name_cstr, &value_cstr);
@@ -105,6 +115,10 @@ namespace usbguard
   void DBusBridge::handlePolicyMethodCall(const std::string& method_name, GVariant* parameters, GDBusMethodInvocation* invocation)
   {
     if (method_name == "listRules") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       const char* query_cstr = nullptr;
       g_variant_get(parameters, "(&s)", &query_cstr);
       std::string query(query_cstr);
@@ -138,6 +152,10 @@ namespace usbguard
     }
 
     if (method_name == "appendRule") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       const char* rule_spec_cstr = nullptr;
       uint32_t parent_id = 0;
       g_variant_get(parameters, "(&su)", &rule_spec_cstr, &parent_id);
@@ -148,6 +166,10 @@ namespace usbguard
     }
 
     if (method_name == "removeRule") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       uint32_t rule_id = 0;
       g_variant_get(parameters, "(u)", &rule_id);
       removeRule(rule_id);
@@ -164,6 +186,10 @@ namespace usbguard
     GDBusMethodInvocation* invocation)
   {
     if (method_name == "listDevices") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       const char* query_cstr = nullptr;
       g_variant_get(parameters, "(&s)", &query_cstr);
       std::string query(query_cstr);
@@ -196,6 +222,10 @@ namespace usbguard
     }
 
     if (method_name == "applyDevicePolicy") {
+      if (! isAuthorizedByPolkit(invocation)) {
+	return;
+      }
+
       uint32_t device_id = 0;
       uint32_t target_integer = 0;
       gboolean permanent = false;
@@ -327,6 +357,113 @@ namespace usbguard
       with_interface_string.c_str());
     return builder;
   }
+
+  std::string DBusBridge::formatGError(GError* error)
+  {
+    if (error) {
+      std::stringstream formatGError;
+      formatGError << error->message << " (code " << error->code << ")";
+      return formatGError.str();
+    }
+    else {
+      return "unknown error";
+    }
+  }
+
+  bool DBusBridge::isAuthorizedByPolkit(GDBusMethodInvocation* invocation)
+  {
+    GError* error = NULL;
+    USBGUARD_LOG(Trace) << "Extracting bus name...";
+    const gchar* const /*no-free!*/ bus_name = g_dbus_method_invocation_get_sender (invocation);
+
+    if (! bus_name) {
+      USBGUARD_LOG(Trace) << "Failed to extract bus name.";
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Extracted bus name \"" << bus_name << "\".";
+    USBGUARD_LOG(Trace) << "Extracting interface name...";
+    const gchar* const /*no-free!*/ interfaceName = g_dbus_method_invocation_get_interface_name(invocation);
+
+    if (! interfaceName) {
+      USBGUARD_LOG(Trace) << "Failed to extract interface name.";
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Extracted interface name \"" << interfaceName << "\".";
+    USBGUARD_LOG(Trace) << "Extracting method name...";
+    const gchar* const /*no-free!*/ methodName = g_dbus_method_invocation_get_method_name(invocation);
+
+    if (! methodName) {
+      USBGUARD_LOG(Trace) << "Failed to extract method name.";
+      return false;
+    }
+
+    std::stringstream action_id;
+    action_id << interfaceName << "." << methodName;
+    USBGUARD_LOG(Trace) << "Extracted method name \"" << methodName << "\".";
+    USBGUARD_LOG(Trace) << "Creating a system bus Polkit subject...";
+    PolkitSubject* const subject = polkit_system_bus_name_new(bus_name);
+
+    if (! subject) {
+      USBGUARD_LOG(Trace) << "Failed to create Polkit subject.";
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Created.";
+    USBGUARD_LOG(Trace) << "Connecting with Polkit authority...";
+    PolkitAuthority* const authority = polkit_authority_get_sync(/*cancellable=*/ NULL, &error);
+
+    if (! authority || error) {
+      USBGUARD_LOG(Trace) << "Failed to connect to Polkit authority: " << formatGError(error) << ".";
+      g_error_free(error);
+      g_object_unref(authority);
+      g_object_unref(subject);
+      return false;
+    }
+
+    USBGUARD_LOG(Trace) << "Connected.";
+    USBGUARD_LOG(Trace) << "Customizing Polkit authentification dialog...";
+    PolkitDetails* const details = polkit_details_new();
+
+    if (! details) {
+      USBGUARD_LOG(Trace) << "Failed to customize the Polkit authentification dialog.";
+      g_object_unref(authority);
+      g_object_unref(subject);
+      return false;
+    }
+
+    polkit_details_insert (details, "polkit.message", "This USBGuard action needs authorization");
+    USBGUARD_LOG(Trace) << "Customized.";
+    USBGUARD_LOG(Trace) << "Checking authorization of action \"" << action_id.str() << "\" with Polkit ...";
+    const PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
+    PolkitAuthorizationResult* const result = polkit_authority_check_authorization_sync
+      (authority,
+        subject,
+	action_id.str().c_str(),
+	details,
+	flags,
+	/*cancellable=*/ NULL,
+	&error);
+
+    if (! result || error) {
+      USBGUARD_LOG(Trace) << "Failed to check back with Polkit for authoriation: " << formatGError(error) << ".";
+      g_error_free(error);
+      g_object_unref(result);
+      g_object_unref(details);
+      g_object_unref(authority);
+      g_object_unref(subject);
+      return false;
+    }
+
+    gboolean isAuthorized = polkit_authorization_result_get_is_authorized(result);
+    USBGUARD_LOG(Trace) << (isAuthorized ? "Authorized" : "Not authorized") << ".";
+    g_object_unref(result);
+    g_object_unref(details);
+    g_object_unref(authority);
+    g_object_unref(subject);
+    return isAuthorized;
+  }
 } /* namespace usbguard */
 
 /* vim: set ts=2 sw=2 et */
diff --git a/src/DBus/DBusBridge.hpp b/src/DBus/DBusBridge.hpp
index bb9d96a..838ab34 100644
--- a/src/DBus/DBusBridge.hpp
+++ b/src/DBus/DBusBridge.hpp
@@ -71,6 +71,8 @@ namespace usbguard
       bool rule_match,
       uint32_t rule_id);
 
+    static std::string formatGError(GError* error);
+    static bool isAuthorizedByPolkit(GDBusMethodInvocation* invocation);
 
     GDBusConnection* const p_gdbus_connection;
     void(*p_ipc_callback)(bool);
-- 
2.23.0

