[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