diff --git a/udisks-2.11.2-mount_as_user_auth.patch b/udisks-2.11.2-mount_as_user_auth.patch new file mode 100644 index 0000000..f61f347 --- /dev/null +++ b/udisks-2.11.2-mount_as_user_auth.patch @@ -0,0 +1,889 @@ +From 6605473b3e47a1b419c434f87e9c21aaf0be9783 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Tue, 28 Apr 2026 19:10:09 +0200 +Subject: [PATCH 1/5] udiskslinuxfilesystem: Separate real caller identity from + as-user target +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When the 'as-user' option is specified in Filesystem.Mount(), the code +previously overwrote caller_uid/caller_gid with the target user's identity. +This meant downstream functions received a uid they believed was the +D-Bus caller's, but was actually the target's. + +Introduce effective_uid/effective_gid/effective_user_name to hold the +as-user target identity (or the caller's identity when as-user is not +set). The real D-Bus caller identity is now always resolved into +caller_uid/caller_gid and used for authorization-related decisions +(setup_by_user, on_user_seat checks), while effective_* is used for +mount point calculation, run_as_uid/run_as_gid, and state tracking. + +Reported-by: Azizcan Daştan +Reported-by: Özlem Ozan +Co-Authored-By: Claude Opus 4.6 +--- + src/udiskslinuxfilesystem.c | 93 +++++++++++++++++++++---------------- + 1 file changed, 53 insertions(+), 40 deletions(-) + +diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c +index 4f03c17e..41bdc317 100644 +--- a/src/udiskslinuxfilesystem.c ++++ b/src/udiskslinuxfilesystem.c +@@ -906,7 +906,8 @@ handle_mount_fstab (UDisksDaemon *daemon, + UDisksObject *object, + uid_t caller_uid, + gid_t caller_gid, +- gboolean mount_other_user, ++ uid_t effective_uid, ++ gid_t effective_gid, + const gchar *mount_point_to_use, + const gchar *fstab_mount_options, + GDBusMethodInvocation *invocation, +@@ -937,7 +938,7 @@ handle_mount_fstab (UDisksDaemon *daemon, + * will be replaced by the name of the drive/device in question + */ + message = N_("Authentication is required to mount $(drive)"); +- if (mount_other_user) ++ if (caller_uid != effective_uid) + { + action_id = "org.freedesktop.udisks2.filesystem-mount-other-user"; + } +@@ -982,15 +983,15 @@ handle_mount_fstab (UDisksDaemon *daemon, + job = udisks_daemon_launch_simple_job (daemon, + UDISKS_OBJECT (object), + "filesystem-mount", +- mount_fstab_as_root ? 0 : caller_uid, ++ mount_fstab_as_root ? 0 : effective_uid, + FALSE, + NULL /* cancellable */); + + /* XXX: using run_as_uid for root doesn't work even if the caller is already root */ +- if (!mount_fstab_as_root && caller_uid != 0) ++ if (!mount_fstab_as_root && effective_uid != 0) + { +- BDExtraArg uid_arg = { g_strdup ("run_as_uid"), g_strdup_printf ("%d", caller_uid) }; +- BDExtraArg gid_arg = { g_strdup ("run_as_gid"), g_strdup_printf ("%d", caller_gid) }; ++ BDExtraArg uid_arg = { g_strdup ("run_as_uid"), g_strdup_printf ("%d", effective_uid) }; ++ BDExtraArg gid_arg = { g_strdup ("run_as_gid"), g_strdup_printf ("%d", effective_gid) }; + const BDExtraArg *extra_args[3] = { &uid_arg, &gid_arg, NULL }; + + success = bd_fs_mount (NULL, mount_point_to_use, NULL, NULL, extra_args, &error); +@@ -1068,8 +1069,9 @@ handle_mount_dynamic (UDisksDaemon *daemon, + UDisksObject *object, + uid_t caller_uid, + gid_t caller_gid, +- const gchar *caller_user_name, +- gboolean mount_other_user, ++ uid_t effective_uid, ++ gid_t effective_gid, ++ const gchar *effective_user_name, + gchar **mount_point_to_use, + gboolean *mpoint_persistent, + GDBusMethodInvocation *invocation, +@@ -1129,7 +1131,7 @@ handle_mount_dynamic (UDisksDaemon *daemon, + * will be replaced by the name of the drive/device in question + */ + message = N_("Authentication is required to mount $(drive)"); +- if (mount_other_user) ++ if (caller_uid != effective_uid) + { + action_id = "org.freedesktop.udisks2.filesystem-mount-other-user"; + } +@@ -1163,9 +1165,9 @@ handle_mount_dynamic (UDisksDaemon *daemon, + /* Calculate mount point (guaranteed to be valid UTF-8) */ + *mount_point_to_use = calculate_mount_point (daemon, + block, +- caller_uid, +- caller_gid, +- caller_user_name, ++ effective_uid, ++ effective_gid, ++ effective_user_name, + fs_type_to_use, + mpoint_persistent, + &error); +@@ -1180,7 +1182,7 @@ handle_mount_dynamic (UDisksDaemon *daemon, + /* Calculate mount options (guaranteed to be valid UTF-8) */ + mount_options = udisks_linux_calculate_mount_options (daemon, + block, +- caller_uid, ++ effective_uid, + fs_signature, + fs_type_to_use, + options, +@@ -1261,14 +1263,16 @@ handle_mount (UDisksFilesystem *filesystem, + UDisksBlock *block; + UDisksDaemon *daemon; + UDisksState *state = NULL; +- gchar *opt_as_user = NULL; ++ const gchar *opt_as_user = NULL; + uid_t caller_uid; + gid_t caller_gid; ++ uid_t effective_uid = 0; ++ gid_t effective_gid = 0; ++ gchar *effective_user_name = NULL; + const gchar * const *existing_mount_points; + gchar *mount_point_to_use = NULL; + gboolean mpoint_persistent = TRUE; + gchar *fstab_mount_options = NULL; +- gchar *caller_user_name = NULL; + GError *error = NULL; + gboolean system_managed = FALSE; + gchar *device = NULL; +@@ -1326,35 +1330,42 @@ handle_mount (UDisksFilesystem *filesystem, + goto out; + } + ++ /* Always resolve the real D-Bus caller identity */ ++ 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, ++ opt_as_user ? NULL : &effective_user_name, ++ &error)) ++ { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ g_clear_error (&error); ++ goto out; ++ } ++ + if (opt_as_user) + { +- if (!udisks_daemon_util_get_user_info_by_name (opt_as_user, &caller_uid, &caller_gid, &error)) ++ if (!udisks_daemon_util_get_user_info_by_name (opt_as_user, &effective_uid, &effective_gid, &error)) + { + g_dbus_method_invocation_return_gerror (invocation, error); + g_clear_error (&error); + goto out; + } +- caller_user_name = g_strdup (opt_as_user); ++ effective_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; +- } ++ effective_uid = caller_uid; ++ effective_gid = caller_gid; + } + + /* Mount it */ +@@ -1364,7 +1375,8 @@ handle_mount (UDisksFilesystem *filesystem, + object, + caller_uid, + caller_gid, +- opt_as_user != NULL, ++ effective_uid, ++ effective_gid, + mount_point_to_use, + fstab_mount_options, + invocation, +@@ -1377,8 +1389,9 @@ handle_mount (UDisksFilesystem *filesystem, + object, + caller_uid, + caller_gid, +- caller_user_name, +- opt_as_user != NULL, ++ effective_uid, ++ effective_gid, ++ effective_user_name, + &mount_point_to_use, + &mpoint_persistent, + invocation, +@@ -1390,7 +1403,7 @@ handle_mount (UDisksFilesystem *filesystem, + udisks_state_add_mounted_fs (state, + mount_point_to_use, + udisks_block_get_device_number (block), +- caller_uid, ++ effective_uid, + system_managed, + system_managed ? FALSE : mpoint_persistent); + +@@ -1398,7 +1411,7 @@ handle_mount (UDisksFilesystem *filesystem, + device, + system_managed ? " (system)" : "", + mount_point_to_use, +- caller_uid); ++ effective_uid); + + udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object), + UDISKS_DEFAULT_WAIT_TIMEOUT); +@@ -1412,7 +1425,7 @@ handle_mount (UDisksFilesystem *filesystem, + udisks_state_check (state); + g_free (mount_point_to_use); + g_free (fstab_mount_options); +- g_free (caller_user_name); ++ g_free (effective_user_name); + g_free (device); + g_clear_object (&object); + +-- +2.54.0 + + +From 07d083bf0f3213e68229f1fc8668952a719b816a Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Tue, 28 Apr 2026 19:29:14 +0200 +Subject: [PATCH 2/5] udiskslinuxfilesystem: Rework fstab mount authorization + for as-user +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The polkit authorization logic in handle_mount_fstab() had two problems +when 'as-user' was combined with fstab entries containing 'user', +'users' or 'x-udisks-auth' mount options: + +1. The 'filesystem-mount-other-user' polkit check was inside the code + block that is skipped when 'user'/'users'/'x-udisks-auth' options + are present, allowing an unprivileged caller to mount on behalf of + another user without any authorization. + +2. When 'user' or 'users' fstab options were present and the initial + mount attempt failed with a permission error, the code would fall + back to mounting as root. This fallback should only be available + when 'x-udisks-auth' is explicitly specified. + +Restructure the authorization flow so that: + - The 'filesystem-mount-other-user' check is evaluated first, + independently of fstab mount options, and is always enforced + when caller_uid != effective_uid. + - The root fallback on BD_FS_ERROR_AUTH is gated on 'x-udisks-auth' + being present, not just any of the user-level mount options. + - When root uses 'as-user' without 'user'/'users' fstab options, + the root fallback is implicitly enabled. + - Regular polkit checks (filesystem-mount, mount-system, + mount-other-seat) only apply for same-user mounts without + 'user'/'users'/'x-udisks-auth'. + +Update the D-Bus API documentation to reflect the new behavior. + +Reported-by: Azizcan Daştan +Reported-by: Özlem Ozan +Co-Authored-By: Claude Opus 4.6 +--- + data/org.freedesktop.UDisks2.xml | 24 ++++++----- + src/udiskslinuxfilesystem.c | 73 ++++++++++++++++++++++++-------- + 2 files changed, 70 insertions(+), 27 deletions(-) + +diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml +index 184340ac..ab9e848d 100644 +--- a/data/org.freedesktop.UDisks2.xml ++++ b/data/org.freedesktop.UDisks2.xml +@@ -2711,7 +2711,8 @@ + 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. ++ filesystem later. This option expects a user name, not a UID ++ and always performs an authorization check. + + If the device in question is referenced in the + /etc/fstab file, the +@@ -2719,15 +2720,18 @@ + and the given options or filesystem type given in @options are + ignored. + +- If x-udisks-auth is specified as an option +- for the device in the /etc/fstab file, +- then the mount command is run as the +- calling user, without performing any authorization check +- mentioned above. If this fails because of insufficient +- permissions, an authorization check is performed (which +- typically results in the user having to authenticate as an +- administrator). If authorized, the mount +- command is then run as root. ++ If x-udisks-auth, user or ++ users are specified as options for the ++ device in the /etc/fstab file, then the ++ mount command is run as the calling user ++ (or the effective user of the as-user ++ option respectively), without performing any authorization check ++ mentioned above. If this fails because of insufficient permissions ++ and x-udisks-auth is specified, an ++ authorization check is performed (which typically results in ++ the user having to authenticate as an administrator). If ++ authorized, the mount command is then run ++ as root. + + The filesystem should be unmounted using the + org.freedesktop.UDisks2.Filesystem.Unmount() method. +diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c +index 41bdc317..ea449cd2 100644 +--- a/src/udiskslinuxfilesystem.c ++++ b/src/udiskslinuxfilesystem.c +@@ -919,30 +919,60 @@ handle_mount_fstab (UDisksDaemon *daemon, + const gchar *message = NULL; + gboolean success = FALSE; + gboolean mount_fstab_as_root = FALSE; ++ gboolean x_udisks_auth = FALSE; ++ gboolean user_mount = FALSE; + UDisksBaseJob *job = NULL; + GError *error = NULL; + + block = udisks_object_peek_block (object); + device = udisks_block_get_device (block); + +- if (!has_option (fstab_mount_options, "x-udisks-auth") && +- !has_option (fstab_mount_options, "user") && +- !has_option (fstab_mount_options, "users")) ++ x_udisks_auth = has_option (fstab_mount_options, "x-udisks-auth"); ++ user_mount = x_udisks_auth || ++ has_option (fstab_mount_options, "user") || ++ has_option (fstab_mount_options, "users"); ++ ++ /* 'as-user' rules: ++ * - when caller is root: ++ * - if 'user'/'users' present, do not fall back to mounting as root, unless 'x-udisks-auth' is specified ++ * - if 'user'/'users' not present, try as effective user first and allow fallback to mounting as root (implies 'x-udisks-auth') ++ * - when caller is unprivileged: ++ * - always require polkit auth to prevent identity spoofing ++ * - do not fall back to mounting as root, unless 'x-udisks-auth' is specified ++ * - if neither 'user'/'users'/'x-udisks-auth' present, failure is expected by nature (unless 'as-user=root') ++ * ++ * general fstab mounting rules: ++ * - when caller is root, mount as root, allow anything ++ * - when caller is unprivileged: ++ * - if 'user'/'users' present, do not fall back to mounting as root, unless 'x-udisks-auth' is specified ++ * - if only 'x-udisks-auth' present, mount as user and fall back to mounting as root if not successful ++ * - if neither 'user'/'users'/'x-udisks-auth' present, require polkit auth and mount as root ++ * ++ * Further assumptions: ++ * - when 'user'/'users' not present and mounting as unprivileged, this typically fails but it is still beneficial to try that e.g. for FUSE mounts ++ * - assuming libblockdev mount error code mapping works reliably (for the BD_FS_ERROR_AUTH check) ++ */ ++ ++ if (caller_uid != effective_uid) ++ { ++ /* Always require authorization when mounting on behalf of another user, ++ * regardless of fstab mount options. ++ */ ++ action_id = "org.freedesktop.udisks2.filesystem-mount-other-user"; ++ ++ /* When 'user'/'users' not present and the caller is root, allow ++ * fallback mounting as root. ++ */ ++ if (caller_uid == 0 && !user_mount) ++ x_udisks_auth = TRUE; ++ } ++ else ++ if (!user_mount) + { + mount_fstab_as_root = TRUE; ++ + action_id = "org.freedesktop.udisks2.filesystem-mount"; +- /* Translators: Shown in authentication dialog when the user +- * requests mounting a filesystem. +- * +- * Do not translate $(drive), it's a placeholder and +- * will be replaced by the name of the drive/device in question +- */ +- message = N_("Authentication is required to mount $(drive)"); +- if (caller_uid != effective_uid) +- { +- action_id = "org.freedesktop.udisks2.filesystem-mount-other-user"; +- } +- else if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid)) ++ if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid)) + { + if (udisks_block_get_hint_system (block)) + { +@@ -953,7 +983,17 @@ handle_mount_fstab (UDisksDaemon *daemon, + action_id = "org.freedesktop.udisks2.filesystem-mount-other-seat"; + } + } ++ } + ++ if (action_id) ++ { ++ /* Translators: Shown in authentication dialog when the user ++ * requests mounting a filesystem. ++ * ++ * Do not translate $(device.name), it's a placeholder and ++ * will be replaced by the name of the drive/device in question ++ */ ++ message = N_("Authentication is required to mount $(device.name)"); + if (!udisks_daemon_util_check_authorization_sync (daemon, + object, + action_id, +@@ -963,7 +1003,6 @@ handle_mount_fstab (UDisksDaemon *daemon, + return FALSE; + } + +- + if (!g_file_test (mount_point_to_use, G_FILE_TEST_IS_DIR)) + { + if (g_mkdir_with_parents (mount_point_to_use, 0755) != 0) +@@ -1013,7 +1052,7 @@ handle_mount_fstab (UDisksDaemon *daemon, + + if (!success) + { +- if (!mount_fstab_as_root && g_error_matches (error, BD_FS_ERROR, BD_FS_ERROR_AUTH)) ++ if (!mount_fstab_as_root && g_error_matches (error, BD_FS_ERROR, BD_FS_ERROR_AUTH) && x_udisks_auth) + { + g_clear_error (&error); + action_id = "org.freedesktop.udisks2.filesystem-fstab"; +-- +2.54.0 + + +From bf1bc55e559c4e42ca39591714a8582790846838 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Sun, 3 May 2026 14:00:01 +0200 +Subject: [PATCH 3/5] udiskslinuxfilesystem: Log real caller uid for as-user + mounts + +When a mount is performed with the as-user option targeting a different +user, log both the effective (target) uid and the real caller uid to +provide an audit trail of who authorized the operation. + +Co-Authored-By: Claude Opus 4.6 +--- + src/udiskslinuxfilesystem.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c +index ea449cd2..e591c821 100644 +--- a/src/udiskslinuxfilesystem.c ++++ b/src/udiskslinuxfilesystem.c +@@ -1446,11 +1446,23 @@ handle_mount (UDisksFilesystem *filesystem, + system_managed, + system_managed ? FALSE : mpoint_persistent); + +- udisks_info ("Mounted %s%s at %s on behalf of uid %u", +- device, +- system_managed ? " (system)" : "", +- mount_point_to_use, +- effective_uid); ++ if (effective_uid != caller_uid) ++ { ++ udisks_info ("Mounted %s%s at %s on behalf of uid %u (requested by uid %u)", ++ device, ++ system_managed ? " (system)" : "", ++ mount_point_to_use, ++ effective_uid, ++ caller_uid); ++ } ++ else ++ { ++ udisks_info ("Mounted %s%s at %s on behalf of uid %u", ++ device, ++ system_managed ? " (system)" : "", ++ mount_point_to_use, ++ effective_uid); ++ } + + udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object), + UDISKS_DEFAULT_WAIT_TIMEOUT); +-- +2.54.0 + + +From 99c5efc4c40812dc04625e1ebc963a2b49b14613 Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Tue, 28 Apr 2026 19:10:38 +0200 +Subject: [PATCH 4/5] udisksdaemonutil: Pass as-user target to polkit details + +When the 'as-user' D-Bus method option is present, propagate the +target username to polkit details as 'mount.as-user'. This allows +polkit rules to make fine-grained authorization decisions based on +who the mount is being performed for. + +Co-Authored-By: Claude Opus 4.6 +--- + src/udisksdaemonutil.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/udisksdaemonutil.c b/src/udisksdaemonutil.c +index 3db92f58..36e3184b 100644 +--- a/src/udisksdaemonutil.c ++++ b/src/udisksdaemonutil.c +@@ -797,6 +797,14 @@ udisks_daemon_util_check_authorization_sync_with_error (UDisksDaemon * + polkit_details_insert (details, "polkit.message", message); + polkit_details_insert (details, "polkit.gettext_domain", "udisks2"); + ++ if (options != NULL && ++ g_strcmp0 (action_id, "org.freedesktop.udisks2.filesystem-mount-other-user") == 0) ++ { ++ const gchar *as_user = NULL; ++ g_variant_lookup (options, "as-user", "&s", &as_user); ++ _safe_polkit_details_insert (details, "mount.as-user", as_user); ++ } ++ + /* Find drive associated with the block device, if any */ + if (object != NULL) + { +-- +2.54.0 + + +From 40bd25d60d9551b67603dc732828109e0c3beaed Mon Sep 17 00:00:00 2001 +From: Tomas Bzatek +Date: Wed, 29 Apr 2026 16:36:45 +0200 +Subject: [PATCH 5/5] tests: Add security tests for as-user mount authorization + +Add tests verifying that an unprivileged D-Bus caller cannot exploit +the 'as-user' option in Filesystem.Mount() to mount on behalf of +another user without polkit authorization. Use a dedicated +'udisks_test_target' user instead of 'nobody' to avoid OS-level +special treatment of system users that could mask real behavior. + +Dynamic mount tests (in UdisksFSTestCase, inherited by all FS types): +- test_mount_as_user_denied: as-user= is denied +- test_mount_as_user_root_denied: as-user=root is denied +- test_mount_as_user_self: as-user= behaves like plain mount + +Fstab mount tests (in NonPOSIXTestCase, inherited by VFAT/EXFAT): +- test_mount_fstab_{users,user,x-udisks-auth}_as_user_denied: + fstab entries that normally allow unprivileged mounting must still + require authorization when as-user is specified +- test_mount_fstab_{users,user,x-udisks-auth}_as_root_denied: + same but with as-user=root +- test_mount_fstab_defaults_as_user_from_root: root can mount + fstab 'defaults' device with as-user targeting a non-root user +- test_mount_fstab_defaults_as_user_denied: unprivileged user + cannot mount fstab 'defaults' device with as-user + +Co-Authored-By: Claude Opus 4.6 +--- + src/tests/dbus-tests/test_80_filesystem.py | 278 +++++++++++++++++++++ + 1 file changed, 278 insertions(+) + +diff --git a/src/tests/dbus-tests/test_80_filesystem.py b/src/tests/dbus-tests/test_80_filesystem.py +index 75865cce..990d9eb8 100644 +--- a/src/tests/dbus-tests/test_80_filesystem.py ++++ b/src/tests/dbus-tests/test_80_filesystem.py +@@ -29,6 +29,8 @@ class UdisksFSTestCase(udiskstestcase.UdisksTestCase): + _can_query_size = False + + username = 'udisks_test_user' ++ # a separate target user used for 'as-user' mount tests ++ target_username = 'udisks_test_target' + + requested_plugins = BlockDev.plugin_specs_from_names(("fs",)) + +@@ -1112,6 +1114,174 @@ class UdisksFSTestCase(udiskstestcase.UdisksTestCase): + pipe.send([True, '']) + pipe.close() + ++ def _mount_with_as_user_fail(self, pipe, uid, gid, device, target_user): ++ """ Try to mount @device as user with given @uid and @gid using the ++ 'as-user' option set to @target_user. This is expected to fail ++ with a NotAuthorized error. ++ """ ++ 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', ++ 'Mount', ++ GLib.Variant('(a{sv})', ({'as-user': GLib.Variant('s', target_user)},))) ++ except Exception as e: ++ msg = str(e) ++ if 'org.freedesktop.UDisks2.Error.NotAuthorized' in msg and \ ++ 'Not authorized to perform operation' in msg: ++ pipe.send([True, '']) ++ pipe.close() ++ return ++ else: ++ pipe.send([False, 'Mount DBus call failed with unexpected exception: %s' % msg]) ++ pipe.close() ++ return ++ ++ pipe.send([False, 'Mount with as-user=%s unexpectedly succeeded for uid %d' % (target_user, uid)]) ++ pipe.close() ++ ++ def _test_mount_as_user_denied(self, target_user): ++ """ Test that an unprivileged user cannot use as-user to mount on ++ behalf of @target_user without polkit authorization. ++ """ ++ self._check_can_create() ++ ++ if not self._can_mount: ++ self.skipTest('Cannot mount %s filesystem' % self._fs_signature) ++ ++ disk = self.get_object('/block_devices/' + os.path.basename(self.vdevs[0])) ++ self.assertIsNotNone(disk) ++ ++ disk.Format(self._fs_signature, self.no_options, dbus_interface=self.iface_prefix + '.Block') ++ self.addCleanup(self.wipe_fs, self.vdevs[0]) ++ self.addCleanup(self.try_unmount, self.vdevs[0]) ++ ++ self.addCleanup(self._remove_user, self.username) ++ uid, gid = self._add_user(self.username) ++ ++ if target_user != 'root': ++ self.addCleanup(self._remove_user, target_user) ++ self._add_user(target_user) ++ ++ parent_conn, child_conn = Pipe() ++ proc = Process(target=self._mount_with_as_user_fail, ++ args=(child_conn, int(uid), int(gid), self.vdevs[0], target_user)) ++ proc.start() ++ res = parent_conn.recv() ++ parent_conn.close() ++ proc.join() ++ ++ if not res[0]: ++ self.fail(res[1]) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_as_user_denied(self): ++ """ Test that an unprivileged user cannot use as-user to mount on ++ behalf of another user without authorization. ++ """ ++ self._test_mount_as_user_denied(self.target_username) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_as_user_root_denied(self): ++ """ Test that an unprivileged user cannot use as-user=root to mount ++ without authorization. ++ """ ++ self._test_mount_as_user_denied('root') ++ ++ def _mount_as_user_result(self, pipe, uid, gid, device, options): ++ """ Try to mount @device as user with given @uid and @gid using ++ the given @options dict. Returns [True, error_string] on D-Bus ++ error or [True, ''] on success. ++ """ ++ 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', ++ 'Mount', ++ GLib.Variant('(a{sv})', (options,))) ++ except Exception as e: ++ pipe.send([True, str(e)]) ++ pipe.close() ++ return ++ ++ pipe.send([True, '']) ++ pipe.close() ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_as_user_self(self): ++ """ Test that as-user targeting the caller's own username behaves ++ identically to mounting without as-user (no extra mount-other-user ++ authorization required). ++ """ ++ self._check_can_create() ++ ++ if not self._can_mount: ++ self.skipTest('Cannot mount %s filesystem' % self._fs_signature) ++ ++ disk = self.get_object('/block_devices/' + os.path.basename(self.vdevs[0])) ++ self.assertIsNotNone(disk) ++ ++ disk.Format(self._fs_signature, self.no_options, dbus_interface=self.iface_prefix + '.Block') ++ self.addCleanup(self.wipe_fs, self.vdevs[0]) ++ self.addCleanup(self.try_unmount, self.vdevs[0]) ++ ++ self.addCleanup(self._remove_user, self.username) ++ uid, gid = self._add_user(self.username) ++ ++ # first try a plain mount without as-user ++ parent_conn, child_conn = Pipe() ++ proc = Process(target=self._mount_as_user_result, ++ args=(child_conn, int(uid), int(gid), self.vdevs[0], {})) ++ proc.start() ++ res_plain = parent_conn.recv() ++ parent_conn.close() ++ proc.join() ++ ++ self.assertTrue(res_plain[0]) ++ plain_error = res_plain[1] ++ ++ # unmount if the plain mount succeeded ++ if not plain_error: ++ self.try_unmount(self.vdevs[0]) ++ ++ # now try with as-user= ++ parent_conn, child_conn = Pipe() ++ proc = Process(target=self._mount_as_user_result, ++ args=(child_conn, int(uid), int(gid), self.vdevs[0], ++ {'as-user': GLib.Variant('s', self.username)})) ++ proc.start() ++ res_as_user = parent_conn.recv() ++ parent_conn.close() ++ proc.join() ++ ++ self.assertTrue(res_as_user[0]) ++ as_user_error = res_as_user[1] ++ ++ if not plain_error and not as_user_error: ++ return ++ if plain_error and as_user_error: ++ # extract the GDBus error name (e.g. 'org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain') ++ dbus_err_re = re.compile(r'GDBus\.Error:([\w.]+):') ++ plain_match = dbus_err_re.search(plain_error) ++ as_user_match = dbus_err_re.search(as_user_error) ++ plain_err_name = plain_match.group(1) if plain_match else plain_error ++ as_user_err_name = as_user_match.group(1) if as_user_match else as_user_error ++ self.assertEqual(plain_err_name, as_user_err_name, ++ 'as-user= produced a different error than plain mount: ' ++ 'plain=%s, as-user=%s' % (plain_error, as_user_error)) ++ return ++ ++ if plain_error: ++ self.fail('Plain mount failed (%s) but as-user= succeeded' % plain_error) ++ else: ++ self.fail('Plain mount succeeded but as-user= failed: %s' % as_user_error) ++ + @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) + def test_mount_as_user(self): + """ Test mounting a filesystem on behalf of a different user.""" +@@ -1624,6 +1794,108 @@ class NonPOSIXTestCase(UdisksFSTestCase): + self.fail(res[1]) + + ++ def _test_mount_fstab_as_user_denied(self, fstab_options, target_user): ++ """ Test that an unprivileged user cannot use 'as-user' option to mount ++ a device listed in /etc/fstab with @fstab_options on behalf of ++ @target_user without polkit authorization. ++ """ ++ disk = self.get_object('/block_devices/' + os.path.basename(self.vdevs[0])) ++ self.assertIsNotNone(disk) ++ ++ self.addCleanup(self._remove_user, self.username) ++ uid, gid = self._add_user(self.username) ++ ++ if target_user != 'root': ++ self.addCleanup(self._remove_user, target_user) ++ self._add_user(target_user) ++ ++ self.addCleanup(self.try_unmount, self.vdevs[0]) ++ self._prepare_mount_test(disk, True, fstab_options) ++ ++ parent_conn, child_conn = Pipe() ++ proc = Process(target=self._mount_with_as_user_fail, ++ args=(child_conn, int(uid), int(gid), self.vdevs[0], target_user)) ++ proc.start() ++ res = parent_conn.recv() ++ parent_conn.close() ++ proc.join() ++ ++ if not res[0]: ++ self.fail(res[1]) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_as_user_denied(self): ++ """ Test that user cannot use as-user to mount fstab 'users' device ++ on behalf of another user without authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('users', self.target_username) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_user_as_user_denied(self): ++ """ Test that user cannot use as-user to mount fstab 'user' device ++ on behalf of another user without authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('user', self.target_username) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_x_udisks_auth_as_user_denied(self): ++ """ Test that user cannot use as-user to mount fstab 'x-udisks-auth' ++ device on behalf of another user without authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('x-udisks-auth', self.target_username) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_as_root_denied(self): ++ """ Test that user cannot use as-user=root to mount fstab 'users' ++ device without authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('users', 'root') ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_user_as_root_denied(self): ++ """ Test that user cannot use as-user=root to mount fstab 'user' ++ device without authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('user', 'root') ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_x_udisks_auth_as_root_denied(self): ++ """ Test that user cannot use as-user=root to mount fstab ++ 'x-udisks-auth' device without authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('x-udisks-auth', 'root') ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_defaults_as_user_from_root(self): ++ """ Test that root can mount a device listed in /etc/fstab with ++ 'defaults' (no 'user', 'users' or 'x-udisks-auth' options) ++ using the 'as-user' option targeting a non-root user. ++ """ ++ disk = self.get_object('/block_devices/' + os.path.basename(self.vdevs[0])) ++ self.assertIsNotNone(disk) ++ ++ self.addCleanup(self._remove_user, self.target_username) ++ self._add_user(self.target_username) ++ ++ self.addCleanup(self.try_unmount, self.vdevs[0]) ++ self._prepare_mount_test(disk, True, 'defaults') ++ ++ d = dbus.Dictionary(signature='sv') ++ d['as-user'] = self.target_username ++ mnt_path = disk.Mount(d, dbus_interface=self.iface_prefix + '.Filesystem') ++ self.assertIsNotNone(mnt_path) ++ self.assertTrue(os.path.ismount(mnt_path)) ++ ++ @udiskstestcase.tag_test(udiskstestcase.TestTags.UNSAFE) ++ def test_mount_fstab_defaults_as_user_denied(self): ++ """ Test that an unprivileged user cannot use 'as-user' option to ++ mount a device listed in /etc/fstab with 'defaults' (no 'user', ++ 'users' or 'x-udisks-auth') on behalf of another user without ++ polkit authorization. ++ """ ++ self._test_mount_fstab_as_user_denied('defaults', self.target_username) ++ ++ + class VFATTestCase(NonPOSIXTestCase): + _fs_signature = 'vfat' + _can_mount = True +@@ -1776,6 +2048,12 @@ class UDFTestCase(UdisksFSTestCase): + def test_mount_fstab_complex_label_bad(self): + super(UDFTestCase, self).test_mount_fstab_complex_label_bad() + ++ def test_mount_as_user_denied(self): ++ self.skipTest('Skipping as-user authorization test for UDF') ++ ++ def test_mount_as_user_root_denied(self): ++ self.skipTest('Skipping as-user authorization test for UDF') ++ + + class FailsystemTestCase(UdisksFSTestCase): + # test that not supported operations fail 'nicely' +-- +2.54.0 + diff --git a/udisks2.spec b/udisks2.spec index a8d41c1..38bd04e 100644 --- a/udisks2.spec +++ b/udisks2.spec @@ -14,16 +14,26 @@ %define git_hash %(git log -1 --pretty=format:"%h" || true) %define build_date %(date '+%Y%m%d') +%define ol_btrfs_arches x86_64 aarch64 + # btrfs is not available on RHEL %if 0%{?rhel} %define with_btrfs 0 %endif +# enable btrfs support for OL supported arches +%if 0%{?oraclelinux} +%ifarch %{ol_btrfs_arches} +%define with_btrfs 1 +%else +%define with_btrfs 0 +%endif +%endif Name: udisks2 Summary: Disk Manager Version: 2.11.0 -Release: 2%{?dist} +Release: 2.0.1%{?dist}.1 License: GPL-2.0-or-later URL: https://github.com/storaged-project/udisks Source0: https://github.com/storaged-project/udisks/releases/download/udisks-%{version}/udisks-%{version}.tar.bz2 @@ -32,6 +42,8 @@ Source0: https://github.com/storaged-project/udisks/releases/download/udisks-%{v Patch0: udisks-2.11.1-polkit_RestoreEncryptedHeader.patch # https://issues.redhat.com/browse/RHEL-148590 Patch1: udisks-2.11.1-polkit_HeaderBackup.patch +# https://redhat.atlassian.net/browse/RHEL-173437 +Patch2: udisks-2.11.2-mount_as_user_auth.patch BuildRequires: make BuildRequires: glib2-devel >= %{glib2_version} @@ -339,6 +351,12 @@ fi %endif %changelog +* Tue Aug 11 2026 EL Errata - 2.11.0-2.0.1.el10_2.1 +- Enable btrfs support for OL supported arches [Orabug: 37464632] + +* Fri Jun 19 2026 Tomas Bzatek - 2.11.0-2.1 +- Rework fstab mount authorization for as-user (CVE-2026-7867) (RHEL-173437) + * Fri Feb 27 2026 Tomas Bzatek - 2.11.0-2 - Add missing polkit check for RestoreEncryptedHeader() (CVE-2026-26103) (RHEL-148566) - Add missing polkit check for HeaderBackup() (CVE-2026-26104) (RHEL-148590)