Merge pull request #25608 from poettering/dissect-moar

dissect: add dissection policies
This commit is contained in:
Lennart Poettering
2023-04-12 13:46:08 +02:00
committed by GitHub
73 changed files with 2569 additions and 269 deletions
+154
View File
@@ -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;
}
+3
View File
@@ -0,0 +1,3 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
int verb_image_policy(int argc, char *argv[], void *userdata);
+20
View File
@@ -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,
+1
View File
@@ -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);
+1
View File
@@ -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',
+19
View File
@@ -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,
+2
View File
@@ -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 */
+61
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+2
View File
@@ -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) {
+3
View File
@@ -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)
+39
View File
@@ -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,
+1
View File
@@ -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
View File
@@ -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;
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+18 -1
View File
@@ -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 |
+21 -1
View File
@@ -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;
}
+18 -2
View File
@@ -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 |
+29 -12
View File
@@ -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 |
+4 -4
View File
@@ -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
View File
@@ -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) {
+20
View File
@@ -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
View File
@@ -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 -2
View File
@@ -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);
+4 -1
View File
@@ -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,
+4 -1
View File
@@ -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",
+29 -5
View File
@@ -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;
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+689
View File
@@ -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,
};
+97
View File
@@ -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);
+1
View File
@@ -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',
+6 -4
View File
@@ -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) {
+1 -1
View File
@@ -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
View File
@@ -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;
+20 -1
View File
@@ -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 |
+2
View File
@@ -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";
+21 -3
View File
@@ -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
View File
@@ -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 |
+1
View File
@@ -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',
+122
View File
@@ -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);
+5 -5
View File
@@ -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);
+3
View File
@@ -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);
+3
View File
@@ -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");
+17
View File
@@ -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 |