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

Merge pull request #28699 from bluca/dtb_addon

stub: add support for dtb addons
This commit is contained in:
Luca Boccassi 2023-10-10 08:58:54 +01:00 committed by GitHub
commit d843ad28fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 419 additions and 119 deletions

3
TODO
View File

@ -233,9 +233,6 @@ Features:
2nd key derived from volume key of the user, with which to wrap all
keys. maintain keys in kernel keyring if possible.
* sd-boot should probably measure its configuration file to PCR 5 at boot, as
per TCG PC Client Platform Firmware Profile Spec.
* use sd-event ratelimit feature optionally for .socket units to "pause" overly
busy sockets temporarily. (as a less drastic version of the trigger
ratelimit)

View File

@ -16,8 +16,8 @@ measurements listed below are (by default) only done if a system is booted with
to systemd's UEFI-mode measurements, and if the latter are not done the former
aren't made either.
systemd will measure to PCRs 11 (`kernel-boot`), 12 (`kernel-config`), 13
(`sysexts`), 15 (`system-identity`).
systemd will measure to PCRs 5 (`boot-loader-config`), 11 (`kernel-boot`),
12 (`kernel-config`), 13 (`sysexts`), 15 (`system-identity`).
Currently, four components will issue TPM2 PCR measurements:
@ -31,6 +31,17 @@ maintained in `/run/log/systemd/tpm2-measure.log`.
## PCR Measurements Made by `systemd-boot` (UEFI)
### PCS 5, `EV_EVENT_TAG`, "loader.conf"
The content of `systemd-boot`'s configuration file, `loader/loader.conf`, is
measured as a tagged event.
**Event Tag** `0xf5bc582a`
**Description** in the event log record is the file name, `loader.conf`.
**Measured hash** covers the content of `loader.conf` as it is read from the ESP.
### PCR 12, `EV_IPL`, "Kernel Command Line"
If the kernel command line was specified explicitly (by the user or in a Boot
@ -77,12 +88,11 @@ PE section order, as per the UKI specification, see above.
### PCR 12, `EV_IPL`, "Kernel Command Line"
Might happen up to four times, for kernel command lines from:
Might happen up to three times, for kernel command lines from:
1. Passed cmdline
2. System cmdline add-ons (one measurement covering all add-ons combined)
3. Per-UKI cmdline add-ons (one measurement covering all add-ons combined)
2. SMBIOS cmdline
2. System and per-UKI cmdline add-ons (one measurement covering all add-ons combined)
3. SMBIOS cmdline
**Description** in the event log record is the literal kernel command line in
UTF-16.
@ -90,6 +100,16 @@ UTF-16.
**Measured hash** covers the literal kernel command line in UTF-16 (without any
trailing NUL bytes).
### PCR 12, `EV_EVENT_TAG`, "Devicetrees"
Devicetree addons are measured individually as a tagged event.
**Event Tag** `0x6c46f751`
**Description** the addon filename.
**Measured hash** covers the content of the Devicetree.
### PCR 12, `EV_IPL`, "Per-UKI Credentials initrd"
**Description** in the event log record is the constant string "Credentials

View File

@ -167,19 +167,20 @@
<citerefentry><refentrytitle>ukify</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool will
add a SBAT policy by default if none is passed when building addons. For more information on SBAT see
<ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">Shim's documentation</ulink>.
Addons are supposed to be used to pass additional kernel command line parameters, regardless of the
kernel image being booted, for example to allow platform vendors to ship platform-specific
configuration. The loaded command line addon files are sorted, loaded, measured into TPM PCR 12 (if a
TPM is present) and appended to the kernel command line. UKI command line options are listed first,
then options from addons in <filename>/loader/addons/*.addon.efi</filename> are appended next, and
finally UKI-specific addons are appended last. Addons are always loaded in the same order based on the
filename, so that, given the same set of addons, the same set of measurements can be expected in
PCR12, however note that the filename is not protected by the PE signature, and as such an attacker
with write access to the ESP could potentially rename these files to change the order in which they
are loaded, in a way that could alter the functionality of the kernel, as some options might be order
dependent. If you sign such addons, you should pay attention to the PCR12 values and make use of an
attestation service so that improper use of your signed addons can be detected and dealt with using
one of the aforementioned revocation mechanisms.</para></listitem>
Addons are supposed to be used to pass additional kernel command line parameters or Devicetree blobs,
regardless of the kernel image being booted, for example to allow platform vendors to ship
platform-specific configuration. The loaded command line addon files are sorted, loaded, and measured
into TPM PCR 12 (if a TPM is present) and appended to the kernel command line. UKI command line options
are listed first, then options from addons in <filename>/loader/addons/*.addon.efi</filename>, and
finally UKI-specific addons. Device tree blobs are loaded and measured following the same algorithm.
Addons are always loaded in the same order based on the filename, so that, given the same set of
addons, the same set of measurements can be expected in PCR12. However, note that the filename is not
protected by the PE signature, and as such an attacker with write access to the ESP could potentially
rename these files to change the order in which they are loaded, in a way that could alter the
functionality of the kernel, as some options might be order dependent. If you sign such addons, you
should pay attention to the PCR12 values and make use of an attestation service so that improper use
of your signed addons can be detected and dealt with using one of the aforementioned revocation
mechanisms.</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>
@ -188,9 +189,9 @@
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 a <literal>.cmdline</literal> section is parsed from them. This is
supposed to be used to pass additional command line parameters to the kernel, regardless of the kernel
being booted.</para></listitem>
verified as PE binaries, and <literal>.cmdline</literal> and/or <literal>.dtb</literal> sections are
parsed from them. This is supposed to be used to pass additional command line parameters or Devicetree
blobs to the kernel, regardless of the kernel being booted.</para></listitem>
</itemizedlist>
<para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd

View File

@ -2,6 +2,7 @@
[Match]
Distribution=debian
Architecture=x86-64
[Content]
Packages=

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Distribution=debian
Architecture=arm64
[Content]
Packages=
bpftool
linux-image-cloud-arm64

View File

@ -15,7 +15,6 @@
typedef void (*free_func_t)(void *p);
typedef void* (*mfree_func_t)(void *p);
typedef void (*free_array_func_t)(void *p, size_t n);
/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
* proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */

View File

@ -104,37 +104,3 @@ static inline void erase_and_freep(void *p) {
static inline void erase_char(char *p) {
explicit_bzero_safe(p, sizeof(char));
}
/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
typedef struct ArrayCleanup {
void **parray;
size_t *pn;
free_array_func_t pfunc;
} ArrayCleanup;
static inline void array_cleanup(const ArrayCleanup *c) {
assert(c);
assert(!c->parray == !c->pn);
if (!c->parray)
return;
if (*c->parray) {
assert(c->pfunc);
c->pfunc(*c->parray, *c->pn);
*c->parray = NULL;
}
*c->pn = 0;
}
#define CLEANUP_ARRAY(array, n, func) \
_cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
.parray = (void**) &(array), \
.pn = &(n), \
.pfunc = (free_array_func_t) ({ \
void (*_f)(typeof(array[0]) *a, size_t b) = func; \
_f; \
}), \
}

View File

@ -384,6 +384,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
{ EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
{ EFI_STUB_FEATURE_CMDLINE_ADDONS, "Pick up .cmdline from addons" },
{ EFI_STUB_FEATURE_CMDLINE_SMBIOS, "Pick up .cmdline from SMBIOS Type 11" },
{ EFI_STUB_FEATURE_DEVICETREE_ADDONS, "Pick up .dtb from addons" },
};
_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;

View File

@ -21,6 +21,7 @@
#include "secure-boot.h"
#include "shim.h"
#include "ticks.h"
#include "tpm2-pcr.h"
#include "util.h"
#include "version.h"
#include "vmm.h"
@ -38,6 +39,8 @@ DECLARE_NOALLOC_SECTION(
DECLARE_SBAT(SBAT_BOOT_SECTION_TEXT);
#define LOADER_CONF_CONTENT_EVENT_TAG_ID UINT32_C(0xf5bc582a)
typedef enum LoaderType {
LOADER_UNDEFINED,
LOADER_AUTO,
@ -1621,7 +1624,7 @@ static EFI_STATUS efivar_get_timeout(const char16_t *var, uint32_t *ret_value) {
static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
_cleanup_free_ char *content = NULL;
size_t value = 0; /* avoid false maybe-uninitialized warning */
size_t content_size, value = 0; /* avoid false maybe-uninitialized warning */
EFI_STATUS err;
assert(root_dir);
@ -1638,9 +1641,19 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
.timeout_sec_efivar = TIMEOUT_UNSET,
};
err = file_read(root_dir, u"\\loader\\loader.conf", 0, 0, &content, NULL);
if (err == EFI_SUCCESS)
err = file_read(root_dir, u"\\loader\\loader.conf", 0, 0, &content, &content_size);
if (err == EFI_SUCCESS) {
config_defaults_load_from_file(config, content);
err = tpm_log_tagged_event(
TPM2_PCR_BOOT_LOADER_CONFIG,
POINTER_TO_PHYSICAL_ADDRESS(content),
content_size,
LOADER_CONF_CONTENT_EVENT_TAG_ID,
u"loader.conf",
/* ret_measured= */ NULL);
if (err != EFI_SUCCESS)
log_error_status(err, "Error measuring loader.conf into TPM: %m");
}
err = efivar_get_timeout(u"LoaderConfigTimeout", &config->timeout_sec_efivar);
if (err == EFI_SUCCESS)

View File

@ -42,6 +42,50 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(
&event_log_last);
}
static EFI_STATUS tpm2_measure_to_pcr_and_tagged_event_log(
EFI_TCG2_PROTOCOL *tcg,
uint32_t pcrindex,
EFI_PHYSICAL_ADDRESS buffer,
uint64_t buffer_size,
uint32_t event_id,
const char16_t *description) {
_cleanup_free_ struct event {
EFI_TCG2_EVENT tcg_event;
EFI_TCG2_TAGGED_EVENT tcg_tagged_event;
} _packed_ *event = NULL;
size_t desc_len, event_size;
assert(tcg);
assert(description);
desc_len = strsize16(description);
event_size = offsetof(EFI_TCG2_EVENT, Event) + offsetof(EFI_TCG2_TAGGED_EVENT, Event) + desc_len;
event = xmalloc0(event_size);
*event = (struct event) {
.tcg_event = (EFI_TCG2_EVENT) {
.Size = event_size,
.Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER),
.Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION,
.Header.PCRIndex = pcrindex,
.Header.EventType = EV_EVENT_TAG,
},
.tcg_tagged_event = {
.EventId = event_id,
.EventSize = desc_len,
},
};
memcpy(event->tcg_tagged_event.Event, description, desc_len);
return tcg->HashLogExtendEvent(
tcg,
0,
buffer, buffer_size,
&event->tcg_event);
}
static EFI_STATUS tpm2_measure_to_pcr_and_event_log(
EFI_TCG2_PROTOCOL *tcg,
uint32_t pcrindex,
@ -185,6 +229,38 @@ EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t
return err;
}
EFI_STATUS tpm_log_tagged_event(
uint32_t pcrindex,
EFI_PHYSICAL_ADDRESS buffer,
size_t buffer_size,
uint32_t event_id,
const char16_t *description,
bool *ret_measured) {
EFI_TCG2_PROTOCOL *tpm2;
EFI_STATUS err;
assert(description || pcrindex == UINT32_MAX);
assert(event_id > 0);
/* If EFI_SUCCESS is returned, will initialize ret_measured to true if we actually measured
* something, or false if measurement was turned off. */
tpm2 = tcg2_interface_check();
if (!tpm2 || pcrindex == UINT32_MAX) { /* PCR disabled? */
if (ret_measured)
*ret_measured = false;
return EFI_SUCCESS;
}
err = tpm2_measure_to_pcr_and_tagged_event_log(tpm2, pcrindex, buffer, buffer_size, event_id, description);
if (err == EFI_SUCCESS && ret_measured)
*ret_measured = true;
return err;
}
EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) {
_cleanup_free_ char16_t *c = NULL;

View File

@ -8,6 +8,7 @@
bool tpm_present(void);
EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured);
EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured);
EFI_STATUS tpm_log_tagged_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, uint32_t event_id, const char16_t *description, bool *ret_measured);
EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured);
#else
@ -28,6 +29,12 @@ static inline EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADD
return EFI_SUCCESS;
}
static inline EFI_STATUS tpm_log_tagged_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, uint32_t event_id, 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, bool *ret_measured) {
if (ret_measured)
*ret_measured = false;

View File

@ -11,6 +11,7 @@
#define TCG_ALG_SHA 0x4
#define EFI_TCG2_EVENT_HEADER_VERSION 1
#define EV_IPL 13
#define EV_EVENT_TAG UINT32_C(6)
typedef struct {
uint8_t Major;
@ -70,6 +71,12 @@ typedef struct {
uint8_t Event[];
} _packed_ EFI_TCG2_EVENT;
typedef struct {
uint32_t EventId;
uint32_t EventSize;
uint8_t Event[];
} _packed_ EFI_TCG2_TAGGED_EVENT;
typedef struct EFI_TCG_PROTOCOL EFI_TCG_PROTOCOL;
struct EFI_TCG_PROTOCOL {
EFI_STATUS (EFIAPI *StatusCheck)(

View File

@ -26,6 +26,8 @@ DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION
DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
#define ADDON_FILENAME_EVENT_TAG_ID UINT32_C(0x6c46f751)
static EFI_STATUS combine_initrd(
EFI_PHYSICAL_ADDRESS initrd_base, size_t initrd_size,
const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
@ -95,6 +97,7 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
EFI_STUB_FEATURE_RANDOM_SEED | /* We pass a random seed to the kernel */
EFI_STUB_FEATURE_CMDLINE_ADDONS | /* We pick up .cmdline addons */
EFI_STUB_FEATURE_CMDLINE_SMBIOS | /* We support extending kernel cmdline from SMBIOS Type #11 */
EFI_STUB_FEATURE_DEVICETREE_ADDONS | /* We pick up .dtb addons */
0;
assert(loaded_image);
@ -253,29 +256,132 @@ static EFI_STATUS load_addons_from_dir(
return EFI_SUCCESS;
}
static EFI_STATUS cmdline_append_and_measure_addons(
static void cmdline_append_and_measure_addons(
char16_t *cmdline_global,
char16_t *cmdline_uki,
char16_t **cmdline_append,
bool *ret_parameters_measured) {
_cleanup_free_ char16_t *tmp = NULL, *merged = NULL;
bool m = false;
assert(cmdline_append);
assert(ret_parameters_measured);
if (isempty(cmdline_global) && isempty(cmdline_uki))
return;
merged = xasprintf("%ls%ls%ls",
strempty(cmdline_global),
isempty(cmdline_global) || isempty(cmdline_uki) ? u"" : u" ",
strempty(cmdline_uki));
mangle_stub_cmdline(merged);
if (isempty(merged))
return;
(void) tpm_log_load_options(merged, &m);
*ret_parameters_measured = m;
tmp = TAKE_PTR(*cmdline_append);
*cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", merged);
}
static void dtb_install_addons(
struct devicetree_state *dt_state,
void **dt_bases,
size_t *dt_sizes,
char16_t **dt_filenames,
size_t n_dts,
bool *ret_parameters_measured) {
int parameters_measured = -1;
EFI_STATUS err;
assert(dt_state);
assert(n_dts == 0 || (dt_bases && dt_sizes && dt_filenames));
assert(ret_parameters_measured);
for (size_t i = 0; i < n_dts; ++i) {
err = devicetree_install_from_memory(dt_state, dt_bases[i], dt_sizes[i]);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading addon devicetree, ignoring: %m");
else {
bool m = false;
err = tpm_log_tagged_event(
TPM2_PCR_KERNEL_CONFIG,
POINTER_TO_PHYSICAL_ADDRESS(dt_bases[i]),
dt_sizes[i],
ADDON_FILENAME_EVENT_TAG_ID,
dt_filenames[i],
&m);
if (err != EFI_SUCCESS)
return (void) log_error_status(
err,
"Unable to add measurement of DTB addon #%zu to PCR %i: %m",
i,
TPM2_PCR_KERNEL_CONFIG);
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
}
}
*ret_parameters_measured = parameters_measured;
}
static void dt_bases_free(void **dt_bases, size_t n_dt) {
assert(dt_bases || n_dt == 0);
for (size_t i = 0; i < n_dt; ++i)
free(dt_bases[i]);
free(dt_bases);
}
static void dt_filenames_free(char16_t **dt_filenames, size_t n_dt) {
assert(dt_filenames || n_dt == 0);
for (size_t i = 0; i < n_dt; ++i)
free(dt_filenames[i]);
free(dt_filenames);
}
static EFI_STATUS load_addons(
EFI_HANDLE stub_image,
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
const char16_t *prefix,
const char *uname,
bool *ret_parameters_measured,
char16_t **cmdline_append) {
char16_t **ret_cmdline,
void ***ret_dt_bases,
size_t **ret_dt_sizes,
char16_t ***ret_dt_filenames,
size_t *ret_n_dt) {
_cleanup_free_ size_t *dt_sizes = NULL;
_cleanup_(strv_freep) char16_t **items = NULL;
_cleanup_(file_closep) EFI_FILE *root = NULL;
_cleanup_free_ char16_t *buffer = NULL;
size_t n_items = 0, n_allocated = 0;
_cleanup_free_ char16_t *cmdline = NULL;
size_t n_items = 0, n_allocated = 0, n_dt = 0;
char16_t **dt_filenames = NULL;
void **dt_bases = NULL;
EFI_STATUS err;
assert(stub_image);
assert(loaded_image);
assert(prefix);
assert(ret_parameters_measured);
assert(cmdline_append);
assert(!!ret_dt_bases == !!ret_dt_sizes);
assert(!!ret_dt_bases == !!ret_n_dt);
assert(!!ret_dt_filenames == !!ret_n_dt);
if (!loaded_image->DeviceHandle)
return EFI_SUCCESS;
CLEANUP_ARRAY(dt_bases, n_dt, dt_bases_free);
CLEANUP_ARRAY(dt_filenames, n_dt, dt_filenames_free);
err = open_volume(loaded_image->DeviceHandle, &root);
if (err == EFI_UNSUPPORTED)
/* Error will be unsupported if the bootloader doesn't implement the file system protocol on
@ -325,11 +431,12 @@ static EFI_STATUS cmdline_append_and_measure_addons(
return log_error_status(err, "Failed to find protocol in %ls: %m", items[i]);
err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, addrs, szs);
if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_CMDLINE] == 0) {
if (err != EFI_SUCCESS ||
(szs[UNIFIED_SECTION_CMDLINE] == 0 && szs[UNIFIED_SECTION_DTB] == 0)) {
if (err == EFI_SUCCESS)
err = EFI_NOT_FOUND;
log_error_status(err,
"Unable to locate embedded .cmdline section in %ls, ignoring: %m",
"Unable to locate embedded .cmdline/.dtb sections in %ls, ignoring: %m",
items[i]);
continue;
}
@ -350,22 +457,42 @@ static EFI_STATUS cmdline_append_and_measure_addons(
continue;
}
_cleanup_free_ char16_t *tmp = TAKE_PTR(buffer),
*extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
szs[UNIFIED_SECTION_CMDLINE]);
buffer = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16);
if (ret_cmdline && szs[UNIFIED_SECTION_CMDLINE] > 0) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline),
*extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
szs[UNIFIED_SECTION_CMDLINE]);
cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16);
}
if (ret_dt_bases && szs[UNIFIED_SECTION_DTB] > 0) {
dt_sizes = xrealloc(dt_sizes,
n_dt * sizeof(size_t),
(n_dt + 1) * sizeof(size_t));
dt_sizes[n_dt] = szs[UNIFIED_SECTION_DTB];
dt_bases = xrealloc(dt_bases,
n_dt * sizeof(void *),
(n_dt + 1) * sizeof(void *));
dt_bases[n_dt] = xmemdup((uint8_t*)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_DTB],
dt_sizes[n_dt]);
dt_filenames = xrealloc(dt_filenames,
n_dt * sizeof(char16_t *),
(n_dt + 1) * sizeof(char16_t *));
dt_filenames[n_dt] = xstrdup16(items[i]);
++n_dt;
}
}
mangle_stub_cmdline(buffer);
if (ret_cmdline && !isempty(cmdline))
*ret_cmdline = TAKE_PTR(cmdline);
if (!isempty(buffer)) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline_append);
bool m = false;
(void) tpm_log_load_options(buffer, &m);
*ret_parameters_measured = m;
*cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", buffer);
if (ret_n_dt && n_dt > 0) {
*ret_dt_filenames = TAKE_PTR(dt_filenames);
*ret_dt_bases = TAKE_PTR(dt_bases);
*ret_dt_sizes = TAKE_PTR(dt_sizes);
*ret_n_dt = n_dt;
}
return EFI_SUCCESS;
@ -374,12 +501,15 @@ static EFI_STATUS cmdline_append_and_measure_addons(
static EFI_STATUS run(EFI_HANDLE image) {
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
size_t linux_size, initrd_size, dt_size;
void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
_cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
size_t linux_size, initrd_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
_cleanup_free_ char16_t *cmdline = NULL;
_cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL;
int sections_measured = -1, parameters_measured = -1;
_cleanup_free_ char *uname = NULL;
bool sysext_measured = false, m;
@ -406,6 +536,40 @@ static EFI_STATUS run(EFI_HANDLE image) {
return log_error_status(err, "Unable to locate embedded .linux section: %m");
}
CLEANUP_ARRAY(dt_bases_addons_global, n_dts_addons_global, dt_bases_free);
CLEANUP_ARRAY(dt_bases_addons_uki, n_dts_addons_uki, dt_bases_free);
CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free);
CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free);
/* 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. */
err = load_addons(
image,
loaded_image,
u"\\loader\\addons",
uname,
&cmdline_addons_global,
&dt_bases_addons_global,
&dt_sizes_addons_global,
&dt_filenames_addons_global,
&n_dts_addons_global);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading global addons, ignoring: %m");
_cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath);
err = load_addons(
image,
loaded_image,
dropin_dir,
uname,
&cmdline_addons_uki,
&dt_bases_addons_uki,
&dt_sizes_addons_uki,
&dt_filenames_addons_uki,
&n_dts_addons_uki);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
/* 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. */
@ -468,30 +632,10 @@ static EFI_STATUS run(EFI_HANDLE image) {
}
/* If we have any extra command line to add via PE addons, load them now and append, and
* measure the additions separately, after the embedded options, but before the smbios ones,
* measure the additions together, after the embedded options, but before the smbios ones,
* so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are
* loaded first, and the image-specific ones later, for the same reason. */
err = cmdline_append_and_measure_addons(
image,
loaded_image,
u"\\loader\\addons",
uname,
&m,
&cmdline);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading global addons, ignoring: %m");
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
_cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath);
err = cmdline_append_and_measure_addons(
image,
loaded_image,
dropin_dir,
uname,
&m,
&cmdline);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
cmdline_append_and_measure_addons(cmdline_addons_global, cmdline_addons_uki, &cmdline, &m);
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
/* SMBIOS OEM Strings data is controlled by the host admin and not covered
@ -552,6 +696,32 @@ static EFI_STATUS run(EFI_HANDLE image) {
&m) == EFI_SUCCESS)
sysext_measured = m;
dt_size = szs[UNIFIED_SECTION_DTB];
dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
/* First load the base device tree, then fix it up using addons - global first, then per-UKI. */
if (dt_size > 0) {
err = devicetree_install_from_memory(
&dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading embedded devicetree: %m");
}
dtb_install_addons(&dt_state,
dt_bases_addons_global,
dt_sizes_addons_global,
dt_filenames_addons_global,
n_dts_addons_global,
&m);
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
dtb_install_addons(&dt_state,
dt_bases_addons_uki,
dt_sizes_addons_uki,
dt_filenames_addons_uki,
n_dts_addons_uki,
&m);
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
if (parameters_measured > 0)
(void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0);
if (sysext_measured)
@ -600,9 +770,6 @@ static EFI_STATUS run(EFI_HANDLE image) {
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;
_cleanup_pages_ Pages initrd_pages = {};
if (credential_initrd || global_credential_initrd || sysext_initrd || pcrsig_initrd || pcrpkey_initrd) {
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
@ -637,13 +804,6 @@ static EFI_STATUS run(EFI_HANDLE image) {
pcrpkey_initrd = mfree(pcrpkey_initrd);
}
if (dt_size > 0) {
err = devicetree_install_from_memory(
&dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
if (err != EFI_SUCCESS)
log_error_status(err, "Error loading embedded devicetree: %m");
}
err = linux_exec(image, cmdline,
PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size,
PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);

View File

@ -58,6 +58,11 @@ static inline void *xrealloc(void *p, size_t old_size, size_t new_size) {
return t;
}
_malloc_ _alloc_(2) _returns_nonnull_ _warn_unused_result_
static inline void* xmemdup(const void *p, size_t l) {
return memcpy(xmalloc(l), p, l);
}
#define xnew(type, n) ((type *) xmalloc_multiply(sizeof(type), (n)))
typedef struct {

View File

@ -31,6 +31,7 @@
#define EFI_STUB_FEATURE_RANDOM_SEED (UINT64_C(1) << 4)
#define EFI_STUB_FEATURE_CMDLINE_ADDONS (UINT64_C(1) << 5)
#define EFI_STUB_FEATURE_CMDLINE_SMBIOS (UINT64_C(1) << 6)
#define EFI_STUB_FEATURE_DEVICETREE_ADDONS (UINT64_C(1) << 7)
typedef enum SecureBootMode {
SECURE_BOOT_UNSUPPORTED,

View File

@ -70,3 +70,39 @@ static inline void erase_varp(struct VarEraser *e) {
.p = (ptr), \
.size = (sz), \
}
typedef void (*free_array_func_t)(void *p, size_t n);
/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
typedef struct ArrayCleanup {
void **parray;
size_t *pn;
free_array_func_t pfunc;
} ArrayCleanup;
static inline void array_cleanup(const ArrayCleanup *c) {
assert(c);
assert(!c->parray == !c->pn);
if (!c->parray)
return;
if (*c->parray) {
assert(c->pfunc);
c->pfunc(*c->parray, *c->pn);
*c->parray = NULL;
}
*c->pn = 0;
}
#define CLEANUP_ARRAY(array, n, func) \
_cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
.parray = (void**) &(array), \
.pn = &(n), \
.pfunc = (free_array_func_t) ({ \
void (*_f)(typeof(array[0]) *a, size_t b) = func; \
_f; \
}), \
}