diff options
author | Lennart Poettering <lennart@poettering.net> | 2023-04-20 19:07:10 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2024-04-06 16:08:24 +0200 |
commit | fdec6d1560896cdce6f7c42c2e6d811c0c12aefc (patch) | |
tree | d9119d36810a30c73c24b9be7fe8892811fcb79f | |
parent | dissect-image: add a generic varlink client side for mountfsd (diff) | |
download | systemd-fdec6d1560896cdce6f7c42c2e6d811c0c12aefc.tar.xz systemd-fdec6d1560896cdce6f7c42c2e6d811c0c12aefc.zip |
dissect-tool: allow systemd-dissect to talk to mountfsd
-rw-r--r-- | src/dissect/dissect.c | 198 |
1 files changed, 135 insertions, 63 deletions
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index d6939152e0..d68c29cbf1 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -31,10 +31,12 @@ #include "log.h" #include "loop-util.h" #include "main-func.h" +#include "missing_syscall.h" #include "mkdir.h" #include "mount-util.h" #include "mountpoint-util.h" #include "namespace-util.h" +#include "nsresource.h" #include "parse-argument.h" #include "parse-util.h" #include "path-util.h" @@ -91,6 +93,7 @@ static char **arg_argv = NULL; static char *arg_loop_ref = NULL; static ImagePolicy *arg_image_policy = NULL; static bool arg_mtree_hash = true; +static bool arg_via_service = false; STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); @@ -704,6 +707,18 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached(); } + r = getenv_bool("SYSTEMD_USE_MOUNTFSD"); + if (r < 0) { + if (r != -ENXIO) + return log_error_errno(r, "Failed to parse $SYSTEMD_USE_MOUNTFSD: %m"); + } 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) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root."); + + SET_FLAG(arg_flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH, isatty(STDIN_FILENO)); + return 1; } @@ -841,7 +856,11 @@ static int get_extension_scopes(DissectedImage *m, ImageClass class, char ***ret return 1; } -static int action_dissect(DissectedImage *m, LoopDevice *d) { +static int action_dissect( + DissectedImage *m, + LoopDevice *d, + int userns_fd) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(table_unrefp) Table *t = NULL; _cleanup_free_ char *bn = NULL; @@ -849,7 +868,6 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { int r; assert(m); - assert(d); r = path_extract_filename(arg_image, &bn); if (r < 0) @@ -875,7 +893,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { fflush(stdout); } - r = dissected_image_acquire_metadata(m, /* userns_fd= */ -EBADF, /* extra_flags= */ 0); + r = dissected_image_acquire_metadata(m, userns_fd, /* extra_flags= */ 0); if (r == -ENXIO) return log_error_errno(r, "No root partition discovered."); if (r == -EUCLEAN) @@ -1087,7 +1105,6 @@ static int action_mount(DissectedImage *m, LoopDevice *d) { int r; assert(m); - assert(d); assert(arg_action == ACTION_MOUNT); r = dissected_image_mount_and_warn( @@ -1100,9 +1117,11 @@ static int action_mount(DissectedImage *m, LoopDevice *d) { if (r < 0) return r; - r = loop_device_flock(d, LOCK_UN); - if (r < 0) - return log_error_errno(r, "Failed to unlock loopback block device: %m"); + if (d) { + r = loop_device_flock(d, LOCK_UN); + if (r < 0) + return log_error_errno(r, "Failed to unlock loopback block device: %m"); + } r = dissected_image_relinquish(m); if (r < 0) @@ -1390,8 +1409,8 @@ static int archive_item( } #endif -static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d) { - _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; +static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopDevice *d, int userns_fd) { + _cleanup_(umount_and_freep) char *mounted_dir = NULL; _cleanup_free_ char *t = NULL; const char *root; int r; @@ -1400,12 +1419,16 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD if (arg_image) { assert(m); - assert(d); - r = detach_mount_namespace(); + if (userns_fd < 0) + r = detach_mount_namespace_harder(0, 0); + else + r = detach_mount_namespace_userns(userns_fd); if (r < 0) return log_error_errno(r, "Failed to detach mount namespace: %m"); + /* Create a place we can mount things onto soon. We use a fixed path shared by all invocations. Given + * the mounts are done in a mount namespace there's not going to be a collision here */ r = get_common_dissect_directory(&t); if (r < 0) return log_error_errno(r, "Failed generate private mount directory: %m"); @@ -1422,9 +1445,11 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD mounted_dir = TAKE_PTR(t); - r = loop_device_flock(d, LOCK_UN); - if (r < 0) - return log_error_errno(r, "Failed to unlock loopback block device: %m"); + if (d) { + r = loop_device_flock(d, LOCK_UN); + if (r < 0) + return log_error_errno(r, "Failed to unlock loopback block device: %m"); + } r = dissected_image_relinquish(m); if (r < 0) @@ -1433,6 +1458,8 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD root = mounted_dir ?: arg_root; + dissected_image_close(m); + switch (arg_action) { case ACTION_COPY_FROM: { @@ -1716,7 +1743,6 @@ static int action_with(DissectedImage *m, LoopDevice *d) { int r, rcode; assert(m); - assert(d); assert(arg_action == ACTION_WITH); r = tempfn_random_child(NULL, program_invocation_short_name, &temp); @@ -1745,9 +1771,11 @@ static int action_with(DissectedImage *m, LoopDevice *d) { if (r < 0) return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m"); - r = loop_device_flock(d, LOCK_UN); - if (r < 0) - return log_error_errno(r, "Failed to unlock loopback block device: %m"); + if (d) { + r = loop_device_flock(d, LOCK_UN); + if (r < 0) + return log_error_errno(r, "Failed to unlock loopback block device: %m"); + } rcode = safe_fork("(with)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL); if (rcode == 0) { @@ -1788,14 +1816,16 @@ static int action_with(DissectedImage *m, LoopDevice *d) { } /* Let's manually detach everything, to make things synchronous */ - r = loop_device_flock(d, LOCK_SH); - if (r < 0) - log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m"); + if (d) { + r = loop_device_flock(d, LOCK_SH); + if (r < 0) + log_warning_errno(r, "Failed to lock loopback block device, ignoring: %m"); + } r = umount_recursive(mounted_dir, 0); if (r < 0) log_warning_errno(r, "Failed to unmount '%s', ignoring: %m", mounted_dir); - else + else if (d) loop_device_unrelinquish(d); /* Let's try to destroy the loopback device */ created_dir = TAKE_PTR(mounted_dir); @@ -1982,8 +2012,8 @@ static int action_validate(void) { static int run(int argc, char *argv[]) { _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; - uint32_t loop_flags; - int open_flags, r; + _cleanup_close_ int userns_fd = -EBADF; + int r; log_setup(); @@ -2015,7 +2045,7 @@ static int run(int argc, char *argv[]) { return action_discover(); default: - /* All other actions need the image dissected */ + /* All other actions need the image dissected (except for ACTION_VALIDATE, see below) */ break; } @@ -2031,50 +2061,92 @@ static int run(int argc, char *argv[]) { * hence if there's external Verity data * available we turn off partition table * support */ + } - if (arg_action == ACTION_VALIDATE) - return action_validate(); + if (arg_action == ACTION_VALIDATE) + return action_validate(); - open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR; - loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN; - if (arg_in_memory) - r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); - else - r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); - if (r < 0) - return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image); + if (arg_image) { + /* First try locally, if we are allowed to */ + if (!arg_via_service) { + uint32_t loop_flags; + int open_flags; + + open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR; + loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN; + + if (arg_in_memory) + r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d); + 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)) + return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image); - if (arg_loop_ref) { - r = loop_device_set_filename(d, arg_loop_ref); - if (r < 0) - log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref); + log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image); + arg_via_service = true; + } else { + if (arg_loop_ref) { + r = loop_device_set_filename(d, arg_loop_ref); + if (r < 0) + log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref); + } + + r = dissect_loop_device_and_warn( + d, + &arg_verity_settings, + /* mount_options= */ NULL, + arg_image_policy, + arg_flags, + &m); + if (r < 0) + return r; + + if (arg_action == ACTION_ATTACH) + return action_attach(m, d); + + r = dissected_image_load_verity_sig_partition( + m, + d->fd, + &arg_verity_settings); + if (r < 0) + return log_error_errno(r, "Failed to load verity signature partition: %m"); + + if (arg_action != ACTION_DISSECT) { + r = dissected_image_decrypt_interactively( + m, NULL, + &arg_verity_settings, + arg_flags); + if (r < 0) + return r; + } + } } - r = dissect_loop_device_and_warn( - d, - &arg_verity_settings, - /* mount_options= */ NULL, - arg_image_policy, - arg_flags, - &m); - if (r < 0) - return r; + /* Try via service */ + if (arg_via_service) { + if (arg_in_memory) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--in-memory= not supported when operating via systemd-mountfsd."); - if (arg_action == ACTION_ATTACH) - return action_attach(m, d); + if (arg_loop_ref) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--loop-ref= not supported when operating via systemd-mountfsd."); - r = dissected_image_load_verity_sig_partition( - m, - d->fd, - &arg_verity_settings); - if (r < 0) - return log_error_errno(r, "Failed to load verity signature partition: %m"); + if (verity_settings_set(&arg_verity_settings)) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Externally configured verity settings not supported when operating via systemd-mountfsd."); + + /* Don't run things in private userns, if the mount shall be attached to the host */ + if (!IN_SET(arg_action, ACTION_MOUNT, ACTION_WITH)) { + userns_fd = nsresource_allocate_userns(/* name= */ NULL, UINT64_C(0x10000)); /* allocate 64K users by default */ + if (userns_fd < 0) + return log_error_errno(userns_fd, "Failed to allocate user namespace with 64K users: %m"); + } - if (arg_action != ACTION_DISSECT) { - r = dissected_image_decrypt_interactively( - m, NULL, - &arg_verity_settings, - arg_flags); + r = mountfsd_mount_image( + arg_image, + userns_fd, + arg_image_policy, + arg_flags, + &m); if (r < 0) return r; } @@ -2083,7 +2155,7 @@ static int run(int argc, char *argv[]) { switch (arg_action) { case ACTION_DISSECT: - return action_dissect(m, d); + return action_dissect(m, d, userns_fd); case ACTION_MOUNT: return action_mount(m, d); @@ -2093,7 +2165,7 @@ static int run(int argc, char *argv[]) { case ACTION_COPY_FROM: case ACTION_COPY_TO: case ACTION_MAKE_ARCHIVE: - return action_list_or_mtree_or_copy_or_make_archive(m, d); + return action_list_or_mtree_or_copy_or_make_archive(m, d, userns_fd); case ACTION_WITH: return action_with(m, d); |