Message ID | 20230216162948.2343115-1-etienne.carriere@linaro.org |
---|---|
State | Accepted |
Commit | aa2d3945ce6df43903d76cadde1c0669d6d5d43b |
Headers | show |
Series | [v2] efi_loader: Measure the loaded DTB | expand |
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@linaro.org> > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org> > Signed-off-by: Etienne Carriere <etienne.carriere@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@linaro.org> Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
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; + + 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 *