Compare commits

..

7 Commits

Author SHA1 Message Date
Steve Dodd 44aaddad06 Request seccomp logging if SYSTEMD_LOG_SECCOMP environment variable is set. 2020-08-21 11:24:53 +02:00
Zbigniew Jędrzejewski-Szmek 3fb01017ee
Merge pull request #16686 from bluca/mount_images_opts
core: add mount options support for MountImages
2020-08-21 10:11:08 +02:00
Aurelien Jarno f9252236c8 seccomp: add support for riscv64
This patch adds seccomp support to the riscv64 architecture. seccomp
support is available in the riscv64 kernel since version 5.5, and it
has just been added to the libseccomp library.

riscv64 uses generic syscalls like aarch64, so I used that architecture
as a reference to find which code has to be modified.

With this patch, the testsuite passes successfully, including the
test-seccomp test. The system boots and works fine with kernel 5.4 (i.e.
without seccomp support) and kernel 5.5 (i.e. with seccomp support). I
have also verified that the "SystemCallFilter=~socket" option prevents a
service to use the ping utility when running on kernel 5.5.
2020-08-21 10:10:29 +02:00
Luca Boccassi 427353f668 core: add mount options support for MountImages
Follow the same model established for RootImage and RootImageOptions,
and allow to either append a single list of options or tuples of
partition_number:options.
2020-08-20 14:45:40 +01:00
Luca Boccassi 9ece644435 core: change RootImageOptions to use names instead of partition numbers
Follow the designations from the Discoverable Partitions Specification
2020-08-20 13:58:02 +01:00
Luca Boccassi bc8d56d305 core: use strv_split_colon_pairs when parsing RootImageOptions 2020-08-20 13:24:32 +01:00
Luca Boccassi c20acbb2bd core: cleanup unused variables
Leftovers from previous implementation of MountImages feature, unused now
2020-08-20 13:24:32 +01:00
15 changed files with 535 additions and 192 deletions

View File

@ -90,6 +90,10 @@ systemctl:
* `$SYSTEMCTL_SKIP_SYSV=1` — if set, do not call out to SysV compatibility hooks. * `$SYSTEMCTL_SKIP_SYSV=1` — if set, do not call out to SysV compatibility hooks.
* `$SYSTEMD_LOG_SECCOMP=1` — if set, system calls blocked by seccomp filtering,
for example in systemd-nspawn, will be logged to the audit log, if the current
kernel version supports this.
systemd-nspawn: systemd-nspawn:
* `$SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1` — if set, force nspawn into unified * `$SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1` — if set, force nspawn into unified

View File

@ -149,11 +149,53 @@
<term><varname>RootImageOptions=</varname></term> <term><varname>RootImageOptions=</varname></term>
<listitem><para>Takes a comma-separated list of mount options that will be used on disk images specified by <listitem><para>Takes a comma-separated list of mount options that will be used on disk images specified by
<varname>RootImage=</varname>. Optionally a partition number can be prefixed, followed by colon, in <varname>RootImage=</varname>. Optionally a partition name can be prefixed, followed by colon, in
case the image has multiple partitions, otherwise partition number 0 is implied. case the image has multiple partitions, otherwise partition name <literal>root</literal> is implied.
Options for multiple partitions can be specified in a single line with space separators. Assigning an empty Options for multiple partitions can be specified in a single line with space separators. Assigning an empty
string removes previous assignments. For a list of valid mount options, please refer to string removes previous assignments. Duplicated options are ignored. For a list of valid mount options, please
<citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> refer to <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>Valid partition names follow the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable
Partitions Specification</ulink>.</para>
<table>
<title>Accepted partition names</title>
<tgroup cols='1'>
<colspec colname='partition' />
<thead>
<row>
<entry>Partition Name</entry>
</row>
</thead>
<tbody>
<row>
<entry>root</entry>
</row>
<row>
<entry>root-secondary</entry>
</row>
<row>
<entry>home</entry>
</row>
<row>
<entry>srv</entry>
</row>
<row>
<entry>esp</entry>
</row>
<row>
<entry>xbootldr</entry>
</row>
<row>
<entry>tmp</entry>
</row>
<row>
<entry>var</entry>
</row>
</tbody>
</tgroup>
</table>
<xi:include href="system-only.xml" xpointer="singular"/></listitem> <xi:include href="system-only.xml" xpointer="singular"/></listitem>
</varlistentry> </varlistentry>
@ -268,13 +310,19 @@
system hierarchy from a block device node or loopback file, but the destination directory can be system hierarchy from a block device node or loopback file, but the destination directory can be
specified as well as mount options. This option expects a whitespace separated list of mount specified as well as mount options. This option expects a whitespace separated list of mount
definitions. Each definition consists of a colon-separated tuple of source path and destination definitions. Each definition consists of a colon-separated tuple of source path and destination
directory. Each mount definition may be prefixed with <literal>-</literal>, in which case it will be definitions, optionally followed by another colon and a list of mount options.</para>
<para>Mount options may be defined as a single comma-separated list of options, in which case they
will be implicitly applied to the root partition on the image, or a series of colon-separated tuples
of partition name and mount options. Valid partition names and mount options are the same as for
<varname>RootImageOptions=</varname> setting described above.</para>
<para>Each mount definition may be prefixed with <literal>-</literal>, in which case it will be
ignored when its source path does not exist. The source argument is a path to a block device node or ignored when its source path does not exist. The source argument is a path to a block device node or
regular file. If source or destination contain a <literal>:</literal>, it needs to be escaped as regular file. If source or destination contain a <literal>:</literal>, it needs to be escaped as
<literal>\:</literal>. <literal>\:</literal>. The device node or file system image file needs to follow the same rules as
The device node or file system image file needs to follow the same rules as specified specified for <varname>RootImage=</varname>. Any mounts created with this option are specific to the
for <varname>RootImage=</varname>. Any mounts created with this option are specific to the unit, and unit, and are not visible in the host's mount table.</para>
are not visible in the host's mount table.</para>
<para>These settings may be used more than once, each usage appends to the unit's list of mount <para>These settings may be used more than once, each usage appends to the unit's list of mount
paths. If the empty string is assigned, the entire list of mount paths defined prior to this is paths. If the empty string is assigned, the entire list of mount paths defined prior to this is

View File

@ -802,12 +802,14 @@ static int property_get_root_image_options(
assert(property); assert(property);
assert(reply); assert(reply);
r = sd_bus_message_open_container(reply, 'a', "(us)"); r = sd_bus_message_open_container(reply, 'a', "(ss)");
if (r < 0) if (r < 0)
return r; return r;
LIST_FOREACH(mount_options, m, c->root_image_options) { LIST_FOREACH(mount_options, m, c->root_image_options) {
r = sd_bus_message_append(reply, "(us)", m->partition_number, m->options); r = sd_bus_message_append(reply, "(ss)",
partition_designator_to_string(m->partition_designator),
m->options);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -832,18 +834,39 @@ static int property_get_mount_images(
assert(property); assert(property);
assert(reply); assert(reply);
r = sd_bus_message_open_container(reply, 'a', "(ssb)"); r = sd_bus_message_open_container(reply, 'a', "(ssba(ss))");
if (r < 0) if (r < 0)
return r; return r;
for (size_t i = 0; i < c->n_mount_images; i++) { for (size_t i = 0; i < c->n_mount_images; i++) {
MountOptions *m;
r = sd_bus_message_open_container(reply, SD_BUS_TYPE_STRUCT, "ssba(ss)");
if (r < 0)
return r;
r = sd_bus_message_append( r = sd_bus_message_append(
reply, "(ssb)", reply, "ssb",
c->mount_images[i].source, c->mount_images[i].source,
c->mount_images[i].destination, c->mount_images[i].destination,
c->mount_images[i].ignore_enoent); c->mount_images[i].ignore_enoent);
if (r < 0) if (r < 0)
return r; return r;
r = sd_bus_message_open_container(reply, 'a', "(ss)");
if (r < 0)
return r;
LIST_FOREACH(mount_options, m, c->mount_images[i].mount_options) {
r = sd_bus_message_append(reply, "(ss)",
partition_designator_to_string(m->partition_designator),
m->options);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
} }
return sd_bus_message_close_container(reply); return sd_bus_message_close_container(reply);
@ -891,13 +914,13 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootImageOptions", "a(us)", property_get_root_image_options, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootImageOptions", "a(ss)", property_get_root_image_options, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountImages", "a(ssb)", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MountImages", "a(ssba(ss))", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@ -1338,6 +1361,74 @@ static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint6
static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flags_to_string); static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flags_to_string);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check); static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check);
/* ret_format_str is an accumulator, so if it has any pre-existing content, new options will be appended to it */
static int read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
_cleanup_free_ char *format_str = NULL;
const char *mount_options, *partition;
int r;
assert(message);
assert(ret_options);
assert(ret_format_str);
assert(separator);
r = sd_bus_message_enter_container(message, 'a', "(ss)");
if (r < 0)
return r;
while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) {
_cleanup_free_ char *previous = NULL, *escaped = NULL;
_cleanup_free_ MountOptions *o = NULL;
int partition_designator;
if (chars_intersect(mount_options, WHITESPACE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid mount options string, contains whitespace character(s): %s", mount_options);
partition_designator = partition_designator_from_string(partition);
if (partition_designator < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid partition name %s", partition);
/* Need to store them in the unit with the escapes, so that they can be parsed again */
escaped = shell_escape(mount_options, ":");
if (!escaped)
return -ENOMEM;
previous = TAKE_PTR(format_str);
format_str = strjoin(previous, previous ? separator : "", partition, ":", escaped);
if (!format_str)
return -ENOMEM;
o = new(MountOptions, 1);
if (!o)
return -ENOMEM;
*o = (MountOptions) {
.partition_designator = partition_designator,
.options = strdup(mount_options),
};
if (!o->options)
return -ENOMEM;
LIST_APPEND(mount_options, options, TAKE_PTR(o));
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
if (!LIST_IS_EMPTY(options)) {
char *final = strjoin(*ret_format_str, !isempty(*ret_format_str) ? separator : "", format_str);
if (!final)
return -ENOMEM;
free_and_replace(*ret_format_str, final);
LIST_JOIN(mount_options, *ret_options, options);
}
return 0;
}
int bus_exec_context_set_transient_property( int bus_exec_context_set_transient_property(
Unit *u, Unit *u,
ExecContext *c, ExecContext *c,
@ -1371,39 +1462,8 @@ int bus_exec_context_set_transient_property(
if (streq(name, "RootImageOptions")) { if (streq(name, "RootImageOptions")) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
_cleanup_free_ char *format_str = NULL; _cleanup_free_ char *format_str = NULL;
const char *mount_options;
unsigned partition_number;
r = sd_bus_message_enter_container(message, 'a', "(us)"); r = read_mount_options(message, error, &options, &format_str, " ");
if (r < 0)
return r;
while ((r = sd_bus_message_read(message, "(us)", &partition_number, &mount_options)) > 0) {
_cleanup_free_ char *previous = TAKE_PTR(format_str);
_cleanup_free_ MountOptions *o = NULL;
if (chars_intersect(mount_options, WHITESPACE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid mount options string, contains whitespace character(s): %s", mount_options);
if (asprintf(&format_str, "%s%s%u:%s", strempty(previous), previous ? " " : "", partition_number, mount_options) < 0)
return -ENOMEM;
o = new(MountOptions, 1);
if (!o)
return -ENOMEM;
*o = (MountOptions) {
.partition_number = partition_number,
.options = strdup(mount_options),
};
if (!o->options)
return -ENOMEM;
LIST_APPEND(mount_options, options, TAKE_PTR(o));
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(message);
if (r < 0) if (r < 0)
return r; return r;
@ -2938,13 +2998,23 @@ int bus_exec_context_set_transient_property(
char *source, *destination; char *source, *destination;
int permissive; int permissive;
r = sd_bus_message_enter_container(message, 'a', "(ssb)"); r = sd_bus_message_enter_container(message, 'a', "(ssba(ss))");
if (r < 0) if (r < 0)
return r; return r;
while ((r = sd_bus_message_read(message, "(ssb)", &source, &destination, &permissive)) > 0) { for (;;) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
_cleanup_free_ char *source_escaped = NULL, *destination_escaped = NULL;
char *tuple; char *tuple;
r = sd_bus_message_enter_container(message, 'r', "ssba(ss)");
if (r < 0)
return r;
r = sd_bus_message_read(message, "ssb", &source, &destination, &permissive);
if (r <= 0)
break;
if (!path_is_absolute(source)) if (!path_is_absolute(source))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not absolute.", source); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not absolute.", source);
if (!path_is_normalized(source)) if (!path_is_normalized(source))
@ -2954,15 +3024,37 @@ int bus_exec_context_set_transient_property(
if (!path_is_normalized(destination)) if (!path_is_normalized(destination))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination);
tuple = strjoin(format_str, format_str ? " " : "", permissive ? "-" : "", source, ":", destination); /* Need to store them in the unit with the escapes, so that they can be parsed again */
source_escaped = shell_escape(source, ":");
if (!source_escaped)
return -ENOMEM;
destination_escaped = shell_escape(destination, ":");
if (!destination_escaped)
return -ENOMEM;
tuple = strjoin(format_str,
format_str ? " " : "",
permissive ? "-" : "",
source_escaped,
":",
destination_escaped);
if (!tuple) if (!tuple)
return -ENOMEM; return -ENOMEM;
free_and_replace(format_str, tuple); free_and_replace(format_str, tuple);
r = read_mount_options(message, error, &options, &format_str, ":");
if (r < 0)
return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
r = mount_image_add(&mount_images, &n_mount_images, r = mount_image_add(&mount_images, &n_mount_images,
&(MountImage) { &(MountImage) {
.source = source, .source = source,
.destination = destination, .destination = destination,
.mount_options = options,
.ignore_enoent = permissive, .ignore_enoent = permissive,
}); });
if (r < 0) if (r < 0)

View File

@ -4634,7 +4634,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%sRootImageOptions:", prefix); fprintf(f, "%sRootImageOptions:", prefix);
LIST_FOREACH(mount_options, o, c->root_image_options) LIST_FOREACH(mount_options, o, c->root_image_options)
if (!isempty(o->options)) if (!isempty(o->options))
fprintf(f, " %u:%s", o->partition_number, o->options); fprintf(f, " %s:%s",
partition_designator_to_string(o->partition_designator),
o->options);
fprintf(f, "\n"); fprintf(f, "\n");
} }
@ -5035,11 +5037,20 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%d\n", c->syscall_errno); fprintf(f, "%d\n", c->syscall_errno);
} }
for (i = 0; i < c->n_mount_images; i++) for (i = 0; i < c->n_mount_images; i++) {
fprintf(f, "%sMountImages: %s%s:%s\n", prefix, MountOptions *o;
fprintf(f, "%sMountImages: %s%s:%s%s", prefix,
c->mount_images[i].ignore_enoent ? "-": "", c->mount_images[i].ignore_enoent ? "-": "",
c->mount_images[i].source, c->mount_images[i].source,
c->mount_images[i].destination); c->mount_images[i].destination,
LIST_IS_EMPTY(c->mount_images[i].mount_options) ? "": ":");
LIST_FOREACH(mount_options, o, c->mount_images[i].mount_options)
fprintf(f, "%s:%s",
partition_designator_to_string(o->partition_designator),
o->options);
fprintf(f, "\n");
}
} }
bool exec_context_maintains_privileges(const ExecContext *c) { bool exec_context_maintains_privileges(const ExecContext *c) {

View File

@ -1429,9 +1429,10 @@ int config_parse_root_image_options(
void *userdata) { void *userdata) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
_cleanup_strv_free_ char **l = NULL;
char **first = NULL, **second = NULL;
ExecContext *c = data; ExecContext *c = data;
const Unit *u = userdata; const Unit *u = userdata;
const char *p = rvalue;
int r; int r;
assert(filename); assert(filename);
@ -1444,44 +1445,32 @@ int config_parse_root_image_options(
return 0; return 0;
} }
for (;;) { r = strv_split_colon_pairs(&l, rvalue);
_cleanup_free_ char *mount_options_resolved = NULL, *first = NULL, *tuple = NULL;
const char *mount_options = NULL, *second = NULL;
MountOptions *o = NULL;
unsigned int partition_number = 0;
r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM) if (r == -ENOMEM)
return log_oom(); return log_oom();
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue); log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
return 0; return 0;
} }
second = tuple; STRV_FOREACH_PAIR(first, second, l) {
r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); MountOptions *o = NULL;
if (r == 0) _cleanup_free_ char *mount_options_resolved = NULL;
continue; const char *mount_options = NULL, *partition = "root";
if (r == -ENOMEM) int partition_designator;
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
continue;
}
/* Format is either '0:foo' or 'foo' (0 is implied) */ /* Format is either 'root:foo' or 'foo' (root is implied) */
if (!isempty(second) && second[-1] == ':') { if (!isempty(*second)) {
mount_options = second; partition = *first;
r = safe_atou(first, &partition_number); mount_options = *second;
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", first);
continue;
}
} else } else
mount_options = first; mount_options = *first;
partition_designator = partition_designator_from_string(partition);
if (partition_designator < 0) {
log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid partition name %s, ignoring", partition);
continue;
}
r = unit_full_printf(u, mount_options, &mount_options_resolved); r = unit_full_printf(u, mount_options, &mount_options_resolved);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options); log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
@ -1492,10 +1481,10 @@ int config_parse_root_image_options(
if (!o) if (!o)
return log_oom(); return log_oom();
*o = (MountOptions) { *o = (MountOptions) {
.partition_number = partition_number, .partition_designator = partition_designator,
.options = TAKE_PTR(mount_options_resolved), .options = TAKE_PTR(mount_options_resolved),
}; };
LIST_APPEND(mount_options, options, o); LIST_APPEND(mount_options, options, TAKE_PTR(o));
} }
/* empty spaces/separators only */ /* empty spaces/separators only */
@ -4633,10 +4622,9 @@ int config_parse_mount_images(
void *data, void *data,
void *userdata) { void *userdata) {
_cleanup_strv_free_ char **l = NULL;
ExecContext *c = data; ExecContext *c = data;
const Unit *u = userdata; const Unit *u = userdata;
char **source = NULL, **destination = NULL; const char *p = rvalue;
int r; int r;
assert(filename); assert(filename);
@ -4650,23 +4638,31 @@ int config_parse_mount_images(
return 0; return 0;
} }
r = strv_split_colon_pairs(&l, rvalue); for (;;) {
if (r == -ENOMEM) _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
return log_oom(); _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
return 0;
}
STRV_FOREACH_PAIR(source, destination, l) {
_cleanup_free_ char *sresolved = NULL, *dresolved = NULL; _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
const char *q = NULL;
char *s = NULL; char *s = NULL;
bool permissive = false; bool permissive = false;
r = unit_full_printf(u, *source, &sresolved); r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
break;
q = tuple;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
r = unit_full_printf(u, first, &sresolved);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to resolve unit specifiers in \"%s\", ignoring: %m", *source); "Failed to resolve unit specifiers in \"%s\", ignoring: %m", first);
continue; continue;
} }
@ -4680,15 +4676,15 @@ int config_parse_mount_images(
if (r < 0) if (r < 0)
continue; continue;
if (isempty(*destination)) { if (isempty(second)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue); log_syntax(unit, LOG_ERR, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
continue; continue;
} }
r = unit_full_printf(u, *destination, &dresolved); r = unit_full_printf(u, second, &dresolved);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to resolve specifiers in \"%s\", ignoring: %m", *destination); "Failed to resolve specifiers in \"%s\", ignoring: %m", second);
continue; continue;
} }
@ -4696,10 +4692,62 @@ int config_parse_mount_images(
if (r < 0) if (r < 0)
continue; continue;
for (;;) {
_cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
MountOptions *o = NULL;
int partition_designator;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
if (r < 0)
return r;
if (r == 0)
break;
/* Single set of options, applying to the root partition/single filesystem */
if (r == 1) {
r = unit_full_printf(u, partition, &mount_options_resolved);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first);
continue;
}
o = new(MountOptions, 1);
if (!o)
return log_oom();
*o = (MountOptions) {
.partition_designator = PARTITION_ROOT,
.options = TAKE_PTR(mount_options_resolved),
};
LIST_APPEND(mount_options, options, o);
break;
}
partition_designator = partition_designator_from_string(partition);
if (partition_designator < 0) {
log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid partition name %s, ignoring", partition);
continue;
}
r = unit_full_printf(u, mount_options, &mount_options_resolved);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
continue;
}
o = new(MountOptions, 1);
if (!o)
return log_oom();
*o = (MountOptions) {
.partition_designator = partition_designator,
.options = TAKE_PTR(mount_options_resolved),
};
LIST_APPEND(mount_options, options, o);
}
r = mount_image_add(&c->mount_images, &c->n_mount_images, r = mount_image_add(&c->mount_images, &c->n_mount_images,
&(MountImage) { &(MountImage) {
.source = s, .source = s,
.destination = dresolved, .destination = dresolved,
.mount_options = options,
.ignore_enoent = permissive, .ignore_enoent = permissive,
}); });
if (r < 0) if (r < 0)

View File

@ -73,7 +73,7 @@ typedef struct MountEntry {
char *options_malloc; char *options_malloc;
unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */ unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */
unsigned n_followed; unsigned n_followed;
LIST_FIELDS(MountEntry, mount_entry); LIST_HEAD(MountOptions, image_options);
} MountEntry; } MountEntry;
/* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted /* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted
@ -247,6 +247,7 @@ static void mount_entry_done(MountEntry *p) {
p->path_malloc = mfree(p->path_malloc); p->path_malloc = mfree(p->path_malloc);
p->source_malloc = mfree(p->source_malloc); p->source_malloc = mfree(p->source_malloc);
p->options_malloc = mfree(p->options_malloc); p->options_malloc = mfree(p->options_malloc);
p->image_options = mount_options_free_all(p->image_options);
} }
static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) { static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) {
@ -339,6 +340,7 @@ static int append_mount_images(MountEntry **p, const MountImage *mount_images, s
.path_const = m->destination, .path_const = m->destination,
.mode = MOUNT_IMAGES, .mode = MOUNT_IMAGES,
.source_const = m->source, .source_const = m->source,
.image_options = m->mount_options,
.ignore = m->ignore_enoent, .ignore = m->ignore_enoent,
}; };
} }
@ -925,10 +927,10 @@ static int mount_images(const MountEntry *m) {
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to create loop device for image: %m"); return log_debug_errno(r, "Failed to create loop device for image: %m");
r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, NULL, dissect_image_flags, &dissected_image); r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, m->image_options, dissect_image_flags, &dissected_image);
/* No partition table? Might be a single-filesystem image, try again */ /* No partition table? Might be a single-filesystem image, try again */
if (!verity_data && r < 0 && r == -ENOPKG) if (!verity_data && r < 0 && r == -ENOPKG)
r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, NULL, dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE, &dissected_image); r = dissect_image(loop_device->fd, root_hash_decoded, root_hash_size, verity_data, m->image_options, dissect_image_flags|DISSECT_IMAGE_NO_PARTITION_TABLE, &dissected_image);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m"); return log_debug_errno(r, "Failed to dissect image: %m");
@ -1838,6 +1840,7 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) {
for (i = 0; i < *n; i++) { for (i = 0; i < *n; i++) {
free(m[i].source); free(m[i].source);
free(m[i].destination); free(m[i].destination);
mount_options_free_all(m[i].mount_options);
} }
free(m); free(m);
@ -1847,6 +1850,8 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) {
int mount_image_add(MountImage **m, size_t *n, const MountImage *item) { int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
_cleanup_free_ char *s = NULL, *d = NULL; _cleanup_free_ char *s = NULL, *d = NULL;
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
MountOptions *i;
MountImage *c; MountImage *c;
assert(m); assert(m);
@ -1861,6 +1866,23 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
LIST_FOREACH(mount_options, i, item->mount_options) {
_cleanup_(mount_options_free_allp) MountOptions *o;
o = new(MountOptions, 1);
if (!o)
return -ENOMEM;
*o = (MountOptions) {
.partition_designator = i->partition_designator,
.options = strdup(i->options),
};
if (!o->options)
return -ENOMEM;
LIST_APPEND(mount_options, options, TAKE_PTR(o));
}
c = reallocarray(*m, *n + 1, sizeof(MountImage)); c = reallocarray(*m, *n + 1, sizeof(MountImage));
if (!c) if (!c)
return -ENOMEM; return -ENOMEM;
@ -1870,6 +1892,7 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
c[(*n) ++] = (MountImage) { c[(*n) ++] = (MountImage) {
.source = TAKE_PTR(s), .source = TAKE_PTR(s),
.destination = TAKE_PTR(d), .destination = TAKE_PTR(d),
.mount_options = TAKE_PTR(options),
.ignore_enoent = item->ignore_enoent, .ignore_enoent = item->ignore_enoent,
}; };

View File

@ -9,7 +9,6 @@ typedef struct NamespaceInfo NamespaceInfo;
typedef struct BindMount BindMount; typedef struct BindMount BindMount;
typedef struct TemporaryFileSystem TemporaryFileSystem; typedef struct TemporaryFileSystem TemporaryFileSystem;
typedef struct MountImage MountImage; typedef struct MountImage MountImage;
typedef struct MountEntry MountEntry;
#include <stdbool.h> #include <stdbool.h>
@ -77,6 +76,7 @@ struct TemporaryFileSystem {
struct MountImage { struct MountImage {
char *source; char *source;
char *destination; char *destination;
LIST_HEAD(MountOptions, mount_options);
bool ignore_enoent; bool ignore_enoent;
}; };

View File

@ -1695,6 +1695,9 @@ static int oci_seccomp_arch_from_string(const char *name, uint32_t *ret) {
{ "SCMP_ARCH_PPC", SCMP_ARCH_PPC }, { "SCMP_ARCH_PPC", SCMP_ARCH_PPC },
{ "SCMP_ARCH_PPC64", SCMP_ARCH_PPC64 }, { "SCMP_ARCH_PPC64", SCMP_ARCH_PPC64 },
{ "SCMP_ARCH_PPC64LE", SCMP_ARCH_PPC64LE }, { "SCMP_ARCH_PPC64LE", SCMP_ARCH_PPC64LE },
#ifdef SCMP_ARCH_RISCV64
{ "SCMP_ARCH_RISCV64", SCMP_ARCH_RISCV64 },
#endif
{ "SCMP_ARCH_S390", SCMP_ARCH_S390 }, { "SCMP_ARCH_S390", SCMP_ARCH_S390 },
{ "SCMP_ARCH_S390X", SCMP_ARCH_S390X }, { "SCMP_ARCH_S390X", SCMP_ARCH_S390X },
{ "SCMP_ARCH_X32", SCMP_ARCH_X32 }, { "SCMP_ARCH_X32", SCMP_ARCH_X32 },

View File

@ -10,6 +10,7 @@
#include "condition.h" #include "condition.h"
#include "coredump-util.h" #include "coredump-util.h"
#include "cpu-set-util.h" #include "cpu-set-util.h"
#include "dissect-image.h"
#include "escape.h" #include "escape.h"
#include "exec-util.h" #include "exec-util.h"
#include "exit-status.h" #include "exit-status.h"
@ -1456,6 +1457,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
} }
if (streq(field, "RootImageOptions")) { if (streq(field, "RootImageOptions")) {
_cleanup_strv_free_ char **l = NULL;
char **first = NULL, **second = NULL;
const char *p = eq; const char *p = eq;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
@ -1466,44 +1469,26 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(us)"); r = sd_bus_message_open_container(m, 'v', "a(ss)");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(us)"); r = sd_bus_message_open_container(m, 'a', "(ss)");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
for (;;) { r = strv_split_colon_pairs(&l, p);
_cleanup_free_ char *first = NULL, *tuple = NULL;
const char *mount_options = NULL, *second = NULL;
unsigned partition_number = 0;
r = extract_first_word(&p, &tuple, WHITESPACE, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m"); return log_error_errno(r, "Failed to parse argument: %m");
second = tuple; STRV_FOREACH_PAIR(first, second, l) {
r = extract_first_word(&second, &first, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); /* Format is either 'root:foo' or 'foo' (root is implied) */
if (r == 0) if (!isempty(*second) && partition_designator_from_string(*first) < 0)
continue; return bus_log_create_error(-EINVAL);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
/* Format is either '0:foo' or 'foo' (0 is implied) */ r = sd_bus_message_append(m, "(ss)",
if (!isempty(second) && second[-1] == ':') { !isempty(*second) ? *first : "root",
mount_options = second; !isempty(*second) ? *second : *first);
r = safe_atou(first, &partition_number);
if (r < 0) {
log_error_errno(r, "Failed to parse partition number from %s: %m", first);
continue;
}
} else
mount_options = first;
r = sd_bus_message_append(m, "(us)", partition_number, mount_options);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
} }
@ -1524,8 +1509,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
} }
if (streq(field, "MountImages")) { if (streq(field, "MountImages")) {
_cleanup_strv_free_ char **l = NULL;
char **source = NULL, **destination = NULL;
const char *p = eq; const char *p = eq;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
@ -1536,33 +1519,85 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'v', "a(ssb)"); r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ssb)"); r = sd_bus_message_open_container(m, 'a', "(ssba(ss))");
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
r = strv_split_colon_pairs(&l, p); for (;;) {
if (r < 0) _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
return log_error_errno(r, "Failed to parse argument: %m"); const char *q = NULL, *source = NULL;
STRV_FOREACH_PAIR(source, destination, l) {
char *s = *source;
bool permissive = false; bool permissive = false;
if (s[0] == '-') { r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
break;
q = tuple;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
source = first;
if (source[0] == '-') {
permissive = true; permissive = true;
s++; source++;
} }
if (isempty(*destination)) if (isempty(second))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Missing argument after ':': %s", "Missing argument after ':': %s",
eq); eq);
r = sd_bus_message_append(m, "(ssb)", s, *destination, permissive); r = sd_bus_message_open_container(m, 'r', "ssba(ss)");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "ssb", source, second, permissive);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_open_container(m, 'a', "(ss)");
if (r < 0)
return bus_log_create_error(r);
for (;;) {
_cleanup_free_ char *partition = NULL, *mount_options = NULL;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
if (r < 0)
return r;
if (r == 0)
break;
/* Single set of options, applying to the root partition/single filesystem */
if (r == 1) {
r = sd_bus_message_append(m, "(ss)", "root", partition);
if (r < 0)
return bus_log_create_error(r);
break;
}
if (partition_designator_from_string(partition) < 0)
return bus_log_create_error(-EINVAL);
r = sd_bus_message_append(m, "(ss)", partition, mount_options);
if (r < 0)
return bus_log_create_error(r);
}
r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_close_container(m);
if (r < 0) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);
} }

View File

@ -420,7 +420,7 @@ int dissect_image(
m->verity = root_hash && verity_data; m->verity = root_hash && verity_data;
m->can_verity = !!verity_data; m->can_verity = !!verity_data;
options = mount_options_from_part(mount_options, 0); options = mount_options_from_part(mount_options, PARTITION_ROOT);
if (options) { if (options) {
o = strdup(options); o = strdup(options);
if (!o) if (!o)
@ -716,7 +716,7 @@ int dissect_image(
if (!n) if (!n)
return -ENOMEM; return -ENOMEM;
options = mount_options_from_part(mount_options, nr); options = mount_options_from_part(mount_options, designator);
if (options) { if (options) {
o = strdup(options); o = strdup(options);
if (!o) if (!o)
@ -773,7 +773,7 @@ int dissect_image(
if (!n) if (!n)
return -ENOMEM; return -ENOMEM;
options = mount_options_from_part(mount_options, nr); options = mount_options_from_part(mount_options, PARTITION_XBOOTLDR);
if (options) { if (options) {
o = strdup(options); o = strdup(options);
if (!o) if (!o)
@ -827,7 +827,7 @@ int dissect_image(
if (multiple_generic) if (multiple_generic)
return -ENOTUNIQ; return -ENOTUNIQ;
options = mount_options_from_part(mount_options, generic_nr); options = mount_options_from_part(mount_options, PARTITION_ROOT);
if (options) { if (options) {
o = strdup(options); o = strdup(options);
if (!o) if (!o)
@ -2023,11 +2023,11 @@ MountOptions* mount_options_free_all(MountOptions *options) {
return NULL; return NULL;
} }
const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number) { const char* mount_options_from_part(const MountOptions *options, int designator) {
MountOptions *m; MountOptions *m;
LIST_FOREACH(mount_options, m, (MountOptions *)options) LIST_FOREACH(mount_options, m, (MountOptions *)options)
if (partition_number == m->partition_number && !isempty(m->options)) if (designator == m->partition_designator && !isempty(m->options))
return m->options; return m->options;
return NULL; return NULL;

View File

@ -87,14 +87,14 @@ struct DissectedImage {
}; };
struct MountOptions { struct MountOptions {
unsigned partition_number; int partition_designator;
char *options; char *options;
LIST_FIELDS(MountOptions, mount_options); LIST_FIELDS(MountOptions, mount_options);
}; };
MountOptions* mount_options_free_all(MountOptions *options); MountOptions* mount_options_free_all(MountOptions *options);
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all); DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
const char* mount_options_from_part(const MountOptions *options, unsigned int partition_number); const char* mount_options_from_part(const MountOptions *options, int designator);
int probe_filesystem(const char *node, char **ret_fstype); int probe_filesystem(const char *node, char **ret_fstype);
int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret); int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, const MountOptions *mount_options, DissectImageFlags flags, DissectedImage **ret);

View File

@ -12,6 +12,7 @@
#include "af-list.h" #include "af-list.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "env-util.h"
#include "errno-list.h" #include "errno-list.h"
#include "macro.h" #include "macro.h"
#include "nsflags.h" #include "nsflags.h"
@ -85,6 +86,8 @@ const uint32_t seccomp_local_archs[] = {
SCMP_ARCH_PPC64LE, /* native */ SCMP_ARCH_PPC64LE, /* native */
#elif defined(__powerpc__) #elif defined(__powerpc__)
SCMP_ARCH_PPC, SCMP_ARCH_PPC,
#elif defined(__riscv) && __riscv_xlen == 64 && defined(SCMP_ARCH_RISCV64)
SCMP_ARCH_RISCV64,
#elif defined(__s390x__) #elif defined(__s390x__)
SCMP_ARCH_S390, SCMP_ARCH_S390,
SCMP_ARCH_S390X, /* native */ SCMP_ARCH_S390X, /* native */
@ -131,6 +134,10 @@ const char* seccomp_arch_to_string(uint32_t c) {
return "ppc64"; return "ppc64";
case SCMP_ARCH_PPC64LE: case SCMP_ARCH_PPC64LE:
return "ppc64-le"; return "ppc64-le";
#ifdef SCMP_ARCH_RISCV64
case SCMP_ARCH_RISCV64:
return "riscv64";
#endif
case SCMP_ARCH_S390: case SCMP_ARCH_S390:
return "s390"; return "s390";
case SCMP_ARCH_S390X: case SCMP_ARCH_S390X:
@ -176,6 +183,10 @@ int seccomp_arch_from_string(const char *n, uint32_t *ret) {
*ret = SCMP_ARCH_PPC64; *ret = SCMP_ARCH_PPC64;
else if (streq(n, "ppc64-le")) else if (streq(n, "ppc64-le"))
*ret = SCMP_ARCH_PPC64LE; *ret = SCMP_ARCH_PPC64LE;
#ifdef SCMP_ARCH_RISCV64
else if (streq(n, "riscv64"))
*ret = SCMP_ARCH_RISCV64;
#endif
else if (streq(n, "s390")) else if (streq(n, "s390"))
*ret = SCMP_ARCH_S390; *ret = SCMP_ARCH_S390;
else if (streq(n, "s390x")) else if (streq(n, "s390x"))
@ -224,6 +235,14 @@ int seccomp_init_for_arch(scmp_filter_ctx *ret, uint32_t arch, uint32_t default_
if (r < 0) if (r < 0)
return r; return r;
#if SCMP_VER_MAJOR >= 3 || (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 4)
if (getenv_bool("SYSTEMD_LOG_SECCOMP") > 0) {
r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_LOG, 1);
if (r < 0)
log_debug_errno(r, "Failed to enable seccomp event logging: %m");
}
#endif
*ret = TAKE_PTR(seccomp); *ret = TAKE_PTR(seccomp);
return 0; return 0;
} }
@ -1248,7 +1267,13 @@ int seccomp_protect_sysctl(void) {
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
if (IN_SET(arch, SCMP_ARCH_X32, SCMP_ARCH_AARCH64)) if (IN_SET(arch,
SCMP_ARCH_AARCH64,
#ifdef SCMP_ARCH_RISCV64
SCMP_ARCH_RISCV64,
#endif
SCMP_ARCH_X32
))
/* No _sysctl syscall */ /* No _sysctl syscall */
continue; continue;
@ -1332,6 +1357,9 @@ int seccomp_restrict_address_families(Set *address_families, bool allow_list) {
case SCMP_ARCH_MIPS64N32: case SCMP_ARCH_MIPS64N32:
case SCMP_ARCH_MIPSEL64: case SCMP_ARCH_MIPSEL64:
case SCMP_ARCH_MIPS64: case SCMP_ARCH_MIPS64:
#ifdef SCMP_ARCH_RISCV64
case SCMP_ARCH_RISCV64:
#endif
/* These we know we support (i.e. are the ones that do not use socketcall()) */ /* These we know we support (i.e. are the ones that do not use socketcall()) */
supported = true; supported = true;
break; break;
@ -1569,7 +1597,7 @@ static int add_seccomp_syscall_filter(scmp_filter_ctx seccomp,
} }
/* For known architectures, check that syscalls are indeed defined or not. */ /* For known architectures, check that syscalls are indeed defined or not. */
#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
assert_cc(SCMP_SYS(shmget) > 0); assert_cc(SCMP_SYS(shmget) > 0);
assert_cc(SCMP_SYS(shmat) > 0); assert_cc(SCMP_SYS(shmat) > 0);
assert_cc(SCMP_SYS(shmdt) > 0); assert_cc(SCMP_SYS(shmdt) > 0);
@ -1614,13 +1642,16 @@ int seccomp_memory_deny_write_execute(void) {
case SCMP_ARCH_X86_64: case SCMP_ARCH_X86_64:
case SCMP_ARCH_X32: case SCMP_ARCH_X32:
case SCMP_ARCH_AARCH64: case SCMP_ARCH_AARCH64:
filter_syscall = SCMP_SYS(mmap); /* amd64, x32 and arm64 have only mmap */ #ifdef SCMP_ARCH_RISCV64
case SCMP_ARCH_RISCV64:
#endif
filter_syscall = SCMP_SYS(mmap); /* amd64, x32, arm64 and riscv64 have only mmap */
shmat_syscall = SCMP_SYS(shmat); shmat_syscall = SCMP_SYS(shmat);
break; break;
/* Please add more definitions here, if you port systemd to other architectures! */ /* Please add more definitions here, if you port systemd to other architectures! */
#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__s390__) && !defined(__s390x__) #if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__) && !defined(__s390__) && !defined(__s390x__) && !(defined(__riscv) && __riscv_xlen == 64)
#warning "Consider adding the right mmap() syscall definitions here!" #warning "Consider adding the right mmap() syscall definitions here!"
#endif #endif
} }

View File

@ -5411,24 +5411,56 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
return 1; return 1;
} else if (streq(name, "MountImages")) { } else if (streq(name, "MountImages")) {
_cleanup_free_ char *paths = NULL; _cleanup_free_ char *paths = NULL;
const char *source, *dest;
int ignore_enoent;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssb)"); r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssba(ss))");
if (r < 0) if (r < 0)
return bus_log_parse_error(r); return bus_log_parse_error(r);
while ((r = sd_bus_message_read(m, "(ssb)", &source, &dest, &ignore_enoent)) > 0) { for (;;) {
_cleanup_free_ char *str = NULL; _cleanup_free_ char *str = NULL;
const char *source, *destination, *partition, *mount_options;
int ignore_enoent;
if (isempty(source)) r = sd_bus_message_enter_container(m, 'r', "ssba(ss)");
continue; if (r < 0)
return r;
if (asprintf(&str, "%s%s:%s", ignore_enoent ? "-" : "", source, dest) < 0) r = sd_bus_message_read(m, "ssb", &source, &destination, &ignore_enoent);
if (r <= 0)
break;
str = strjoin(ignore_enoent ? "-" : "",
source,
":",
destination);
if (!str)
return log_oom(); return log_oom();
r = sd_bus_message_enter_container(m, 'a', "(ss)");
if (r < 0)
return r;
while ((r = sd_bus_message_read(m, "(ss)", &partition, &mount_options)) > 0) {
_cleanup_free_ char *previous = NULL;
previous = TAKE_PTR(str);
str = strjoin(strempty(previous), previous ? ":" : "", partition, ":", mount_options);
if (!str)
return log_oom();
}
if (r < 0)
return r;
if (!strextend_with_separator(&paths, " ", str, NULL)) if (!strextend_with_separator(&paths, " ", str, NULL))
return log_oom(); return log_oom();
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
} }
if (r < 0) if (r < 0)
return bus_log_parse_error(r); return bus_log_parse_error(r);

View File

@ -73,6 +73,9 @@ static void test_architecture_table(void) {
"ppc\0" "ppc\0"
"ppc64\0" "ppc64\0"
"ppc64-le\0" "ppc64-le\0"
#ifdef SCMP_ARCH_RISCV64
"riscv64\0"
#endif
"s390\0" "s390\0"
"s390x\0") { "s390x\0") {
uint32_t c; uint32_t c;

View File

@ -128,56 +128,69 @@ umount ${image_dir}/mount
systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="1:ro,noatime 2:ro,dev nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid" systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="1:ro,noatime 1:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime" systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="root:ro,noatime root:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime"
mkdir -p mkdir -p ${image_dir}/result
cat > /run/systemd/system/testservice-50a.service <<EOF cat > /run/systemd/system/testservice-50a.service <<EOF
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=mount ExecStart=bash -c "mount > /run/result/a"
BindPaths=${image_dir}/result:/run/result
TemporaryFileSystem=/run
MountAPIVFS=yes MountAPIVFS=yes
RootImage=${image}.raw RootImage=${image}.raw
RootImageOptions=1:ro,noatime,nosuid 2:ro,dev noatime,dev RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
RootImageOptions=nosuid,dev RootImageOptions=nosuid,dev
EOF EOF
systemctl start testservice-50a.service systemctl start testservice-50a.service
journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F "noatime" grep -F "squashfs" ${image_dir}/result/a | grep -q -F "noatime"
journalctl -b -u testservice-50a.service | grep -F "squashfs" | grep -q -F -v "nosuid" grep -F "squashfs" ${image_dir}/result/a | grep -q -F -v "nosuid"
cat > /run/systemd/system/testservice-50b.service <<EOF cat > /run/systemd/system/testservice-50b.service <<EOF
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=mount ExecStart=bash -c "mount > /run/result/b"
BindPaths=${image_dir}/result:/run/result
TemporaryFileSystem=/run
MountAPIVFS=yes MountAPIVFS=yes
RootImage=${image}.gpt RootImage=${image}.gpt
RootImageOptions=1:ro,noatime,nosuid 2:ro,dev nosuid,dev RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
RootImageOptions=2:ro,dev nosuid,dev,%%foo RootImageOptions=home:ro,dev nosuid,dev,%%foo
EOF EOF
systemctl start testservice-50b.service systemctl start testservice-50b.service
journalctl -b -u testservice-50b.service | grep -F "squashfs" | grep -q -F "noatime" grep -F "squashfs" ${image_dir}/result/b | grep -q -F "noatime"
# Check that specifier escape is applied %%foo -> %foo # Check that specifier escape is applied %%foo -> %foo
busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo" busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
# Now do some checks with MountImages, both by itself and in combination with RootImage, and as single FS or GPT image # Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property MountImages="${image}.raw:/run/img2\:3" /usr/bin/cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property MountImages="${image}.raw:/run/img2\:3" /usr/bin/cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.raw:/run/img2\:3:nosuid" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.gpt --property RootHash=${roothash} --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1" systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.gpt --property RootHash=${roothash} --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50.service <<EOF cat >/run/systemd/system/testservice-50c.service <<EOF
[Service] [Service]
MountAPIVFS=yes
TemporaryFileSystem=/run TemporaryFileSystem=/run
RootImage=${image}.raw RootImage=${image}.raw
MountImages=${image}.gpt:/run/img1 MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
MountImages=${image}.raw:/run/img2\:3 MountImages=${image}.raw:/run/img2\:3:nosuid
ExecStart=/usr/bin/cat /run/img1/usr/lib/os-release ExecStart=bash -c "cat /run/img1/usr/lib/os-release > /run/result/c"
ExecStart=/usr/bin/cat /run/img2:3/usr/lib/os-release ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >> /run/result/c"
ExecStart=bash -c "mount >> /run/result/c"
BindPaths=${image_dir}/result:/run/result
Type=oneshot Type=oneshot
EOF EOF
systemctl start testservice-50.service systemctl start testservice-50c.service
journalctl -b -u testservice-50.service | grep -q -F "MARKER=1" grep -q -F "MARKER=1" ${image_dir}/result/c
grep -F "squashfs" ${image_dir}/result/c | grep -q -F "noatime"
grep -F "squashfs" ${image_dir}/result/c | grep -q -F -v "nosuid"
echo OK > /testok echo OK > /testok