Skip to content

Commit 2f2203c

Browse files
authored
boot,stub: explicitly measure select SMBIOS objects (#42233)
This is supposed to protect our SMBIOS type 11 importing for credentials. Note that firmwares are supposed to measure SMBIOS anyway to PCR 1. Alas firmware doesn't really do that in various cases. Hence let's do so again, for select objects. This closes a gap where some of the input for OS (i.e. system credentials places in smbios11) isn't measured properly. (I really want this to get into v261, because this will fuck up the PCRs a bit more, and we already have the new separator measurement in v261, hence there's value in getting this merged at the same time, so that we don't break the measurements a 2nd time)
2 parents 2b8ba0e + 0761c9f commit 2f2203c

16 files changed

Lines changed: 345 additions & 68 deletions

TODO.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,12 +1212,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
12121212
- in pid1: include ExecStart= cmdlines (and other Exec*= cmdlines) in polkit
12131213
request, so that policies can match against command lines.
12141214

1215-
- in sd-boot and sd-stub measure the SMBIOS vendor strings to some PCR (at
1216-
least some subset of them that look like systemd stuff), because apparently
1217-
some firmware does not, but systemd honours it. avoid duplicate measurement
1218-
by sd-boot and sd-stub by adding LoaderFeatures/StubFeatures flag for this,
1219-
so that sd-stub can avoid it if sd-boot already did it.
1220-
12211215
- in sd-id128: also parse UUIDs in RFC4122 URN syntax (i.e. chop off urn:uuid: prefix)
12221216

12231217
- in sd-stub: optionally add support for a new PE section .keyring or so that
@@ -1745,8 +1739,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
17451739

17461740
- measure all log-in attempts into a new nvpcr
17471741

1748-
- measure credentials picked up from SMBIOS to some suitable PCR
1749-
17501742
- measure GPT and LUKS headers somewhere when we use them (i.e. in
17511743
systemd-gpt-auto-generator/systemd-repart and in systemd-cryptsetup?)
17521744

docs/BOOT_LOADER_INTERFACE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ Variables will be listed below using the Linux efivarfs naming,
145145
* `1 << 19` → The boot loader supports the `LoaderEntryPreferred` variable when set.
146146
* `1 << 20` → The boot loader reports the firmware-configured keyboard layout in the
147147
EFI variable `LoaderKeyboardLayout-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`.
148+
* `1 << 21` → The boot loader measures SMBIOS information into a TPM2 PCR and reports the PCR index in the
149+
EFI variable `LoaderPcrSMBIOS-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`.
148150

149151
* The EFI variable `LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`
150152
contains binary random data,
@@ -184,6 +186,17 @@ Variables will be listed below using the Linux efivarfs naming,
184186
uses this as a lowest-priority fallback keyboard layout
185187
when no explicit configuration is provided.
186188

189+
* The EFI variable `LoaderPcrSMBIOS-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`
190+
contains the index of the TPM2 PCR (as a decimal ASCII string formatted as a
191+
NUL-terminated UTF-16 string, e.g. `1`) into which the boot loader measured
192+
select SMBIOS structures: type 1 (system information, with the volatile
193+
"Wake-up Type" field zeroed out), type 2 (baseboard information) and type 11
194+
(OEM strings). This is a volatile (non-persistent) variable, set only if a
195+
measurement was successfully completed, and remains unset otherwise. Both
196+
`systemd-boot` and `systemd-stub` perform this measurement; whichever runs
197+
first sets the variable, and its presence suppresses a second measurement of
198+
the same data into the same PCR during the same boot.
199+
187200
If `LoaderTimeInitUSec` and `LoaderTimeExecUSec` are set, `systemd-analyze`
188201
will include them in its boot-time analysis. If `LoaderDevicePartUUID` is set,
189202
systemd will mount the ESP that was used for the boot to `/boot`, but only if

docs/TPM2_PCR_MEASUREMENTS.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,45 @@ must be employed when designing a system that uses this feature.
9191

9292
## PCR Measurements Made by `systemd-boot` (UEFI)
9393

94+
### PCR 1, `EV_EVENT_TAG`, SMBIOS information
95+
96+
Select SMBIOS structures provided by the firmware are measured into PCR 1 (the
97+
TCG-defined register for platform configuration data), one tagged event per
98+
structure:
99+
100+
* SMBIOS type 1 (system information). The volatile "Wake-up Type" field is
101+
zeroed before measuring, since it varies depending on how the machine was
102+
powered on (cold boot, resume from sleep, AC restore, …) and would otherwise
103+
make the measurement non-reproducible.
104+
* SMBIOS type 2 (baseboard information).
105+
* SMBIOS type 11 (OEM strings). There may be more than one such structure; all
106+
are measured.
107+
108+
Note that these measurements are – strictly speaking – redundant, since
109+
firmwares are supposed to measure SMBIOS data anyway on their own. However, it
110+
has been found this is not the case on many real-life implementations. Since in
111+
particular SMBIOS type 11 may carry highly relevant input for the OS
112+
(e.g. system credentials), an explicit measurement is made here to ensure all
113+
parameters for the OS are comprehensively measured even on flaky firmwares.
114+
115+
**Event Tag** `0xd5cb7cbc` for type 1, `0xe0d47bc8` for type 2, `0xc0b3bd23`
116+
for type 11.
117+
118+
**Description** in the event log record is `smbios:type1`, `smbios:type2` or
119+
`smbios:type11` respectively, in UTF-16.
120+
121+
**Measured hash** covers the raw bytes of the SMBIOS structure (formatted area
122+
plus trailing string set), with the type 1 "Wake-up Type" field zeroed out as
123+
described above.
124+
125+
This measurement is also performed by `systemd-stub` (see below), so that systems
126+
that boot a UKI directly, bypassing `systemd-boot`, still get it. Whichever
127+
component runs first performs the measurement and sets the volatile
128+
`LoaderPcrSMBIOS` EFI variable to the PCR index used; its presence suppresses a
129+
second measurement of the same data into the same PCR during the same boot. Note
130+
that the firmware itself typically also extends PCR 1, so its final value is not
131+
solely determined by this measurement.
132+
94133
### PCR 5, `EV_EVENT_TAG`, `loader.conf`
95134

96135
The content of `systemd-boot`'s configuration file, `loader/loader.conf`, is
@@ -120,6 +159,14 @@ trailing NUL bytes).
120159

121160
## PCR Measurements Made by `systemd-stub` (UEFI)
122161

162+
### PCR 1, `EV_EVENT_TAG`, SMBIOS information
163+
164+
Identical to the SMBIOS measurement described above for `systemd-boot`. When
165+
`systemd-stub` is invoked by `systemd-boot`, the measurement has typically already
166+
been made (tracked via the `LoaderPcrSMBIOS` EFI variable) and is not repeated;
167+
when the UKI is booted directly by the firmware, `systemd-stub` performs it
168+
itself.
169+
123170
### PCR 11, `EV_IPL`, PE section name
124171

125172
A measurement is made for each PE section of the UKI that is defined by the

man/systemd-boot.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,19 @@ extra /6a9857a393724b7a981ebb5b8495b9ea/1.2.3/baz.confext.raw</programlist
622622
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
623623
</varlistentry>
624624

625+
<varlistentry>
626+
<term><varname>LoaderPcrSMBIOS</varname></term>
627+
628+
<listitem><para>The PCR register index select SMBIOS structures are measured into — type 1
629+
(system information, with the volatile "Wake-up Type" field zeroed out), type 2 (baseboard
630+
information) and type 11 (OEM strings). Formatted as decimal ASCII string (e.g.
631+
<literal>1</literal>). Set by the boot loader if a measurement was successfully completed, and remains
632+
unset otherwise. (Note that <command>systemd-stub</command> performs the same measurement when booted
633+
directly, bypassing the boot loader.)</para>
634+
635+
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
636+
</varlistentry>
637+
625638
<varlistentry>
626639
<term><varname>LoaderImageIdentifier</varname></term>
627640

man/systemd-stub.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,20 @@
664664
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
665665
</varlistentry>
666666

667+
<varlistentry>
668+
<term><varname>LoaderPcrSMBIOS</varname></term>
669+
670+
<listitem><para>The PCR register index select SMBIOS structures are measured into — type 1
671+
(system information, with the volatile "Wake-up Type" field zeroed out), type 2 (baseboard
672+
information) and type 11 (OEM strings). Formatted as decimal ASCII string (e.g.
673+
<literal>1</literal>). This variable is set if a measurement was successfully completed, and remains
674+
unset otherwise. <command>systemd-stub</command> performs this measurement only if it has not already
675+
been done in the same boot (e.g. by the boot loader), as indicated by this variable being set
676+
already.</para>
677+
678+
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
679+
</varlistentry>
680+
667681
<varlistentry>
668682
<term><varname>LoaderBootSecret</varname></term>
669683

src/boot/boot.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "iovec-util.h"
1818
#include "line-edit.h"
1919
#include "measure.h"
20+
#include "measure-smbios.h"
2021
#include "memory-util.h"
2122
#include "part-discovery.h"
2223
#include "pe.h"
@@ -3267,6 +3268,7 @@ static void export_loader_variables(
32673268
EFI_LOADER_FEATURE_TYPE1_UKI_URL |
32683269
EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS |
32693270
EFI_LOADER_FEATURE_KEYBOARD_LAYOUT |
3271+
EFI_LOADER_FEATURE_SMBIOS_MEASURED |
32703272
0;
32713273

32723274
assert(loaded_image);
@@ -3434,6 +3436,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
34343436
export_common_variables(loaded_image);
34353437
export_loader_variables(loaded_image, init_usec);
34363438

3439+
/* Measure SMBIOS data into PCR 1. This is done early, and suppressed if sd-stub later runs in
3440+
* the same boot (and vice versa), via the LoaderPcrSMBIOS EFI variable. */
3441+
measure_smbios();
3442+
34373443
(void) load_drivers(image, loaded_image, root_dir);
34383444

34393445
_cleanup_free_ char16_t *loaded_image_path = NULL;

src/boot/measure-smbios.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include "efi-efivars.h"
4+
#include "efi-log.h"
5+
#include "measure.h"
6+
#include "measure-smbios.h"
7+
#include "smbios.h"
8+
#include "tpm2-pcr.h"
9+
#include "util.h"
10+
11+
static void measure_smbios_raw(
12+
const void *p,
13+
size_t size,
14+
uint32_t event_id,
15+
const char16_t *description,
16+
bool *measured) {
17+
18+
EFI_STATUS err;
19+
bool m = false;
20+
21+
assert(p);
22+
assert(description);
23+
assert(measured);
24+
25+
err = tpm_log_tagged_event(
26+
TPM2_PCR_PLATFORM_CONFIG,
27+
POINTER_TO_PHYSICAL_ADDRESS(p),
28+
size,
29+
event_id,
30+
description,
31+
&m);
32+
if (err != EFI_SUCCESS)
33+
log_error_status(err, "Unable to measure SMBIOS structure (%ls), ignoring: %m", description);
34+
35+
*measured = *measured || m;
36+
}
37+
38+
static void measure_smbios_type1(const SmbiosHeader *header, size_t size, bool *measured) {
39+
assert(header);
40+
assert(measured);
41+
42+
/* The wake-up type field varies depending on how the machine was powered on (cold boot, resume
43+
* from sleep, AC restore, …), which would make the measurement non-reproducible. Hence measure a
44+
* copy with that field zeroed out. */
45+
46+
assert(size >= sizeof(SmbiosTableType1));
47+
48+
_cleanup_free_ SmbiosTableType1 *copy = xmemdup(header, size);
49+
copy->wake_up_type = 0;
50+
51+
measure_smbios_raw(copy, size, SMBIOS_TYPE1_EVENT_TAG_ID, u"smbios:type1", measured);
52+
}
53+
54+
static bool measure_smbios_object(const SmbiosHeader *header, size_t size, void *userdata) {
55+
bool *measured = ASSERT_PTR(userdata);
56+
57+
switch (header->type) {
58+
59+
case 1: /* System Information */
60+
measure_smbios_type1(header, size, measured);
61+
break;
62+
63+
case 2: /* Baseboard Information */
64+
measure_smbios_raw(header, size, SMBIOS_TYPE2_EVENT_TAG_ID, u"smbios:type2", measured);
65+
break;
66+
67+
case 11: /* OEM Strings */
68+
measure_smbios_raw(header, size, SMBIOS_TYPE11_EVENT_TAG_ID, u"smbios:type11", measured);
69+
break;
70+
71+
default:
72+
break;
73+
}
74+
75+
return true; /* Keep iterating: there may be more than one matching structure (e.g. type 11). */
76+
}
77+
78+
void measure_smbios(void) {
79+
bool measured = false;
80+
81+
if (!tpm_present())
82+
return;
83+
84+
/* If the measurement was already done this boot (e.g. by sd-boot before it chainloaded us), don't
85+
* do it again — re-extending PCR 1 would invalidate the value. */
86+
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderPcrSMBIOS", /* ret_data= */ NULL, /* ret_size= */ NULL) == EFI_SUCCESS)
87+
return;
88+
89+
/* Measure SMBIOS type 1 (system information), type 2 (baseboard information) and type 11 (OEM
90+
* strings) into PCR 1, in a single pass over the SMBIOS table. */
91+
smbios_foreach(measure_smbios_object, &measured);
92+
93+
/* If we measured something, tell the OS which PCR we used (and suppress a second pass). */
94+
if (measured)
95+
(void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"LoaderPcrSMBIOS", TPM2_PCR_PLATFORM_CONFIG, /* flags= */ 0);
96+
}

src/boot/measure-smbios.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
#pragma once
3+
4+
/* Measures SMBIOS type 1 (system information, with the volatile "Wake-up Type" field masked) and all
5+
* type 11 (OEM strings) structures into PCR 1, and records the PCR index in the transient
6+
* LoaderPcrSMBIOS EFI variable. Called by both sd-boot and sd-stub; the presence of LoaderPcrSMBIOS
7+
* suppresses a redundant second measurement when both run during the same boot. */
8+
void measure_smbios(void);

src/boot/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ libefi_sources = files(
323323
'hii.c',
324324
'initrd.c',
325325
'measure.c',
326+
'measure-smbios.c',
326327
'part-discovery.c',
327328
'pe.c',
328329
'random-seed.c',

0 commit comments

Comments
 (0)