[PATCH v9 4/6] bootm: Support boot measurement
Eddie James
eajames at linux.ibm.com
Mon Aug 7 16:57:05 CEST 2023
On 8/7/23 09:52, Ilias Apalodimas wrote:
> Hi,
>
> On Mon, 7 Aug 2023 at 17:43, Eddie James <eajames at linux.ibm.com> wrote:
>>
>> On 8/4/23 13:10, Sean Edmond wrote:
>>> On 2023-03-08 1:25 p.m., Eddie James wrote:
>>>> Add a configuration option to measure the boot through the bootm
>>>> function. Add the measurement state to the booti and bootz paths
>>>> as well.
>>>>
>>>> Signed-off-by: Eddie James <eajames at linux.ibm.com>
>>>> Reviewed-by: Simon Glass <sjg at chromium.org>
>>>> ---
>>>> Changes since v8:
>>>> - Added a configuration option to select to ignore any existing
>>>> event log. This would only be selected for systems that know
>>>> that U-Boot is the first stage bootloader. This is necessary
>>>> because the reserved memory region may persist through resets
>>>> and so U-Boot attempts to append to the previous boot's log.
>>>>
>>>> Changes since v6:
>>>> - Added comment for bootm_measure
>>>> - Fixed line length in bootm_measure
>>>>
>>>> boot/Kconfig | 32 +++++++++++++++++++++
>>>> boot/bootm.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>> cmd/booti.c | 1 +
>>>> cmd/bootm.c | 2 ++
>>>> cmd/bootz.c | 1 +
>>>> include/bootm.h | 11 ++++++++
>>>> include/image.h | 1 +
>>>> 7 files changed, 122 insertions(+)
>>>>
>>>> diff --git a/boot/Kconfig b/boot/Kconfig
>>>> index 5f491625c8..8119519c9f 100644
>>>> --- a/boot/Kconfig
>>>> +++ b/boot/Kconfig
>>>> @@ -629,6 +629,38 @@ config LEGACY_IMAGE_FORMAT
>>>> loaded. If a board needs the legacy image format support in this
>>>> case, enable it here.
>>>> +config MEASURED_BOOT
>>>> + bool "Measure boot images and configuration to TPM and event log"
>>>> + depends on HASH && TPM_V2
>>>> + help
>>>> + This option enables measurement of the boot process. Measurement
>>>> + involves creating cryptographic hashes of the binary images that
>>>> + are booting and storing them in the TPM. In addition, a log of
>>>> + these hashes is stored in memory for the OS to verify the booted
>>>> + images and configuration. Enable this if the OS has configured
>>>> + some memory area for the event log and you intend to use some
>>>> + attestation tools on your system.
>>>> +
>>>> +if MEASURED_BOOT
>>>> + config MEASURE_DEVICETREE
>>>> + bool "Measure the devicetree image"
>>>> + default y if MEASURED_BOOT
>>>> + help
>>>> + On some platforms, the devicetree is not static as it may contain
>>>> + random MAC addresses or other such data that changes each boot.
>>>> + Therefore, it should not be measured into the TPM. In that case,
>>>> + disable the measurement here.
>>>> +
>>>> + config MEASURE_IGNORE_LOG
>>>> + bool "Ignore the existing event log"
>>>> + default n
>>>> + help
>>>> + On platforms that use an event log memory region that persists
>>>> + through system resets and are the first stage bootloader, then
>>>> + this option should be enabled to ignore any existing data in the
>>>> + event log memory region.
>>>> +endif # MEASURED_BOOT
>>>> +
>>>> config SUPPORT_RAW_INITRD
>>>> bool "Enable raw initrd images"
>>>> help
>>>> diff --git a/boot/bootm.c b/boot/bootm.c
>>>> index 2eec60ec7b..2685bdbd74 100644
>>>> --- a/boot/bootm.c
>>>> +++ b/boot/bootm.c
>>>> @@ -22,6 +22,7 @@
>>>> #include <asm/global_data.h>
>>>> #include <asm/io.h>
>>>> #include <linux/sizes.h>
>>>> +#include <tpm-v2.h>
>>>> #if defined(CONFIG_CMD_USB)
>>>> #include <usb.h>
>>>> #endif
>>>> @@ -659,6 +660,75 @@ int bootm_process_cmdline_env(int flags)
>>>> return 0;
>>>> }
>>>> +int bootm_measure(struct bootm_headers *images)
>>>> +{
>>>> + int ret = 0;
>>>> +
>>>> + /* Skip measurement if EFI is going to do it */
>>>> + if (images->os.os == IH_OS_EFI &&
>>>> + IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL) &&
>>>> + IS_ENABLED(CONFIG_BOOTM_EFI))
>>>> + return ret;
>>>> +
>>> it looks like your measured boot implementation is hardcoding the
>>> following PCR indexes:
>>>
>>> PCR #8 - kernel image measurement
>>> PCR #9 - initrd measurement
>>> PCR #0 - kernel DTB measurement
>>> PCR #1 - bootargs measurement
>>
>> Hi,
>>
>>
>> Yes, I followed this document as closely as I could:
>> https://trustedcomputinggroup.org/wp-content/uploads/TCG_ServerManagementDomainFirmwareProfile_v1p00_11aug2020.pdf
>>
>> Which provides what should go in what PCR. I can understand users
>> wanting a different setup, but as you say, that's probably out of the
>> scope of this series.
>>
> Completely out of scope* of the series. The purpose is follow the TCG
> spec. The minor deviations is our choice of the DTB in PCR1 (but
> that's what the spec does for ACPI tables) and the choice for initrd
> (which is what we do in the linux kernel).
>
> We can reuse the functions ofc to measure random blobs, but that would
> require some kind of config (maybe in a dts??) of what to measure.
>
>
> Eddie, I've pinged you in the past. I rebased and fixed a few issues
> of your tree here [0]. Do you plan to resend it at some point?
>
> [0]https://source.denx.de/u-boot/custodians/u-boot-tpm/-/commits/eddie2/
Yes, sorry for the delay. I'll resend it now. Thank you for rebasing!
Thanks,
Eddie
>
> Cheers
> /Ilias
>> Thanks,
>>
>> Eddie
>>
>>
>>> I wasn't able to find any specificaton on this measured boot
>>> "profile". Are you able to provide a reference?
>>>
>>> We've implemented our own version of measured boot, which maps
>>> measurements to different PCR indexes. In many cases, the data we're
>>> measuring is also different.
>>>
>>> To make this feature more usable by others it would be nice to see a
>>> more generic interface that would allow the user to specify the PCR
>>> indexes, and the data to hash into these indexes. This would allow
>>> everyone to create their own custom measured boot "profile". This
>>> request is probably beyond the scope of your current efforts, but I
>>> except this implementation to evolve significantly if/when it's accepted.
>>>
>>>> + if (IS_ENABLED(CONFIG_MEASURED_BOOT)) {
>>>> + struct tcg2_event_log elog;
>>>> + struct udevice *dev;
>>>> + void *initrd_buf;
>>>> + void *image_buf;
>>>> + const char *s;
>>>> + u32 rd_len;
>>>> + bool ign;
>>>> +
>>>> + elog.log_size = 0;
>>>> + ign = IS_ENABLED(CONFIG_MEASURE_IGNORE_LOG);
>>>> + ret = tcg2_measurement_init(&dev, &elog, ign);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + image_buf = map_sysmem(images->os.image_start,
>>>> + images->os.image_len);
>>>> + ret = tcg2_measure_data(dev, &elog, 8, images->os.image_len,
>>>> + image_buf, EV_COMPACT_HASH,
>>>> + strlen("linux") + 1, (u8 *)"linux");
>>>> + if (ret)
>>>> + goto unmap_image;
>>>> +
>>>> + rd_len = images->rd_end - images->rd_start;
>>>> + initrd_buf = map_sysmem(images->rd_start, rd_len);
>>>> + ret = tcg2_measure_data(dev, &elog, 9, rd_len, initrd_buf,
>>>> + EV_COMPACT_HASH, strlen("initrd") + 1,
>>>> + (u8 *)"initrd");
>>>> + if (ret)
>>>> + goto unmap_initrd;
>>>> +
>>>> + if (IS_ENABLED(CONFIG_MEASURE_DEVICETREE)) {
>>>> + ret = tcg2_measure_data(dev, &elog, 0, images->ft_len,
>>>> + (u8 *)images->ft_addr,
>>>> + EV_TABLE_OF_DEVICES,
>>>> + strlen("dts") + 1,
>>>> + (u8 *)"dts");
>>>> + if (ret)
>>>> + goto unmap_initrd;
>>>> + }
>>>> +
>>>> + s = env_get("bootargs");
>>>> + if (!s)
>>>> + s = "";
>>>> + ret = tcg2_measure_data(dev, &elog, 1, strlen(s) + 1, (u8 *)s,
>>>> + EV_PLATFORM_CONFIG_FLAGS,
>>>> + strlen(s) + 1, (u8 *)s);
>>>> +
>>>> +unmap_initrd:
>>>> + unmap_sysmem(initrd_buf);
>>>> +
>>>> +unmap_image:
>>>> + unmap_sysmem(image_buf);
>>>> + tcg2_measurement_term(dev, &elog, ret != 0);
>>>> + }
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> /**
>>>> * Execute selected states of the bootm command.
>>>> *
>>>> @@ -710,6 +780,10 @@ int do_bootm_states(struct cmd_tbl *cmdtp, int
>>>> flag, int argc,
>>>> if (!ret && (states & BOOTM_STATE_FINDOTHER))
>>>> ret = bootm_find_other(cmdtp, flag, argc, argv);
>>>> + if (IS_ENABLED(CONFIG_MEASURED_BOOT) && !ret &&
>>>> + (states & BOOTM_STATE_MEASURE))
>>>> + bootm_measure(images);
>>>> +
>>>> /* Load the OS */
>>>> if (!ret && (states & BOOTM_STATE_LOADOS)) {
>>>> iflag = bootm_disable_interrupts();
>>>> diff --git a/cmd/booti.c b/cmd/booti.c
>>>> index 6ac39193db..659bb10549 100644
>>>> --- a/cmd/booti.c
>>>> +++ b/cmd/booti.c
>>>> @@ -127,6 +127,7 @@ int do_booti(struct cmd_tbl *cmdtp, int flag, int
>>>> argc, char *const argv[])
>>>> #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
>>>> BOOTM_STATE_RAMDISK |
>>>> #endif
>>>> + BOOTM_STATE_MEASURE |
>>>> BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
>>>> BOOTM_STATE_OS_GO,
>>>> &images, 1);
>>>> diff --git a/cmd/bootm.c b/cmd/bootm.c
>>>> index 37c2af96e0..0c4a713e02 100644
>>>> --- a/cmd/bootm.c
>>>> +++ b/cmd/bootm.c
>>>> @@ -161,6 +161,8 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int
>>>> argc, char *const argv[])
>>>> BOOTM_STATE_OS_GO;
>>>> if (IS_ENABLED(CONFIG_SYS_BOOT_RAMDISK_HIGH))
>>>> states |= BOOTM_STATE_RAMDISK;
>>>> + if (IS_ENABLED(CONFIG_MEASURED_BOOT))
>>>> + states |= BOOTM_STATE_MEASURE;
>>>> if (IS_ENABLED(CONFIG_PPC) || IS_ENABLED(CONFIG_MIPS))
>>>> states |= BOOTM_STATE_OS_CMDLINE;
>>>> ret = do_bootm_states(cmdtp, flag, argc, argv, states, &images,
>>>> 1);
>>>> diff --git a/cmd/bootz.c b/cmd/bootz.c
>>>> index f1423573d2..87922bfc3c 100644
>>>> --- a/cmd/bootz.c
>>>> +++ b/cmd/bootz.c
>>>> @@ -81,6 +81,7 @@ int do_bootz(struct cmd_tbl *cmdtp, int flag, int
>>>> argc, char *const argv[])
>>>> #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
>>>> BOOTM_STATE_RAMDISK |
>>>> #endif
>>>> + BOOTM_STATE_MEASURE |
>>>> BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
>>>> BOOTM_STATE_OS_GO,
>>>> &images, 1);
>>>> diff --git a/include/bootm.h b/include/bootm.h
>>>> index 044a4797ed..76e8e38c82 100644
>>>> --- a/include/bootm.h
>>>> +++ b/include/bootm.h
>>>> @@ -55,6 +55,17 @@ ulong bootm_disable_interrupts(void);
>>>> int bootm_find_images(int flag, int argc, char *const argv[], ulong
>>>> start,
>>>> ulong size);
>>>> +/*
>>>> + * Measure the boot images. Measurement is the process of hashing
>>>> some binary
>>>> + * data and storing it into secure memory, i.e. TPM PCRs. In
>>>> addition, each
>>>> + * measurement is logged into the platform event log such that the
>>>> operating
>>>> + * system can access it and perform attestation of the boot.
>>>> + *
>>>> + * @images: The structure containing the various images to boot
>>>> (linux,
>>>> + * initrd, dts, etc.)
>>>> + */
>>>> +int bootm_measure(struct bootm_headers *images);
>>>> +
>>>> int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc,
>>>> char *const argv[], int states, struct bootm_headers
>>>> *images,
>>>> int boot_progress);
>>>> diff --git a/include/image.h b/include/image.h
>>>> index 7717a4c13d..f7414b5338 100644
>>>> --- a/include/image.h
>>>> +++ b/include/image.h
>>>> @@ -407,6 +407,7 @@ struct bootm_headers {
>>>> #define BOOTM_STATE_OS_FAKE_GO 0x00000200 /* 'Almost' run the
>>>> OS */
>>>> #define BOOTM_STATE_OS_GO 0x00000400
>>>> #define BOOTM_STATE_PRE_LOAD 0x00000800
>>>> +#define BOOTM_STATE_MEASURE 0x00001000
>>>> int state;
>>>> #if defined(CONFIG_LMB) && !defined(USE_HOSTCC)
>>>
More information about the U-Boot
mailing list