udev: new "image_dissect" builtin that can be used to generate symlinks based on partition designators (#36631)

This replaces #36209 and implements this via a new builtin, rather than
by shelling out to systemd-dissect.

Replaces: #36209 
Replaces: #28894
Fixes: #34319 
Fixes: #33453 
Fixes: #27897
Fixes: #18035
This commit is contained in:
Lennart Poettering
2025-04-03 13:32:11 +02:00
committed by GitHub
36 changed files with 1601 additions and 261 deletions

28
TODO
View File

@@ -126,6 +126,8 @@ Deprecations and removals:
* In v260: remove support for deprecated FactoryReset EFI variable in
systemd-repart, replaced by FactoryResetRequest.
* Consider removing root=gpt-auto, and push people to use root=dissect instead.
Features:
* maybe replace nss-machines with logic in networkd that registers records with
@@ -163,6 +165,8 @@ Features:
the PCR then also reboot.
* cryptsetup: add boolean for disabling use of any password/recovery key slots.
(i.e. that we can operate in a tpm-only mode, and thus protect us from rogue
root disks)
* complete varlink introspection comments:
- io.systemd.BootControl
@@ -176,12 +180,6 @@ Features:
- io.systemd.oom
- io.systemd.sysext
* dissect: instead of searching for root and /usr partitions first, look for
verity signature partitions first instead, then match up what we find with
locally available keys, and then use first that works.
* gpt-auto-root doesn't take image policy into account.
* maybe define a /etc/machine-info field for the ANSI color to associate with a
hostname. Then use it for the shell prompt to highlight the hostname. If no
color is explicitly set, hash a color automatically from the hostname as a
@@ -1177,10 +1175,6 @@ Features:
* consider adding a new partition type, just for /opt/ for usage in system
extensions
* gpt-auto-discovery: also use the pkcs7 signature stuff, and pass signature to
kernel. So far we only did this for the various --image= switches, but not
for the root fs or /usr/.
* dissection policy should enforce that unlocking can only take place by
certain means, i.e. only via pw, only via tpm2, or only via fido, or a
combination thereof.
@@ -1345,9 +1339,6 @@ Features:
* chase(): take inspiration from path_extract_filename() and return
O_DIRECTORY if input path contains trailing slash.
* chase(): refuse resolution if trailing slash is specified on input,
but final node is not a directory
* document in boot loader spec that symlinks in XBOOTLDR/ESP are not OK even if
non-VFAT fs is used.
@@ -1667,12 +1658,6 @@ Features:
data in the image, make sure the image filename actually matches this, so
that images cannot be misused.
* New udev block device symlink names:
/dev/disk/by-parttypelabel/<pttype>-<ptlabel>. Use case: if pt label is used
as partition image version string, this is a safe way to reference a specific
version of a specific partition type, in particular where related partitions
are processed (e.g. verity + rootfs both named "LennartOS_0.7").
* sysupdate:
- add fuzzing to the pattern parser
- support casync as download mechanism
@@ -1689,11 +1674,6 @@ Features:
* systemd-sysext: optionally, run it in initrd already, before transitioning
into host, to open up possibility for services shipped like that.
* introduce /dev/disk/root/* symlinks that allow referencing partitions on the
disk the rootfs is on in a reasonably secure way. (or maybe: add
/dev/gpt-auto-{home,srv,boot,…} similar in style to /dev/gpt-auto-root as we
already have it.
* whenever we receive fds via SCM_RIGHTS make sure none got dropped due to the
reception limit the kernel silently enforces.

View File

@@ -529,6 +529,14 @@ disk images with `--image=` or similar:
images. Defaults to true, i.e. userspace signature validation is allowed. If
false, authentication can be done only via the kernel's internal keyring.
* `$SYSTEMD_DISSECT_VERITY_GUESS` takes a boolean. Controls whether to guess
the Verity root hash from the partition UUIDs of a suitable pair of data
partition and matching Verity partition: the UUIDs two are simply joined and
used as root hash, in accordance with the recommendations in [Discoverable
Partitions
Specification](https://uapi-group.org/specifications/specs/discoverable_partitions_specification). Defaults
to true.
`systemd-cryptsetup`:
* `$SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE` takes a boolean, which controls

View File

@@ -481,6 +481,18 @@
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.image_filter=</varname></term>
<listitem><para>When GPT-based partition auto-discovery is used, configures the image dissection
filter string to apply, as per
<citerefentry><refentrytitle>systemd.image-filter</refentrytitle><manvolnum>7</manvolnum></citerefentry>. For
details see
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.default_timeout_start_sec=</varname></term>

View File

@@ -1188,6 +1188,7 @@ manpages = [
['systemd.environment-generator', '7', [], 'ENABLE_ENVIRONMENT_D'],
['systemd.exec', '5', [], ''],
['systemd.generator', '7', [], ''],
['systemd.image-filter', '7', [], ''],
['systemd.image-policy', '7', [], ''],
['systemd.journal-fields', '7', [], ''],
['systemd.kill', '5', [], ''],

View File

@@ -114,6 +114,14 @@
in the image are used.</para></listitem>
</varlistentry>
<varlistentry id='image-filter'>
<term><option>--image-filter=<replaceable>filter</replaceable></option></term> <listitem><para>Takes an
image filter string as argument, as per
<citerefentry><refentrytitle>systemd.image-filter</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
filter is taken into consideration when operating on the disk image specified via
<option>--image=</option>, see above. If not specified no filtering is applied.</para></listitem>
</varlistentry>
<varlistentry id='esp-path'>
<term><option>--esp-path=</option></term>

View File

@@ -582,6 +582,7 @@
</varlistentry>
<xi:include href="standard-options.xml" xpointer="image-policy-open" />
<xi:include href="standard-options.xml" xpointer="image-filter" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />

View File

@@ -83,10 +83,14 @@
<listitem><para>Configures the operating system's root filesystem to mount when running in the
initrd. This accepts a device node path (usually <filename>/dev/disk/by-uuid/…</filename> or
<filename>/dev/disk/by-label/…</filename> or similar), or the special values <literal>gpt-auto</literal>,
<literal>fstab</literal>, and <literal>tmpfs</literal>.</para>
<filename>/dev/disk/by-label/…</filename> or similar), or the special values
<literal>gpt-auto</literal>, <literal>gpt-auto-force</literal>, <literal>dissect</literal>,
<literal>dissect-force</literal>, <literal>fstab</literal>, <literal>fstab</literal>, and
<literal>off</literal>.</para>
<para>Use <literal>gpt-auto</literal> to explicitly request automatic root file system discovery via
<para>Set to <literal>gpt-auto</literal>, <literal>gpt-auto-force</literal>,
<literal>dissect</literal>, <literal>dissect-force</literal> to explicitly request automatic root
file system discovery, implemented in
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>Use <literal>fstab</literal> to explicitly request automatic root file system discovery via
@@ -103,6 +107,11 @@
filesystems (added in v258). Expects an absolute path name referencing an existing directory within the initrd's file
hierarchy to boot into.</para>
<para>Set to <literal>off</literal> to turn off mounting of a root file system.</para>
<para>Note that further <varname>root=</varname> values may be supported, implemented in additional
packages.</para>
<xi:include href="version-info.xml" xpointer="v217"/></listitem>
</varlistentry>
@@ -133,20 +142,18 @@
<varlistentry>
<term><varname>mount.usr=</varname></term>
<listitem><para>Takes the <filename>/usr/</filename> filesystem
to be mounted by the initrd. If
<varname>mount.usrfstype=</varname> or
<varname>mount.usrflags=</varname> is set, then
<varname>mount.usr=</varname> will default to the value set in
<listitem><para>Takes the <filename>/usr/</filename> filesystem to be mounted by the initrd. If
<varname>mount.usrfstype=</varname> or <varname>mount.usrflags=</varname> is set, then the mount
configured via <varname>mount.usr=</varname> will default to the the same value set in
<varname>root=</varname>.</para>
<para>Otherwise, this parameter defaults to the
<filename>/usr/</filename> entry found in
<filename>/etc/fstab</filename> on the root filesystem.</para>
<para>Set to <literal>dissect</literal> to explicitly request automatic <filename>/usr/</filename>
file system discovery, implemented in
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><varname>mount.usr=</varname> is honored by the initrd.
</para>
<para>Set to <literal>off</literal> to turn off mounting of a separate <filename>/usr/</filename> file system.</para>
<para><varname>mount.usr=</varname> is honored by the initrd.</para>
<xi:include href="version-info.xml" xpointer="v217"/></listitem>
</varlistentry>

View File

@@ -70,11 +70,11 @@
<citerefentry><refentrytitle>systemd-import-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
make sure to set the <literal>blockdev</literal> option and set the local name string to
<literal>rootdisk</literal> to achieve this effect. Note that discovery of the root file system on
loopback block devices like this is only done if <literal>root=gpt-auto</literal> is specified
explicitly on the kernel command line, unlike the discovery based on the boot loader reported ESP which
is also enabled if no <literal>root=</literal> parameter is specified at all. (The latter relies on
<command>systemd-udevd.service</command>'s <filename>/dev/gpt-auto-root</filename> block device symlink
generation).</para></listitem>
loopback block devices like this is only done if <literal>root=gpt-auto</literal> or
<literal>root=dissect</literal> is specified explicitly on the kernel command line, unlike the
discovery based on the boot loader reported ESP which is also enabled if no <literal>root=</literal>
parameter is specified at all. (The latter relies on <command>systemd-udevd.service</command>'s
<filename>/dev/gpt-auto-root</filename> block device symlink generation).</para></listitem>
</itemizedlist>
</para>
@@ -317,26 +317,64 @@
and allows enforcing a policy on dissection and use of the automatically discovered GPT partition
table entries.</para>
<para>Note that the specified image policy is not taken into account for automatic root or
<filename>/usr/</filename> file system discovery unless
<varname>root=dissect</varname>/<varname>mount.usr=dissect</varname> (or
<varname>root=dissect-force</varname>) are specified. (The policy will always be applied to the other
auto-discoverable partition types.)</para>
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.image_filter=</varname></term>
<listitem><para>Takes an image dissection filter string as argument (as per
<citerefentry><refentrytitle>systemd.image-filter</refentrytitle><manvolnum>7</manvolnum></citerefentry>),
and allows enforcing a set of globbing patterns on the partition matching of the automatically
discovered GPT partition table entries.</para>
<para>Note that the specified image filter is not taken into account for automatic root or
<filename>/usr/</filename> file system discovery unless
<varname>root=dissect</varname>/<varname>mount.usr=dissect</varname> (or
<varname>root=dissect-force</varname>) are specified. (The filter will always be applied to the other
auto-discoverable partition types.)</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>root=</varname></term>
<term><varname>rootfstype=</varname></term>
<term><varname>rootflags=</varname></term>
<listitem><para>When <varname>root=</varname> is used with the special value
<literal>gpt-auto</literal>, full automatic discovery of the root partition based on the GPT
<literal>gpt-auto</literal>, basic automatic discovery of the root partition based on the GPT
partition type is enabled. Use of the root partition is delayed until factory reset mode is left, in
case it is enabled during the current boot. See <ulink url="https://systemd.io/FACTORY_RESET">Factory
Reset</ulink> for more information on that. If <literal>gpt-auto-force</literal> is specified
automatic discovery of the root partition is enabled, ignoring any factory reset mode. Any other
value disables this logic.</para>
automatic discovery of the root partition is enabled, ignoring any factory reset mode.</para>
<para>If <varname>root=</varname> is set to the special value <literal>dissect</literal> full
automatic discovery of the root partition based on GPT partition information is enabled. This is a
superset of <varname>root=gpt-auto</varname>, as it automatically configures Verity partitions
(including signature-based setup) following the logic defined for that in the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification/">Discoverable
Partitions Specification</ulink>. Moreover it takes the configured image policy and image filter into
account for all partition types, including the root file system. <literal>root=dissect</literal> will
wait for the factory reset phase to be completed if it is in effect before activating the root file
system. Use <literal>root=dissect-force</literal> to ignore the factory reset phase and activate the
root file system immediately.</para>
<para>Any other value (i.e. besides <literal>gpt-auto</literal>, <literal>gpt-auto-force</literal>,
<literal>dissect</literal>, <literal>dissect-force</literal>) disables automatic root file system
discovery.</para>
<para>If <varname>root=</varname> is not specified at all on the kernel command line automatic
discovery of the root partition via the ESP reported by the boot loader is also enabled (taking
factory reset state into account), however in this case discovery based on the loopback block device
<literal>.lo_name</literal> field is not enabled.</para>
factory reset state into account, i.e. equivalent to <literal>root=gpt-auto</literal>), however in
this case discovery based on the loopback block device <literal>.lo_name</literal> field is not
enabled.</para>
<para>The <varname>rootfstype=</varname> and <varname>rootflags=</varname> options are used to select
the file system type and options when the root file system is automatically discovered.</para>
@@ -344,6 +382,24 @@
<xi:include href="version-info.xml" xpointer="v242"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>mount.usr=</varname></term>
<term><varname>mount.usrfstype=</varname></term>
<term><varname>mount.usrflags=</varname></term>
<listitem><para>Similar to <varname>root=</varname>, <varname>rootfstype=</varname>,
<varname>rootflags=</varname> (see above), but applies to the <filename>/usr/</filename> partition
instead. Note that the <literal>gpt-auto</literal>, <literal>gpt-auto-force</literal>,
<literal>dissect-force</literal> settings that <filename>root=</filename> understands are not
supported by <varname>mount.usr=</varname> (however <literal>dissect</literal> is).</para>
<para>Also note that automatic partition discovery for <filename>/usr/</filename> must be enabled
explicitly, unlike the discovery for the root file system, which is enabled if no
<varname>root=</varname> paramater is passed at all.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>rw</varname></term>
<term><varname>ro</varname></term>

View File

@@ -0,0 +1,80 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd.image-filter">
<refentryinfo>
<title>systemd.image-filter</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd.image-filter</refentrytitle>
<manvolnum>7</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd.image-filter</refname>
<refpurpose>Disk Image Dissection Filter</refpurpose>
</refnamediv>
<refsect1>
<title>Description</title>
<para>In systemd, whenever a disk image (DDI) implementing the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable
Partitions Specification</ulink> is activated, a filter may be specified controlling which partitions to
consider for mounting. Such a disk image dissection filter is a string that contains per-partition-type
patterns, separated by colons (<literal>:</literal>). The individual rules consist of a partition
identifier, an equal sign (<literal>=</literal>), and a shell globbing pattern applied to the GPT label
string of the partition. See <citerefentry
project='man-pages'><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details on shell globbing.</para>
<para>The partition identifiers currently defined are: <option>root</option>, <option>usr</option>,
<option>home</option>, <option>srv</option>, <option>esp</option>, <option>xbootldr</option>,
<option>swap</option>, <option>root-verity</option>, <option>root-verity-sig</option>,
<option>usr-verity</option>, <option>usr-verity-sig</option>, <option>tmp</option>,
<option>var</option>. These identifiers match the relevant partition types in the Discoverable Partitions
Specification, but are agnostic to CPU architectures.</para>
</refsect1>
<refsect1>
<title>Use</title>
<para>Various systemd components that support operating with disk images support a
<option>--image-filter=</option> command line option to specify the image filter to use. If no filter is
specified all partitions in partition table are considered and no per-label filtering is applied (except
that partitions with the <literal>_empty</literal> label are always ignored).</para>
<para>For the host root file system image itself
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is responsible for processing the GPT partition table and making use of the included discoverable
partitions. It accepts an image filter via the kernel command line option
<option>systemd.image_filter=</option>.</para>
</refsect1>
<refsect1>
<title>Examples</title>
<para>The following image filter string dictates that for the root file system partition only partitions
shall be considered whose label begins with <literal>ParticleOS-</literal>. For the
<filename>/usr/</filename> partition the precise label <literal>ParticleOS_47110815</literal> is
required.</para>
<programlisting>root=ParticleOS-*:usr=ParticleOS_47110815</programlisting>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.image-policy</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -185,6 +185,7 @@
<member><citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.filter</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View File

@@ -53,7 +53,9 @@ This is based on crypttab(5).
<para>The third field contains a path to the underlying block hash device, or a specification of a block device via
<varname>UUID=</varname> followed by the <replaceable>UUID</replaceable>.</para>
<para>The fourth field is the <replaceable>roothash</replaceable> in hexadecimal.</para>
<para>The fourth field is the <replaceable>roothash</replaceable> in hexadecimal. If this field is
specified as dash, it is attempted to read the root hash from the udev property
<literal>ID_DISSECT_PART_ROOTHASH=</literal> (encoded in hexadecimal) of the data device.</para>
<para>The fifth field, if present, is a comma-delimited list of options. The following options are
recognized:</para>
@@ -213,11 +215,14 @@ This is based on crypttab(5).
</varlistentry>
<varlistentry>
<term><option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>BASE64</replaceable></option></term>
<term><option>root-hash-signature=<replaceable>PATH</replaceable>|base64:<replaceable>BASE64</replaceable>|auto</option></term>
<listitem><para>A base64 string encoding the root hash signature prefixed by
<literal>base64:</literal> or an absolute path to a root hash signature file used to verify the root
hash (in kernel). This feature requires Linux kernel version 5.4 or more recent.</para>
<listitem><para>A Base64 string encoding the root hash signature prefixed by
<literal>base64:</literal>, or an absolute path to a root hash signature file used to verify the root
hash (in kernel). If the special string <literal>auto</literal> is specified, the root hash signature
is attempted to be read from the udev property <literal>ID_DISSECT_PART_ROOTHASH_SIG=</literal> (in
Base64 format) of the data device. This feature requires Linux kernel version 5.4 or more
recent.</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>

View File

@@ -30,4 +30,27 @@ LABEL="gpt_auto_root_end"
ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", IMPORT{builtin}="factory_reset status", SYMLINK+="gpt-auto-root"
ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
# If this is the whole disk that we booted from, then dissect it
ENV{DEVTYPE}=="disk", ENV{ID_PART_GPT_AUTO_ROOT_DISK}=="1", IMPORT{builtin}="dissect_image probe"
ENV{DEVTYPE}=="disk", ENV{ID_PART_GPT_AUTO_ROOT_DISK}=="1", ENV{ID_FACTORY_RESET}=="", IMPORT{builtin}="factory_reset status"
# If this is a partition, and we found something on the parent, then copy the
# right properties from the parent, and rename them
ENV{DEVTYPE}=="partition", ENV{ID_DISSECT_IMAGE}!="", IMPORT{builtin}="dissect_image copy"
# Create symlinks based on the designator for the partitions themselves. If we detect LUKS or Verity, suffix them with "-luks" or "-vdata"
ENV{DEVTYPE}!="partition", GOTO="dissect_partition_symlinks_end"
ENV{ID_DISSECT_PART_DESIGNATOR}=="", GOTO="dissect_partition_symlinks_end"
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}!="1", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}"
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}!="1", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-ignore-factory-reset"
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-luks"
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-luks-ignore-factory-reset"
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}=="1", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-verity-data"
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}=="1", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-verity-data-ignore-factory-reset"
LABEL="dissect_partition_symlinks_end"
# For LUKS or Verity partitions we rely on the selected volume name
ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root|usr|home|srv|swap|tmp|var", IMPORT{builtin}="factory_reset status", SYMLINK+="disk/by-designator/$env{DM_NAME}"
ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root|usr|home|srv|swap|tmp|var", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{DM_NAME}-ignore-factory-reset"
LABEL="image_dissect_end"

View File

@@ -1476,9 +1476,8 @@ static int mount_image(
const char *root_directory,
const ImagePolicy *image_policy) {
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
*host_os_release_sysext_level = NULL, *host_os_release_confext_level = NULL,
*extension_name = NULL;
_cleanup_(extension_release_data_done) ExtensionReleaseData rdata = {};
_cleanup_free_ char *extension_name = NULL;
int r;
assert(m);
@@ -1490,14 +1489,14 @@ static int mount_image(
if (m->mode == MOUNT_EXTENSION_IMAGE) {
r = parse_os_release(
empty_to_root(root_directory),
"ID", &host_os_release_id,
"VERSION_ID", &host_os_release_version_id,
image_class_info[IMAGE_SYSEXT].level_env, &host_os_release_sysext_level,
image_class_info[IMAGE_CONFEXT].level_env, &host_os_release_confext_level,
"ID", &rdata.os_release_id,
"VERSION_ID", &rdata.os_release_version_id,
image_class_info[IMAGE_SYSEXT].level_env, &rdata.os_release_sysext_level,
image_class_info[IMAGE_CONFEXT].level_env, &rdata.os_release_confext_level,
NULL);
if (r < 0)
return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
if (isempty(host_os_release_id))
if (isempty(rdata.os_release_id))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s'.", empty_to_root(root_directory));
}
@@ -1507,26 +1506,23 @@ static int mount_image(
mount_entry_path(m),
m->image_options_const,
image_policy,
host_os_release_id,
host_os_release_version_id,
host_os_release_sysext_level,
host_os_release_confext_level,
/* required_sysext_scope= */ NULL,
/* image_filter= */ NULL,
&rdata,
&m->verity,
/* ret_image= */ NULL);
if (r == -ENOENT && m->ignore)
return 0;
if (r == -ESTALE && host_os_release_id)
if (r == -ESTALE && rdata.os_release_id)
return log_error_errno(r, // FIXME: this should not be logged ad LOG_ERR, as it will result in duplicate logging.
"Failed to mount image %s, extension-release metadata does not match the lower layer's: ID=%s%s%s%s%s%s%s",
mount_entry_source(m),
host_os_release_id,
host_os_release_version_id ? " VERSION_ID=" : "",
strempty(host_os_release_version_id),
host_os_release_sysext_level ? image_class_info[IMAGE_SYSEXT].level_env_print : "",
strempty(host_os_release_sysext_level),
host_os_release_confext_level ? image_class_info[IMAGE_CONFEXT].level_env_print : "",
strempty(host_os_release_confext_level));
rdata.os_release_id,
rdata.os_release_version_id ? " VERSION_ID=" : "",
strempty(rdata.os_release_version_id),
rdata.os_release_sysext_level ? image_class_info[IMAGE_SYSEXT].level_env_print : "",
strempty(rdata.os_release_sysext_level),
rdata.os_release_confext_level ? image_class_info[IMAGE_CONFEXT].level_env_print : "",
strempty(rdata.os_release_confext_level));
if (r < 0)
return log_debug_errno(r, "Failed to mount image %s on %s: %m", mount_entry_source(m), mount_entry_path(m));
@@ -2352,6 +2348,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
p->verity,
p->root_image_options,
p->root_image_policy,
/* image_filter= */ NULL,
dissect_image_flags,
&dissected_image);
if (r < 0)
@@ -2364,6 +2361,12 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
if (r < 0)
return r;
r = dissected_image_guess_verity_roothash(
dissected_image,
p->verity);
if (r < 0)
return r;
r = dissected_image_decrypt(
dissected_image,
NULL,

View File

@@ -102,6 +102,7 @@ static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
static bool arg_all = false;
static uid_t arg_uid_base = UID_INVALID;
static bool arg_quiet = false;
static ImageFilter *arg_image_filter = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -110,6 +111,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
STATIC_DESTRUCTOR_REGISTER(arg_argv, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_loop_ref, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_filter, image_filter_freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@@ -155,6 +157,8 @@ static int help(void) {
" not embedded in IMAGE\n"
" --image-policy=POLICY\n"
" Specify image dissection policy\n"
" --image-filter=FILTER\n"
" Specify image dissection filter\n"
" --json=pretty|short|off\n"
" Generate JSON output\n"
" --loop-ref=NAME Set reference string for loopback device\n"
@@ -295,6 +299,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SYSTEM,
ARG_USER,
ARG_ALL,
ARG_IMAGE_FILTER,
};
static const struct option options[] = {
@@ -336,6 +341,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "user", no_argument, NULL, ARG_USER },
{ "all", no_argument, NULL, ARG_ALL },
{ "quiet", no_argument, NULL, 'q' },
{ "image-filter", required_argument, NULL, ARG_IMAGE_FILTER },
{}
};
@@ -610,6 +616,17 @@ static int parse_argv(int argc, char *argv[]) {
arg_quiet = true;
break;
case ARG_IMAGE_FILTER: {
_cleanup_(image_filter_freep) ImageFilter *f = NULL;
r = image_filter_parse(optarg, &f);
if (r < 0)
return log_error_errno(r, "Failed to parse image filter expression: %s", optarg);
image_filter_free(arg_image_filter);
arg_image_filter = TAKE_PTR(f);
break;
}
case '?':
return -EINVAL;
@@ -2106,10 +2123,11 @@ static int action_validate(void) {
r = dissect_image_file_and_warn(
arg_image,
&arg_verity_settings,
NULL,
/* mount_options= */ NULL,
arg_image_policy,
arg_image_filter,
arg_flags,
NULL);
/* ret= */ NULL);
if (r < 0)
return r;
@@ -2177,8 +2195,10 @@ static int run(int argc, char *argv[]) {
if (arg_image) {
r = verity_settings_load(
&arg_verity_settings,
arg_image, NULL, NULL);
&arg_verity_settings,
arg_image,
/* root_hash_path= */ NULL,
/* root_hash_sig_path= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
@@ -2229,6 +2249,7 @@ static int run(int argc, char *argv[]) {
&arg_verity_settings,
/* mount_options= */ NULL,
arg_image_policy,
arg_image_filter,
arg_flags,
&m);
if (r < 0)
@@ -2244,6 +2265,12 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to load verity signature partition: %m");
r = dissected_image_guess_verity_roothash(
m,
&arg_verity_settings);
if (r < 0)
return log_error_errno(r, "Failed to guess verity root hash: %m");
if (arg_action != ACTION_DISSECT) {
r = dissected_image_decrypt_interactively(
m, NULL,

View File

@@ -1132,7 +1132,12 @@ static bool validate_root_or_usr_mount_source(const char *what, const char *swit
return false;
}
if (parse_gpt_auto_root(what) > 0) {
if (streq(what, "off")) {
log_debug("Skipping %s directory handling, as this was explicitly turned off.", switch_name);
return false;
}
if (parse_gpt_auto_root(switch_name, what) > 0) {
/* This is handled by gpt-auto-generator */
log_debug("Skipping %s directory handling, as gpt-auto was requested.", switch_name);
return false;

View File

@@ -24,6 +24,7 @@
#include "fstab-util.h"
#include "generator.h"
#include "gpt.h"
#include "hexdecoct.h"
#include "image-policy.h"
#include "initrd-util.h"
#include "mountpoint-util.h"
@@ -46,17 +47,24 @@ typedef enum MountPointFlags {
} MountPointFlags;
static const char *arg_dest = NULL;
static const char *arg_dest_late = NULL;
static bool arg_enabled = true;
static GptAutoRoot arg_auto_root = _GPT_AUTO_ROOT_INVALID;
static GptAutoRoot arg_auto_usr = _GPT_AUTO_ROOT_INVALID;
static bool arg_swap_enabled = true;
static char *arg_root_fstype = NULL;
static char *arg_root_options = NULL;
static int arg_root_rw = -1;
static char *arg_usr_fstype = NULL;
static char *arg_usr_options = NULL;
static ImagePolicy *arg_image_policy = NULL;
static ImageFilter *arg_image_filter = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
STATIC_DESTRUCTOR_REGISTER(arg_usr_fstype, freep);
STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep);
#define LOADER_PARTITION_IDLE_USEC (120 * USEC_PER_SEC)
@@ -88,7 +96,7 @@ static int add_cryptsetup(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, /* source = */ NULL, n, &f);
r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, n, &f);
if (r < 0)
return r;
@@ -135,27 +143,27 @@ static int add_cryptsetup(
if (r < 0)
return log_error_errno(r, "Failed to write file %s: %m", n);
r = generator_write_device_timeout(arg_dest, what, mount_opts, /* filtered = */ NULL);
r = generator_write_device_timeout(arg_dest_late, what, mount_opts, /* filtered = */ NULL);
if (r < 0)
return r;
r = generator_add_symlink(arg_dest, d, "wants", n);
r = generator_add_symlink(arg_dest_late, d, "wants", n);
if (r < 0)
return r;
const char *dmname = strjoina("dev-mapper-", e, ".device");
if (require) {
r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n);
r = generator_add_symlink(arg_dest_late, "cryptsetup.target", "requires", n);
if (r < 0)
return r;
r = generator_add_symlink(arg_dest, dmname, "requires", n);
r = generator_add_symlink(arg_dest_late, dmname, "requires", n);
if (r < 0)
return r;
}
r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout",
r = write_drop_in_format(arg_dest_late, dmname, 50, "job-timeout",
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"JobTimeoutSec=infinity"); /* the binary handles timeouts anyway */
@@ -179,6 +187,106 @@ static int add_cryptsetup(
#endif
}
#if ENABLE_EFI
static int add_veritysetup(
const char *id,
const char *data_what,
const char *hash_what,
const char *mount_opts) {
#if HAVE_LIBCRYPTSETUP
int r;
assert(id);
assert(data_what);
assert(hash_what);
_cleanup_free_ char *dd = NULL;
r = unit_name_from_path(data_what, ".device", &dd);
if (r < 0)
return log_error_errno(r, "Failed to generate data device unit name: %m");
_cleanup_free_ char *dh = NULL;
r = unit_name_from_path(hash_what, ".device", &dh);
if (r < 0)
return log_error_errno(r, "Failed to generate hash device unit name: %m");
_cleanup_free_ char *e = unit_name_escape(id);
if (!e)
return log_oom();
_cleanup_free_ char *n = NULL;
r = unit_name_build("systemd-veritysetup", e, ".service", &n);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
_cleanup_fclose_ FILE *f = NULL;
r = generator_open_unit_file(arg_dest_late, /* source= */ NULL, n, &f);
if (r < 0)
return r;
r = generator_write_veritysetup_unit_section(f, /* source= */ NULL);
if (r < 0)
return r;
fprintf(f,
"Before=veritysetup.target\n"
"BindsTo=%1$s %2$s\n"
"After=%1$s %2$s\n",
dd, dh);
r = generator_write_veritysetup_service_section(
f,
id,
data_what,
hash_what,
/* roothash= */ NULL, /* NULL means: derive root hash from udev property ID_DISSECT_PART_ROOTHASH */
"root-hash-signature=auto"); /* auto means: derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
if (r < 0)
return r;
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write file %s: %m", n);
r = generator_write_device_timeout(arg_dest_late, data_what, mount_opts, /* filtered= */ NULL);
if (r < 0)
return r;
r = generator_write_device_timeout(arg_dest_late, hash_what, mount_opts, /* filtered= */ NULL);
if (r < 0)
return r;
r = generator_add_symlink(arg_dest_late, dd, "wants", n);
if (r < 0)
return r;
r = generator_add_symlink(arg_dest_late, dh, "wants", n);
if (r < 0)
return r;
_cleanup_free_ char *dmname = NULL;
dmname = strjoin("dev-mapper-", e, ".device");
if (!dmname)
return log_oom();
r = write_drop_in_format(
arg_dest_late,
dmname, 50, "job-timeout",
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"JobTimeoutSec=infinity"); /* the binary handles timeouts anyway */
if (r < 0)
return log_error_errno(r, "Failed to write device timeout drop-in: %m");
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Partition is Verity protected, but systemd-gpt-auto-generator was compiled without libcryptsetup support.");
#endif
}
#endif
static int add_mount(
const char *id,
const char *what,
@@ -224,7 +332,7 @@ static int add_mount(
fstype, where);
}
r = generator_write_device_timeout(arg_dest, what, options, &opts_filtered);
r = generator_write_device_timeout(arg_dest_late, what, options, &opts_filtered);
if (r < 0)
return r;
@@ -232,7 +340,7 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, /* source = */ NULL, unit, &f);
r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, unit, &f);
if (r < 0)
return r;
@@ -245,6 +353,10 @@ static int add_mount(
if (post)
fprintf(f, "Before=%s\n", post);
/* NB: here we do not write to arg_dest_late, but to arg_dest! We typically leave the normal
* generator drop-in dir for explicit configuration via systemd-fstab-generator or similar, and put
* out automatic configuration in the arg_dest_late directory. But this one is an exception, since we
* need to override the static version of the fsck root service file. */
r = generator_write_fsck_deps(f, arg_dest, what, where, fstype, opts_filtered);
if (r < 0)
return r;
@@ -275,25 +387,25 @@ static int add_mount(
return log_error_errno(r, "Failed to write unit %s: %m", unit);
if (FLAGS_SET(flags, MOUNT_VALIDATEFS)) {
r = generator_hook_up_validatefs(arg_dest, where, post);
r = generator_hook_up_validatefs(arg_dest_late, where, post);
if (r < 0)
return r;
}
if (FLAGS_SET(flags, MOUNT_GROWFS)) {
r = generator_hook_up_growfs(arg_dest, where, post);
r = generator_hook_up_growfs(arg_dest_late, where, post);
if (r < 0)
return r;
}
if (FLAGS_SET(flags, MOUNT_MEASURE)) {
r = generator_hook_up_pcrfs(arg_dest, where, post);
r = generator_hook_up_pcrfs(arg_dest_late, where, post);
if (r < 0)
return r;
}
if (post) {
r = generator_add_symlink(arg_dest, post, "requires", unit);
r = generator_add_symlink(arg_dest_late, post, "requires", unit);
if (r < 0)
return r;
}
@@ -407,7 +519,7 @@ static int add_partition_swap(DissectedPartition *p) {
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, /* source = */ NULL, name, &f);
r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, name, &f);
if (r < 0)
return r;
@@ -430,7 +542,7 @@ static int add_partition_swap(DissectedPartition *p) {
if (r < 0)
return log_error_errno(r, "Failed to write unit %s: %m", name);
return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name);
return generator_add_symlink(arg_dest_late, SPECIAL_SWAP_TARGET, "wants", name);
}
static int add_automount(
@@ -466,7 +578,7 @@ static int add_automount(
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = generator_open_unit_file(arg_dest, /* source = */ NULL, unit, &f);
r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, unit, &f);
if (r < 0)
return r;
@@ -485,7 +597,7 @@ static int add_automount(
if (r < 0)
return log_error_errno(r, "Failed to write unit %s: %m", unit);
return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
return generator_add_symlink(arg_dest_late, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
}
static int add_partition_xbootldr(DissectedPartition *p) {
@@ -608,11 +720,11 @@ static int add_partition_root_rw(DissectedPartition *p) {
return 0;
}
r = generator_enable_remount_fs_service(arg_dest);
r = generator_enable_remount_fs_service(arg_dest_late);
if (r < 0)
return r;
path = strjoina(arg_dest, "/systemd-remount-fs.service.d/50-remount-rw.conf");
path = strjoina(arg_dest_late, "/systemd-remount-fs.service.d/50-remount-rw.conf");
r = write_string_file(path,
"# Automatically generated by systemd-gpt-auto-generator\n\n"
@@ -637,7 +749,7 @@ static int add_partition_root_growfs(DissectedPartition *p) {
return 0;
}
return generator_hook_up_growfs(arg_dest, "/", SPECIAL_LOCAL_FS_TARGET);
return generator_hook_up_growfs(arg_dest_late, "/", SPECIAL_LOCAL_FS_TARGET);
}
static int add_partition_root_flags(DissectedPartition *p) {
@@ -656,22 +768,36 @@ static int add_partition_root_flags(DissectedPartition *p) {
static int add_root_cryptsetup(void) {
#if HAVE_LIBCRYPTSETUP
/* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
* sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
assert(arg_auto_root != GPT_AUTO_ROOT_OFF);
const char *bdev = "/dev/gpt-auto-root-luks";
/* If a device /dev/gpt-auto-root-luks or /dev/disk/by-designator/root-luks appears, then make it
* pull in systemd-cryptsetup@root.service, which sets it up, and causes /dev/gpt-auto-root or
* /dev/disk/by-designator/root to appear which is all we are looking for. */
if (arg_auto_root == GPT_AUTO_ROOT_FORCE) {
const char *bdev =
IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
"/dev/disk/by-designator/root-luks" : "/dev/gpt-auto-root-luks";
if (IN_SET(arg_auto_root, GPT_AUTO_ROOT_FORCE, GPT_AUTO_ROOT_DISSECT_FORCE)) {
/* Similar logic as in add_root_mount(), see below */
FactoryResetMode f = factory_reset_mode();
if (f < 0)
log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
bdev = "/dev/gpt-auto-root-luks-ignore-factory-reset";
bdev =
IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
"/dev/disk/by-designator/root-luks-ignore-factory-reset" :
"/dev/gpt-auto-root-luks-ignore-factory-reset";
}
return add_cryptsetup("root", bdev, arg_root_options, MOUNT_RW|MOUNT_MEASURE, /* require= */ false, NULL);
return add_cryptsetup(
"root",
bdev,
arg_root_options,
MOUNT_RW|MOUNT_MEASURE,
/* require= */ false,
/* ret_device= */ NULL);
#else
return 0;
#endif
@@ -715,24 +841,41 @@ static int add_root_mount(void) {
* factory reset the latter is the link to use, otherwise the former (so that we don't accidentally
* mount a root partition too early that is about to be wiped and replaced by another one). */
const char *bdev = "/dev/gpt-auto-root";
if (arg_auto_root == GPT_AUTO_ROOT_FORCE) {
const char *bdev =
IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
"/dev/disk/by-designator/root" : "/dev/gpt-auto-root";
if (IN_SET(arg_auto_root, GPT_AUTO_ROOT_FORCE, GPT_AUTO_ROOT_DISSECT_FORCE)) {
FactoryResetMode f = factory_reset_mode();
if (f < 0)
log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
bdev = "/dev/gpt-auto-root-ignore-factory-reset";
bdev =
IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
"/dev/disk/by-designator/root-ignore-factory-reset" :
"/dev/gpt-auto-root-ignore-factory-reset";
}
if (in_initrd()) {
r = generator_write_initrd_root_device_deps(arg_dest, bdev);
r = generator_write_initrd_root_device_deps(arg_dest_late, bdev);
if (r < 0)
return 0;
r = add_root_cryptsetup();
if (r < 0)
return r;
/* If a device /dev/disk/by-designator/root-verity or
* /dev/disk/by-designator/root-verity-data appears, then make it pull in
* systemd-cryptsetup@root.service, which sets it up, and causes /dev/disk/by-designator/root
* to appear. */
r = add_veritysetup(
"root",
"/dev/disk/by-designator/root-verity-data",
"/dev/disk/by-designator/root-verity",
arg_root_options);
if (r < 0)
return r;
}
/* Note that we do not need to enable systemd-remount-fs.service here. If /etc/fstab exists,
@@ -769,6 +912,95 @@ static int add_root_mount(void) {
#endif
}
static int add_usr_mount(void) {
#if ENABLE_EFI
int r;
/* /usr/ discovery must be enabled explicitly. */
if (arg_auto_usr == GPT_AUTO_ROOT_OFF ||
arg_auto_usr < 0)
return 0;
/* We do not support the other gpt-auto modes for /usr/, but the parser should already have checked that. */
assert(arg_auto_usr == GPT_AUTO_ROOT_DISSECT);
if (arg_root_fstype && !arg_usr_fstype) {
arg_usr_fstype = strdup(arg_root_fstype);
if (!arg_usr_fstype)
return log_oom();
}
if (arg_root_options && !arg_usr_options) {
arg_usr_options = strdup(arg_root_options);
if (!arg_usr_options)
return log_oom();
}
if (in_initrd()) {
r = add_cryptsetup(
"usr",
"/dev/disk/by-designator/usr-luks",
arg_usr_options,
MOUNT_RW|MOUNT_MEASURE,
/* require= */ false,
/* ret_device= */ NULL);
if (r < 0)
return r;
/* If a device /dev/disk/by-designator/usr-verity or
* /dev/disk/by-designator/usr-verity-data appears, then make it pull in
* systemd-cryptsetup@usr.service, which sets it up, and causes /dev/disk/by-designator/usr
* to appear. */
r = add_veritysetup(
"usr",
"/dev/disk/by-designator/usr-verity-data",
"/dev/disk/by-designator/usr-verity",
arg_usr_options);
if (r < 0)
return r;
}
_cleanup_free_ char *options = NULL;
r = partition_pick_mount_options(
PARTITION_USR,
arg_usr_fstype,
/* rw= */ false,
/* discard= */ true,
&options,
/* ret_ms_flags= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to pick /usr/ mount options: %m");
if (arg_usr_options)
if (!strextend_with_separator(&options, ",", arg_usr_options))
return log_oom();
r = add_mount("usr",
"/dev/disk/by-designator/usr",
in_initrd() ? "/sysusr/usr" : "/usr",
arg_usr_fstype,
(in_initrd() ? MOUNT_VALIDATEFS : 0),
options,
"/usr/ Partition",
in_initrd() ? SPECIAL_INITRD_USR_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
if (r < 0)
return r;
log_debug("Synthesizing entry what=/sysusr/usr where=/sysroot/usr opts=bind");
return add_mount("usr-bind",
"/sysusr/usr",
"/sysroot/usr",
/* fstype= */ NULL,
/* flags= */ 0,
"bind",
"/usr/ Partition (Final)",
in_initrd() ? SPECIAL_INITRD_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
#else
return 0;
#endif
}
static int process_loader_partitions(DissectedPartition *esp, DissectedPartition *xbootldr) {
sd_id128_t loader_uuid;
int r;
@@ -877,6 +1109,7 @@ static int enumerate_partitions(dev_t devnum) {
/* verity= */ NULL,
/* mount_options= */ NULL,
image_policy,
arg_image_filter,
DISSECT_IMAGE_GPT_ONLY|
DISSECT_IMAGE_USR_NO_ROOT|
DISSECT_IMAGE_DISKSEQ_DEVNODE|
@@ -960,7 +1193,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
/* Disable root disk logic if there's a root= value specified (unless it happens to be
* "gpt-auto" or "gpt-auto-force") */
arg_auto_root = parse_gpt_auto_root(value);
arg_auto_root = parse_gpt_auto_root("root=", value);
assert(arg_auto_root >= 0);
} else if (streq(key, "roothash")) {
@@ -988,14 +1221,54 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (!strextend_with_separator(&arg_root_options, ",", value))
return log_oom();
} else if (streq(key, "mount.usr")) {
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's a root= value specified (unless it happens to be
* "gpt-auto" or "gpt-auto-force") */
arg_auto_usr = parse_gpt_auto_root("mount.usr=", value);
assert(arg_auto_usr >= 0);
if (IN_SET(arg_auto_usr, GPT_AUTO_ROOT_ON, GPT_AUTO_ROOT_FORCE, GPT_AUTO_ROOT_DISSECT_FORCE)) {
log_warning("'gpt-auto', 'gpt-auto-force' and 'dissect-force' are not supported for mount.usr=. Automatically resorting to mount.usr=dissect mode instead.");
arg_auto_usr = GPT_AUTO_ROOT_DISSECT;
}
} else if (streq(key, "mount.usrfstype")) {
if (proc_cmdline_value_missing(key, value))
return 0;
return free_and_strdup_warn(&arg_usr_fstype, empty_to_null(value));
} else if (streq(key, "mount.usrflags")) {
if (proc_cmdline_value_missing(key, value))
return 0;
if (!strextend_with_separator(&arg_usr_options, ",", value))
return log_oom();
} else if (streq(key, "rw") && !value)
arg_root_rw = true;
else if (streq(key, "ro") && !value)
arg_root_rw = false;
else if (proc_cmdline_key_streq(key, "systemd.image_policy"))
return parse_image_policy_argument(value, &arg_image_policy);
else if (proc_cmdline_key_streq(key, "systemd.image_filter")) {
_cleanup_(image_filter_freep) ImageFilter *f = NULL;
else if (streq(key, "systemd.swap")) {
r = image_filter_parse(value, &f);
if (r < 0)
return log_error_errno(r, "Failed to parse image filter: %s", value);
image_filter_free(arg_image_filter);
arg_image_filter = TAKE_PTR(f);
} else if (streq(key, "systemd.swap")) {
r = value ? parse_boolean(value) : 1;
if (r < 0)
@@ -1014,7 +1287,8 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r;
assert_se(arg_dest = dest_late);
assert_se(arg_dest = dest);
assert_se(arg_dest_late = dest_late);
if (detect_container() > 0) {
log_debug("In a container, exiting.");
@@ -1032,6 +1306,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
r = 0;
RET_GATHER(r, add_root_mount());
RET_GATHER(r, add_usr_mount());
RET_GATHER(r, add_mounts());
return r;

View File

@@ -414,6 +414,7 @@ static int vl_method_mount_image(
&verity,
/* mount_options= */ NULL,
use_policy,
/* image_filter= */ NULL,
dissect_flags,
&di);
if (r == -ENOPKG)
@@ -461,6 +462,12 @@ static int vl_method_mount_image(
if (r < 0)
return r;
r = dissected_image_guess_verity_roothash(
di,
&verity);
if (r < 0)
return r;
r = dissected_image_decrypt(
di,
p.password,

View File

@@ -6370,8 +6370,9 @@ static int run(int argc, char *argv[]) {
r = dissect_loop_device_and_warn(
loop,
&arg_verity_settings,
/* mount_options=*/ NULL,
/* mount_options= */ NULL,
arg_image_policy ?: &image_policy_container,
/* image_filter= */ NULL,
dissect_image_flags,
&dissected_image);
if (r == -ENOPKG) {
@@ -6391,10 +6392,20 @@ static int run(int argc, char *argv[]) {
dissected_image,
loop->fd,
&arg_verity_settings);
if (r < 0)
if (r < 0) {
log_error_errno(r, "Failed to load Verity signature partition: %m");
goto finish;
}
if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
r = dissected_image_guess_verity_roothash(
dissected_image,
&arg_verity_settings);
if (r < 0) {
log_error_errno(r, "Failed to guess Verity root hash: %m");
goto finish;
}
if (dissected_image->has_verity && !arg_verity_settings.root_hash)
log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
"root hash signature found! Proceeding without integrity checking.", arg_image);

View File

@@ -425,6 +425,7 @@ static int portable_extract_by_path(
/* verity= */ NULL,
/* mount_options= */ NULL,
image_policy,
/* image_filter= */ NULL,
flags,
&m);
if (r == -ENOPKG)

View File

@@ -1726,6 +1726,7 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
/* verity= */ NULL,
/* mount_options= */ NULL,
image_policy,
/* image_filter= */ NULL,
flags,
&m);
if (r < 0)

View File

@@ -667,6 +667,18 @@ static int compare_arch(Architecture a, Architecture b) {
return 0;
}
static bool image_filter_test(const ImageFilter *filter, PartitionDesignator d, const char *name) {
assert(d < _PARTITION_DESIGNATOR_MAX);
if (d < 0) /* For unspecified designators we have no filter expression */
return true;
if (!filter || !filter->pattern[d])
return true;
return fnmatch(filter->pattern[d], strempty(name), FNM_NOESCAPE) == 0;
}
static int dissect_image(
DissectedImage *m,
int fd,
@@ -674,6 +686,7 @@ static int dissect_image(
const VeritySettings *verity,
const MountOptions *mount_options,
const ImagePolicy *policy,
const ImageFilter *filter,
DissectImageFlags flags) {
sd_id128_t root_uuid = SD_ID128_NULL, root_verity_uuid = SD_ID128_NULL;
@@ -792,6 +805,9 @@ static int dissect_image(
/* OK, we have found a file system, that's our root partition then. */
if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL)) /* do a filter check with an empty partition label */
return -ECOMM;
r = image_policy_may_use(policy, PARTITION_ROOT);
if (r < 0)
return r;
@@ -1006,6 +1022,9 @@ static int dissect_image(
if (streq_ptr(label, "_empty"))
continue;
if (!image_filter_test(filter, type.designator, label))
continue;
log_debug("Dissecting %s partition with label %s and UUID %s",
strna(partition_designator_to_string(type.designator)), strna(label), SD_ID128_TO_UUID_STRING(id));
@@ -1162,6 +1181,9 @@ static int dissect_image(
/* We don't have a designator for SD_GPT_LINUX_GENERIC so check the UUID instead. */
} else if (sd_id128_equal(type.uuid, SD_GPT_LINUX_GENERIC)) {
if (!image_filter_test(filter, PARTITION_ROOT, label))
continue;
check_partition_flags(node, pflags,
SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
@@ -1307,6 +1329,9 @@ static int dissect_image(
if (pflags != 0x80) /* Bootable flag */
continue;
if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL))
continue;
if (generic_node)
multiple_generic = true;
else {
@@ -1324,6 +1349,9 @@ static int dissect_image(
sd_id128_t id = SD_ID128_NULL;
const char *options = NULL;
if (!image_filter_test(filter, PARTITION_XBOOTLDR, /* label= */ NULL))
continue;
r = image_policy_may_use(policy, PARTITION_XBOOTLDR);
if (r < 0)
return r;
@@ -1487,12 +1515,6 @@ static int dissect_image(
if (verity->designator >= 0 && !m->partitions[verity->designator].found)
return -EADDRNOTAVAIL;
bool have_verity_sig_partition;
if (verity->designator >= 0)
have_verity_sig_partition = m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found;
else
have_verity_sig_partition = m->partitions[PARTITION_USR_VERITY_SIG].found || m->partitions[PARTITION_ROOT_VERITY_SIG].found;
if (verity->root_hash) {
/* If we have an explicit root hash and found the partitions for it, then we are ready to use
* Verity, set things up for it */
@@ -1503,8 +1525,6 @@ static int dissect_image(
/* If we found a verity setup, then the root partition is necessarily read-only. */
m->partitions[PARTITION_ROOT].rw = false;
m->verity_ready = true;
} else {
assert(verity->designator == PARTITION_USR);
@@ -1512,23 +1532,12 @@ static int dissect_image(
return -EADDRNOTAVAIL;
m->partitions[PARTITION_USR].rw = false;
m->verity_ready = true;
}
if (m->verity_ready)
m->verity_sig_ready = verity->root_hash_sig || have_verity_sig_partition;
m->verity_ready = true;
} else if (have_verity_sig_partition) {
/* If we found an embedded signature partition, we are ready, too. */
m->verity_ready = m->verity_sig_ready = true;
if (verity->designator >= 0)
m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR : PARTITION_ROOT].rw = false;
else if (m->partitions[PARTITION_USR_VERITY_SIG].found)
m->partitions[PARTITION_USR].rw = false;
else if (m->partitions[PARTITION_ROOT_VERITY_SIG].found)
m->partitions[PARTITION_ROOT].rw = false;
if (verity->root_hash_sig)
m->verity_sig_ready = true;
}
}
@@ -1538,20 +1547,29 @@ static int dissect_image(
* we don't check encryption requirements here, because we haven't probed the file system yet, hence
* don't know if this is encrypted or not) */
for (PartitionDesignator di = 0; di < _PARTITION_DESIGNATOR_MAX; di++) {
PartitionDesignator vi, si;
PartitionPolicyFlags found_flags;
any = any || m->partitions[di].found;
vi = partition_verity_of(di);
si = partition_verity_sig_of(di);
/* Determine the verity protection level for this partition. */
found_flags = m->partitions[di].found ?
(vi >= 0 && m->partitions[vi].found ?
(si >= 0 && m->partitions[si].found ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY) :
PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED) :
(m->partitions[di].ignored ? PARTITION_POLICY_UNUSED : PARTITION_POLICY_ABSENT);
PartitionPolicyFlags found_flags;
if (m->partitions[di].found) {
found_flags = PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED;
PartitionDesignator vi = partition_verity_of(di);
if (vi >= 0 && m->partitions[vi].found) {
found_flags |= PARTITION_POLICY_VERITY;
PartitionDesignator si = partition_verity_sig_of(di);
if (si >= 0 && m->partitions[si].found)
found_flags |= PARTITION_POLICY_SIGNED;
}
} else
found_flags = m->partitions[di].ignored ? PARTITION_POLICY_UNUSED : PARTITION_POLICY_ABSENT;
if (DEBUG_LOGGING) {
_cleanup_free_ char *s = NULL;
(void) partition_policy_flags_to_string(found_flags, /* simplify= */ false, &s);
log_debug("Found for designator %s: %s", partition_designator_to_string(di), strna(s));
}
r = image_policy_check_protection(policy, di, found_flags);
if (r < 0)
@@ -1580,6 +1598,7 @@ int dissect_image_file(
const VeritySettings *verity,
const MountOptions *mount_options,
const ImagePolicy *image_policy,
const ImageFilter *image_filter,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -1612,7 +1631,7 @@ int dissect_image_file(
if (r < 0)
return r;
r = dissect_image(m, fd, path, verity, mount_options, image_policy, flags);
r = dissect_image(m, fd, path, verity, mount_options, image_policy, image_filter, flags);
if (r < 0)
return r;
@@ -1682,12 +1701,13 @@ int dissect_image_file_and_warn(
const VeritySettings *verity,
const MountOptions *mount_options,
const ImagePolicy *image_policy,
const ImageFilter *image_filter,
DissectImageFlags flags,
DissectedImage **ret) {
return dissect_log_error(
LOG_ERR,
dissect_image_file(path, verity, mount_options, image_policy, flags, ret),
dissect_image_file(path, verity, mount_options, image_policy, image_filter, flags, ret),
path,
verity);
}
@@ -3144,6 +3164,68 @@ int dissected_image_relinquish(DissectedImage *m) {
return 0;
}
void image_filter_done(ImageFilter *f) {
assert(f);
FOREACH_ELEMENT(p, f->pattern)
*p = mfree(*p);
}
ImageFilter *image_filter_free(ImageFilter *f) {
if (!f)
return NULL;
image_filter_done(f);
return mfree(f);
}
int image_filter_parse(const char *s, ImageFilter **ret) {
_cleanup_(image_filter_freep) ImageFilter *f = NULL;
int r;
if (isempty(s)) {
if (ret)
*ret = NULL;
return 0;
}
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&s, &word, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_debug_errno(r, "Failed to extract word: %m");
if (r == 0)
break;
_cleanup_free_ char *designator = NULL, *pattern = NULL;
const char *x = word;
r = extract_many_words(&x, "=", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &designator, &pattern);
if (r < 0)
return log_debug_errno(r, "Failed to extract designator: %m");
if (r != 2 || !isempty(x))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to split: %m");
PartitionDesignator d = partition_designator_from_string(designator);
if (d < 0)
return log_debug_errno(d, "Failed to parse partition designator: %s", designator);
if (!f) {
f = new0(ImageFilter, 1);
if (!f)
return log_oom_debug();
} else if (f->pattern[d])
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate pattern for '%s', refusing.", partition_designator_to_string(d));
f->pattern[d] = TAKE_PTR(pattern);
}
if (ret)
*ret = TAKE_PTR(f);
return 0;
}
static char *build_auxiliary_path(const char *image, const char *suffix) {
const char *e;
char *n;
@@ -3384,20 +3466,52 @@ int verity_settings_load(
return 1;
}
int verity_settings_copy(VeritySettings *dest, const VeritySettings *source) {
assert(dest);
if (!source) {
*dest = VERITY_SETTINGS_DEFAULT;
return 0;
}
_cleanup_free_ void *rh = NULL;
if (source->root_hash_size > 0) {
rh = memdup(source->root_hash, source->root_hash_size);
if (!rh)
return log_oom_debug();
}
_cleanup_free_ void *sig = NULL;
if (source->root_hash_sig_size > 0) {
sig = memdup(source->root_hash_sig, source->root_hash_sig_size);
if (!sig)
return log_oom_debug();
}
_cleanup_free_ char *p = NULL;
if (source->data_path) {
p = strdup(source->data_path);
if (!p)
return log_oom_debug();
}
*dest = (VeritySettings) {
.root_hash = TAKE_PTR(rh),
.root_hash_size = source->root_hash_size,
.root_hash_sig = TAKE_PTR(sig),
.root_hash_sig_size = source->root_hash_sig_size,
.data_path = TAKE_PTR(p),
.designator = source->designator,
};
return 1;
}
int dissected_image_load_verity_sig_partition(
DissectedImage *m,
int fd,
VeritySettings *verity) {
_cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
size_t root_hash_size, root_hash_sig_size;
_cleanup_free_ char *buf = NULL;
PartitionDesignator d;
DissectedPartition *p;
sd_json_variant *rh, *sig;
ssize_t n;
char *e;
int r;
assert(m);
@@ -3413,10 +3527,28 @@ int dissected_image_load_verity_sig_partition(
if (r == 0)
return 0;
d = partition_verity_sig_of(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
assert(d >= 0);
PartitionDesignator dd = verity->designator;
if (dd < 0) {
if (m->partitions[PARTITION_ROOT_VERITY].found)
dd = PARTITION_ROOT;
else if (m->partitions[PARTITION_USR_VERITY].found)
dd = PARTITION_USR;
else
return 0;
}
p = m->partitions + d;
if (!m->partitions[dd].found)
return 0;
PartitionDesignator dv = partition_verity_of(dd);
assert(dv >= 0);
if (!m->partitions[dv].found)
return 0;
PartitionDesignator ds = partition_verity_sig_of(dd);
assert(ds >= 0);
DissectedPartition *p = m->partitions + ds;
if (!p->found)
return 0;
if (p->offset == UINT64_MAX || p->size == UINT64_MAX)
@@ -3425,17 +3557,17 @@ int dissected_image_load_verity_sig_partition(
if (p->size > 4*1024*1024) /* Signature data cannot possible be larger than 4M, refuse that */
return log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "Verity signature partition is larger than 4M, refusing.");
buf = new(char, p->size+1);
_cleanup_free_ char *buf = new(char, p->size+1);
if (!buf)
return -ENOMEM;
n = pread(fd, buf, p->size, p->offset);
ssize_t n = pread(fd, buf, p->size, p->offset);
if (n < 0)
return -ENOMEM;
if ((uint64_t) n != p->size)
return -EIO;
e = memchr(buf, 0, p->size);
const char *e = memchr(buf, 0, p->size);
if (e) {
/* If we found a NUL byte then the rest of the data must be NUL too */
if (!memeqzero(e, p->size - (e - buf)))
@@ -3443,14 +3575,17 @@ int dissected_image_load_verity_sig_partition(
} else
buf[p->size] = 0;
r = sd_json_parse(buf, 0, &v, NULL, NULL);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = sd_json_parse(buf, 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_debug_errno(r, "Failed to parse signature JSON data: %m");
rh = sd_json_variant_by_key(v, "rootHash");
sd_json_variant *rh = sd_json_variant_by_key(v, "rootHash");
if (!rh)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field.");
_cleanup_free_ void *root_hash = NULL;
size_t root_hash_size;
r = sd_json_variant_unhex(rh, &root_hash, &root_hash_size);
if (r < 0)
return log_debug_errno(r, "Failed to parse root hash field: %m");
@@ -3466,10 +3601,12 @@ int dissected_image_load_verity_sig_partition(
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(a), strna(b));
}
sig = sd_json_variant_by_key(v, "signature");
sd_json_variant *sig = sd_json_variant_by_key(v, "signature");
if (!sig)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field.");
_cleanup_free_ void *root_hash_sig = NULL;
size_t root_hash_sig_size;
r = sd_json_variant_unbase64(sig, &root_hash_sig, &root_hash_sig_size);
if (r < 0)
return log_debug_errno(r, "Failed to parse signature field: %m");
@@ -3480,9 +3617,78 @@ int dissected_image_load_verity_sig_partition(
free_and_replace(verity->root_hash_sig, root_hash_sig);
verity->root_hash_sig_size = root_hash_sig_size;
verity->designator = dd;
m->verity_ready = true;
m->verity_sig_ready = true;
m->partitions[dd].rw = false;
return 1;
}
int dissected_image_guess_verity_roothash(
DissectedImage *m,
VeritySettings *verity) {
int r;
assert(m);
assert(verity);
/* Guesses the Verity root hash from the partitions we found, taking into account that as per
* https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ the UUIDS of
* the data and verity partitions are respectively the first and second halves of the dm-verity
* roothash.
*
* Note of course that relying on this guesswork is mostly useful for later attestation, not so much
* for a-priori security. */
if (verity->root_hash) /* Already loaded? */
return 0;
r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_GUESS");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_GUESS, ignoring: %m");
if (r == 0)
return 0;
PartitionDesignator dd = verity->designator;
if (dd < 0) {
if (m->partitions[PARTITION_ROOT_VERITY].found)
dd = PARTITION_ROOT;
else if (m->partitions[PARTITION_USR_VERITY].found)
dd = PARTITION_USR;
else
return 0;
}
DissectedPartition *d = m->partitions + dd;
if (!d->found)
return 0;
PartitionDesignator dv = partition_verity_of(dd);
assert(dv >= 0);
DissectedPartition *p = m->partitions + dv;
if (!p->found)
return 0;
_cleanup_free_ uint8_t *rh = malloc(sizeof(sd_id128_t) * 2);
if (!rh)
return log_oom_debug();
memcpy(mempcpy(rh, &d->uuid, sizeof(sd_id128_t)), &p->uuid, sizeof(sd_id128_t));
verity->root_hash = TAKE_PTR(rh);
verity->root_hash_size = sizeof(sd_id128_t) * 2;
verity->designator = dd;
m->verity_ready = true;
m->partitions[dd].rw = false;
return 0;
}
int dissected_image_acquire_metadata(
DissectedImage *m,
int userns_fd,
@@ -3826,6 +4032,7 @@ int dissect_loop_device(
const VeritySettings *verity,
const MountOptions *mount_options,
const ImagePolicy *image_policy,
const ImageFilter *image_filter,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -3843,7 +4050,15 @@ int dissect_loop_device(
m->image_size = m->loop->device_size;
m->sector_size = m->loop->sector_size;
r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags);
r = dissect_image(
m,
loop->fd,
loop->node,
verity,
mount_options,
image_policy,
image_filter,
flags);
if (r < 0)
return r;
@@ -3861,6 +4076,7 @@ int dissect_loop_device_and_warn(
const VeritySettings *verity,
const MountOptions *mount_options,
const ImagePolicy *image_policy,
const ImageFilter *image_filter,
DissectImageFlags flags,
DissectedImage **ret) {
@@ -3868,7 +4084,7 @@ int dissect_loop_device_and_warn(
return dissect_log_error(
LOG_ERR,
dissect_loop_device(loop, verity, mount_options, image_policy, flags, ret),
dissect_loop_device(loop, verity, mount_options, image_policy, image_filter, flags, ret),
loop->backing_file ?: loop->node,
verity);
}
@@ -3987,6 +4203,7 @@ int mount_image_privately_interactively(
&verity,
/* mount_options= */ NULL,
image_policy,
/* image_filter= */ NULL,
flags,
&dissected_image);
if (r < 0)
@@ -3996,6 +4213,10 @@ int mount_image_privately_interactively(
if (r < 0)
return r;
r = dissected_image_guess_verity_roothash(dissected_image, &verity);
if (r < 0)
return r;
r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags);
if (r < 0)
return r;
@@ -4064,11 +4285,8 @@ int verity_dissect_and_mount(
const char *dest,
const MountOptions *options,
const ImagePolicy *image_policy,
const char *required_host_os_release_id,
const char *required_host_os_release_version_id,
const char *required_host_os_release_sysext_level,
const char *required_host_os_release_confext_level,
const char *required_sysext_scope,
const ImageFilter *image_filter,
const ExtensionReleaseData *extension_release_data,
VeritySettings *verity,
DissectedImage **ret_image) {
@@ -4082,7 +4300,7 @@ int verity_dissect_and_mount(
assert(src);
/* Verifying release metadata requires mounted image for now, so ensure the check is skipped when
* opening an image without mounting it immediately (i.e.: 'dest' is NULL). */
assert(!required_host_os_release_id || dest);
assert(!extension_release_data || dest);
relax_extension_release_check = mount_options_relax_extension_release_checks(options);
@@ -4121,6 +4339,7 @@ int verity_dissect_and_mount(
verity,
options,
image_policy,
image_filter,
dissect_image_flags,
&dissected_image);
/* No partition table? Might be a single-filesystem image, try again */
@@ -4130,6 +4349,7 @@ int verity_dissect_and_mount(
verity,
options,
image_policy,
image_filter,
dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
&dissected_image);
if (r < 0)
@@ -4139,6 +4359,10 @@ int verity_dissect_and_mount(
if (r < 0)
return r;
r = dissected_image_guess_verity_roothash(dissected_image, verity);
if (r < 0)
return r;
r = dissected_image_decrypt(
dissected_image,
NULL,
@@ -4175,11 +4399,11 @@ int verity_dissect_and_mount(
* First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
* available, or else fallback to VERSION_ID. If neither is present (eg: rolling release),
* then a simple match on the ID will be performed. */
if (required_host_os_release_id) {
if (extension_release_data && extension_release_data->os_release_id) {
_cleanup_strv_free_ char **extension_release = NULL;
ImageClass class = IMAGE_SYSEXT;
assert(!isempty(required_host_os_release_id));
assert(!isempty(extension_release_data->os_release_id));
r = load_extension_release_pairs(dest, IMAGE_SYSEXT, dissected_image->image_name, relax_extension_release_check, &extension_release);
if (r == -ENOENT) {
@@ -4192,10 +4416,10 @@ int verity_dissect_and_mount(
r = extension_release_validate(
dissected_image->image_name,
required_host_os_release_id,
required_host_os_release_version_id,
class == IMAGE_SYSEXT ? required_host_os_release_sysext_level : required_host_os_release_confext_level,
required_sysext_scope,
extension_release_data->os_release_id,
extension_release_data->os_release_version_id,
class == IMAGE_SYSEXT ? extension_release_data->os_release_sysext_level : extension_release_data->os_release_confext_level,
extension_release_data->os_release_extension_scope,
extension_release,
class);
if (r == 0)
@@ -4214,6 +4438,16 @@ int verity_dissect_and_mount(
return 0;
}
void extension_release_data_done(ExtensionReleaseData *data) {
assert(data);
data->os_release_id = mfree(data->os_release_id);
data->os_release_version_id = mfree(data->os_release_version_id);
data->os_release_sysext_level = mfree(data->os_release_sysext_level);
data->os_release_confext_level = mfree(data->os_release_confext_level);
data->os_release_extension_scope = mfree(data->os_release_extension_scope);
}
int get_common_dissect_directory(char **ret) {
_cleanup_free_ char *t = NULL;
int r;

View File

@@ -19,6 +19,8 @@ typedef struct DissectedPartition DissectedPartition;
typedef struct DecryptedImage DecryptedImage;
typedef struct MountOptions MountOptions;
typedef struct VeritySettings VeritySettings;
typedef struct ImageFilter ImageFilter;
typedef struct ExtensionReleaseData ExtensionReleaseData;
struct DissectedPartition {
bool found:1;
@@ -144,10 +146,23 @@ struct VeritySettings {
PartitionDesignator designator;
};
#define VERITY_SETTINGS_DEFAULT { \
#define VERITY_SETTINGS_DEFAULT (VeritySettings) { \
.designator = _PARTITION_DESIGNATOR_INVALID \
}
struct ImageFilter {
/* A per designator glob matching against the partition label */
char *pattern[_PARTITION_DESIGNATOR_MAX];
};
struct ExtensionReleaseData {
char *os_release_id;
char *os_release_version_id;
char *os_release_sysext_level;
char *os_release_confext_level;
char *os_release_extension_scope;
};
/* We include image-policy.h down here, since ImagePolicy wants a complete definition of PartitionDesignator first. */
#include "image-policy.h"
@@ -161,10 +176,10 @@ static inline int probe_filesystem(const char *path, char **ret_fstype) {
}
int dissect_log_error(int log_level, int r, const char *name, const VeritySettings *verity);
int dissect_image_file(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
int dissect_image_file_and_warn(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, DissectImageFlags flags, DissectedImage **ret);
int dissect_image_file(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
int dissect_image_file_and_warn(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
void dissected_image_close(DissectedImage *m);
DissectedImage* dissected_image_unref(DissectedImage *m);
@@ -201,6 +216,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
int dissected_image_relinquish(DissectedImage *m);
void image_filter_done(ImageFilter *f);
ImageFilter *image_filter_free(ImageFilter *f);
DEFINE_TRIVIAL_CLEANUP_FUNC(ImageFilter*, image_filter_free);
int image_filter_parse(const char *s, ImageFilter **ret);
int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
static inline bool verity_settings_set(const VeritySettings *settings) {
@@ -226,7 +246,10 @@ static inline bool verity_settings_data_covers(const VeritySettings *verity, Par
verity->data_path;
}
int verity_settings_copy(VeritySettings *dest, const VeritySettings *source);
int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
int dissected_image_guess_verity_roothash(DissectedImage *m, VeritySettings *verity);
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator d);
@@ -234,7 +257,7 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
int mount_image_privately_interactively(const char *path, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_host_os_release_confext_level, const char *required_sysext_scope, VeritySettings *verity, DissectedImage **ret_image);
int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const ImageFilter *image_filter, const ExtensionReleaseData *required_release_data, VeritySettings *verity, DissectedImage **ret_image);
int dissect_fstype_ok(const char *fstype);
@@ -243,6 +266,8 @@ int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret);
int partition_pick_mount_options(PartitionDesignator d, const char *fstype, bool rw, bool discard, char **ret_options, unsigned long *ret_ms_flags);
void extension_release_data_done(ExtensionReleaseData *data);
static inline const char* dissected_partition_fstype(const DissectedPartition *m) {
assert(m);

View File

@@ -216,8 +216,8 @@ static int write_fsck_sysroot_service(
/* Writes out special versions of systemd-fsck-root.service and systemd-fsck-usr.service for use in
* the initrd. The regular statically shipped versions of these unit files use / and /usr for as
* paths, which doesn't match what we need for the initrd (where the dirs are /sysroot +
* /sysusr/usr), hence we overwrite those versions here. */
* paths, which doesn't match what we need for the initrd (where the dirs are /sysroot/ +
* /sysusr/usr/), hence we overwrite those versions here. */
escaped = specifier_escape(what);
if (!escaped)
@@ -281,7 +281,7 @@ int generator_write_fsck_deps(
assert(where);
/* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid
* generating confusing log messages */
* generating confusing log messages. */
if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) {
log_debug("Skipping fsck for %s in initrd.", where);
return 0;
@@ -1026,9 +1026,11 @@ int generator_write_veritysetup_service_section(
if (!hash_what_escaped)
return log_oom();
roothash_escaped = specifier_escape(roothash);
if (!roothash_escaped)
return log_oom();
if (roothash) {
roothash_escaped = specifier_escape(roothash);
if (!roothash_escaped)
return log_oom();
}
if (options) {
options_escaped = specifier_escape(options);
@@ -1043,7 +1045,7 @@ int generator_write_veritysetup_service_section(
"RemainAfterExit=yes\n"
"ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
"ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
name_escaped, data_what_escaped, hash_what_escaped, roothash_escaped, strempty(options_escaped),
name_escaped, data_what_escaped, hash_what_escaped, empty_to_dash(roothash_escaped), strempty(options_escaped),
name_escaped);
return 0;
@@ -1086,21 +1088,39 @@ bool generator_soft_rebooted(void) {
return (cached = (u > 0));
}
GptAutoRoot parse_gpt_auto_root(const char *value) {
GptAutoRoot parse_gpt_auto_root(const char *switch_name, const char *value) {
assert(switch_name);
assert(value);
/* Parses the 'gpt-auto'/'gpt-auto-root' parameters to root= */
/* Parses the 'gpt-auto'/'gpt-auto-root'/'dissect'/'dissect-force' parameters to root=
*
* note that we are not using a regular string table here, because the mode names don't fully match
* the parameter names. And root= being something else is not an error. */
if (streq(value, "gpt-auto")) {
log_debug("Enabling root partition auto-detection (respecting factory reset mode), root= is explicitly set to 'gpt-auto'.");
log_debug("Enabling partition auto-detection (respecting factory reset mode), %s is explicitly set to 'gpt-auto'.", switch_name);
return GPT_AUTO_ROOT_ON;
}
if (streq(value, "gpt-auto-force")) {
log_debug("Enabling root partition auto-detection (ignoring factory reset mode), root= is explicitly set to 'gpt-auto-force'.");
log_debug("Enabling partition auto-detection (ignoring factory reset mode), %s is explicitly set to 'gpt-auto-force'.", switch_name);
return GPT_AUTO_ROOT_FORCE;
}
log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto' or 'gpt-auto-force'.");
if (streq(value, "dissect")) {
log_debug("Enabling partition auto-detection via full image dissection (respecting factory reset mode), %s is explicitly set to 'dissect'.", switch_name);
return GPT_AUTO_ROOT_DISSECT;
}
if (streq(value, "dissect-force")) {
log_debug("Enabling partition auto-detection via full image dissection (ignoring factory reset mode), %s is explicitly set to 'dissect-force'.", switch_name);
return GPT_AUTO_ROOT_DISSECT_FORCE;
}
if (streq(value, "off"))
log_debug("Disabling partition auto-detection, %s handling is explicitly turned off.", switch_name);
else
log_debug("Disabling partition auto-detection, %s is neither unset, nor set to 'gpt-auto', 'gpt-auto-force', 'dissect' or 'dissect-force'.", switch_name);
return GPT_AUTO_ROOT_OFF;
}

View File

@@ -122,11 +122,13 @@ bool generator_soft_rebooted(void);
exit_failure_if_negative)
typedef enum GptAutoRoot {
GPT_AUTO_ROOT_OFF = 0, /* root= set to something else */
GPT_AUTO_ROOT_ON, /* root= set explicitly to "gpt-auto" */
GPT_AUTO_ROOT_FORCE, /* root= set explicitly to "gpt-auto-force" → ignores factory reset mode */
GPT_AUTO_ROOT_OFF = 0, /* root= set to something else */
GPT_AUTO_ROOT_ON, /* root= set explicitly to "gpt-auto" */
GPT_AUTO_ROOT_FORCE, /* root= set explicitly to "gpt-auto-force" → ignores factory reset mode */
GPT_AUTO_ROOT_DISSECT, /* root= set to "dissect" */
GPT_AUTO_ROOT_DISSECT_FORCE, /* root= set to "dissect-force" → ignores factory reset mode */
_GPT_AUTO_ROOT_MAX,
_GPT_AUTO_ROOT_INVALID = -EINVAL,
} GptAutoRoot;
GptAutoRoot parse_gpt_auto_root(const char *value);
GptAutoRoot parse_gpt_auto_root(const char *switch_name, const char *value);

View File

@@ -969,11 +969,8 @@ static int mount_in_namespace_legacy(
mount_tmp,
options,
image_policy,
/* required_host_os_release_id= */ NULL,
/* required_host_os_release_version_id= */ NULL,
/* required_host_os_release_sysext_level= */ NULL,
/* required_host_os_release_confext_level= */ NULL,
/* required_sysext_scope= */ NULL,
/* image_filter= */ NULL,
/* extension_release_data= */ NULL,
/* verity= */ NULL,
/* ret_image= */ NULL);
else
@@ -1193,11 +1190,8 @@ static int mount_in_namespace(
/* dest= */ NULL,
options,
image_policy,
/* required_host_os_release_id= */ NULL,
/* required_host_os_release_version_id= */ NULL,
/* required_host_os_release_sysext_level= */ NULL,
/* required_host_os_release_confext_level= */ NULL,
/* required_sysext_scope= */ NULL,
/* image_filter= */ NULL,
/* extension_release_data= */ NULL,
/* verity= */ NULL,
&img);
if (r < 0)

View File

@@ -1802,6 +1802,7 @@ static int merge_subprocess(
&verity_settings,
/* mount_options= */ NULL,
pick_image_policy(img),
/* image_filter= */ NULL,
flags,
&m);
if (r < 0)
@@ -1814,6 +1815,12 @@ static int merge_subprocess(
if (r < 0)
return r;
r = dissected_image_guess_verity_roothash(
m,
&verity_settings);
if (r < 0)
return r;
r = dissected_image_decrypt_interactively(
m, NULL,
&verity_settings,

View File

@@ -106,6 +106,7 @@ simple_tests += files(
'test-hostname-setup.c',
'test-hostname-util.c',
'test-id128.c',
'test-image-filter.c',
'test-image-policy.c',
'test-import-util.c',
'test-in-addr-prefix-util.c',

View File

@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "dissect-image.h"
#include "tests.h"
TEST(image_filter) {
_cleanup_(image_filter_freep) ImageFilter *f = NULL;
ASSERT_OK(image_filter_parse(NULL, &f));
ASSERT_NULL(f);
ASSERT_OK(image_filter_parse("", &f));
ASSERT_NULL(f);
ASSERT_OK(image_filter_parse("root=*", &f));
ASSERT_NOT_NULL(f);
ASSERT_STREQ(f->pattern[PARTITION_ROOT], "*");
f = image_filter_free(f);
ASSERT_OK(image_filter_parse("usr=foox?:root=kn*arz", &f));
ASSERT_NOT_NULL(f);
ASSERT_STREQ(f->pattern[PARTITION_ROOT], "kn*arz");
ASSERT_STREQ(f->pattern[PARTITION_USR], "foox?");
f = image_filter_free(f);
ASSERT_OK(image_filter_parse("usr=foox?:root=kn*arz:home=wumpi", &f));
ASSERT_NOT_NULL(f);
ASSERT_STREQ(f->pattern[PARTITION_ROOT], "kn*arz");
ASSERT_STREQ(f->pattern[PARTITION_USR], "foox?");
ASSERT_STREQ(f->pattern[PARTITION_HOME], "wumpi");
f = image_filter_free(f);
ASSERT_ERROR(image_filter_parse("usr=foox?:root=kn*arz:home=wumpi:schlumpf=smurf", &f), EINVAL);
ASSERT_ERROR(image_filter_parse(":", &f), EINVAL);
ASSERT_ERROR(image_filter_parse("::", &f), EINVAL);
ASSERT_ERROR(image_filter_parse("-", &f), EINVAL);
ASSERT_ERROR(image_filter_parse("root=knuff:root=knuff", &f), EINVAL);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@@ -81,7 +81,14 @@ static void* thread_func(void *ptr) {
log_notice("Acquired loop device %s, will mount on %s", loop->node, mounted);
r = dissect_loop_device(loop, NULL, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected);
r = dissect_loop_device(
loop,
/* verity= */ NULL,
/* mount_options= */ NULL,
/* image_policy= */ NULL,
/* image_filter= */ NULL,
DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES,
&dissected);
if (r < 0)
log_error_errno(r, "Failed dissect loopback device %s: %m", loop->node);
assert_se(r >= 0);
@@ -218,7 +225,14 @@ static int run(int argc, char *argv[]) {
sfdisk = NULL;
#if HAVE_BLKID
assert_se(dissect_image_file(p, NULL, NULL, NULL, 0, &dissected) >= 0);
assert_se(dissect_image_file(
p,
/* verity= */ NULL,
/* mount_options= */ NULL,
/* image_policy= */ NULL,
/* image_filter= */ NULL,
/* flags= */ 0,
&dissected) >= 0);
verify_dissected_image(dissected);
dissected = dissected_image_unref(dissected);
#endif
@@ -232,7 +246,14 @@ static int run(int argc, char *argv[]) {
assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, LO_FLAGS_PARTSCAN, LOCK_EX, &loop) >= 0);
#if HAVE_BLKID
assert_se(dissect_loop_device(loop, NULL, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
assert_se(dissect_loop_device(
loop,
/* verity= */ NULL,
/* mount_options= */ NULL,
/* image_policy= */ NULL,
/* image_filter= */ NULL,
DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES,
&dissected) >= 0);
verify_dissected_image(dissected);
FOREACH_STRING(fs, "vfat", "ext4") {
@@ -268,12 +289,26 @@ static int run(int argc, char *argv[]) {
/* Try to read once, without pinning or adding partitions, i.e. by only accessing the whole block
* device. */
assert_se(dissect_loop_device(loop, NULL, NULL, NULL, 0, &dissected) >= 0);
assert_se(dissect_loop_device(
loop,
/* verity= */ NULL,
/* mount_options= */ NULL,
/* image_policy= */ NULL,
/* image_filter= */ NULL,
/* flags= */ 0,
&dissected) >= 0);
verify_dissected_image_harder(dissected);
dissected = dissected_image_unref(dissected);
/* Now go via the loopback device after all, but this time add/pin, because now we want to mount it. */
assert_se(dissect_loop_device(loop, NULL, NULL, NULL, DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES, &dissected) >= 0);
assert_se(dissect_loop_device(
loop,
/* verity= */ NULL,
/* mount_options= */ NULL,
/* image_policy= */ NULL,
/* image_filter= */ NULL,
DISSECT_IMAGE_ADD_PARTITION_DEVICES|DISSECT_IMAGE_PIN_PARTITION_DEVICES,
&dissected) >= 0);
verify_dissected_image_harder(dissected);
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);

View File

@@ -22,6 +22,7 @@ libudevd_core_sources = files(
'net/link-config.c',
'udev-builtin.c',
'udev-builtin-btrfs.c',
'udev-builtin-dissect_image.c',
'udev-builtin-factory_reset.c',
'udev-builtin-hwdb.c',
'udev-builtin-input_id.c',

View File

@@ -0,0 +1,412 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "blockdev-util.h"
#include "device-util.h"
#include "dissect-image.h"
#include "fd-util.h"
#include "hexdecoct.h"
#include "image-policy.h"
#include "initrd-util.h"
#include "loop-util.h"
#include "proc-cmdline.h"
#include "udev-builtin.h"
static ImagePolicy *arg_image_policy = NULL;
static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
static ImageFilter *arg_image_filter = NULL;
static int acquire_image_policy(ImagePolicy **ret) {
int r;
assert(ret);
_cleanup_free_ char *value = NULL;
r = proc_cmdline_get_key("systemd.image_policy", /* flags= */ 0, &value);
if (r < 0)
return log_error_errno(r, "Failed to read systemd.image_policy= kernel command line switch: %m");
if (r == 0) {
*ret = NULL;
return 0;
}
r = image_policy_from_string(value, ret);
if (r < 0)
return log_error_errno(r, "Failed to parse image policy '%s': %m", value);
if (DEBUG_LOGGING) {
_cleanup_free_ char *s = NULL;
(void) image_policy_to_string(*ret, /* simplify= */ true, &s);
log_debug("Loaded image policy: %s", strna(s));
}
return 1;
}
static int acquire_verity_settings(VeritySettings *ret) {
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
int r;
assert(ret);
_cleanup_free_ char *h = NULL;
r = proc_cmdline_get_key("roothash", /* flags= */ 0, &h);
if (r < 0)
return log_error_errno(r, "Failed to read roothash= kernel command line switch: %m");
if (r > 0)
verity.designator = PARTITION_ROOT;
_cleanup_free_ char *uh = NULL;
r = proc_cmdline_get_key("usrhash", /* flags= */ 0, &uh);
if (r < 0)
return log_error_errno(r, "Failed to read usrhash= kernel command line switch: %m");
if (r > 0) {
if (h)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both roothash= and usrhash= specified, refusing.");
h = TAKE_PTR(uh);
verity.designator = PARTITION_USR;
}
if (h) {
r = unhexmem(h, &verity.root_hash, &verity.root_hash_size);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash from kernel command line switch: %m");
}
*ret = TAKE_GENERIC(verity, VeritySettings, VERITY_SETTINGS_DEFAULT);
return 0;
}
static int acquire_image_filter(ImageFilter **ret) {
int r;
assert(ret);
_cleanup_free_ char *value = NULL;
r = proc_cmdline_get_key("systemd.image_filter", /* flags= */ 0, &value);
if (r < 0)
return log_error_errno(r, "Failed to read systemd.image_filter= kernel command line switch: %m");
if (r == 0) {
*ret = NULL;
return 0;
}
r = image_filter_parse(value, ret);
if (r < 0)
return log_error_errno(r, "Failed to parse image filter '%s': %m", value);
return 1;
}
static int verb_probe(UdevEvent *event, sd_device *dev) {
int r;
assert(event);
assert(dev);
/* This is invoked on 'main' block devices to probe the partition table. We will generate some
* properties with general image information, and then a bunch of properties for each partition, with
* the partition index in the variable name. These fields will be copied into partition block devices
* when the dissect_image builtin is later called with the "copy" verb, i.e. in verb_copy() below. */
const char *devnode;
r = sd_device_get_devname(dev, &devnode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device node: %m");
if (block_device_is_whole_disk(dev) <= 0) {
log_device_debug(dev, "Must be invoked on whole block device (was invoked in '%s), ignoring.", devnode);
return 0;
}
r = blockdev_partscan_enabled(dev);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to determine if block device '%s' supports partitions: %m", devnode);
if (r == 0) {
log_device_debug(dev, "Invoked on block device '%s' that lacks partition scanning, ignoring.", devnode);
return 0;
}
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
r = loop_device_open(dev, O_RDONLY, LOCK_SH, &loop);
if (ERRNO_IS_NEG_DEVICE_ABSENT(r)) {
log_device_debug(dev, "Device absent while opening block device '%s', ignoring.", devnode);
return 0;
}
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to open block device '%s: %m", devnode);
const ImagePolicy *image_policy = arg_image_policy ?: &image_policy_host;
_cleanup_(dissected_image_unrefp) DissectedImage *image = NULL;
r = dissect_loop_device(
loop,
&arg_verity_settings,
/* mount_options= */ NULL,
image_policy,
arg_image_filter,
DISSECT_IMAGE_READ_ONLY|
DISSECT_IMAGE_GPT_ONLY|
DISSECT_IMAGE_USR_NO_ROOT|
DISSECT_IMAGE_ALLOW_EMPTY,
&image);
if (r == -ERFKILL && !in_initrd()) {
/* If we transitioned into the main system and we couldn't dissect the image with the full
* policy, let's see if it works if we set the policies for /usr/ and the root fs out of the
* policy. After all, we already made our choices, there's no point in insisting on the
* policy here. */
static const PartitionDesignator ignore_designators[] = {
PARTITION_ROOT,
PARTITION_ROOT_VERITY,
PARTITION_ROOT_VERITY_SIG,
PARTITION_USR,
PARTITION_USR_VERITY,
PARTITION_USR_VERITY_SIG,
};
_cleanup_(image_policy_freep) ImagePolicy *image_policy_mangled = NULL;
r = image_policy_ignore_designators(
image_policy,
ignore_designators,
ELEMENTSOF(ignore_designators),
&image_policy_mangled);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to remove root/usr partitions from image policy: %m");
if (image_policy_equal(image_policy, image_policy_mangled))
r = -ERFKILL; /* restore original error, if this didn't change anything */
else {
if (DEBUG_LOGGING) {
_cleanup_free_ char *a = NULL, *b = NULL;
(void) image_policy_to_string(image_policy, /* simplify= */ false, &a);
(void) image_policy_to_string(image_policy_mangled, /* simplify= */ false, &b);
log_device_debug_errno(dev, ERFKILL, "Couldn't dissect block device with regular policy '%s', retrying with policy where root/usr are set to ignore '%s'.", a, b);
}
r = dissect_loop_device(
loop,
&arg_verity_settings,
/* mount_options= */ NULL,
image_policy_mangled,
arg_image_filter,
DISSECT_IMAGE_READ_ONLY|
DISSECT_IMAGE_GPT_ONLY|
DISSECT_IMAGE_USR_NO_ROOT|
DISSECT_IMAGE_ALLOW_EMPTY,
&image);
}
}
if (IN_SET(r, -ENOPKG, -ENOMSG, -ENXIO, -ENOTUNIQ)) {
log_device_debug_errno(dev, r, "Device does not carry a GPT disk label with suitable partitions, ignoring.");
return 0;
}
if (r == -ERFKILL) {
log_device_debug_errno(dev, r, "Device carries GPT disk label that doesn't match our image policy, ignoring.");
return 0;
}
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to dissect disk image: %m");
/* Let's try to load verity data from the image now, so that we can attach it to the device via udev
* properties */
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
r = verity_settings_copy(&verity, &arg_verity_settings);
if (r < 0)
return r;
r = dissected_image_load_verity_sig_partition(image, loop->fd, &verity);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to load verity signature data from image: %m");
r = dissected_image_guess_verity_roothash(image, &verity);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to guess root hash from image: %m");
/* Marker that we determined this to be a suitable image */
(void) udev_builtin_add_property(event, "ID_DISSECT_IMAGE", "1");
/* Output the primary architecture this image is intended for */
Architecture a = dissected_image_architecture(image);
if (a >= 0)
(void) udev_builtin_add_property(event, "ID_DISSECT_IMAGE_ARCHITECTURE", architecture_to_string(a));
/* And now output the intended designator and architecture (if it applies) for all partitions we
* found and think belong to this system */
FOREACH_ELEMENT(p, image->partitions) {
PartitionDesignator d = p - image->partitions;
if (!p->found)
continue;
assert(p->partno > 0);
/* Indicate designator for partition */
_cleanup_free_ char *df = NULL;
if (asprintf(&df, "ID_DISSECT_PART%i_DESIGNATOR", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_property(event, df, partition_designator_to_string(d));
if (p->architecture >= 0) {
_cleanup_free_ char *f = NULL;
if (asprintf(&f, "ID_DISSECT_PART%i_ARCHITECTURE", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_property(event, f, architecture_to_string(p->architecture));
}
/* Indicate whether this partition has verity protection */
PartitionDesignator dv = partition_verity_of(d);
if (dv >= 0 && image->partitions[dv].found) {
_cleanup_free_ char *f = NULL;
if (asprintf(&f, "ID_DISSECT_PART%i_HAS_VERITY", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_property(event, f, "1");
}
dv = partition_verity_sig_of(d);
if (dv >= 0 && image->partitions[dv].found) {
_cleanup_free_ char *f = NULL;
if (asprintf(&f, "ID_DISSECT_PART%i_HAS_VERITY_SIG", p->partno) < 0)
return log_oom_debug();
(void) udev_builtin_add_property(event, f, "1");
}
if (d == verity.designator) {
if (verity.root_hash_size > 0) {
_cleanup_free_ char *f = NULL;
if (asprintf(&f, "ID_DISSECT_PART%i_ROOTHASH", p->partno) < 0)
return log_oom_debug();
_cleanup_free_ char *h = hexmem(verity.root_hash, verity.root_hash_size);
if (!h)
return log_oom_debug();
(void) udev_builtin_add_property(event, f, h);
}
if (verity.root_hash_sig_size > 0) {
_cleanup_free_ char *f = NULL;
if (asprintf(&f, "ID_DISSECT_PART%i_ROOTHASH_SIG", p->partno) < 0)
return log_oom_debug();
_cleanup_free_ char *h = NULL;
if (base64mem(verity.root_hash_sig, verity.root_hash_sig_size, &h) < 0)
return log_oom_debug();
(void) udev_builtin_add_property(event, f, h);
}
}
}
return 0;
}
static int verb_copy(UdevEvent *event, sd_device *dev) {
int r;
assert(event);
assert(dev);
/* This is called for the partition block devices, and will copy the per-partition properties we
* probed on the main block device into the partition device */
const char *devnode;
r = sd_device_get_devname(dev, &devnode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device node: %m");
if (!device_in_subsystem(dev, "block"))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invoked on non-block device '%s', refusing: %m", devnode);
if (!device_is_devtype(dev, "partition"))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invoked on non-partition block device '%s', refusing: %m", devnode);
sd_device *parent;
r = sd_device_get_parent(dev, &parent);
if (r < 0)
return log_error_errno(r, "Failed to get parent of device '%s': %m", devnode);
if (!device_in_subsystem(parent, "block"))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Parent of block device '%s' is not a block device, refusing: %m", devnode);
if (!device_is_devtype(parent, "disk"))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Parent of block device '%s' is not a whole block device, refusing: %m", devnode);
const char *partn;
r = sd_device_get_property_value(dev, "PARTN", &partn);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get partition number of partition block device '%s': %m", devnode);
FOREACH_STRING(f, "_DESIGNATOR", "_ARCHITECTURE", "_HAS_VERITY", "_HAS_VERITY_SIG", "_ROOTHASH", "_ROOTHASH_SIG") {
/* The property on the parent device contains the partition number */
_cleanup_free_ char *p = strjoin("ID_DISSECT_PART", partn, f);
if (!p)
return log_oom_debug();
const char *v;
r = sd_device_get_property_value(parent, p, &v);
if (r == -ENOENT)
continue;
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get '%s' property of parent of '%s': %m", p, devnode);
/* When we copy this property to the partition we drop the partition number, so that we have
* a constant field name */
_cleanup_free_ char *c = strjoin("ID_DISSECT_PART", f);
if (!c)
return log_oom_debug();
(void) udev_builtin_add_property(event, c, v);
}
return 0;
}
static int builtin_dissect_image(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
if (argc != 2)
return log_device_warning_errno(
dev, SYNTHETIC_ERRNO(EINVAL), "%s: expected single argument.", argv[0]);
if (streq(argv[1], "probe"))
return verb_probe(event, dev);
if (streq(argv[1], "copy"))
return verb_copy(event, dev);
return log_device_warning_errno(
dev, SYNTHETIC_ERRNO(EINVAL), "%s: unknown verb '%s'", argv[0], argv[1]);
}
static int builtin_dissect_image_init(void) {
int r;
r = acquire_image_policy(&arg_image_policy);
if (r < 0)
return r;
r = acquire_verity_settings(&arg_verity_settings);
if (r < 0)
return r;
r = acquire_image_filter(&arg_image_filter);
if (r < 0)
return r;
return 0;
}
static void builtin_dissect_image_exit(void) {
arg_image_policy = image_policy_free(arg_image_policy);
verity_settings_done(&arg_verity_settings);
arg_image_filter = image_filter_free(arg_image_filter);
}
const UdevBuiltin udev_builtin_dissect_image = {
.name = "dissect_image",
.cmd = builtin_dissect_image,
.init = builtin_dissect_image_init,
.exit = builtin_dissect_image_exit,
.help = "Dissect Disk Images",
.run_once = true,
};

View File

@@ -15,6 +15,7 @@ static const UdevBuiltin *const builtins[_UDEV_BUILTIN_MAX] = {
[UDEV_BUILTIN_BLKID] = &udev_builtin_blkid,
#endif
[UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs,
[UDEV_BUILTIN_DISSECT_IMAGE] = &udev_builtin_dissect_image,
[UDEV_BUILTIN_FACTORY_RESET] = &udev_builtin_factory_reset,
[UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb,
[UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id,

View File

@@ -37,6 +37,7 @@ typedef struct UdevBuiltin {
extern const UdevBuiltin udev_builtin_blkid;
#endif
extern const UdevBuiltin udev_builtin_btrfs;
extern const UdevBuiltin udev_builtin_dissect_image;
extern const UdevBuiltin udev_builtin_factory_reset;
extern const UdevBuiltin udev_builtin_hwdb;
extern const UdevBuiltin udev_builtin_input_id;

View File

@@ -40,6 +40,7 @@ typedef enum UdevBuiltinCommand {
UDEV_BUILTIN_BLKID,
#endif
UDEV_BUILTIN_BTRFS,
UDEV_BUILTIN_DISSECT_IMAGE,
UDEV_BUILTIN_FACTORY_RESET,
UDEV_BUILTIN_HWDB,
UDEV_BUILTIN_INPUT_ID,
@@ -64,6 +65,7 @@ typedef enum UdevReloadFlags {
UDEV_RELOAD_BUILTIN_BLKID = 1u << UDEV_BUILTIN_BLKID,
#endif
UDEV_RELOAD_BUILTIN_BTRFS = 1u << UDEV_BUILTIN_BTRFS,
UDEV_RELOAD_BUILTIN_DISSECT_IMAGE = 1u << UDEV_BUILTIN_DISSECT_IMAGE,
UDEV_RELOAD_BUILTIN_FACTORY_RESET = 1u << UDEV_BUILTIN_FACTORY_RESET,
UDEV_RELOAD_BUILTIN_HWDB = 1u << UDEV_BUILTIN_HWDB,
UDEV_RELOAD_BUILTIN_INPUT_ID = 1u << UDEV_BUILTIN_INPUT_ID,

View File

@@ -4,6 +4,8 @@
#include <stdio.h>
#include <sys/stat.h>
#include "sd-device.h"
#include "alloc-util.h"
#include "cryptsetup-util.h"
#include "fileio.h"
@@ -19,7 +21,7 @@
#include "terminal-util.h"
#include "verbs.h"
static char *arg_hash = NULL;
static char *arg_hash = NULL; /* the hash algorithm */
static bool arg_superblock = true;
static int arg_format = 1;
static uint64_t arg_data_block_size = 4096;
@@ -33,7 +35,9 @@ static uint32_t arg_activate_flags = CRYPT_ACTIVATE_READONLY;
static char *arg_fec_what = NULL;
static uint64_t arg_fec_offset = 0;
static uint64_t arg_fec_roots = 2;
static char *arg_root_hash_signature = NULL;
static void *arg_root_hash_signature = NULL;
static size_t arg_root_hash_signature_size = 0;
static bool arg_root_hash_signature_auto = false;
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
STATIC_DESTRUCTOR_REGISTER(arg_salt, freep);
@@ -60,26 +64,52 @@ static int help(void) {
return 0;
}
static int save_roothashsig_option(const char *option, bool strict) {
static int parse_roothashsig_option(const char *option, bool strict) {
_cleanup_free_ void *rhs = NULL;
size_t rhss = 0;
bool set_auto = false;
int r;
if (path_is_absolute(option) || startswith(option, "base64:")) {
if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
assert(option);
r = free_and_strdup_warn(&arg_root_hash_signature, option);
const char *value = startswith(option, "base64:");
if (value) {
r = unbase64mem(value, &rhs, &rhss);
if (r < 0)
return r;
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", option);
return true;
}
} else if (path_is_absolute(option)) {
r = read_full_file_full(
AT_FDCWD,
option,
/* offset= */ UINT64_MAX,
/* size= */ SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
/* bind_name= */ NULL,
(char**) &rhs,
&rhss);
if (r < 0)
return log_error_errno(r, "Failed to read root hash signature: %m");
if (!strict)
} else if (streq(option, "auto"))
/* auto → Derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
set_auto = true;
else if (strict)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"root-hash-signature= expects either full path to signature file or "
"base64 string encoding signature prefixed by base64:.");
else
return false;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"root-hash-signature= expects either full path to signature file or "
"base64 string encoding signature prefixed by base64:.");
if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
free_and_replace(arg_root_hash_signature, rhs);
arg_root_hash_signature_size = rhss;
arg_root_hash_signature_auto = set_auto;
return true;
}
static int parse_block_size(const char *t, uint64_t *size) {
@@ -105,7 +135,7 @@ static int parse_options(const char *options) {
int r;
/* backward compatibility with the obsolete ROOTHASHSIG positional argument */
r = save_roothashsig_option(options, /* strict= */ false);
r = parse_roothashsig_option(options, /* strict= */ false);
if (r < 0)
return r;
if (r > 0) {
@@ -264,7 +294,7 @@ static int parse_options(const char *options) {
arg_fec_roots = u;
} else if ((val = startswith(word, "root-hash-signature="))) {
r = save_roothashsig_option(val, /* strict= */ true);
r = parse_roothashsig_option(val, /* strict= */ true);
if (r < 0)
return r;
@@ -277,10 +307,10 @@ static int parse_options(const char *options) {
static int verb_attach(int argc, char *argv[], void *userdata) {
_cleanup_(crypt_freep) struct crypt_device *cd = NULL;
_cleanup_free_ void *m = NULL;
_cleanup_free_ void *rh = NULL;
struct crypt_params_verity p = {};
crypt_status_info status;
size_t l;
size_t rh_size = 0;
int r;
assert(argc >= 5);
@@ -294,10 +324,48 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
if (!filename_is_valid(volume))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
r = unhexmem(root_hash, &m, &l);
if (options) {
r = parse_options(options);
if (r < 0)
return log_error_errno(r, "Failed to parse options: %m");
}
if (empty_or_dash(root_hash) || streq_ptr(root_hash, "auto"))
root_hash = NULL;
_cleanup_(sd_device_unrefp) sd_device *datadev = NULL;
if (!root_hash || arg_root_hash_signature_auto) {
r = sd_device_new_from_path(&datadev, data_device);
if (r < 0)
return log_error_errno(r, "Failed to acquire udev object for data device '%s': %m", data_device);
}
if (!root_hash) {
/* If no literal root hash is specified try to determine it automatically from the
* ID_DISSECT_PART_ROOTHASH udev property. */
r = sd_device_get_property_value(ASSERT_PTR(datadev), "ID_DISSECT_PART_ROOTHASH", &root_hash);
if (r < 0)
return log_error_errno(r, "No root hash specified, and device doesn't carry ID_DISSECT_PART_ROOTHASH property, cannot determine root hash.");
}
r = unhexmem(root_hash, &rh, &rh_size);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash: %m");
if (arg_root_hash_signature_auto) {
assert(!arg_root_hash_signature);
assert(arg_root_hash_signature_size == 0);
const char *t;
r = sd_device_get_property_value(ASSERT_PTR(datadev), "ID_DISSECT_PART_ROOTHASH_SIG", &t);
if (r < 0)
return log_error_errno(r, "Automatic root hash signature pick up requested, and device doesn't carry ID_DISSECT_PART_ROOTHASH_SIG property, cannot determine root hash signature.");
r = unbase64mem(t, &arg_root_hash_signature, &arg_root_hash_signature_size);
if (r < 0)
return log_error_errno(r, "Failed to decode root hash signature data from udev data device: %m");
}
r = crypt_init(&cd, verity_device);
if (r < 0)
return log_error_errno(r, "Failed to open verity device %s: %m", verity_device);
@@ -310,12 +378,6 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
return 0;
}
if (options) {
r = parse_options(options);
if (r < 0)
return log_error_errno(r, "Failed to parse options: %m");
}
if (arg_superblock) {
p = (struct crypt_params_verity) {
.fec_device = arg_fec_what,
@@ -353,32 +415,23 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to configure data device: %m");
if (arg_root_hash_signature) {
if (arg_root_hash_signature_size > 0) {
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
_cleanup_free_ char *hash_sig = NULL;
size_t hash_sig_size;
char *value;
r = crypt_activate_by_signed_key(cd, volume, rh, rh_size, arg_root_hash_signature, arg_root_hash_signature_size, arg_activate_flags);
if (r < 0) {
log_info_errno(r, "Unable to activate verity device '%s' with root hash signature (%m), retrying without.", volume);
if ((value = startswith(arg_root_hash_signature, "base64:"))) {
r = unbase64mem(value, (void*) &hash_sig, &hash_sig_size);
r = crypt_activate_by_volume_key(cd, volume, rh, rh_size, arg_activate_flags);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", arg_root_hash_signature);
} else {
r = read_full_file_full(
AT_FDCWD, arg_root_hash_signature, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&hash_sig, &hash_sig_size);
if (r < 0)
return log_error_errno(r, "Failed to read root hash signature: %m");
return log_error_errno(r, "Failed to activate verity device '%s' both with and without root hash signature: %m", volume);
log_info("Activation of verity device '%s' succeeded without root hash signature.", volume);
}
r = crypt_activate_by_signed_key(cd, volume, m, l, hash_sig, hash_sig_size, arg_activate_flags);
#else
assert_not_reached();
#endif
} else
r = crypt_activate_by_volume_key(cd, volume, m, l, arg_activate_flags);
r = crypt_activate_by_volume_key(cd, volume, rh, rh_size, arg_activate_flags);
if (r < 0)
return log_error_errno(r, "Failed to set up verity device '%s': %m", volume);

View File

@@ -1194,6 +1194,7 @@ static int discover_root(char **ret) {
/* verity= */ NULL,
/* mount_options= */ NULL,
/* image_policy= */ NULL,
/* image_filter= */ NULL,
/* flags= */ 0,
&image);
if (r < 0)