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