Merge pull request #4962 from poettering/root-directory-2
Add new MountAPIVFS= boolean unit file setting + RootImage=
This commit is contained in:
commit
fc6149a6ce
4
TODO
4
TODO
|
@ -122,8 +122,6 @@ Features:
|
||||||
|
|
||||||
* switch to ProtectSystem=strict for all our long-running services where that's possible
|
* switch to ProtectSystem=strict for all our long-running services where that's possible
|
||||||
|
|
||||||
* If RootDirectory= is used, mount /proc, /sys, /dev into it, if not mounted yet
|
|
||||||
|
|
||||||
* Permit masking specific netlink APIs with RestrictAddressFamily=
|
* Permit masking specific netlink APIs with RestrictAddressFamily=
|
||||||
|
|
||||||
* nspawn: start UID allocation loop from hash of container name
|
* nspawn: start UID allocation loop from hash of container name
|
||||||
|
@ -153,8 +151,6 @@ Features:
|
||||||
* Add DataDirectory=, CacheDirectory= and LogDirectory= to match
|
* Add DataDirectory=, CacheDirectory= and LogDirectory= to match
|
||||||
RuntimeDirectory=, and create it as necessary when starting a service, owned by the right user.
|
RuntimeDirectory=, and create it as necessary when starting a service, owned by the right user.
|
||||||
|
|
||||||
* Add RootImage= for mounting a disk image or file as root directory
|
|
||||||
|
|
||||||
* make sure the ratelimit object can deal with USEC_INFINITY as way to turn off things
|
* make sure the ratelimit object can deal with USEC_INFINITY as way to turn off things
|
||||||
|
|
||||||
* journalctl: make sure -f ends when the container indicated by -M terminates
|
* journalctl: make sure -f ends when the container indicated by -M terminates
|
||||||
|
|
|
@ -256,10 +256,14 @@
|
||||||
|
|
||||||
<listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal. This option enables data
|
<listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal. This option enables data
|
||||||
integrity checks using dm-verity, if the used image contains the appropriate integrity data (see above). The
|
integrity checks using dm-verity, if the used image contains the appropriate integrity data (see above). The
|
||||||
specified hash must match the root hash of integrity data, and is usually at least 256bits (and hence 64
|
specified hash must match the root hash of integrity data, and is usually at least 256 bits (and hence 64
|
||||||
hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but a file with
|
formatted hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but
|
||||||
the <filename>.roothash</filename> suffix is found next to the image file, bearing otherwise the same name the
|
the image file carries the <literal>user.verity.roothash</literal> extended file attribute (see <citerefentry
|
||||||
root hash is read from it and automatically used.</para></listitem>
|
project='man-pages'><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>), then the root
|
||||||
|
hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or
|
||||||
|
is not supported by the underlying file system), but a file with the <filename>.roothash</filename> suffix is
|
||||||
|
found next to the image file, bearing otherwise the same name, the root hash is read from it and automatically
|
||||||
|
used, also as formatted hexadecimal characters.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|
|
@ -86,12 +86,10 @@
|
||||||
<para>A few execution parameters result in additional, automatic
|
<para>A few execution parameters result in additional, automatic
|
||||||
dependencies to be added.</para>
|
dependencies to be added.</para>
|
||||||
|
|
||||||
<para>Units with <varname>WorkingDirectory=</varname> or
|
<para>Units with <varname>WorkingDirectory=</varname>, <varname>RootDirectory=</varname> or
|
||||||
<varname>RootDirectory=</varname> set automatically gain
|
<varname>RootImage=</varname> set automatically gain dependencies of type <varname>Requires=</varname> and
|
||||||
dependencies of type <varname>Requires=</varname> and
|
<varname>After=</varname> on all mount units required to access the specified paths. This is equivalent to having
|
||||||
<varname>After=</varname> on all mount units required to access
|
them listed explicitly in <varname>RequiresMountsFor=</varname>.</para>
|
||||||
the specified paths. This is equivalent to having them listed
|
|
||||||
explicitly in <varname>RequiresMountsFor=</varname>.</para>
|
|
||||||
|
|
||||||
<para>Similar, units with <varname>PrivateTmp=</varname> enabled automatically get mount unit dependencies for all
|
<para>Similar, units with <varname>PrivateTmp=</varname> enabled automatically get mount unit dependencies for all
|
||||||
mounts required to access <filename>/tmp</filename> and <filename>/var/tmp</filename>. They will also gain an
|
mounts required to access <filename>/tmp</filename> and <filename>/var/tmp</filename>. They will also gain an
|
||||||
|
@ -117,9 +115,10 @@
|
||||||
<varname>User=</varname> is used. If not set, defaults to the root directory when systemd is running as a
|
<varname>User=</varname> is used. If not set, defaults to the root directory when systemd is running as a
|
||||||
system instance and the respective user's home directory if run as user. If the setting is prefixed with the
|
system instance and the respective user's home directory if run as user. If the setting is prefixed with the
|
||||||
<literal>-</literal> character, a missing working directory is not considered fatal. If
|
<literal>-</literal> character, a missing working directory is not considered fatal. If
|
||||||
<varname>RootDirectory=</varname> is not set, then <varname>WorkingDirectory=</varname> is relative to the root
|
<varname>RootDirectory=</varname>/<varname>RootImage=</varname> is not set, then
|
||||||
of the system running the service manager. Note that setting this parameter might result in additional
|
<varname>WorkingDirectory=</varname> is relative to the root of the system running the service manager. Note
|
||||||
dependencies to be added to the unit (see above).</para></listitem>
|
that setting this parameter might result in additional dependencies to be added to the unit (see
|
||||||
|
above).</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -132,8 +131,33 @@
|
||||||
the <function>chroot()</function> jail. Note that setting this parameter might result in additional
|
the <function>chroot()</function> jail. Note that setting this parameter might result in additional
|
||||||
dependencies to be added to the unit (see above).</para>
|
dependencies to be added to the unit (see above).</para>
|
||||||
|
|
||||||
<para>The <varname>PrivateUsers=</varname> setting is particularly useful in conjunction with
|
<para>The <varname>MountAPIVFS=</varname> and <varname>PrivateUsers=</varname> settings are particularly useful
|
||||||
<varname>RootDirectory=</varname>. For details, see below.</para></listitem>
|
in conjunction with <varname>RootDirectory=</varname>. For details, see below.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>RootImage=</varname></term>
|
||||||
|
<listitem><para>Takes a path to a block device node or regular file as argument. This call is similar to
|
||||||
|
<varname>RootDirectory=</varname> however mounts a file system hierarchy from a block device node or loopack
|
||||||
|
file instead of a directory. The device node or file system image file needs to contain a file system without a
|
||||||
|
partition table, or a file system within an MBR/MS-DOS or GPT partition table with only a single
|
||||||
|
Linux-compatible partition, or a set of file systems within a GPT partition table that follows the <ulink
|
||||||
|
url="http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions
|
||||||
|
Specification</ulink>.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>MountAPIVFS=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a boolean argument. If on, a private mount namespace for the unit's processes is created
|
||||||
|
and the API file systems <filename>/proc</filename>, <filename>/sys</filename>, and <filename>/dev</filename>
|
||||||
|
are mounted inside of it, unless they are already mounted. Note that this option has no effect unless used in
|
||||||
|
conjunction with <varname>RootDirectory=</varname>/<varname>RootImage=</varname> as these three mounts are
|
||||||
|
generally mounted in the host anyway, and unless the root directory is changed, the private mount namespace
|
||||||
|
will be a 1:1 copy of the host's, and include these three mounts. Note that the <filename>/dev</filename> file
|
||||||
|
system of the host is bind mounted if this option is used without <varname>PrivateDevices=</varname>. To run
|
||||||
|
the service with a private, minimal version of <filename>/dev/</filename>, combine this option with
|
||||||
|
<varname>PrivateDevices=</varname>.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -938,7 +962,7 @@
|
||||||
access a process might have to the file system hierarchy. Each setting takes a space-separated list of paths
|
access a process might have to the file system hierarchy. Each setting takes a space-separated list of paths
|
||||||
relative to the host's root directory (i.e. the system running the service manager). Note that if paths
|
relative to the host's root directory (i.e. the system running the service manager). Note that if paths
|
||||||
contain symlinks, they are resolved relative to the root directory set with
|
contain symlinks, they are resolved relative to the root directory set with
|
||||||
<varname>RootDirectory=</varname>.</para>
|
<varname>RootDirectory=</varname>/<varname>RootImage=</varname>.</para>
|
||||||
|
|
||||||
<para>Paths listed in <varname>ReadWritePaths=</varname> are accessible from within the namespace with the same
|
<para>Paths listed in <varname>ReadWritePaths=</varname> are accessible from within the namespace with the same
|
||||||
access modes as from outside of it. Paths listed in <varname>ReadOnlyPaths=</varname> are accessible for
|
access modes as from outside of it. Paths listed in <varname>ReadOnlyPaths=</varname> are accessible for
|
||||||
|
@ -957,9 +981,10 @@
|
||||||
<para>Paths in <varname>ReadWritePaths=</varname>, <varname>ReadOnlyPaths=</varname> and
|
<para>Paths in <varname>ReadWritePaths=</varname>, <varname>ReadOnlyPaths=</varname> and
|
||||||
<varname>InaccessiblePaths=</varname> may be prefixed with <literal>-</literal>, in which case they will be
|
<varname>InaccessiblePaths=</varname> may be prefixed with <literal>-</literal>, in which case they will be
|
||||||
ignored when they do not exist. If prefixed with <literal>+</literal> the paths are taken relative to the root
|
ignored when they do not exist. If prefixed with <literal>+</literal> the paths are taken relative to the root
|
||||||
directory of the unit, as configured with <varname>RootDirectory=</varname>, instead of relative to the root
|
directory of the unit, as configured with <varname>RootDirectory=</varname>/<varname>RootImage=</varname>,
|
||||||
directory of the host (see above). When combining <literal>-</literal> and <literal>+</literal> on the same
|
instead of relative to the root directory of the host (see above). When combining <literal>-</literal> and
|
||||||
path make sure to specify <literal>-</literal> first, and <literal>+</literal> second.</para>
|
<literal>+</literal> on the same path make sure to specify <literal>-</literal> first, and <literal>+</literal>
|
||||||
|
second.</para>
|
||||||
|
|
||||||
<para>Note that using this setting will disconnect propagation of mounts from the service to the host
|
<para>Note that using this setting will disconnect propagation of mounts from the service to the host
|
||||||
(propagation in the opposite direction continues to work). This means that this setting may not be used for
|
(propagation in the opposite direction continues to work). This means that this setting may not be used for
|
||||||
|
@ -990,9 +1015,9 @@
|
||||||
that in this case both read-only and regular bind mounts are reset, regardless which of the two settings is
|
that in this case both read-only and regular bind mounts are reset, regardless which of the two settings is
|
||||||
used.</para>
|
used.</para>
|
||||||
|
|
||||||
<para>This option is particularly useful when <varname>RootDirectory=</varname> is used. In this case the
|
<para>This option is particularly useful when <varname>RootDirectory=</varname>/<varname>RootImage=</varname>
|
||||||
source path refers to a path on the host file system, while the destination path refers to a path below the
|
is used. In this case the source path refers to a path on the host file system, while the destination path
|
||||||
root directory of the unit.</para></listitem>
|
refers to a path below the root directory of the unit.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -1080,10 +1105,10 @@
|
||||||
such as <varname>CapabilityBoundingSet=</varname> will affect only the latter, and there's no way to acquire
|
such as <varname>CapabilityBoundingSet=</varname> will affect only the latter, and there's no way to acquire
|
||||||
additional capabilities in the host's user namespace. Defaults to off.</para>
|
additional capabilities in the host's user namespace. Defaults to off.</para>
|
||||||
|
|
||||||
<para>This setting is particularly useful in conjunction with <varname>RootDirectory=</varname>, as the need to
|
<para>This setting is particularly useful in conjunction with
|
||||||
synchronize the user and group databases in the root directory and on the host is reduced, as the only users
|
<varname>RootDirectory=</varname>/<varname>RootImage=</varname>, as the need to synchronize the user and group
|
||||||
and groups who need to be matched are <literal>root</literal>, <literal>nobody</literal> and the unit's own
|
databases in the root directory and on the host is reduced, as the only users and groups who need to be matched
|
||||||
user and group.</para></listitem>
|
are <literal>root</literal>, <literal>nobody</literal> and the unit's own user and group.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|
|
@ -758,6 +758,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||||
SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
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("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("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),
|
||||||
SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
@ -828,6 +829,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||||
SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_VTABLE_END
|
SD_BUS_VTABLE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1047,7 +1049,7 @@ int bus_exec_context_set_transient_property(
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) {
|
} else if (STR_IN_SET(name, "TTYPath", "RootDirectory", "RootImage")) {
|
||||||
const char *s;
|
const char *s;
|
||||||
|
|
||||||
r = sd_bus_message_read(message, "s", &s);
|
r = sd_bus_message_read(message, "s", &s);
|
||||||
|
@ -1060,6 +1062,8 @@ int bus_exec_context_set_transient_property(
|
||||||
if (mode != UNIT_CHECK) {
|
if (mode != UNIT_CHECK) {
|
||||||
if (streq(name, "TTYPath"))
|
if (streq(name, "TTYPath"))
|
||||||
r = free_and_strdup(&c->tty_path, s);
|
r = free_and_strdup(&c->tty_path, s);
|
||||||
|
else if (streq(name, "RootImage"))
|
||||||
|
r = free_and_strdup(&c->root_image, s);
|
||||||
else {
|
else {
|
||||||
assert(streq(name, "RootDirectory"));
|
assert(streq(name, "RootDirectory"));
|
||||||
r = free_and_strdup(&c->root_directory, s);
|
r = free_and_strdup(&c->root_directory, s);
|
||||||
|
@ -1207,7 +1211,7 @@ int bus_exec_context_set_transient_property(
|
||||||
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
|
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
|
||||||
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
|
"NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
|
||||||
"RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
|
"RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
|
||||||
"ProtectKernelModules", "ProtectControlGroups")) {
|
"ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) {
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
r = sd_bus_message_read(message, "b", &b);
|
r = sd_bus_message_read(message, "b", &b);
|
||||||
|
@ -1247,6 +1251,8 @@ int bus_exec_context_set_transient_property(
|
||||||
c->protect_kernel_modules = b;
|
c->protect_kernel_modules = b;
|
||||||
else if (streq(name, "ProtectControlGroups"))
|
else if (streq(name, "ProtectControlGroups"))
|
||||||
c->protect_control_groups = b;
|
c->protect_control_groups = b;
|
||||||
|
else if (streq(name, "MountAPIVFS"))
|
||||||
|
c->mount_apivfs = b;
|
||||||
|
|
||||||
unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
|
unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
|
||||||
}
|
}
|
||||||
|
@ -1495,12 +1501,15 @@ int bus_exec_context_set_transient_property(
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
STRV_FOREACH(p, l) {
|
STRV_FOREACH(p, l) {
|
||||||
int offset;
|
const char *i = *p;
|
||||||
if (!utf8_is_valid(*p))
|
size_t offset;
|
||||||
|
|
||||||
|
if (!utf8_is_valid(i))
|
||||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
|
||||||
|
|
||||||
offset = **p == '-';
|
offset = i[0] == '-';
|
||||||
if (!path_is_absolute(*p + offset))
|
offset += i[offset] == '+';
|
||||||
|
if (!path_is_absolute(i + offset))
|
||||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1519,7 +1528,6 @@ int bus_exec_context_set_transient_property(
|
||||||
unit_write_drop_in_private_format(u, mode, name, "%s=", name);
|
unit_write_drop_in_private_format(u, mode, name, "%s=", name);
|
||||||
} else {
|
} else {
|
||||||
r = strv_extend_strv(dirs, l, true);
|
r = strv_extend_strv(dirs, l, true);
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
|
@ -1640,6 +1640,9 @@ static bool exec_needs_mount_namespace(
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(params);
|
assert(params);
|
||||||
|
|
||||||
|
if (context->root_image)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!strv_isempty(context->read_write_paths) ||
|
if (!strv_isempty(context->read_write_paths) ||
|
||||||
!strv_isempty(context->read_only_paths) ||
|
!strv_isempty(context->read_only_paths) ||
|
||||||
!strv_isempty(context->inaccessible_paths))
|
!strv_isempty(context->inaccessible_paths))
|
||||||
|
@ -1662,6 +1665,9 @@ static bool exec_needs_mount_namespace(
|
||||||
context->protect_control_groups)
|
context->protect_control_groups)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (context->mount_apivfs)
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1935,13 +1941,14 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
|
||||||
int r;
|
int r;
|
||||||
_cleanup_strv_free_ char **rw = NULL;
|
_cleanup_strv_free_ char **rw = NULL;
|
||||||
char *tmp = NULL, *var = NULL;
|
char *tmp = NULL, *var = NULL;
|
||||||
const char *root_dir = NULL;
|
const char *root_dir = NULL, *root_image = NULL;
|
||||||
NameSpaceInfo ns_info = {
|
NameSpaceInfo ns_info = {
|
||||||
.ignore_protect_paths = false,
|
.ignore_protect_paths = false,
|
||||||
.private_dev = context->private_devices,
|
.private_dev = context->private_devices,
|
||||||
.protect_control_groups = context->protect_control_groups,
|
.protect_control_groups = context->protect_control_groups,
|
||||||
.protect_kernel_tunables = context->protect_kernel_tunables,
|
.protect_kernel_tunables = context->protect_kernel_tunables,
|
||||||
.protect_kernel_modules = context->protect_kernel_modules,
|
.protect_kernel_modules = context->protect_kernel_modules,
|
||||||
|
.mount_apivfs = context->mount_apivfs,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
|
@ -1961,8 +1968,12 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (params->flags & EXEC_APPLY_CHROOT)
|
if (params->flags & EXEC_APPLY_CHROOT) {
|
||||||
|
root_image = context->root_image;
|
||||||
|
|
||||||
|
if (!root_image)
|
||||||
root_dir = context->root_directory;
|
root_dir = context->root_directory;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If DynamicUser=no and RootDirectory= is set then lets pass a relaxed
|
* If DynamicUser=no and RootDirectory= is set then lets pass a relaxed
|
||||||
|
@ -1972,7 +1983,8 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
|
||||||
if (!context->dynamic_user && root_dir)
|
if (!context->dynamic_user && root_dir)
|
||||||
ns_info.ignore_protect_paths = true;
|
ns_info.ignore_protect_paths = true;
|
||||||
|
|
||||||
r = setup_namespace(root_dir, &ns_info, rw,
|
r = setup_namespace(root_dir, root_image,
|
||||||
|
&ns_info, rw,
|
||||||
context->read_only_paths,
|
context->read_only_paths,
|
||||||
context->inaccessible_paths,
|
context->inaccessible_paths,
|
||||||
context->bind_mounts,
|
context->bind_mounts,
|
||||||
|
@ -1981,7 +1993,8 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
|
||||||
var,
|
var,
|
||||||
context->protect_home,
|
context->protect_home,
|
||||||
context->protect_system,
|
context->protect_system,
|
||||||
context->mount_flags);
|
context->mount_flags,
|
||||||
|
DISSECT_IMAGE_DISCARD_ON_LOOP);
|
||||||
|
|
||||||
/* If we couldn't set up the namespace this is probably due to a
|
/* If we couldn't set up the namespace this is probably due to a
|
||||||
* missing capability. In this case, silently proceeed. */
|
* missing capability. In this case, silently proceeed. */
|
||||||
|
@ -1995,10 +2008,12 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context,
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int apply_working_directory(const ExecContext *context,
|
static int apply_working_directory(
|
||||||
|
const ExecContext *context,
|
||||||
const ExecParameters *params,
|
const ExecParameters *params,
|
||||||
const char *home,
|
const char *home,
|
||||||
const bool needs_mount_ns) {
|
const bool needs_mount_ns) {
|
||||||
|
|
||||||
const char *d;
|
const char *d;
|
||||||
const char *wd;
|
const char *wd;
|
||||||
|
|
||||||
|
@ -2979,6 +2994,7 @@ void exec_context_done(ExecContext *c) {
|
||||||
|
|
||||||
c->working_directory = mfree(c->working_directory);
|
c->working_directory = mfree(c->working_directory);
|
||||||
c->root_directory = mfree(c->root_directory);
|
c->root_directory = mfree(c->root_directory);
|
||||||
|
c->root_image = mfree(c->root_image);
|
||||||
c->tty_path = mfree(c->tty_path);
|
c->tty_path = mfree(c->tty_path);
|
||||||
c->syslog_identifier = mfree(c->syslog_identifier);
|
c->syslog_identifier = mfree(c->syslog_identifier);
|
||||||
c->user = mfree(c->user);
|
c->user = mfree(c->user);
|
||||||
|
@ -3294,6 +3310,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||||
"%sPrivateUsers: %s\n"
|
"%sPrivateUsers: %s\n"
|
||||||
"%sProtectHome: %s\n"
|
"%sProtectHome: %s\n"
|
||||||
"%sProtectSystem: %s\n"
|
"%sProtectSystem: %s\n"
|
||||||
|
"%sMountAPIVFS: %s\n"
|
||||||
"%sIgnoreSIGPIPE: %s\n"
|
"%sIgnoreSIGPIPE: %s\n"
|
||||||
"%sMemoryDenyWriteExecute: %s\n"
|
"%sMemoryDenyWriteExecute: %s\n"
|
||||||
"%sRestrictRealtime: %s\n",
|
"%sRestrictRealtime: %s\n",
|
||||||
|
@ -3310,10 +3327,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||||
prefix, yes_no(c->private_users),
|
prefix, yes_no(c->private_users),
|
||||||
prefix, protect_home_to_string(c->protect_home),
|
prefix, protect_home_to_string(c->protect_home),
|
||||||
prefix, protect_system_to_string(c->protect_system),
|
prefix, protect_system_to_string(c->protect_system),
|
||||||
|
prefix, yes_no(c->mount_apivfs),
|
||||||
prefix, yes_no(c->ignore_sigpipe),
|
prefix, yes_no(c->ignore_sigpipe),
|
||||||
prefix, yes_no(c->memory_deny_write_execute),
|
prefix, yes_no(c->memory_deny_write_execute),
|
||||||
prefix, yes_no(c->restrict_realtime));
|
prefix, yes_no(c->restrict_realtime));
|
||||||
|
|
||||||
|
if (c->root_image)
|
||||||
|
fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
|
||||||
|
|
||||||
STRV_FOREACH(e, c->environment)
|
STRV_FOREACH(e, c->environment)
|
||||||
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
|
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ struct ExecContext {
|
||||||
char **pass_environment;
|
char **pass_environment;
|
||||||
|
|
||||||
struct rlimit *rlimit[_RLIMIT_MAX];
|
struct rlimit *rlimit[_RLIMIT_MAX];
|
||||||
char *working_directory, *root_directory;
|
char *working_directory, *root_directory, *root_image;
|
||||||
bool working_directory_missing_ok;
|
bool working_directory_missing_ok;
|
||||||
bool working_directory_home;
|
bool working_directory_home;
|
||||||
|
|
||||||
|
@ -183,6 +183,7 @@ struct ExecContext {
|
||||||
bool protect_kernel_tunables;
|
bool protect_kernel_tunables;
|
||||||
bool protect_kernel_modules;
|
bool protect_kernel_modules;
|
||||||
bool protect_control_groups;
|
bool protect_control_groups;
|
||||||
|
bool mount_apivfs;
|
||||||
|
|
||||||
bool no_new_privileges;
|
bool no_new_privileges;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ m4_dnl Define the context options only once
|
||||||
m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
|
m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
|
||||||
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
|
`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
|
||||||
$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory)
|
$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory)
|
||||||
|
$1.RootImage, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_image)
|
||||||
$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user)
|
$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user)
|
||||||
$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group)
|
$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group)
|
||||||
$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups)
|
$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups)
|
||||||
|
@ -101,6 +102,7 @@ $1.PrivateUsers, config_parse_bool, 0,
|
||||||
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context)
|
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context)
|
||||||
$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context)
|
$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context)
|
||||||
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context)
|
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context)
|
||||||
|
$1.MountAPIVFS, config_parse_bool, 0, offsetof($1, exec_context.mount_apivfs)
|
||||||
$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality)
|
$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality)
|
||||||
$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode)
|
$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode)
|
||||||
$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory)
|
$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory)
|
||||||
|
|
|
@ -3839,7 +3839,8 @@ int config_parse_namespace_path_strv(
|
||||||
cur = rvalue;
|
cur = rvalue;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
_cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
|
_cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
|
||||||
bool ignore_enoent;
|
const char *w;
|
||||||
|
bool ignore_enoent = false, shall_prefix = false;
|
||||||
|
|
||||||
r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
|
r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
@ -3856,9 +3857,17 @@ int config_parse_namespace_path_strv(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ignore_enoent = word[0] == '-';
|
w = word;
|
||||||
|
if (startswith(w, "-")) {
|
||||||
|
ignore_enoent = true;
|
||||||
|
w++;
|
||||||
|
}
|
||||||
|
if (startswith(w, "+")) {
|
||||||
|
shall_prefix = true;
|
||||||
|
w++;
|
||||||
|
}
|
||||||
|
|
||||||
r = unit_full_printf(u, word + ignore_enoent, &resolved);
|
r = unit_full_printf(u, w, &resolved);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
|
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
|
||||||
continue;
|
continue;
|
||||||
|
@ -3871,7 +3880,9 @@ int config_parse_namespace_path_strv(
|
||||||
|
|
||||||
path_kill_slashes(resolved);
|
path_kill_slashes(resolved);
|
||||||
|
|
||||||
joined = strjoin(ignore_enoent ? "-" : "", resolved);
|
joined = strjoin(ignore_enoent ? "-" : "",
|
||||||
|
shall_prefix ? "+" : "",
|
||||||
|
resolved);
|
||||||
|
|
||||||
r = strv_push(sv, joined);
|
r = strv_push(sv, joined);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "dev-setup.h"
|
#include "dev-setup.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "loop-util.h"
|
||||||
#include "loopback-setup.h"
|
#include "loopback-setup.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
|
@ -52,10 +53,13 @@ typedef enum MountMode {
|
||||||
INACCESSIBLE,
|
INACCESSIBLE,
|
||||||
BIND_MOUNT,
|
BIND_MOUNT,
|
||||||
BIND_MOUNT_RECURSIVE,
|
BIND_MOUNT_RECURSIVE,
|
||||||
READONLY,
|
|
||||||
PRIVATE_TMP,
|
PRIVATE_TMP,
|
||||||
PRIVATE_VAR_TMP,
|
PRIVATE_VAR_TMP,
|
||||||
PRIVATE_DEV,
|
PRIVATE_DEV,
|
||||||
|
BIND_DEV,
|
||||||
|
SYSFS,
|
||||||
|
PROCFS,
|
||||||
|
READONLY,
|
||||||
READWRITE,
|
READWRITE,
|
||||||
} MountMode;
|
} MountMode;
|
||||||
|
|
||||||
|
@ -70,13 +74,13 @@ typedef struct MountEntry {
|
||||||
char *source_malloc;
|
char *source_malloc;
|
||||||
} 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
|
||||||
* The following Protect tables are to protect paths and mark some of them
|
* something there already. These mounts are hence overriden by any other explicitly configured mounts. */
|
||||||
* READONLY, in case a path is covered by an option from another table, then
|
static const MountEntry apivfs_table[] = {
|
||||||
* it is marked READWRITE in the current one, and the more restrictive mode is
|
{ "/proc", PROCFS, false },
|
||||||
* applied from that other table. This way all options can be combined in a
|
{ "/dev", BIND_DEV, false },
|
||||||
* safe and comprehensible way for users.
|
{ "/sys", SYSFS, false },
|
||||||
*/
|
};
|
||||||
|
|
||||||
/* ProtectKernelTunables= option and the related filesystem APIs */
|
/* ProtectKernelTunables= option and the related filesystem APIs */
|
||||||
static const MountEntry protect_kernel_tunables_table[] = {
|
static const MountEntry protect_kernel_tunables_table[] = {
|
||||||
|
@ -176,6 +180,13 @@ static const char *mount_entry_source(const MountEntry *p) {
|
||||||
return p->source_malloc ?: p->source_const;
|
return p->source_malloc ?: p->source_const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mount_entry_done(MountEntry *p) {
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
p->path_malloc = mfree(p->path_malloc);
|
||||||
|
p->source_malloc = mfree(p->source_malloc);
|
||||||
|
}
|
||||||
|
|
||||||
static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) {
|
static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) {
|
||||||
char **i;
|
char **i;
|
||||||
|
|
||||||
|
@ -351,7 +362,7 @@ static void drop_duplicates(MountEntry *m, unsigned *n) {
|
||||||
if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) {
|
if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) {
|
||||||
log_debug("%s is duplicate.", mount_entry_path(f));
|
log_debug("%s is duplicate.", mount_entry_path(f));
|
||||||
previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */
|
previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */
|
||||||
f->path_malloc = mfree(f->path_malloc);
|
mount_entry_done(f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +390,7 @@ static void drop_inaccessible(MountEntry *m, unsigned *n) {
|
||||||
* it, as inaccessible paths really should drop the entire subtree. */
|
* it, as inaccessible paths really should drop the entire subtree. */
|
||||||
if (clear && path_startswith(mount_entry_path(f), clear)) {
|
if (clear && path_startswith(mount_entry_path(f), clear)) {
|
||||||
log_debug("%s is masked by %s.", mount_entry_path(f), clear);
|
log_debug("%s is masked by %s.", mount_entry_path(f), clear);
|
||||||
f->path_malloc = mfree(f->path_malloc);
|
mount_entry_done(f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,7 +430,7 @@ static void drop_nop(MountEntry *m, unsigned *n) {
|
||||||
/* We found it, let's see if it's the same mode, if so, we can drop this entry */
|
/* We found it, let's see if it's the same mode, if so, we can drop this entry */
|
||||||
if (found && p->mode == f->mode) {
|
if (found && p->mode == f->mode) {
|
||||||
log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p));
|
log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p));
|
||||||
f->path_malloc = mfree(f->path_malloc);
|
mount_entry_done(f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,7 +458,7 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne
|
||||||
|
|
||||||
if (!path_startswith(mount_entry_path(f), root_directory)) {
|
if (!path_startswith(mount_entry_path(f), root_directory)) {
|
||||||
log_debug("%s is outside of root directory.", mount_entry_path(f));
|
log_debug("%s is outside of root directory.", mount_entry_path(f));
|
||||||
f->path_malloc = mfree(f->path_malloc);
|
mount_entry_done(f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,7 +469,7 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne
|
||||||
*n = t - m;
|
*n = t - m;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mount_dev(MountEntry *m) {
|
static int mount_private_dev(MountEntry *m) {
|
||||||
static const char devnodes[] =
|
static const char devnodes[] =
|
||||||
"/dev/null\0"
|
"/dev/null\0"
|
||||||
"/dev/zero\0"
|
"/dev/zero\0"
|
||||||
|
@ -597,6 +608,62 @@ fail:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mount_bind_dev(MountEntry *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
/* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the service's
|
||||||
|
* /dev. This is only used when RootDirectory= is set. */
|
||||||
|
|
||||||
|
r = path_is_mount_point(mount_entry_path(m), NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Unable to determine whether /dev is already mounted: %m");
|
||||||
|
if (r > 0) /* make this a NOP if /dev is already a mount point */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (mount("/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||||
|
return log_debug_errno(errno, "Failed to bind mount %s: %m", mount_entry_path(m));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mount_sysfs(MountEntry *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = path_is_mount_point(mount_entry_path(m), NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Unable to determine whether /sys is already mounted: %m");
|
||||||
|
if (r > 0) /* make this a NOP if /sys is already a mount point */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Bind mount the host's version so that we get all child mounts of it, too. */
|
||||||
|
if (mount("/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||||
|
return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mount_procfs(MountEntry *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = path_is_mount_point(mount_entry_path(m), NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m");
|
||||||
|
if (r > 0) /* make this a NOP if /proc is already a mount point */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Mount a new instance, so that we get the one that matches our user namespace, if we are running in one */
|
||||||
|
if (mount("proc", mount_entry_path(m), "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0)
|
||||||
|
return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int mount_entry_chase(
|
static int mount_entry_chase(
|
||||||
const char *root_directory,
|
const char *root_directory,
|
||||||
MountEntry *m,
|
MountEntry *m,
|
||||||
|
@ -684,6 +751,7 @@ static int apply_mount(
|
||||||
|
|
||||||
case BIND_MOUNT_RECURSIVE:
|
case BIND_MOUNT_RECURSIVE:
|
||||||
/* Also chase the source mount */
|
/* Also chase the source mount */
|
||||||
|
|
||||||
r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc);
|
r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -700,7 +768,16 @@ static int apply_mount(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PRIVATE_DEV:
|
case PRIVATE_DEV:
|
||||||
return mount_dev(m);
|
return mount_private_dev(m);
|
||||||
|
|
||||||
|
case BIND_DEV:
|
||||||
|
return mount_bind_dev(m);
|
||||||
|
|
||||||
|
case SYSFS:
|
||||||
|
return mount_sysfs(m);
|
||||||
|
|
||||||
|
case PROCFS:
|
||||||
|
return mount_procfs(m);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert_not_reached("Unknown mode");
|
assert_not_reached("Unknown mode");
|
||||||
|
@ -722,7 +799,7 @@ static int make_read_only(MountEntry *m, char **blacklist) {
|
||||||
|
|
||||||
if (mount_entry_read_only(m))
|
if (mount_entry_read_only(m))
|
||||||
r = bind_remount_recursive(mount_entry_path(m), true, blacklist);
|
r = bind_remount_recursive(mount_entry_path(m), true, blacklist);
|
||||||
else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/
|
else if (m->mode == PRIVATE_DEV) { /* Superblock can be readonly but the submounts can't*/
|
||||||
if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
|
if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
|
||||||
r = -errno;
|
r = -errno;
|
||||||
} else
|
} else
|
||||||
|
@ -738,6 +815,17 @@ static int make_read_only(MountEntry *m, char **blacklist) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool namespace_info_mount_apivfs(const NameSpaceInfo *ns_info) {
|
||||||
|
assert(ns_info);
|
||||||
|
|
||||||
|
/* ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=, since to protect the API VFS mounts,
|
||||||
|
* they need to be around in the first place... */
|
||||||
|
|
||||||
|
return ns_info->mount_apivfs ||
|
||||||
|
ns_info->protect_control_groups ||
|
||||||
|
ns_info->protect_kernel_tunables;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned namespace_calculate_mounts(
|
static unsigned namespace_calculate_mounts(
|
||||||
const NameSpaceInfo *ns_info,
|
const NameSpaceInfo *ns_info,
|
||||||
char** read_write_paths,
|
char** read_write_paths,
|
||||||
|
@ -774,11 +862,13 @@ static unsigned namespace_calculate_mounts(
|
||||||
(ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
|
(ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
|
||||||
(ns_info->protect_control_groups ? 1 : 0) +
|
(ns_info->protect_control_groups ? 1 : 0) +
|
||||||
(ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
|
(ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
|
||||||
protect_home_cnt + protect_system_cnt;
|
protect_home_cnt + protect_system_cnt +
|
||||||
|
(namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int setup_namespace(
|
int setup_namespace(
|
||||||
const char* root_directory,
|
const char* root_directory,
|
||||||
|
const char* root_image,
|
||||||
const NameSpaceInfo *ns_info,
|
const NameSpaceInfo *ns_info,
|
||||||
char** read_write_paths,
|
char** read_write_paths,
|
||||||
char** read_only_paths,
|
char** read_only_paths,
|
||||||
|
@ -789,16 +879,57 @@ int setup_namespace(
|
||||||
const char* var_tmp_dir,
|
const char* var_tmp_dir,
|
||||||
ProtectHome protect_home,
|
ProtectHome protect_home,
|
||||||
ProtectSystem protect_system,
|
ProtectSystem protect_system,
|
||||||
unsigned long mount_flags) {
|
unsigned long mount_flags,
|
||||||
|
DissectImageFlags dissect_image_flags) {
|
||||||
|
|
||||||
|
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
|
||||||
|
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
|
||||||
|
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
|
||||||
|
_cleanup_free_ void *root_hash = NULL;
|
||||||
MountEntry *m, *mounts = NULL;
|
MountEntry *m, *mounts = NULL;
|
||||||
|
size_t root_hash_size = 0;
|
||||||
bool make_slave = false;
|
bool make_slave = false;
|
||||||
unsigned n_mounts;
|
unsigned n_mounts;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
|
assert(ns_info);
|
||||||
|
|
||||||
if (mount_flags == 0)
|
if (mount_flags == 0)
|
||||||
mount_flags = MS_SHARED;
|
mount_flags = MS_SHARED;
|
||||||
|
|
||||||
|
if (root_image) {
|
||||||
|
dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
|
||||||
|
|
||||||
|
if (protect_system == PROTECT_SYSTEM_STRICT && strv_isempty(read_write_paths))
|
||||||
|
dissect_image_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||||
|
|
||||||
|
r = loop_device_make_by_path(root_image,
|
||||||
|
dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
|
||||||
|
&loop_device);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = root_hash_load(root_image, &root_hash, &root_hash_size);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!root_directory) {
|
||||||
|
/* Create a mount point for the image, if it's still missing. We use the same mount point for
|
||||||
|
* all images, which is safe, since they all live in their own namespaces after all, and hence
|
||||||
|
* won't see each other. */
|
||||||
|
root_directory = "/run/systemd/unit-root";
|
||||||
|
(void) mkdir(root_directory, 0700);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
n_mounts = namespace_calculate_mounts(
|
n_mounts = namespace_calculate_mounts(
|
||||||
ns_info,
|
ns_info,
|
||||||
read_write_paths,
|
read_write_paths,
|
||||||
|
@ -878,6 +1009,12 @@ int setup_namespace(
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
|
if (namespace_info_mount_apivfs(ns_info)) {
|
||||||
|
r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
assert(mounts + n_mounts == m);
|
assert(mounts + n_mounts == m);
|
||||||
|
|
||||||
/* Prepend the root directory where that's necessary */
|
/* Prepend the root directory where that's necessary */
|
||||||
|
@ -907,7 +1044,19 @@ int setup_namespace(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root_directory) {
|
if (root_image) {
|
||||||
|
r = dissected_image_mount(dissected_image, root_directory, dissect_image_flags);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
r = decrypted_image_relinquish(decrypted_image);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
loop_device_relinquish(loop_device);
|
||||||
|
|
||||||
|
} else if (root_directory) {
|
||||||
|
|
||||||
/* Turn directory into bind mount, if it isn't one yet */
|
/* Turn directory into bind mount, if it isn't one yet */
|
||||||
r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW);
|
r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -964,7 +1113,7 @@ int setup_namespace(
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
for (m = mounts; m < mounts + n_mounts; m++)
|
for (m = mounts; m < mounts + n_mounts; m++)
|
||||||
free(m->path_malloc);
|
mount_entry_done(m);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ typedef struct BindMount BindMount;
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "dissect-image.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
|
|
||||||
typedef enum ProtectHome {
|
typedef enum ProtectHome {
|
||||||
|
@ -50,6 +51,7 @@ struct NameSpaceInfo {
|
||||||
bool protect_control_groups:1;
|
bool protect_control_groups:1;
|
||||||
bool protect_kernel_tunables:1;
|
bool protect_kernel_tunables:1;
|
||||||
bool protect_kernel_modules:1;
|
bool protect_kernel_modules:1;
|
||||||
|
bool mount_apivfs:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BindMount {
|
struct BindMount {
|
||||||
|
@ -62,6 +64,7 @@ struct BindMount {
|
||||||
|
|
||||||
int setup_namespace(
|
int setup_namespace(
|
||||||
const char *root_directory,
|
const char *root_directory,
|
||||||
|
const char *root_image,
|
||||||
const NameSpaceInfo *ns_info,
|
const NameSpaceInfo *ns_info,
|
||||||
char **read_write_paths,
|
char **read_write_paths,
|
||||||
char **read_only_paths,
|
char **read_only_paths,
|
||||||
|
@ -72,7 +75,8 @@ int setup_namespace(
|
||||||
const char *var_tmp_dir,
|
const char *var_tmp_dir,
|
||||||
ProtectHome protect_home,
|
ProtectHome protect_home,
|
||||||
ProtectSystem protect_system,
|
ProtectSystem protect_system,
|
||||||
unsigned long mount_flags);
|
unsigned long mount_flags,
|
||||||
|
DissectImageFlags dissected_image_flags);
|
||||||
|
|
||||||
int setup_tmp_dirs(
|
int setup_tmp_dirs(
|
||||||
const char *id,
|
const char *id,
|
||||||
|
|
|
@ -862,6 +862,12 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c->root_image) {
|
||||||
|
r = unit_require_mounts_for(u, c->root_image);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
if (!MANAGER_IS_SYSTEM(u->manager))
|
if (!MANAGER_IS_SYSTEM(u->manager))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,14 @@ int main(int argc, char *argv[]) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!arg_root_hash) {
|
||||||
|
r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m);
|
r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m);
|
||||||
if (r == -ENOPKG) {
|
if (r == -ENOPKG) {
|
||||||
log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
|
log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image);
|
||||||
|
|
|
@ -3507,53 +3507,6 @@ static int run(int master,
|
||||||
return 1; /* loop again */
|
return 1; /* loop again */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_root_hash(const char *image) {
|
|
||||||
_cleanup_free_ char *text = NULL, *fn = NULL;
|
|
||||||
char *n, *e;
|
|
||||||
void *k;
|
|
||||||
size_t l;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert_se(image);
|
|
||||||
|
|
||||||
/* Try to load the root hash from a file next to the image file if it exists. */
|
|
||||||
|
|
||||||
if (arg_root_hash)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fn = new(char, strlen(image) + strlen(".roothash") + 1);
|
|
||||||
if (!fn)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
n = stpcpy(fn, image);
|
|
||||||
e = endswith(fn, ".raw");
|
|
||||||
if (e)
|
|
||||||
n = e;
|
|
||||||
|
|
||||||
strcpy(n, ".roothash");
|
|
||||||
|
|
||||||
r = read_one_line_file(fn, &text);
|
|
||||||
if (r == -ENOENT)
|
|
||||||
return 0;
|
|
||||||
if (r < 0) {
|
|
||||||
log_warning_errno(r, "Failed to read %s, ignoring: %m", fn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = unhexmem(text, strlen(text), &k, &l);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Invalid root hash: %s", text);
|
|
||||||
if (l < sizeof(sd_id128_t)) {
|
|
||||||
free(k);
|
|
||||||
return log_error_errno(r, "Root hash too short: %s", text);
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_root_hash = k;
|
|
||||||
arg_root_hash_size = l;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
_cleanup_free_ char *console = NULL;
|
_cleanup_free_ char *console = NULL;
|
||||||
|
@ -3769,10 +3722,14 @@ int main(int argc, char *argv[]) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = load_root_hash(arg_image);
|
if (!arg_root_hash) {
|
||||||
if (r < 0)
|
r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to load root hash file for %s: %m", arg_image);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!mkdtemp(tmprootdir)) {
|
if (!mkdtemp(tmprootdir)) {
|
||||||
r = log_error_errno(errno, "Failed to create temporary directory: %m");
|
r = log_error_errno(errno, "Failed to create temporary directory: %m");
|
||||||
|
|
|
@ -208,7 +208,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||||
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
|
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges",
|
||||||
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
|
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute",
|
||||||
"RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
|
"RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
|
||||||
"ProtectKernelModules", "ProtectControlGroups")) {
|
"ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) {
|
||||||
|
|
||||||
r = parse_boolean(eq);
|
r = parse_boolean(eq);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -266,7 +266,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||||
"StandardInput", "StandardOutput", "StandardError",
|
"StandardInput", "StandardOutput", "StandardError",
|
||||||
"Description", "Slice", "Type", "WorkingDirectory",
|
"Description", "Slice", "Type", "WorkingDirectory",
|
||||||
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
|
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
|
||||||
"ProtectHome", "SELinuxContext", "Restart"))
|
"ProtectHome", "SELinuxContext", "Restart", "RootImage"))
|
||||||
r = sd_bus_message_append(m, "v", "s", eq);
|
r = sd_bus_message_append(m, "v", "s", eq);
|
||||||
|
|
||||||
else if (streq(field, "SyslogLevel")) {
|
else if (streq(field, "SyslogLevel")) {
|
||||||
|
@ -484,7 +484,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||||
|
|
||||||
for (p = eq;;) {
|
for (p = eq;;) {
|
||||||
_cleanup_free_ char *word = NULL;
|
_cleanup_free_ char *word = NULL;
|
||||||
int offset;
|
size_t offset;
|
||||||
|
|
||||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
|
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
@ -500,6 +500,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = word[0] == '-';
|
offset = word[0] == '-';
|
||||||
|
offset += word[offset] == '+';
|
||||||
|
|
||||||
if (!path_is_absolute(word + offset)) {
|
if (!path_is_absolute(word + offset)) {
|
||||||
log_error("Failed to parse %s value %s", field, eq);
|
log_error("Failed to parse %s value %s", field, eq);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -28,14 +28,19 @@
|
||||||
#include "blkid-util.h"
|
#include "blkid-util.h"
|
||||||
#include "dissect-image.h"
|
#include "dissect-image.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
|
#include "fileio.h"
|
||||||
|
#include "fs-util.h"
|
||||||
#include "gpt.h"
|
#include "gpt.h"
|
||||||
|
#include "hexdecoct.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "stdio-util.h"
|
#include "stdio-util.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
#include "strv.h"
|
||||||
#include "udev-util.h"
|
#include "udev-util.h"
|
||||||
|
#include "xattr-util.h"
|
||||||
|
|
||||||
static int probe_filesystem(const char *node, char **ret_fstype) {
|
static int probe_filesystem(const char *node, char **ret_fstype) {
|
||||||
#ifdef HAVE_BLKID
|
#ifdef HAVE_BLKID
|
||||||
|
@ -657,7 +662,9 @@ static int mount_partition(
|
||||||
DissectImageFlags flags) {
|
DissectImageFlags flags) {
|
||||||
|
|
||||||
const char *p, *options = NULL, *node, *fstype;
|
const char *p, *options = NULL, *node, *fstype;
|
||||||
|
_cleanup_free_ char *chased = NULL;
|
||||||
bool rw;
|
bool rw;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(where);
|
assert(where);
|
||||||
|
@ -674,9 +681,13 @@ static int mount_partition(
|
||||||
|
|
||||||
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
|
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
|
||||||
|
|
||||||
if (directory)
|
if (directory) {
|
||||||
p = strjoina(where, directory);
|
r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased);
|
||||||
else
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
p = chased;
|
||||||
|
} else
|
||||||
p = where;
|
p = where;
|
||||||
|
|
||||||
/* If requested, turn on discard support. */
|
/* If requested, turn on discard support. */
|
||||||
|
@ -710,24 +721,25 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (m->partitions[PARTITION_ESP].found) {
|
if (m->partitions[PARTITION_ESP].found) {
|
||||||
const char *mp, *x;
|
const char *mp;
|
||||||
|
|
||||||
/* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
|
/* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
|
||||||
|
|
||||||
mp = "/efi";
|
FOREACH_STRING(mp, "/efi", "/boot") {
|
||||||
x = strjoina(where, mp);
|
_cleanup_free_ char *p = NULL;
|
||||||
r = dir_is_empty(x);
|
|
||||||
if (r == -ENOENT) {
|
r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p);
|
||||||
mp = "/boot";
|
if (r < 0)
|
||||||
x = strjoina(where, mp);
|
continue;
|
||||||
r = dir_is_empty(x);
|
|
||||||
}
|
r = dir_is_empty(p);
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
|
r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1111,6 +1123,62 @@ int decrypted_image_relinquish(DecryptedImage *d) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int root_hash_load(const char *image, void **ret, size_t *ret_size) {
|
||||||
|
_cleanup_free_ char *text = NULL;
|
||||||
|
_cleanup_free_ void *k = NULL;
|
||||||
|
size_t l;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(image);
|
||||||
|
assert(ret);
|
||||||
|
assert(ret_size);
|
||||||
|
|
||||||
|
if (is_device_path(image)) {
|
||||||
|
/* If we are asked to load the root hash for a device node, exit early */
|
||||||
|
*ret = NULL;
|
||||||
|
*ret_size = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = getxattr_malloc(image, "user.verity.roothash", &text, true);
|
||||||
|
if (r < 0) {
|
||||||
|
char *fn, *e, *n;
|
||||||
|
|
||||||
|
if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
|
||||||
|
return r;
|
||||||
|
|
||||||
|
fn = newa(char, strlen(image) + strlen(".roothash") + 1);
|
||||||
|
n = stpcpy(fn, image);
|
||||||
|
e = endswith(fn, ".raw");
|
||||||
|
if (e)
|
||||||
|
n = e;
|
||||||
|
|
||||||
|
strcpy(n, ".roothash");
|
||||||
|
|
||||||
|
r = read_one_line_file(fn, &text);
|
||||||
|
if (r == -ENOENT) {
|
||||||
|
*ret = NULL;
|
||||||
|
*ret_size = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = unhexmem(text, strlen(text), &k, &l);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (l < sizeof(sd_id128_t))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*ret = k;
|
||||||
|
*ret_size = l;
|
||||||
|
|
||||||
|
k = NULL;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *const partition_designator_table[] = {
|
static const char *const partition_designator_table[] = {
|
||||||
[PARTITION_ROOT] = "root",
|
[PARTITION_ROOT] = "root",
|
||||||
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
||||||
|
|
|
@ -94,3 +94,5 @@ int decrypted_image_relinquish(DecryptedImage *d);
|
||||||
|
|
||||||
const char* partition_designator_to_string(int i) _const_;
|
const char* partition_designator_to_string(int i) _const_;
|
||||||
int partition_designator_from_string(const char *name) _pure_;
|
int partition_designator_from_string(const char *name) _pure_;
|
||||||
|
|
||||||
|
int root_hash_load(const char *image, void **ret, size_t *ret_size);
|
||||||
|
|
|
@ -77,6 +77,7 @@ int main(int argc, char *argv[]) {
|
||||||
log_info("Not chrooted");
|
log_info("Not chrooted");
|
||||||
|
|
||||||
r = setup_namespace(root_directory,
|
r = setup_namespace(root_directory,
|
||||||
|
NULL,
|
||||||
&ns_info,
|
&ns_info,
|
||||||
(char **) writable,
|
(char **) writable,
|
||||||
(char **) readonly,
|
(char **) readonly,
|
||||||
|
@ -86,6 +87,7 @@ int main(int argc, char *argv[]) {
|
||||||
var_tmp_dir,
|
var_tmp_dir,
|
||||||
PROTECT_HOME_NO,
|
PROTECT_HOME_NO,
|
||||||
PROTECT_SYSTEM_NO,
|
PROTECT_SYSTEM_NO,
|
||||||
|
0,
|
||||||
0);
|
0);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error_errno(r, "Failed to setup namespace: %m");
|
log_error_errno(r, "Failed to setup namespace: %m");
|
||||||
|
|
Loading…
Reference in New Issue