Merge pull request #25608 from poettering/dissect-moar
dissect: add dissection policies
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "analyze-image-policy.h"
|
||||
#include "analyze.h"
|
||||
#include "format-table.h"
|
||||
#include "terminal-util.h"
|
||||
|
||||
static int table_add_designator_line(Table *table, PartitionDesignator d, PartitionPolicyFlags f) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
const char *color;
|
||||
int r;
|
||||
|
||||
assert(table);
|
||||
assert(f >= 0);
|
||||
|
||||
if (partition_policy_flags_to_string(f & _PARTITION_POLICY_USE_MASK, /* simplify= */ true, &q) < 0)
|
||||
return log_oom();
|
||||
|
||||
color = (f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE ? ansi_grey() :
|
||||
((f & (PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ==
|
||||
(PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT)) ? ansi_highlight_yellow() :
|
||||
(f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT ? ansi_highlight_red() :
|
||||
!(f & PARTITION_POLICY_UNPROTECTED) ? ansi_highlight_green() : NULL;
|
||||
|
||||
if (d < 0)
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, "default",
|
||||
TABLE_SET_COLOR, ansi_highlight_green(),
|
||||
TABLE_STRING, q,
|
||||
TABLE_SET_COLOR, color);
|
||||
else
|
||||
r = table_add_many(table,
|
||||
TABLE_STRING, partition_designator_to_string(d),
|
||||
TABLE_SET_COLOR, ansi_normal(),
|
||||
TABLE_STRING, q,
|
||||
TABLE_SET_COLOR, color);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
switch (f & _PARTITION_POLICY_READ_ONLY_MASK) {
|
||||
|
||||
case PARTITION_POLICY_READ_ONLY_ON:
|
||||
r = table_add_many(table, TABLE_BOOLEAN, true);
|
||||
break;
|
||||
|
||||
case PARTITION_POLICY_READ_ONLY_OFF:
|
||||
r = table_add_many(table, TABLE_BOOLEAN, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = table_add_many(table, TABLE_EMPTY);
|
||||
break;
|
||||
}
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
switch (f & _PARTITION_POLICY_GROWFS_MASK) {
|
||||
|
||||
case PARTITION_POLICY_GROWFS_ON:
|
||||
r = table_add_many(table, TABLE_BOOLEAN, true);
|
||||
break;
|
||||
|
||||
case PARTITION_POLICY_GROWFS_OFF:
|
||||
r = table_add_many(table, TABLE_BOOLEAN, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
r = table_add_many(table, TABLE_EMPTY);
|
||||
break;
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verb_image_policy(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
_cleanup_(image_policy_freep) ImagePolicy *pbuf = NULL;
|
||||
_cleanup_free_ char *as_string = NULL, *as_string_simplified = NULL;
|
||||
const ImagePolicy *p;
|
||||
|
||||
/* NB: The magic '@' strings are not officially documented for now, since we might change
|
||||
* around defaults (and in particular where precisely to reuse policy). We should document
|
||||
* them once the dust has settled a bit. For now it's just useful for debugging and
|
||||
* introspect our own defaults without guaranteeing API safety. */
|
||||
if (streq(argv[i], "@sysext"))
|
||||
p = &image_policy_sysext;
|
||||
else if (streq(argv[i], "@sysext-strict"))
|
||||
p = &image_policy_sysext_strict;
|
||||
else if (streq(argv[i], "@container"))
|
||||
p = &image_policy_container;
|
||||
else if (streq(argv[i], "@service"))
|
||||
p = &image_policy_service;
|
||||
else if (streq(argv[i], "@host"))
|
||||
p = &image_policy_host;
|
||||
else {
|
||||
r = image_policy_from_string(argv[i], &pbuf);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy '%s': %m", argv[i]);
|
||||
|
||||
p = pbuf;
|
||||
}
|
||||
|
||||
r = image_policy_to_string(p, /* simplify= */ false, &as_string);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format policy '%s' as string: %m", argv[i]);
|
||||
|
||||
r = image_policy_to_string(p, /* simplify= */ true, &as_string_simplified);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to format policy '%s' as string: %m", argv[i]);
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
if (streq(as_string, as_string_simplified))
|
||||
printf("Analyzing policy: %s%s%s\n", ansi_highlight_magenta_underline(), as_string, ansi_normal());
|
||||
else
|
||||
printf("Analyzing policy: %s%s%s\n"
|
||||
" Long form: %s%s%s\n",
|
||||
ansi_highlight(), as_string_simplified, ansi_normal(),
|
||||
ansi_grey(), as_string, ansi_normal());
|
||||
|
||||
table = table_new("partition", "mode", "read-only", "growfs");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
|
||||
|
||||
for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
|
||||
PartitionPolicyFlags f = image_policy_get_exhaustively(p, d);
|
||||
assert(f >= 0);
|
||||
|
||||
r = table_add_designator_line(table, d, f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = table_add_designator_line(table, _PARTITION_DESIGNATOR_INVALID, image_policy_default(p));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
putc('\n', stdout);
|
||||
|
||||
r = table_print(table, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
int verb_image_policy(int argc, char *argv[], void *userdata);
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "analyze-unit-files.h"
|
||||
#include "analyze-unit-paths.h"
|
||||
#include "analyze-verify.h"
|
||||
#include "analyze-image-policy.h"
|
||||
#include "build.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
@@ -109,6 +110,7 @@ bool arg_quiet = false;
|
||||
char *arg_profile = NULL;
|
||||
bool arg_legend = true;
|
||||
bool arg_table = false;
|
||||
ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
|
||||
@@ -117,6 +119,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
int acquire_bus(sd_bus **bus, bool *use_full_bus) {
|
||||
int r;
|
||||
@@ -268,6 +271,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" -q --quiet Do not emit hints\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
@@ -307,6 +311,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PROFILE,
|
||||
ARG_TABLE,
|
||||
ARG_NO_LEGEND,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -339,6 +344,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "profile", required_argument, NULL, ARG_PROFILE },
|
||||
{ "table", optional_argument, NULL, ARG_TABLE },
|
||||
{ "no-legend", optional_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -522,6 +528,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_legend = false;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -623,6 +641,7 @@ static int run(int argc, char *argv[]) {
|
||||
{ "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection },
|
||||
{ "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc },
|
||||
{ "fdstore", 2, VERB_ANY, 0, verb_fdstore },
|
||||
{ "image-policy", 2, 2, 0, verb_image_policy },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -643,6 +662,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_READ_ONLY,
|
||||
|
||||
@@ -38,6 +38,7 @@ extern bool arg_quiet;
|
||||
extern char *arg_profile;
|
||||
extern bool arg_legend;
|
||||
extern bool arg_table;
|
||||
extern ImagePolicy *arg_image_policy;
|
||||
|
||||
int acquire_bus(sd_bus **bus, bool *use_full_bus);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ systemd_analyze_sources = files(
|
||||
'analyze-exit-status.c',
|
||||
'analyze-fdstore.c',
|
||||
'analyze-filesystems.c',
|
||||
'analyze-image-policy.c',
|
||||
'analyze-inspect-elf.c',
|
||||
'analyze-log-control.c',
|
||||
'analyze-malloc.c',
|
||||
|
||||
@@ -52,6 +52,7 @@ char *arg_image = NULL;
|
||||
InstallSource arg_install_source = ARG_INSTALL_SOURCE_AUTO;
|
||||
char *arg_efi_boot_option_description = NULL;
|
||||
bool arg_dry_run = false;
|
||||
ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
|
||||
@@ -60,6 +61,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
int acquire_esp(
|
||||
bool unprivileged_mode,
|
||||
@@ -168,6 +170,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --boot-path=PATH Path to the $BOOT partition\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify disk image dissection policy\n"
|
||||
" --install-source=auto|image|host\n"
|
||||
" Where to pick files when using --root=/--image=\n"
|
||||
" -p --print-esp-path Print path to the EFI System Partition mount point\n"
|
||||
@@ -218,6 +222,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ARCH_ALL,
|
||||
ARG_EFI_BOOT_OPTION_DESCRIPTION,
|
||||
ARG_DRY_RUN,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -244,6 +249,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
|
||||
{ "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
|
||||
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -376,6 +382,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_dry_run = true;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -478,6 +496,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK,
|
||||
&unlink_dir,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "boot-entry.h"
|
||||
#include "image-policy.h"
|
||||
#include "json.h"
|
||||
#include "pager.h"
|
||||
|
||||
@@ -34,6 +35,7 @@ extern char *arg_image;
|
||||
extern InstallSource arg_install_source;
|
||||
extern char *arg_efi_boot_option_description;
|
||||
extern bool arg_dry_run;
|
||||
extern ImagePolicy *arg_image_policy;
|
||||
|
||||
static inline const char *arg_dollar_boot_path(void) {
|
||||
/* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
|
||||
|
||||
@@ -1156,6 +1156,30 @@ static int bus_property_get_exec_dir_symlink(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_image_policy(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
ImagePolicy **pp = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
r = image_policy_to_string(*pp ?: &image_policy_service, /* simplify= */ true, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_message_append(reply, "s", s);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -1324,6 +1348,9 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("ProtectHostname", "b", bus_property_get_bool, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RootImagePolicy", "s", property_get_image_policy, offsetof(ExecContext, root_image_policy), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MountImagePolicy", "s", property_get_image_policy, offsetof(ExecContext, mount_image_policy), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ExtensionImagePolicy", "s", property_get_image_policy, offsetof(ExecContext, extension_image_policy), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
/* Obsolete/redundant properties: */
|
||||
SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
|
||||
@@ -3900,6 +3927,40 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (STR_IN_SET(name, "RootImagePolicy", "MountImagePolicy", "ExtensionImagePolicy")) {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
const char *s;
|
||||
|
||||
r = sd_bus_message_read(message, "s", &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_policy_from_string(s, &p);
|
||||
if (r < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse image policy string: %s", s);
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
ImagePolicy **pp =
|
||||
streq(name, "RootImagePolicy") ? &c->root_image_policy :
|
||||
streq(name, "MountImagePolicy") ? &c->mount_image_policy :
|
||||
&c->extension_image_policy;
|
||||
|
||||
r = image_policy_to_string(p, /* simplify= */ true, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
image_policy_free(*pp);
|
||||
*pp = TAKE_PTR(p);
|
||||
|
||||
unit_write_settingf(
|
||||
u, flags, name,
|
||||
"%s=%s",
|
||||
name,
|
||||
t); /* no escaping necessary */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+16
-8
@@ -197,15 +197,23 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
|
||||
|
||||
propagate_directory = strjoina("/run/systemd/propagate/", u->id);
|
||||
if (is_image)
|
||||
r = mount_image_in_namespace(unit_pid,
|
||||
propagate_directory,
|
||||
"/run/systemd/incoming/",
|
||||
src, dest, read_only, make_file_or_directory, options);
|
||||
r = mount_image_in_namespace(
|
||||
unit_pid,
|
||||
propagate_directory,
|
||||
"/run/systemd/incoming/",
|
||||
src, dest,
|
||||
read_only,
|
||||
make_file_or_directory,
|
||||
options,
|
||||
c->mount_image_policy ?: &image_policy_service);
|
||||
else
|
||||
r = bind_mount_in_namespace(unit_pid,
|
||||
propagate_directory,
|
||||
"/run/systemd/incoming/",
|
||||
src, dest, read_only, make_file_or_directory);
|
||||
r = bind_mount_in_namespace(
|
||||
unit_pid,
|
||||
propagate_directory,
|
||||
"/run/systemd/incoming/",
|
||||
src, dest,
|
||||
read_only,
|
||||
make_file_or_directory);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in unit's namespace: %m", src, dest);
|
||||
|
||||
|
||||
+41
-30
@@ -3799,36 +3799,43 @@ static int apply_mount_namespace(
|
||||
if (asprintf(&extension_dir, "/run/user/" UID_FMT "/systemd/unit-extensions", geteuid()) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = setup_namespace(root_dir, root_image, context->root_image_options,
|
||||
&ns_info, read_write_paths,
|
||||
needs_sandboxing ? context->read_only_paths : NULL,
|
||||
needs_sandboxing ? context->inaccessible_paths : NULL,
|
||||
needs_sandboxing ? context->exec_paths : NULL,
|
||||
needs_sandboxing ? context->no_exec_paths : NULL,
|
||||
empty_directories,
|
||||
symlinks,
|
||||
bind_mounts,
|
||||
n_bind_mounts,
|
||||
context->temporary_filesystems,
|
||||
context->n_temporary_filesystems,
|
||||
context->mount_images,
|
||||
context->n_mount_images,
|
||||
tmp_dir,
|
||||
var_tmp_dir,
|
||||
creds_path,
|
||||
context->log_namespace,
|
||||
context->mount_propagation_flag,
|
||||
context->root_hash, context->root_hash_size, context->root_hash_path,
|
||||
context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path,
|
||||
context->root_verity,
|
||||
context->extension_images,
|
||||
context->n_extension_images,
|
||||
context->extension_directories,
|
||||
propagate_dir,
|
||||
incoming_dir,
|
||||
extension_dir,
|
||||
root_dir || root_image ? params->notify_socket : NULL,
|
||||
error_path);
|
||||
r = setup_namespace(
|
||||
root_dir,
|
||||
root_image,
|
||||
context->root_image_options,
|
||||
context->root_image_policy ?: &image_policy_service,
|
||||
&ns_info,
|
||||
read_write_paths,
|
||||
needs_sandboxing ? context->read_only_paths : NULL,
|
||||
needs_sandboxing ? context->inaccessible_paths : NULL,
|
||||
needs_sandboxing ? context->exec_paths : NULL,
|
||||
needs_sandboxing ? context->no_exec_paths : NULL,
|
||||
empty_directories,
|
||||
symlinks,
|
||||
bind_mounts,
|
||||
n_bind_mounts,
|
||||
context->temporary_filesystems,
|
||||
context->n_temporary_filesystems,
|
||||
context->mount_images,
|
||||
context->n_mount_images,
|
||||
context->mount_image_policy ?: &image_policy_service,
|
||||
tmp_dir,
|
||||
var_tmp_dir,
|
||||
creds_path,
|
||||
context->log_namespace,
|
||||
context->mount_propagation_flag,
|
||||
context->root_hash, context->root_hash_size, context->root_hash_path,
|
||||
context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path,
|
||||
context->root_verity,
|
||||
context->extension_images,
|
||||
context->n_extension_images,
|
||||
context->extension_image_policy ?: &image_policy_sysext,
|
||||
context->extension_directories,
|
||||
propagate_dir,
|
||||
incoming_dir,
|
||||
extension_dir,
|
||||
root_dir || root_image ? params->notify_socket : NULL,
|
||||
error_path);
|
||||
|
||||
/* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
|
||||
* that with a special, recognizable error ENOANO. In this case, silently proceed, but only if exclusively
|
||||
@@ -5767,6 +5774,10 @@ void exec_context_done(ExecContext *c) {
|
||||
|
||||
c->load_credentials = hashmap_free(c->load_credentials);
|
||||
c->set_credentials = hashmap_free(c->set_credentials);
|
||||
|
||||
c->root_image_policy = image_policy_free(c->root_image_policy);
|
||||
c->mount_image_policy = image_policy_free(c->mount_image_policy);
|
||||
c->extension_image_policy = image_policy_free(c->extension_image_policy);
|
||||
}
|
||||
|
||||
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
|
||||
|
||||
@@ -359,6 +359,8 @@ struct ExecContext {
|
||||
|
||||
Hashmap *set_credentials; /* output id → ExecSetCredential */
|
||||
Hashmap *load_credentials; /* output id → ExecLoadCredential */
|
||||
|
||||
ImagePolicy *root_image_policy, *mount_image_policy, *extension_image_policy;
|
||||
};
|
||||
|
||||
static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) {
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
{{type}}.RootDirectory, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_directory)
|
||||
{{type}}.RootImage, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_image)
|
||||
{{type}}.RootImageOptions, config_parse_root_image_options, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.RootImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.root_image_policy)
|
||||
{{type}}.RootHash, config_parse_exec_root_hash, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.RootVerity, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_verity)
|
||||
{{type}}.ExtensionDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.extension_directories)
|
||||
{{type}}.ExtensionImages, config_parse_extension_images, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.ExtensionImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.extension_image_policy)
|
||||
{{type}}.MountImages, config_parse_mount_images, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.MountImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.mount_image_policy)
|
||||
{{type}}.User, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.user)
|
||||
{{type}}.Group, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.group)
|
||||
{{type}}.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof({{type}}, exec_context.supplementary_groups)
|
||||
|
||||
@@ -1705,6 +1705,45 @@ int config_parse_root_image_options(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_image_policy(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(image_policy_freep) ImagePolicy *np = NULL;
|
||||
ImagePolicy **p = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*p = image_policy_free(*p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = image_policy_from_string(rvalue, &np);
|
||||
if (r == -ENOTUNIQ)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate rule in image policy, refusing: %s", rvalue);
|
||||
if (r == -EBADSLT)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Unknown partition type in image policy, refusing: %s", rvalue);
|
||||
if (r == -EBADRQC)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Unknown partition policy flag in image policy, refusing: %s", rvalue);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse image policy, refusing: %s", rvalue);
|
||||
|
||||
image_policy_free(*p);
|
||||
*p = TAKE_PTR(np);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_root_hash(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
||||
@@ -52,6 +52,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_apivfs);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_root_image_options);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_image_policy);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
|
||||
|
||||
+27
-9
@@ -1240,7 +1240,10 @@ static int mount_mqueuefs(const MountEntry *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_image(const MountEntry *m, const char *root_directory) {
|
||||
static int mount_image(
|
||||
const MountEntry *m,
|
||||
const char *root_directory,
|
||||
const ImagePolicy *image_policy) {
|
||||
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
|
||||
*host_os_release_sysext_level = NULL;
|
||||
@@ -1262,8 +1265,15 @@ static int mount_image(const MountEntry *m, const char *root_directory) {
|
||||
}
|
||||
|
||||
r = verity_dissect_and_mount(
|
||||
/* src_fd= */ -1, mount_entry_source(m), mount_entry_path(m), m->image_options,
|
||||
host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
|
||||
/* src_fd= */ -1,
|
||||
mount_entry_source(m),
|
||||
mount_entry_path(m),
|
||||
m->image_options,
|
||||
image_policy,
|
||||
host_os_release_id,
|
||||
host_os_release_version_id,
|
||||
host_os_release_sysext_level,
|
||||
NULL);
|
||||
if (r == -ENOENT && m->ignore)
|
||||
return 0;
|
||||
if (r == -ESTALE && host_os_release_id)
|
||||
@@ -1336,6 +1346,8 @@ static int follow_symlink(
|
||||
static int apply_one_mount(
|
||||
const char *root_directory,
|
||||
MountEntry *m,
|
||||
const ImagePolicy *mount_image_policy,
|
||||
const ImagePolicy *extension_image_policy,
|
||||
const NamespaceInfo *ns_info) {
|
||||
|
||||
_cleanup_free_ char *inaccessible = NULL;
|
||||
@@ -1506,10 +1518,10 @@ static int apply_one_mount(
|
||||
return mount_mqueuefs(m);
|
||||
|
||||
case MOUNT_IMAGES:
|
||||
return mount_image(m, NULL);
|
||||
return mount_image(m, NULL, mount_image_policy);
|
||||
|
||||
case EXTENSION_IMAGES:
|
||||
return mount_image(m, root_directory);
|
||||
return mount_image(m, root_directory, extension_image_policy);
|
||||
|
||||
case OVERLAY_MOUNT:
|
||||
return mount_overlay(m);
|
||||
@@ -1779,6 +1791,8 @@ static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) {
|
||||
|
||||
static int apply_mounts(
|
||||
const char *root,
|
||||
const ImagePolicy *mount_image_policy,
|
||||
const ImagePolicy *extension_image_policy,
|
||||
const NamespaceInfo *ns_info,
|
||||
MountEntry *mounts,
|
||||
size_t *n_mounts,
|
||||
@@ -1833,7 +1847,7 @@ static int apply_mounts(
|
||||
break;
|
||||
}
|
||||
|
||||
r = apply_one_mount(root, m, ns_info);
|
||||
r = apply_one_mount(root, m, mount_image_policy, extension_image_policy, ns_info);
|
||||
if (r < 0) {
|
||||
if (error_path && mount_entry_path(m))
|
||||
*error_path = strdup(mount_entry_path(m));
|
||||
@@ -2012,7 +2026,8 @@ static int verity_settings_prepare(
|
||||
int setup_namespace(
|
||||
const char* root_directory,
|
||||
const char* root_image,
|
||||
const MountOptions *root_image_options,
|
||||
const MountOptions *root_image_mount_options,
|
||||
const ImagePolicy *root_image_policy,
|
||||
const NamespaceInfo *ns_info,
|
||||
char** read_write_paths,
|
||||
char** read_only_paths,
|
||||
@@ -2027,6 +2042,7 @@ int setup_namespace(
|
||||
size_t n_temporary_filesystems,
|
||||
const MountImage *mount_images,
|
||||
size_t n_mount_images,
|
||||
const ImagePolicy *mount_image_policy,
|
||||
const char* tmp_dir,
|
||||
const char* var_tmp_dir,
|
||||
const char *creds_path,
|
||||
@@ -2041,6 +2057,7 @@ int setup_namespace(
|
||||
const char *verity_data_path,
|
||||
const MountImage *extension_images,
|
||||
size_t n_extension_images,
|
||||
const ImagePolicy *extension_image_policy,
|
||||
char **extension_directories,
|
||||
const char *propagate_dir,
|
||||
const char *incoming_dir,
|
||||
@@ -2114,7 +2131,8 @@ int setup_namespace(
|
||||
r = dissect_loop_device(
|
||||
loop_device,
|
||||
&verity,
|
||||
root_image_options,
|
||||
root_image_mount_options,
|
||||
root_image_policy,
|
||||
dissect_image_flags,
|
||||
&dissected_image);
|
||||
if (r < 0)
|
||||
@@ -2502,7 +2520,7 @@ int setup_namespace(
|
||||
(void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
|
||||
|
||||
/* Now make the magic happen */
|
||||
r = apply_mounts(root, ns_info, mounts, &n_mounts, exec_dir_symlinks, error_path);
|
||||
r = apply_mounts(root, mount_image_policy, extension_image_policy, ns_info, mounts, &n_mounts, exec_dir_symlinks, error_path);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ int setup_namespace(
|
||||
const char *root_directory,
|
||||
const char *root_image,
|
||||
const MountOptions *root_image_options,
|
||||
const ImagePolicy *root_image_policy,
|
||||
const NamespaceInfo *ns_info,
|
||||
char **read_write_paths,
|
||||
char **read_only_paths,
|
||||
@@ -117,6 +118,7 @@ int setup_namespace(
|
||||
size_t n_temporary_filesystems,
|
||||
const MountImage *mount_images,
|
||||
size_t n_mount_images,
|
||||
const ImagePolicy *mount_image_policy,
|
||||
const char *tmp_dir,
|
||||
const char *var_tmp_dir,
|
||||
const char *creds_path,
|
||||
@@ -131,6 +133,7 @@ int setup_namespace(
|
||||
const char *root_verity,
|
||||
const MountImage *extension_images,
|
||||
size_t n_extension_images,
|
||||
const ImagePolicy *extension_image_policy,
|
||||
char **extension_directories,
|
||||
const char *propagate_dir,
|
||||
const char *incoming_dir,
|
||||
|
||||
+36
-18
@@ -64,9 +64,11 @@ static const char* arg_output = NULL;
|
||||
static bool arg_reverse = false;
|
||||
static bool arg_quiet = false;
|
||||
static bool arg_all = false;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_debugger_args, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static int add_match(sd_journal *j, const char *match) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
@@ -198,6 +200,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
|
||||
" --all Look at all journal files instead of local ones\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@@ -220,29 +223,31 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ROOT,
|
||||
ARG_IMAGE,
|
||||
ARG_ALL,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version" , no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "debugger", required_argument, NULL, ARG_DEBUGGER },
|
||||
{ "debugger-arguments", required_argument, NULL, 'A' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "field", required_argument, NULL, 'F' },
|
||||
{ "file", required_argument, NULL, ARG_FILE },
|
||||
{ "directory", required_argument, NULL, 'D' },
|
||||
{ "reverse", no_argument, NULL, 'r' },
|
||||
{ "since", required_argument, NULL, 'S' },
|
||||
{ "until", required_argument, NULL, 'U' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "all", no_argument, NULL, ARG_ALL },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version" , no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "debugger", required_argument, NULL, ARG_DEBUGGER },
|
||||
{ "debugger-arguments", required_argument, NULL, 'A' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "field", required_argument, NULL, 'F' },
|
||||
{ "file", required_argument, NULL, ARG_FILE },
|
||||
{ "directory", required_argument, NULL, 'D' },
|
||||
{ "reverse", no_argument, NULL, 'r' },
|
||||
{ "since", required_argument, NULL, 'S' },
|
||||
{ "until", required_argument, NULL, 'U' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "all", no_argument, NULL, ARG_ALL },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -363,6 +368,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_all = true;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1361,6 +1378,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
|
||||
+69
-2
@@ -61,6 +61,7 @@ static enum {
|
||||
ACTION_COPY_FROM,
|
||||
ACTION_COPY_TO,
|
||||
ACTION_DISCOVER,
|
||||
ACTION_VALIDATE,
|
||||
} arg_action = ACTION_DISSECT;
|
||||
static char *arg_image = NULL;
|
||||
static char *arg_path = NULL;
|
||||
@@ -83,6 +84,7 @@ static bool arg_rmdir = false;
|
||||
static bool arg_in_memory = false;
|
||||
static char **arg_argv = NULL;
|
||||
static char *arg_loop_ref = NULL;
|
||||
static ImagePolicy* arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
|
||||
@@ -126,6 +128,8 @@ static int help(void) {
|
||||
" 'base64:'\n"
|
||||
" --verity-data=PATH Specify data file with hash tree for verity if it is\n"
|
||||
" not embedded in IMAGE\n"
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify image dissection policy\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" --loop-ref=NAME Set reference string for loopback device\n"
|
||||
@@ -145,6 +149,7 @@ static int help(void) {
|
||||
" -x --copy-from Copy files from image to host\n"
|
||||
" -a --copy-to Copy files from host to image\n"
|
||||
" --discover Discover DDIs in well known directories\n"
|
||||
" --validate Validate image and image policy\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@@ -221,6 +226,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ATTACH,
|
||||
ARG_DETACH,
|
||||
ARG_LOOP_REF,
|
||||
ARG_IMAGE_POLICY,
|
||||
ARG_VALIDATE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -250,6 +257,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "discover", no_argument, NULL, ARG_DISCOVER },
|
||||
{ "loop-ref", required_argument, NULL, ARG_LOOP_REF },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{ "validate", no_argument, NULL, ARG_VALIDATE },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -457,6 +466,22 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_VALIDATE:
|
||||
arg_action = ACTION_VALIDATE;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -476,7 +501,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
/* when dumping image info be even more liberal than otherwise, do not even require a single valid partition */
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_ALLOW_EMPTY;
|
||||
break;
|
||||
|
||||
case ACTION_MOUNT:
|
||||
@@ -593,7 +619,19 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (optind != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected no argument.");
|
||||
break;
|
||||
|
||||
case ACTION_VALIDATE:
|
||||
if (optind + 1 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path as only argument.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
arg_flags &= ~(DISSECT_IMAGE_PIN_PARTITION_DEVICES|DISSECT_IMAGE_ADD_PARTITION_DEVICES);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1689,6 +1727,31 @@ static int action_detach(const char *path) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_validate(void) {
|
||||
int r;
|
||||
|
||||
r = dissect_image_file_and_warn(
|
||||
arg_image,
|
||||
&arg_verity_settings,
|
||||
NULL,
|
||||
arg_image_policy,
|
||||
arg_flags,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isatty(STDOUT_FILENO) && emoji_enabled())
|
||||
printf("%s ", special_glyph(SPECIAL_GLYPH_SPARKLES));
|
||||
|
||||
printf("%sOK%s", ansi_highlight_green(), ansi_normal());
|
||||
|
||||
if (isatty(STDOUT_FILENO) && emoji_enabled())
|
||||
printf(" %s", special_glyph(SPECIAL_GLYPH_SPARKLES));
|
||||
|
||||
putc('\n', stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
@@ -1731,6 +1794,9 @@ static int run(int argc, char *argv[]) {
|
||||
* available we turn off partition table
|
||||
* support */
|
||||
|
||||
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;
|
||||
|
||||
@@ -1750,7 +1816,8 @@ static int run(int argc, char *argv[]) {
|
||||
r = dissect_loop_device_and_warn(
|
||||
d,
|
||||
&arg_verity_settings,
|
||||
NULL,
|
||||
/* mount_options= */ NULL,
|
||||
arg_image_policy,
|
||||
arg_flags,
|
||||
&m);
|
||||
if (r < 0)
|
||||
|
||||
@@ -73,6 +73,7 @@ static bool arg_delete_root_password = false;
|
||||
static bool arg_root_password_is_hashed = false;
|
||||
static bool arg_welcome = true;
|
||||
static bool arg_reset = false;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
@@ -82,6 +83,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static bool press_any_key(void) {
|
||||
char k = 0;
|
||||
@@ -1163,7 +1165,8 @@ static int help(void) {
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on an alternate filesystem image\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" --locale=LOCALE Set primary locale (LANG=)\n"
|
||||
" --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n"
|
||||
" --keymap=KEYMAP Set keymap\n"
|
||||
@@ -1234,6 +1237,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_DELETE_ROOT_PASSWORD,
|
||||
ARG_WELCOME,
|
||||
ARG_RESET,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -1270,6 +1274,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "delete-root-password", no_argument, NULL, ARG_DELETE_ROOT_PASSWORD },
|
||||
{ "welcome", required_argument, NULL, ARG_WELCOME },
|
||||
{ "reset", no_argument, NULL, ARG_RESET },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1476,6 +1481,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_reset = true;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1528,6 +1544,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "fstab-util.h"
|
||||
#include "generator.h"
|
||||
#include "gpt.h"
|
||||
#include "image-policy.h"
|
||||
#include "initrd-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "mountpoint-util.h"
|
||||
@@ -43,6 +44,9 @@ static bool arg_root_enabled = true;
|
||||
static char *arg_root_fstype = NULL;
|
||||
static char *arg_root_options = NULL;
|
||||
static int arg_root_rw = -1;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
|
||||
@@ -744,7 +748,9 @@ static int enumerate_partitions(dev_t devnum) {
|
||||
|
||||
r = dissect_loop_device(
|
||||
loop,
|
||||
NULL, NULL,
|
||||
/* verity= */ NULL,
|
||||
/* mount_options= */ NULL,
|
||||
arg_image_policy ?: &image_policy_host,
|
||||
DISSECT_IMAGE_GPT_ONLY|
|
||||
DISSECT_IMAGE_USR_NO_ROOT|
|
||||
DISSECT_IMAGE_DISKSEQ_DEVNODE,
|
||||
@@ -882,6 +888,20 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
arg_root_rw = true;
|
||||
else if (proc_cmdline_key_streq(key, "ro") && !value)
|
||||
arg_root_rw = false;
|
||||
else if (proc_cmdline_key_streq(key, "systemd.image_policy")) {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = image_policy_from_string(value, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", value);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ static Set *arg_output_fields = NULL;
|
||||
static const char *arg_pattern = NULL;
|
||||
static pcre2_code *arg_compiled_pattern = NULL;
|
||||
static PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
|
||||
ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
|
||||
@@ -145,6 +146,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_output_fields, set_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pattern_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static enum {
|
||||
ACTION_SHOW,
|
||||
@@ -326,8 +328,9 @@ static int help(void) {
|
||||
" -m --merge Show entries from all available journals\n"
|
||||
" -D --directory=PATH Show journal files from directory\n"
|
||||
" --file=PATH Show journal file\n"
|
||||
" --root=ROOT Operate on files below a root directory\n"
|
||||
" --image=IMAGE Operate on files in filesystem image\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" --namespace=NAMESPACE Show journal data from specified journal namespace\n"
|
||||
"\n%3$sFiltering Options:%4$s\n"
|
||||
" -S --since=DATE Show entries not older than the specified date\n"
|
||||
@@ -444,6 +447,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_NO_HOSTNAME,
|
||||
ARG_OUTPUT_FIELDS,
|
||||
ARG_NAMESPACE,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -511,6 +515,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
|
||||
{ "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
|
||||
{ "namespace", required_argument, NULL, ARG_NAMESPACE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1033,7 +1038,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
}
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2126,6 +2141,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
|
||||
@@ -22,9 +22,11 @@ static char *arg_root = NULL;
|
||||
static char *arg_image = NULL;
|
||||
static bool arg_commit = false;
|
||||
static bool arg_print = false;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@@ -36,12 +38,13 @@ static int help(void) {
|
||||
|
||||
printf("%s [OPTIONS...]\n"
|
||||
"\n%sInitialize /etc/machine-id from a random source.%s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --root=PATH Operate relative to root path\n"
|
||||
" --image=PATH Operate relative to image file\n"
|
||||
" --commit Commit transient ID\n"
|
||||
" --print Print used machine ID\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" --commit Commit transient ID\n"
|
||||
" --print Print used machine ID\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
@@ -59,15 +62,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_IMAGE,
|
||||
ARG_COMMIT,
|
||||
ARG_PRINT,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "commit", no_argument, NULL, ARG_COMMIT },
|
||||
{ "print", no_argument, NULL, ARG_PRINT },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "commit", no_argument, NULL, ARG_COMMIT },
|
||||
{ "print", no_argument, NULL, ARG_PRINT },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -106,6 +111,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_print = true;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -141,6 +157,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
|
||||
@@ -313,7 +313,7 @@ int bus_image_method_get_hostname(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image);
|
||||
r = image_read_metadata(image, &image_policy_container);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -331,7 +331,7 @@ int bus_image_method_get_machine_id(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image);
|
||||
r = image_read_metadata(image, &image_policy_container);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -359,7 +359,7 @@ int bus_image_method_get_machine_info(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image);
|
||||
r = image_read_metadata(image, &image_policy_container);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -376,7 +376,7 @@ int bus_image_method_get_os_release(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image);
|
||||
r = image_read_metadata(image, &image_policy_container);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
|
||||
+19
-1
@@ -234,6 +234,7 @@ static char **arg_bind_user = NULL;
|
||||
static bool arg_suppress_sync = false;
|
||||
static char *arg_settings_filename = NULL;
|
||||
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_template, freep);
|
||||
@@ -268,6 +269,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_reset);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static int handle_arg_console(const char *arg) {
|
||||
if (streq(arg, "help")) {
|
||||
@@ -330,6 +332,7 @@ static int help(void) {
|
||||
" remove it after exit\n"
|
||||
" -i --image=PATH Root file system disk image (or device node) for\n"
|
||||
" the container\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" --oci-bundle=PATH OCI bundle directory\n"
|
||||
" --read-only Mount the root directory read-only\n"
|
||||
" --volatile[=MODE] Run the system in volatile mode\n"
|
||||
@@ -732,6 +735,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_LOAD_CREDENTIAL,
|
||||
ARG_BIND_USER,
|
||||
ARG_SUPPRESS_SYNC,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -805,6 +809,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
|
||||
{ "bind-user", required_argument, NULL, ARG_BIND_USER },
|
||||
{ "suppress-sync", required_argument, NULL, ARG_SUPPRESS_SYNC },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1699,6 +1704,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_settings_mask |= SETTING_SUPPRESS_SYNC;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -5758,7 +5775,8 @@ static int run(int argc, char *argv[]) {
|
||||
r = dissect_loop_device_and_warn(
|
||||
loop,
|
||||
&arg_verity_settings,
|
||||
NULL,
|
||||
/* mount_options=*/ NULL,
|
||||
arg_image_policy ?: &image_policy_container,
|
||||
dissect_image_flags,
|
||||
&dissected_image);
|
||||
if (r == -ENOPKG) {
|
||||
|
||||
@@ -148,6 +148,7 @@ static FilterPartitionsType arg_filter_partitions_type = FILTER_PARTITIONS_NONE;
|
||||
static sd_id128_t *arg_defer_partitions = NULL;
|
||||
static size_t arg_n_defer_partitions = 0;
|
||||
static uint64_t arg_sector_size = 0;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
@@ -158,6 +159,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
typedef struct FreeArea FreeArea;
|
||||
|
||||
@@ -5653,6 +5655,8 @@ static int help(void) {
|
||||
" --can-factory-reset Test whether factory reset is defined\n"
|
||||
" --root=PATH Operate relative to root path\n"
|
||||
" --image=PATH Operate relative to image file\n"
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify disk image dissection policy\n"
|
||||
" --definitions=DIR Find partition definitions in specified directory\n"
|
||||
" --key-file=PATH Key to use when encrypting partitions\n"
|
||||
" --private-key=PATH Private key to use when generating verity roothash\n"
|
||||
@@ -5718,6 +5722,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_EXCLUDE_PARTITIONS,
|
||||
ARG_DEFER_PARTITIONS,
|
||||
ARG_SECTOR_SIZE,
|
||||
ARG_SKIP_PARTITIONS,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -5749,6 +5755,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
|
||||
{ "defer-partitions", required_argument, NULL, ARG_DEFER_PARTITIONS },
|
||||
{ "sector-size", required_argument, NULL, ARG_SECTOR_SIZE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -6043,6 +6050,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -6543,6 +6562,7 @@ static int run(int argc, char *argv[]) {
|
||||
* systems */
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_MOUNT_READ_ONLY |
|
||||
(arg_node ? DISSECT_IMAGE_DEVICE_READ_ONLY : 0) | /* If a different node to make changes to is specified let's open the device in read-only mode) */
|
||||
DISSECT_IMAGE_GPT_ONLY |
|
||||
|
||||
+27
-3
@@ -324,6 +324,7 @@ static int portable_extract_by_path(
|
||||
bool path_is_extension,
|
||||
bool relax_extension_release_check,
|
||||
char **matches,
|
||||
const ImagePolicy *image_policy,
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
sd_bus_error *error) {
|
||||
@@ -369,7 +370,9 @@ static int portable_extract_by_path(
|
||||
|
||||
r = dissect_loop_device(
|
||||
d,
|
||||
NULL, NULL,
|
||||
/* verity= */ NULL,
|
||||
/* mount_options= */ NULL,
|
||||
image_policy,
|
||||
DISSECT_IMAGE_READ_ONLY |
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
@@ -510,6 +513,7 @@ static int extract_image_and_extensions(
|
||||
char **extension_image_paths,
|
||||
bool validate_sysext,
|
||||
bool relax_extension_release_check,
|
||||
const ImagePolicy *image_policy,
|
||||
Image **ret_image,
|
||||
OrderedHashmap **ret_extension_images,
|
||||
OrderedHashmap **ret_extension_releases,
|
||||
@@ -558,7 +562,15 @@ static int extract_image_and_extensions(
|
||||
}
|
||||
}
|
||||
|
||||
r = portable_extract_by_path(image->path, /* path_is_extension= */ false, /* relax_extension_release_check= */ false, matches, &os_release, &unit_files, error);
|
||||
r = portable_extract_by_path(
|
||||
image->path,
|
||||
/* path_is_extension= */ false,
|
||||
/* relax_extension_release_check= */ false,
|
||||
matches,
|
||||
image_policy,
|
||||
&os_release,
|
||||
&unit_files,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -591,7 +603,15 @@ static int extract_image_and_extensions(
|
||||
_cleanup_strv_free_ char **extension_release = NULL;
|
||||
const char *e;
|
||||
|
||||
r = portable_extract_by_path(ext->path, /* path_is_extension= */ true, relax_extension_release_check, matches, &extension_release_meta, &extra_unit_files, error);
|
||||
r = portable_extract_by_path(
|
||||
ext->path,
|
||||
/* path_is_extension= */ true,
|
||||
relax_extension_release_check,
|
||||
matches,
|
||||
image_policy,
|
||||
&extension_release_meta,
|
||||
&extra_unit_files,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -657,6 +677,7 @@ int portable_extract(
|
||||
const char *name_or_path,
|
||||
char **matches,
|
||||
char **extension_image_paths,
|
||||
const ImagePolicy *image_policy,
|
||||
PortableFlags flags,
|
||||
PortableMetadata **ret_os_release,
|
||||
OrderedHashmap **ret_extension_releases,
|
||||
@@ -679,6 +700,7 @@ int portable_extract(
|
||||
extension_image_paths,
|
||||
/* validate_sysext= */ false,
|
||||
/* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT),
|
||||
image_policy,
|
||||
&image,
|
||||
&extension_images,
|
||||
&extension_releases,
|
||||
@@ -1392,6 +1414,7 @@ int portable_attach(
|
||||
char **matches,
|
||||
const char *profile,
|
||||
char **extension_image_paths,
|
||||
const ImagePolicy *image_policy,
|
||||
PortableFlags flags,
|
||||
PortableChange **changes,
|
||||
size_t *n_changes,
|
||||
@@ -1412,6 +1435,7 @@ int portable_attach(
|
||||
extension_image_paths,
|
||||
/* validate_sysext= */ true,
|
||||
/* relax_extension_release_check= */ FLAGS_SET(flags, PORTABLE_FORCE_SYSEXT),
|
||||
image_policy,
|
||||
&image,
|
||||
&extension_images,
|
||||
&extension_releases,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "dissect-image.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "set.h"
|
||||
@@ -67,9 +68,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
|
||||
|
||||
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
|
||||
|
||||
int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
|
||||
int portable_extract(const char *image, char **matches, char **extension_image_paths, const ImagePolicy *image_policy, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
|
||||
|
||||
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, const ImagePolicy* image_policy, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
|
||||
int portable_get_state(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
|
||||
|
||||
@@ -60,7 +60,7 @@ int bus_image_common_get_os_release(
|
||||
return 1;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
r = image_read_metadata(image);
|
||||
r = image_read_metadata(image, &image_policy_service);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -163,6 +163,7 @@ int bus_image_common_get_metadata(
|
||||
image->path,
|
||||
matches,
|
||||
extension_images,
|
||||
/* image_policy= */ NULL,
|
||||
flags,
|
||||
&os_release,
|
||||
&extension_releases,
|
||||
@@ -387,6 +388,7 @@ int bus_image_common_attach(
|
||||
matches,
|
||||
profile,
|
||||
extension_images,
|
||||
/* image_policy= */ NULL,
|
||||
flags,
|
||||
&changes,
|
||||
&n_changes,
|
||||
@@ -729,6 +731,7 @@ int bus_image_common_reattach(
|
||||
matches,
|
||||
profile,
|
||||
extension_images,
|
||||
/* image_policy= */ NULL,
|
||||
flags,
|
||||
&changes_attached,
|
||||
&n_changes_attached,
|
||||
|
||||
@@ -959,7 +959,10 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
||||
"ProcSubset",
|
||||
"NetworkNamespacePath",
|
||||
"IPCNamespacePath",
|
||||
"LogNamespace"))
|
||||
"LogNamespace",
|
||||
"RootImagePolicy",
|
||||
"MountImagePolicy",
|
||||
"ExtensionImagePolicy"))
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "IgnoreSIGPIPE",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "id128-util.h"
|
||||
#include "initrd-util.h"
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
@@ -73,6 +74,19 @@ static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
|
||||
"/usr/lib/confexts\0",
|
||||
};
|
||||
|
||||
/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext in extension
|
||||
* search dir) */
|
||||
static const char* const image_search_path_initrd[_IMAGE_CLASS_MAX] = {
|
||||
/* (entries that aren't listed here will get the same search path as for the non initrd-case) */
|
||||
|
||||
[IMAGE_EXTENSION] = "/etc/extensions\0" /* only place symlinks here */
|
||||
"/run/extensions\0" /* and here too */
|
||||
"/var/lib/extensions\0" /* the main place for images */
|
||||
"/usr/local/lib/extensions\0"
|
||||
"/usr/lib/extensions\0"
|
||||
"/.extra/sysext\0" /* put sysext picked up by systemd-stub last, since not trusted */
|
||||
};
|
||||
|
||||
static Image *image_free(Image *i) {
|
||||
assert(i);
|
||||
|
||||
@@ -446,6 +460,14 @@ static int image_make(
|
||||
return -EMEDIUMTYPE;
|
||||
}
|
||||
|
||||
static const char *pick_image_search_path(ImageClass class) {
|
||||
if (class < 0 || class >= _IMAGE_CLASS_MAX)
|
||||
return NULL;
|
||||
|
||||
/* Use the initrd search path if there is one, otherwise use the common one */
|
||||
return in_initrd() && image_search_path_initrd[class] ? image_search_path_initrd[class] : image_search_path[class];
|
||||
}
|
||||
|
||||
int image_find(ImageClass class,
|
||||
const char *name,
|
||||
const char *root,
|
||||
@@ -461,7 +483,7 @@ int image_find(ImageClass class,
|
||||
if (!image_name_is_valid(name))
|
||||
return -ENOENT;
|
||||
|
||||
NULSTR_FOREACH(path, image_search_path[class]) {
|
||||
NULSTR_FOREACH(path, pick_image_search_path(class)) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct stat st;
|
||||
@@ -560,7 +582,7 @@ int image_discover(
|
||||
assert(class < _IMAGE_CLASS_MAX);
|
||||
assert(h);
|
||||
|
||||
NULSTR_FOREACH(path, image_search_path[class]) {
|
||||
NULSTR_FOREACH(path, pick_image_search_path(class)) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
|
||||
@@ -1138,7 +1160,7 @@ int image_set_limit(Image *i, uint64_t referenced_max) {
|
||||
return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
|
||||
}
|
||||
|
||||
int image_read_metadata(Image *i) {
|
||||
int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
|
||||
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
|
||||
int r;
|
||||
|
||||
@@ -1219,7 +1241,9 @@ int image_read_metadata(Image *i) {
|
||||
|
||||
r = dissect_loop_device(
|
||||
d,
|
||||
NULL, NULL,
|
||||
/* verity= */ NULL,
|
||||
/* mount_options= */ NULL,
|
||||
image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
@@ -1287,7 +1311,7 @@ bool image_in_search_path(
|
||||
|
||||
assert(image);
|
||||
|
||||
NULSTR_FOREACH(path, image_search_path[class]) {
|
||||
NULSTR_FOREACH(path, pick_image_search_path(class)) {
|
||||
const char *p, *q;
|
||||
size_t k;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "image-policy.h"
|
||||
#include "lock-util.h"
|
||||
#include "macro.h"
|
||||
#include "os-util.h"
|
||||
@@ -75,7 +76,7 @@ int image_name_lock(const char *name, int operation, LockFile *ret);
|
||||
|
||||
int image_set_limit(Image *i, uint64_t referenced_max);
|
||||
|
||||
int image_read_metadata(Image *i);
|
||||
int image_read_metadata(Image *i, const ImagePolicy *image_policy);
|
||||
|
||||
bool image_in_search_path(ImageClass class, const char *root, const char *image);
|
||||
|
||||
|
||||
+335
-93
@@ -301,7 +301,99 @@ not_found:
|
||||
}
|
||||
|
||||
#if HAVE_BLKID
|
||||
static int dissected_image_probe_filesystems(DissectedImage *m, int fd) {
|
||||
static int image_policy_may_use(
|
||||
const ImagePolicy *policy,
|
||||
PartitionDesignator designator) {
|
||||
|
||||
PartitionPolicyFlags f;
|
||||
|
||||
/* For each partition we find in the partition table do a first check if it may exist at all given
|
||||
* the policy, or if it shall be ignored. */
|
||||
|
||||
f = image_policy_get_exhaustively(policy, designator);
|
||||
if (f < 0)
|
||||
return f;
|
||||
|
||||
if ((f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT)
|
||||
/* only flag set in policy is "absent"? then this partition may not exist at all */
|
||||
return log_debug_errno(
|
||||
SYNTHETIC_ERRNO(ERFKILL),
|
||||
"Partition of designator '%s' exists, but not allowed by policy, refusing.",
|
||||
partition_designator_to_string(designator));
|
||||
if ((f & _PARTITION_POLICY_USE_MASK & ~PARTITION_POLICY_ABSENT) == PARTITION_POLICY_UNUSED) {
|
||||
/* only "unused" or "unused" + "absent" are set? then don't use it */
|
||||
log_debug("Partition of designator '%s' exists, and policy dictates to ignore it, doing so.",
|
||||
partition_designator_to_string(designator));
|
||||
return false; /* ignore! */
|
||||
}
|
||||
|
||||
return true; /* use! */
|
||||
}
|
||||
|
||||
static int image_policy_check_protection(
|
||||
const ImagePolicy *policy,
|
||||
PartitionDesignator designator,
|
||||
PartitionPolicyFlags found_flags) {
|
||||
|
||||
PartitionPolicyFlags policy_flags;
|
||||
|
||||
/* Checks if the flags in the policy for the designated partition overlap the flags of what we found */
|
||||
|
||||
if (found_flags < 0)
|
||||
return found_flags;
|
||||
|
||||
policy_flags = image_policy_get_exhaustively(policy, designator);
|
||||
if (policy_flags < 0)
|
||||
return policy_flags;
|
||||
|
||||
if ((found_flags & policy_flags) == 0) {
|
||||
_cleanup_free_ char *found_flags_string = NULL, *policy_flags_string = NULL;
|
||||
|
||||
(void) partition_policy_flags_to_string(found_flags, /* simplify= */ true, &found_flags_string);
|
||||
(void) partition_policy_flags_to_string(policy_flags, /* simplify= */ true, &policy_flags_string);
|
||||
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s discovered with policy '%s' but '%s' was required, refusing.",
|
||||
partition_designator_to_string(designator),
|
||||
strnull(found_flags_string), strnull(policy_flags_string));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int image_policy_check_partition_flags(
|
||||
const ImagePolicy *policy,
|
||||
PartitionDesignator designator,
|
||||
uint64_t gpt_flags) {
|
||||
|
||||
PartitionPolicyFlags policy_flags;
|
||||
bool b;
|
||||
|
||||
/* Checks if the partition flags in the policy match reality */
|
||||
|
||||
policy_flags = image_policy_get_exhaustively(policy, designator);
|
||||
if (policy_flags < 0)
|
||||
return policy_flags;
|
||||
|
||||
b = FLAGS_SET(gpt_flags, SD_GPT_FLAG_READ_ONLY);
|
||||
if ((policy_flags & _PARTITION_POLICY_READ_ONLY_MASK) == (b ? PARTITION_POLICY_READ_ONLY_OFF : PARTITION_POLICY_READ_ONLY_ON))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s has 'read-only' flag incorrectly set (must be %s, is %s), refusing.",
|
||||
partition_designator_to_string(designator),
|
||||
one_zero(!b), one_zero(b));
|
||||
|
||||
b = FLAGS_SET(gpt_flags, SD_GPT_FLAG_GROWFS);
|
||||
if ((policy_flags & _PARTITION_POLICY_GROWFS_MASK) == (b ? PARTITION_POLICY_GROWFS_OFF : PARTITION_POLICY_GROWFS_ON))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s has 'growfs' flag incorrectly set (must be %s, is %s), refusing.",
|
||||
partition_designator_to_string(designator),
|
||||
one_zero(!b), one_zero(b));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dissected_image_probe_filesystems(
|
||||
DissectedImage *m,
|
||||
int fd,
|
||||
const ImagePolicy *policy) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@@ -310,6 +402,7 @@ static int dissected_image_probe_filesystems(DissectedImage *m, int fd) {
|
||||
|
||||
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
DissectedPartition *p = m->partitions + i;
|
||||
PartitionPolicyFlags found_flags;
|
||||
|
||||
if (!p->found)
|
||||
continue;
|
||||
@@ -325,14 +418,34 @@ static int dissected_image_probe_filesystems(DissectedImage *m, int fd) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (streq_ptr(p->fstype, "crypto_LUKS"))
|
||||
if (streq_ptr(p->fstype, "crypto_LUKS")) {
|
||||
m->encrypted = true;
|
||||
found_flags = PARTITION_POLICY_ENCRYPTED; /* found this one, and its definitely encrypted */
|
||||
} else
|
||||
/* found it, but it's definitely not encrypted, hence mask the encrypted flag, but
|
||||
* set all other ways that indicate "present". */
|
||||
found_flags = PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED;
|
||||
|
||||
if (p->fstype && fstype_is_ro(p->fstype))
|
||||
p->rw = false;
|
||||
|
||||
if (!p->rw)
|
||||
p->growfs = false;
|
||||
|
||||
/* We might have learnt more about the file system now (i.e. whether it is encrypted or not),
|
||||
* hence we need to validate this against policy again, to see if the policy still matches
|
||||
* with this new information. Note that image_policy_check_protection() will check for
|
||||
* overlap between what's allowed in the policy and what we pass as 'found_policy' here. In
|
||||
* the unencrypted case we thus might pass an overly unspecific mask here (i.e. unprotected
|
||||
* OR verity OR signed), but that's fine since the earlier policy check already checked more
|
||||
* specific which of those three cases where OK. Keep in mind that this function here only
|
||||
* looks at specific partitions (and thus can only deduce encryption or not) but not the
|
||||
* overall partition table (and thus cannot deduce verity or not). The earlier dissection
|
||||
* checks already did the relevant checks that look at the whole partition table, and
|
||||
* enforced policy there as needed. */
|
||||
r = image_policy_check_protection(policy, i, found_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -363,9 +476,7 @@ static void check_partition_flags(
|
||||
log_debug("Unexpected partition flag %llu set on %s!", bit, node);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_BLKID
|
||||
static int dissected_image_new(const char *path, DissectedImage **ret) {
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
_cleanup_free_ char *name = NULL;
|
||||
@@ -543,6 +654,7 @@ static int dissect_image(
|
||||
const char *devname,
|
||||
const VeritySettings *verity,
|
||||
const MountOptions *mount_options,
|
||||
const ImagePolicy *policy,
|
||||
DissectImageFlags flags) {
|
||||
|
||||
sd_id128_t root_uuid = SD_ID128_NULL, root_verity_uuid = SD_ID128_NULL;
|
||||
@@ -572,7 +684,11 @@ static int dissect_image(
|
||||
* Returns -ENOPKG if no suitable partition table or file system could be found.
|
||||
* Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found.
|
||||
* Returns -ENXIO if we couldn't find any partition suitable as root or /usr partition
|
||||
* Returns -ENOTUNIQ if we only found multiple generic partitions and thus don't know what to do with that */
|
||||
* Returns -ENOTUNIQ if we only found multiple generic partitions and thus don't know what to do with that
|
||||
* Returns -ERFKILL if image doesn't match image policy
|
||||
* Returns -EBADR if verity data was provided externally for an image that has a GPT partition table (i.e. is not just a naked fs)
|
||||
* Returns -EPROTONOSUPPORT if DISSECT_IMAGE_ADD_PARTITION_DEVICES is set but the block device does not have partition logic enabled
|
||||
* Returns -ENOMSG if we didn't find a single usable partition (and DISSECT_IMAGE_REFUSE_EMPTY is set) */
|
||||
|
||||
uint64_t diskseq = m->loop ? m->loop->diskseq : 0;
|
||||
|
||||
@@ -650,6 +766,34 @@ static int dissect_image(
|
||||
const char *fstype = NULL, *options = NULL, *suuid = NULL;
|
||||
_cleanup_close_ int mount_node_fd = -EBADF;
|
||||
sd_id128_t uuid = SD_ID128_NULL;
|
||||
PartitionPolicyFlags found_flags;
|
||||
bool encrypted;
|
||||
|
||||
/* OK, we have found a file system, that's our root partition then. */
|
||||
|
||||
r = image_policy_may_use(policy, PARTITION_ROOT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* policy says ignore this, so we ignore it */
|
||||
return -ENOPKG;
|
||||
|
||||
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
||||
(void) blkid_probe_lookup_value(b, "UUID", &suuid, NULL);
|
||||
|
||||
encrypted = streq_ptr(fstype, "crypto_LUKS");
|
||||
|
||||
if (verity_settings_data_covers(verity, PARTITION_ROOT))
|
||||
found_flags = verity->root_hash_sig ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
|
||||
else
|
||||
found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED;
|
||||
|
||||
r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
|
||||
mount_node_fd = open_partition(devname, /* is_partition = */ false, m->loop);
|
||||
@@ -657,10 +801,6 @@ static int dissect_image(
|
||||
return mount_node_fd;
|
||||
}
|
||||
|
||||
/* OK, we have found a file system, that's our root partition then. */
|
||||
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
||||
(void) blkid_probe_lookup_value(b, "UUID", &suuid, NULL);
|
||||
|
||||
if (fstype) {
|
||||
t = strdup(fstype);
|
||||
if (!t)
|
||||
@@ -681,7 +821,7 @@ static int dissect_image(
|
||||
return r;
|
||||
|
||||
m->single_file_system = true;
|
||||
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
|
||||
m->encrypted = encrypted;
|
||||
|
||||
m->has_verity = verity && verity->data_path;
|
||||
m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
|
||||
@@ -1049,6 +1189,18 @@ static int dissect_image(
|
||||
_cleanup_close_ int mount_node_fd = -EBADF;
|
||||
const char *options = NULL;
|
||||
|
||||
r = image_policy_may_use(policy, type.designator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* Policy says: ignore; Remember this fact, so that we later can distinguish between "found but ignored" and "not found at all" */
|
||||
|
||||
if (!m->partitions[type.designator].found)
|
||||
m->partitions[type.designator].ignored = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m->partitions[type.designator].found) {
|
||||
/* For most partition types the first one we see wins. Except for the
|
||||
* rootfs and /usr, where we do a version compare of the label, and
|
||||
@@ -1139,6 +1291,16 @@ static int dissect_image(
|
||||
sd_id128_t id = SD_ID128_NULL;
|
||||
const char *options = NULL;
|
||||
|
||||
r = image_policy_may_use(policy, PARTITION_XBOOTLDR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) { /* policy says: ignore */
|
||||
if (!m->partitions[PARTITION_XBOOTLDR].found)
|
||||
m->partitions[PARTITION_XBOOTLDR].ignored = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* First one wins */
|
||||
if (m->partitions[PARTITION_XBOOTLDR].found)
|
||||
continue;
|
||||
@@ -1223,41 +1385,49 @@ static int dissect_image(
|
||||
|
||||
/* If we didn't find a generic node, then we can't fix this up either */
|
||||
if (generic_node) {
|
||||
_cleanup_close_ int mount_node_fd = -EBADF;
|
||||
_cleanup_free_ char *o = NULL, *n = NULL;
|
||||
const char *options;
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
|
||||
mount_node_fd = open_partition(generic_node, /* is_partition = */ true, m->loop);
|
||||
if (mount_node_fd < 0)
|
||||
return mount_node_fd;
|
||||
}
|
||||
|
||||
r = make_partition_devname(devname, diskseq, generic_nr, flags, &n);
|
||||
r = image_policy_may_use(policy, PARTITION_ROOT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
/* Policy says: ignore; remember that we did */
|
||||
m->partitions[PARTITION_ROOT].ignored = true;
|
||||
else {
|
||||
_cleanup_close_ int mount_node_fd = -EBADF;
|
||||
_cleanup_free_ char *o = NULL, *n = NULL;
|
||||
const char *options;
|
||||
|
||||
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
|
||||
mount_node_fd = open_partition(generic_node, /* is_partition = */ true, m->loop);
|
||||
if (mount_node_fd < 0)
|
||||
return mount_node_fd;
|
||||
}
|
||||
|
||||
r = make_partition_devname(devname, diskseq, generic_nr, flags, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
assert(generic_nr >= 0);
|
||||
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
|
||||
.found = true,
|
||||
.rw = generic_rw,
|
||||
.growfs = generic_growfs,
|
||||
.partno = generic_nr,
|
||||
.architecture = _ARCHITECTURE_INVALID,
|
||||
.node = TAKE_PTR(n),
|
||||
.uuid = generic_uuid,
|
||||
.mount_options = TAKE_PTR(o),
|
||||
.mount_node_fd = TAKE_FD(mount_node_fd),
|
||||
.offset = UINT64_MAX,
|
||||
.size = UINT64_MAX,
|
||||
};
|
||||
}
|
||||
|
||||
assert(generic_nr >= 0);
|
||||
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
|
||||
.found = true,
|
||||
.rw = generic_rw,
|
||||
.growfs = generic_growfs,
|
||||
.partno = generic_nr,
|
||||
.architecture = _ARCHITECTURE_INVALID,
|
||||
.node = TAKE_PTR(n),
|
||||
.uuid = generic_uuid,
|
||||
.mount_options = TAKE_PTR(o),
|
||||
.mount_node_fd = TAKE_FD(mount_node_fd),
|
||||
.offset = UINT64_MAX,
|
||||
.size = UINT64_MAX,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1319,7 +1489,42 @@ static int dissect_image(
|
||||
}
|
||||
}
|
||||
|
||||
r = dissected_image_probe_filesystems(m, fd);
|
||||
bool any = false;
|
||||
|
||||
/* After we discovered all partitions let's see if the verity requirements match the policy. (Note:
|
||||
* we don't check encryption requirements here, because we haven't probed the file system yet, hence
|
||||
* don't know if this is encrypted or not) */
|
||||
for (PartitionDesignator di = 0; di < _PARTITION_DESIGNATOR_MAX; di++) {
|
||||
PartitionDesignator vi, si;
|
||||
PartitionPolicyFlags found_flags;
|
||||
|
||||
any = any || m->partitions[di].found;
|
||||
|
||||
vi = partition_verity_of(di);
|
||||
si = partition_verity_sig_of(di);
|
||||
|
||||
/* Determine the verity protection level for this partition. */
|
||||
found_flags = m->partitions[di].found ?
|
||||
(vi >= 0 && m->partitions[vi].found ?
|
||||
(si >= 0 && m->partitions[si].found ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY) :
|
||||
PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED) :
|
||||
(m->partitions[di].ignored ? PARTITION_POLICY_UNUSED : PARTITION_POLICY_ABSENT);
|
||||
|
||||
r = image_policy_check_protection(policy, di, found_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->partitions[di].found) {
|
||||
r = image_policy_check_partition_flags(policy, di, m->partitions[di].gpt_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any && !FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_EMPTY))
|
||||
return -ENOMSG;
|
||||
|
||||
r = dissected_image_probe_filesystems(m, fd, policy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1331,6 +1536,7 @@ int dissect_image_file(
|
||||
const char *path,
|
||||
const VeritySettings *verity,
|
||||
const MountOptions *mount_options,
|
||||
const ImagePolicy *image_policy,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret) {
|
||||
|
||||
@@ -1340,7 +1546,6 @@ int dissect_image_file(
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
|
||||
if (fd < 0)
|
||||
@@ -1358,17 +1563,81 @@ int dissect_image_file(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(m, fd, path, verity, mount_options, flags);
|
||||
r = dissect_image(m, fd, path, verity, mount_options, image_policy, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int dissect_log_error(int r, const char *name, const VeritySettings *verity) {
|
||||
assert(name);
|
||||
|
||||
switch (r) {
|
||||
|
||||
case 0 ... INT_MAX: /* success! */
|
||||
return r;
|
||||
|
||||
case -EOPNOTSUPP:
|
||||
return log_error_errno(r, "Dissecting images is not supported, compiled without blkid support.");
|
||||
|
||||
case -ENOPKG:
|
||||
return log_error_errno(r, "%s: Couldn't identify a suitable partition table or file system.", name);
|
||||
|
||||
case -ENOMEDIUM:
|
||||
return log_error_errno(r, "%s: The image does not pass os-release/extension-release validation.", name);
|
||||
|
||||
case -EADDRNOTAVAIL:
|
||||
return log_error_errno(r, "%s: No root partition for specified root hash found.", name);
|
||||
|
||||
case -ENOTUNIQ:
|
||||
return log_error_errno(r, "%s: Multiple suitable root partitions found in image.", name);
|
||||
|
||||
case -ENXIO:
|
||||
return log_error_errno(r, "%s: No suitable root partition found in image.", name);
|
||||
|
||||
case -EPROTONOSUPPORT:
|
||||
return log_error_errno(r, "Device '%s' is a loopback block device with partition scanning turned off, please turn it on.", name);
|
||||
|
||||
case -ENOTBLK:
|
||||
return log_error_errno(r, "%s: Image is not a block device.", name);
|
||||
|
||||
case -EBADR:
|
||||
return log_error_errno(r,
|
||||
"Combining partitioned images (such as '%s') with external Verity data (such as '%s') not supported. "
|
||||
"(Consider setting $SYSTEMD_DISSECT_VERITY_SIDECAR=0 to disable automatic discovery of external Verity data.)",
|
||||
name, strna(verity ? verity->data_path : NULL));
|
||||
|
||||
case -ERFKILL:
|
||||
return log_error_errno(r, "%s: image does not match image policy.", name);
|
||||
|
||||
case -ENOMSG:
|
||||
return log_error_errno(r, "%s: no suitable partitions found.", name);
|
||||
|
||||
default:
|
||||
return log_error_errno(r, "Failed to dissect image '%s': %m", name);
|
||||
}
|
||||
}
|
||||
|
||||
int dissect_image_file_and_warn(
|
||||
const char *path,
|
||||
const VeritySettings *verity,
|
||||
const MountOptions *mount_options,
|
||||
const ImagePolicy *image_policy,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret) {
|
||||
|
||||
return dissect_log_error(
|
||||
dissect_image_file(path, verity, mount_options, image_policy, flags, ret),
|
||||
path,
|
||||
verity);
|
||||
}
|
||||
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m) {
|
||||
if (!m)
|
||||
return NULL;
|
||||
@@ -3250,6 +3519,7 @@ int dissect_loop_device(
|
||||
LoopDevice *loop,
|
||||
const VeritySettings *verity,
|
||||
const MountOptions *mount_options,
|
||||
const ImagePolicy *image_policy,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret) {
|
||||
|
||||
@@ -3258,7 +3528,6 @@ int dissect_loop_device(
|
||||
int r;
|
||||
|
||||
assert(loop);
|
||||
assert(ret);
|
||||
|
||||
r = dissected_image_new(loop->backing_file ?: loop->node, &m);
|
||||
if (r < 0)
|
||||
@@ -3267,11 +3536,13 @@ int dissect_loop_device(
|
||||
m->loop = loop_device_ref(loop);
|
||||
m->sector_size = m->loop->sector_size;
|
||||
|
||||
r = dissect_image(m, loop->fd, loop->node, verity, mount_options, flags);
|
||||
r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(m);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
@@ -3282,56 +3553,17 @@ int dissect_loop_device_and_warn(
|
||||
LoopDevice *loop,
|
||||
const VeritySettings *verity,
|
||||
const MountOptions *mount_options,
|
||||
const ImagePolicy *image_policy,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret) {
|
||||
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(loop);
|
||||
assert(loop->fd >= 0);
|
||||
|
||||
name = ASSERT_PTR(loop->backing_file ?: loop->node);
|
||||
return dissect_log_error(
|
||||
dissect_loop_device(loop, verity, mount_options, image_policy, flags, ret),
|
||||
loop->backing_file ?: loop->node,
|
||||
verity);
|
||||
|
||||
r = dissect_loop_device(loop, verity, mount_options, flags, ret);
|
||||
switch (r) {
|
||||
|
||||
case -EOPNOTSUPP:
|
||||
return log_error_errno(r, "Dissecting images is not supported, compiled without blkid support.");
|
||||
|
||||
case -ENOPKG:
|
||||
return log_error_errno(r, "%s: Couldn't identify a suitable partition table or file system.", name);
|
||||
|
||||
case -ENOMEDIUM:
|
||||
return log_error_errno(r, "%s: The image does not pass validation.", name);
|
||||
|
||||
case -EADDRNOTAVAIL:
|
||||
return log_error_errno(r, "%s: No root partition for specified root hash found.", name);
|
||||
|
||||
case -ENOTUNIQ:
|
||||
return log_error_errno(r, "%s: Multiple suitable root partitions found in image.", name);
|
||||
|
||||
case -ENXIO:
|
||||
return log_error_errno(r, "%s: No suitable root partition found in image.", name);
|
||||
|
||||
case -EPROTONOSUPPORT:
|
||||
return log_error_errno(r, "Device '%s' is loopback block device with partition scanning turned off, please turn it on.", name);
|
||||
|
||||
case -ENOTBLK:
|
||||
return log_error_errno(r, "%s: Image is not a block device.", name);
|
||||
|
||||
case -EBADR:
|
||||
return log_error_errno(r,
|
||||
"Combining partitioned images (such as '%s') with external Verity data (such as '%s') not supported. "
|
||||
"(Consider setting $SYSTEMD_DISSECT_VERITY_SIDECAR=0 to disable automatic discovery of external Verity data.)",
|
||||
name, strna(verity ? verity->data_path : NULL));
|
||||
|
||||
default:
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to dissect image '%s': %m", name);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator partition_designator) {
|
||||
@@ -3407,6 +3639,7 @@ const char* mount_options_from_designator(const MountOptions *options, Partition
|
||||
|
||||
int mount_image_privately_interactively(
|
||||
const char *image,
|
||||
const ImagePolicy *image_policy,
|
||||
DissectImageFlags flags,
|
||||
char **ret_directory,
|
||||
int *ret_dir_fd,
|
||||
@@ -3449,7 +3682,13 @@ int mount_image_privately_interactively(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device for %s: %m", image);
|
||||
|
||||
r = dissect_loop_device_and_warn(d, &verity, NULL, flags, &dissected_image);
|
||||
r = dissect_loop_device_and_warn(
|
||||
d,
|
||||
&verity,
|
||||
/* mount_options= */ NULL,
|
||||
image_policy,
|
||||
flags,
|
||||
&dissected_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -3513,6 +3752,7 @@ int verity_dissect_and_mount(
|
||||
const char *src,
|
||||
const char *dest,
|
||||
const MountOptions *options,
|
||||
const ImagePolicy *image_policy,
|
||||
const char *required_host_os_release_id,
|
||||
const char *required_host_os_release_version_id,
|
||||
const char *required_host_os_release_sysext_level,
|
||||
@@ -3556,6 +3796,7 @@ int verity_dissect_and_mount(
|
||||
loop_device,
|
||||
&verity,
|
||||
options,
|
||||
image_policy,
|
||||
dissect_image_flags,
|
||||
&dissected_image);
|
||||
/* No partition table? Might be a single-filesystem image, try again */
|
||||
@@ -3564,6 +3805,7 @@ int verity_dissect_and_mount(
|
||||
loop_device,
|
||||
&verity,
|
||||
options,
|
||||
image_policy,
|
||||
dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
|
||||
&dissected_image);
|
||||
if (r < 0)
|
||||
|
||||
+12
-10
@@ -19,6 +19,7 @@ typedef struct VeritySettings VeritySettings;
|
||||
|
||||
struct DissectedPartition {
|
||||
bool found:1;
|
||||
bool ignored:1;
|
||||
bool rw:1;
|
||||
bool growfs:1;
|
||||
int partno; /* -1 if there was no partition and the images contains a file system directly */
|
||||
@@ -79,6 +80,7 @@ typedef enum DissectImageFlags {
|
||||
DISSECT_IMAGE_PIN_PARTITION_DEVICES = 1 << 21, /* Open dissected partitions and decrypted partitions and pin them by fd */
|
||||
DISSECT_IMAGE_RELAX_SYSEXT_CHECK = 1 << 22, /* Don't insist that the extension-release file name matches the image name */
|
||||
DISSECT_IMAGE_DISKSEQ_DEVNODE = 1 << 23, /* Prefer /dev/disk/by-diskseq/… device nodes */
|
||||
DISSECT_IMAGE_ALLOW_EMPTY = 1 << 24, /* Allow that no usable partitions is present */
|
||||
} DissectImageFlags;
|
||||
|
||||
struct DissectedImage {
|
||||
@@ -133,6 +135,9 @@ struct VeritySettings {
|
||||
.designator = _PARTITION_DESIGNATOR_INVALID \
|
||||
}
|
||||
|
||||
/* We include image-policy.h down here, since ImagePolicy wants a complete definition of PartitionDesignator first. */
|
||||
#include "image-policy.h"
|
||||
|
||||
MountOptions* mount_options_free_all(MountOptions *options);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
|
||||
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
|
||||
@@ -141,14 +146,11 @@ int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t si
|
||||
static inline int probe_filesystem(const char *path, char **ret_fstype) {
|
||||
return probe_filesystem_full(-1, path, 0, UINT64_MAX, ret_fstype);
|
||||
}
|
||||
int dissect_image_file(
|
||||
const char *path,
|
||||
const VeritySettings *verity,
|
||||
const MountOptions *mount_options,
|
||||
DissectImageFlags flags,
|
||||
DissectedImage **ret);
|
||||
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);
|
||||
|
||||
int dissect_image_file(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_image_file_and_warn(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
|
||||
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
|
||||
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
@@ -185,9 +187,9 @@ bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesi
|
||||
bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator d);
|
||||
bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator d);
|
||||
|
||||
int mount_image_privately_interactively(const char *path, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
|
||||
int mount_image_privately_interactively(const char *path, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
|
||||
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
|
||||
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
|
||||
|
||||
int dissect_fstype_ok(const char *fstype);
|
||||
|
||||
|
||||
@@ -0,0 +1,689 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "image-policy.h"
|
||||
#include "logarithm.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
/* Rationale for the chosen syntax:
|
||||
*
|
||||
* → one line, so that it can be reasonably added to a shell command line, for example via `systemd-dissect
|
||||
* --image-policy=…` or to the kernel command line via `systemd.image_policy=`.
|
||||
*
|
||||
* → no use of "," or ";" as separators, so that it can be included in mount/fstab-style option strings and
|
||||
* doesn't require escaping. Instead, separators are ":", "=", "+" which should be fine both in shell
|
||||
* command lines and in mount/fstab style option strings.
|
||||
*/
|
||||
|
||||
static int partition_policy_compare(const PartitionPolicy *a, const PartitionPolicy *b) {
|
||||
return CMP(ASSERT_PTR(a)->designator, ASSERT_PTR(b)->designator);
|
||||
}
|
||||
|
||||
static PartitionPolicy* image_policy_bsearch(const ImagePolicy *policy, PartitionDesignator designator) {
|
||||
if (!policy)
|
||||
return NULL;
|
||||
|
||||
return typesafe_bsearch(
|
||||
&(PartitionPolicy) { .designator = designator },
|
||||
ASSERT_PTR(policy)->policies,
|
||||
ASSERT_PTR(policy)->n_policies,
|
||||
partition_policy_compare);
|
||||
}
|
||||
|
||||
static PartitionPolicyFlags partition_policy_normalized_flags(const PartitionPolicy *policy) {
|
||||
PartitionPolicyFlags flags = ASSERT_PTR(policy)->flags;
|
||||
|
||||
/* This normalizes the per-partition policy flags. This means if the user left some things
|
||||
* unspecified, we'll fill in the appropriate "dontcare" policy instead. We'll also mask out bits
|
||||
* that do not make any sense for specific partition types. */
|
||||
|
||||
/* If no protection flag is set, then this means all are set */
|
||||
if ((flags & _PARTITION_POLICY_USE_MASK) == 0)
|
||||
flags |= PARTITION_POLICY_OPEN;
|
||||
|
||||
/* If this is a verity or verity signature designator, then mask off all protection bits, this after
|
||||
* all needs no protection, because it *is* the protection */
|
||||
if (partition_verity_to_data(policy->designator) >= 0 ||
|
||||
partition_verity_sig_to_data(policy->designator) >= 0)
|
||||
flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED);
|
||||
|
||||
/* if this designator has no verity concept, then mask off verity protection flags */
|
||||
if (partition_verity_of(policy->designator) < 0)
|
||||
flags &= ~(PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED);
|
||||
|
||||
if ((flags & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT)
|
||||
/* If the partition must be absent, then the gpt flags don't matter */
|
||||
flags &= ~(_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK);
|
||||
else {
|
||||
/* If the gpt flags bits are not specified, set both options for each */
|
||||
if ((flags & _PARTITION_POLICY_READ_ONLY_MASK) == 0)
|
||||
flags |= PARTITION_POLICY_READ_ONLY_ON|PARTITION_POLICY_READ_ONLY_OFF;
|
||||
if ((flags & _PARTITION_POLICY_GROWFS_MASK) == 0)
|
||||
flags |= PARTITION_POLICY_GROWFS_ON|PARTITION_POLICY_GROWFS_OFF;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
PartitionPolicyFlags image_policy_get(const ImagePolicy *policy, PartitionDesignator designator) {
|
||||
PartitionDesignator data_designator = _PARTITION_DESIGNATOR_INVALID;
|
||||
PartitionPolicy *pp;
|
||||
|
||||
/* No policy means: everything may be used in any mode */
|
||||
if (!policy)
|
||||
return partition_policy_normalized_flags(
|
||||
&(const PartitionPolicy) {
|
||||
.flags = PARTITION_POLICY_OPEN,
|
||||
.designator = designator,
|
||||
});
|
||||
|
||||
pp = image_policy_bsearch(policy, designator);
|
||||
if (pp)
|
||||
return partition_policy_normalized_flags(pp);
|
||||
|
||||
/* Hmm, so this didn't work, then let's see if we can derive some policy from the underlying data
|
||||
* partition in case of verity/signature partitions */
|
||||
|
||||
data_designator = partition_verity_to_data(designator);
|
||||
if (data_designator >= 0) {
|
||||
PartitionPolicyFlags data_flags;
|
||||
|
||||
/* So we are asked for the policy for a verity partition, and there's no explicit policy for
|
||||
* that case. Let's synthesize a policy from the protection setting for the underlying data
|
||||
* partition. */
|
||||
|
||||
data_flags = image_policy_get(policy, data_designator);
|
||||
if (data_flags < 0)
|
||||
return data_flags;
|
||||
|
||||
/* We need verity if verity or verity with sig is requested */
|
||||
if (!(data_flags & (PARTITION_POLICY_SIGNED|PARTITION_POLICY_VERITY)))
|
||||
return _PARTITION_POLICY_FLAGS_INVALID;
|
||||
|
||||
/* If the data partition may be unused or absent, then the verity partition may too. Also, inherit the partition flags policy */
|
||||
return partition_policy_normalized_flags(
|
||||
&(const PartitionPolicy) {
|
||||
.flags = PARTITION_POLICY_UNPROTECTED | (data_flags & (PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT)) |
|
||||
(data_flags & _PARTITION_POLICY_PFLAGS_MASK),
|
||||
.designator = designator,
|
||||
});
|
||||
}
|
||||
|
||||
data_designator = partition_verity_sig_to_data(designator);
|
||||
if (data_designator >= 0) {
|
||||
PartitionPolicyFlags data_flags;
|
||||
|
||||
/* Similar case as for verity partitions, but slightly more strict rules */
|
||||
|
||||
data_flags = image_policy_get(policy, data_designator);
|
||||
if (data_flags < 0)
|
||||
return data_flags;
|
||||
|
||||
if (!(data_flags & PARTITION_POLICY_SIGNED))
|
||||
return _PARTITION_POLICY_FLAGS_INVALID;
|
||||
|
||||
return partition_policy_normalized_flags(
|
||||
&(const PartitionPolicy) {
|
||||
.flags = PARTITION_POLICY_UNPROTECTED | (data_flags & (PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT)) |
|
||||
(data_flags & _PARTITION_POLICY_PFLAGS_MASK),
|
||||
.designator = designator,
|
||||
});
|
||||
}
|
||||
|
||||
return _PARTITION_POLICY_FLAGS_INVALID; /* got nothing */
|
||||
}
|
||||
|
||||
PartitionPolicyFlags image_policy_get_exhaustively(const ImagePolicy *policy, PartitionDesignator designator) {
|
||||
PartitionPolicyFlags flags;
|
||||
|
||||
/* This is just like image_policy_get() but whenever there is no policy for a specific designator, we
|
||||
* return the default policy. */
|
||||
|
||||
flags = image_policy_get(policy, designator);
|
||||
if (flags < 0)
|
||||
return partition_policy_normalized_flags(
|
||||
&(const PartitionPolicy) {
|
||||
.flags = image_policy_default(policy),
|
||||
.designator = designator,
|
||||
});
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static PartitionPolicyFlags policy_flag_from_string_one(const char *s) {
|
||||
assert(s);
|
||||
|
||||
/* This is a bitmask (i.e. not dense), hence we don't use the "string-table.h" stuff here. */
|
||||
|
||||
if (streq(s, "verity"))
|
||||
return PARTITION_POLICY_VERITY;
|
||||
if (streq(s, "signed"))
|
||||
return PARTITION_POLICY_SIGNED;
|
||||
if (streq(s, "encrypted"))
|
||||
return PARTITION_POLICY_ENCRYPTED;
|
||||
if (streq(s, "unprotected"))
|
||||
return PARTITION_POLICY_UNPROTECTED;
|
||||
if (streq(s, "unused"))
|
||||
return PARTITION_POLICY_UNUSED;
|
||||
if (streq(s, "absent"))
|
||||
return PARTITION_POLICY_ABSENT;
|
||||
if (streq(s, "open")) /* shortcut alias */
|
||||
return PARTITION_POLICY_OPEN;
|
||||
if (streq(s, "ignore")) /* ditto */
|
||||
return PARTITION_POLICY_IGNORE;
|
||||
if (streq(s, "read-only-on"))
|
||||
return PARTITION_POLICY_READ_ONLY_ON;
|
||||
if (streq(s, "read-only-off"))
|
||||
return PARTITION_POLICY_READ_ONLY_OFF;
|
||||
if (streq(s, "growfs-on"))
|
||||
return PARTITION_POLICY_GROWFS_ON;
|
||||
if (streq(s, "growfs-off"))
|
||||
return PARTITION_POLICY_GROWFS_OFF;
|
||||
|
||||
return _PARTITION_POLICY_FLAGS_INVALID;
|
||||
}
|
||||
|
||||
PartitionPolicyFlags partition_policy_flags_from_string(const char *s) {
|
||||
PartitionPolicyFlags flags = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (empty_or_dash(s))
|
||||
return 0;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *f = NULL;
|
||||
PartitionPolicyFlags ff;
|
||||
|
||||
r = extract_first_word(&s, &f, "+", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
ff = policy_flag_from_string_one(strstrip(f));
|
||||
if (ff < 0)
|
||||
return -EBADRQC; /* recognizable error */
|
||||
|
||||
flags |= ff;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static ImagePolicy* image_policy_new(size_t n_policies) {
|
||||
ImagePolicy *p;
|
||||
|
||||
if (n_policies > (SIZE_MAX - offsetof(ImagePolicy, policies)) / sizeof(PartitionPolicy)) /* overflow check */
|
||||
return NULL;
|
||||
|
||||
p = malloc(offsetof(ImagePolicy, policies) + sizeof(PartitionPolicy) * n_policies);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
*p = (ImagePolicy) {
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
return p;
|
||||
}
|
||||
|
||||
int image_policy_from_string(const char *s, ImagePolicy **ret) {
|
||||
_cleanup_free_ ImagePolicy *p = NULL;
|
||||
uint64_t dmask = 0;
|
||||
ImagePolicy *t;
|
||||
PartitionPolicyFlags symbolic_policy;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert_cc(sizeof(dmask) * 8 >= _PARTITION_DESIGNATOR_MAX);
|
||||
|
||||
/* Recognizable errors:
|
||||
*
|
||||
* ENOTUNIQ → Two or more rules for the same partition
|
||||
* EBADSLT → Unknown partition designator
|
||||
* EBADRQC → Unknown policy flags
|
||||
*/
|
||||
|
||||
/* First, let's handle "symbolic" policies, i.e. "-", "*", "~" */
|
||||
if (empty_or_dash(s))
|
||||
/* ignore policy: everything may exist, but nothing used */
|
||||
symbolic_policy = PARTITION_POLICY_IGNORE;
|
||||
else if (streq(s, "*"))
|
||||
/* allow policy: everything is allowed */
|
||||
symbolic_policy = PARTITION_POLICY_OPEN;
|
||||
else if (streq(s, "~"))
|
||||
/* deny policy: nothing may exist */
|
||||
symbolic_policy = PARTITION_POLICY_ABSENT;
|
||||
else
|
||||
symbolic_policy = _PARTITION_POLICY_FLAGS_INVALID;
|
||||
|
||||
if (symbolic_policy >= 0) {
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
p = image_policy_new(0);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->default_flags = symbolic_policy;
|
||||
*ret = TAKE_PTR(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate the policy at maximum size, i.e. for all designators. We might overshoot a bit, but the
|
||||
* items are cheap, and we can return unused space to libc once we know we don't need it */
|
||||
p = image_policy_new(_PARTITION_DESIGNATOR_MAX);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
const char *q = s;
|
||||
bool default_specified = false;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *e = NULL, *d = NULL;
|
||||
PartitionDesignator designator;
|
||||
PartitionPolicyFlags flags;
|
||||
char *f, *ds, *fs;
|
||||
|
||||
r = extract_first_word(&q, &e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
f = e;
|
||||
r = extract_first_word((const char**) &f, &d, "=", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Expected designator name followed by '='; got instead: %s", e);
|
||||
if (!f) /* no separator? */
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Missing '=' in policy expression: %s", e);
|
||||
|
||||
ds = strstrip(d);
|
||||
if (isempty(ds)) {
|
||||
/* Not partition name? then it's the default policy */
|
||||
if (default_specified)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Default partition policy flags specified more than once.");
|
||||
|
||||
designator = _PARTITION_DESIGNATOR_INVALID;
|
||||
default_specified = true;
|
||||
} else {
|
||||
designator = partition_designator_from_string(ds);
|
||||
if (designator < 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EBADSLT), "Unknown partition designator: %s", ds); /* recognizable error */
|
||||
if (dmask & (UINT64_C(1) << designator))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Partition designator specified more than once: %s", ds);
|
||||
dmask |= UINT64_C(1) << designator;
|
||||
}
|
||||
|
||||
fs = strstrip(f);
|
||||
flags = partition_policy_flags_from_string(fs);
|
||||
if (flags == -EBADRQC)
|
||||
return log_debug_errno(flags, "Unknown partition policy flag: %s", fs);
|
||||
if (flags < 0)
|
||||
return log_debug_errno(flags, "Failed to parse partition policy flags '%s': %m", fs);
|
||||
|
||||
if (designator < 0)
|
||||
p->default_flags = flags;
|
||||
else {
|
||||
p->policies[p->n_policies++] = (PartitionPolicy) {
|
||||
.designator = designator,
|
||||
.flags = flags,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
assert(p->n_policies <= _PARTITION_DESIGNATOR_MAX);
|
||||
|
||||
/* Return unused space to libc */
|
||||
t = realloc(p, offsetof(ImagePolicy, policies) + sizeof(PartitionPolicy) * p->n_policies);
|
||||
if (t)
|
||||
p = t;
|
||||
|
||||
typesafe_qsort(p->policies, p->n_policies, partition_policy_compare);
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
const char *l[CONST_LOG2U(_PARTITION_POLICY_MASK) + 1]; /* one string per known flag at most */
|
||||
size_t m = 0;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (flags < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* If 'simplify' is false we'll output the precise value of every single flag.
|
||||
*
|
||||
* If 'simplify' is true we'll try to make the output shorter, by doing the following:
|
||||
*
|
||||
* → we'll spell the long form "verity+signed+encrypted+unprotected+unused+absent" via its
|
||||
* equivalent shortcut form "open" (which we happily parse btw, see above)
|
||||
*
|
||||
* → we'll spell the long form "unused+absent" via its shortcut "ignore" (which we are also happy
|
||||
* to parse)
|
||||
*
|
||||
* → if the read-only/growfs policy flags are both set, we suppress them. this thus removes the
|
||||
* distinction between "user explicitly declared don't care" and "we implied don't care because
|
||||
* user didn't say anything".
|
||||
*
|
||||
* net result: the resulting string is shorter, but the effective policy declared that way will have
|
||||
* the same results as the long form. */
|
||||
|
||||
if (simplify && (flags & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_OPEN)
|
||||
l[m++] = "open";
|
||||
else if (simplify && (flags & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_IGNORE)
|
||||
l[m++] = "ignore";
|
||||
else {
|
||||
if (flags & PARTITION_POLICY_VERITY)
|
||||
l[m++] = "verity";
|
||||
if (flags & PARTITION_POLICY_SIGNED)
|
||||
l[m++] = "signed";
|
||||
if (flags & PARTITION_POLICY_ENCRYPTED)
|
||||
l[m++] = "encrypted";
|
||||
if (flags & PARTITION_POLICY_UNPROTECTED)
|
||||
l[m++] = "unprotected";
|
||||
if (flags & PARTITION_POLICY_UNUSED)
|
||||
l[m++] = "unused";
|
||||
if (flags & PARTITION_POLICY_ABSENT)
|
||||
l[m++] = "absent";
|
||||
}
|
||||
|
||||
if (!simplify || (!(flags & PARTITION_POLICY_READ_ONLY_ON) != !(flags & PARTITION_POLICY_READ_ONLY_OFF))) {
|
||||
if (flags & PARTITION_POLICY_READ_ONLY_ON)
|
||||
l[m++] = "read-only-on";
|
||||
if (flags & PARTITION_POLICY_READ_ONLY_OFF)
|
||||
l[m++] = "read-only-off";
|
||||
}
|
||||
|
||||
if (!simplify || (!(flags & PARTITION_POLICY_GROWFS_ON) != !(flags & PARTITION_POLICY_GROWFS_OFF))) {
|
||||
if (flags & PARTITION_POLICY_GROWFS_OFF)
|
||||
l[m++] = "growfs-off";
|
||||
if (flags & PARTITION_POLICY_GROWFS_ON)
|
||||
l[m++] = "growfs-on";
|
||||
}
|
||||
|
||||
if (m == 0)
|
||||
buf = strdup("-");
|
||||
else {
|
||||
assert(m+1 < ELEMENTSOF(l));
|
||||
l[m] = NULL;
|
||||
|
||||
buf = strv_join((char**) l, "+");
|
||||
}
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = TAKE_PTR(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int image_policy_flags_all_match(const ImagePolicy *policy, PartitionPolicyFlags expected) {
|
||||
|
||||
if (expected < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (image_policy_default(policy) != expected)
|
||||
return false;
|
||||
|
||||
for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
|
||||
PartitionPolicyFlags f, w;
|
||||
|
||||
f = image_policy_get_exhaustively(policy, d);
|
||||
if (f < 0)
|
||||
return f;
|
||||
|
||||
w = partition_policy_normalized_flags(
|
||||
&(const PartitionPolicy) {
|
||||
.flags = expected,
|
||||
.designator = d,
|
||||
});
|
||||
if (w < 0)
|
||||
return w;
|
||||
if (f != w)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image_policy_equiv_ignore(const ImagePolicy *policy) {
|
||||
/* Checks if this is the ignore policy (or equivalent to it), i.e. everything is ignored, aka '-', aka '' */
|
||||
return image_policy_flags_all_match(policy, PARTITION_POLICY_IGNORE);
|
||||
}
|
||||
|
||||
bool image_policy_equiv_allow(const ImagePolicy *policy) {
|
||||
/* Checks if this is the allow policy (or equivalent to it), i.e. everything is allowed, aka '*' */
|
||||
return image_policy_flags_all_match(policy, PARTITION_POLICY_OPEN);
|
||||
}
|
||||
|
||||
bool image_policy_equiv_deny(const ImagePolicy *policy) {
|
||||
/* Checks if this is the deny policy (or equivalent to it), i.e. everything must be absent, aka '~' */
|
||||
return image_policy_flags_all_match(policy, PARTITION_POLICY_ABSENT);
|
||||
}
|
||||
|
||||
int image_policy_to_string(const ImagePolicy *policy, bool simplify, char **ret) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (simplify) {
|
||||
const char *fixed;
|
||||
|
||||
if (image_policy_equiv_allow(policy))
|
||||
fixed = "*";
|
||||
else if (image_policy_equiv_ignore(policy))
|
||||
fixed = "-";
|
||||
else if (image_policy_equiv_deny(policy))
|
||||
fixed = "~";
|
||||
else
|
||||
fixed = NULL;
|
||||
|
||||
if (fixed) {
|
||||
s = strdup(fixed);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < image_policy_n_entries(policy); i++) {
|
||||
const PartitionPolicy *p = policy->policies + i;
|
||||
_cleanup_free_ char *f = NULL;
|
||||
const char *t;
|
||||
|
||||
assert(i == 0 || p->designator > policy->policies[i-1].designator); /* Validate perfect ordering */
|
||||
|
||||
assert_se(t = partition_designator_to_string(p->designator));
|
||||
|
||||
if (simplify) {
|
||||
/* Skip policy entries that match the default anyway */
|
||||
PartitionPolicyFlags df;
|
||||
|
||||
df = partition_policy_normalized_flags(
|
||||
&(const PartitionPolicy) {
|
||||
.flags = image_policy_default(policy),
|
||||
.designator = p->designator,
|
||||
});
|
||||
if (df < 0)
|
||||
return df;
|
||||
|
||||
if (df == p->flags)
|
||||
continue;
|
||||
}
|
||||
|
||||
r = partition_policy_flags_to_string(p->flags, simplify, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strextend(&s, isempty(s) ? "" : ":", t, "=", f))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!simplify || image_policy_default(policy) != PARTITION_POLICY_IGNORE) {
|
||||
_cleanup_free_ char *df = NULL;
|
||||
|
||||
r = partition_policy_flags_to_string(image_policy_default(policy), simplify, &df);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strextend(&s, isempty(s) ? "" : ":", "=", df))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (isempty(s)) { /* no rule and default policy? then let's return "-" */
|
||||
s = strdup("-");
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b) {
|
||||
if (a == b)
|
||||
return true;
|
||||
if (image_policy_n_entries(a) != image_policy_n_entries(b))
|
||||
return false;
|
||||
if (image_policy_default(a) != image_policy_default(b))
|
||||
return false;
|
||||
for (size_t i = 0; i < image_policy_n_entries(a); i++) {
|
||||
if (a->policies[i].designator != b->policies[i].designator)
|
||||
return false;
|
||||
if (a->policies[i].flags != b->policies[i].flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b) {
|
||||
|
||||
/* The image_policy_equal() function checks if the policy is defined the exact same way. This
|
||||
* function here instead looks at the outcome of the two policies instead. Where does this come to
|
||||
* different results you ask? We imply some logic regarding Verity/Encryption: when no rule is
|
||||
* defined for a verity partition we can synthesize it from the protection level of the data
|
||||
* partition it protects. Or: any per-partition rule that is identical to the default rule is
|
||||
* redundant, and will be recognized as such by image_policy_equivalent() but not by
|
||||
* image_policy_equal()- */
|
||||
|
||||
if (image_policy_default(a) != image_policy_default(b))
|
||||
return false;
|
||||
|
||||
for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
|
||||
PartitionPolicyFlags f, w;
|
||||
|
||||
f = image_policy_get_exhaustively(a, d);
|
||||
if (f < 0)
|
||||
return f;
|
||||
|
||||
w = image_policy_get_exhaustively(b, d);
|
||||
if (w < 0)
|
||||
return w;
|
||||
|
||||
if (f != w)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const ImagePolicy image_policy_allow = {
|
||||
/* Allow policy */
|
||||
.n_policies = 0,
|
||||
.default_flags = PARTITION_POLICY_OPEN,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_deny = {
|
||||
/* Allow policy */
|
||||
.n_policies = 0,
|
||||
.default_flags = PARTITION_POLICY_ABSENT,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_ignore = {
|
||||
/* Allow policy */
|
||||
.n_policies = 0,
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_sysext = {
|
||||
/* For system extensions, honour root file system, and /usr/ and ignore everything else. After all,
|
||||
* we are only interested in /usr/ + /opt/ trees anyway, and that's really the only place they can
|
||||
* be. */
|
||||
.n_policies = 2,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_sysext_strict = {
|
||||
/* For system extensions, requiring signing */
|
||||
.n_policies = 2,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_container = {
|
||||
/* For systemd-nspawn containers we use all partitions, with the exception of swap */
|
||||
.n_policies = 8,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_host = {
|
||||
/* For the host policy we basically use everything */
|
||||
.n_policies = 9,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_ESP, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_XBOOTLDR, PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SWAP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
|
||||
const ImagePolicy image_policy_service = {
|
||||
/* For RootImage= in services we skip ESP/XBOOTLDR and swap */
|
||||
.n_policies = 6,
|
||||
.policies = {
|
||||
{ PARTITION_ROOT, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_USR, PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_HOME, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_SRV, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_TMP, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
{ PARTITION_VAR, PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_ABSENT },
|
||||
},
|
||||
.default_flags = PARTITION_POLICY_IGNORE,
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
typedef struct ImagePolicy ImagePolicy;
|
||||
|
||||
#include "dissect-image.h"
|
||||
#include "errno-list.h"
|
||||
|
||||
typedef enum PartitionPolicyFlags {
|
||||
/* Not all policy flags really make sense on all partition types, see comments. But even if they
|
||||
* don't make sense we'll parse them anyway, because maybe one day we'll add them for more partition
|
||||
* types, too. Moreover, we allow configuring a "default" policy for all partition types for which no
|
||||
* explicit policy is specified. It's useful if we can use policy flags in there and apply this
|
||||
* default policy gracefully even to partition types where they don't really make too much sense
|
||||
* on. Example: a default policy of "verity+encrypted" certainly makes sense, but for /home/
|
||||
* partitions this gracefully degrades to "encrypted" (as we do not have a concept of verity for
|
||||
* /home/), and so on. */
|
||||
PARTITION_POLICY_VERITY = 1 << 0, /* must exist, activate with verity (only applies to root/usr partitions) */
|
||||
PARTITION_POLICY_SIGNED = 1 << 1, /* must exist, activate with signed verity (only applies to root/usr partitions) */
|
||||
PARTITION_POLICY_ENCRYPTED = 1 << 2, /* must exist, activate with LUKS encryption (applies to any data partition, but not to verity/signature partitions */
|
||||
PARTITION_POLICY_UNPROTECTED = 1 << 3, /* must exist, activate without encryption/verity */
|
||||
PARTITION_POLICY_UNUSED = 1 << 4, /* must exist, don't use */
|
||||
PARTITION_POLICY_ABSENT = 1 << 5, /* must not exist */
|
||||
PARTITION_POLICY_OPEN = PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED|PARTITION_POLICY_ENCRYPTED|
|
||||
PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
|
||||
PARTITION_POLICY_IGNORE = PARTITION_POLICY_UNUSED|PARTITION_POLICY_ABSENT,
|
||||
_PARTITION_POLICY_USE_MASK = PARTITION_POLICY_OPEN,
|
||||
|
||||
PARTITION_POLICY_READ_ONLY_OFF = 1 << 6, /* State of GPT partition flag "read-only" must be on */
|
||||
PARTITION_POLICY_READ_ONLY_ON = 1 << 7,
|
||||
_PARTITION_POLICY_READ_ONLY_MASK = PARTITION_POLICY_READ_ONLY_OFF|PARTITION_POLICY_READ_ONLY_ON,
|
||||
PARTITION_POLICY_GROWFS_OFF = 1 << 8, /* State of GPT partition flag "growfs" must be on */
|
||||
PARTITION_POLICY_GROWFS_ON = 1 << 9,
|
||||
_PARTITION_POLICY_GROWFS_MASK = PARTITION_POLICY_GROWFS_OFF|PARTITION_POLICY_GROWFS_ON,
|
||||
_PARTITION_POLICY_PFLAGS_MASK = _PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
|
||||
|
||||
_PARTITION_POLICY_MASK = _PARTITION_POLICY_USE_MASK|_PARTITION_POLICY_READ_ONLY_MASK|_PARTITION_POLICY_GROWFS_MASK,
|
||||
|
||||
_PARTITION_POLICY_FLAGS_INVALID = -EINVAL,
|
||||
_PARTITION_POLICY_FLAGS_ERRNO_MAX = -ERRNO_MAX, /* Ensure the whole errno range fits into this enum */
|
||||
} PartitionPolicyFlags;
|
||||
|
||||
assert_cc((_PARTITION_POLICY_USE_MASK | _PARTITION_POLICY_PFLAGS_MASK) >= 0); /* ensure flags don't collide with errno range */
|
||||
|
||||
typedef struct PartitionPolicy {
|
||||
PartitionDesignator designator;
|
||||
PartitionPolicyFlags flags;
|
||||
} PartitionPolicy;
|
||||
|
||||
struct ImagePolicy {
|
||||
PartitionPolicyFlags default_flags; /* for any designator not listed in the list below */
|
||||
size_t n_policies;
|
||||
PartitionPolicy policies[]; /* sorted by designator, hence suitable for binary search */
|
||||
};
|
||||
|
||||
/* Default policies for various usecases */
|
||||
extern const ImagePolicy image_policy_allow;
|
||||
extern const ImagePolicy image_policy_deny;
|
||||
extern const ImagePolicy image_policy_ignore;
|
||||
extern const ImagePolicy image_policy_sysext; /* No verity required */
|
||||
extern const ImagePolicy image_policy_sysext_strict; /* Signed verity required */
|
||||
extern const ImagePolicy image_policy_container;
|
||||
extern const ImagePolicy image_policy_service;
|
||||
extern const ImagePolicy image_policy_host;
|
||||
|
||||
PartitionPolicyFlags image_policy_get(const ImagePolicy *policy, PartitionDesignator designator);
|
||||
PartitionPolicyFlags image_policy_get_exhaustively(const ImagePolicy *policy, PartitionDesignator designator);
|
||||
|
||||
/* We want that the NULL image policy means "everything" allowed, hence use these simple accessors to make
|
||||
* NULL policies work reasonably */
|
||||
static inline PartitionPolicyFlags image_policy_default(const ImagePolicy *policy) {
|
||||
return policy ? policy->default_flags : PARTITION_POLICY_OPEN;
|
||||
}
|
||||
|
||||
static inline size_t image_policy_n_entries(const ImagePolicy *policy) {
|
||||
return policy ? policy->n_policies : 0;
|
||||
}
|
||||
|
||||
PartitionPolicyFlags partition_policy_flags_from_string(const char *s);
|
||||
int partition_policy_flags_to_string(PartitionPolicyFlags flags, bool simplify, char **ret);
|
||||
|
||||
int image_policy_from_string(const char *s, ImagePolicy **ret);
|
||||
int image_policy_to_string(const ImagePolicy *policy, bool simplify, char **ret);
|
||||
|
||||
/* Recognizes three special policies by equivalence */
|
||||
bool image_policy_equiv_ignore(const ImagePolicy *policy);
|
||||
bool image_policy_equiv_allow(const ImagePolicy *policy);
|
||||
bool image_policy_equiv_deny(const ImagePolicy *policy);
|
||||
|
||||
bool image_policy_equal(const ImagePolicy *a, const ImagePolicy *b); /* checks if defined the same way, i.e. has literally the same ruleset */
|
||||
int image_policy_equivalent(const ImagePolicy *a, const ImagePolicy *b); /* checks if the outcome is the same, i.e. for all partitions results in the same decisions. */
|
||||
|
||||
static inline ImagePolicy* image_policy_free(ImagePolicy *p) {
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ImagePolicy*, image_policy_free);
|
||||
@@ -81,6 +81,7 @@ shared_sources = files(
|
||||
'id128-print.c',
|
||||
'idn-util.c',
|
||||
'ima-util.c',
|
||||
'image-policy.c',
|
||||
'import-util.c',
|
||||
'in-addr-prefix-util.c',
|
||||
'install-file.c',
|
||||
|
||||
@@ -805,6 +805,7 @@ static int mount_in_namespace(
|
||||
bool read_only,
|
||||
bool make_file_or_directory,
|
||||
const MountOptions *options,
|
||||
const ImagePolicy *image_policy,
|
||||
bool is_image) {
|
||||
|
||||
_cleanup_close_pair_ int errno_pipe_fd[2] = PIPE_EBADF;
|
||||
@@ -892,7 +893,7 @@ static int mount_in_namespace(
|
||||
mount_tmp_created = true;
|
||||
|
||||
if (is_image)
|
||||
r = verity_dissect_and_mount(chased_src_fd, chased_src_path, mount_tmp, options, NULL, NULL, NULL, NULL);
|
||||
r = verity_dissect_and_mount(chased_src_fd, chased_src_path, mount_tmp, options, image_policy, NULL, NULL, NULL, NULL);
|
||||
else
|
||||
r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
|
||||
if (r < 0)
|
||||
@@ -1042,7 +1043,7 @@ int bind_mount_in_namespace(
|
||||
bool read_only,
|
||||
bool make_file_or_directory) {
|
||||
|
||||
return mount_in_namespace(target, propagate_path, incoming_path, src, dest, read_only, make_file_or_directory, NULL, false);
|
||||
return mount_in_namespace(target, propagate_path, incoming_path, src, dest, read_only, make_file_or_directory, /* options= */ NULL, /* image_policy= */ NULL, /* is_image= */ false);
|
||||
}
|
||||
|
||||
int mount_image_in_namespace(
|
||||
@@ -1053,9 +1054,10 @@ int mount_image_in_namespace(
|
||||
const char *dest,
|
||||
bool read_only,
|
||||
bool make_file_or_directory,
|
||||
const MountOptions *options) {
|
||||
const MountOptions *options,
|
||||
const ImagePolicy *image_policy) {
|
||||
|
||||
return mount_in_namespace(target, propagate_path, incoming_path, src, dest, read_only, make_file_or_directory, options, true);
|
||||
return mount_in_namespace(target, propagate_path, incoming_path, src, dest, read_only, make_file_or_directory, options, image_policy, /* is_image=*/ true);
|
||||
}
|
||||
|
||||
int make_mount_point(const char *path) {
|
||||
|
||||
@@ -81,7 +81,7 @@ static inline char* umount_and_rmdir_and_free(char *p) {
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free);
|
||||
|
||||
int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory);
|
||||
int mount_image_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory, const MountOptions *options);
|
||||
int mount_image_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory, const MountOptions *options, const ImagePolicy *image_policy);
|
||||
|
||||
int make_mount_point(const char *path);
|
||||
|
||||
|
||||
+45
-9
@@ -45,12 +45,14 @@ static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
static bool arg_legend = true;
|
||||
static bool arg_force = false;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
/* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
|
||||
static ImageClass arg_image_class = IMAGE_SYSEXT;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
/* Helper struct for naming simplicity and reusability */
|
||||
static const struct {
|
||||
@@ -441,6 +443,24 @@ static int strverscmp_improvedp(char *const* a, char *const* b) {
|
||||
return strverscmp_improved(*a, *b);
|
||||
}
|
||||
|
||||
static const ImagePolicy *pick_image_policy(const Image *img) {
|
||||
assert(img);
|
||||
assert(img->path);
|
||||
|
||||
/* Explicitly specified policy always wins */
|
||||
if (arg_image_policy)
|
||||
return arg_image_policy;
|
||||
|
||||
/* If located in /.extra/sysext/ in the initrd, then it was placed there by systemd-stub, and was
|
||||
* picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the
|
||||
* other directories we assume the appropriate level of trust was already established already. */
|
||||
|
||||
if (in_initrd() && path_startswith(img->path, "/.extra/sysext/"))
|
||||
return &image_policy_sysext_strict;
|
||||
|
||||
return &image_policy_sysext;
|
||||
}
|
||||
|
||||
static int merge_subprocess(Hashmap *images, const char *workspace) {
|
||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_sysext_level = NULL,
|
||||
*host_os_release_confext_level = NULL, *buf = NULL;
|
||||
@@ -558,7 +578,8 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
||||
r = dissect_loop_device_and_warn(
|
||||
d,
|
||||
&verity_settings,
|
||||
NULL,
|
||||
/* mount_options= */ NULL,
|
||||
pick_image_policy(img),
|
||||
flags,
|
||||
&m);
|
||||
if (r < 0)
|
||||
@@ -770,7 +791,7 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) {
|
||||
return log_error_errno(r, "Failed to discover images: %m");
|
||||
|
||||
HASHMAP_FOREACH(img, images) {
|
||||
r = image_read_metadata(img);
|
||||
r = image_read_metadata(img, &image_policy_sysext);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
|
||||
}
|
||||
@@ -922,6 +943,8 @@ static int verb_help(int argc, char **argv, void *userdata) {
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" --force Ignore version incompatibilities\n"
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify disk image dissection policy\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@@ -942,16 +965,18 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_ROOT,
|
||||
ARG_JSON,
|
||||
ARG_FORCE,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "force", no_argument, NULL, ARG_FORCE },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "force", no_argument, NULL, ARG_FORCE },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -995,6 +1020,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_force = true;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -121,6 +121,7 @@ bool arg_read_only = false;
|
||||
bool arg_mkdir = false;
|
||||
bool arg_marked = false;
|
||||
const char *arg_drop_in = NULL;
|
||||
ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
|
||||
@@ -135,6 +136,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_host, unsetp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_boot_loader_entry, unsetp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_drop_in, unsetp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static int systemctl_help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@@ -305,7 +307,9 @@ static int systemctl_help(void) {
|
||||
" --root=PATH Edit/enable/disable/mask unit files in the specified\n"
|
||||
" root directory\n"
|
||||
" --image=PATH Edit/enable/disable/mask unit files in the specified\n"
|
||||
" image\n"
|
||||
" disk image\n"
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify disk image dissection policy\n"
|
||||
" -n --lines=INTEGER Number of journal entries to show\n"
|
||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||
" short-iso, short-iso-precise, short-full,\n"
|
||||
@@ -450,6 +454,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
ARG_NO_WARN,
|
||||
ARG_DROP_IN,
|
||||
ARG_WHEN,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -515,6 +520,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
{ "marked", no_argument, NULL, ARG_MARKED },
|
||||
{ "drop-in", required_argument, NULL, ARG_DROP_IN },
|
||||
{ "when", required_argument, NULL, ARG_WHEN },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1003,6 +1009,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case '.':
|
||||
/* Output an error mimicking getopt, and print a hint afterwards */
|
||||
log_error("%s: invalid option -- '.'", program_invocation_name);
|
||||
@@ -1248,6 +1266,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "bus-print-properties.h"
|
||||
#include "bus-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "install.h"
|
||||
#include "output-mode.h"
|
||||
#include "pager.h"
|
||||
@@ -100,6 +101,7 @@ extern bool arg_read_only;
|
||||
extern bool arg_mkdir;
|
||||
extern bool arg_marked;
|
||||
extern const char *arg_drop_in;
|
||||
extern ImagePolicy *arg_image_policy;
|
||||
|
||||
static inline const char* arg_job_mode(void) {
|
||||
return _arg_job_mode ?: "replace";
|
||||
|
||||
@@ -46,11 +46,13 @@ static char *arg_image = NULL;
|
||||
static bool arg_reboot = false;
|
||||
static char *arg_component = NULL;
|
||||
static int arg_verify = -1;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_component, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
typedef struct Context {
|
||||
Transfer **transfers;
|
||||
@@ -872,6 +874,7 @@ static int process_image(
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
(ro ? DISSECT_IMAGE_READ_ONLY : 0) |
|
||||
DISSECT_IMAGE_FSCK |
|
||||
DISSECT_IMAGE_MKDIR |
|
||||
@@ -1022,7 +1025,7 @@ static int verb_pending_or_reboot(int argc, char **argv, void *userdata) {
|
||||
|
||||
if (arg_image || arg_root)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"The --root=/--image switches may not be combined with the '%s' operation.", argv[0]);
|
||||
"The --root=/--image= switches may not be combined with the '%s' operation.", argv[0]);
|
||||
|
||||
r = context_make_offline(&context, NULL);
|
||||
if (r < 0)
|
||||
@@ -1205,8 +1208,10 @@ static int verb_help(int argc, char **argv, void *userdata) {
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -C --component=NAME Select component to update\n"
|
||||
" --definitions=DIR Find transfer definitions in specified directory\n"
|
||||
" --root=PATH Operate relative to root path\n"
|
||||
" --image=PATH Operate relative to image file\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify disk image dissection policy\n"
|
||||
" -m --instances-max=INT How many instances to maintain\n"
|
||||
" --sync=BOOL Controls whether to sync data to disk\n"
|
||||
" --verify=BOOL Force signature verification on or off\n"
|
||||
@@ -1238,6 +1243,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_IMAGE,
|
||||
ARG_REBOOT,
|
||||
ARG_VERIFY,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -1254,6 +1260,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "reboot", no_argument, NULL, ARG_REBOOT },
|
||||
{ "component", required_argument, NULL, 'C' },
|
||||
{ "verify", required_argument, NULL, ARG_VERIFY },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1351,6 +1358,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
+26
-9
@@ -99,6 +99,7 @@ static const char *arg_replace = NULL;
|
||||
static bool arg_dry_run = false;
|
||||
static bool arg_inline = false;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
static OrderedHashmap *users = NULL, *groups = NULL;
|
||||
static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL;
|
||||
@@ -128,6 +129,7 @@ STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(uid_range, uid_range_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static int errno_is_not_exists(int code) {
|
||||
/* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
|
||||
@@ -1964,6 +1966,7 @@ static int help(void) {
|
||||
" --cat-config Show configuration files\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" --replace=PATH Treat arguments as replacement for PATH\n"
|
||||
" --dry-run Just print what would be done\n"
|
||||
" --inline Treat arguments as configuration lines\n"
|
||||
@@ -1986,18 +1989,20 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_DRY_RUN,
|
||||
ARG_INLINE,
|
||||
ARG_NO_PAGER,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "replace", required_argument, NULL, ARG_REPLACE },
|
||||
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
|
||||
{ "inline", no_argument, NULL, ARG_INLINE },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "replace", required_argument, NULL, ARG_REPLACE },
|
||||
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
|
||||
{ "inline", no_argument, NULL, ARG_INLINE },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -2058,6 +2063,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_pager_flags |= PAGER_DISABLE;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2173,6 +2189,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
|
||||
@@ -95,6 +95,7 @@ simple_tests += files(
|
||||
'test-hostname-setup.c',
|
||||
'test-hostname-util.c',
|
||||
'test-id128.c',
|
||||
'test-image-policy.c',
|
||||
'test-import-util.c',
|
||||
'test-in-addr-prefix-util.c',
|
||||
'test-in-addr-util.c',
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "image-policy.h"
|
||||
#include "pretty-print.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "pager.h"
|
||||
|
||||
static void test_policy(const ImagePolicy *p, const char *name) {
|
||||
_cleanup_free_ char *as_string = NULL, *as_string_simplified = NULL;
|
||||
_cleanup_free_ ImagePolicy *parsed = NULL;
|
||||
|
||||
assert_se(image_policy_to_string(p, /* simplified= */ false, &as_string) >= 0);
|
||||
assert_se(image_policy_to_string(p, /* simplified= */ true, &as_string_simplified) >= 0);
|
||||
|
||||
printf("%s%s", ansi_underline(), name);
|
||||
|
||||
if (!streq(as_string_simplified, name)) {
|
||||
printf(" → %s", as_string_simplified);
|
||||
|
||||
if (!streq(as_string, as_string_simplified))
|
||||
printf(" (aka %s)", as_string);
|
||||
}
|
||||
|
||||
printf("%s\n", ansi_normal());
|
||||
|
||||
assert_se(image_policy_from_string(as_string, &parsed) >= 0);
|
||||
assert_se(image_policy_equal(p, parsed));
|
||||
parsed = image_policy_free(parsed);
|
||||
|
||||
assert_se(image_policy_from_string(as_string_simplified, &parsed) >= 0);
|
||||
assert_se(image_policy_equivalent(p, parsed));
|
||||
parsed = image_policy_free(parsed);
|
||||
|
||||
for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
|
||||
_cleanup_free_ char *k = NULL;
|
||||
PartitionPolicyFlags f;
|
||||
|
||||
f = image_policy_get(p, d);
|
||||
if (f < 0) {
|
||||
f = image_policy_get_exhaustively(p, d);
|
||||
assert_se(f >= 0);
|
||||
assert_se(partition_policy_flags_to_string(f, /* simplified= */ true, &k) >= 0);
|
||||
|
||||
printf("%s\t%s → n/a (exhaustively: %s)%s\n", ansi_grey(), partition_designator_to_string(d), k, ansi_normal());
|
||||
} else {
|
||||
assert_se(partition_policy_flags_to_string(f, /* simplified= */ true, &k) >= 0);
|
||||
printf("\t%s → %s\n", partition_designator_to_string(d), k);
|
||||
}
|
||||
}
|
||||
|
||||
_cleanup_free_ char *w = NULL;
|
||||
assert_se(partition_policy_flags_to_string(image_policy_default(p), /* simplified= */ true, &w) >= 0);
|
||||
printf("\tdefault → %s\n", w);
|
||||
}
|
||||
|
||||
static void test_policy_string(const char *t) {
|
||||
_cleanup_free_ ImagePolicy *parsed = NULL;
|
||||
|
||||
assert_se(image_policy_from_string(t, &parsed) >= 0);
|
||||
test_policy(parsed, t);
|
||||
}
|
||||
|
||||
static void test_policy_equiv(const char *s, bool (*func)(const ImagePolicy *p)) {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
assert_se(image_policy_from_string(s, &p) >= 0);
|
||||
|
||||
assert_se(func(p));
|
||||
assert_se(func == image_policy_equiv_ignore || !image_policy_equiv_ignore(p));
|
||||
assert_se(func == image_policy_equiv_allow || !image_policy_equiv_allow(p));
|
||||
assert_se(func == image_policy_equiv_deny || !image_policy_equiv_deny(p));
|
||||
}
|
||||
|
||||
TEST_RET(test_image_policy_to_string) {
|
||||
test_policy(&image_policy_allow, "*");
|
||||
test_policy(&image_policy_ignore, "-");
|
||||
test_policy(&image_policy_deny, "~");
|
||||
test_policy(&image_policy_sysext, "sysext");
|
||||
test_policy(&image_policy_sysext_strict, "sysext-strict");
|
||||
test_policy(&image_policy_container, "container");
|
||||
test_policy(&image_policy_host, "host");
|
||||
test_policy(&image_policy_service, "service");
|
||||
test_policy(NULL, "null");
|
||||
|
||||
test_policy_string("");
|
||||
test_policy_string("-");
|
||||
test_policy_string("*");
|
||||
test_policy_string("~");
|
||||
test_policy_string("swap=open");
|
||||
test_policy_string("swap=open:root=signed");
|
||||
test_policy_string("swap=open:root=signed+read-only-on+growfs-off:=absent");
|
||||
test_policy_string("=-");
|
||||
test_policy_string("=");
|
||||
|
||||
test_policy_equiv("", image_policy_equiv_ignore);
|
||||
test_policy_equiv("-", image_policy_equiv_ignore);
|
||||
test_policy_equiv("*", image_policy_equiv_allow);
|
||||
test_policy_equiv("~", image_policy_equiv_deny);
|
||||
test_policy_equiv("=absent", image_policy_equiv_deny);
|
||||
test_policy_equiv("=open", image_policy_equiv_allow);
|
||||
test_policy_equiv("=verity+signed+encrypted+unprotected+unused+absent", image_policy_equiv_allow);
|
||||
test_policy_equiv("=signed+verity+encrypted+unused+unprotected+absent", image_policy_equiv_allow);
|
||||
test_policy_equiv("=ignore", image_policy_equiv_ignore);
|
||||
test_policy_equiv("=absent+unused", image_policy_equiv_ignore);
|
||||
test_policy_equiv("=unused+absent", image_policy_equiv_ignore);
|
||||
test_policy_equiv("root=ignore:=ignore", image_policy_equiv_ignore);
|
||||
|
||||
assert_se(image_policy_from_string("pfft", NULL) == -EINVAL);
|
||||
assert_se(image_policy_from_string("öäüß", NULL) == -EINVAL);
|
||||
assert_se(image_policy_from_string(":", NULL) == -EINVAL);
|
||||
assert_se(image_policy_from_string("a=", NULL) == -EBADSLT);
|
||||
assert_se(image_policy_from_string("=a", NULL) == -EBADRQC);
|
||||
assert_se(image_policy_from_string("==", NULL) == -EBADRQC);
|
||||
assert_se(image_policy_from_string("root=verity:root=encrypted", NULL) == -ENOTUNIQ);
|
||||
assert_se(image_policy_from_string("root=grbl", NULL) == -EBADRQC);
|
||||
assert_se(image_policy_from_string("wowza=grbl", NULL) == -EBADSLT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
@@ -82,7 +82,7 @@ static void* thread_func(void *ptr) {
|
||||
|
||||
log_notice("Acquired loop device %s, will mount on %s", loop->node, mounted);
|
||||
|
||||
r = dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected);
|
||||
r = dissect_loop_device(loop, NULL, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed dissect loopback device %s: %m", loop->node);
|
||||
assert_se(r >= 0);
|
||||
@@ -213,7 +213,7 @@ static int run(int argc, char *argv[]) {
|
||||
sfdisk = NULL;
|
||||
|
||||
#if HAVE_BLKID
|
||||
assert_se(dissect_image_file(p, NULL, NULL, 0, &dissected) >= 0);
|
||||
assert_se(dissect_image_file(p, NULL, NULL, NULL, 0, &dissected) >= 0);
|
||||
verify_dissected_image(dissected);
|
||||
dissected = dissected_image_unref(dissected);
|
||||
#endif
|
||||
@@ -231,7 +231,7 @@ static int run(int argc, char *argv[]) {
|
||||
assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, LO_FLAGS_PARTSCAN, LOCK_EX, &loop) >= 0);
|
||||
|
||||
#if HAVE_BLKID
|
||||
assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
|
||||
assert_se(dissect_loop_device(loop, NULL, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
|
||||
verify_dissected_image(dissected);
|
||||
|
||||
FOREACH_STRING(fs, "vfat", "ext4") {
|
||||
@@ -267,12 +267,12 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
/* Try to read once, without pinning or adding partitions, i.e. by only accessing the whole block
|
||||
* device. */
|
||||
assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0);
|
||||
assert_se(dissect_loop_device(loop, NULL, NULL, NULL, 0, &dissected) >= 0);
|
||||
verify_dissected_image_harder(dissected);
|
||||
dissected = dissected_image_unref(dissected);
|
||||
|
||||
/* Now go via the loopback device after all, but this time add/pin, because now we want to mount it. */
|
||||
assert_se(dissect_loop_device(loop, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
|
||||
assert_se(dissect_loop_device(loop, NULL, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
|
||||
verify_dissected_image_harder(dissected);
|
||||
|
||||
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
|
||||
|
||||
@@ -176,6 +176,7 @@ TEST(protect_kernel_logs) {
|
||||
assert_se(fd > 0);
|
||||
|
||||
r = setup_namespace(NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&ns_info,
|
||||
@@ -193,6 +194,7 @@ TEST(protect_kernel_logs) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
@@ -208,6 +210,7 @@ TEST(protect_kernel_logs) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
assert_se(r == 0);
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ int main(int argc, char *argv[]) {
|
||||
log_info("Not chrooted");
|
||||
|
||||
r = setup_namespace(root_directory,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&ns_info,
|
||||
@@ -91,6 +92,7 @@ int main(int argc, char *argv[]) {
|
||||
&(TemporaryFileSystem) { .path = (char*) "/var", .options = (char*) "ro" }, 1,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
tmp_dir,
|
||||
var_tmp_dir,
|
||||
NULL,
|
||||
@@ -110,6 +112,7 @@ int main(int argc, char *argv[]) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set up namespace: %m");
|
||||
|
||||
@@ -206,6 +206,7 @@ static char **arg_exclude_prefixes = NULL;
|
||||
static char *arg_root = NULL;
|
||||
static char *arg_image = NULL;
|
||||
static char *arg_replace = NULL;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
|
||||
#define MAX_DEPTH 256
|
||||
|
||||
@@ -219,6 +220,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
|
||||
static const char *const creation_mode_verb_table[_CREATION_MODE_MAX] = {
|
||||
[CREATION_NORMAL] = "Created",
|
||||
@@ -3699,6 +3701,7 @@ static int help(void) {
|
||||
" -E Ignore rules prefixed with /dev, /proc, /run, /sys\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" --replace=PATH Treat arguments as replacement for PATH\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
"\nSee the %s for details.\n",
|
||||
@@ -3726,6 +3729,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_IMAGE,
|
||||
ARG_REPLACE,
|
||||
ARG_NO_PAGER,
|
||||
ARG_IMAGE_POLICY,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -3743,6 +3747,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "image", required_argument, NULL, ARG_IMAGE },
|
||||
{ "replace", required_argument, NULL, ARG_REPLACE },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -3833,6 +3838,17 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_pager_flags |= PAGER_DISABLE;
|
||||
break;
|
||||
|
||||
case ARG_IMAGE_POLICY: {
|
||||
_cleanup_(image_policy_freep) ImagePolicy *p = NULL;
|
||||
|
||||
r = image_policy_from_string(optarg, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse image policy: %s", optarg);
|
||||
|
||||
image_policy_free(arg_image_policy);
|
||||
arg_image_policy = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -4153,6 +4169,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
arg_image_policy,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
|
||||
Reference in New Issue
Block a user