[PATCH v2] efi_loader: Measure the loaded DTB
Ilias Apalodimas
ilias.apalodimas at linaro.org
Fri Feb 17 18:18:39 CET 2023
Hi Etienne
On Thu, Feb 16, 2023 at 05:29:48PM +0100, Etienne Carriere wrote:
> Measures the DTB passed to the EFI application upon new boolean config
> switch CONFIG_EFI_TCG2_PROTOCOL_MEASURE_DTB. For platforms where the
> content of the DTB passed to the OS can change across reboots, there is
> not point measuring it hence the config switch to allow platform to not
> embed this feature.
>
> Co-developed-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
> Signed-off-by: Etienne Carriere <etienne.carriere at linaro.org>
> ---
> Changes since v1
> - Moved measurement to after DTB tweaks in efi_install_fdt() and change
> its measure to hash only populated areas in the DTB (header, structs,
> strings and reserved memory maps). efi_tcg2_measure_dtb() computes
> the hash of these concatenated areas and used it as DTB measurement.
> - Remove useless default value 'n' for EFI_TCG2_PROTOCOL_MEASURE_DTB.
> - I did not add EFI_TCG2_PROTOCOL_MEASURE_DTB dependencies on
> !NET_RANDOM_ETHADDR neither on !CMD_KASLRSEED. If ethernet address
> is random but always the same at each boot as saved in environment,
> it's measure is meaningful. CMD_KASLRSEED effect in DT if already
> addressed by efi_try_purge_kaslr_seed() prior measurement.
That's correct, and since the TPM is guaranteed to have an RNG, we will
always install the EFI_RNG protocol and get rid of kaslr-seed for the DT.
> ---
> cmd/bootefi.c | 8 +++++
> include/efi_loader.h | 2 ++
> include/efi_tcg2.h | 10 ++++++
> include/tpm-v2.h | 2 ++
> lib/efi_loader/Kconfig | 11 ++++++
> lib/efi_loader/efi_tcg2.c | 73 +++++++++++++++++++++++++++++++++++++++
> 6 files changed, 106 insertions(+)
>
> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> index 2a7d42925d..6618335ddf 100644
> --- a/cmd/bootefi.c
> +++ b/cmd/bootefi.c
> @@ -332,6 +332,14 @@ efi_status_t efi_install_fdt(void *fdt)
>
> efi_try_purge_kaslr_seed(fdt);
>
> + if (CONFIG_IS_ENABLED(EFI_TCG2_PROTOCOL_MEASURE_DTB)) {
> + ret = efi_tcg2_measure_dtb(fdt);
> + if (ret == EFI_SECURITY_VIOLATION) {
> + log_err("ERROR: failed to measure DTB\n");
> + return ret;
> + }
> + }
> +
> /* Install device tree as UEFI table */
> ret = efi_install_configuration_table(&efi_guid_fdt, fdt);
> if (ret != EFI_SUCCESS) {
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 4560b0d04c..4ecfdf919b 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -531,6 +531,8 @@ efi_status_t efi_tcg2_notify_exit_boot_services_failed(void);
> efi_status_t efi_tcg2_measure_efi_app_invocation(struct efi_loaded_image_obj *handle);
> /* Measure efi application exit */
> efi_status_t efi_tcg2_measure_efi_app_exit(void);
> +/* Measure DTB */
> +efi_status_t efi_tcg2_measure_dtb(void *dtb);
> /* Called by bootefi to initialize root node */
> efi_status_t efi_root_node_register(void);
> /* Called by bootefi to initialize runtime */
> diff --git a/include/efi_tcg2.h b/include/efi_tcg2.h
> index 874306dc11..b1c3abd097 100644
> --- a/include/efi_tcg2.h
> +++ b/include/efi_tcg2.h
> @@ -233,6 +233,16 @@ struct efi_gpt_data {
> gpt_entry partitions[];
> } __packed;
>
> +/**
> + * struct tdUEFI_PLATFORM_FIRMWARE_BLOB2
> + * @blob_description_size: Byte size of @data
> + * @data: Description data
> + */
> +struct uefi_platform_firmware_blob2 {
> + u8 blob_description_size;
> + u8 data[];
> +} __packed;
> +
> struct efi_tcg2_protocol {
> efi_status_t (EFIAPI * get_capability)(struct efi_tcg2_protocol *this,
> struct efi_tcg2_boot_service_capability *capability);
> diff --git a/include/tpm-v2.h b/include/tpm-v2.h
> index 737e57551d..2df3dad553 100644
> --- a/include/tpm-v2.h
> +++ b/include/tpm-v2.h
> @@ -105,6 +105,8 @@ struct udevice;
> "Exit Boot Services Returned with Failure"
> #define EFI_EXIT_BOOT_SERVICES_SUCCEEDED \
> "Exit Boot Services Returned with Success"
> +#define EFI_DTB_EVENT_STRING \
> + "DTB DATA"
>
> /* TPMS_TAGGED_PROPERTY Structure */
> struct tpms_tagged_property {
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index c56904afc2..c05a54df16 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -346,6 +346,17 @@ config EFI_TCG2_PROTOCOL_EVENTLOG_SIZE
> this is going to be allocated twice. One for the eventlog it self
> and one for the configuration table that is required from the spec
>
> +config EFI_TCG2_PROTOCOL_MEASURE_DTB
> + bool "Measure DTB with EFI_TCG2_PROTOCOL"
> + depends on EFI_TCG2_PROTOCOL
> + help
> + When enabled, the DTB image passed to the booted EFI image is
> + measured using the EFI TCG2 protocol. Do not enable this feature if
> + the passed DTB contains data that change across platform reboots
> + and cannot be used has a predictable measurement. Otherwise
> + this feature allows better measurement of the system boot
> + sequence.
> +
> config EFI_LOAD_FILE2_INITRD
> bool "EFI_FILE_LOAD2_PROTOCOL for Linux initial ramdisk"
> default y
> diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
> index 918e9a2686..2dcc317157 100644
> --- a/lib/efi_loader/efi_tcg2.c
> +++ b/lib/efi_loader/efi_tcg2.c
> @@ -2175,6 +2175,79 @@ out1:
> return ret;
> }
>
> +/* Return the byte size of reserved map area in DTB or -1 upon error */
> +static ssize_t size_of_rsvmap(void *dtb)
> +{
> + struct fdt_reserve_entry e;
> + ssize_t size_max;
> + ssize_t size;
> + u8 *rsvmap_base;
> +
> + rsvmap_base = (u8 *)dtb + fdt_off_mem_rsvmap(dtb);
> + size_max = fdt_totalsize(dtb) - fdt_off_mem_rsvmap(dtb);
> + size = 0;
> +
> + do {
> + memcpy(&e, rsvmap_base + size, sizeof(e));
> + size += sizeof(e);
> + if (size > size_max)
> + return -1;
> + } while (e.size);
> +
> + return size;
> +}
> +
> +/**
> + * efi_tcg2_measure_dtb() - measure DTB passed to the OS
> + *
> + * @dtb: pointer to the device tree blob
> + *
> + * Return: status code
> + */
> +efi_status_t efi_tcg2_measure_dtb(void *dtb)
> +{
> + struct uefi_platform_firmware_blob2 *blob;
> + struct fdt_header *header;
> + sha256_context hash_ctx;
> + struct udevice *dev;
> + ssize_t rsvmap_size;
> + efi_status_t ret;
> + u32 event_size;
> +
> + if (!is_tcg2_protocol_installed())
> + return EFI_SUCCESS;
> +
> + ret = platform_get_tpm2_device(&dev);
> + if (ret != EFI_SUCCESS)
> + return EFI_SECURITY_VIOLATION;
> +
> + rsvmap_size = size_of_rsvmap(dtb);
> + if (rsvmap_size < 0)
> + return EFI_SECURITY_VIOLATION;
Why is this a security violation? Maybe EFI_INVALID_PARAMETER?
> +
> + event_size = sizeof(*blob) + sizeof(EFI_DTB_EVENT_STRING) + SHA256_SUM_LEN;
> + blob = calloc(1, event_size);
> + if (!blob)
> + return EFI_OUT_OF_RESOURCES;
> +
> + blob->blob_description_size = sizeof(EFI_DTB_EVENT_STRING);
> + memcpy(blob->data, EFI_DTB_EVENT_STRING, blob->blob_description_size);
> +
> + /* Measure populated areas of the DTB */
> + header = dtb;
> + sha256_starts(&hash_ctx);
> + sha256_update(&hash_ctx, (u8 *)header, sizeof(struct fdt_header));
> + sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_dt_struct(dtb), fdt_size_dt_strings(dtb));
> + sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_dt_strings(dtb), fdt_size_dt_struct(dtb));
> + sha256_update(&hash_ctx, (u8 *)dtb + fdt_off_mem_rsvmap(dtb), rsvmap_size);
> + sha256_finish(&hash_ctx, blob->data + blob->blob_description_size);
> +
> + ret = tcg2_measure_event(dev, 0, EV_POST_CODE, event_size, (u8 *)blob);
> +
> + free(blob);
> + return ret;
> +}
> +
> /**
> * efi_tcg2_measure_efi_app_invocation() - measure efi app invocation
> *
> --
> 2.25.1
>
With the return code changed
Tested-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
More information about the U-Boot
mailing list