From ef155c909a53fc1c04c8ea5cd4b032e548b91a87 Mon Sep 17 00:00:00 2001 From: DaanDeMeyer Date: Fri, 26 Dec 2025 22:18:29 +0100 Subject: [PATCH] dissect: Make --mount/--unmount/--with work unprivileged Let's check for CAP_SYS_ADMIN instead of root for these, and make unmounting more graceful if we can't access the backing loop device because of permission issues. This allows mounting and unmounting images from an unprvileged mount namespace. The actual files in the image will end up owned by nobody:nobody because we'll be in an unprivileged user namespace, but assuming the directory permissions are not too strict, this still allows interacting with the image in useful ways. --- src/dissect/dissect.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 95db618b83c..deee8f6c1d1 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -14,6 +14,7 @@ #include "argv-util.h" #include "blockdev-util.h" #include "build.h" +#include "capability-util.h" #include "chase.h" #include "copy.h" #include "device-util.h" @@ -837,7 +838,14 @@ static int parse_argv(int argc, char *argv[]) { } else arg_via_service = r; - if (!IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_DISCOVER, ACTION_VALIDATE) && geteuid() != 0) + r = have_effective_cap(CAP_SYS_ADMIN); + if (r < 0) + return log_error_errno(r, "Failed to determine if we have CAP_SYS_ADMIN: %m"); + + if (IN_SET(arg_action, ACTION_MOUNT, ACTION_UMOUNT) && r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to have CAP_SYS_ADMIN to mount/unmount images"); + + if (IN_SET(arg_action, ACTION_ATTACH, ACTION_DETACH, ACTION_SHIFT) && geteuid() != 0) return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty_safe(STDIN_FILENO)); @@ -1721,7 +1729,7 @@ static int action_umount(const char *path) { return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical); r = loop_device_open(dev, 0, LOCK_EX, &d); - if (r < 0) + if (r < 0 && !ERRNO_IS_PRIVILEGE(r)) return log_device_error_errno(dev, r, "Failed to open loopback block device: %m"); /* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have @@ -1733,7 +1741,8 @@ static int action_umount(const char *path) { return log_error_errno(r, "Failed to unmount '%s': %m", canonical); /* We managed to lock and unmount successfully? That means we can try to remove the loop device. */ - loop_device_unrelinquish(d); + if (d) + loop_device_unrelinquish(d); if (arg_rmdir) { r = RET_NERRNO(rmdir(canonical)); @@ -2114,7 +2123,7 @@ static int run(int argc, char *argv[]) { else r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); if (r < 0) { - if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_SHIFT)) + if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_MOUNT, ACTION_UMOUNT, ACTION_WITH, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_SHIFT)) return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image); log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image); -- 2.47.3