mirror of
https://github.com/systemd/systemd
synced 2024-07-21 02:05:05 +00:00
Merge pull request #24146 from poettering/efi-stub-measure-payload
stub: measure kernel/initrd/parameters into clean PCRs 11/12/13, and add "systemd-measure" tool to be able to pre-calculate values
This commit is contained in:
commit
675a644de2
27
TODO
27
TODO
|
@ -119,6 +119,14 @@ Features:
|
|||
on other disks. Always boot into them via NextBoot EFI variable, to not
|
||||
affect PCR values.
|
||||
|
||||
* 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.
|
||||
|
||||
|
@ -385,12 +393,6 @@ Features:
|
|||
case the same wd is reused multiple times before we start processing
|
||||
IN_IGNORED again)
|
||||
|
||||
* sd-stub: set efi var indicating stub features, i.e. whether they pick up
|
||||
creds, sysexts and so on. similar to existing variable of sd-boot
|
||||
|
||||
* sd-stub: set efi vars declaring TPM PCRs we measured creds/cmdline + sysext
|
||||
into (even if we hardcode them)
|
||||
|
||||
* systemd-fstab-generator: support addition mount specifications via kernel
|
||||
cmdline. Usecase: invoke a VM, and mount a host homedir into it via
|
||||
virtio-fs.
|
||||
|
@ -413,10 +415,6 @@ Features:
|
|||
- sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
|
||||
and synthesize initrd from it, and measure it. Signing is not necessary, as
|
||||
microcode does that on its own. Pass as first initrd to kernel.
|
||||
- sd-stub should measure the kernel/initrd/… into a separate PCR, so that we
|
||||
have one PCR we can bind the encrypted creds to that is not effected by
|
||||
anything else but what we drop in via kernel-install, i.e. by earlier EFI
|
||||
code running (i.e. like PCR 4)
|
||||
|
||||
* Add a new service type very similar to Type=notify, that goes one step
|
||||
further and extends the protocol to cover reloads. Specifically, SIGHUP will
|
||||
|
@ -660,7 +658,7 @@ Features:
|
|||
dep in the base OS image)
|
||||
|
||||
* sysext: automatically activate sysext images dropped in via new sd-stub
|
||||
sysext pickup logic.
|
||||
sysext pickup logic. (must insist on verity + signature on those though)
|
||||
|
||||
* add concept for "exitrd" as inverse of "initrd", that we can transition to at
|
||||
shutdown, and has similar security semantics. This should then take the place
|
||||
|
@ -708,9 +706,9 @@ Features:
|
|||
what must be read-only, what requires encryption, and what requires
|
||||
authentication.
|
||||
|
||||
* in uefi stub: query firmware regarding which PCRs are being used, store that
|
||||
in EFI var. then use this when enrolling TPM2 in cryptsetup to verify that
|
||||
the selected PCRs actually are used by firmware.
|
||||
* in uefi stub: query firmware regarding which PCR banks are being used, store
|
||||
that in EFI var. then use this when enrolling TPM2 in cryptsetup to verify
|
||||
that the selected PCRs actually are used by firmware.
|
||||
|
||||
* rework recursive read-only remount to use new mount API
|
||||
|
||||
|
@ -1606,7 +1604,6 @@ Features:
|
|||
- show whether UEFI audit mode is available
|
||||
- teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
|
||||
- teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
|
||||
- make it operate on loopback files, dissecting enough to find ESP to operate on
|
||||
- bootspec: properly support boot attempt counters when parsing entry file names
|
||||
|
||||
* kernel-install:
|
||||
|
|
|
@ -953,6 +953,7 @@ manpages = [
|
|||
'systemd-makefs',
|
||||
'systemd-mkswap@.service'],
|
||||
''],
|
||||
['systemd-measure', '1', [], 'HAVE_GNU_EFI'],
|
||||
['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
|
||||
['systemd-mount', '1', ['systemd-umount'], ''],
|
||||
['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
|
||||
|
|
|
@ -296,11 +296,21 @@
|
|||
<entry>The IMA project measures its runtime state into this PCR.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>11</entry>
|
||||
<entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initial RAM disk (initrd).</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>12</entry>
|
||||
<entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>13</entry>
|
||||
<entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>14</entry>
|
||||
<entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
|
||||
|
|
154
man/systemd-measure.xml
Normal file
154
man/systemd-measure.xml
Normal file
|
@ -0,0 +1,154 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
<refentry id="systemd-measure" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_GNU_EFI'>
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-measure</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-measure</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-measure</refname>
|
||||
<refpurpose>Pre-calculate expected TPM2 PCR values for booted unified kernel images</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>/usr/lib/systemd/systemd-measure <arg choice="opt" rep="repeat">OPTIONS</arg></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>Note: this command is experimental for now. While it is likely to become a regular component of
|
||||
systemd, it might still change in behaviour and interface.</para>
|
||||
|
||||
<para><command>systemd-measure</command> is a tool that may be used to pre-calculate the expected TPM2
|
||||
PCR 11 values that should be seen when a unified Linux kernel image based on
|
||||
<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.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Commands</title>
|
||||
|
||||
<para>The following commands are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><command>status</command></term>
|
||||
|
||||
<listitem><para>This is the default command if none is specified. This queries the local system's
|
||||
TPM2 PCR 11+12+13 values and displays them. The data is written in a similar format as the
|
||||
<command>calculate</command> command below, and may be used to quickly compare expectation with
|
||||
reality.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>calculate</command></term>
|
||||
|
||||
<listitem><para>Pre-calculate the expected value 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.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--linux=PATH</option></term>
|
||||
<term><option>--osrel=PATH</option></term>
|
||||
<term><option>--cmdline=PATH</option></term>
|
||||
<term><option>--initrd=PATH</option></term>
|
||||
<term><option>--splash=PATH</option></term>
|
||||
<term><option>--dtb=PATH</option></term>
|
||||
|
||||
<listitem><para>When used with the <command>calculate</command> verb, configures the files to read
|
||||
the unified kernel image components from. Each option corresponds with the equally named section in
|
||||
the unified kernel PE file. The <option>--linux=</option> switch expects the path to the ELF kernel
|
||||
file that the unified PE kernel will wrap. All switches except <option>--linux=</option> are
|
||||
optional. Each option may be used at most once.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--bank=DIGEST</option></term>
|
||||
|
||||
<listitem><para>Controls the PCR banks to pre-calculate the PCR values for – in case
|
||||
<command>calculate</command> is invoked –, or the banks to show in the <command>status</command>
|
||||
output. May be used more then once to specify multiple banks. If not specified, defaults to the four
|
||||
banks <literal>sha1</literal>, <literal>sha256</literal>, <literal>sha384</literal>,
|
||||
<literal>sha512</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Examples</title>
|
||||
|
||||
<example>
|
||||
<title>Generate a unified kernel image, and calculate the expected TPM PCR 11 value</title>
|
||||
|
||||
<programlisting># 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 calculate \
|
||||
--linux=vmlinux \
|
||||
--osrel=os-release \
|
||||
--cmdline=cmdline.txt \
|
||||
--initrd=initrd.cpio \
|
||||
--splash=splash.bmp \
|
||||
--dtb=devicetree.dtb
|
||||
11:sha1=d775a7b4482450ac77e03ee19bda90bd792d6ec7
|
||||
11:sha256=bc6170f9ce28eb051ab465cd62be8cf63985276766cf9faf527ffefb66f45651
|
||||
11:sha384=1cf67dff4757e61e5a73d2a21a6694d668629bbc3761747d493f7f49ad720be02fd07263e1f93061243aec599d1ee4b4
|
||||
11:sha512=8e79acd3ddbbc8282e98091849c3530f996303c8ac8e87a3b2378b71c8b3a6e86d5c4f41ecea9e1517090c3e8ec0c714821032038f525f744960bcd082d937da
|
||||
</programlisting>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
|
@ -53,6 +53,10 @@
|
|||
<listitem><para>The ELF Linux kernel images will be looked for in the <literal>.linux</literal> PE
|
||||
section of the executed image.</para></listitem>
|
||||
|
||||
<listitem><para>OS release information, i.e. the
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file of
|
||||
the OS the kernel belongs to, in the <literal>.osrel</literal> PE section.</para></listitem>
|
||||
|
||||
<listitem><para>The initial RAM disk (initrd) will be looked for in the <literal>.initrd</literal> PE
|
||||
section.</para></listitem>
|
||||
|
||||
|
@ -76,6 +80,9 @@
|
|||
<para>If a DeviceTree is embedded in the <literal>.dtb</literal> section, it replaces an existing
|
||||
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>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -108,7 +115,7 @@
|
|||
images to the initrd. See
|
||||
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
|
||||
details on system extension images. The generated <command>cpio</command> archive containing these
|
||||
system extension images is measured into TPM PCR 4 (if a TPM is present).</para></listitem>
|
||||
system extension images is measured into TPM PCR 13 (if a TPM is present).</para></listitem>
|
||||
|
||||
<listitem><para>Files <filename>/loader/credentials/*.cred</filename> are packed up in a
|
||||
<command>cpio</command> archive and placed in the <filename>/.extra/global_credentials/</filename>
|
||||
|
@ -133,10 +140,10 @@
|
|||
core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
|
||||
|
||||
<para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
|
||||
every type of initrd will be measured twice: the initrd embedded in the kernel image will be measured to
|
||||
both PCR 4 and PCR 9; the initrd synthesized from credentials will be measured to both PCR 12 and PCR 9;
|
||||
the initrd synthesized from system extensions will be measured to both PCR 4 and PCR 9. Let's summarize
|
||||
the OS resources and the PCRs they are measured to:</para>
|
||||
every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
|
||||
measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials will be measured to both PCR
|
||||
9 and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR
|
||||
9. Let's summarize the OS resources and the PCRs they are measured to:</para>
|
||||
|
||||
<table>
|
||||
<title>OS Resource PCR Summary</title>
|
||||
|
@ -160,22 +167,22 @@
|
|||
|
||||
<row>
|
||||
<entry>Boot splash (embedded in the unified PE binary)</entry>
|
||||
<entry>4</entry>
|
||||
<entry>4 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Core kernel code (embedded in unified PE binary)</entry>
|
||||
<entry>4</entry>
|
||||
<entry>4 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Main initrd (embedded in unified PE binary)</entry>
|
||||
<entry>4 + 9</entry>
|
||||
<entry>4 + 9 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>Default kernel command line (embedded in unified PE binary)</entry>
|
||||
<entry>4</entry>
|
||||
<entry>4 + 11</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
@ -185,12 +192,12 @@
|
|||
|
||||
<row>
|
||||
<entry>Credentials (synthesized initrd from companion files)</entry>
|
||||
<entry>12 + 9</entry>
|
||||
<entry>9 + 12</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>System Extensions (synthesized initrd from companion files)</entry>
|
||||
<entry>4 + 9</entry>
|
||||
<entry>9 + 13</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
|
@ -239,6 +246,32 @@
|
|||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view
|
||||
this data.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StubPcrKernelImage</varname></term>
|
||||
|
||||
<listitem><para>The PCR register index the ELF kernel image/initial RAM disk image/boot
|
||||
splash/devicetree database/embedded command line are measured into, formatted as decimal ASCII string
|
||||
(i.e. <literal>11</literal>). This variable is set if a measurement was successfully completed, and
|
||||
remains unset otherwise.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StubPcrKernelParameters</varname></term>
|
||||
|
||||
<listitem><para>The PCR register index the kernel command line and credentials are measured into,
|
||||
formatted as decimal ASCII string (i.e. <literal>12</literal>). This variable is set if a measurement
|
||||
was successfully completed, and remains unset otherwise.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StubPcrInitRDSysExts</varname></term>
|
||||
|
||||
<listitem><para>The PCR register index the systemd extensions for the initial RAM disk image, which
|
||||
are picked up from the file system the kernel image is located on. Formatted as decimal ASCII string
|
||||
(i.e. <literal>13</literal>). This variable is set if a measurement was successfully completed, and
|
||||
remains unset otherwise.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>Note that some of the variables above may also be set by the boot loader. The stub will only set
|
||||
|
|
14
meson.build
14
meson.build
|
@ -310,6 +310,8 @@ conf.set_quoted('STATUS_UNIT_FORMAT_DEFAULT_STR', status_unit_format
|
|||
|
||||
conf.set10('FIRST_BOOT_FULL_PRESET', get_option('first-boot-full-preset'))
|
||||
|
||||
conf.set10('EFI_TPM_PCR_COMPAT', get_option('efi-tpm-pcr-compat'))
|
||||
|
||||
#####################################################################
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
@ -2539,6 +2541,18 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
|
|||
install_rpath : rootpkglibdir,
|
||||
install : true,
|
||||
install_dir : systemgeneratordir)
|
||||
|
||||
if conf.get('HAVE_OPENSSL') == 1
|
||||
executable(
|
||||
'systemd-measure',
|
||||
'src/boot/measure.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
dependencies : [libopenssl],
|
||||
install_rpath : rootpkglibdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
endif
|
||||
endif
|
||||
|
||||
executable(
|
||||
|
|
|
@ -456,13 +456,21 @@ static const char *get_efi_arch(void) {
|
|||
return EFI_MACHINE_TYPE_NAME;
|
||||
}
|
||||
|
||||
static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
|
||||
static int enumerate_binaries(
|
||||
const char *esp_path,
|
||||
const char *path,
|
||||
const char *prefix,
|
||||
char **previous,
|
||||
bool *is_first) {
|
||||
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
const char *p;
|
||||
int c = 0, r;
|
||||
|
||||
assert(esp_path);
|
||||
assert(path);
|
||||
assert(previous);
|
||||
assert(is_first);
|
||||
|
||||
p = prefix_roota(esp_path, path);
|
||||
d = opendir(p);
|
||||
|
@ -490,10 +498,24 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
|
|||
r = get_file_version(fd, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (*previous) { /* let's output the previous entry now, since now we know that there will be one more, and can draw the tree glyph properly */
|
||||
printf(" %s %s%s\n",
|
||||
*is_first ? "File:" : " ",
|
||||
special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
|
||||
*is_first = false;
|
||||
*previous = mfree(*previous);
|
||||
}
|
||||
|
||||
/* Do not output this entry immediately, but store what should be printed in a state
|
||||
* variable, because we only will know the tree glyph to print (branch or final edge) once we
|
||||
* read one more entry */
|
||||
if (r > 0)
|
||||
printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
|
||||
r = asprintf(previous, "/%s/%s (%s%s%s)", path, de->d_name, ansi_highlight(), v, ansi_normal());
|
||||
else
|
||||
printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name);
|
||||
r = asprintf(previous, "/%s/%s", path, de->d_name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
c++;
|
||||
}
|
||||
|
@ -502,9 +524,11 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
|
|||
}
|
||||
|
||||
static int status_binaries(const char *esp_path, sd_id128_t partition) {
|
||||
int r;
|
||||
_cleanup_free_ char *last = NULL;
|
||||
bool is_first = true;
|
||||
int r, k;
|
||||
|
||||
printf("Available Boot Loaders on ESP:\n");
|
||||
printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
if (!esp_path) {
|
||||
printf(" ESP: Cannot find or access mount point of ESP.\n\n");
|
||||
|
@ -516,42 +540,56 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
|
|||
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
|
||||
printf("\n");
|
||||
|
||||
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
|
||||
if (r < 0) {
|
||||
printf("\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
|
||||
if (k < 0) {
|
||||
printf("\n");
|
||||
return k;
|
||||
}
|
||||
|
||||
if (last) /* let's output the last entry now, since now we know that there will be no more, and can draw the tree glyph properly */
|
||||
printf(" %s %s%s\n",
|
||||
is_first ? "File:" : " ",
|
||||
special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
|
||||
|
||||
if (r == 0 && !arg_quiet)
|
||||
log_info("systemd-boot not installed in ESP.");
|
||||
|
||||
r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0 && !arg_quiet)
|
||||
if (k == 0 && !arg_quiet)
|
||||
log_info("No default/fallback boot loader installed in ESP.");
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
printf("\n");
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_efi_option(uint16_t id, bool in_order) {
|
||||
static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
|
||||
_cleanup_free_ char *title = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
sd_id128_t partition;
|
||||
bool active;
|
||||
int r;
|
||||
|
||||
assert(n_printed);
|
||||
|
||||
r = efi_get_boot_option(id, &title, &partition, &path, &active);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to read boot option %u: %m", id);
|
||||
|
||||
/* print only configured entries with partition information */
|
||||
if (!path || sd_id128_is_null(partition))
|
||||
if (!path || sd_id128_is_null(partition)) {
|
||||
log_debug("Ignoring boot entry %u without partition information.", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
efi_tilt_backslashes(path);
|
||||
|
||||
if (*n_printed == 0) /* Print section title before first entry */
|
||||
printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
|
||||
printf(" ID: 0x%04X\n", id);
|
||||
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
|
||||
|
@ -560,12 +598,13 @@ static int print_efi_option(uint16_t id, bool in_order) {
|
|||
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
(*n_printed)++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int status_variables(void) {
|
||||
_cleanup_free_ uint16_t *options = NULL, *order = NULL;
|
||||
int n_options, n_order;
|
||||
int n_options, n_order, n_printed = 0;
|
||||
|
||||
n_options = efi_get_boot_options(&options);
|
||||
if (n_options == -ENOENT)
|
||||
|
@ -582,9 +621,8 @@ static int status_variables(void) {
|
|||
return log_error_errno(n_order, "Failed to read EFI boot order: %m");
|
||||
|
||||
/* print entries in BootOrder first */
|
||||
printf("Boot Loaders Listed in EFI Variables:\n");
|
||||
for (int i = 0; i < n_order; i++)
|
||||
print_efi_option(order[i], true);
|
||||
(void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
|
||||
|
||||
/* print remaining entries */
|
||||
for (int i = 0; i < n_options; i++) {
|
||||
|
@ -592,12 +630,15 @@ static int status_variables(void) {
|
|||
if (options[i] == order[j])
|
||||
goto next_option;
|
||||
|
||||
print_efi_option(options[i], false);
|
||||
(void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
|
||||
|
||||
next_option:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n_printed == 0)
|
||||
printf("No boot loaders listed in EFI Variables.\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -655,8 +696,8 @@ static int status_entries(
|
|||
dollar_boot_partition_uuid = esp_partition_uuid;
|
||||
}
|
||||
|
||||
printf("Boot Loader Entries:\n"
|
||||
" $BOOT: %s", dollar_boot_path);
|
||||
printf("%sBoot Loader Entries:%s\n"
|
||||
" $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
|
||||
if (!sd_id128_is_null(dollar_boot_partition_uuid))
|
||||
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
|
||||
SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
|
||||
|
@ -665,7 +706,7 @@ static int status_entries(
|
|||
if (config->default_entry < 0)
|
||||
printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
|
||||
else {
|
||||
printf("Default Boot Loader Entry:\n");
|
||||
printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
|
||||
|
||||
r = show_boot_entry(
|
||||
boot_config_default_entry(config),
|
||||
|
@ -1714,7 +1755,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
static const struct {
|
||||
uint64_t flag;
|
||||
const char *name;
|
||||
} flags[] = {
|
||||
} loader_flags[] = {
|
||||
{ EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
|
||||
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
|
||||
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
|
||||
|
@ -1723,10 +1764,22 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
{ EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
|
||||
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
|
||||
{ EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
|
||||
{ EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
|
||||
{ EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
|
||||
{ EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
|
||||
};
|
||||
static const struct {
|
||||
uint64_t flag;
|
||||
const char *name;
|
||||
} stub_flags[] = {
|
||||
{ EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
|
||||
{ EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
|
||||
{ EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
|
||||
};
|
||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
|
||||
sd_id128_t loader_part_uuid = SD_ID128_NULL;
|
||||
uint64_t loader_features = 0;
|
||||
uint64_t loader_features = 0, stub_features = 0;
|
||||
Tpm2Support s;
|
||||
int have;
|
||||
|
||||
|
@ -1736,6 +1789,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
|
||||
read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
(void) efi_stub_get_features(&stub_features);
|
||||
|
||||
if (loader_path)
|
||||
efi_tilt_backslashes(loader_path);
|
||||
|
@ -1745,7 +1799,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
|
||||
|
||||
SecureBootMode secure = efi_get_secure_boot_mode();
|
||||
printf("System:\n");
|
||||
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
|
||||
printf(" Firmware Arch: %s\n", get_efi_arch());
|
||||
printf(" Secure Boot: %sd (%s)\n",
|
||||
|
@ -1774,11 +1828,11 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Current Boot Loader:\n");
|
||||
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
|
||||
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(loader_features, flags[i].flag), flags[i].name);
|
||||
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
|
||||
|
||||
sd_id128_t bootloader_esp_uuid;
|
||||
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
|
||||
|
@ -1787,8 +1841,11 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
if (have_bootloader_esp_uuid && !sd_id128_equal(esp_uuid, bootloader_esp_uuid))
|
||||
printf("WARNING: The boot loader reports a different ESP UUID than detected!\n");
|
||||
|
||||
if (stub)
|
||||
if (stub) {
|
||||
printf(" Stub: %s\n", stub);
|
||||
for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
|
||||
print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
|
||||
}
|
||||
if (!sd_id128_is_null(loader_part_uuid))
|
||||
printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
|
||||
SD_ID128_FORMAT_VAL(loader_part_uuid));
|
||||
|
@ -1797,7 +1854,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
|
||||
printf("\n");
|
||||
|
||||
printf("Random Seed:\n");
|
||||
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
|
||||
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), F_OK) >= 0;
|
||||
printf(" Passed to OS: %s\n", yes_no(have));
|
||||
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
|
||||
|
@ -1816,7 +1873,9 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
|
||||
printf("\n");
|
||||
} else
|
||||
printf("System:\n Not booted with EFI\n\n");
|
||||
printf("%sSystem:%s\n"
|
||||
"Not booted with EFI\n\n",
|
||||
ansi_underline(), ansi_normal());
|
||||
|
||||
if (arg_esp_path) {
|
||||
k = status_binaries(arg_esp_path, esp_uuid);
|
||||
|
|
|
@ -2339,7 +2339,7 @@ static EFI_STATUS image_start(
|
|||
loaded_image->LoadOptionsSize = strsize16(options);
|
||||
|
||||
/* Try to log any options to the TPM, especially to catch manually edited options */
|
||||
(void) tpm_log_load_options(options);
|
||||
(void) tpm_log_load_options(options, NULL);
|
||||
}
|
||||
|
||||
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
|
||||
|
@ -2443,6 +2443,9 @@ static void export_variables(
|
|||
EFI_LOADER_FEATURE_XBOOTLDR |
|
||||
EFI_LOADER_FEATURE_RANDOM_SEED |
|
||||
EFI_LOADER_FEATURE_LOAD_DRIVER |
|
||||
EFI_LOADER_FEATURE_SORT_KEY |
|
||||
EFI_LOADER_FEATURE_SAVED_ENTRY |
|
||||
EFI_LOADER_FEATURE_DEVICETREE |
|
||||
0;
|
||||
|
||||
_cleanup_free_ char16_t *infostr = NULL, *typestr = NULL;
|
||||
|
|
|
@ -315,7 +315,8 @@ EFI_STATUS pack_cpio(
|
|||
UINTN n_tpm_pcr,
|
||||
const char16_t *tpm_description,
|
||||
void **ret_buffer,
|
||||
UINTN *ret_buffer_size) {
|
||||
UINTN *ret_buffer_size,
|
||||
bool *ret_measured) {
|
||||
|
||||
_cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL;
|
||||
UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0;
|
||||
|
@ -324,6 +325,7 @@ EFI_STATUS pack_cpio(
|
|||
_cleanup_(strv_freep) char16_t **items = NULL;
|
||||
_cleanup_free_ void *buffer = NULL;
|
||||
uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
|
||||
int measured = -1;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(loaded_image);
|
||||
|
@ -432,23 +434,40 @@ EFI_STATUS pack_cpio(
|
|||
return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
|
||||
|
||||
for (UINTN i = 0; i < n_tpm_pcr; i++) {
|
||||
bool m;
|
||||
|
||||
if (tpm_pcr[i] == UINT32_MAX) /* Disabled */
|
||||
continue;
|
||||
|
||||
err = tpm_log_event(
|
||||
tpm_pcr[i],
|
||||
POINTER_TO_PHYSICAL_ADDRESS(buffer),
|
||||
buffer_size,
|
||||
tpm_description);
|
||||
if (err != EFI_SUCCESS)
|
||||
tpm_description,
|
||||
&m);
|
||||
if (err != EFI_SUCCESS) {
|
||||
log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
|
||||
measured = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
measured = measured < 0 ? m : (measured && m);
|
||||
}
|
||||
|
||||
*ret_buffer = TAKE_PTR(buffer);
|
||||
*ret_buffer_size = buffer_size;
|
||||
|
||||
if (ret_measured)
|
||||
*ret_measured = measured;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
|
||||
nothing:
|
||||
*ret_buffer = NULL;
|
||||
*ret_buffer_size = 0;
|
||||
|
||||
if (ret_measured)
|
||||
*ret_measured = true;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include <stdbool.h>
|
||||
#include <uchar.h>
|
||||
|
||||
EFI_STATUS pack_cpio(
|
||||
|
@ -15,4 +16,5 @@ EFI_STATUS pack_cpio(
|
|||
UINTN n_tpm_pcr,
|
||||
const char16_t *tpm_description,
|
||||
void **ret_buffer,
|
||||
UINTN *ret_buffer_size);
|
||||
UINTN *ret_buffer_size,
|
||||
bool *ret_measured);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
|
||||
#include "tpm-pcr.h"
|
||||
#include "macro-fundamental.h"
|
||||
#include "measure.h"
|
||||
#include "missing_efi.h"
|
||||
|
@ -141,43 +142,79 @@ bool tpm_present(void) {
|
|||
return tcg2_interface_check() || tcg1_interface_check();
|
||||
}
|
||||
|
||||
EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description) {
|
||||
EFI_TCG *tpm1;
|
||||
EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured) {
|
||||
EFI_TCG2 *tpm2;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(description);
|
||||
|
||||
/* PCR disabled */
|
||||
if (pcrindex == UINT32_MAX)
|
||||
/* If EFI_SUCCESS is returned, will initialize ret_measured to true if we actually measured
|
||||
* something, or false if measurement was turned off. */
|
||||
|
||||
if (pcrindex == UINT32_MAX) { /* PCR disabled? */
|
||||
if (ret_measured)
|
||||
*ret_measured = false;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
tpm2 = tcg2_interface_check();
|
||||
if (tpm2)
|
||||
return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
|
||||
err = tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
|
||||
else {
|
||||
EFI_TCG *tpm1;
|
||||
|
||||
tpm1 = tcg1_interface_check();
|
||||
if (tpm1)
|
||||
return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description);
|
||||
tpm1 = tcg1_interface_check();
|
||||
if (tpm1)
|
||||
err = tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description);
|
||||
else {
|
||||
/* No active TPM found, so don't return an error */
|
||||
|
||||
/* No active TPM found, so don't return an error */
|
||||
return EFI_SUCCESS;
|
||||
if (ret_measured)
|
||||
*ret_measured = false;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (err == EFI_SUCCESS && ret_measured)
|
||||
*ret_measured = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
EFI_STATUS tpm_log_load_options(const char16_t *load_options) {
|
||||
EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured) {
|
||||
_cleanup_free_ char16_t *c = NULL;
|
||||
|
||||
if (description)
|
||||
c = xstra_to_str(description);
|
||||
|
||||
return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured);
|
||||
}
|
||||
|
||||
EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured) {
|
||||
int measured = -1;
|
||||
EFI_STATUS err;
|
||||
|
||||
/* Measures a load options string into the TPM2, i.e. the kernel command line */
|
||||
|
||||
for (UINTN i = 0; i < 2; i++) {
|
||||
uint32_t pcr = i == 0 ? TPM_PCR_INDEX_KERNEL_PARAMETERS : TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT;
|
||||
bool m;
|
||||
|
||||
err = tpm_log_event(pcr,
|
||||
POINTER_TO_PHYSICAL_ADDRESS(load_options),
|
||||
strsize16(load_options), load_options);
|
||||
if (pcr == UINT32_MAX) /* Skip this one, if it's invalid, so that our 'measured' return value is not corrupted by it */
|
||||
continue;
|
||||
|
||||
err = tpm_log_event(pcr, POINTER_TO_PHYSICAL_ADDRESS(load_options), strsize16(load_options), load_options, &m);
|
||||
if (err != EFI_SUCCESS)
|
||||
return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r", pcr, err);
|
||||
|
||||
measured = measured < 0 ? m : (measured && m);
|
||||
}
|
||||
|
||||
if (ret_measured)
|
||||
*ret_measured = measured < 0 ? false : measured;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,25 +5,12 @@
|
|||
#include <stdbool.h>
|
||||
#include <uchar.h>
|
||||
|
||||
/* This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */
|
||||
#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
|
||||
|
||||
/* sd-stub used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
|
||||
* some compatibility. (Remove in 2023!) */
|
||||
#if EFI_TPM_PCR_COMPAT
|
||||
#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
|
||||
#else
|
||||
#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
|
||||
#endif
|
||||
|
||||
/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */
|
||||
#define TPM_PCR_INDEX_INITRD 4U
|
||||
|
||||
#if ENABLE_TPM
|
||||
|
||||
bool tpm_present(void);
|
||||
EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description);
|
||||
EFI_STATUS tpm_log_load_options(const char16_t *cmdline);
|
||||
EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured);
|
||||
EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured);
|
||||
EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -31,11 +18,21 @@ static inline bool tpm_present(void) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description) {
|
||||
static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured) {
|
||||
if (ret_measured)
|
||||
*ret_measured = false;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static inline EFI_STATUS tpm_log_load_options(const char16_t *cmdline) {
|
||||
static inline EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured) {
|
||||
if (ret_measured)
|
||||
*ret_measured = false;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static inline EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured) {
|
||||
if (ret_measured)
|
||||
*ret_measured = false;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "pe.h"
|
||||
#include "secure-boot.h"
|
||||
#include "splash.h"
|
||||
#include "tpm-pcr.h"
|
||||
#include "util.h"
|
||||
|
||||
/* magic string to find in the binary image */
|
||||
|
@ -102,6 +103,13 @@ static EFI_STATUS combine_initrd(
|
|||
}
|
||||
|
||||
static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
|
||||
static const uint64_t stub_features =
|
||||
EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */
|
||||
EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */
|
||||
EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */
|
||||
EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */
|
||||
0;
|
||||
|
||||
char16_t uuid[37];
|
||||
|
||||
assert(loaded_image);
|
||||
|
@ -142,31 +150,15 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
|
|||
efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
|
||||
}
|
||||
|
||||
|
||||
/* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
|
||||
* own data) */
|
||||
(void) efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
|
||||
|
||||
(void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0);
|
||||
}
|
||||
|
||||
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
||||
|
||||
enum {
|
||||
SECTION_CMDLINE,
|
||||
SECTION_LINUX,
|
||||
SECTION_INITRD,
|
||||
SECTION_SPLASH,
|
||||
SECTION_DTB,
|
||||
_SECTION_MAX,
|
||||
};
|
||||
|
||||
static const char * const sections[_SECTION_MAX + 1] = {
|
||||
[SECTION_CMDLINE] = ".cmdline",
|
||||
[SECTION_LINUX] = ".linux",
|
||||
[SECTION_INITRD] = ".initrd",
|
||||
[SECTION_SPLASH] = ".splash",
|
||||
[SECTION_DTB] = ".dtb",
|
||||
NULL,
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -174,10 +166,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
|||
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
|
||||
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
|
||||
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
|
||||
UINTN addrs[_SECTION_MAX] = {};
|
||||
UINTN szs[_SECTION_MAX] = {};
|
||||
UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
|
||||
char *cmdline = NULL;
|
||||
_cleanup_free_ char *cmdline_owned = NULL;
|
||||
int sections_measured = -1, parameters_measured = -1;
|
||||
bool sysext_measured = false, m;
|
||||
EFI_STATUS err;
|
||||
|
||||
InitializeLib(image, sys_table);
|
||||
|
@ -195,83 +188,136 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
|
|||
if (err != EFI_SUCCESS)
|
||||
return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
|
||||
|
||||
err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
|
||||
if (err != EFI_SUCCESS || szs[SECTION_LINUX] == 0) {
|
||||
err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs);
|
||||
if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
|
||||
if (err == EFI_SUCCESS)
|
||||
err = EFI_NOT_FOUND;
|
||||
return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
|
||||
}
|
||||
|
||||
/* Show splash screen as early as possible */
|
||||
graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);
|
||||
/* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
|
||||
* 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 (szs[SECTION_CMDLINE] > 0) {
|
||||
cmdline = (char *) loaded_image->ImageBase + addrs[SECTION_CMDLINE];
|
||||
cmdline_len = szs[SECTION_CMDLINE];
|
||||
if (szs[section] == 0) /* not found */
|
||||
continue;
|
||||
|
||||
/* First measure the name of the section */
|
||||
(void) tpm_log_event_ascii(
|
||||
TPM_PCR_INDEX_KERNEL_IMAGE,
|
||||
POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]),
|
||||
strsize8(unified_sections[section]), /* including NUL byte */
|
||||
unified_sections[section],
|
||||
&m);
|
||||
|
||||
sections_measured = sections_measured < 0 ? m : (sections_measured && m);
|
||||
|
||||
/* Then measure the data of the section */
|
||||
(void) tpm_log_event_ascii(
|
||||
TPM_PCR_INDEX_KERNEL_IMAGE,
|
||||
POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section],
|
||||
szs[section],
|
||||
unified_sections[section],
|
||||
&m);
|
||||
|
||||
sections_measured = sections_measured < 0 ? m : (sections_measured && m);
|
||||
}
|
||||
|
||||
/* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
|
||||
if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
|
||||
*(char16_t *) loaded_image->LoadOptions > 0x1F) {
|
||||
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
|
||||
cmdline = cmdline_owned = xmalloc(cmdline_len);
|
||||
/* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
|
||||
* in it which PCR was used. */
|
||||
if (sections_measured > 0)
|
||||
(void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE, 0);
|
||||
|
||||
for (UINTN i = 0; i < cmdline_len; i++)
|
||||
cmdline[i] = ((char16_t *) loaded_image->LoadOptions)[i];
|
||||
/* Show splash screen as early as possible */
|
||||
graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH], NULL);
|
||||
|
||||
if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
|
||||
cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE];
|
||||
cmdline_len = szs[UNIFIED_SECTION_CMDLINE];
|
||||
}
|
||||
|
||||
/* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
|
||||
* the built-in one. We also do a superficial check whether first chararacter of passed command line
|
||||
* is printable character (for compat with some Dell systems which fill in garbage?). */
|
||||
if ((!secure_boot_enabled() || cmdline_len == 0) &&
|
||||
loaded_image->LoadOptionsSize > 0 &&
|
||||
((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
|
||||
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
|
||||
cmdline = cmdline_owned = xnew(char, cmdline_len);
|
||||
|
||||
for (UINTN i = 0; i < cmdline_len; i++) {
|
||||
char16_t c = ((char16_t *) loaded_image->LoadOptions)[i];
|
||||
cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */
|
||||
}
|
||||
|
||||
/* Let's measure the passed kernel command line into the TPM. Note that this possibly
|
||||
* duplicates what we already did in the boot menu, if that was already used. However, since
|
||||
* we want the boot menu to support an EFI binary, and want to this stub to be usable from
|
||||
* any boot menu, let's measure things anyway. */
|
||||
(void) tpm_log_load_options(loaded_image->LoadOptions);
|
||||
m = false;
|
||||
(void) tpm_log_load_options(loaded_image->LoadOptions, &m);
|
||||
parameters_measured = m;
|
||||
}
|
||||
|
||||
export_variables(loaded_image);
|
||||
|
||||
(void) pack_cpio(loaded_image,
|
||||
NULL,
|
||||
L".cred",
|
||||
".extra/credentials",
|
||||
/* dir_mode= */ 0500,
|
||||
/* access_mode= */ 0400,
|
||||
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
|
||||
/* n_tpm_pcr= */ 2,
|
||||
L"Credentials initrd",
|
||||
&credential_initrd,
|
||||
&credential_initrd_size);
|
||||
if (pack_cpio(loaded_image,
|
||||
NULL,
|
||||
L".cred",
|
||||
".extra/credentials",
|
||||
/* dir_mode= */ 0500,
|
||||
/* access_mode= */ 0400,
|
||||
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
|
||||
/* n_tpm_pcr= */ 2,
|
||||
L"Credentials initrd",
|
||||
&credential_initrd,
|
||||
&credential_initrd_size,
|
||||
&m) == EFI_SUCCESS)
|
||||
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
|
||||
|
||||
(void) pack_cpio(loaded_image,
|
||||
L"\\loader\\credentials",
|
||||
L".cred",
|
||||
".extra/global_credentials",
|
||||
/* dir_mode= */ 0500,
|
||||
/* access_mode= */ 0400,
|
||||
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
|
||||
/* n_tpm_pcr= */ 2,
|
||||
L"Global credentials initrd",
|
||||
&global_credential_initrd,
|
||||
&global_credential_initrd_size);
|
||||
if (pack_cpio(loaded_image,
|
||||
L"\\loader\\credentials",
|
||||
L".cred",
|
||||
".extra/global_credentials",
|
||||
/* dir_mode= */ 0500,
|
||||
/* access_mode= */ 0400,
|
||||
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
|
||||
/* n_tpm_pcr= */ 2,
|
||||
L"Global credentials initrd",
|
||||
&global_credential_initrd,
|
||||
&global_credential_initrd_size,
|
||||
&m) == EFI_SUCCESS)
|
||||
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
|
||||
|
||||
(void) pack_cpio(loaded_image,
|
||||
NULL,
|
||||
L".raw",
|
||||
".extra/sysext",
|
||||
/* dir_mode= */ 0555,
|
||||
/* access_mode= */ 0444,
|
||||
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD },
|
||||
/* n_tpm_pcr= */ 1,
|
||||
L"System extension initrd",
|
||||
&sysext_initrd,
|
||||
&sysext_initrd_size);
|
||||
if (pack_cpio(loaded_image,
|
||||
NULL,
|
||||
L".raw",
|
||||
".extra/sysext",
|
||||
/* dir_mode= */ 0555,
|
||||
/* access_mode= */ 0444,
|
||||
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD_SYSEXTS },
|
||||
/* n_tpm_pcr= */ 1,
|
||||
L"System extension initrd",
|
||||
&sysext_initrd,
|
||||
&sysext_initrd_size,
|
||||
&m) == EFI_SUCCESS)
|
||||
sysext_measured = m;
|
||||
|
||||
linux_size = szs[SECTION_LINUX];
|
||||
linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
|
||||
if (parameters_measured > 0)
|
||||
(void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelParameters", TPM_PCR_INDEX_KERNEL_PARAMETERS, 0);
|
||||
if (sysext_measured)
|
||||
(void) efivar_set_uint_string(LOADER_GUID, L"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS, 0);
|
||||
|
||||
initrd_size = szs[SECTION_INITRD];
|
||||
initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0;
|
||||
linux_size = szs[UNIFIED_SECTION_LINUX];
|
||||
linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX];
|
||||
|
||||
dt_size = szs[SECTION_DTB];
|
||||
dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_DTB] : 0;
|
||||
initrd_size = szs[UNIFIED_SECTION_INITRD];
|
||||
initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
|
||||
|
||||
dt_size = szs[UNIFIED_SECTION_DTB];
|
||||
dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
|
||||
|
||||
if (credential_initrd || global_credential_initrd || sysext_initrd) {
|
||||
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
|
||||
|
|
|
@ -637,6 +637,31 @@ __attribute__((noinline)) void debug_break(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EFI_DEBUG
|
||||
void hexdump(const char16_t *prefix, const void *data, UINTN size) {
|
||||
static const char hex[16] = "0123456789abcdef";
|
||||
_cleanup_free_ char16_t *buf = NULL;
|
||||
const uint8_t *d = data;
|
||||
|
||||
assert(prefix);
|
||||
assert(data || size == 0);
|
||||
|
||||
/* Debugging helper — please keep this around, even if not used */
|
||||
|
||||
buf = xnew(char16_t, size*2+1);
|
||||
|
||||
for (UINTN i = 0; i < size; i++) {
|
||||
buf[i*2] = hex[d[i] >> 4];
|
||||
buf[i*2+1] = hex[d[i] & 0x0F];
|
||||
}
|
||||
|
||||
buf[size*2] = 0;
|
||||
|
||||
log_error_stall(L"%s[%" PRIuN "]: %s", prefix, size, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t value;
|
||||
|
|
|
@ -167,6 +167,10 @@ extern uint8_t _text, _data;
|
|||
# define debug_hook(identity)
|
||||
#endif
|
||||
|
||||
#ifdef EFI_DEBUG
|
||||
void hexdump(const char16_t *prefix, const void *data, UINTN size);
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
void beep(UINTN beep_count);
|
||||
#else
|
||||
|
|
533
src/boot/measure.c
Normal file
533
src/boot/measure.c
Normal file
|
@ -0,0 +1,533 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "efi-loader.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "main-func.h"
|
||||
#include "openssl-util.h"
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tpm-pcr.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
/* Tool for pre-calculating expected TPM PCR values based on measured resources. This is intended to be used
|
||||
* to pre-calculate suitable values for PCR 11, the way sd-stub measures into it. */
|
||||
|
||||
static char *arg_sections[_UNIFIED_SECTION_MAX] = {};
|
||||
static char **arg_banks = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
|
||||
|
||||
static inline void free_sections(char*(*sections)[_UNIFIED_SECTION_MAX]) {
|
||||
for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++)
|
||||
free((*sections)[c]);
|
||||
}
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_sections, free_sections);
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-measure", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%1$s [OPTIONS...] COMMAND ...\n"
|
||||
"\n%5$sPre-calculate PCR hash for kernel image.%6$s\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" status Show current PCR values\n"
|
||||
" calculate Calculate expected PCR values\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version\n"
|
||||
" --linux=PATH Path Linux kernel ELF image\n"
|
||||
" --osrel=PATH Path to os-release file\n"
|
||||
" --cmdline=PATH Path to file with kernel command line\n"
|
||||
" --initrd=PATH Path to initrd image\n"
|
||||
" --splash=PATH Path to splash bitmap\n"
|
||||
" --dtb=PATH Path to Devicetree file\n"
|
||||
" --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
_ARG_SECTION_FIRST,
|
||||
ARG_LINUX = _ARG_SECTION_FIRST,
|
||||
ARG_OSREL,
|
||||
ARG_CMDLINE,
|
||||
ARG_INITRD,
|
||||
ARG_SPLASH,
|
||||
_ARG_SECTION_LAST,
|
||||
ARG_DTB = _ARG_SECTION_LAST,
|
||||
ARG_BANK,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "linux", required_argument, NULL, ARG_LINUX },
|
||||
{ "osrel", required_argument, NULL, ARG_OSREL },
|
||||
{ "cmdline", required_argument, NULL, ARG_CMDLINE },
|
||||
{ "initrd", required_argument, NULL, ARG_INITRD },
|
||||
{ "splash", required_argument, NULL, ARG_SPLASH },
|
||||
{ "dtb", required_argument, NULL, ARG_DTB },
|
||||
{ "bank", required_argument, NULL, ARG_BANK },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
/* Make sure the arguments list and the section list, stays in sync */
|
||||
assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help(0, NULL, NULL);
|
||||
return 0;
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case _ARG_SECTION_FIRST..._ARG_SECTION_LAST: {
|
||||
UnifiedSection section = c - _ARG_SECTION_FIRST;
|
||||
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, arg_sections + section);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_BANK: {
|
||||
const EVP_MD *implementation;
|
||||
|
||||
implementation = EVP_get_digestbyname(optarg);
|
||||
if (!implementation)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", optarg);
|
||||
|
||||
if (strv_extend(&arg_banks, EVP_MD_name(implementation)) < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (strv_isempty(arg_banks)) {
|
||||
/* If no banks are specifically selected, pick all known banks */
|
||||
arg_banks = strv_new("SHA1", "SHA256", "SHA384", "SHA512");
|
||||
if (!arg_banks)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
strv_sort(arg_banks);
|
||||
strv_uniq(arg_banks);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct PcrState {
|
||||
const EVP_MD *md;
|
||||
void *value;
|
||||
size_t value_size;
|
||||
} PcrState;
|
||||
|
||||
static void pcr_state_free_all(PcrState **pcr_state) {
|
||||
assert(pcr_state);
|
||||
|
||||
if (!*pcr_state)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; (*pcr_state)[i].value; i++)
|
||||
free((*pcr_state)[i].value);
|
||||
|
||||
*pcr_state = mfree(*pcr_state);
|
||||
}
|
||||
|
||||
static void evp_md_ctx_free_all(EVP_MD_CTX **md[]) {
|
||||
assert(md);
|
||||
|
||||
if (!*md)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; (*md)[i]; i++)
|
||||
EVP_MD_CTX_free((*md)[i]);
|
||||
|
||||
*md = mfree(*md);
|
||||
}
|
||||
|
||||
static int pcr_state_extend(PcrState *pcr_state, const void *data, size_t sz) {
|
||||
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mc = NULL;
|
||||
unsigned value_size;
|
||||
|
||||
assert(pcr_state);
|
||||
assert(data || sz == 0);
|
||||
assert(pcr_state->md);
|
||||
assert(pcr_state->value);
|
||||
assert(pcr_state->value_size > 0);
|
||||
|
||||
/* Extends a (virtual) PCR by the given data */
|
||||
|
||||
mc = EVP_MD_CTX_new();
|
||||
if (!mc)
|
||||
return log_oom();
|
||||
|
||||
if (EVP_DigestInit_ex(mc, pcr_state->md, NULL) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize %s context.", EVP_MD_name(pcr_state->md));
|
||||
|
||||
/* First thing we do, is hash the old PCR value */
|
||||
if (EVP_DigestUpdate(mc, pcr_state->value, pcr_state->value_size) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
|
||||
|
||||
/* Then, we hash the new data */
|
||||
if (EVP_DigestUpdate(mc, data, sz) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
|
||||
|
||||
if (EVP_DigestFinal_ex(mc, pcr_state->value, &value_size) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
|
||||
|
||||
assert(value_size == pcr_state->value_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE (16U * 1024U)
|
||||
|
||||
static int measure_pcr(PcrState *pcr_states, size_t n) {
|
||||
_cleanup_free_ void *buffer = NULL;
|
||||
int r;
|
||||
|
||||
assert(n > 0);
|
||||
assert(pcr_states);
|
||||
|
||||
buffer = malloc(BUFFER_SIZE);
|
||||
if (!buffer)
|
||||
return log_oom();
|
||||
|
||||
for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++) {
|
||||
_cleanup_(evp_md_ctx_free_all) EVP_MD_CTX **mdctx = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
uint64_t m = 0;
|
||||
|
||||
if (!arg_sections[c])
|
||||
continue;
|
||||
|
||||
fd = open(arg_sections[c], O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open '%s': %m", arg_sections[c]);
|
||||
|
||||
/* Allocate one message digest context per bank (NULL terminated) */
|
||||
mdctx = new0(EVP_MD_CTX*, n + 1);
|
||||
if (!mdctx)
|
||||
return log_oom();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
mdctx[i] = EVP_MD_CTX_new();
|
||||
if (!mdctx[i])
|
||||
return log_oom();
|
||||
|
||||
if (EVP_DigestInit_ex(mdctx[i], pcr_states[i].md, NULL) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize data %s context.", EVP_MD_name(pcr_states[i].md));
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ssize_t sz;
|
||||
|
||||
sz = read(fd, buffer, BUFFER_SIZE);
|
||||
if (sz < 0)
|
||||
return log_error_errno(errno, "Failed to read '%s': %m", arg_sections[c]);
|
||||
if (sz == 0) /* EOF */
|
||||
break;
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (EVP_DigestUpdate(mdctx[i], buffer, sz) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
|
||||
|
||||
m += sz;
|
||||
}
|
||||
|
||||
fd = safe_close(fd);
|
||||
|
||||
if (m == 0) /* We skip over empty files, the stub does so too */
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
_cleanup_free_ void *data_hash = NULL;
|
||||
unsigned data_hash_size;
|
||||
|
||||
data_hash = malloc(pcr_states[i].value_size);
|
||||
if (!data_hash)
|
||||
return log_oom();
|
||||
|
||||
/* Measure name of section */
|
||||
if (EVP_Digest(unified_sections[c], strlen(unified_sections[c]) + 1, data_hash, &data_hash_size, pcr_states[i].md, NULL) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to hash section name with %s.", EVP_MD_name(pcr_states[i].md));
|
||||
|
||||
assert(data_hash_size == (unsigned) pcr_states[i].value_size);
|
||||
|
||||
r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Retrieve hash of data an measure it*/
|
||||
if (EVP_DigestFinal_ex(mdctx[i], data_hash, &data_hash_size) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
|
||||
|
||||
assert(data_hash_size == (unsigned) pcr_states[i].value_size);
|
||||
|
||||
r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_calculate(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
if (!arg_sections[UNIFIED_SECTION_LINUX])
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--linux= switch must be specified, refusing.");
|
||||
|
||||
pcr_states = new0(PcrState, strv_length(arg_banks) + 1);
|
||||
if (!pcr_states)
|
||||
return log_oom();
|
||||
|
||||
/* Allocate a PCR state structure, one for each bank */
|
||||
STRV_FOREACH(d, arg_banks) {
|
||||
const EVP_MD *implementation;
|
||||
_cleanup_free_ void *v = NULL;
|
||||
int sz;
|
||||
|
||||
assert_se(implementation = EVP_get_digestbyname(*d)); /* Must work, we already checked while parsing command line */
|
||||
|
||||
sz = EVP_MD_size(implementation);
|
||||
if (sz <= 0 || sz >= INT_MAX)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected digest size: %i", sz);
|
||||
|
||||
v = malloc0(sz); /* initial PCR state is all zeroes */
|
||||
if (!v)
|
||||
return log_oom();
|
||||
|
||||
pcr_states[n++] = (struct PcrState) {
|
||||
.md = implementation,
|
||||
.value = TAKE_PTR(v),
|
||||
.value_size = sz,
|
||||
};
|
||||
}
|
||||
|
||||
r = measure_pcr(pcr_states, n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
_cleanup_free_ char *hd = NULL, *b = NULL;
|
||||
|
||||
hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
|
||||
if (!hd)
|
||||
return log_oom();
|
||||
|
||||
b = strdup(EVP_MD_name(pcr_states[i].md));
|
||||
if (!b)
|
||||
return log_oom();
|
||||
|
||||
printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, ascii_strlower(b), hd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
uint32_t v;
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_string(varname, &s);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read EFI variable '%s': %m", varname);
|
||||
|
||||
r = safe_atou32(s, &v);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse EFI variable '%s': %s", varname, s);
|
||||
|
||||
if (pcr != v)
|
||||
log_warning("PCR number reported by stub for %s (%" PRIu32 ") different from our expectation (%" PRIu32 ").\n"
|
||||
"The measurements are likely inconsistent.", description, v, pcr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_stub(void) {
|
||||
uint64_t features;
|
||||
bool found = false;
|
||||
int r;
|
||||
|
||||
if (tpm2_support() != TPM2_SUPPORT_FULL)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Sorry, system lacks full TPM2 support.");
|
||||
|
||||
r = efi_stub_get_features(&features);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to get stub features: %m");
|
||||
|
||||
if (!FLAGS_SET(features, EFI_STUB_FEATURE_THREE_PCRS))
|
||||
log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
|
||||
"The PCR measurements seen are unlikely to be valid.");
|
||||
|
||||
r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_IMAGE, EFI_LOADER_VARIABLE("StubPcrKernelImage"), "kernel image");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_PARAMETERS, EFI_LOADER_VARIABLE("StubPcrKernelParameters"), "kernel parameters");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = compare_reported_pcr_nr(TPM_PCR_INDEX_INITRD_SYSEXTS, EFI_LOADER_VARIABLE("StubPcrInitRDSysExts"), "initrd system extension images");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(bank, arg_banks) {
|
||||
_cleanup_free_ char *b = NULL, *p = NULL;
|
||||
|
||||
b = strdup(*bank);
|
||||
if (!b)
|
||||
return log_oom();
|
||||
|
||||
if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/", ascii_strlower(b)) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (access(p, F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to detect if '%s' exists: %m", b);
|
||||
} else
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "None of the select PCR banks appear to exist.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_status(int argc, char *argv[], void *userdata) {
|
||||
|
||||
static const struct {
|
||||
uint32_t nr;
|
||||
const char *description;
|
||||
} relevant_pcrs[] = {
|
||||
{ TPM_PCR_INDEX_KERNEL_IMAGE, "Unified Kernel Image" },
|
||||
{ TPM_PCR_INDEX_KERNEL_PARAMETERS, "Kernel Parameters" },
|
||||
{ TPM_PCR_INDEX_INITRD_SYSEXTS, "initrd System Extensions" },
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
r = validate_stub();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) {
|
||||
|
||||
STRV_FOREACH(bank, arg_banks) {
|
||||
_cleanup_free_ char *b = NULL, *p = NULL, *s = NULL, *f = NULL;
|
||||
_cleanup_free_ void *h = NULL;
|
||||
size_t l;
|
||||
|
||||
b = strdup(*bank);
|
||||
if (!b)
|
||||
return log_oom();
|
||||
|
||||
if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, ascii_strlower(b), relevant_pcrs[i].nr) < 0)
|
||||
return log_oom();
|
||||
|
||||
r = read_virtual_file(p, 4096, &s, NULL);
|
||||
if (r == -ENOENT)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read '%s': %m", p);
|
||||
|
||||
r = unhexmem(strstrip(s), SIZE_MAX, &h, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
|
||||
|
||||
f = hexmem(h, l);
|
||||
if (!h)
|
||||
return log_oom();
|
||||
|
||||
if (bank == arg_banks) {
|
||||
/* before the first line for each PCR, write a short descriptive text to
|
||||
* stderr, and leave the primary content on stdout */
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
|
||||
ansi_grey(),
|
||||
relevant_pcrs[i].nr,
|
||||
relevant_pcrs[i].description,
|
||||
memeqzero(h, l) ? " (NOT SET!)" : "",
|
||||
ansi_normal());
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int measure_main(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
|
||||
{ "calculate", VERB_ANY, 1, 0, verb_calculate },
|
||||
{}
|
||||
};
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, NULL);
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_show_color(true);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return measure_main(argc, argv);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
|
@ -4,6 +4,7 @@
|
|||
#include <errno.h>
|
||||
#include "string-util-fundamental.h"
|
||||
|
||||
/* Features of the loader, i.e. systemd-boot */
|
||||
#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0)
|
||||
#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
|
||||
#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2)
|
||||
|
@ -12,6 +13,15 @@
|
|||
#define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5)
|
||||
#define EFI_LOADER_FEATURE_RANDOM_SEED (UINT64_C(1) << 6)
|
||||
#define EFI_LOADER_FEATURE_LOAD_DRIVER (UINT64_C(1) << 7)
|
||||
#define EFI_LOADER_FEATURE_SORT_KEY (UINT64_C(1) << 8)
|
||||
#define EFI_LOADER_FEATURE_SAVED_ENTRY (UINT64_C(1) << 9)
|
||||
#define EFI_LOADER_FEATURE_DEVICETREE (UINT64_C(1) << 10)
|
||||
|
||||
/* Features of the stub, i.e. systemd-stub */
|
||||
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
|
||||
#define EFI_STUB_FEATURE_PICK_UP_CREDENTIALS (UINT64_C(1) << 1)
|
||||
#define EFI_STUB_FEATURE_PICK_UP_SYSEXTS (UINT64_C(1) << 2)
|
||||
#define EFI_STUB_FEATURE_THREE_PCRS (UINT64_C(1) << 3)
|
||||
|
||||
typedef enum SecureBootMode {
|
||||
SECURE_BOOT_UNSUPPORTED,
|
||||
|
|
|
@ -8,6 +8,7 @@ fundamental_headers = files(
|
|||
'macro-fundamental.h',
|
||||
'sha256.h',
|
||||
'string-util-fundamental.h',
|
||||
'tpm-pcr.h',
|
||||
)
|
||||
|
||||
# for sd-boot
|
||||
|
@ -16,6 +17,7 @@ fundamental_source_paths = files(
|
|||
'efivars-fundamental.c',
|
||||
'sha256.c',
|
||||
'string-util-fundamental.c',
|
||||
'tpm-pcr.c',
|
||||
)
|
||||
|
||||
# for libbasic
|
||||
|
|
15
src/fundamental/tpm-pcr.c
Normal file
15
src/fundamental/tpm-pcr.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "tpm-pcr.h"
|
||||
|
||||
const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
|
||||
[UNIFIED_SECTION_LINUX] = ".linux",
|
||||
[UNIFIED_SECTION_OSREL] = ".osrel",
|
||||
[UNIFIED_SECTION_CMDLINE] = ".cmdline",
|
||||
[UNIFIED_SECTION_INITRD] = ".initrd",
|
||||
[UNIFIED_SECTION_SPLASH] = ".splash",
|
||||
[UNIFIED_SECTION_DTB] = ".dtb",
|
||||
NULL,
|
||||
};
|
38
src/fundamental/tpm-pcr.h
Normal file
38
src/fundamental/tpm-pcr.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
/* 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,
|
||||
* embedded initrd, and so on. In contrast to PCR 4 (which also contains this data, given the whole
|
||||
* surrounding PE image is measured into it) this should be reasonably pre-calculatable, because it *only*
|
||||
* consists of static data from the kernel PE image. */
|
||||
#define TPM_PCR_INDEX_KERNEL_IMAGE 11U
|
||||
|
||||
/* This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */
|
||||
#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
|
||||
|
||||
/* sd-stub used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
|
||||
* some compatibility. (Remove in 2023!) */
|
||||
#if EFI_TPM_PCR_COMPAT
|
||||
#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
|
||||
#else
|
||||
#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
|
||||
#endif
|
||||
|
||||
/* This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */
|
||||
#define TPM_PCR_INDEX_INITRD_SYSEXTS 13U
|
||||
|
||||
/* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in
|
||||
* which we measure the sections into TPM PCR 11 (see above). PLEASE DO NOT REORDER! */
|
||||
typedef enum UnifiedSection {
|
||||
UNIFIED_SECTION_LINUX,
|
||||
UNIFIED_SECTION_OSREL,
|
||||
UNIFIED_SECTION_CMDLINE,
|
||||
UNIFIED_SECTION_INITRD,
|
||||
UNIFIED_SECTION_SPLASH,
|
||||
UNIFIED_SECTION_DTB,
|
||||
_UNIFIED_SECTION_MAX,
|
||||
} UnifiedSection;
|
||||
|
||||
extern const char* const unified_sections[_UNIFIED_SECTION_MAX + 1];
|
|
@ -187,6 +187,54 @@ int efi_loader_get_features(uint64_t *ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int efi_stub_get_features(uint64_t *ret) {
|
||||
_cleanup_free_ void *v = NULL;
|
||||
size_t s;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(StubFeatures), NULL, &v, &s);
|
||||
if (r == -ENOENT) {
|
||||
_cleanup_free_ char *info = NULL;
|
||||
|
||||
/* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubInfo), &info);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return r;
|
||||
|
||||
/* Variable not set, definitely means not systemd-stub */
|
||||
|
||||
} else if (first_word(info, "systemd-stub")) {
|
||||
|
||||
/* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
|
||||
* static in all its versions. */
|
||||
|
||||
*ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No features supported */
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (s != sizeof(uint64_t))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"StubFeatures EFI variable doesn't have the right size.");
|
||||
|
||||
memcpy(ret, v, sizeof(uint64_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
static struct stat cache_stat = {};
|
||||
|
|
|
@ -16,6 +16,7 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader);
|
|||
int efi_loader_get_entries(char ***ret);
|
||||
|
||||
int efi_loader_get_features(uint64_t *ret);
|
||||
int efi_stub_get_features(uint64_t *ret);
|
||||
|
||||
int efi_loader_get_config_timeout_one_shot(usec_t *ret);
|
||||
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
|
||||
|
@ -38,6 +39,10 @@ static inline int efi_loader_get_features(uint64_t *ret) {
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_stub_get_features(uint64_t *ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,18 @@ env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 $
|
|||
tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
|
||||
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && exit 1
|
||||
|
||||
echo HALLO > /tmp/tpmdata1
|
||||
echo foobar > /tmp/tpmdata2
|
||||
|
||||
cat > /tmp/result <<EOF
|
||||
11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
|
||||
11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
|
||||
11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
|
||||
11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
|
||||
EOF
|
||||
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 | cmp - /tmp/result
|
||||
|
||||
echo OK >/testok
|
||||
|
||||
exit 0
|
||||
|
|
Loading…
Reference in a new issue