From 55e4946f9ca75c35e87ff7f0c0d871e0d80e8ca0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 12 Nov 2024 09:44:48 +0100 Subject: [PATCH] dissect: add new --shift command --- man/systemd-dissect.xml | 24 ++++++++++++++++++++ src/dissect/dissect.c | 50 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index 3aaa1744f3e..2718feccb75 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -62,6 +62,9 @@ systemd-dissect OPTIONS --validate IMAGE + + systemd-dissect OPTIONS --shift IMAGE UIDBASE + @@ -350,6 +353,27 @@ + + + + Recursively iterates through all inodes of the specified image and shifts the UIDs + and GIDs the inodes are owned by into the specified UID range. Takes an image path and a UID base as + parameter. The UID base can be specified numerically (in which case it must be a multiple of 65536, + and either 0 or within the container or foreign UID range, as per Users, Groups, UIDs and GIDs on systemd Systems), or as + the symbolic identifier foreign which is shorthand to the foreign UID base. This + command is useful for preparing directory container images for unprivileged use. Note that this + command is intended for images that use the 16bit UIDs/GIDs range only, and it always ignores the + upper 16bit of the current UID/GID ownership, combining the lower 16 bit with the target UID + base. + + Use systemd-dissect --shift /some/container/tree foreign to shift a + container image into the foreign UID range, or systemd-dissect --shift /some/container/tree + 0 to shift it to host UID range. + + + + diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index cb51d713843..3ca1e17be4b 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -45,6 +45,7 @@ #include "process-util.h" #include "recurse-dir.h" #include "sha256.h" +#include "shift-uid.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -68,6 +69,7 @@ static enum { ACTION_DISCOVER, ACTION_VALIDATE, ACTION_MAKE_ARCHIVE, + ACTION_SHIFT, } arg_action = ACTION_DISSECT; static char *arg_image = NULL; static char *arg_root = NULL; @@ -97,6 +99,7 @@ static bool arg_mtree_hash = true; static bool arg_via_service = false; static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID; static bool arg_all = false; +static uid_t arg_uid_base = UID_INVALID; STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); @@ -129,6 +132,7 @@ static int help(void) { "%1$s [OPTIONS...] --make-archive IMAGE [TARGET]\n" "%1$s [OPTIONS...] --discover\n" "%1$s [OPTIONS...] --validate IMAGE\n" + "%1$s [OPTIONS...] --shift IMAGE UIDBASE\n" "\n%5$sDissect a Discoverable Disk Image (DDI).%6$s\n\n" "%3$sOptions:%4$s\n" " --no-pager Do not pipe output into a pager\n" @@ -174,6 +178,7 @@ static int help(void) { " --make-archive Convert the DDI to an archive file\n" " --discover Discover DDIs in well known directories\n" " --validate Validate image and image policy\n" + " --shift Shift UID range to selected base\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -279,6 +284,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VALIDATE, ARG_MTREE_HASH, ARG_MAKE_ARCHIVE, + ARG_SHIFT, ARG_SYSTEM, ARG_USER, ARG_ALL, @@ -315,6 +321,7 @@ static int parse_argv(int argc, char *argv[]) { { "validate", no_argument, NULL, ARG_VALIDATE }, { "mtree-hash", required_argument, NULL, ARG_MTREE_HASH }, { "make-archive", no_argument, NULL, ARG_MAKE_ARCHIVE }, + { "shift", no_argument, NULL, ARG_SHIFT }, { "system", no_argument, NULL, ARG_SYSTEM }, { "user", no_argument, NULL, ARG_USER }, { "all", no_argument, NULL, ARG_ALL }, @@ -550,6 +557,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_MAKE_ARCHIVE; break; + case ARG_SHIFT: + arg_action = ACTION_SHIFT; + break; + case ARG_SYSTEM: system_scope_requested = true; break; @@ -731,6 +742,33 @@ static int parse_argv(int argc, char *argv[]) { arg_flags &= ~(DISSECT_IMAGE_PIN_PARTITION_DEVICES|DISSECT_IMAGE_ADD_PARTITION_DEVICES); break; + case ACTION_SHIFT: + if (optind + 2 != argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Expected an image path and a UID base as only argument."); + + r = parse_image_path_argument(argv[optind], &arg_root, &arg_image); + if (r < 0) + return r; + + if (streq(argv[optind + 1], "foreign")) + arg_uid_base = FOREIGN_UID_BASE; + else { + r = parse_uid(argv[optind + 1], &arg_uid_base); + if (r < 0) + return log_error_errno(r, "Failed to parse UID base: %s", argv[optind + 1]); + + if ((arg_uid_base & 0xFFFF) != 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected UID base not a multiple of 64K: " UID_FMT, arg_uid_base); + if (arg_uid_base != 0 && + !uid_is_container(arg_uid_base) && + !uid_is_foreign(arg_uid_base)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected UID range is not in the container range, nor the foreign one, refusing."); + } + + arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT; + break; + default: assert_not_reached(); } @@ -1444,7 +1482,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD const char *root; int r; - assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE)); + assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE, ACTION_SHIFT)); if (arg_image) { assert(m); @@ -1699,6 +1737,13 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD #endif } + case ACTION_SHIFT: + r = path_patch_uid(root, arg_uid_base, 0x10000); + if (r < 0) + return log_error_errno(r, "Failed to shift UID base: %m"); + + return 0; + default: assert_not_reached(); } @@ -2121,7 +2166,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)) + if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, 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); @@ -2206,6 +2251,7 @@ static int run(int argc, char *argv[]) { case ACTION_COPY_FROM: case ACTION_COPY_TO: case ACTION_MAKE_ARCHIVE: + case ACTION_SHIFT: return action_list_or_mtree_or_copy_or_make_archive(m, d, userns_fd); case ACTION_WITH: