boot: measure select SMBIOS objects explicitly
This commit is contained in:
@@ -622,6 +622,19 @@ extra /6a9857a393724b7a981ebb5b8495b9ea/1.2.3/baz.confext.raw</programlist
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderPcrSMBIOS</varname></term>
|
||||
|
||||
<listitem><para>The PCR register index select SMBIOS structures are measured into — type 1
|
||||
(system information, with the volatile "Wake-up Type" field zeroed out), type 2 (baseboard
|
||||
information) and type 11 (OEM strings). Formatted as decimal ASCII string (e.g.
|
||||
<literal>1</literal>). Set by the boot loader if a measurement was successfully completed, and remains
|
||||
unset otherwise. (Note that <command>systemd-stub</command> performs the same measurement when booted
|
||||
directly, bypassing the boot loader.)</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderImageIdentifier</varname></term>
|
||||
|
||||
|
||||
@@ -664,6 +664,20 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderPcrSMBIOS</varname></term>
|
||||
|
||||
<listitem><para>The PCR register index select SMBIOS structures are measured into — type 1
|
||||
(system information, with the volatile "Wake-up Type" field zeroed out), type 2 (baseboard
|
||||
information) and type 11 (OEM strings). Formatted as decimal ASCII string (e.g.
|
||||
<literal>1</literal>). This variable is set if a measurement was successfully completed, and remains
|
||||
unset otherwise. <command>systemd-stub</command> performs this measurement only if it has not already
|
||||
been done in the same boot (e.g. by the boot loader), as indicated by this variable being set
|
||||
already.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderBootSecret</varname></term>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "iovec-util.h"
|
||||
#include "line-edit.h"
|
||||
#include "measure.h"
|
||||
#include "measure-smbios.h"
|
||||
#include "memory-util.h"
|
||||
#include "part-discovery.h"
|
||||
#include "pe.h"
|
||||
@@ -3267,6 +3268,7 @@ static void export_loader_variables(
|
||||
EFI_LOADER_FEATURE_TYPE1_UKI_URL |
|
||||
EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS |
|
||||
EFI_LOADER_FEATURE_KEYBOARD_LAYOUT |
|
||||
EFI_LOADER_FEATURE_SMBIOS_MEASURED |
|
||||
0;
|
||||
|
||||
assert(loaded_image);
|
||||
@@ -3434,6 +3436,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
export_common_variables(loaded_image);
|
||||
export_loader_variables(loaded_image, init_usec);
|
||||
|
||||
/* Measure SMBIOS data into PCR 1. This is done early, and suppressed if sd-stub later runs in
|
||||
* the same boot (and vice versa), via the LoaderPcrSMBIOS EFI variable. */
|
||||
measure_smbios();
|
||||
|
||||
(void) load_drivers(image, loaded_image, root_dir);
|
||||
|
||||
_cleanup_free_ char16_t *loaded_image_path = NULL;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "efi-efivars.h"
|
||||
#include "efi-log.h"
|
||||
#include "measure.h"
|
||||
#include "measure-smbios.h"
|
||||
#include "smbios.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "util.h"
|
||||
|
||||
static void measure_smbios_raw(
|
||||
const void *p,
|
||||
size_t size,
|
||||
uint32_t event_id,
|
||||
const char16_t *description,
|
||||
bool *measured) {
|
||||
|
||||
EFI_STATUS err;
|
||||
bool m = false;
|
||||
|
||||
assert(p);
|
||||
assert(description);
|
||||
assert(measured);
|
||||
|
||||
err = tpm_log_tagged_event(
|
||||
TPM2_PCR_PLATFORM_CONFIG,
|
||||
POINTER_TO_PHYSICAL_ADDRESS(p),
|
||||
size,
|
||||
event_id,
|
||||
description,
|
||||
&m);
|
||||
if (err != EFI_SUCCESS)
|
||||
log_error_status(err, "Unable to measure SMBIOS structure (%ls), ignoring: %m", description);
|
||||
|
||||
*measured = *measured || m;
|
||||
}
|
||||
|
||||
static void measure_smbios_type1(const SmbiosHeader *header, size_t size, bool *measured) {
|
||||
assert(header);
|
||||
assert(measured);
|
||||
|
||||
/* The wake-up type field varies depending on how the machine was powered on (cold boot, resume
|
||||
* from sleep, AC restore, …), which would make the measurement non-reproducible. Hence measure a
|
||||
* copy with that field zeroed out. */
|
||||
|
||||
assert(size >= sizeof(SmbiosTableType1));
|
||||
|
||||
_cleanup_free_ SmbiosTableType1 *copy = xmemdup(header, size);
|
||||
copy->wake_up_type = 0;
|
||||
|
||||
measure_smbios_raw(copy, size, SMBIOS_TYPE1_EVENT_TAG_ID, u"smbios:type1", measured);
|
||||
}
|
||||
|
||||
static bool measure_smbios_object(const SmbiosHeader *header, size_t size, void *userdata) {
|
||||
bool *measured = ASSERT_PTR(userdata);
|
||||
|
||||
switch (header->type) {
|
||||
|
||||
case 1: /* System Information */
|
||||
measure_smbios_type1(header, size, measured);
|
||||
break;
|
||||
|
||||
case 2: /* Baseboard Information */
|
||||
measure_smbios_raw(header, size, SMBIOS_TYPE2_EVENT_TAG_ID, u"smbios:type2", measured);
|
||||
break;
|
||||
|
||||
case 11: /* OEM Strings */
|
||||
measure_smbios_raw(header, size, SMBIOS_TYPE11_EVENT_TAG_ID, u"smbios:type11", measured);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true; /* Keep iterating: there may be more than one matching structure (e.g. type 11). */
|
||||
}
|
||||
|
||||
void measure_smbios(void) {
|
||||
bool measured = false;
|
||||
|
||||
if (!tpm_present())
|
||||
return;
|
||||
|
||||
/* If the measurement was already done this boot (e.g. by sd-boot before it chainloaded us), don't
|
||||
* do it again — re-extending PCR 1 would invalidate the value. */
|
||||
if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderPcrSMBIOS", /* ret_data= */ NULL, /* ret_size= */ NULL) == EFI_SUCCESS)
|
||||
return;
|
||||
|
||||
/* Measure SMBIOS type 1 (system information), type 2 (baseboard information) and type 11 (OEM
|
||||
* strings) into PCR 1, in a single pass over the SMBIOS table. */
|
||||
smbios_foreach(measure_smbios_object, &measured);
|
||||
|
||||
/* If we measured something, tell the OS which PCR we used (and suppress a second pass). */
|
||||
if (measured)
|
||||
(void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"LoaderPcrSMBIOS", TPM2_PCR_PLATFORM_CONFIG, /* flags= */ 0);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
/* Measures SMBIOS type 1 (system information, with the volatile "Wake-up Type" field masked) and all
|
||||
* type 11 (OEM strings) structures into PCR 1, and records the PCR index in the transient
|
||||
* LoaderPcrSMBIOS EFI variable. Called by both sd-boot and sd-stub; the presence of LoaderPcrSMBIOS
|
||||
* suppresses a redundant second measurement when both run during the same boot. */
|
||||
void measure_smbios(void);
|
||||
@@ -323,6 +323,7 @@ libefi_sources = files(
|
||||
'hii.c',
|
||||
'initrd.c',
|
||||
'measure.c',
|
||||
'measure-smbios.c',
|
||||
'part-discovery.c',
|
||||
'pe.c',
|
||||
'random-seed.c',
|
||||
|
||||
+77
-45
@@ -39,12 +39,6 @@ typedef struct {
|
||||
uint64_t table_address;
|
||||
} _packed_ Smbios3EntryPoint;
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t handle[2];
|
||||
} _packed_ SmbiosHeader;
|
||||
|
||||
typedef struct {
|
||||
SmbiosHeader header;
|
||||
uint8_t vendor;
|
||||
@@ -56,18 +50,6 @@ typedef struct {
|
||||
uint8_t bios_characteristics_ext[2];
|
||||
} _packed_ SmbiosTableType0;
|
||||
|
||||
typedef struct {
|
||||
SmbiosHeader header;
|
||||
uint8_t manufacturer;
|
||||
uint8_t product_name;
|
||||
uint8_t version;
|
||||
uint8_t serial_number;
|
||||
EFI_GUID uuid;
|
||||
uint8_t wake_up_type;
|
||||
uint8_t sku_number;
|
||||
uint8_t family;
|
||||
} _packed_ SmbiosTableType1;
|
||||
|
||||
typedef struct {
|
||||
SmbiosHeader header;
|
||||
uint8_t manufacturer;
|
||||
@@ -103,6 +85,44 @@ static const void* find_smbios_configuration_table(uint64_t *ret_size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Given 'p' pointing at a structure header with 'size' bytes left in the table from there onwards,
|
||||
* returns a pointer just past the end of this structure (i.e. the start of the next one), accounting
|
||||
* for the formatted area and the trailing string set (terminated by a double NUL byte). Returns NULL
|
||||
* if the structure is malformed or runs past the end of the table. */
|
||||
static const uint8_t* smbios_structure_end(const uint8_t *p, uint64_t size) {
|
||||
assert(p);
|
||||
|
||||
if (size < sizeof(SmbiosHeader))
|
||||
return NULL;
|
||||
|
||||
const SmbiosHeader *header = (const SmbiosHeader *) p;
|
||||
if (size < header->length)
|
||||
return NULL;
|
||||
|
||||
/* Skip over formatted area. */
|
||||
const uint8_t *q = p + header->length;
|
||||
size -= header->length;
|
||||
|
||||
/* Special case: if there are no strings appended, we'll see two NUL bytes. */
|
||||
if (size >= 2 && q[0] == 0 && q[1] == 0)
|
||||
return q + 2;
|
||||
|
||||
/* Skip over a populated string table. */
|
||||
bool first = true;
|
||||
for (;;) {
|
||||
const uint8_t *e = memchr(q, 0, size);
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
if (!first && e == q) /* Double NUL byte means we've reached the end of the string table. */
|
||||
return q + 1;
|
||||
|
||||
size -= e + 1 - q;
|
||||
q = e + 1;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
static const SmbiosHeader* get_smbios_table(uint8_t type, size_t min_size, uint64_t *ret_size_left) {
|
||||
uint64_t size;
|
||||
const uint8_t *p = find_smbios_configuration_table(&size);
|
||||
@@ -132,34 +152,12 @@ static const SmbiosHeader* get_smbios_table(uint8_t type, size_t min_size, uint6
|
||||
return header; /* Yay! */
|
||||
}
|
||||
|
||||
/* Skip over formatted area. */
|
||||
size -= header->length;
|
||||
p += header->length;
|
||||
const uint8_t *next = smbios_structure_end(p, size);
|
||||
if (!next)
|
||||
goto not_found;
|
||||
|
||||
/* Special case: if there are no strings appended, we'll see two NUL bytes, skip over them */
|
||||
if (size >= 2 && p[0] == 0 && p[1] == 0) {
|
||||
size -= 2;
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip over a populated string table. */
|
||||
bool first = true;
|
||||
for (;;) {
|
||||
const uint8_t *e = memchr(p, 0, size);
|
||||
if (!e)
|
||||
goto not_found;
|
||||
|
||||
if (!first && e == p) {/* Double NUL byte means we've reached the end of the string table. */
|
||||
p++;
|
||||
size--;
|
||||
break;
|
||||
}
|
||||
|
||||
size -= e + 1 - p;
|
||||
p = e + 1;
|
||||
first = false;
|
||||
}
|
||||
size -= next - p;
|
||||
p = next;
|
||||
}
|
||||
|
||||
not_found:
|
||||
@@ -169,6 +167,40 @@ not_found:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void smbios_foreach(SmbiosForeachFunc func, void *userdata) {
|
||||
assert(func);
|
||||
|
||||
uint64_t size;
|
||||
const uint8_t *p = find_smbios_configuration_table(&size);
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
/* Walks the SMBIOS table exactly once, invoking 'func' for every structure. 'func' receives the
|
||||
* structure's header (which carries its type) and the structure's total length (formatted area +
|
||||
* trailing string set); returning false stops the iteration early. */
|
||||
|
||||
for (;;) {
|
||||
if (size < sizeof(SmbiosHeader))
|
||||
return;
|
||||
|
||||
const SmbiosHeader *header = (const SmbiosHeader *) p;
|
||||
|
||||
/* End of table. */
|
||||
if (header->type == 127)
|
||||
return;
|
||||
|
||||
const uint8_t *next = smbios_structure_end(p, size);
|
||||
if (!next)
|
||||
return;
|
||||
|
||||
if (!func(header, next - p, userdata))
|
||||
return;
|
||||
|
||||
size -= next - p;
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool smbios_in_hypervisor(void) {
|
||||
/* Look up BIOS Information (Type 0). */
|
||||
const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0, sizeof(SmbiosTableType0), /* ret_size_left= */ NULL);
|
||||
|
||||
@@ -3,10 +3,35 @@
|
||||
|
||||
#include "efi.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t handle[2];
|
||||
} _packed_ SmbiosHeader;
|
||||
|
||||
typedef struct {
|
||||
SmbiosHeader header;
|
||||
uint8_t manufacturer;
|
||||
uint8_t product_name;
|
||||
uint8_t version;
|
||||
uint8_t serial_number;
|
||||
EFI_GUID uuid;
|
||||
uint8_t wake_up_type;
|
||||
uint8_t sku_number;
|
||||
uint8_t family;
|
||||
} _packed_ SmbiosTableType1;
|
||||
|
||||
bool smbios_in_hypervisor(void);
|
||||
|
||||
const char* smbios_find_oem_string(const char *name, const char *after);
|
||||
|
||||
/* Invoked by smbios_foreach() for each SMBIOS structure. 'header' points at the structure (which
|
||||
* carries its type), and 'size' is the structure's total length (formatted area + trailing string
|
||||
* set). Returning false stops the iteration. */
|
||||
typedef bool (*SmbiosForeachFunc)(const SmbiosHeader *header, size_t size, void *userdata);
|
||||
|
||||
void smbios_foreach(SmbiosForeachFunc func, void *userdata);
|
||||
|
||||
typedef struct RawSmbiosInfo {
|
||||
const char *manufacturer;
|
||||
const char *product_name;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "iovec-util.h"
|
||||
#include "linux.h"
|
||||
#include "measure.h"
|
||||
#include "measure-smbios.h"
|
||||
#include "memory-util.h"
|
||||
#include "part-discovery.h"
|
||||
#include "pe.h"
|
||||
@@ -116,6 +117,7 @@ static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, unsig
|
||||
EFI_STUB_FEATURE_MULTI_PROFILE_UKI | /* We grok the "@1" profile command line argument */
|
||||
EFI_STUB_FEATURE_REPORT_STUB_PARTITION | /* We set StubDevicePartUUID + StubImageIdentifier */
|
||||
EFI_STUB_FEATURE_REPORT_URL | /* We set StubDeviceURL + LoaderDeviceURL */
|
||||
EFI_STUB_FEATURE_SMBIOS_MEASURED | /* We measure SMBIOS data into PCR 1 */
|
||||
0;
|
||||
|
||||
assert(loaded_image);
|
||||
@@ -1252,6 +1254,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
export_common_variables(loaded_image);
|
||||
export_stub_variables(loaded_image, profile);
|
||||
|
||||
/* Measure SMBIOS data into PCR 1, unless sd-boot already did so in the same boot (tracked via
|
||||
* the LoaderPcrSMBIOS EFI variable). */
|
||||
measure_smbios();
|
||||
|
||||
/* First load the base device tree, then fix it up using addons - global first, then per-UKI. */
|
||||
install_embedded_devicetree(loaded_image, sections, &dt_state);
|
||||
install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, ¶meters_measured);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#define EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS (UINT64_C(1) << 18)
|
||||
#define EFI_LOADER_FEATURE_ENTRY_PREFERRED (UINT64_C(1) << 19)
|
||||
#define EFI_LOADER_FEATURE_KEYBOARD_LAYOUT (UINT64_C(1) << 20)
|
||||
#define EFI_LOADER_FEATURE_SMBIOS_MEASURED (UINT64_C(1) << 21)
|
||||
|
||||
/* Features of the stub, i.e. systemd-stub */
|
||||
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
|
||||
@@ -44,6 +45,7 @@
|
||||
#define EFI_STUB_FEATURE_MULTI_PROFILE_UKI (UINT64_C(1) << 9)
|
||||
#define EFI_STUB_FEATURE_REPORT_STUB_PARTITION (UINT64_C(1) << 10)
|
||||
#define EFI_STUB_FEATURE_REPORT_URL (UINT64_C(1) << 11)
|
||||
#define EFI_STUB_FEATURE_SMBIOS_MEASURED (UINT64_C(1) << 12)
|
||||
|
||||
typedef enum SecureBootMode {
|
||||
SECURE_BOOT_UNSUPPORTED,
|
||||
|
||||
@@ -56,3 +56,12 @@ enum {
|
||||
|
||||
/* The tag used for EV_EVENT_TAG event log records covering the selected UKI profile */
|
||||
#define UKI_PROFILE_EVENT_TAG_ID UINT32_C(0x13aed6db)
|
||||
|
||||
/* The tag used for EV_EVENT_TAG event log records covering the SMBIOS type 1 (system information) structure */
|
||||
#define SMBIOS_TYPE1_EVENT_TAG_ID UINT32_C(0xd5cb7cbc)
|
||||
|
||||
/* The tag used for EV_EVENT_TAG event log records covering the SMBIOS type 2 (baseboard information) structure */
|
||||
#define SMBIOS_TYPE2_EVENT_TAG_ID UINT32_C(0xe0d47bc8)
|
||||
|
||||
/* The tag used for EV_EVENT_TAG event log records covering SMBIOS type 11 (OEM strings) structures */
|
||||
#define SMBIOS_TYPE11_EVENT_TAG_ID UINT32_C(0xc0b3bd23)
|
||||
|
||||
Reference in New Issue
Block a user