1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

stub: Add support for .initrd addon files

Teaches systemd-stub how to load additional initrds from addon files.
This is very similar to the support for .ucode sections in addon files,
but with different ordering. Initrds from addons have a chance to
overwrite files from the base initrd in the UKI.
This commit is contained in:
Tobias Fleig 2024-10-08 07:54:43 -07:00 committed by Luca Boccassi
parent 61808636a7
commit 2ea0487c1b
4 changed files with 95 additions and 13 deletions

View File

@ -120,6 +120,16 @@ Devicetree addons are measured individually as a tagged event.
**Measured hash** covers the content of the Devicetree.
### PCR 12, `EV_EVENT_TAG`, "Initrd addons"
Initrd addons are measured individually as a tagged event.
**Event Tag** `0x49dffe0f`
**Description** the addon filename.
**Measured hash** covers the contents of the initrd.
### PCR 12, `EV_EVENT_TAG`, "Ucode addons"
Ucode addons are measured individually as a tagged event.

View File

@ -193,9 +193,10 @@
<filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
PE binaries and specific sections are loaded from them. Addons are used to pass additional kernel
command line parameters (<literal>.cmdline</literal> section), or Devicetree blobs
(<literal>.dtb</literal> section), and microcode updates (<literal>.ucode</literal> section). Addons
allow those resources to be passed regardless of the kernel version being booted, for example allowing
platform vendors to ship platform-specific configuration.</para>
(<literal>.dtb</literal> section), additional initrds (<literal>.initrd</literal> section),
and microcode updates (<literal>.ucode</literal> section). Addons allow those resources to be passed
regardless of the kernel version being booted, for example allowing platform vendors to ship
platform-specific configuration.</para>
<para>In case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or
Shim's MOK, and only loaded if the check passes. Additionally, if both the addon and the UKI contain a
@ -227,10 +228,10 @@
archive is measured into TPM PCR 12 (if a TPM is present).</para></listitem>
<listitem><para>Additionally, files <filename>/loader/addons/*.addon.efi</filename> are loaded and
verified as PE binaries, and <literal>.cmdline</literal>, <literal>.dtb</literal>, and
<literal>.ucode</literal> sections are parsed from them. This is supposed to be used to pass additional
command line parameters, DeviceTree blobs, and microcode updates to the kernel, regardless of the
kernel version being booted.</para></listitem>
verified as PE binaries, and <literal>.cmdline</literal>, <literal>.dtb</literal>,
<literal>.initrd</literal>, and <literal>.ucode</literal> sections are parsed from them.
This is supposed to be used to pass additional command line parameters, DeviceTree blobs, initrds,
and microcode updates to the kernel, regardless of the kernel version being booted.</para></listitem>
</itemizedlist>
<para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd

View File

@ -449,6 +449,42 @@ static inline void iovec_array_extend(struct iovec **arr, size_t *n_arr, struct
(*arr)[(*n_arr)++] = elem;
}
static void measure_and_append_initrd_addons(
struct iovec **all_initrds,
size_t *n_all_initrds,
const NamedAddon *initrd_addons,
size_t n_initrd_addons,
int *sections_measured) {
EFI_STATUS err;
assert(all_initrds);
assert(n_all_initrds);
assert(initrd_addons || n_initrd_addons == 0);
assert(sections_measured);
FOREACH_ARRAY(i, initrd_addons, n_initrd_addons) {
bool m = false;
err = tpm_log_tagged_event(
TPM2_PCR_KERNEL_CONFIG,
POINTER_TO_PHYSICAL_ADDRESS(i->blob.iov_base),
i->blob.iov_len,
INITRD_ADDON_EVENT_TAG_ID,
i->filename,
&m);
if (err != EFI_SUCCESS)
return (void) log_error_status(
err,
"Unable to extend PCR %i with INITRD addon '%ls': %m",
TPM2_PCR_KERNEL_CONFIG,
i->filename);
combine_measured_flag(sections_measured, m);
iovec_array_extend(all_initrds, n_all_initrds, i->blob);
}
}
static void measure_and_append_ucode_addons(
struct iovec **all_initrds,
size_t *n_all_initrds,
@ -508,6 +544,8 @@ static EFI_STATUS load_addons(
char16_t **cmdline, /* Both input+output, extended with new addons we find */
NamedAddon **devicetree_addons, /* Ditto */
size_t *n_devicetree_addons,
NamedAddon **initrd_addons, /* Ditto */
size_t *n_initrd_addons,
NamedAddon **ucode_addons, /* Ditto */
size_t *n_ucode_addons) {
@ -575,11 +613,12 @@ static EFI_STATUS load_addons(
if (err != EFI_SUCCESS ||
(!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) &&
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB) &&
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD) &&
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE))) {
if (err == EFI_SUCCESS)
err = EFI_NOT_FOUND;
log_error_status(err,
"Unable to locate embedded .cmdline/.dtb/.ucode sections in %ls, ignoring: %m",
"Unable to locate embedded .cmdline/.dtb/.initrd/.ucode sections in %ls, ignoring: %m",
items[i]);
continue;
}
@ -621,6 +660,19 @@ static EFI_STATUS load_addons(
};
}
if (initrd_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) {
*initrd_addons = xrealloc(*initrd_addons,
*n_initrd_addons * sizeof(NamedAddon),
(*n_initrd_addons + 1) * sizeof(NamedAddon));
(*initrd_addons)[(*n_initrd_addons)++] = (NamedAddon) {
.blob = {
.iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_INITRD].memory_offset, sections[UNIFIED_SECTION_INITRD].size),
.iov_len = sections[UNIFIED_SECTION_INITRD].size,
},
.filename = xstrdup16(items[i]),
};
}
if (ucode_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) {
*ucode_addons = xrealloc(*ucode_addons,
*n_ucode_addons * sizeof(NamedAddon),
@ -933,6 +985,8 @@ static void load_all_addons(
char16_t **cmdline_addons,
NamedAddon **dt_addons,
size_t *n_dt_addons,
NamedAddon **initrd_addons,
size_t *n_initrd_addons,
NamedAddon **ucode_addons,
size_t *n_ucode_addons) {
@ -942,6 +996,8 @@ static void load_all_addons(
assert(cmdline_addons);
assert(dt_addons);
assert(n_dt_addons);
assert(initrd_addons);
assert(n_initrd_addons);
assert(ucode_addons);
assert(n_ucode_addons);
@ -953,6 +1009,8 @@ static void load_all_addons(
cmdline_addons,
dt_addons,
n_dt_addons,
initrd_addons,
n_initrd_addons,
ucode_addons,
n_ucode_addons);
if (err != EFI_SUCCESS)
@ -971,6 +1029,8 @@ static void load_all_addons(
cmdline_addons,
dt_addons,
n_dt_addons,
initrd_addons,
n_initrd_addons,
ucode_addons,
n_ucode_addons);
if (err != EFI_SUCCESS)
@ -1097,8 +1157,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
PeSectionVector sections[ELEMENTSOF(unified_sections)] = {};
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
_cleanup_free_ char *uname = NULL;
NamedAddon *dt_addons = NULL, *ucode_addons = NULL;
size_t n_dt_addons = 0, n_ucode_addons = 0;
NamedAddon *dt_addons = NULL, *initrd_addons = NULL, *ucode_addons = NULL;
size_t n_dt_addons = 0, n_initrd_addons = 0, n_ucode_addons = 0;
_cleanup_free_ struct iovec *all_initrds = NULL;
size_t n_all_initrds = 0;
unsigned profile = 0;
@ -1134,8 +1194,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
/* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
* addons. The data is loaded at once, and then used later. */
CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_many);
CLEANUP_ARRAY(initrd_addons, n_initrd_addons, named_addon_free_many);
CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_many);
load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &ucode_addons, &n_ucode_addons);
load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &initrd_addons, &n_initrd_addons, &ucode_addons, &n_ucode_addons);
/* If we have any extra command line to add via PE addons, load them now and append, and measure the
* additions together, after the embedded options, but before the smbios ones, so that the order is
@ -1156,10 +1217,17 @@ static EFI_STATUS run(EFI_HANDLE image) {
generate_embedded_initrds(loaded_image, sections, initrds);
lookup_embedded_initrds(loaded_image, sections, initrds);
/* Measures ucode addons and puts them into all_initrds */
/* Add initrds in the right order. Generally, later initrds can overwrite files in earlier ones,
* except for ucode, where the kernel uses the first matching embedded filename.
* We want addons to take precedence over the base initrds, so the order is:
* 1. Ucode addons
* 2. UKI ucode
* 3. UKI initrd
* 4. Generated initrds
* 5. initrd addons */
measure_and_append_ucode_addons(&all_initrds, &n_all_initrds, ucode_addons, n_ucode_addons, &parameters_measured);
/* Adds all other initrds to all_initrds */
extend_initrds(initrds, &all_initrds, &n_all_initrds);
measure_and_append_initrd_addons(&all_initrds, &n_all_initrds, initrd_addons, n_initrd_addons, &parameters_measured);
/* Export variables indicating what we measured */
export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured);

View File

@ -50,6 +50,9 @@ enum {
/* The tag used for EV_EVENT_TAG event log records covering Devicetree blobs */
#define DEVICETREE_ADDON_EVENT_TAG_ID UINT32_C(0x6c46f751)
/* The tag used for EV_EVENT_TAG event log records covering initrd addons */
#define INITRD_ADDON_EVENT_TAG_ID UINT32_C(0x49dffe0f)
/* The tag used for EV_EVENT_TAG event log records covering ucode addons (effectively initrds) */
#define UCODE_ADDON_EVENT_TAG_ID UINT32_C(0xdac08e1a)