mirror of
https://github.com/ublue-os/bazzite.git
synced 2025-01-01 12:02:58 +00:00
352 lines
14 KiB
Diff
352 lines
14 KiB
Diff
From ae24405030d6759f209a19d2eedaf5060715701a Mon Sep 17 00:00:00 2001
|
|
From: Alberto Garcia <berto@igalia.com>
|
|
Date: Wed, 8 Mar 2023 18:00:34 +0100
|
|
Subject: [PATCH] Extend Filesystem.Mount() to allow mounting on behalf of
|
|
another user
|
|
|
|
Filesystems are mounted for the user who makes the D-Bus API call.
|
|
This has an effect on a few things, notably who has permissions
|
|
to unmount it later and also the path of the mount point, usually in
|
|
the form of /run/media/USER/LABEL
|
|
|
|
This patch adds a new parameter to the Filesystem.Mount() method that
|
|
allows mounting a filesystem on behalf of a different user.
|
|
|
|
Fixes #1065
|
|
|
|
(cherry picked from commit 2fddf2b951b993303fe07813ad2398ab91937911)
|
|
---
|
|
data/org.freedesktop.UDisks2.policy.in | 11 +++
|
|
data/org.freedesktop.UDisks2.xml | 8 ++-
|
|
src/tests/dbus-tests/test_80_filesystem.py | 80 ++++++++++++++++++++++
|
|
src/udisksdaemonutil.c | 53 ++++++++++++++
|
|
src/udisksdaemonutil.h | 6 ++
|
|
src/udiskslinuxfilesystem.c | 60 ++++++++++++----
|
|
6 files changed, 202 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/data/org.freedesktop.UDisks2.policy.in b/data/org.freedesktop.UDisks2.policy.in
|
|
index 4b02f46f..816620d7 100644
|
|
--- a/data/org.freedesktop.UDisks2.policy.in
|
|
+++ b/data/org.freedesktop.UDisks2.policy.in
|
|
@@ -44,6 +44,17 @@
|
|
</defaults>
|
|
</action>
|
|
|
|
+ <!-- mount a device on behalf of another user -->
|
|
+ <action id="org.freedesktop.udisks2.filesystem-mount-other-user">
|
|
+ <description>Mount a filesystem on behalf of another user</description>
|
|
+ <message>Authentication is required to mount the filesystem</message>
|
|
+ <defaults>
|
|
+ <allow_any>auth_admin</allow_any>
|
|
+ <allow_inactive>auth_admin</allow_inactive>
|
|
+ <allow_active>auth_admin_keep</allow_active>
|
|
+ </defaults>
|
|
+ </action>
|
|
+
|
|
<!-- mount a device referenced in the /etc/fstab file with the x-udisks-auth option -->
|
|
<action id="org.freedesktop.udisks2.filesystem-fstab">
|
|
<description>Mount/unmount filesystems defined in the fstab file with the x-udisks-auth option</description>
|
|
diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml
|
|
index a3ea4901..2a1704ee 100644
|
|
--- a/data/org.freedesktop.UDisks2.xml
|
|
+++ b/data/org.freedesktop.UDisks2.xml
|
|
@@ -1853,7 +1853,7 @@
|
|
|
|
<!--
|
|
Mount:
|
|
- @options: Options - known options (in addition to <link linkend="udisks-std-options">standard options</link>) includes <parameter>fstype</parameter> (of type 's') and <parameter>options</parameter> (of type 's').
|
|
+ @options: Options - known options (in addition to <link linkend="udisks-std-options">standard options</link>) includes <parameter>fstype</parameter> (of type 's'), <parameter>as-user</parameter> (of type 's') and <parameter>options</parameter> (of type 's').
|
|
@mount_path: The filesystem path where the device was mounted.
|
|
|
|
Mounts the filesystem.
|
|
@@ -1875,6 +1875,12 @@
|
|
filesystem types are validated against a (small) whitelist to
|
|
avoid unexpected privilege escalation
|
|
|
|
+ If the <parameter>as-user</parameter> option is set, the
|
|
+ filesystem is mounted on behalf of the specified user instead
|
|
+ of the calling one. This has usually an effect on the returned
|
|
+ @mount_path and it also allows that user to unmount the
|
|
+ filesystem later. This option expects a user name, not a UID.
|
|
+
|
|
If the device in question is referenced in the
|
|
<filename>/etc/fstab</filename> file, the
|
|
<command>mount</command> command is called directly (as root)
|
|
diff --git a/src/tests/dbus-tests/test_80_filesystem.py b/src/tests/dbus-tests/test_80_filesystem.py
|
|
index c16d32ca..babc85f3 100644
|
|
--- a/src/tests/dbus-tests/test_80_filesystem.py
|
|
+++ b/src/tests/dbus-tests/test_80_filesystem.py
|
|
@@ -648,6 +648,86 @@ class UdisksFSTestCase(udiskstestcase.UdisksTestCase):
|
|
|
|
|
|
|
|
+ def _unmount_as_user(self, pipe, uid, gid, device):
|
|
+ """ Try to unmount @device as user with given @uid and @gid."""
|
|
+ os.setresgid(gid, gid, gid)
|
|
+ os.setresuid(uid, uid, uid)
|
|
+
|
|
+ try:
|
|
+ safe_dbus.call_sync(self.iface_prefix,
|
|
+ self.path_prefix + '/block_devices/' + os.path.basename(device),
|
|
+ self.iface_prefix + '.Filesystem',
|
|
+ 'Unmount',
|
|
+ GLib.Variant('(a{sv})', ({},)))
|
|
+ except Exception as e:
|
|
+ pipe.send([False, 'Unmount DBus call failed: %s' % str(e)])
|
|
+ pipe.close()
|
|
+ return
|
|
+
|
|
+ pipe.send([True, ''])
|
|
+ pipe.close()
|
|
+
|
|
+ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE)
|
|
+ def test_mount_as_user(self):
|
|
+ """ Test mounting a filesystem on behalf of a different user."""
|
|
+ if not self._can_create:
|
|
+ self.skipTest('Cannot create %s filesystem' % self._fs_name)
|
|
+
|
|
+ if not self._can_label:
|
|
+ self.skipTest('Cannot set label when creating %s filesystem' % self._fs_name)
|
|
+
|
|
+ if not self._can_mount:
|
|
+ self.skipTest('Cannot mount %s filesystem' % self._fs_name)
|
|
+
|
|
+ disk = self.get_object('/block_devices/' + os.path.basename(self.vdevs[0]))
|
|
+ self.assertIsNotNone(disk)
|
|
+
|
|
+ # create filesystem with label
|
|
+ d = dbus.Dictionary(signature='sv')
|
|
+ d['label'] = 'UDISKS'
|
|
+ disk.Format(self._fs_name, d, dbus_interface=self.iface_prefix + '.Block')
|
|
+ self.addCleanup(self.wipe_fs, self.vdevs[0])
|
|
+
|
|
+ # get real block object for the newly created filesystem
|
|
+ block_fs, block_fs_dev = self._get_formatted_block_object(self.vdevs[0])
|
|
+ self.assertIsNotNone(block_fs)
|
|
+ self.assertIsNotNone(block_fs_dev)
|
|
+
|
|
+ # create user for our test
|
|
+ self.addCleanup(self._remove_user, self.username)
|
|
+ uid, gid = self._add_user(self.username)
|
|
+
|
|
+ # prepare the mount options
|
|
+ d = dbus.Dictionary(signature='sv')
|
|
+ d['options'] = 'ro'
|
|
+ if self._fs_name:
|
|
+ d['fstype'] = self._fs_name
|
|
+
|
|
+ # try to mount it now: first with a nonexistent user
|
|
+ d['as-user'] = 'nonexistent'
|
|
+ with six.assertRaisesRegex(self, dbus.exceptions.DBusException, "User with name nonexistent does not exist"):
|
|
+ block_fs.Mount(d, dbus_interface=self.iface_prefix + '.Filesystem')
|
|
+
|
|
+ # now mount it with the actual user
|
|
+ d['as-user'] = self.username
|
|
+ mnt_path = block_fs.Mount(d, dbus_interface=self.iface_prefix + '.Filesystem')
|
|
+ self.addCleanup(self.try_unmount, block_fs_dev)
|
|
+ self.addCleanup(self.try_unmount, self.vdevs[0])
|
|
+
|
|
+ # check that the name of the mount point matches the expectation
|
|
+ self.assertTrue(mnt_path.endswith("%s/UDISKS" % self.username))
|
|
+
|
|
+ # try to unmount it as the other user
|
|
+ parent_conn, child_conn = Pipe()
|
|
+ proc = Process(target=self._unmount_as_user, args=(child_conn, int(uid), int(gid), block_fs_dev))
|
|
+ proc.start()
|
|
+ res = parent_conn.recv()
|
|
+ parent_conn.close()
|
|
+ proc.join()
|
|
+
|
|
+ if not res[0]:
|
|
+ self.fail(res[1])
|
|
+
|
|
class Ext2TestCase(UdisksFSTestCase):
|
|
_fs_name = 'ext2'
|
|
_can_create = True and UdisksFSTestCase.command_exists('mke2fs')
|
|
diff --git a/src/udisksdaemonutil.c b/src/udisksdaemonutil.c
|
|
index 1695b524..f7cc865d 100644
|
|
--- a/src/udisksdaemonutil.c
|
|
+++ b/src/udisksdaemonutil.c
|
|
@@ -1080,6 +1080,59 @@ out:
|
|
return FALSE;
|
|
}
|
|
|
|
+/**
|
|
+ * udisks_daemon_util_get_user_info_by_name:
|
|
+ * @out_uid: (out) (allow-none): Return location for resolved uid or %NULL.
|
|
+ * @out_gid: (out) (allow-none): Return location for resolved gid or %NULL.
|
|
+ * @error: Return location for error.
|
|
+ *
|
|
+ * Gets the UNIX id and group for a user name.
|
|
+ *
|
|
+ * Returns: %TRUE if the user information was obtained, %FALSE otherwise
|
|
+ */
|
|
+gboolean
|
|
+udisks_daemon_util_get_user_info_by_name (const gchar *user_name,
|
|
+ uid_t *out_uid,
|
|
+ gid_t *out_gid,
|
|
+ GError **error)
|
|
+{
|
|
+ struct passwd pwstruct;
|
|
+ gchar pwbuf[8192];
|
|
+ struct passwd *pw = NULL;
|
|
+ int rc;
|
|
+
|
|
+ g_return_val_if_fail (user_name != NULL, FALSE);
|
|
+
|
|
+ rc = getpwnam_r (user_name, &pwstruct, pwbuf, sizeof pwbuf, &pw);
|
|
+ if (rc == 0 && pw == NULL)
|
|
+ {
|
|
+ g_set_error (error,
|
|
+ UDISKS_ERROR,
|
|
+ UDISKS_ERROR_FAILED,
|
|
+ "User with name %s does not exist", user_name);
|
|
+ goto out;
|
|
+ }
|
|
+ else if (pw == NULL)
|
|
+ {
|
|
+ g_set_error (error,
|
|
+ UDISKS_ERROR,
|
|
+ UDISKS_ERROR_FAILED,
|
|
+ "Error looking up passwd struct for user %s: %m", user_name);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (out_uid != NULL)
|
|
+ *out_uid = pw->pw_uid;
|
|
+
|
|
+ if (out_gid != NULL)
|
|
+ *out_gid = pw->pw_gid;
|
|
+
|
|
+ return TRUE;
|
|
+
|
|
+out:
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
/**
|
|
* udisks_daemon_util_get_caller_uid_sync:
|
|
* @daemon: A #UDisksDaemon.
|
|
diff --git a/src/udisksdaemonutil.h b/src/udisksdaemonutil.h
|
|
index df584de4..cd263c76 100644
|
|
--- a/src/udisksdaemonutil.h
|
|
+++ b/src/udisksdaemonutil.h
|
|
@@ -94,6 +94,12 @@ gboolean udisks_daemon_util_get_user_info (const uid_t uid,
|
|
gchar **out_user_name,
|
|
GError **error);
|
|
|
|
+gboolean
|
|
+udisks_daemon_util_get_user_info_by_name (const gchar *user_name,
|
|
+ uid_t *out_uid,
|
|
+ gid_t *out_gid,
|
|
+ GError **error);
|
|
+
|
|
gboolean udisks_daemon_util_get_caller_uid_sync (UDisksDaemon *daemon,
|
|
GDBusMethodInvocation *invocation,
|
|
GCancellable *cancellable,
|
|
diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c
|
|
index 619e0b85..92d3ab37 100644
|
|
--- a/src/udiskslinuxfilesystem.c
|
|
+++ b/src/udiskslinuxfilesystem.c
|
|
@@ -795,6 +795,7 @@ handle_mount (UDisksFilesystem *filesystem,
|
|
UDisksBlock *block;
|
|
UDisksDaemon *daemon;
|
|
UDisksState *state = NULL;
|
|
+ gchar *opt_as_user = NULL;
|
|
uid_t caller_uid;
|
|
gid_t caller_gid;
|
|
const gchar * const *existing_mount_points;
|
|
@@ -824,6 +825,14 @@ handle_mount (UDisksFilesystem *filesystem,
|
|
goto out;
|
|
}
|
|
|
|
+ if (options != NULL)
|
|
+ {
|
|
+ g_variant_lookup (options,
|
|
+ "as-user",
|
|
+ "&s",
|
|
+ &opt_as_user);
|
|
+ }
|
|
+
|
|
block = udisks_object_peek_block (object);
|
|
daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
|
|
state = udisks_daemon_get_state (daemon);
|
|
@@ -862,22 +871,35 @@ handle_mount (UDisksFilesystem *filesystem,
|
|
goto out;
|
|
}
|
|
|
|
- if (!udisks_daemon_util_get_caller_uid_sync (daemon,
|
|
- invocation,
|
|
- NULL /* GCancellable */,
|
|
- &caller_uid,
|
|
- &error))
|
|
+ if (opt_as_user)
|
|
{
|
|
- g_dbus_method_invocation_return_gerror (invocation, error);
|
|
- g_clear_error (&error);
|
|
- goto out;
|
|
+ if (!udisks_daemon_util_get_user_info_by_name (opt_as_user, &caller_uid, &caller_gid, &error))
|
|
+ {
|
|
+ g_dbus_method_invocation_return_gerror (invocation, error);
|
|
+ g_clear_error (&error);
|
|
+ goto out;
|
|
+ }
|
|
+ caller_user_name = g_strdup (opt_as_user);
|
|
}
|
|
+ else
|
|
+ {
|
|
+ if (!udisks_daemon_util_get_caller_uid_sync (daemon,
|
|
+ invocation,
|
|
+ NULL /* GCancellable */,
|
|
+ &caller_uid,
|
|
+ &error))
|
|
+ {
|
|
+ g_dbus_method_invocation_return_gerror (invocation, error);
|
|
+ g_clear_error (&error);
|
|
+ goto out;
|
|
+ }
|
|
|
|
- if (!udisks_daemon_util_get_user_info (caller_uid, &caller_gid, &caller_user_name, &error))
|
|
- {
|
|
- g_dbus_method_invocation_return_gerror (invocation, error);
|
|
- g_clear_error (&error);
|
|
- goto out;
|
|
+ if (!udisks_daemon_util_get_user_info (caller_uid, &caller_gid, &caller_user_name, &error))
|
|
+ {
|
|
+ g_dbus_method_invocation_return_gerror (invocation, error);
|
|
+ g_clear_error (&error);
|
|
+ goto out;
|
|
+ }
|
|
}
|
|
|
|
if (system_managed)
|
|
@@ -896,7 +918,11 @@ handle_mount (UDisksFilesystem *filesystem,
|
|
* will be replaced by the name of the drive/device in question
|
|
*/
|
|
message = N_("Authentication is required to mount $(drive)");
|
|
- if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
|
|
+ if (opt_as_user != NULL)
|
|
+ {
|
|
+ action_id = "org.freedesktop.udisks2.filesystem-mount-other-user";
|
|
+ }
|
|
+ else if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
|
|
{
|
|
if (udisks_block_get_hint_system (block))
|
|
{
|
|
@@ -1078,7 +1104,11 @@ handle_mount (UDisksFilesystem *filesystem,
|
|
* will be replaced by the name of the drive/device in question
|
|
*/
|
|
message = N_("Authentication is required to mount $(drive)");
|
|
- if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
|
|
+ if (opt_as_user != NULL)
|
|
+ {
|
|
+ action_id = "org.freedesktop.udisks2.filesystem-mount-other-user";
|
|
+ }
|
|
+ else if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
|
|
{
|
|
if (udisks_block_get_hint_system (block))
|
|
{
|
|
--
|
|
2.30.2
|
|
|