mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
Merge pull request #24458 from poettering/stub-embedd-sig
optionally, embed PCR signature and public key in new sd-stub PE sections
This commit is contained in:
65
TODO
65
TODO
@@ -117,6 +117,11 @@ Deprecations and removals:
|
||||
|
||||
Features:
|
||||
|
||||
* during the initrd → host transition measure a fixed value into TPM PCR 11
|
||||
(where we already measure the UKI into), so that unlock policies for disk
|
||||
enryption/credential encryption can be put together that only work in the
|
||||
initrd or only on the host (or both).
|
||||
|
||||
* Add support for extra verity configuration options to systemd-reart (FEC, hash type, etc)
|
||||
|
||||
* chase_symlinks(): take inspiraton from path_extract_filename() and return
|
||||
@@ -160,11 +165,6 @@ Features:
|
||||
|
||||
* systemd-measure tool:
|
||||
- pre-calculate PCR 12 (command line) + PCR 13 (sysext) the same way we can precalculate PCR 11
|
||||
- sign pre-calculated hashes in a way compatible with TPM2 PCR hash signature
|
||||
policies, in a way they can be included in unified PE kernel images, and
|
||||
made available to userspace. There, this should be consumed by
|
||||
systemd-cryptsetup to implement PCR signature based TPM volume unlock
|
||||
policies.
|
||||
|
||||
* in sd-boot: load EFI drivers from a new PE section. That way, one can have a
|
||||
"supercharged" sd-boot binary, that could carry ext4 drivers built-in.
|
||||
@@ -249,8 +249,7 @@ Features:
|
||||
|
||||
* repart: allow defining additional partitions via credential
|
||||
|
||||
* tmpfiles: add snippet that provisions /etc/hosts, /etc/motd,
|
||||
/root/.ssh/authorized_keys from credential
|
||||
* tmpfiles: add snippet that provisions /root/.ssh/authorized_keys from credential
|
||||
|
||||
* timesyncd: pick NTP server info from credential
|
||||
|
||||
@@ -343,50 +342,11 @@ Features:
|
||||
* given that /etc/ssh/ssh_config.d/ is a thing now, ship a drop-in for that
|
||||
that hooks up userbdctl ssh-key stuff.
|
||||
|
||||
* allow embedding a signature blob for PCR hashes into separate section in
|
||||
unified kernel binaries. This section should be picked up by sd-stub, and
|
||||
passed in a file to the booted kernel (via initrd cpio, as usual). Usecase:
|
||||
this way we can implement disk encryption policies that bind to specific
|
||||
kernel PCR state, without breaking things on every kernel update. As long as
|
||||
the kernel includes the PCR signature blob we should be good, as disk
|
||||
encryption can then pass the signature to the TPM to unlock their secrets.
|
||||
Why do this via a separate PE section? That's because the PCR state depends
|
||||
on the measured kernel/initrd of course, thus we cannot put the signature
|
||||
into the kernel/initrd itself, because that would require a time machine.
|
||||
Hence we have to find a separate place. A simple solution is a PE section
|
||||
of its own, because then it is next to the kernel and initrd which after all
|
||||
are stored in PE sections of their own too. Building a unified kernel would
|
||||
thus mean, calculating PCR values for the raw kernel image, and raw initrd
|
||||
image, then signing those PCR values with a vendor key, and then combining
|
||||
sd-stub, raw kernel image, raw initrd, and PCR signature into a unified
|
||||
kernel image.
|
||||
|
||||
* a new tool "systemd-trust" or so, that can calculate PCR hashes offline, and
|
||||
optionally sign them. for that we should extend our syntax for specifying pcr
|
||||
policies (e.g. the string like "4+7+9") so that it can also include explicit
|
||||
hash values, i.e.
|
||||
4=sha256:0ef149998289474e4bb31813edda6ad7f3c991b2d8dec6e8fe4db7a1f039f2d1+7=sha256:87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7+9=sha256:0263829989b6fd954f72baaf2fc64bc2e2f01d692d4de72986ea808f6e99813f
|
||||
and file names to calculate hashes from, i.e.
|
||||
4=file:/boot/vmlinuz+7=file:/boot/initrd/+9=file:/etc/fstab"
|
||||
The systemd-trust tool should then be able to resolve any "underspecifed"
|
||||
form into the form with explicit hash values.
|
||||
|
||||
* maybe add support for binding and connecting AF_UNIX sockets in the file
|
||||
system outside of the 108ch limit. When connecting, open O_PATH fd to socket
|
||||
inode first, then connect to /proc/self/fd/XYZ. When binding, create symlink
|
||||
to target dir in /tmp, and bind through it.
|
||||
|
||||
* tmpfiles: for f/F/w lines, if the argument columns is left unspecified, look
|
||||
for a service credential named after the file path to write to, and load
|
||||
contents to write from there. Usecase: provision arbitrary files from
|
||||
credentials. Example use: with a line like "f /root/.ssh/authorized-keys
|
||||
0644 root root" in a tmpfiles.d/ snippet add
|
||||
LoadCredential=root.ssh.authorized-keys via drop-in to
|
||||
systemd-tmpfiles.service, and then provision an SSH access key through
|
||||
nspawn's --load-credential=, through qemu's fw_cfg, or via systemd-stub's
|
||||
credntial pick-up. The latter is particularly interesting to implement SSH
|
||||
access to an initrd.
|
||||
|
||||
* systemd-homed: when initializing, look for a credential sysemd.homed.register
|
||||
or so with JSON user records to automatically register if not registered yet.
|
||||
Usecase: deploy a system, and add an account one can directly log into.
|
||||
@@ -406,14 +366,11 @@ Features:
|
||||
set up the directory so that it can only be accessed if host and app are in
|
||||
order.
|
||||
|
||||
* TPM2: add auth policy for signed PCR values to make updates easy. i.e. do
|
||||
what tpm2_policyauthorize tool does. To be truly useful scheme needs to be a
|
||||
bit more elaborate though: policy probably must take some nvram based
|
||||
generation counter into account that can only monotonically increase and can
|
||||
be used to invalidate old PCR signatures. Otherwise people could downgrade to
|
||||
old signed PCR sets whenever they want. Usecase: encrypt the rootfs with LUKS
|
||||
with a key that can only be unlocked via a pristine pre-built Fedora
|
||||
kernel+initrd.
|
||||
* TPM2: extend unlock policy to protect against version downgrades in signed
|
||||
policies: policy probably must take some nvram based generation counter into
|
||||
account that can only monotonically increase and can be used to invalidate
|
||||
old PCR signatures. Otherwise people could downgrade to old signed PCR sets
|
||||
whenever they want.
|
||||
|
||||
* update HACKING.md to suggest developing systemd with the ideas from:
|
||||
https://0pointer.net/blog/testing-my-system-code-in-usr-without-modifying-usr.html
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> is
|
||||
booted up. It accepts paths to the ELF kernel image file, initial ram disk image file, devicetree file,
|
||||
kernel command line file,
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file, and
|
||||
boot splash file that make up the unified kernel image, and determines the PCR values expected to be in
|
||||
place after booting the image. Calculation starts with a zero-initialized PCR 11, and is executed in a
|
||||
fashion compatible with what <filename>systemd-stub</filename> does at boot. The result may optionally be
|
||||
signed cryptographically, to allow TPM2 policies that can only be unlocked if a certain set of kernels is
|
||||
booted, for which such a PCR signature can be provided.</para>
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file, boot
|
||||
splash file, and TPM2 PCR PEM public key file that make up the unified kernel image, and determines the
|
||||
PCR values expected to be in place after booting the image. Calculation starts with a zero-initialized
|
||||
PCR 11, and is executed in a fashion compatible with what <filename>systemd-stub</filename> does at
|
||||
boot. The result may optionally be signed cryptographically, to allow TPM2 policies that can only be
|
||||
unlocked if a certain set of kernels is booted, for which such a PCR signature can be provided.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@@ -66,9 +66,9 @@
|
||||
<listitem><para>Pre-calculate the expected values seen in PCR register 11 after boot-up of a unified
|
||||
kernel image consisting of the components specified with <option>--linux=</option>,
|
||||
<option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
|
||||
<option>--splash=</option>, <option>--dtb=</option>, see below. Only <option>--linux=</option> is
|
||||
mandatory. (Alternatively, specify <option>--current</option> to use the current values of PCR
|
||||
register 11 instead.)</para></listitem>
|
||||
<option>--splash=</option>, <option>--dtb=</option>, <option>--pcrpkey=</option> see below. Only
|
||||
<option>--linux=</option> is mandatory. (Alternatively, specify <option>--current</option> to use the
|
||||
current values of PCR register 11 instead.)</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@@ -104,6 +104,7 @@
|
||||
<term><option>--initrd=PATH</option></term>
|
||||
<term><option>--splash=PATH</option></term>
|
||||
<term><option>--dtb=PATH</option></term>
|
||||
<term><option>--pcrpkey=PATH</option></term>
|
||||
|
||||
<listitem><para>When used with the <command>calculate</command> or <command>sign</command> verb,
|
||||
configures the files to read the unified kernel image components from. Each option corresponds with
|
||||
@@ -135,7 +136,14 @@
|
||||
<term><option>--public-key=PATH</option></term>
|
||||
|
||||
<listitem><para>These switches take paths to a pair of PEM encoded RSA key files, for use with
|
||||
the <command>sign</command> command.</para></listitem>
|
||||
the <command>sign</command> command.</para>
|
||||
|
||||
<para>Note the difference between the <option>--pcrpkey=</option> and <option>--public-key=</option>
|
||||
switches. The former selects the data to include in the <literal>.pcrpkey</literal> PE section of the
|
||||
unified kernel image, the latter picks the public key of the key pair used to sign the resulting PCR
|
||||
11 values. The former is the key that the booted system will likely use to lock disk and credential
|
||||
encryption to, the latter is the key used for unlocking such resources again. Hence, typically the
|
||||
same PEM key should be supplied in both cases.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@@ -185,19 +193,11 @@
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Generate a private/public key pair, and a unified kernel image, and a TPM PCR 11 signature for it</title>
|
||||
<title>Generate a private/public key pair, and a unified kernel image, and a TPM PCR 11 signature for
|
||||
it, and embed the signature and the public key in the image</title>
|
||||
|
||||
<programlisting># openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out tpm2-pcr-private.pem
|
||||
# openssl rsa -pubout -in tpm2-pcr-private.pem -out tpm2-pcr-public.pem
|
||||
# objcopy \
|
||||
--add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
|
||||
--add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
|
||||
--add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
|
||||
--add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
|
||||
--add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
|
||||
--add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
|
||||
/usr/lib/systemd/boot/efi/linuxx64.efi.stub \
|
||||
foo.efi
|
||||
# systemd-measure sign \
|
||||
--linux=vmlinux \
|
||||
--osrel=os-release.txt \
|
||||
@@ -205,10 +205,22 @@
|
||||
--initrd=initrd.cpio \
|
||||
--splash=splash.bmp \
|
||||
--dtb=devicetree.dtb \
|
||||
--pcrpkey=tpm2-pcr-public.pem \
|
||||
--bank=sha1 \
|
||||
--bank=sha256 \
|
||||
--private-key=tpm2-pcr-private.pem \
|
||||
--public-key=tpm2-pcr-public.pem > tpm2-pcr-signature.json</programlisting>
|
||||
--public-key=tpm2-pcr-public.pem > tpm2-pcr-signature.json
|
||||
# objcopy \
|
||||
--add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
|
||||
--add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
|
||||
--add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
|
||||
--add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
|
||||
--add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
|
||||
--add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
|
||||
--add-section .pcrsig=tpm2-pcr-signature.json --change-section-vma .splash=0x80000 \
|
||||
--add-section .pcrpkey=tpm2-pcr-public.pem --change-section-vma .splash=0x90000 \
|
||||
/usr/lib/systemd/boot/efi/linuxx64.efi.stub \
|
||||
foo.efi</programlisting>
|
||||
|
||||
<para>Later on, enroll the signed PCR policy on a LUKS volume:</para>
|
||||
|
||||
@@ -217,6 +229,11 @@
|
||||
<para>And then unlock the device with the signature:</para>
|
||||
|
||||
<programlisting># /usr/lib/systemd/systemd-cryptsetup attach myvolume /dev/sda5 - tpm2-device=auto,tpm2-signature=/path/to/tpm2-pcr-signature.json</programlisting>
|
||||
|
||||
<para>Note that when the generated unified kernel image <filename>foo.efi</filename> is booted the
|
||||
signature and public key files will be placed at locations <command>systemd-cryptenroll</command> and
|
||||
<command>systemd-cryptsetup</command> will look for anyway, and thus these paths do not actually need to
|
||||
be specified.</para>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
system into the Linux world.</para>
|
||||
|
||||
<para>The UEFI boot stub looks for various resources for the kernel invocation inside the UEFI PE binary
|
||||
itself. This allows combining various resources inside a single PE binary image, which may then be signed
|
||||
via UEFI SecureBoot as a whole, covering all individual resources at once. Specifically it may
|
||||
include:</para>
|
||||
itself. This allows combining various resources inside a single PE binary image (usually called "Unified
|
||||
Kernel Image", or "UKI" for short), which may then be signed via UEFI SecureBoot as a whole, covering all
|
||||
individual resources at once. Specifically it may include:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>The ELF Linux kernel images will be looked for in the <literal>.linux</literal> PE
|
||||
@@ -68,6 +68,14 @@
|
||||
|
||||
<listitem><para>A boot splash (in Windows <filename>.BMP</filename> format) to show on screen before
|
||||
invoking the kernel will be looked for in the <literal>.splash</literal> PE section.</para></listitem>
|
||||
|
||||
<listitem><para>A set of cryptographic signatures for expected TPM2 PCR values when this kernel is
|
||||
booted, in JSON format, in the <literal>.pcrsig</literal> section. This is useful for implementing TPM2
|
||||
policies that bind disk encryption and similar to kernels that are signed by a specific
|
||||
key.</para></listitem>
|
||||
|
||||
<listitem><para>A public key in PEM format matching this TPM2 PCR signature data in the
|
||||
<literal>.pcrpkey</literal> section.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>If UEFI SecureBoot is enabled and the <literal>.cmdline</literal> section is present in the executed
|
||||
@@ -81,8 +89,25 @@
|
||||
DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the
|
||||
<literal>EFI_DT_FIXUP_PROTOCOL</literal> for hardware specific fixups to the DeviceTree.</para>
|
||||
|
||||
<para>The contents of these six PE sections are measured into TPM PCR 11, that is otherwise not
|
||||
used. Thus, it can be pre-calculated without too much effort.</para>
|
||||
<para>The contents of seven of these eight PE sections are measured into TPM PCR 11, that is otherwise
|
||||
not used. Thus, it can be pre-calculated without too much effort. The <literal>.pcrsig</literal> section
|
||||
is not included in this PCR measurement, since it's supposed to contain signatures for the expected
|
||||
results for these measurements, i.e. of the outputs of the measurement operation, and thus cannot also be
|
||||
input to it.</para>
|
||||
|
||||
<para>When <literal>.pcrsig</literal> and/or <literal>.pcrpkey</literal> are present in a unified kernel
|
||||
image their contents are passed to the booted kernel in an synthetic initrd cpio archive that places them in the
|
||||
<filename>/.extra/tpm2-pcr-signature.json</filename> and
|
||||
<filename>/.extra/tpm2-pcr-public-key.pem</filename> files. Typically, a
|
||||
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> line then
|
||||
ensures they are copied into <filename>/run/systemd/tpm2-pcr-signature.json</filename> and
|
||||
<filename>/run/systemd/tpm2-pcr-public-key.pem</filename> where they remain accessible even after the
|
||||
system transitions out of the initrd environment into the host file system. Tools such
|
||||
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
and <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
will automatically use files present under these paths to unlock protected resources (encrypted storage
|
||||
or credentials) or bind encryption to booted kernels.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@@ -133,7 +158,7 @@
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>TPM2 PCR Notes</title>
|
||||
<title>TPM PCR Notes</title>
|
||||
|
||||
<para>Note that when a unified kernel using <command>systemd-stub</command> is invoked the firmware will
|
||||
measure it as a whole to TPM PCR 4, covering all embedded resources, such as the stub code itself, the
|
||||
@@ -166,12 +191,12 @@
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Boot splash (embedded in the unified PE binary)</entry>
|
||||
<entry>Core kernel code (embedded in unified PE binary)</entry>
|
||||
<entry>4 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Core kernel code (embedded in unified PE binary)</entry>
|
||||
<entry>OS release information (embedded in the unified PE binary)</entry>
|
||||
<entry>4 + 11</entry>
|
||||
</row>
|
||||
|
||||
@@ -190,6 +215,21 @@
|
||||
<entry>12</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Boot splash (embedded in the unified PE binary)</entry>
|
||||
<entry>4 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>TPM2 PCR signature JSON (embedded in unified PE binary, synthesized into initrd)</entry>
|
||||
<entry>4 + 9</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>TPM2 PCR PEM public key (embedded in unified PE binary, synthesized into initrd)</entry>
|
||||
<entry>4 + 9 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Credentials (synthesized initrd from companion files)</entry>
|
||||
<entry>9 + 12</entry>
|
||||
@@ -279,6 +319,66 @@
|
||||
url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>initrd Resources</title>
|
||||
|
||||
<para>The following resources are passed as initrd cpio archives to the booted kernel, and thus make up
|
||||
the initial file system hierarchy in the initrd execution environment:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><filename>/</filename></term>
|
||||
|
||||
<listitem><para>The main initrd from the <literal>.initrd</literal> PE section of the unified kernel image.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>/.extra/credentials/*.cred</filename></term>
|
||||
<listitem><para>Credential files (suffix <literal>.cred</literal>) that are placed next to the
|
||||
unified kernel image (as described above) are copied into the
|
||||
<filename>/.extra/credentials/</filename> directory in the initrd execution
|
||||
environment.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>/.extra/global_credentials/*.cred</filename></term>
|
||||
<listitem><para>Similar, credential files in the <filename>/loader/credentials/</filename> directory
|
||||
in the file system the unified kernel image is placed in are copied into the
|
||||
<filename>/.extra/global_credentials/</filename> directory in the initrd execution
|
||||
environment.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>/.extra/sysext/*.raw</filename></term>
|
||||
<listitem><para>System extension image files (suffix <literal>.raw</literal>) that are placed next to
|
||||
the unified kernel image (as described above) are copied into the
|
||||
<filename>/.extra/sysext/</filename> directory in the initrd execution environment.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>/.extra/tpm2-pcr-signature.json</filename></term>
|
||||
<listitem><para>The TPM2 PCR signature JSON object included in the <literal>.pcrsig</literal> PE
|
||||
section of the unified kernel image is copied into the
|
||||
<filename>/.extra/tpm2-pcr-signature.json</filename> file in the initrd execution
|
||||
environment.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>/.extra/tpm2-pcr-pkey.pem</filename></term>
|
||||
<listitem><para>The PEM public key included in the <literal>.pcrpkey</literal> PE section of the
|
||||
unified kernel image is copied into the <filename>/.extra/tpm2-pcr-public-key.pem</filename> file in
|
||||
the initrd execution environment.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>Note that all these files are located in the <literal>tmpfs</literal> file system the kernel sets
|
||||
up for the initrd file hierarchy and are thus lost when the system transitions from the initrd execution
|
||||
environment into the host file system. If these resources shall be kept around over this transition they
|
||||
need to be copied to a place that survives the transition first, for example via a suitable
|
||||
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> line. By
|
||||
default, this is done for the TPM2 PCR signature and public key files.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Assembling Kernel Images</title>
|
||||
|
||||
@@ -313,6 +413,10 @@
|
||||
<para>This expects a pair of X.509 private key and certificate as parameters and then signs the UEFI PE
|
||||
executable we generated above for UEFI SecureBoot and generates a signed UEFI PE executable as
|
||||
result.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
|
||||
an example involving the <literal>.pcrsig</literal> and <literal>.pcrpkey</literal> sections.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@@ -325,7 +429,8 @@
|
||||
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
|
||||
<ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>,
|
||||
<citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry project='archlinux'><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
<citerefentry project='archlinux'><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
@@ -487,3 +487,60 @@ nothing:
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS pack_cpio_literal(
|
||||
const void *data,
|
||||
size_t data_size,
|
||||
const char *target_dir_prefix,
|
||||
const char16_t *target_filename,
|
||||
uint32_t dir_mode,
|
||||
uint32_t access_mode,
|
||||
const uint32_t tpm_pcr[],
|
||||
UINTN n_tpm_pcr,
|
||||
const char16_t *tpm_description,
|
||||
void **ret_buffer,
|
||||
UINTN *ret_buffer_size,
|
||||
bool *ret_measured) {
|
||||
|
||||
uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
|
||||
_cleanup_free_ void *buffer = NULL;
|
||||
UINTN buffer_size;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(data || data_size == 0);
|
||||
assert(target_dir_prefix);
|
||||
assert(target_filename);
|
||||
assert(tpm_pcr || n_tpm_pcr == 0);
|
||||
assert(ret_buffer);
|
||||
assert(ret_buffer_size);
|
||||
|
||||
/* Generate the leading directory inodes right before adding the first files, to the
|
||||
* archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
|
||||
|
||||
err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
|
||||
|
||||
err = pack_cpio_one(
|
||||
target_filename,
|
||||
data, data_size,
|
||||
target_dir_prefix,
|
||||
access_mode,
|
||||
&inode,
|
||||
&buffer, &buffer_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", target_filename, err);
|
||||
|
||||
err = pack_cpio_trailer(&buffer, &buffer_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
|
||||
|
||||
err = measure_cpio(buffer, buffer_size, tpm_pcr, n_tpm_pcr, tpm_description, ret_measured);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
*ret_buffer = TAKE_PTR(buffer);
|
||||
*ret_buffer_size = buffer_size;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -18,3 +18,17 @@ EFI_STATUS pack_cpio(
|
||||
void **ret_buffer,
|
||||
UINTN *ret_buffer_size,
|
||||
bool *ret_measured);
|
||||
|
||||
EFI_STATUS pack_cpio_literal(
|
||||
const void *data,
|
||||
size_t data_size,
|
||||
const char *target_dir_prefix,
|
||||
const char16_t *target_filename,
|
||||
uint32_t dir_mode,
|
||||
uint32_t access_mode,
|
||||
const uint32_t tpm_pcr[],
|
||||
UINTN n_tpm_pcr,
|
||||
const char16_t *tpm_description,
|
||||
void **ret_buffer,
|
||||
UINTN *ret_buffer_size,
|
||||
bool *ret_measured);
|
||||
|
||||
@@ -20,9 +20,7 @@ _used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: syste
|
||||
|
||||
static EFI_STATUS combine_initrd(
|
||||
EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size,
|
||||
const void *credential_initrd, UINTN credential_initrd_size,
|
||||
const void *global_credential_initrd, UINTN global_credential_initrd_size,
|
||||
const void *sysext_initrd, UINTN sysext_initrd_size,
|
||||
const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
|
||||
EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) {
|
||||
|
||||
EFI_PHYSICAL_ADDRESS base = UINT32_MAX; /* allocate an area below the 32bit boundary for this */
|
||||
@@ -36,23 +34,15 @@ static EFI_STATUS combine_initrd(
|
||||
/* Combines four initrds into one, by simple concatenation in memory */
|
||||
|
||||
n = ALIGN4(initrd_size); /* main initrd might not be padded yet */
|
||||
if (credential_initrd) {
|
||||
if (n > UINTN_MAX - credential_initrd_size)
|
||||
|
||||
for (size_t i = 0; i < n_extra_initrds; i++) {
|
||||
if (!extra_initrds[i])
|
||||
continue;
|
||||
|
||||
if (n > UINTN_MAX - extra_initrd_sizes[i])
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
n += credential_initrd_size;
|
||||
}
|
||||
if (global_credential_initrd) {
|
||||
if (n > UINTN_MAX - global_credential_initrd_size)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
n += global_credential_initrd_size;
|
||||
}
|
||||
if (sysext_initrd) {
|
||||
if (n > UINTN_MAX - sysext_initrd_size)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
n += sysext_initrd_size;
|
||||
n += extra_initrd_sizes[i];
|
||||
}
|
||||
|
||||
err = BS->AllocatePages(
|
||||
@@ -78,12 +68,12 @@ static EFI_STATUS combine_initrd(
|
||||
}
|
||||
}
|
||||
|
||||
if (credential_initrd)
|
||||
p = mempcpy(p, credential_initrd, credential_initrd_size);
|
||||
if (global_credential_initrd)
|
||||
p = mempcpy(p, global_credential_initrd, global_credential_initrd_size);
|
||||
if (sysext_initrd)
|
||||
p = mempcpy(p, sysext_initrd, sysext_initrd_size);
|
||||
for (size_t i = 0; i < n_extra_initrds; i++) {
|
||||
if (!extra_initrds[i])
|
||||
continue;
|
||||
|
||||
p = mempcpy(p, extra_initrds[i], extra_initrd_sizes[i]);
|
||||
}
|
||||
|
||||
assert((uint8_t*) PHYSICAL_ADDRESS_TO_POINTER(base) + n == p);
|
||||
|
||||
@@ -150,10 +140,9 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
|
||||
}
|
||||
|
||||
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
|
||||
UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
|
||||
UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
|
||||
UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0;
|
||||
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL;
|
||||
_cleanup_free_ void *sysext_initrd = NULL;
|
||||
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
|
||||
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
|
||||
@@ -190,11 +179,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
* into so far), so that we have one PCR that we can nicely write policies against because it
|
||||
* contains all static data of this image, and thus can be easily be pre-calculated. */
|
||||
for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) {
|
||||
m = false;
|
||||
|
||||
if (!unified_section_measure(section)) /* shall not measure? */
|
||||
continue;
|
||||
|
||||
if (szs[section] == 0) /* not found */
|
||||
continue;
|
||||
|
||||
m = false;
|
||||
|
||||
/* First measure the name of the section */
|
||||
(void) tpm_log_event_ascii(
|
||||
TPM_PCR_INDEX_KERNEL_IMAGE,
|
||||
@@ -301,6 +294,45 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
if (sysext_measured)
|
||||
(void) efivar_set_uint_string(LOADER_GUID, L"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS, 0);
|
||||
|
||||
/* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it
|
||||
* to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section
|
||||
* is not measured, neither as raw section (see above), nor as cpio (here), because it is the
|
||||
* signature of expected PCR values, i.e. it's input are PCR measurement, and hence it shouldn't
|
||||
* itself be input for PCR measurements. */
|
||||
if (szs[UNIFIED_SECTION_PCRSIG] > 0)
|
||||
(void) pack_cpio_literal(
|
||||
(uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRSIG],
|
||||
szs[UNIFIED_SECTION_PCRSIG],
|
||||
".extra",
|
||||
L"tpm2-pcr-signature.json",
|
||||
/* dir_mode= */ 0555,
|
||||
/* access_mode= */ 0444,
|
||||
/* tpm_pcr= */ NULL,
|
||||
/* n_tpm_pcr= */ 0,
|
||||
/* tpm_description= */ NULL,
|
||||
&pcrsig_initrd,
|
||||
&pcrsig_initrd_size,
|
||||
/* ret_measured= */ NULL);
|
||||
|
||||
/* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in
|
||||
* a cpio and also pass it to the kernel, so that it can be read from
|
||||
* /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the
|
||||
* cpio. */
|
||||
if (szs[UNIFIED_SECTION_PCRPKEY] > 0)
|
||||
(void) pack_cpio_literal(
|
||||
(uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRPKEY],
|
||||
szs[UNIFIED_SECTION_PCRPKEY],
|
||||
".extra",
|
||||
L"tpm2-pcr-public-key.pem",
|
||||
/* dir_mode= */ 0555,
|
||||
/* access_mode= */ 0444,
|
||||
/* tpm_pcr= */ NULL,
|
||||
/* n_tpm_pcr= */ 0,
|
||||
/* tpm_description= */ NULL,
|
||||
&pcrpkey_initrd,
|
||||
&pcrpkey_initrd_size,
|
||||
/* ret_measured= */ NULL);
|
||||
|
||||
linux_size = szs[UNIFIED_SECTION_LINUX];
|
||||
linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX];
|
||||
|
||||
@@ -314,9 +346,21 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
|
||||
err = combine_initrd(
|
||||
initrd_base, initrd_size,
|
||||
credential_initrd, credential_initrd_size,
|
||||
global_credential_initrd, global_credential_initrd_size,
|
||||
sysext_initrd, sysext_initrd_size,
|
||||
(const void*const[]) {
|
||||
credential_initrd,
|
||||
global_credential_initrd,
|
||||
sysext_initrd,
|
||||
pcrsig_initrd,
|
||||
pcrpkey_initrd,
|
||||
},
|
||||
(const size_t[]) {
|
||||
credential_initrd_size,
|
||||
global_credential_initrd_size,
|
||||
sysext_initrd_size,
|
||||
pcrsig_initrd_size,
|
||||
pcrpkey_initrd_size,
|
||||
},
|
||||
5,
|
||||
&initrd_base, &initrd_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
@@ -325,6 +369,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
credential_initrd = mfree(credential_initrd);
|
||||
global_credential_initrd = mfree(global_credential_initrd);
|
||||
sysext_initrd = mfree(sysext_initrd);
|
||||
pcrsig_initrd = mfree(pcrsig_initrd);
|
||||
pcrpkey_initrd = mfree(pcrpkey_initrd);
|
||||
}
|
||||
|
||||
if (dt_size > 0) {
|
||||
|
||||
@@ -68,6 +68,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --initrd=PATH Path to initrd image\n"
|
||||
" --splash=PATH Path to splash bitmap\n"
|
||||
" --dtb=PATH Path to Devicetree file\n"
|
||||
" --pcrpkey=PATH Path to public key for PCR signatures in DER format\n"
|
||||
" -c --current Use current PCR values\n"
|
||||
" --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
|
||||
" --tpm2-device=PATH Use specified TPM2 device\n"
|
||||
@@ -96,8 +97,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_CMDLINE,
|
||||
ARG_INITRD,
|
||||
ARG_SPLASH,
|
||||
ARG_DTB,
|
||||
_ARG_PCRSIG, /* the .pcrsig section is not input for signing, hence not actually an argument here */
|
||||
_ARG_SECTION_LAST,
|
||||
ARG_DTB = _ARG_SECTION_LAST,
|
||||
ARG_PCRPKEY = _ARG_SECTION_LAST,
|
||||
ARG_BANK,
|
||||
ARG_PRIVATE_KEY,
|
||||
ARG_PUBLIC_KEY,
|
||||
@@ -115,6 +118,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "initrd", required_argument, NULL, ARG_INITRD },
|
||||
{ "splash", required_argument, NULL, ARG_SPLASH },
|
||||
{ "dtb", required_argument, NULL, ARG_DTB },
|
||||
{ "pcrpkey", required_argument, NULL, ARG_PCRPKEY },
|
||||
{ "current", no_argument, NULL, 'c' },
|
||||
{ "bank", required_argument, NULL, ARG_BANK },
|
||||
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
|
||||
|
||||
@@ -11,5 +11,7 @@ const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
|
||||
[UNIFIED_SECTION_INITRD] = ".initrd",
|
||||
[UNIFIED_SECTION_SPLASH] = ".splash",
|
||||
[UNIFIED_SECTION_DTB] = ".dtb",
|
||||
[UNIFIED_SECTION_PCRSIG] = ".pcrsig",
|
||||
[UNIFIED_SECTION_PCRPKEY] = ".pcrpkey",
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "macro-fundamental.h"
|
||||
|
||||
/* The various TPM PCRs we measure into from sd-stub and sd-boot. */
|
||||
|
||||
/* This TPM PCR is where we extend the sd-stub "payloads" into, before using them. i.e. the kernel ELF image,
|
||||
@@ -32,7 +34,15 @@ typedef enum UnifiedSection {
|
||||
UNIFIED_SECTION_INITRD,
|
||||
UNIFIED_SECTION_SPLASH,
|
||||
UNIFIED_SECTION_DTB,
|
||||
UNIFIED_SECTION_PCRSIG,
|
||||
UNIFIED_SECTION_PCRPKEY,
|
||||
_UNIFIED_SECTION_MAX,
|
||||
} UnifiedSection;
|
||||
|
||||
extern const char* const unified_sections[_UNIFIED_SECTION_MAX + 1];
|
||||
|
||||
static inline bool unified_section_measure(UnifiedSection section) {
|
||||
/* Don't include the PCR signature in the PCR measurements, since they sign the expected result of
|
||||
* the measurement, and hence shouldn't be input to it. */
|
||||
return section >= 0 && section < _UNIFIED_SECTION_MAX && section != UNIFIED_SECTION_PCRSIG;
|
||||
}
|
||||
|
||||
@@ -64,3 +64,9 @@ d /var/lib/systemd/coredump 0755 root root 3d
|
||||
d /var/lib/private 0700 root root -
|
||||
d /var/log/private 0700 root root -
|
||||
d /var/cache/private 0700 root root -
|
||||
|
||||
{% if ENABLE_EFI %}
|
||||
# Copy sd-stub provided PCR signature and and public key file from initrd into /run/, so that it will survive the initrd stage
|
||||
C /run/systemd/tpm2-pcr-signature.json 0444 root root - /.extra/tpm2-pcr-signature.json
|
||||
C /run/systemd/tpm2-pcr-public-key.pem 0444 root root - /.extra/tpm2-pcr-public-key.pem
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user