From 8e39ee06c8268606054f0f8ae9926d48db83e6ad Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 16 Oct 2026 16:15:16 +0200 Subject: [PATCH] portabled: authenticate extension images specified by path GetImageMetadata() and GetImageMetadataWithExtensions() ask polkit before inspecting an image that is specified by path, because inspecting loop-mounts the image as root. Extension images aren't authenticated at all, so any local user can make portabled set up loop devices for files they choose, probe them and mount them, by passing them as extensions of an installed image. Let's ask polkit for the inspect-images action if any extension image is specified by path, like we do for the image itself. (cherry picked from commit 5ddab5b8f47fcd9e5b125e3e575d4bbabd6742cf) (cherry picked from commit 38ab8023a50ef362b277f2c0e72ee23e79ca36f9) --- src/portable/portabled-image-bus.c | 11 ++++++++++- test/units/TEST-29-PORTABLE.image.sh | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 5a5393e986a..698e75db9e4 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -145,11 +145,20 @@ int bus_image_common_get_metadata( flags |= input_flags; } + /* Inspecting an image loop-mounts it as root, so we authenticate extension images that are + * specified by path like the image itself. */ + bool extension_by_path = false; + STRV_FOREACH(i, extension_images) + if (!image_name_is_valid(*i)) { + extension_by_path = true; + break; + } + r = bus_image_acquire(m, message, name_or_path, image, - BUS_IMAGE_AUTHENTICATE_BY_PATH, + extension_by_path ? BUS_IMAGE_AUTHENTICATE_ALL : BUS_IMAGE_AUTHENTICATE_BY_PATH, "org.freedesktop.portable1.inspect-images", &image, error); diff --git a/test/units/TEST-29-PORTABLE.image.sh b/test/units/TEST-29-PORTABLE.image.sh index 1e8e872a3e4..ad00589daa6 100755 --- a/test/units/TEST-29-PORTABLE.image.sh +++ b/test/units/TEST-29-PORTABLE.image.sh @@ -17,6 +17,25 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then ARGS+=(--profile=trusted) fi +# Unprivileged users can inspect images and extension images by name, but not extension images specified by +# path, because inspecting loop-mounts the extension images as root. +mkdir -p /run/portables +cp /usr/share/minimal_0.raw /run/portables/minimal_8.raw +cp /tmp/app0.raw /run/portables/app0.raw +runas testuser busctl call org.freedesktop.portable1 /org/freedesktop/portable1 org.freedesktop.portable1.Manager \ + GetImageMetadata sas minimal_8 0 >/dev/null +runas testuser busctl call org.freedesktop.portable1 /org/freedesktop/portable1 org.freedesktop.portable1.Manager \ + GetImageMetadataWithExtensions sasast minimal_8 1 app0 0 0 >/dev/null +# portabled fails with "Permission denied" if polkit denies the call. It fails with a message that starts with +# "Access denied" if polkit requires interactive authentication. +(! runas testuser busctl call org.freedesktop.portable1 /org/freedesktop/portable1 org.freedesktop.portable1.Manager \ + GetImageMetadataWithExtensions sasast minimal_8 1 /tmp/app0.raw 0 0 2>&1 >/dev/null) | + grep -E "Permission denied|Access denied" >/dev/null +(! runas testuser busctl call org.freedesktop.portable1 /org/freedesktop/portable1/image/minimal_5f8 org.freedesktop.portable1.Image \ + GetMetadataWithExtensions asast 1 /tmp/app0.raw 0 0 2>&1 >/dev/null) | + grep -E "Permission denied|Access denied" >/dev/null +rm /run/portables/minimal_8.raw /run/portables/app0.raw + # Attaching with a profile that doesn't exist must fail and must not leave anything behind (! portablectl attach --runtime --profile=nonexistent /usr/share/minimal_0.raw minimal-app0) test ! -e /run/systemd/system.attached/minimal-app0.service