"TCG PC Client Platform Firmware Profile Specification"
requires to measure every attempt to load and execute
a OS Loader(a UEFI application) into PCR[4].
This commit adds the PE/COFF image measurement, extends PCR,
and appends measurement into Event Log.

Acked-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>
Tested-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>
Signed-off-by: Masahisa Kojima <masahisa.koj...@linaro.org>
---

Changes in v6:
- update lib/Makefile to add hash-checksum.c as a compilation target

(no changes since v2)

Changes in v2:
- Remove duplicate <efi.h> include
- Remove unnecessary __packed attribute
- Add all EV_EFI_* event definition
- Create common function to prepare 8-byte aligned image
- Add measurement for EV_EFI_BOOT_SERVICES_DRIVER and
  EV_EFI_RUNTIME_SERVICES_DRIVER
- Use efi_search_protocol() to get device_path
- Add function comment

 include/efi_loader.h              |   6 +
 include/efi_tcg2.h                |   9 ++
 include/tpm-v2.h                  |  18 +++
 lib/Makefile                      |   2 +-
 lib/efi_loader/efi_image_loader.c |  59 +++++++--
 lib/efi_loader/efi_tcg2.c         | 207 ++++++++++++++++++++++++++++--
 6 files changed, 276 insertions(+), 25 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index de1a496a97..9f2854a255 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -426,6 +426,10 @@ efi_status_t efi_disk_register(void);
 efi_status_t efi_rng_register(void);
 /* Called by efi_init_obj_list() to install EFI_TCG2_PROTOCOL */
 efi_status_t efi_tcg2_register(void);
+/* measure the pe-coff image, extend PCR and add Event Log */
+efi_status_t tcg2_measure_pe_image(void *efi, u64 efi_size,
+                                  struct efi_loaded_image_obj *handle,
+                                  struct efi_loaded_image *loaded_image_info);
 /* Create handles and protocols for the partitions of a block device */
 int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
                               const char *if_typename, int diskid,
@@ -847,6 +851,8 @@ bool efi_secure_boot_enabled(void);
 
 bool efi_capsule_auth_enabled(void);
 
+void *efi_prepare_aligned_image(void *efi, u64 *efi_size, void **new_efi);
+
 bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
                     WIN_CERTIFICATE **auth, size_t *auth_len);
 
diff --git a/include/efi_tcg2.h b/include/efi_tcg2.h
index 40e241ce31..bcfb98168a 100644
--- a/include/efi_tcg2.h
+++ b/include/efi_tcg2.h
@@ -9,6 +9,7 @@
 #if !defined _EFI_TCG2_PROTOCOL_H_
 #define _EFI_TCG2_PROTOCOL_H_
 
+#include <efi_api.h>
 #include <tpm-v2.h>
 
 #define EFI_TCG2_PROTOCOL_GUID \
@@ -53,6 +54,14 @@ struct efi_tcg2_event {
        u8 event[];
 } __packed;
 
+struct uefi_image_load_event {
+       efi_physical_addr_t image_location_in_memory;
+       u64 image_length_in_memory;
+       u64 image_link_time_address;
+       u64 length_of_device_path;
+       struct efi_device_path device_path[];
+};
+
 struct efi_tcg2_boot_service_capability {
        u8 size;
        struct efi_tcg2_version structure_version;
diff --git a/include/tpm-v2.h b/include/tpm-v2.h
index 7de7d6a57d..247b386967 100644
--- a/include/tpm-v2.h
+++ b/include/tpm-v2.h
@@ -70,6 +70,24 @@ struct udevice;
 #define EV_TABLE_OF_DEVICES            ((u32)0x0000000B)
 #define EV_COMPACT_HASH                        ((u32)0x0000000C)
 
+/*
+ * event types, cf.
+ * "TCG PC Client Platform Firmware Profile Specification", Family "2.0"
+ * rev 1.04, June 3, 2019
+ */
+#define EV_EFI_EVENT_BASE                      ((u32)0x80000000)
+#define EV_EFI_VARIABLE_DRIVER_CONFIG          ((u32)0x80000001)
+#define EV_EFI_VARIABLE_BOOT                   ((u32)0x80000002)
+#define EV_EFI_BOOT_SERVICES_APPLICATION       ((u32)0x80000003)
+#define EV_EFI_BOOT_SERVICES_DRIVER            ((u32)0x80000004)
+#define EV_EFI_RUNTIME_SERVICES_DRIVER         ((u32)0x80000005)
+#define EV_EFI_GPT_EVENT                       ((u32)0x80000006)
+#define EV_EFI_ACTION                          ((u32)0x80000007)
+#define EV_EFI_PLATFORM_FIRMWARE_BLOB          ((u32)0x80000008)
+#define EV_EFI_HANDOFF_TABLES                  ((u32)0x80000009)
+#define EV_EFI_HCRTM_EVENT                     ((u32)0x80000010)
+#define EV_EFI_VARIABLE_AUTHORITY              ((u32)0x800000E0)
+
 /* TPMS_TAGGED_PROPERTY Structure */
 struct tpms_tagged_property {
        u32 property;
diff --git a/lib/Makefile b/lib/Makefile
index bd022dd5d3..ff5c32baed 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -62,7 +62,7 @@ obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi/
 obj-$(CONFIG_$(SPL_)MD5) += md5.o
 obj-$(CONFIG_$(SPL_)RSA) += rsa/
 ifneq (,$(filter y,$(CONFIG_FIT_SIGNATURE)$(CONFIG_EFI_SECURE_BOOT)\
-$(CONFIG_EFI_CAPSULE_AUTHENTICATE)))
+$(CONFIG_EFI_CAPSULE_AUTHENTICATE)$(CONFIG_EFI_TCG2_PROTOCOL)))
 obj-y += hash-checksum.o
 endif
 obj-$(CONFIG_SHA1) += sha1.o
diff --git a/lib/efi_loader/efi_image_loader.c 
b/lib/efi_loader/efi_image_loader.c
index fe1ee198e2..f37a85e56e 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -302,6 +302,40 @@ static int cmp_pe_section(const void *arg1, const void 
*arg2)
                return 1;
 }
 
+/**
+ * efi_prepare_aligned_image() - prepare 8-byte aligned image
+ * @efi:               pointer to the EFI binary
+ * @efi_size:          size of @efi binary
+ * @new_efi:           pointer to the newly allocated image
+ *
+ * If @efi is not 8-byte aligned, this function newly allocates
+ * the image buffer and updates @efi_size.
+ *
+ * Return:     valid pointer to a image, return NULL if allocation fails.
+ */
+void *efi_prepare_aligned_image(void *efi, u64 *efi_size, void **new_efi)
+{
+       size_t new_efi_size;
+       void *p;
+
+       /*
+        * Size must be 8-byte aligned and the trailing bytes must be
+        * zero'ed. Otherwise hash value may be incorrect.
+        */
+       if (!IS_ALIGNED(*efi_size, 8)) {
+               new_efi_size = ALIGN(*efi_size, 8);
+               p = calloc(new_efi_size, 1);
+               if (!p)
+                       return NULL;
+               memcpy(p, efi, *efi_size);
+               *efi_size = new_efi_size;
+               *new_efi = p;
+               return p;
+       } else {
+               return efi;
+       }
+}
+
 /**
  * efi_image_parse() - parse a PE image
  * @efi:       Pointer to image
@@ -561,7 +595,7 @@ static bool efi_image_authenticate(void *efi, size_t 
efi_size)
        struct efi_signature_store *db = NULL, *dbx = NULL;
        void *new_efi = NULL;
        u8 *auth, *wincerts_end;
-       size_t new_efi_size, auth_size;
+       size_t auth_size;
        bool ret = false;
 
        EFI_PRINT("%s: Enter, %d\n", __func__, ret);
@@ -569,19 +603,9 @@ static bool efi_image_authenticate(void *efi, size_t 
efi_size)
        if (!efi_secure_boot_enabled())
                return true;
 
-       /*
-        * Size must be 8-byte aligned and the trailing bytes must be
-        * zero'ed. Otherwise hash value may be incorrect.
-        */
-       if (efi_size & 0x7) {
-               new_efi_size = (efi_size + 0x7) & ~0x7ULL;
-               new_efi = calloc(new_efi_size, 1);
-               if (!new_efi)
-                       return false;
-               memcpy(new_efi, efi, efi_size);
-               efi = new_efi;
-               efi_size = new_efi_size;
-       }
+       efi = efi_prepare_aligned_image(efi, (u64 *)&efi_size, &new_efi);
+       if (!efi)
+               return false;
 
        if (!efi_image_parse(efi, efi_size, &regs, &wincerts,
                             &wincerts_len)) {
@@ -891,6 +915,13 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj 
*handle,
                goto err;
        }
 
+#if CONFIG_IS_ENABLED(EFI_TCG2_PROTOCOL)
+       /* Measure an PE/COFF image */
+       if (tcg2_measure_pe_image(efi, efi_size, handle,
+                                 loaded_image_info))
+               log_err("PE image measurement failed\n");
+#endif
+
        /* Copy PE headers */
        memcpy(efi_reloc, efi,
               sizeof(*dos)
diff --git a/lib/efi_loader/efi_tcg2.c b/lib/efi_loader/efi_tcg2.c
index 4530a47b63..6a89cdede5 100644
--- a/lib/efi_loader/efi_tcg2.c
+++ b/lib/efi_loader/efi_tcg2.c
@@ -13,8 +13,10 @@
 #include <efi_loader.h>
 #include <efi_tcg2.h>
 #include <log.h>
+#include <malloc.h>
 #include <version.h>
 #include <tpm-v2.h>
+#include <u-boot/rsa.h>
 #include <u-boot/sha1.h>
 #include <u-boot/sha256.h>
 #include <u-boot/sha512.h>
@@ -706,6 +708,183 @@ out:
        return EFI_EXIT(ret);
 }
 
+/**
+ * tcg2_hash_pe_image() - calculate PE/COFF image hash
+ *
+ * @efi:               pointer to the EFI binary
+ * @efi_size:          size of @efi binary
+ * @digest_list:       list of digest algorithms to extend
+ *
+ * Return:     status code
+ */
+static efi_status_t tcg2_hash_pe_image(void *efi, u64 efi_size,
+                                      struct tpml_digest_values *digest_list)
+{
+       WIN_CERTIFICATE *wincerts = NULL;
+       size_t wincerts_len;
+       struct efi_image_regions *regs = NULL;
+       void *new_efi = NULL;
+       u8 hash[TPM2_SHA512_DIGEST_SIZE];
+       efi_status_t ret;
+       u32 active;
+       int i;
+
+       efi = efi_prepare_aligned_image(efi, &efi_size, &new_efi);
+       if (!efi)
+               return EFI_OUT_OF_RESOURCES;
+
+       if (!efi_image_parse(efi, efi_size, &regs, &wincerts,
+                            &wincerts_len)) {
+               log_err("Parsing PE executable image failed\n");
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       ret = __get_active_pcr_banks(&active);
+       if (ret != EFI_SUCCESS) {
+               ret = EFI_DEVICE_ERROR;
+               goto out;
+       }
+
+       digest_list->count = 0;
+       for (i = 0; i < MAX_HASH_COUNT; i++) {
+               u16 hash_alg = hash_algo_list[i].hash_alg;
+
+               if (!(active & alg_to_mask(hash_alg)))
+                       continue;
+               switch (hash_alg) {
+               case TPM2_ALG_SHA1:
+                       hash_calculate("sha1", regs->reg, regs->num, hash);
+                       break;
+               case TPM2_ALG_SHA256:
+                       hash_calculate("sha256", regs->reg, regs->num, hash);
+                       break;
+               case TPM2_ALG_SHA384:
+                       hash_calculate("sha384", regs->reg, regs->num, hash);
+                       break;
+               case TPM2_ALG_SHA512:
+                       hash_calculate("sha512", regs->reg, regs->num, hash);
+                       break;
+               default:
+                       EFI_PRINT("Unsupported algorithm %x\n", hash_alg);
+                       return EFI_INVALID_PARAMETER;
+               }
+               digest_list->digests[i].hash_alg = hash_alg;
+               memcpy(&digest_list->digests[i].digest, hash, 
(u32)alg_to_len(hash_alg));
+               digest_list->count++;
+       }
+
+out:
+       free(new_efi);
+       free(regs);
+
+       return ret;
+}
+
+/**
+ * tcg2_measure_pe_image() - measure PE/COFF image
+ *
+ * @efi:               pointer to the EFI binary
+ * @efi_size:          size of @efi binary
+ * @handle:            loaded image handle
+ * @loaded_image:      loaded image protocol
+ *
+ * Return:     status code
+ */
+efi_status_t tcg2_measure_pe_image(void *efi, u64 efi_size,
+                                  struct efi_loaded_image_obj *handle,
+                                  struct efi_loaded_image *loaded_image)
+{
+       struct tpml_digest_values digest_list;
+       efi_status_t ret;
+       struct udevice *dev;
+       u32 pcr_index, event_type, event_size;
+       struct uefi_image_load_event *image_load_event;
+       struct efi_device_path *device_path;
+       u32 device_path_length;
+       IMAGE_DOS_HEADER *dos;
+       IMAGE_NT_HEADERS32 *nt;
+       struct efi_handler *handler;
+
+       ret = platform_get_tpm2_device(&dev);
+       if (ret != EFI_SUCCESS)
+               return ret;
+
+       switch (handle->image_type) {
+       case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+               pcr_index = 4;
+               event_type = EV_EFI_BOOT_SERVICES_APPLICATION;
+               break;
+       case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+               pcr_index = 2;
+               event_type = EV_EFI_BOOT_SERVICES_DRIVER;
+               break;
+       case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+               pcr_index = 2;
+               event_type = EV_EFI_RUNTIME_SERVICES_DRIVER;
+               break;
+       default:
+               return EFI_UNSUPPORTED;
+       }
+
+       ret = tcg2_hash_pe_image(efi, efi_size, &digest_list);
+       if (ret != EFI_SUCCESS)
+               return ret;
+
+       ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
+       if (ret != EFI_SUCCESS)
+               return ret;
+
+       ret = EFI_CALL(efi_search_protocol(&handle->header,
+                                          &efi_guid_loaded_image_device_path,
+                                          &handler));
+       if (ret != EFI_SUCCESS)
+               return ret;
+
+       device_path = EFI_CALL(handler->protocol_interface);
+       device_path_length = efi_dp_size(device_path);
+       if (device_path_length > 0) {
+               /* add end node size */
+               device_path_length += sizeof(struct efi_device_path);
+       }
+       event_size = sizeof(struct uefi_image_load_event) + device_path_length;
+       image_load_event = (struct uefi_image_load_event *)malloc(event_size);
+       if (!image_load_event)
+               return EFI_OUT_OF_RESOURCES;
+
+       image_load_event->image_location_in_memory = (efi_physical_addr_t)efi;
+       image_load_event->image_length_in_memory = efi_size;
+       image_load_event->length_of_device_path = device_path_length;
+
+       dos = (IMAGE_DOS_HEADER *)efi;
+       nt = (IMAGE_NT_HEADERS32 *)(efi + dos->e_lfanew);
+       if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+               IMAGE_NT_HEADERS64 *nt64 = (IMAGE_NT_HEADERS64 *)nt;
+
+               image_load_event->image_link_time_address =
+                               nt64->OptionalHeader.ImageBase;
+       } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+               image_load_event->image_link_time_address =
+                               nt->OptionalHeader.ImageBase;
+       } else {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       if (device_path_length > 0) {
+               memcpy(image_load_event->device_path, device_path,
+                      device_path_length);
+       }
+
+       ret = tcg2_agile_log_append(pcr_index, event_type, &digest_list,
+                                   event_size, (u8 *)image_load_event);
+
+out:
+       free(image_load_event);
+
+       return ret;
+}
+
 /**
  * efi_tcg2_hash_log_extend_event() - extend and optionally log events
  *
@@ -758,24 +937,32 @@ efi_tcg2_hash_log_extend_event(struct efi_tcg2_protocol 
*this, u64 flags,
        /*
         * if PE_COFF_IMAGE is set we need to make sure the image is not
         * corrupted, verify it and hash the PE/COFF image in accordance with
-        * the  procedure  specified  in  "Calculating  the  PE  Image  Hash"
-        * section  of the "Windows Authenticode Portable Executable Signature
+        * the procedure specified in "Calculating the PE Image Hash"
+        * section of the "Windows Authenticode Portable Executable Signature
         * Format"
-        * Not supported for now
         */
        if (flags & PE_COFF_IMAGE) {
-               ret = EFI_UNSUPPORTED;
-               goto out;
-       }
+               IMAGE_NT_HEADERS32 *nt;
 
-       pcr_index = efi_tcg_event->header.pcr_index;
-       event_type = efi_tcg_event->header.event_type;
+               ret = efi_check_pe((void *)data_to_hash, data_to_hash_len,
+                                  (void **)&nt);
+               if (ret != EFI_SUCCESS) {
+                       log_err("Not a valid PE-COFF file\n");
+                       goto out;
+               }
 
-       ret = tcg2_create_digest((u8 *)data_to_hash, data_to_hash_len,
-                                &digest_list);
+               ret = tcg2_hash_pe_image((void *)data_to_hash, data_to_hash_len,
+                                        &digest_list);
+       } else {
+               ret = tcg2_create_digest((u8 *)data_to_hash, data_to_hash_len,
+                                        &digest_list);
+       }
        if (ret != EFI_SUCCESS)
                goto out;
 
+       pcr_index = efi_tcg_event->header.pcr_index;
+       event_type = efi_tcg_event->header.event_type;
+
        ret = tcg2_pcr_extend(dev, pcr_index, &digest_list);
        if (ret != EFI_SUCCESS)
                goto out;
-- 
2.17.1

Reply via email to