1
0
mirror of https://github.com/systemd/systemd synced 2026-03-28 17:54:51 +01:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Lennart Poettering
5b81fa7ae1
Merge pull request #20870 from jwrdegoede/hwdb-2-accel-quirks
Hwdb 2 accel quirks
2021-09-28 23:13:54 +02:00
Lennart Poettering
ca3f47eff3
Merge pull request #20691 from poettering/gpt-sig
dissect: add support for GPT images with embedded dm-verity signatures
2021-09-28 20:52:01 +02:00
Hans de Goede
ddd19fce8a hwdb: Add accel orientation quirk for the Cyberbook T116 tablet
Add a quirk for the accelerometer orientation for the
Cyberbook T116 rugged tablet.
2021-09-28 19:50:27 +02:00
Hans de Goede
2bdc8dc341 hwdb: Add accel orientation quirk for the Chuwi Hi10 Plus (CWI527)
Add a quirk for the accelerometer orientation for the
Chuwi Hi10 Plus (CWI527) tablet.
2021-09-28 19:50:27 +02:00
Lennart Poettering
636c8a1f55 update TODO 2021-09-28 17:06:51 +02:00
Lennart Poettering
2c424ee0aa tests: extend TEST-50-DISSECT to look for verity signatures 2021-09-28 17:06:45 +02:00
Lennart Poettering
54dcf847b1 docs: document the three new env vars for tweaking GPT dissection/validation 2021-09-28 17:03:44 +02:00
Lennart Poettering
77617993ee docs: document the new Verity signature partition type, and its UUIDs 2021-09-28 17:03:39 +02:00
Lennart Poettering
c2fa92e7e8 dissect-image: optionally, validate dm-verity signatures in userspace
Getting certificates for dm-verity roothash signing into the trusted
kernel keychain is a royal PITA (means recompiling or rebooting with
shim), hence let's add a minimal userspace PKCS7 validation as well.

The mechanism is really simple and compatible with the verification the
kernel does. The only difference is that the certificates are searched
in /etc/verity.d/*.crt (and similar dirs in /usr/lib/, …).

We'll first try validation by passing the PKCS#7 data to the kernel, but
if that doesn't work we'll see if one of the certificates found that way
works and then attempt to attach the image without passing the PKCS#7
data to the kernel.

This makes it very easy to have fully validated GPT disk images. For
example, just copy the 'mkosi.secure-boot.crt' file you have in your
mkosi build dir to /etc/verity.d/ and things should just work.
2021-09-28 17:03:31 +02:00
Lennart Poettering
d5fcc5b053 dissect-image: add env var for disabling "sidecar" loading of verity params
Just to make debugging easier.
2021-09-28 17:03:26 +02:00
Lennart Poettering
88b3300fdc dissect-image: load embedded verity signature info from image
This adds support for actually using embedded signature data from
partitions.
2021-09-28 17:02:54 +02:00
Lennart Poettering
8ee9615e10 dissect-image: discover verity signature partitions
This doesn't make use of the discovered partitions yet, but it finds
them at least.
2021-09-28 17:02:27 +02:00
Lennart Poettering
1420cfb4b4 gpt: add partition type for PKCS#7 signatures for root hashes 2021-09-28 17:01:29 +02:00
16 changed files with 894 additions and 150 deletions

15
TODO
View File

@ -83,6 +83,19 @@ Janitorial Clean-ups:
Features:
* 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.
* nspawn: maybe optionally insert .nspawn file as GPT partition into images, so
that such container images are entirely stand-alone and can be updated as
one.
* we probably should extend the root verity hash of the root fs into some PCR
on boot. (i.e. maybe add a crypttab option tpm2-measure=8 or so to measure it
into PCR 8)
@ -102,8 +115,6 @@ Features:
* tpm2: figure out if we need to do anything for TPM2 parameter encryption? And
if so, what precisely?
* insert pkcs7 signature for verity gpt
* when mounting disk images: if IMAGE_ID/IMAGE_VERSION is set in os-release
data in the image, make sure the image filename actually matches this, so
that images cannot be misused.

View File

@ -4,7 +4,7 @@ category: Concepts
layout: default
SPDX-License-Identifier: LGPL-2.1-or-later
---
# The Discoverable Partitions Specification
# The Discoverable Partitions Specification (DPS)
_TL;DR: Let's automatically discover, mount and enable the root partition,
`/home/`, `/srv/`, `/var/` and `/var/tmp/` and the swap partitions based on
@ -50,7 +50,7 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
| `77055800-792c-4f94-b39a-98c91b762bb6` | _Root Partition (LoongArch 64-bit)_ | ditto | ditto |
| `60d5a7fe-8e7d-435c-b714-3dd8162144e1` | _Root Partition (RISC-V 32-bit)_ | ditto | ditto |
| `72ec70a6-cf74-40e6-bd49-4bda08e8f224` | _Root Partition (RISC-V 64-bit)_ | ditto | ditto |
| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | Contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128 bits of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128 bits of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | Contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128 bits of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128 bits of it, so that the root partition and its Verity partition can be discovered easily, simply by specifying the root hash. |
| `2c7357ed-ebd2-46d9-aec1-23d437ec2bf5` | _Root Verity Partition (x86-64)_ | ditto | ditto |
| `7386cdf2-203c-47a9-a498-f2ecce45a2d6` | _Root Verity Partition (32-bit ARM)_ | ditto | ditto |
| `df3300ce-d69f-4c92-978c-9bfb0f38d820` | _Root Verity Partition (64-bit ARM/AArch64)_ | ditto | ditto |
@ -58,6 +58,14 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
| `f3393b22-e9af-4613-a948-9d3bfbd0c535` | _Root Verity Partition (LoongArch 64-bit)_ | ditto | ditto |
| `ae0253be-1167-4007-ac68-43926c14c5de` | _Root Verity Partition (RISC-V 32-bit)_ | ditto | ditto |
| `b6ed5582-440b-4209-b8da-5ff7c419ea3d` | _Root Verity Partition (RISC-V 64-bit)_ | ditto | ditto |
| `5996fc05-109c-48de-808b-23fa0830b676` | _Root Verity Signature Partition (x86)_ | A serialized JSON object, see below | Contains a root hash and a PKCS#7 signature for it, permitting signed dm-verity GPT images |
| `41092b05-9fc8-4523-994f-2def0408b176` | _Root Verity Signature Partition (x86-64)_ | ditto | ditto |
| `42b0455f-eb11-491d-98d3-56145ba9d037` | _Root Verity Signature Partition (32-bit ARM)_ | ditto | ditto |
| `6db69de6-29f4-4758-a7a5-962190f00ce3` | _Root Verity Signature Partition (64-bit ARM/AArch64)_ | ditto | ditto |
| `e98b36ee-32ba-4882-9b12-0ce14655f46a` | _Root Verity Signature Partition (Itanium/IA-64)_ | ditto | ditto |
| `5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0` | _Root Verity Signature Partition (LoongArch 64-bit)_ | ditto | ditto |
| `3a112a75-8729-4380-b4cf-764d79934448` | _Root Verity Signature Partition (RISC-V 32-bit)_ | ditto | ditto |
| `efe0f087-ea8d-4469-821a-4c2a96a8386a` | _Root Verity Signature Partition (RISC-V 64-bit)_ | ditto | ditto |
| `75250d76-8cc6-458e-bd66-bd47cc81a812` | _`/usr/` Partition (x86)_ | Any native, optionally in LUKS | Similar semantics to root partition, but just the `/usr/` partition. |
| `8484680c-9521-48c6-9c11-b0720656f69e` | _`/usr/` Partition (x86-64)_ | ditto | ditto |
| `7d0359a3-02b3-4f0a-865c-654403e70625` | _`/usr/` Partition (32-bit ARM)_ | ditto | ditto |
@ -74,6 +82,14 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
| `f46b2c26-59ae-48f0-9106-c50ed47f673d` | _`/usr/` Verity Partition (LoongArch 64-bit)_ | ditto | ditto |
| `cb1ee4e3-8cd0-4136-a0a4-aa61a32e8730` | _`/usr/` Verity Partition (RISC-V 32-bit)_ | ditto | ditto |
| `8f1056be-9b05-47c4-81d6-be53128e5b54` | _`/usr/` Verity Partition (RISC-V 64-bit)_ | ditto | ditto |
| `974a71c0-de41-43c3-be5d-5c5ccd1ad2c0` | _`/usr/` Verity Signature Partition (x86)_ | A serialized JSON object, see below | Similar semantics to root Verity signature partition, but just for the `/usr/` partition. |
| `e7bb33fb-06cf-4e81-8273-e543b413e2e2` | _`/usr/` Verity Signature Partition (x86-64)_ | ditto | ditto |
| `d7ff812f-37d1-4902-a810-d76ba57b975a` | _`/usr/` Verity Signature Partition (32-bit ARM)_ | ditto | ditto |
| `c23ce4ff-44bd-4b00-b2d4-b41b3419e02a` | _`/usr/` Verity Signature Partition (64-bit ARM/AArch64)_ | ditto | ditto |
| `8de58bc2-2a43-460d-b14e-a76e4a17b47f` | _`/usr/` Verity Signature Partition (Itanium/IA-64)_ | ditto | ditto |
| `b024f315-d330-444c-8461-44bbde524e99` | _`/usr/` Verity Signature Partition (LoongArch 64-bit)_ | ditto | ditto |
| `c3836a13-3137-45ba-b583-b16c50fe5eb4` | _`/usr/` Verity Signature Partition (RISC-V 32-bit)_ | ditto | ditto |
| `d2f9000a-7a18-453f-b5cd-4d32f77a7b32` | _`/usr/` Verity Signature Partition (RISC-V 64-bit)_ | ditto | ditto |
| `933ac7e1-2eb4-4f13-b844-0e14e2aef915` | _Home Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/home/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/home`. |
| `3b8f8425-20e0-4f3b-907f-1a25a76f98e8` | _Server Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/srv/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/srv`. |
| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bits of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
@ -87,8 +103,8 @@ Other GPT type IDs might be used on Linux, for example to mark software RAID or
LVM partitions. The definitions of those GPT types is outside of the scope of
this specification.
[systemd-id128(1)](http://www.freedesktop.org/software/systemd/man/systemd-id128.html)
may be used to list those UUIDs.
[systemd-id128(1)](http://www.freedesktop.org/software/systemd/man/systemd-id128.html)'s
`show` command may be used to list those GPT partition type UUIDs.
## Partition Names
@ -97,31 +113,36 @@ human-friendly, descriptive partition names in the GPT partition table, for
example "*Home*", "*Server* *Data*", "*Fedora* *Root*" and similar, possibly
localized.
For the Root/Verity/Verity signature partitions it might make sense to use a
versioned naming scheme reflecting the OS name and its version,
e.g. "fooOS_2021.4" or similar.
## Partition Flags
This specification defines three GPT partition flags that may be set for the
partition types defined above:
1. For the root, `/usr/`, Verity, home, server data, variable data, temporary data,
swap and extended boot loader partitions, the partition flag bit 63
("*no-auto*") may be used to turn off auto-discovery for the specific
partition. If set, the partition will not be automatically mounted or
enabled.
1. For the root, `/usr/`, Verity, Verity signature, home, server data, variable
data, temporary data, swap and extended boot loader partitions, the
partition flag bit 63 ("*no-auto*") may be used to turn off auto-discovery
for the specific partition. If set, the partition will not be automatically
mounted or enabled.
2. For the root, `/usr/`, Verity, home, server data, variable data, temporary
data and extended boot loader partitions, the partition flag bit 60
("*read-only*") may be used to mark a partition for read-only mounts only.
If set, the partition will be mounted read-only instead of read-write. Note
that the variable data partition and the temporary data partition will
generally not be able to serve their purpose if marked read-only, since by
their very definition they are supposed to be mutable. (The home and server
data partitions are generally assumed to be mutable as well, but the
requirement for them is not equally strong.) Because of that, while the
read-only flag is defined and supported, it's almost never a good idea to
actually use it for these partitions. Also note that Verity partitions are
by their semantics always read-only. The flag is hence of little effect for
them, and it is recommended to set it unconditionally for the Verity
partition types.
2. For the root, `/usr/`, Verity, Verity signature home, server data, variable
data, temporary data and extended boot loader partitions, the partition flag
bit 60 ("*read-only*") may be used to mark a partition for read-only mounts
only. If set, the partition will be mounted read-only instead of
read-write. Note that the variable data partition and the temporary data
partition will generally not be able to serve their purpose if marked
read-only, since by their very definition they are supposed to be
mutable. (The home and server data partitions are generally assumed to be
mutable as well, but the requirement for them is not equally strong.)
Because of that, while the read-only flag is defined and supported, it's
almost never a good idea to actually use it for these partitions. Also note
that Verity and signature partitions are by their semantics always
read-only. The flag is hence of little effect for them, and it is
recommended to set it unconditionally for the Verity and signature partition
types.
3. For the root, `/usr/`, home, server data, variable data, temporary data and
extended boot loader partitions, the partition flag bit 59
@ -129,11 +150,11 @@ partition types defined above:
of the contained file system to the size of the partition when
mounted. Tools that automatically mount disk image with a GPT partition
table are suggested to implicitly grow the contained file system to the
partition size they are contained in. This flag is without effect on
partitions marked read-only.
partition size they are contained in, if they are found to be smaller. This
flag is without effect on partitions marked read-only.
Note that the first two flag definitions happen to map nicely to the ones used
by Microsoft Basic Data Partitions.
Note that the first two flag definitions happen to correspond nicely to the
same ones used by Microsoft Basic Data Partitions.
All three of these flags generally affect only auto-discovery and automatic
mounting of disk images. If partitions marked with these flags are mounted
@ -142,6 +163,63 @@ using low-level commands like
[mount(2)](https://man7.org/linux/man-pages/man2/mount.2.html), they typically
have no effect.
## Verity
The Root/`/usr/` partition types and their matching Verity and Verity signature
partitions enable relatively automatic handling of `dm-verity` protected
setups. These types are defined with two modes of operation in mind:
1. A trusted Verity root hash is passed in externally, for example is specified
on the kernel command line that is signed along with the kernel image using
SecureBoot PE signing (which in turn is tested against a set of
firmware-provided set of signing keys). If so, discovery and setup of a
Verity volume may be fully automatic: if the root partition's UUID is chosen
to match the first 128 bit of the root hash, and the matching Verity
partition UUIDs is chosen to match the last 128bit of the root hash, then
automatic discovery and match-up of the two partitions is possible, as the
root hash is enough to both find the partitions and then combine them in a
Verity volume. In this mode a Verity signature partition is not used and
unnecessary.
2. A Verity signature partition is included on the disk, with a signature to be
tested against a system-provided set of signing keys. The signature
partition primarily contains two fields: the root hash to use, and a PKCS#7
signature of it, using a signature key trusted by the OS. If so, discovery
and setup of a Verity volume may be fully automatic. First, the specified
root hash is validated with the signature and the OS-provided trusted
keys. If the signature checks out the root hash is then used in the same way
as in the first mode of operation described above.
Both modes of operation may be combined in a single image. This is particularly
useful for images that shall be usable in two different contexts: for example
an image that shall be able to boot directly on UEFI systems (in which
case it makes sense to include the root hash on the kernel command line that is
included in the signed kernel image to boot, as per mode of operation #1
above), but also be able to used as image for a container engine (such as
`systemd-nspawn`), which can use the signature partition to validate the image,
without making use of the signed kernel image (and thus following mode of
operation #2).
The Verity signature partition's contents should be a serialized JSON object in
text form, padded with NUL bytes to the next multiple of 4096 bytes in
size. Currently three fields are defined for the JSON object:
1. The (mandatory) `rootHash` field should be a string containing the Verity root hash,
formatted as series of (lowercase) hex characters.
2. The (mandatory) `signature` field should be a string containing the PKCS#7
signature of the root hash, in Base64-encoded DER format. This should be the
same format used by the Linux kernel's dm-verity signature logic, i.e. the
signed data should be the exact string representation of the hash, as stored
in `rootHash` above.
3. The (optional) `certificateFingerprint` field should be a string containing
a SHA256 fingerprint of the X.509 certificate for the key that signed the
root hash, formatted as series of (lowercase) hex characters (no `:`
separators or such).
More fields might be added in later revisions of this specification.
## Suggested Mode of Operation
An *installer* that repartitions the hard disk _should_ use the above UUID
@ -158,20 +236,20 @@ An *installer* _may_ omit creating entries in `/etc/fstab` for root, `/home/`,
`/srv/`, `/var/`, `/var/tmp` and for the swap partitions if they use these UUID
partition types, and are the first partitions on the disk of each type. If the
ESP shall be mounted to `/efi/` (or `/boot/`), it may additionally omit
creating the entry for it in `/etc/fstab`. If an extended boot partition is
used, or if the EFI partition shall not be mounted to `/efi/` or `/boot/`, it
_must_ create `/etc/fstab` entries for them. If other partitions are used (for
example for `/usr/` or `/var/lib/mysql/`), the installer _must_ register these
in `/etc/fstab`. The `root=` parameter passed to the kernel by the boot loader
may be omitted if the root partition is the first one on the disk of its type.
If the root partition is not the first one on the disk, the `root=` parameter
_must_ be passed to the kernel by the boot loader. An installer that mounts a
root, `/usr/`, `/home/`, `/srv/`, `/var/`, or `/var/tmp/` file system with the
partition types defined as above which contains a LUKS header _must_ call the
device mapper device "root", "usr", "home", "srv", "var" or "tmp",
respectively. This is necessary to ensure that the automatic discovery will
never result in different device mapper names than any static configuration by
the installer, thus eliminating possible naming conflicts and ambiguities.
creating the entry for it in `/etc/fstab`. If the EFI partition shall not be
mounted to `/efi/` or `/boot/`, it _must_ create `/etc/fstab` entries for them.
If other partitions are used (for example for `/usr/local/` or
`/var/lib/mysql/`), the installer _must_ register these in `/etc/fstab`. The
`root=` parameter passed to the kernel by the boot loader may be omitted if the
root partition is the first one on the disk of its type. If the root partition
is not the first one on the disk, the `root=` parameter _must_ be passed to the
kernel by the boot loader. An installer that mounts a root, `/usr/`, `/home/`,
`/srv/`, `/var/`, or `/var/tmp/` file system with the partition types defined
as above which contains a LUKS header _must_ call the device mapper device
"root", "usr", "home", "srv", "var" or "tmp", respectively. This is necessary
to ensure that the automatic discovery will never result in different device
mapper names than any static configuration by the installer, thus eliminating
possible naming conflicts and ambiguities.
An *operating* *system* _should_ automatically discover and mount the first
root partition that does not have the no-auto flag set (as described above) by

View File

@ -23,17 +23,17 @@ All tools:
* `$SYSTEMD_OFFLINE=[0|1]` — if set to `1`, then `systemctl` will refrain from
talking to PID 1; this has the same effect as the historical detection of
`chroot()`. Setting this variable to `0` instead has a similar effect as
`SYSTEMD_IGNORE_CHROOT=1`; i.e. tools will try to communicate with PID 1 even
if a `chroot()` environment is detected. You almost certainly want to set
this to `1` if you maintain a package build system or similar and are trying
to use a modern container system and not plain `chroot()`.
`$SYSTEMD_IGNORE_CHROOT=1`; i.e. tools will try to communicate with PID 1
even if a `chroot()` environment is detected. You almost certainly want to
set this to `1` if you maintain a package build system or similar and are
trying to use a modern container system and not plain `chroot()`.
* `$SYSTEMD_IGNORE_CHROOT=1` — if set, don't check whether being invoked in a
`chroot()` environment. This is particularly relevant for systemctl, as it
will not alter its behaviour for `chroot()` environments if set. Normally it
refrains from talking to PID 1 in such a case; turning most operations such
as `start` into no-ops. If that's what's explicitly desired, you might
consider setting `SYSTEMD_OFFLINE=1`.
consider setting `$SYSTEMD_OFFLINE=1`.
* `$SD_EVENT_PROFILE_DELAYS=1` — if set, the sd-event event loop implementation
will print latency information at runtime.
@ -96,7 +96,7 @@ All tools:
* `$SYSTEMD_RDRAND=0` — if set, the RDRAND instruction will never be used,
even if the CPU supports it.
* `$SYSTEMD_SECCOMP=0` if set, seccomp filters will not be enforced, even if
* `$SYSTEMD_SECCOMP=0` if set, seccomp filters will not be enforced, even if
support for it is compiled in and available in the kernel.
* `$SYSTEMD_LOG_SECCOMP=1` — if set, system calls blocked by seccomp filtering,
@ -189,7 +189,7 @@ All tools:
`systemd-udevd`:
* `$NET_NAMING_SCHEME=` if set, takes a network naming scheme (i.e. one of
* `$NET_NAMING_SCHEME=` if set, takes a network naming scheme (i.e. one of
"v238", "v239", "v240"…, or the special value "latest") as parameter. If
specified udev's `net_id` builtin will follow the specified naming scheme
when determining stable network interface names. This may be used to revert
@ -267,13 +267,13 @@ All tools:
`systemd-firstboot` and `localectl`:
* `SYSTEMD_LIST_NON_UTF8_LOCALES=1` if set, non-UTF-8 locales are listed among
* `$SYSTEMD_LIST_NON_UTF8_LOCALES=1` — if set, non-UTF-8 locales are listed among
the installed ones. By default non-UTF-8 locales are suppressed from the
selection, since we are living in the 21st century.
`systemd-sysext`:
* `SYSTEMD_SYSEXT_HIERARCHIES` this variable may be used to override which
* `$SYSTEMD_SYSEXT_HIERARCHIES` — this variable may be used to override which
hierarchies are managed by `systemd-sysext`. By default only `/usr/` and
`/opt/` are managed, and directories may be added or removed to that list by
setting this environment variable to a colon-separated list of absolute
@ -284,7 +284,7 @@ All tools:
`systemd-tmpfiles`:
* `SYSTEMD_TMPFILES_FORCE_SUBVOL` — if unset, `v`/`q`/`Q` lines will create
* `$SYSTEMD_TMPFILES_FORCE_SUBVOL` — if unset, `v`/`q`/`Q` lines will create
subvolumes only if the OS itself is installed into a subvolume. If set to `1`
(or another value interpreted as true), these lines will always create
subvolumes if the backing filesystem supports them. If set to `0`, these
@ -318,21 +318,49 @@ fuzzers:
Note that is may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
is suppressed by default.
systemd-importd:
`systemd-importd`:
* `SYSTEMD_IMPORT_BTRFS_SUBVOL` takes a boolean, which controls whether to
* `$SYSTEMD_IMPORT_BTRFS_SUBVOL` — takes a boolean, which controls whether to
prefer creating btrfs subvolumes over plain directories for machine
images. Has no effect on non-btrfs file systems where subvolumes are not
available anyway. If not set, defaults to true.
* `SYSTEMD_IMPORT_BTRFS_QUOTA` takes a boolean, which controls whether to set
* `$SYSTEMD_IMPORT_BTRFS_QUOTA` — takes a boolean, which controls whether to set
up quota automatically for created btrfs subvolumes for machine images. If
not set, defaults to true. Has no effect if machines are placed in regular
directories, because btrfs subvolumes are not supported or disabled. If
enabled, the quota group of the subvolume is automatically added to a
combined quota group for all such machine subvolumes.
* `SYSTEMD_IMPORT_SYNC` takes a boolean, which controls whether to
* `$SYSTEMD_IMPORT_SYNC` — takes a boolean, which controls whether to
synchronize images to disk after installing them, before completing the
operation. If not set, defaults to true. If disabled installation of images
will be quicker, but not as safe.
`systemd-dissect`, `systemd-nspawn` and all other tools that may operate on
disk images with `--image=` or similar:
* `$SYSTEMD_DISSECT_VERITY_SIDECAR` — takes a boolean, which controls whether to
load "sidecar" Verity metadata files. If enabled (which is the default),
whenever a disk image is used, a set of files with the `.roothash`,
`.usrhash`, `.roothash.p7s`, `.usrhash.p7s`, `.verity` suffixes are searched
adjacent to disk image file, containing the Verity root hashes, their
signatures or the Verity data itself. If disabled this automatic discovery of
Verity metadata files is turned off.
* `$SYSTEMD_DISSECT_VERITY_EMBEDDED` — takes a boolean, which controls whether
to load the embedded Verity signature data. If enabled (which is the
default), Verity root hash information and a suitable signature is
automatically acquired from a signature partition, following the
[Discoverable Partitions
Specification](https://systemd.io/DISCOVERABLE_PARTITIONS). If disabled any
such partition is ignored. Note that this only disables discovery of the root
hash and its signature, the Verity data partition itself is still searched in
the GPT image.
* `$SYSTEMD_DISSECT_VERITY_SIGNATURE` — takes a boolean, which controls whether
to validate the signature of the Verity root hash if available. If enabled
(which is the default), the signature of suitable disk images is validated
against any of the certificates in `/etc/verity.d/*.crt` (and similar
directores in `/usr/lib/`, `/run`, …) or passed to the kernel for validation
against its built-in certificates.

View File

@ -198,7 +198,11 @@ sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:*:svnDefaul
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrP02A_C106.60E:*:svnDefaultstring:pnDefaultstring:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
# Chuwi Hi10 Pro
# Chuwi Hi10 Plus (CWI527)
sensor:modalias:acpi:BOSC0200*:dmi:*:svnCHUWIINNOVATIONANDTECHNOLOGY*:pnHi10plustablet:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
# Chuwi Hi10 Pro (CWI529)
sensor:modalias:acpi:BOSC0200*:dmi:*:svn*CHUWIINNOVATIONANDTECHNOLOGY*:pnHi10protablet:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
@ -281,6 +285,14 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svncube:pni8-T:*
sensor:modalias:acpi:KIOX000A*:dmi:*:svnALLDOCUBE:pni1102:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
#########################################
# Cyberbook
#########################################
# Cyberbook T116
sensor:modalias:acpi:KIOX000A*:dmi:*:rvnDefaultstring:rnCherryTrailCR:*:sku20170531:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
#########################################
# Cytrix (Mytrix)
#########################################

View File

@ -1913,6 +1913,13 @@ int setup_namespace(
if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_load_verity_sig_partition(
dissected_image,
loop_device->fd,
&verity);
if (r < 0)
return r;
r = dissected_image_decrypt(
dissected_image,
NULL,

View File

@ -515,7 +515,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
if (arg_verity_settings.data_path)
r = table_add_cell(t, NULL, TABLE_STRING, "external");
else if (dissected_image_verity_candidate(m, i))
r = table_add_cell(t, NULL, TABLE_STRING, yes_no(dissected_image_verity_ready(m, i)));
r = table_add_cell(t, NULL, TABLE_STRING,
dissected_image_verity_sig_ready(m, i) ? "signed" :
yes_no(dissected_image_verity_ready(m, i)));
else
r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
if (r < 0)
@ -803,6 +805,13 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
r = dissected_image_load_verity_sig_partition(
m,
d->fd,
&arg_verity_settings);
if (r < 0)
return r;
switch (arg_action) {
case ACTION_DISSECT:

View File

@ -5710,8 +5710,16 @@ static int run(int argc, char *argv[]) {
if (r < 0)
goto finish;
if (!arg_verity_settings.root_hash && dissected_image->has_verity)
log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
r = dissected_image_load_verity_sig_partition(
dissected_image,
loop->fd,
&arg_verity_settings);
if (r < 0)
goto finish;
if (dissected_image->has_verity && !arg_verity_settings.root_hash && !dissected_image->has_verity_sig)
log_notice("Note: image %s contains verity information, but no root hash specified and no embedded "
"root hash signature found! Proceeding without integrity checking.", arg_image);
r = dissected_image_decrypt_interactively(
dissected_image,

View File

@ -9,13 +9,11 @@
#include <openssl/x509v3.h>
#include "io-util.h"
#include "openssl-util.h"
#include "resolved-dns-stream.h"
#include "resolved-dnstls.h"
#include "resolved-manager.h"
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
static int dnstls_flush_write_buffer(DnsStream *stream) {
ssize_t ss;

View File

@ -11,6 +11,12 @@
#include <sys/wait.h>
#include <sysexits.h>
#if HAVE_OPENSSL
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#endif
#include "sd-device.h"
#include "sd-id128.h"
@ -18,6 +24,7 @@
#include "ask-password-api.h"
#include "blkid-util.h"
#include "blockdev-util.h"
#include "conf-files.h"
#include "copy.h"
#include "cryptsetup-util.h"
#include "def.h"
@ -27,6 +34,7 @@
#include "dissect-image.h"
#include "dm-util.h"
#include "env-file.h"
#include "env-util.h"
#include "extension-release.h"
#include "fd-util.h"
#include "fileio.h"
@ -42,6 +50,7 @@
#include "mountpoint-util.h"
#include "namespace-util.h"
#include "nulstr-util.h"
#include "openssl-util.h"
#include "os-util.h"
#include "path-util.h"
#include "process-util.h"
@ -807,6 +816,10 @@ int dissect_image(
verity->root_hash &&
(verity->designator < 0 || verity->designator == PARTITION_ROOT);
m->has_verity_sig = false; /* signature not embedded, must be specified */
m->verity_sig_ready = m->verity_ready &&
verity->root_hash_sig;
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
if (options) {
o = strdup(options);
@ -822,6 +835,8 @@ int dissect_image(
.fstype = TAKE_PTR(t),
.node = TAKE_PTR(n),
.mount_options = TAKE_PTR(o),
.offset = 0,
.size = UINT64_MAX,
};
*ret = TAKE_PTR(m);
@ -865,6 +880,7 @@ int dissect_image(
for (int i = 0; i < n_partitions; i++) {
_cleanup_(sd_device_unrefp) sd_device *q = NULL;
unsigned long long pflags;
blkid_loff_t start, size;
blkid_partition pp;
const char *node;
int nr;
@ -889,6 +905,20 @@ int dissect_image(
if (nr < 0)
return errno_or_else(EIO);
errno = 0;
start = blkid_partition_get_start(pp);
if (start < 0)
return errno_or_else(EIO);
assert((uint64_t) start < UINT64_MAX/512);
errno = 0;
size = blkid_partition_get_size(pp);
if (size < 0)
return errno_or_else(EIO);
assert((uint64_t) size < UINT64_MAX/512);
if (is_gpt) {
PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID;
int architecture = _ARCHITECTURE_INVALID;
@ -982,14 +1012,42 @@ int dissect_image(
m->has_verity = true;
/* Ignore verity unless a root hash is specified */
if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id))
/* If no verity configuration is specified, then don't do verity */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
continue;
/* If root hash is specified, then ignore everything but the root id */
if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id))
continue;
designator = PARTITION_ROOT_VERITY;
fstype = "DM_verity_hash";
architecture = native_architecture();
rw = false;
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY_SIG)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
m->has_verity_sig = true;
/* If root hash is specified explicitly, then ignore any embedded signature */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
continue;
if (verity->root_hash)
continue;
designator = PARTITION_ROOT_VERITY_SIG;
fstype = "verity_hash_signature";
architecture = native_architecture();
rw = false;
}
#endif
#ifdef GPT_ROOT_SECONDARY
@ -1018,14 +1076,42 @@ int dissect_image(
m->has_verity = true;
/* Ignore verity unless root has is specified */
if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id))
/* Don't do verity if no verity config is passed in */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
continue;
/* If root hash is specified, then ignore everything but the root id */
if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id))
continue;
designator = PARTITION_ROOT_SECONDARY_VERITY;
fstype = "DM_verity_hash";
architecture = SECONDARY_ARCHITECTURE;
rw = false;
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY_SIG)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
m->has_verity_sig = true;
/* If root hash is specified explicitly, then ignore any embedded signature */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
continue;
if (verity->root_hash)
continue;
designator = PARTITION_ROOT_SECONDARY_VERITY_SIG;
fstype = "verity_hash_signature";
architecture = native_architecture();
rw = false;
}
#endif
#ifdef GPT_USR_NATIVE
@ -1054,14 +1140,41 @@ int dissect_image(
m->has_verity = true;
/* Ignore verity unless a usr hash is specified */
if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id))
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_USR)
continue;
/* If usr hash is specified, then ignore everything but the usr id */
if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id))
continue;
designator = PARTITION_USR_VERITY;
fstype = "DM_verity_hash";
architecture = native_architecture();
rw = false;
} else if (sd_id128_equal(type_id, GPT_USR_NATIVE_VERITY_SIG)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
m->has_verity_sig = true;
/* If usr hash is specified explicitly, then ignore any embedded signature */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_USR)
continue;
if (verity->root_hash)
continue;
designator = PARTITION_USR_VERITY_SIG;
fstype = "verity_hash_signature";
architecture = native_architecture();
rw = false;
}
#endif
#ifdef GPT_USR_SECONDARY
@ -1090,14 +1203,41 @@ int dissect_image(
m->has_verity = true;
/* Ignore verity unless usr has is specified */
if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id))
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_USR)
continue;
/* If usr hash is specified, then ignore everything but the root id */
if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id))
continue;
designator = PARTITION_USR_SECONDARY_VERITY;
fstype = "DM_verity_hash";
architecture = SECONDARY_ARCHITECTURE;
rw = false;
} else if (sd_id128_equal(type_id, GPT_USR_SECONDARY_VERITY_SIG)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
m->has_verity_sig = true;
/* If usr hash is specified explicitly, then ignore any embedded signature */
if (!verity)
continue;
if (verity->designator >= 0 && verity->designator != PARTITION_USR)
continue;
if (verity->root_hash)
continue;
designator = PARTITION_USR_SECONDARY_VERITY_SIG;
fstype = "verity_hash_signature";
architecture = native_architecture();
rw = false;
}
#endif
else if (sd_id128_equal(type_id, GPT_SWAP)) {
@ -1223,6 +1363,8 @@ int dissect_image(
.label = TAKE_PTR(l),
.uuid = id,
.mount_options = TAKE_PTR(o),
.offset = (uint64_t) start * 512,
.size = (uint64_t) size * 512,
};
}
@ -1281,6 +1423,8 @@ int dissect_image(
.node = TAKE_PTR(n),
.uuid = id,
.mount_options = TAKE_PTR(o),
.offset = (uint64_t) start * 512,
.size = (uint64_t) size * 512,
};
break;
@ -1293,10 +1437,13 @@ int dissect_image(
* since we never want to mount the secondary arch in this case. */
m->partitions[PARTITION_ROOT_SECONDARY].found = false;
m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG].found = false;
m->partitions[PARTITION_USR_SECONDARY].found = false;
m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found = false;
} else if (m->partitions[PARTITION_ROOT_VERITY].found)
} else if (m->partitions[PARTITION_ROOT_VERITY].found ||
m->partitions[PARTITION_ROOT_VERITY_SIG].found)
return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
else if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
@ -1308,22 +1455,32 @@ int dissect_image(
zero(m->partitions[PARTITION_ROOT_SECONDARY]);
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
m->partitions[PARTITION_ROOT_VERITY_SIG] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG];
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG]);
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
zero(m->partitions[PARTITION_USR_SECONDARY]);
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG];
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
} else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found)
} else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found ||
m->partitions[PARTITION_ROOT_SECONDARY_VERITY_SIG].found)
return -EADDRNOTAVAIL; /* as above */
if (m->partitions[PARTITION_USR].found) {
/* Hmm, we found a signature partition but no Verity data? Something is off. */
if (m->partitions[PARTITION_ROOT_VERITY_SIG].found && !m->partitions[PARTITION_ROOT_VERITY].found)
return -EADDRNOTAVAIL;
if (m->partitions[PARTITION_USR].found) {
/* Invalidate secondary arch /usr/ if we found the primary arch */
m->partitions[PARTITION_USR_SECONDARY].found = false;
m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found = false;
} else if (m->partitions[PARTITION_USR_VERITY].found)
} else if (m->partitions[PARTITION_USR_VERITY].found ||
m->partitions[PARTITION_USR_VERITY_SIG].found)
return -EADDRNOTAVAIL; /* as above */
else if (m->partitions[PARTITION_USR_SECONDARY].found) {
@ -1333,10 +1490,17 @@ int dissect_image(
zero(m->partitions[PARTITION_USR_SECONDARY]);
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
m->partitions[PARTITION_USR_VERITY_SIG] = m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG];
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG]);
} else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found)
} else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found ||
m->partitions[PARTITION_USR_SECONDARY_VERITY_SIG].found)
return -EADDRNOTAVAIL; /* as above */
/* Hmm, we found a signature partition but no Verity data? Something is off. */
if (m->partitions[PARTITION_USR_VERITY_SIG].found && !m->partitions[PARTITION_USR_VERITY].found)
return -EADDRNOTAVAIL;
/* If root and /usr are combined then insist that the architecture matches */
if (m->partitions[PARTITION_ROOT].found &&
m->partitions[PARTITION_USR].found &&
@ -1381,6 +1545,8 @@ int dissect_image(
.node = TAKE_PTR(generic_node),
.uuid = generic_uuid,
.mount_options = TAKE_PTR(o),
.offset = UINT64_MAX,
.size = UINT64_MAX,
};
}
}
@ -1407,6 +1573,9 @@ int dissect_image(
return -EADDRNOTAVAIL;
if (verity->root_hash) {
/* If we have an explicit root hash and found the partitions for it, then we are ready to use
* Verity, set things up for it */
if (verity->designator < 0 || verity->designator == PARTITION_ROOT) {
if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
return -EADDRNOTAVAIL;
@ -1424,6 +1593,16 @@ int dissect_image(
m->partitions[PARTITION_USR].rw = false;
m->verity_ready = true;
}
if (m->verity_ready)
m->verity_sig_ready = !!verity->root_hash_sig;
} else if (m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found) {
/* If we found an embedded signature partition, we are ready, too. */
m->verity_ready = m->verity_sig_ready = true;
m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR : PARTITION_ROOT].rw = false;
}
}
@ -2088,6 +2267,146 @@ static inline char* dm_deferred_remove_clean(char *name) {
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
static int validate_signature_userspace(const VeritySettings *verity) {
#if HAVE_OPENSSL
_cleanup_(sk_X509_free_allp) STACK_OF(X509) *sk = NULL;
_cleanup_strv_free_ char **certs = NULL;
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
_cleanup_free_ char *s = NULL;
_cleanup_(BIO_freep) BIO *bio = NULL; /* 'bio' must be freed first, 's' second, hence keep this order
* of declaration in place, please */
const unsigned char *d;
char **i;
int r;
assert(verity);
assert(verity->root_hash);
assert(verity->root_hash_sig);
/* Because installing a signature certificate into the kernel chain is so messy, let's optionally do
* userspace validation. */
r = conf_files_list_nulstr(&certs, ".crt", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, CONF_PATHS_NULSTR("verity.d"));
if (r < 0)
return log_debug_errno(r, "Failed to enumerate certificates: %m");
if (strv_isempty(certs)) {
log_debug("No userspace dm-verity certificates found.");
return 0;
}
d = verity->root_hash_sig;
p7 = d2i_PKCS7(NULL, &d, (long) verity->root_hash_sig_size);
if (!p7)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PKCS7 DER signature data.");
s = hexmem(verity->root_hash, verity->root_hash_size);
if (!s)
return log_oom_debug();
bio = BIO_new_mem_buf(s, strlen(s));
if (!bio)
return log_oom_debug();
sk = sk_X509_new_null();
if (!sk)
return log_oom_debug();
STRV_FOREACH(i, certs) {
_cleanup_(X509_freep) X509 *c = NULL;
_cleanup_fclose_ FILE *f = NULL;
f = fopen(*i, "re");
if (!f) {
log_debug_errno(errno, "Failed to open '%s', ignoring: %m", *i);
continue;
}
c = PEM_read_X509(f, NULL, NULL, NULL);
if (!c) {
log_debug("Failed to load X509 certificate '%s', ignoring.", *i);
continue;
}
if (sk_X509_push(sk, c) == 0)
return log_oom_debug();
TAKE_PTR(c);
}
r = PKCS7_verify(p7, sk, NULL, bio, NULL, PKCS7_NOINTERN|PKCS7_NOVERIFY);
if (r)
log_debug("Userspace PKCS#7 validation succeeded.");
else
log_debug("Userspace PKCS#7 validation failed: %s", ERR_error_string(ERR_get_error(), NULL));
return r;
#else
log_debug("Not doing client-side validation of dm-verity root hash signatures, OpenSSL support disabled.");
return 0;
#endif
}
static int do_crypt_activate_verity(
struct crypt_device *cd,
const char *name,
const VeritySettings *verity) {
bool check_signature;
int r;
assert(cd);
assert(name);
assert(verity);
if (verity->root_hash_sig) {
r = getenv_bool_secure("SYSTEMD_DISSECT_VERITY_SIGNATURE");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_SIGNATURE");
check_signature = r != 0;
} else
check_signature = false;
if (check_signature) {
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
/* First, if we have support for signed keys in the kernel, then try that first. */
r = sym_crypt_activate_by_signed_key(
cd,
name,
verity->root_hash,
verity->root_hash_size,
verity->root_hash_sig,
verity->root_hash_sig_size,
CRYPT_ACTIVATE_READONLY);
if (r >= 0)
return r;
log_debug("Validation of dm-verity signature failed via the kernel, trying userspace validation instead.");
#else
log_debug("Activation of verity device with signature requested, but not supported via the kernel by %s due to missing crypt_activate_by_signed_key(), trying userspace validation instead.",
program_invocation_short_name);
#endif
/* So this didn't work via the kernel, then let's try userspace validation instead. If that
* works we'll try to activate without telling the kernel the signature. */
r = validate_signature_userspace(verity);
if (r < 0)
return r;
if (r == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY),
"Activation of signed Verity volume worked neither via the kernel nor in userspace, can't activate.");
}
return sym_crypt_activate_by_volume_key(
cd,
name,
verity->root_hash,
verity->root_hash_size,
CRYPT_ACTIVATE_READONLY);
}
static int verity_partition(
PartitionDesignator designator,
DissectedPartition *m,
@ -2159,27 +2478,8 @@ static int verity_partition(
* In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time,
* retry a few times before giving up. */
for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
if (verity->root_hash_sig) {
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
r = sym_crypt_activate_by_signed_key(
cd,
name,
verity->root_hash,
verity->root_hash_size,
verity->root_hash_sig,
verity->root_hash_sig_size,
CRYPT_ACTIVATE_READONLY);
#else
r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Activation of verity device with signature requested, but not supported by %s due to missing crypt_activate_by_signed_key().", program_invocation_short_name);
#endif
} else
r = sym_crypt_activate_by_volume_key(
cd,
name,
verity->root_hash,
verity->root_hash_size,
CRYPT_ACTIVATE_READONLY);
r = do_crypt_activate_verity(cd, name, verity);
/* libdevmapper can return EINVAL when the device is already in the activation stage.
* There's no way to distinguish this situation from a genuine error due to invalid
* parameters, so immediately fall back to activating the device with a unique name.
@ -2438,6 +2738,12 @@ int verity_settings_load(
if (is_device_path(image))
return 0;
r = getenv_bool_secure("SYSTEMD_DISSECT_VERITY_SIDECAR");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_SIDECAR, ignoring: %m");
if (r == 0)
return 0;
designator = verity->designator;
/* We only fill in what isn't already filled in */
@ -2594,6 +2900,109 @@ int verity_settings_load(
return 1;
}
int dissected_image_load_verity_sig_partition(
DissectedImage *m,
int fd,
VeritySettings *verity) {
_cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
size_t root_hash_size, root_hash_sig_size;
_cleanup_free_ char *buf = NULL;
PartitionDesignator d;
DissectedPartition *p;
JsonVariant *rh, *sig;
ssize_t n;
char *e;
int r;
assert(m);
assert(fd >= 0);
assert(verity);
if (verity->root_hash && verity->root_hash_sig) /* Already loaded? */
return 0;
r = getenv_bool_secure("SYSTEMD_DISSECT_VERITY_EMBEDDED");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_EMBEDDED, ignoring: %m");
if (r == 0)
return 0;
d = PARTITION_VERITY_SIG_OF(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
assert(d >= 0);
p = m->partitions + d;
if (!p->found)
return 0;
if (p->offset == UINT64_MAX || p->size == UINT64_MAX)
return -EINVAL;
if (p->size > 4*1024*1024) /* Signature data cannot possible be larger than 4M, refuse that */
return -EFBIG;
buf = new(char, p->size+1);
if (!buf)
return -ENOMEM;
n = pread(fd, buf, p->size, p->offset);
if (n < 0)
return -ENOMEM;
if ((uint64_t) n != p->size)
return -EIO;
e = memchr(buf, 0, p->size);
if (e) {
/* If we found a NUL byte then the rest of the data must be NUL too */
if (!memeqzero(e, p->size - (e - buf)))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature data contains embedded NUL byte.");
} else
buf[p->size] = 0;
r = json_parse(buf, 0, &v, NULL, NULL);
if (r < 0)
return log_debug_errno(r, "Failed to parse signature JSON data: %m");
rh = json_variant_by_key(v, "rootHash");
if (!rh)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field.");
if (!json_variant_is_string(rh))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'rootHash' field of signature JSON object is not a string.");
r = unhexmem(json_variant_string(rh), SIZE_MAX, &root_hash, &root_hash_size);
if (r < 0)
return log_debug_errno(r, "Failed to parse root hash field: %m");
/* Check if specified root hash matches if it is specified */
if (verity->root_hash &&
memcmp_nn(verity->root_hash, verity->root_hash_size, root_hash, root_hash_size) != 0) {
_cleanup_free_ char *a = NULL, *b = NULL;
a = hexmem(root_hash, root_hash_size);
b = hexmem(verity->root_hash, verity->root_hash_size);
return log_debug_errno(r, "Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(a), strna(b));
}
sig = json_variant_by_key(v, "signature");
if (!sig)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field.");
if (!json_variant_is_string(sig))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'signature' field of signature JSON object is not a string.");
r = unbase64mem(json_variant_string(sig), SIZE_MAX, &root_hash_sig, &root_hash_sig_size);
if (r < 0)
return log_debug_errno(r, "Failed to parse signature field: %m");
free_and_replace(verity->root_hash, root_hash);
verity->root_hash_size = root_hash_size;
free_and_replace(verity->root_hash_sig, root_hash_sig);
verity->root_hash_sig_size = root_hash_sig_size;
return 1;
}
int dissected_image_acquire_metadata(DissectedImage *m) {
enum {
@ -2901,6 +3310,23 @@ bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignat
return k >= 0 && image->partitions[k].found;
}
bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
PartitionDesignator k;
assert(image);
/* Checks if this partition has verity signature data available that we can use. */
if (!image->verity_sig_ready)
return false;
if (image->single_file_system)
return partition_designator == PARTITION_ROOT;
k = PARTITION_VERITY_SIG_OF(partition_designator);
return k >= 0 && image->partitions[k].found;
}
MountOptions* mount_options_free_all(MountOptions *options) {
MountOptions *m;
@ -2967,6 +3393,10 @@ int mount_image_privately_interactively(
if (r < 0)
return r;
r = dissected_image_load_verity_sig_partition(dissected_image, d->fd, &verity);
if (r < 0)
return r;
r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags, &decrypted_image);
if (r < 0)
return r;
@ -3014,6 +3444,10 @@ static const char *const partition_designator_table[] = {
[PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
[PARTITION_USR_VERITY] = "usr-verity",
[PARTITION_USR_SECONDARY_VERITY] = "usr-secondary-verity",
[PARTITION_ROOT_VERITY_SIG] = "root-verity-sig",
[PARTITION_ROOT_SECONDARY_VERITY_SIG] = "root-secondary-verity-sig",
[PARTITION_USR_VERITY_SIG] = "usr-verity-sig",
[PARTITION_USR_SECONDARY_VERITY_SIG] = "usr-secondary-verity-sig",
[PARTITION_TMP] = "tmp",
[PARTITION_VAR] = "var",
};
@ -3073,6 +3507,10 @@ int verity_dissect_and_mount(
if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, &verity);
if (r < 0)
return r;
r = dissected_image_decrypt(
dissected_image,
NULL,

View File

@ -28,6 +28,8 @@ struct DissectedPartition {
char *decrypted_node;
char *decrypted_fstype;
char *mount_options;
uint64_t size;
uint64_t offset;
};
typedef enum PartitionDesignator {
@ -44,6 +46,10 @@ typedef enum PartitionDesignator {
PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
PARTITION_USR_VERITY,
PARTITION_USR_SECONDARY_VERITY,
PARTITION_ROOT_VERITY_SIG, /* PKCS#7 signature for root hash for the PARTITION_ROOT partition */
PARTITION_ROOT_SECONDARY_VERITY_SIG, /* ditto for the PARTITION_ROOT_SECONDARY partition */
PARTITION_USR_VERITY_SIG,
PARTITION_USR_SECONDARY_VERITY_SIG,
PARTITION_TMP,
PARTITION_VAR,
_PARTITION_DESIGNATOR_MAX,
@ -64,7 +70,11 @@ static inline bool PARTITION_DESIGNATOR_VERSIONED(PartitionDesignator d) {
PARTITION_ROOT_VERITY,
PARTITION_ROOT_SECONDARY_VERITY,
PARTITION_USR_VERITY,
PARTITION_USR_SECONDARY_VERITY);
PARTITION_USR_SECONDARY_VERITY,
PARTITION_ROOT_VERITY_SIG,
PARTITION_ROOT_SECONDARY_VERITY_SIG,
PARTITION_USR_VERITY_SIG,
PARTITION_USR_SECONDARY_VERITY_SIG);
}
static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
@ -87,6 +97,26 @@ static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
}
}
static inline PartitionDesignator PARTITION_VERITY_SIG_OF(PartitionDesignator p) {
switch (p) {
case PARTITION_ROOT:
return PARTITION_ROOT_VERITY_SIG;
case PARTITION_ROOT_SECONDARY:
return PARTITION_ROOT_SECONDARY_VERITY_SIG;
case PARTITION_USR:
return PARTITION_USR_VERITY_SIG;
case PARTITION_USR_SECONDARY:
return PARTITION_USR_SECONDARY_VERITY_SIG;
default:
return _PARTITION_DESIGNATOR_INVALID;
}
}
typedef enum DissectImageFlags {
DISSECT_IMAGE_DEVICE_READ_ONLY = 1 << 0, /* Make device read-only */
DISSECT_IMAGE_DISCARD_ON_LOOP = 1 << 1, /* Turn on "discard" if on a loop device and file system supports it */
@ -119,11 +149,14 @@ typedef enum DissectImageFlags {
struct DissectedImage {
bool encrypted:1;
bool has_verity:1; /* verity available in image, but not necessarily used */
bool has_verity_sig:1; /* pkcs#7 signature embedded in image */
bool verity_ready:1; /* verity available, fully specified and usable */
bool verity_sig_ready:1; /* verity signature logic, fully specified and usable */
bool single_file_system:1; /* MBR/GPT or single file system */
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
/* Meta information extracted from /etc/os-release and similar */
char *image_name;
char *hostname;
sd_id128_t machine_id;
@ -186,8 +219,11 @@ PartitionDesignator partition_designator_from_string(const char *name) _pure_;
int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
void verity_settings_done(VeritySettings *verity);
int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
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, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image);

View File

@ -5,63 +5,83 @@
#include "utf8.h"
const GptPartitionType gpt_partition_type_table[] = {
{ GPT_ROOT_X86, "root-x86" },
{ GPT_ROOT_X86_VERITY, "root-x86-verity" },
{ GPT_ROOT_X86_64, "root-x86-64" },
{ GPT_ROOT_X86_64_VERITY, "root-x86-64-verity" },
{ GPT_ROOT_ARM, "root-arm" },
{ GPT_ROOT_ARM_VERITY, "root-arm-verity" },
{ GPT_ROOT_ARM_64, "root-arm64" },
{ GPT_ROOT_ARM_64_VERITY, "root-arm64-verity" },
{ GPT_ROOT_IA64, "root-ia64" },
{ GPT_ROOT_IA64_VERITY, "root-ia64-verity" },
{ GPT_ROOT_LOONGARCH64, "root-loongarch64" },
{ GPT_ROOT_LOONGARCH64_VERITY, "root-loongarch64-verity" },
{ GPT_ROOT_RISCV32, "root-riscv32" },
{ GPT_ROOT_RISCV32_VERITY, "root-riscv32-verity" },
{ GPT_ROOT_RISCV64, "root-riscv64" },
{ GPT_ROOT_RISCV64_VERITY, "root-riscv64-verity" },
{ GPT_ROOT_X86, "root-x86" },
{ GPT_ROOT_X86_VERITY, "root-x86-verity" },
{ GPT_ROOT_X86_VERITY_SIG, "root-x86-verity-sig" },
{ GPT_ROOT_X86_64, "root-x86-64" },
{ GPT_ROOT_X86_64_VERITY, "root-x86-64-verity" },
{ GPT_ROOT_X86_64_VERITY_SIG, "root-x86-64-verity-sig" },
{ GPT_ROOT_ARM, "root-arm" },
{ GPT_ROOT_ARM_VERITY, "root-arm-verity" },
{ GPT_ROOT_ARM_VERITY_SIG, "root-arm-verity-sig" },
{ GPT_ROOT_ARM_64, "root-arm64" },
{ GPT_ROOT_ARM_64_VERITY, "root-arm64-verity" },
{ GPT_ROOT_ARM_64_VERITY_SIG, "root-arm64-verity-sig" },
{ GPT_ROOT_IA64, "root-ia64" },
{ GPT_ROOT_IA64_VERITY, "root-ia64-verity" },
{ GPT_ROOT_IA64_VERITY_SIG, "root-ia64-verity-sig" },
{ GPT_ROOT_LOONGARCH64, "root-loongarch64" },
{ GPT_ROOT_LOONGARCH64_VERITY, "root-loongarch64-verity" },
{ GPT_ROOT_LOONGARCH64_VERITY_SIG, "root-loongarch64-verity-sig" },
{ GPT_ROOT_RISCV32, "root-riscv32" },
{ GPT_ROOT_RISCV32_VERITY, "root-riscv32-verity" },
{ GPT_ROOT_RISCV32_VERITY_SIG, "root-riscv32-verity-sig" },
{ GPT_ROOT_RISCV64, "root-riscv64" },
{ GPT_ROOT_RISCV64_VERITY, "root-riscv64-verity" },
{ GPT_ROOT_RISCV64_VERITY_SIG, "root-riscv64-verity-sig" },
#ifdef GPT_ROOT_NATIVE
{ GPT_ROOT_NATIVE, "root" },
{ GPT_ROOT_NATIVE_VERITY, "root-verity" },
{ GPT_ROOT_NATIVE, "root" },
{ GPT_ROOT_NATIVE_VERITY, "root-verity" },
{ GPT_ROOT_NATIVE_VERITY_SIG, "root-verity-sig" },
#endif
#ifdef GPT_ROOT_SECONDARY
{ GPT_ROOT_SECONDARY, "root-secondary" },
{ GPT_ROOT_SECONDARY_VERITY, "root-secondary-verity" },
{ GPT_ROOT_SECONDARY, "root-secondary" },
{ GPT_ROOT_SECONDARY_VERITY, "root-secondary-verity" },
{ GPT_ROOT_SECONDARY_VERITY_SIG, "root-secondary-verity-sig" },
#endif
{ GPT_USR_X86, "usr-x86" },
{ GPT_USR_X86_VERITY, "usr-x86-verity" },
{ GPT_USR_X86_64, "usr-x86-64" },
{ GPT_USR_X86_64_VERITY, "usr-x86-64-verity" },
{ GPT_USR_ARM, "usr-arm" },
{ GPT_USR_ARM_VERITY, "usr-arm-verity" },
{ GPT_USR_ARM_64, "usr-arm64" },
{ GPT_USR_ARM_64_VERITY, "usr-arm64-verity" },
{ GPT_USR_IA64, "usr-ia64" },
{ GPT_USR_IA64_VERITY, "usr-ia64-verity" },
{ GPT_USR_LOONGARCH64, "usr-loongarch64" },
{ GPT_USR_LOONGARCH64_VERITY, "usr-loongarch64-verity" },
{ GPT_USR_RISCV32, "usr-riscv32" },
{ GPT_USR_RISCV32_VERITY, "usr-riscv32-verity" },
{ GPT_USR_RISCV64, "usr-riscv64" },
{ GPT_USR_RISCV64_VERITY, "usr-riscv64-verity" },
{ GPT_USR_X86, "usr-x86" },
{ GPT_USR_X86_VERITY, "usr-x86-verity" },
{ GPT_USR_X86_VERITY_SIG, "usr-x86-verity-sig" },
{ GPT_USR_X86_64, "usr-x86-64" },
{ GPT_USR_X86_64_VERITY, "usr-x86-64-verity" },
{ GPT_USR_X86_64_VERITY_SIG, "usr-x86-64-verity-sig" },
{ GPT_USR_ARM, "usr-arm" },
{ GPT_USR_ARM_VERITY, "usr-arm-verity" },
{ GPT_USR_ARM_VERITY_SIG, "usr-arm-verity-sig" },
{ GPT_USR_ARM_64, "usr-arm64" },
{ GPT_USR_ARM_64_VERITY, "usr-arm64-verity" },
{ GPT_USR_ARM_64_VERITY_SIG, "usr-arm64-verity-sig" },
{ GPT_USR_IA64, "usr-ia64" },
{ GPT_USR_IA64_VERITY, "usr-ia64-verity" },
{ GPT_USR_IA64_VERITY_SIG, "usr-ia64-verity-sig" },
{ GPT_USR_LOONGARCH64, "usr-loongarch64" },
{ GPT_USR_LOONGARCH64_VERITY, "usr-loongarch64-verity" },
{ GPT_USR_LOONGARCH64_VERITY_SIG, "usr-loongarch64-verity-sig" },
{ GPT_USR_RISCV32, "usr-riscv32" },
{ GPT_USR_RISCV32_VERITY, "usr-riscv32-verity" },
{ GPT_USR_RISCV32_VERITY_SIG, "usr-riscv32-verity-sig" },
{ GPT_USR_RISCV64, "usr-riscv64" },
{ GPT_USR_RISCV64_VERITY, "usr-riscv64-verity" },
{ GPT_USR_RISCV64_VERITY_SIG, "usr-riscv64-verity-sig" },
#ifdef GPT_USR_NATIVE
{ GPT_USR_NATIVE, "usr" },
{ GPT_USR_NATIVE_VERITY, "usr-verity" },
{ GPT_USR_NATIVE, "usr" },
{ GPT_USR_NATIVE_VERITY, "usr-verity" },
{ GPT_USR_NATIVE_VERITY_SIG, "usr-verity-sig" },
#endif
#ifdef GPT_USR_SECONDARY
{ GPT_USR_SECONDARY, "usr-secondary" },
{ GPT_USR_SECONDARY_VERITY, "usr-secondary-verity" },
{ GPT_USR_SECONDARY, "usr-secondary" },
{ GPT_USR_SECONDARY_VERITY, "usr-secondary-verity" },
{ GPT_USR_SECONDARY_VERITY_SIG, "usr-secondary-verity-sig" },
#endif
{ GPT_ESP, "esp" },
{ GPT_XBOOTLDR, "xbootldr" },
{ GPT_SWAP, "swap" },
{ GPT_HOME, "home" },
{ GPT_SRV, "srv" },
{ GPT_VAR, "var" },
{ GPT_TMP, "tmp" },
{ GPT_USER_HOME, "user-home" },
{ GPT_LINUX_GENERIC, "linux-generic" },
{ GPT_ESP, "esp" },
{ GPT_XBOOTLDR, "xbootldr" },
{ GPT_SWAP, "swap" },
{ GPT_HOME, "home" },
{ GPT_SRV, "srv" },
{ GPT_VAR, "var" },
{ GPT_TMP, "tmp" },
{ GPT_USER_HOME, "user-home" },
{ GPT_LINUX_GENERIC, "linux-generic" },
{}
};

View File

@ -56,27 +56,53 @@
#define GPT_USR_RISCV32_VERITY SD_ID128_MAKE(cb,1e,e4,e3,8c,d0,41,36,a0,a4,aa,61,a3,2e,87,30)
#define GPT_USR_RISCV64_VERITY SD_ID128_MAKE(8f,10,56,be,9b,05,47,c4,81,d6,be,53,12,8e,5b,54)
/* PKCS#7 Signatures for the Verity Root Hashes */
#define GPT_ROOT_X86_VERITY_SIG SD_ID128_MAKE(59,96,fc,05,10,9c,48,de,80,8b,23,fa,08,30,b6,76)
#define GPT_ROOT_X86_64_VERITY_SIG SD_ID128_MAKE(41,09,2b,05,9f,c8,45,23,99,4f,2d,ef,04,08,b1,76)
#define GPT_ROOT_ARM_VERITY_SIG SD_ID128_MAKE(42,b0,45,5f,eb,11,49,1d,98,d3,56,14,5b,a9,d0,37)
#define GPT_ROOT_ARM_64_VERITY_SIG SD_ID128_MAKE(6d,b6,9d,e6,29,f4,47,58,a7,a5,96,21,90,f0,0c,e3)
#define GPT_ROOT_IA64_VERITY_SIG SD_ID128_MAKE(e9,8b,36,ee,32,ba,48,82,9b,12,0c,e1,46,55,f4,6a)
#define GPT_ROOT_LOONGARCH64_VERITY_SIG SD_ID128_MAKE(5a,fb,67,eb,ec,c8,4f,85,ae,8e,ac,1e,7c,50,e7,d0)
#define GPT_ROOT_RISCV32_VERITY_SIG SD_ID128_MAKE(3a,11,2a,75,87,29,43,80,b4,cf,76,4d,79,93,44,48)
#define GPT_ROOT_RISCV64_VERITY_SIG SD_ID128_MAKE(ef,e0,f0,87,ea,8d,44,69,82,1a,4c,2a,96,a8,38,6a)
#define GPT_USR_X86_VERITY_SIG SD_ID128_MAKE(97,4a,71,c0,de,41,43,c3,be,5d,5c,5c,cd,1a,d2,c0)
#define GPT_USR_X86_64_VERITY_SIG SD_ID128_MAKE(e7,bb,33,fb,06,cf,4e,81,82,73,e5,43,b4,13,e2,e2)
#define GPT_USR_ARM_VERITY_SIG SD_ID128_MAKE(d7,ff,81,2f,37,d1,49,02,a8,10,d7,6b,a5,7b,97,5a)
#define GPT_USR_ARM_64_VERITY_SIG SD_ID128_MAKE(c2,3c,e4,ff,44,bd,4b,00,b2,d4,b4,1b,34,19,e0,2a)
#define GPT_USR_IA64_VERITY_SIG SD_ID128_MAKE(8d,e5,8b,c2,2a,43,46,0d,b1,4e,a7,6e,4a,17,b4,7f)
#define GPT_USR_LOONGARCH64_VERITY_SIG SD_ID128_MAKE(b0,24,f3,15,d3,30,44,4c,84,61,44,bb,de,52,4e,99)
#define GPT_USR_RISCV32_VERITY_SIG SD_ID128_MAKE(c3,83,6a,13,31,37,45,ba,b5,83,b1,6c,50,fe,5e,b4)
#define GPT_USR_RISCV64_VERITY_SIG SD_ID128_MAKE(d2,f9,00,0a,7a,18,45,3f,b5,cd,4d,32,f7,7a,7b,32)
#if defined(__x86_64__)
# define GPT_ROOT_NATIVE GPT_ROOT_X86_64
# define GPT_ROOT_SECONDARY GPT_ROOT_X86
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_64_VERITY
# define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_X86_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_X86_64_VERITY_SIG
# define GPT_ROOT_SECONDARY_VERITY_SIG GPT_ROOT_X86_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_X86_64
# define GPT_USR_SECONDARY GPT_USR_X86
# define GPT_USR_NATIVE_VERITY GPT_USR_X86_64_VERITY
# define GPT_USR_SECONDARY_VERITY GPT_USR_X86_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_X86_64_VERITY_SIG
# define GPT_USR_SECONDARY_VERITY_SIG GPT_USR_X86_VERITY_SIG
#elif defined(__i386__)
# define GPT_ROOT_NATIVE GPT_ROOT_X86
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_X86_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_X86_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_X86
# define GPT_USR_NATIVE_VERITY GPT_USR_X86_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_X86_VERITY_SIG
#endif
#if defined(__ia64__)
# define GPT_ROOT_NATIVE GPT_ROOT_IA64
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_IA64_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_IA64_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_IA64
# define GPT_USR_NATIVE_VERITY GPT_USR_IA64_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_IA64_VERITY_SIG
#endif
#if defined(__aarch64__) && (__BYTE_ORDER != __BIG_ENDIAN)
@ -84,35 +110,47 @@
# define GPT_ROOT_SECONDARY GPT_ROOT_ARM
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_64_VERITY
# define GPT_ROOT_SECONDARY_VERITY GPT_ROOT_ARM_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_ARM_64_VERITY_SIG
# define GPT_ROOT_SECONDARY_VERITY_SIG GPT_ROOT_ARM_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_ARM_64
# define GPT_USR_SECONDARY GPT_USR_ARM
# define GPT_USR_NATIVE_VERITY GPT_USR_ARM_64_VERITY
# define GPT_USR_SECONDARY_VERITY GPT_USR_ARM_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_ARM_64_VERITY_SIG
# define GPT_USR_SECONDARY_VERITY_SIG GPT_USR_ARM_VERITY_SIG
#elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN)
# define GPT_ROOT_NATIVE GPT_ROOT_ARM
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_ARM_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_ARM
# define GPT_USR_NATIVE_VERITY GPT_USR_ARM_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_ARM_VERITY_SIG
#endif
#if defined(__loongarch64)
# define GPT_ROOT_NATIVE GPT_ROOT_LOONGARCH64
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_LOONGARCH64_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_LOONGARCH64_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_LOONGARCH64
# define GPT_USR_NATIVE_VERITY GPT_USR_LOONGARCH64_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_LOONGARCH64_VERITY_SIG
#endif
#if defined(__riscv)
#if (__riscv_xlen == 32)
# define GPT_ROOT_NATIVE GPT_ROOT_RISCV32
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_RISCV32_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_RISCV32_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_RISCV32
# define GPT_USR_NATIVE_VERITY GPT_USR_RISCV32_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_RISCV32_VERITY_SIG
#elif (__riscv_xlen == 64)
# define GPT_ROOT_NATIVE GPT_ROOT_RISCV64
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_RISCV64_VERITY
# define GPT_ROOT_NATIVE_VERITY_SIG GPT_ROOT_RISCV64_VERITY_SIG
# define GPT_USR_NATIVE GPT_USR_RISCV64
# define GPT_USR_NATIVE_VERITY GPT_USR_RISCV64_VERITY
# define GPT_USR_NATIVE_VERITY_SIG GPT_USR_RISCV64_VERITY_SIG
#endif
#endif

View File

@ -4,13 +4,26 @@
#include "macro.h"
#if HAVE_OPENSSL
# include <openssl/bio.h>
# include <openssl/evp.h>
# include <openssl/x509.h>
# include <openssl/pkcs7.h>
# include <openssl/ssl.h>
# include <openssl/x509v3.h>
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(PKCS7*, PKCS7_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
static inline void sk_X509_free_allp(STACK_OF(X509) **sk) {
if (!sk || !*sk)
return;
sk_X509_pop_free(*sk, X509_free);
}
int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size);

View File

@ -540,6 +540,13 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
if (r < 0)
return r;
r = dissected_image_load_verity_sig_partition(
m,
d->fd,
&verity_settings);
if (r < 0)
return r;
r = dissected_image_decrypt_interactively(
m, NULL,
&verity_settings,

View File

@ -14,6 +14,7 @@ TEST_INSTALL_VERITY_MINIMAL=1
command -v mksquashfs >/dev/null 2>&1 || exit 0
command -v veritysetup >/dev/null 2>&1 || exit 0
command -v sfdisk >/dev/null 2>&1 || exit 0
command -v openssl >/dev/null 2>&1 || exit 0
# Need loop devices for systemd-dissect
test_append_files() {
@ -26,6 +27,7 @@ test_append_files() {
generate_module_dependencies
inst_binary losetup
inst_binary wc
inst_binary openssl
install_verity_minimal
)
}

View File

@ -74,22 +74,27 @@ machine="$(uname -m)"
if [ "${machine}" = "x86_64" ]; then
root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
signature_guid=41092b05-9fc8-4523-994f-2def0408b176
architecture="x86-64"
elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
root_guid=44479540-f297-41b2-9af7-d131d5f0458a
verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
signature_guid=5996fc05-109c-48de-808b-23fa0830b676
architecture="x86"
elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
signature_guid=6db69de6-29f4-4758-a7a5-962190f00ce3
architecture="arm64"
elif [ "${machine}" = "arm" ]; then
root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
signature_guid=42b0455f-eb11-491d-98d3-56145ba9d037
architecture="arm"
elif [ "${machine}" = "ia64" ]; then
root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
signature_guid=e98b36ee-32ba-4882-9b12-0ce14655f46a
architecture="ia64"
elif [ "${machine}" = "ppc64le" ]; then
# There's no support of PPC in the discoverable partitions specification yet, so skip the rest for now
@ -102,8 +107,9 @@ fi
# du rounds up to block size, which is more helpful for partitioning
root_size="$(du -k "${image}.raw" | cut -f1)"
verity_size="$(du -k "${image}.verity" | cut -f1)"
signature_size=4
# 4MB seems to be the minimum size blkid will accept, below that probing fails
dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_size*2))
dd if=/dev/zero of="${image}.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
# so do some basic rounding up if the minimal image is more than 1 MB
if [ "${root_size}" -ge 1024 ]; then
@ -112,6 +118,36 @@ else
root_size="${root_size}KiB"
fi
verity_size="$((verity_size * 2))KiB"
signature_size="$((signature_size * 2))KiB"
# Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
cat >> "${image}.openssl.cnf" <<EOF
[ req ]
prompt = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
C = DE
ST = Test State
L = Test Locality
O = Org Name
OU = Org Unit Name
CN = Common Name
emailAddress = test@email.com
EOF
# Create key pair
openssl req -config "${image}.openssl.cnf" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes
# Sign Verity root hash with it
openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s"
# Generate signature partition JSON data
echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 < "${image}.roothash.p7s")"'"}' > "${image}.verity-sig"
# Pad it
truncate -s "${signature_size}" "${image}.verity-sig"
# Register certificate in the (userspace) verity key ring
mkdir -p /run/verity.d
ln -s "${image}.crt" /run/verity.d/ok.crt
# Construct a UUID from hash
# input: 11111111222233334444555566667777
# output: 11111111-2222-3333-4444-555566667777
@ -119,11 +155,14 @@ uuid="$(head -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/
echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk "${image}.gpt"
uuid="$(tail -c 32 "${image}.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk "${image}.gpt" --append
echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append
sfdisk --part-label "${image}.gpt" 1 "Root Partition"
sfdisk --part-label "${image}.gpt" 2 "Verity Partition"
sfdisk --part-label "${image}.gpt" 3 "Signature Partition"
loop="$(losetup --show -P -f "${image}.gpt")"
dd if="${image}.raw" of="${loop}p1"
dd if="${image}.verity" of="${loop}p2"
dd if="${image}.verity-sig" of="${loop}p3"
losetup -d "${loop}"
# Derive partition UUIDs from root hash, in UUID syntax