From 01fa922bbbac378902ef85e522724dd7c7a10a8b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 22 Jul 2020 06:29:38 +0200 Subject: [PATCH 01/16] efi_loader: efi_current_var after SetVirtualAddressMap Variable efi_current_var is a pointer to a physical memory address that becomes invalid after SetVirtualAddressMap(). Instead of converting it via ConvertPointer() simply set it to NULL. Fixes: b02a707152dc ("efi_loader: enable UEFI variables at runtime") Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_var_mem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c index 7a2dba7dc26..856e5e1d56f 100644 --- a/lib/efi_loader/efi_var_mem.c +++ b/lib/efi_loader/efi_var_mem.c @@ -231,6 +231,7 @@ static void EFIAPI __efi_runtime efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context) { efi_convert_pointer(0, (void **)&efi_var_buf); + efi_current_var = NULL; } efi_status_t efi_var_mem_init(void) From ebbad02c1b7140f7e9b479586d58aeca03f5350d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 22 Jul 2020 07:56:14 +0200 Subject: [PATCH 02/16] efi_loader: don't use memmove() in efi_var_mem_del() efi_var_mem_del() is in __efi_runtime because it would be needed for a runtime implementation of SetVariable(). memmove() is not in __efi_runtime. So we should not use it in efi_var_mem_del(). Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_runtime.c | 2 ++ lib/efi_loader/efi_var_mem.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 91a45514488..78fd8014d90 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -144,6 +144,8 @@ efi_status_t efi_init_runtime_supported(void) * * At runtime memcpy() is not available. * + * Overlapping memory areas can be copied safely if src >= dest. + * * @dest: destination buffer * @src: source buffer * @n: number of bytes to copy diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c index 856e5e1d56f..bfa8a56a8f6 100644 --- a/lib/efi_loader/efi_var_mem.c +++ b/lib/efi_loader/efi_var_mem.c @@ -120,7 +120,8 @@ void __efi_runtime efi_var_mem_del(struct efi_var_entry *var) ALIGN((uintptr_t)data + var->length, 8); efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var; - memmove(var, next, (uintptr_t)last - (uintptr_t)next); + /* efi_memcpy_runtime() can be used because next >= var. */ + efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next); efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var, efi_var_buf->length - sizeof(struct efi_var_file)); From 5d1f79ba438dc372c9bddb729d630abbc6e1068b Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Fri, 17 Jul 2020 07:55:03 +0300 Subject: [PATCH 03/16] efi_loader: Rename and correct values for ARM_SMC_MM_* Instead of adding the definition for the specific MM SVC used in StandAloneMM we added the one used in the standard SMC calls. So change the value from -4 to -5 to match the correct one defined in EDK2 and rename them to avoid future confusion Fixes 23a397d2e2fb: ("efi_loader: Add headers for EDK2 StandAloneMM communication") Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- include/mm_communication.h | 14 +++++++------- lib/efi_loader/efi_variable_tee.c | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/mm_communication.h b/include/mm_communication.h index f9c05bb7f10..e464cbb48e2 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -52,14 +52,14 @@ struct efi_mm_communicate_header { #define MM_COMMUNICATE_HEADER_SIZE \ (sizeof(struct efi_mm_communicate_header)) -/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmStdSmc.h */ +/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */ -/* MM return error codes */ -#define ARM_SMC_MM_RET_SUCCESS 0 -#define ARM_SMC_MM_RET_NOT_SUPPORTED -1 -#define ARM_SMC_MM_RET_INVALID_PARAMS -2 -#define ARM_SMC_MM_RET_DENIED -3 -#define ARM_SMC_MM_RET_NO_MEMORY -4 +/* SPM return error codes */ +#define ARM_SVC_SPM_RET_SUCCESS 0 +#define ARM_SVC_SPM_RET_NOT_SUPPORTED -1 +#define ARM_SVC_SPM_RET_INVALID_PARAMS -2 +#define ARM_SVC_SPM_RET_DENIED -3 +#define ARM_SVC_SPM_RET_NO_MEMORY -5 /* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */ diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index c0423489388..5f4aae60bf2 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -106,19 +106,19 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) tee_close_session(conn.tee, conn.session); switch (param[1].u.value.a) { - case ARM_SMC_MM_RET_SUCCESS: + case ARM_SVC_SPM_RET_SUCCESS: ret = EFI_SUCCESS; break; - case ARM_SMC_MM_RET_INVALID_PARAMS: + case ARM_SVC_SPM_RET_INVALID_PARAMS: ret = EFI_INVALID_PARAMETER; break; - case ARM_SMC_MM_RET_DENIED: + case ARM_SVC_SPM_RET_DENIED: ret = EFI_ACCESS_DENIED; break; - case ARM_SMC_MM_RET_NO_MEMORY: + case ARM_SVC_SPM_RET_NO_MEMORY: ret = EFI_OUT_OF_RESOURCES; break; From 9b87d4429c145ebb66895c7e053e8d53192180e2 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Wed, 22 Jul 2020 10:32:22 +0300 Subject: [PATCH 04/16] efi_loader: Check for the native OP-TEE result on mm_communicate calls Currently we only check for the return value of tee_invoke_func(). Although OP-TEE and StMM will correctly set param[1].u.value.a and we'll eventually return an error, the correct thing to do is check for the OP_TEE return code as well. So let's check for that and move tee_shm_free() and tee_close_session() before exiting with an error to make sure we always clear the registered memory. Fixes: f042e47e8fb43 ("efi_loader: Implement EFI variable handling via OP-TEE") Signed-off-by: Ilias Apalodimas Use EFI_DEVICE_ERROR for TEE communication problems. Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_variable_tee.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index 5f4aae60bf2..94c4de87034 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -100,10 +100,10 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) param[1].attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT; rc = tee_invoke_func(conn.tee, &arg, 2, param); - if (rc) - return EFI_INVALID_PARAMETER; tee_shm_free(shm); tee_close_session(conn.tee, conn.session); + if (rc || arg.ret != TEE_SUCCESS) + return EFI_DEVICE_ERROR; switch (param[1].u.value.a) { case ARM_SVC_SPM_RET_SUCCESS: From 1ef1cf1f93345cbeb5ff52cbf00c6ee6fee1a47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20S=C3=B8rensen?= Date: Wed, 22 Jul 2020 09:43:31 +0200 Subject: [PATCH 05/16] efi_loader: loosen buffer parameter check in efi_file_read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reading a directory, EFI_BUFFER_TOO_SMALL should be returned when the supplied buffer is too small, so a use-case is to call efi_file_read with *buffer_size=0 and buffer=NULL to obtain the needed size before doing the actual read. So move the buffer!=NULL check to after the buffer size has been checked. This fix allows the Redhat shim fallback to run and e.g. Fedora 32 now boots out of the box. Signed-off-by: Stefan Sørensen Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_file.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 19afa69f530..44fafae0586 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -349,6 +349,11 @@ static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, efi_status_t ret; loff_t file_size; + if (!buffer) { + ret = EFI_INVALID_PARAMETER; + return ret; + } + ret = efi_get_file_size(fh, &file_size); if (ret != EFI_SUCCESS) return ret; @@ -414,6 +419,8 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size, fh->dent = dent; return EFI_BUFFER_TOO_SMALL; } + if (!buffer) + return EFI_INVALID_PARAMETER; fh->dent = NULL; *buffer_size = required_size; @@ -443,7 +450,7 @@ static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file, EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); - if (!buffer_size || !buffer) { + if (!buffer_size) { ret = EFI_INVALID_PARAMETER; goto error; } From e7d64065cb4b74357e50b54e54adc780e2f44db1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 18 Jul 2020 09:53:01 +0200 Subject: [PATCH 06/16] efi_loader: document efi_save_gd(), efi_restore_gd() Provide function descriptions for efi_save_gd() and efi_restore_gd(). Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 0b16554ba23..d49145fc76b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -104,7 +104,15 @@ int __efi_exit_check(void) return ret; } -/* Called from do_bootefi_exec() */ +/** + * efi_save_gd() - save global data register + * + * On the ARM architecture gd is mapped to a fixed register (r9 or x18). + * As this register may be overwritten by an EFI payload we save it here + * and restore it on every callback entered. + * + * This function is called after relocation from initr_reloc_global_data(). + */ void efi_save_gd(void) { #ifdef CONFIG_ARM @@ -112,10 +120,12 @@ void efi_save_gd(void) #endif } -/* - * Special case handler for error/abort that just forces things back to u-boot - * world so we can dump out an abort message, without any care about returning - * back to UEFI world. +/** + * efi_restore_gd() - restore global data register + * + * On the ARM architecture gd is mapped to a fixed register (r9 or x18). + * Restore it after returning from the UEFI world to the value saved via + * efi_save_gd(). */ void efi_restore_gd(void) { From bf758125d826a5c09828b54c3d866490346f033c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 18 Jul 2020 10:54:26 +0200 Subject: [PATCH 07/16] efi_loader: returning from UEFI FIT images Do not reset the board when returning from an UEFI FIT image. For failed UEFI binary we already print the return status in efi_run_image. Remove duplicate output. Signed-off-by: Heinrich Schuchardt --- common/bootm_os.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/bootm_os.c b/common/bootm_os.c index 6a95e0de338..e9aaddf3e61 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -542,15 +542,14 @@ static int do_bootm_efi(int flag, int argc, char *const argv[], images->ep); bootstage_mark(BOOTSTAGE_ID_RUN_OS); + /* We expect to return */ + images->os.type = IH_TYPE_STANDALONE; + image_buf = map_sysmem(images->ep, images->os.image_len); efi_ret = efi_run_image(image_buf, images->os.image_len); - if (efi_ret != EFI_SUCCESS) { - printf("## Failed to run EFI image: r = %lu\n", - efi_ret & ~EFI_ERROR_MASK); + if (efi_ret != EFI_SUCCESS) return 1; - } - return 0; } #endif From c00183740097124502151409a6680522efc61e1e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 17 Jul 2020 20:21:00 +0200 Subject: [PATCH 08/16] efi_loader: use logging for bootefi command Log messages of the bootefi command instead of simply printing them to the console. Do not show "## Application terminated" message when the UEFI binary completed successfully. Adjust the python tests testing for '## Application terminated'. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 42 ++++++++++++++++++-------------- test/py/tests/test_efi_fit.py | 9 +++---- test/py/tests/test_efi_loader.py | 9 +++---- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 57552f99fcf..8154efde52a 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -5,6 +5,8 @@ * Copyright (c) 2016 Alexander Graf */ +#define LOG_CATEGORY LOGC_EFI + #include #include #include @@ -14,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +65,7 @@ static efi_status_t set_load_options(efi_handle_t handle, const char *env_var, size = utf8_utf16_strlen(env) + 1; loaded_image_info->load_options = calloc(size, sizeof(u16)); if (!loaded_image_info->load_options) { - printf("ERROR: Out of memory\n"); + log_err("ERROR: Out of memory\n"); EFI_CALL(systab.boottime->close_protocol(handle, &efi_guid_loaded_image, efi_root, NULL)); @@ -137,7 +140,7 @@ static efi_status_t copy_fdt(void **fdtp) EFI_ACPI_RECLAIM_MEMORY, fdt_pages, &new_fdt_addr); if (ret != EFI_SUCCESS) { - printf("ERROR: Failed to reserve space for FDT\n"); + log_err("ERROR: Failed to reserve space for FDT\n"); goto done; } } @@ -156,8 +159,8 @@ static void efi_reserve_memory(u64 addr, u64 size) addr = (uintptr_t)map_sysmem(addr, 0); if (efi_add_memory_map(addr, size, EFI_RESERVED_MEMORY_TYPE) != EFI_SUCCESS) - printf("Reserved memory mapping failed addr %llx size %llx\n", - addr, size); + log_err("Reserved memory mapping failed addr %llx size %llx\n", + addr, size); } /** @@ -252,7 +255,7 @@ efi_status_t efi_install_fdt(void *fdt) */ #if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) if (fdt) { - printf("ERROR: can't have ACPI table and device tree.\n"); + log_err("ERROR: can't have ACPI table and device tree.\n"); return EFI_LOAD_ERROR; } #else @@ -272,13 +275,13 @@ efi_status_t efi_install_fdt(void *fdt) if (!fdt_opt) { fdt_opt = env_get("fdtcontroladdr"); if (!fdt_opt) { - printf("ERROR: need device tree\n"); + log_err("ERROR: need device tree\n"); return EFI_NOT_FOUND; } } fdt_addr = simple_strtoul(fdt_opt, NULL, 16); if (!fdt_addr) { - printf("ERROR: invalid $fdt_addr or $fdtcontroladdr\n"); + log_err("ERROR: invalid $fdt_addr or $fdtcontroladdr\n"); return EFI_LOAD_ERROR; } fdt = map_sysmem(fdt_addr, 0); @@ -286,19 +289,19 @@ efi_status_t efi_install_fdt(void *fdt) /* Install device tree */ if (fdt_check_header(fdt)) { - printf("ERROR: invalid device tree\n"); + log_err("ERROR: invalid device tree\n"); return EFI_LOAD_ERROR; } /* Prepare device tree for payload */ ret = copy_fdt(&fdt); if (ret) { - printf("ERROR: out of memory\n"); + log_err("ERROR: out of memory\n"); return EFI_OUT_OF_RESOURCES; } if (image_setup_libfdt(&img, fdt, 0, NULL)) { - printf("ERROR: failed to process device tree\n"); + log_err("ERROR: failed to process device tree\n"); return EFI_LOAD_ERROR; } @@ -308,7 +311,7 @@ efi_status_t efi_install_fdt(void *fdt) /* Install device tree as UEFI table */ ret = efi_install_configuration_table(&efi_guid_fdt, fdt); if (ret != EFI_SUCCESS) { - printf("ERROR: failed to install device tree\n"); + log_err("ERROR: failed to install device tree\n"); return ret; } #endif /* GENERATE_ACPI_TABLE */ @@ -339,10 +342,13 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle) /* Call our payload! */ ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data)); - printf("## Application terminated, r = %lu\n", ret & ~EFI_ERROR_MASK); - if (ret && exit_data) { - printf("## %ls\n", exit_data); - efi_free_pool(exit_data); + if (ret != EFI_SUCCESS) { + log_err("## Application failed, r = %lu\n", + ret & ~EFI_ERROR_MASK); + if (exit_data) { + log_err("## %ls\n", exit_data); + efi_free_pool(exit_data); + } } efi_restore_gd(); @@ -364,7 +370,7 @@ static int do_efibootmgr(void) ret = efi_bootmgr_load(&handle); if (ret != EFI_SUCCESS) { - printf("EFI boot manager: Cannot load any image\n"); + log_notice("EFI boot manager: Cannot load any image\n"); return CMD_RET_FAILURE; } @@ -611,8 +617,8 @@ static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc, /* Initialize EFI drivers */ ret = efi_init_obj_list(); if (ret != EFI_SUCCESS) { - printf("Error: Cannot initialize UEFI sub-system, r = %lu\n", - ret & ~EFI_ERROR_MASK); + log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); return CMD_RET_FAILURE; } diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py index 06fb151c138..068a35a559d 100644 --- a/test/py/tests/test_efi_fit.py +++ b/test/py/tests/test_efi_fit.py @@ -420,12 +420,11 @@ def test_efi_fit_launch(u_boot_console): fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt' # Try booting. - cons.run_command( - 'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False) + output = cons.run_command('bootm %x#%s' % (addr, fit_config)) if enable_fdt: - cons.wait_for('Booting using the fdt blob') - cons.wait_for('Hello, world') - cons.wait_for('## Application terminated, r = 0') + assert 'Booting using the fdt blob' in output + assert 'Hello, world' in output + assert '## Application failed' not in output cons.restart_uboot() cons = u_boot_console diff --git a/test/py/tests/test_efi_loader.py b/test/py/tests/test_efi_loader.py index 7aa422e7645..ca68626cec7 100644 --- a/test/py/tests/test_efi_loader.py +++ b/test/py/tests/test_efi_loader.py @@ -161,8 +161,8 @@ def test_efi_helloworld_net(u_boot_console): output = u_boot_console.run_command('bootefi %x' % addr) expected_text = 'Hello, world' assert expected_text in output - expected_text = '## Application terminated, r = 0' - assert expected_text in output + expected_text = '## Application failed' + assert expected_text not in output @pytest.mark.buildconfigspec('cmd_bootefi_hello') def test_efi_helloworld_builtin(u_boot_console): @@ -198,8 +198,7 @@ def test_efi_grub_net(u_boot_console): # Then exit cleanly u_boot_console.wait_for('grub>') - output = u_boot_console.run_command('exit', wait_for_prompt=False, wait_for_echo=False) - u_boot_console.wait_for('r = 0') - + u_boot_console.run_command('exit', wait_for_prompt=False, wait_for_echo=False) + u_boot_console.wait_for('=>') # And give us our U-Boot prompt back u_boot_console.run_command('') From af457cfca9ddabc9971a5fa4e748633be42c8a9d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 17 Jul 2020 20:33:05 +0200 Subject: [PATCH 09/16] efi_loader: use logging for block device messages Use logging instead of printf() for messages occurring when scanning block devices during the initialization of the UEFI sub-system. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_disk.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 670bf2b8ef0..7bd1ccec450 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -5,11 +5,14 @@ * Copyright (c) 2016 Alexander Graf */ +#define LOG_CATEGORY LOGC_EFI + #include #include #include #include #include +#include #include #include @@ -490,7 +493,7 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, ret = efi_disk_add_dev(parent, dp, if_typename, desc, diskid, info.start, part, NULL); if (ret != EFI_SUCCESS) { - printf("Adding partition %s failed\n", pdevname); + log_err("Adding partition %s failed\n", pdevname); continue; } disks++; @@ -528,16 +531,16 @@ efi_status_t efi_disk_register(void) const char *if_typename = blk_get_if_type_name(desc->if_type); /* Add block device for the full device */ - printf("Scanning disk %s...\n", dev->name); + log_info("Scanning disk %s...\n", dev->name); ret = efi_disk_add_dev(NULL, NULL, if_typename, desc, desc->devnum, 0, 0, &disk); if (ret == EFI_NOT_READY) { - printf("Disk %s not ready\n", dev->name); + log_notice("Disk %s not ready\n", dev->name); continue; } if (ret) { - printf("ERROR: failure to add disk device %s, r = %lu\n", - dev->name, ret & ~EFI_ERROR_MASK); + log_err("ERROR: failure to add disk device %s, r = %lu\n", + dev->name, ret & ~EFI_ERROR_MASK); return ret; } disks++; @@ -560,7 +563,7 @@ efi_status_t efi_disk_register(void) continue; if_typename = cur_drvr->if_typename; - printf("Scanning disks on %s...\n", if_typename); + log_info("Scanning disks on %s...\n", if_typename); for (i = 0; i < 4; i++) { struct blk_desc *desc; char devname[32] = { 0 }; /* dp->str is u16[32] long */ @@ -578,12 +581,12 @@ efi_status_t efi_disk_register(void) ret = efi_disk_add_dev(NULL, NULL, if_typename, desc, i, 0, 0, &disk); if (ret == EFI_NOT_READY) { - printf("Disk %s not ready\n", devname); + log_notice("Disk %s not ready\n", devname); continue; } if (ret) { - printf("ERROR: failure to add disk device %s, r = %lu\n", - devname, ret & ~EFI_ERROR_MASK); + log_err("ERROR: failure to add disk device %s, r = %lu\n", + devname, ret & ~EFI_ERROR_MASK); return ret; } disks++; @@ -595,7 +598,7 @@ efi_status_t efi_disk_register(void) } } #endif - printf("Found %d disks\n", disks); + log_info("Found %d disks\n", disks); return EFI_SUCCESS; } From d09745b1967708bffbbc5ba466753df638000d40 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Mon, 20 Jul 2020 15:34:09 +0900 Subject: [PATCH 10/16] test/py: efi_secboot: remove unused function 'tool_is_in_path' function is no longer used anywhere after Heinrich has removed 'sudo' version of fixture setup. Signed-off-by: AKASHI Takahiro Signed-off-by: Heinrich Schuchardt --- test/py/tests/test_efi_secboot/conftest.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py index c6709700a87..4073732da60 100644 --- a/test/py/tests/test_efi_secboot/conftest.py +++ b/test/py/tests/test_efi_secboot/conftest.py @@ -8,15 +8,6 @@ from subprocess import call, check_call, check_output, CalledProcessError import pytest from defs import * -# from test/py/conftest.py - - -def tool_is_in_path(tool): - for path in os.environ["PATH"].split(os.pathsep): - full_path = os.path.join(path, tool) - if os.path.isfile(full_path) and os.access(full_path, os.X_OK): - return True - return False # # Fixture for UEFI secure boot test From a58dfd29698c65a22e3956e7aae96c7ce7fdddd3 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Mon, 20 Jul 2020 15:33:39 +0900 Subject: [PATCH 11/16] test/py: efi_secboot: fix additional pylint errors This is a fixup by autopep8 after the commit ("test/py: efi_secboot: apply autopep8"). Signed-off-by: AKASHI Takahiro Signed-off-by: Heinrich Schuchardt --- test/py/tests/test_efi_secboot/conftest.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py index 4073732da60..c0943b62501 100644 --- a/test/py/tests/test_efi_secboot/conftest.py +++ b/test/py/tests/test_efi_secboot/conftest.py @@ -78,21 +78,21 @@ def efi_boot_env(request, u_boot_config): # db1-update check_call('cd %s; %ssign-efi-sig-list -t "2020-04-06" -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth' % (mnt_point, EFITOOLS_PATH), shell=True) - ## dbx (TEST_dbx certificate) + # dbx (TEST_dbx certificate) check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365' % mnt_point, shell=True) check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth' % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), shell=True) - ## dbx_hash (digest of TEST_db certificate) + # dbx_hash (digest of TEST_db certificate) check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db.crt dbx_hash.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash.crl dbx_hash.auth' % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), shell=True) - ## dbx_hash1 (digest of TEST_db1 certificate) + # dbx_hash1 (digest of TEST_db1 certificate) check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db1.crt dbx_hash1.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash1.crl dbx_hash1.auth' % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), shell=True) - ## dbx_db (with TEST_db certificate) + # dbx_db (with TEST_db certificate) check_call('cd %s; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx db.esl dbx_db.auth' % (mnt_point, EFITOOLS_PATH), shell=True) @@ -103,10 +103,10 @@ def efi_boot_env(request, u_boot_config): # Sign image check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi' % mnt_point, shell=True) - ## Sign already-signed image with another key + # Sign already-signed image with another key check_call('cd %s; sbsign --key db1.key --cert db1.crt --output helloworld.efi.signed_2sigs helloworld.efi.signed' % mnt_point, shell=True) - ## Digest image + # Digest image check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth' % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH), shell=True) @@ -117,7 +117,8 @@ def efi_boot_env(request, u_boot_config): % (mnt_point, EFITOOLS_PATH), shell=True) - check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(mnt_point, image_path), shell=True) + check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format( + mnt_point, image_path), shell=True) check_call('rm -rf {}'.format(mnt_point), shell=True) except CalledProcessError as exception: From b2a1049b5c364961726add2796b6028b27008ca4 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 21 Jul 2020 19:35:17 +0900 Subject: [PATCH 12/16] lib: crypto: add public_key_verify_signature() This function will be called from x509_check_for_self_signed() and pkcs7_verify_one(), which will be imported from linux in a later patch. While it does exist in linux code and has a similar functionality of rsa_verify(), it calls further linux-specific interfaces inside. That could lead to more files being imported from linux. So simply re-implement it here instead of re-using the code. Signed-off-by: AKASHI Takahiro --- include/crypto/public_key.h | 2 +- lib/crypto/public_key.c | 70 ++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 436a1ee1ee6..3ba90fcc348 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -82,9 +82,9 @@ extern int decrypt_blob(struct kernel_pkey_params *, const void *, void *); extern int create_signature(struct kernel_pkey_params *, const void *, void *); extern int verify_signature(const struct key *, const struct public_key_signature *); +#endif /* __UBOOT__ */ int public_key_verify_signature(const struct public_key *pkey, const struct public_key_signature *sig); -#endif /* !__UBOOT__ */ #endif /* _LINUX_PUBLIC_KEY_H */ diff --git a/lib/crypto/public_key.c b/lib/crypto/public_key.c index e12ebbb3d0c..a8f7fbed458 100644 --- a/lib/crypto/public_key.c +++ b/lib/crypto/public_key.c @@ -25,7 +25,10 @@ #include #endif #include -#ifndef __UBOOT__ +#ifdef __UBOOT__ +#include +#include +#else #include #endif @@ -80,6 +83,71 @@ void public_key_signature_free(struct public_key_signature *sig) } EXPORT_SYMBOL_GPL(public_key_signature_free); +/** + * public_key_verify_signature - Verify a signature using a public key. + * + * @pkey: Public key + * @sig: Signature + * + * Verify a signature, @sig, using a RSA public key, @pkey. + * + * Return: 0 - verified, non-zero error code - otherwise + */ +int public_key_verify_signature(const struct public_key *pkey, + const struct public_key_signature *sig) +{ + struct image_sign_info info; + struct image_region region; + int ret; + + pr_devel("==>%s()\n", __func__); + + if (!pkey || !sig) + return -EINVAL; + + if (pkey->key_is_private) + return -EINVAL; + + memset(&info, '\0', sizeof(info)); + info.padding = image_get_padding_algo("pkcs-1.5"); + /* + * Note: image_get_[checksum|crypto]_algo takes a string + * argument like "," + * TODO: support other hash algorithms + */ + if (strcmp(sig->pkey_algo, "rsa") || (sig->s_size * 8) != 2048) { + pr_warn("Encryption is not RSA2048: %s%d\n", + sig->pkey_algo, sig->s_size * 8); + return -ENOPKG; + } + if (!strcmp(sig->hash_algo, "sha1")) { + info.checksum = image_get_checksum_algo("sha1,rsa2048"); + info.name = "sha1,rsa2048"; + } else if (!strcmp(sig->hash_algo, "sha256")) { + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.name = "sha256,rsa2048"; + } else { + pr_warn("unknown msg digest algo: %s\n", sig->hash_algo); + return -ENOPKG; + } + info.crypto = image_get_crypto_algo(info.name); + if (IS_ERR(info.checksum) || IS_ERR(info.crypto)) + return -ENOPKG; + + info.key = pkey->key; + info.keylen = pkey->keylen; + + region.data = sig->digest; + region.size = sig->digest_size; + + if (rsa_verify_with_pkey(&info, sig->digest, sig->s, sig->s_size)) + ret = -EKEYREJECTED; + else + ret = 0; + + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} #else /* * Destroy a public key algorithm key. From 6244b3c7d947ca6465426f18922135595ce9cd44 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 21 Jul 2020 19:35:18 +0900 Subject: [PATCH 13/16] lib: crypto: enable x509_check_for_self_signed() When the file, x509_public_key.c, was imported from linux code in commit b4adf627d5b7 ("lib: crypto: add x509 parser"), x509_check_for_self_signed() was commented out for simplicity. Now it need be enabled in order to make pkcs7_verify_one(), which will be imported in a later patch, functional. Signed-off-by: AKASHI Takahiro --- lib/crypto/x509_cert_parser.c | 2 -- lib/crypto/x509_public_key.c | 33 +++++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/crypto/x509_cert_parser.c b/lib/crypto/x509_cert_parser.c index 5f984b9dfda..eb24349460c 100644 --- a/lib/crypto/x509_cert_parser.c +++ b/lib/crypto/x509_cert_parser.c @@ -142,12 +142,10 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) } cert->id = kid; -#ifndef __UBOOT__ /* Detect self-signed certificates */ ret = x509_check_for_self_signed(cert); if (ret < 0) goto error_decode; -#endif kfree(ctx); return cert; diff --git a/lib/crypto/x509_public_key.c b/lib/crypto/x509_public_key.c index 571af9a0adf..91810a86404 100644 --- a/lib/crypto/x509_public_key.c +++ b/lib/crypto/x509_public_key.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "X.509: "fmt #ifdef __UBOOT__ #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #ifdef __UBOOT__ #include +#include #else #include #include @@ -35,7 +37,9 @@ int x509_get_sig_params(struct x509_certificate *cert) { struct public_key_signature *sig = cert->sig; -#ifndef __UBOOT__ +#ifdef __UBOOT__ + struct image_region region; +#else struct crypto_shash *tfm; struct shash_desc *desc; size_t desc_size; @@ -63,12 +67,25 @@ int x509_get_sig_params(struct x509_certificate *cert) sig->s_size = cert->raw_sig_size; #ifdef __UBOOT__ - /* - * Note: - * This part (filling sig->digest) should be implemented if - * x509_check_for_self_signed() is enabled x509_cert_parse(). - * Currently, this check won't affect UEFI secure boot. - */ + if (!sig->hash_algo) + return -ENOPKG; + if (!strcmp(sig->hash_algo, "sha256")) + sig->digest_size = SHA256_SUM_LEN; + else if (!strcmp(sig->hash_algo, "sha1")) + sig->digest_size = SHA1_SUM_LEN; + else + return -ENOPKG; + + sig->digest = calloc(1, sig->digest_size); + if (!sig->digest) + return -ENOMEM; + + region.data = cert->tbs; + region.size = cert->tbs_size; + hash_calculate(sig->hash_algo, ®ion, 1, sig->digest); + + /* TODO: is_hash_blacklisted()? */ + ret = 0; #else /* Allocate the hashing algorithm we're going to need and find out how @@ -118,7 +135,6 @@ error: return ret; } -#ifndef __UBOOT__ /* * Check for self-signedness in an X.509 cert and if found, check the signature * immediately if we can. @@ -175,6 +191,7 @@ not_self_signed: return 0; } +#ifndef __UBOOT__ /* * Attempt to parse a data blob for a key as an X509 certificate. */ From 063499e38e41bd23563fb6f98438ddd1ce0f7e6a Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 21 Jul 2020 19:35:19 +0900 Subject: [PATCH 14/16] lib: crypto: import pkcs7_verify.c from linux The file, pkcs7_verify.c, will now be imported from linux code (crypto/asymmetric_keys/pkcs7_verify.c in 5.7) and modified to fit into U-Boot environment. In particular, pkcs7_verify_one() function will be used in a later patch to rework signature verification logic aiming to support intermediate certificates in "chain of trust." Signed-off-by: AKASHI Takahiro --- lib/crypto/Kconfig | 3 + lib/crypto/Makefile | 1 + lib/crypto/pkcs7_verify.c | 524 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 528 insertions(+) create mode 100644 lib/crypto/pkcs7_verify.c diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index 2b221b915aa..6369bafac07 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -49,4 +49,7 @@ config PKCS7_MESSAGE_PARSER This option provides support for parsing PKCS#7 format messages for signature data and provides the ability to verify the signature. +config PKCS7_VERIFY + bool + endif # ASYMMETRIC_KEY_TYPE diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile index 8267fee0a7b..f3a414525d2 100644 --- a/lib/crypto/Makefile +++ b/lib/crypto/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o pkcs7_message-y := \ pkcs7.asn1.o \ pkcs7_parser.o +obj-$(CONFIG_PKCS7_VERIFY) += pkcs7_verify.o $(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h $(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h diff --git a/lib/crypto/pkcs7_verify.c b/lib/crypto/pkcs7_verify.c new file mode 100644 index 00000000000..192e39bae90 --- /dev/null +++ b/lib/crypto/pkcs7_verify.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Verify the signature on a PKCS#7 message. + * + * Imported from crypto/asymmetric_keys/pkcs7_verify.c of linux 5.7 + * with modification marked as __UBOOT__. + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "PKCS7: "fmt +#ifdef __UBOOT__ +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include "pkcs7_parser.h" +#endif + +/* + * Digest the relevant parts of the PKCS#7 data + */ +#ifdef __UBOOT__ +static int pkcs7_digest(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + return 0; +} +#else +static int pkcs7_digest(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + struct public_key_signature *sig = sinfo->sig; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t desc_size; + int ret; + + kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo); + + /* The digest was calculated already. */ + if (sig->digest) + return 0; + + if (!sinfo->sig->hash_algo) + return -ENOPKG; + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + sig->digest_size = crypto_shash_digestsize(tfm); + + ret = -ENOMEM; + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) + goto error_no_desc; + + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) + goto error_no_desc; + + desc->tfm = tfm; + + /* Digest the message [RFC2315 9.3] */ + ret = crypto_shash_digest(desc, pkcs7->data, pkcs7->data_len, + sig->digest); + if (ret < 0) + goto error; + pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest); + + /* However, if there are authenticated attributes, there must be a + * message digest attribute amongst them which corresponds to the + * digest we just calculated. + */ + if (sinfo->authattrs) { + u8 tag; + + if (!sinfo->msgdigest) { + pr_warn("Sig %u: No messageDigest\n", sinfo->index); + ret = -EKEYREJECTED; + goto error; + } + + if (sinfo->msgdigest_len != sig->digest_size) { + pr_debug("Sig %u: Invalid digest size (%u)\n", + sinfo->index, sinfo->msgdigest_len); + ret = -EBADMSG; + goto error; + } + + if (memcmp(sig->digest, sinfo->msgdigest, + sinfo->msgdigest_len) != 0) { + pr_debug("Sig %u: Message digest doesn't match\n", + sinfo->index); + ret = -EKEYREJECTED; + goto error; + } + + /* We then calculate anew, using the authenticated attributes + * as the contents of the digest instead. Note that we need to + * convert the attributes from a CONT.0 into a SET before we + * hash it. + */ + memset(sig->digest, 0, sig->digest_size); + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + tag = ASN1_CONS_BIT | ASN1_SET; + ret = crypto_shash_update(desc, &tag, 1); + if (ret < 0) + goto error; + ret = crypto_shash_finup(desc, sinfo->authattrs, + sinfo->authattrs_len, sig->digest); + if (ret < 0) + goto error; + pr_devel("AADigest = [%*ph]\n", 8, sig->digest); + } + +error: + kfree(desc); +error_no_desc: + crypto_free_shash(tfm); + kleave(" = %d", ret); + return ret; +} + +int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf, u32 *len, + enum hash_algo *hash_algo) +{ + struct pkcs7_signed_info *sinfo = pkcs7->signed_infos; + int i, ret; + + /* + * This function doesn't support messages with more than one signature. + */ + if (sinfo == NULL || sinfo->next != NULL) + return -EBADMSG; + + ret = pkcs7_digest(pkcs7, sinfo); + if (ret) + return ret; + + *buf = sinfo->sig->digest; + *len = sinfo->sig->digest_size; + + for (i = 0; i < HASH_ALGO__LAST; i++) + if (!strcmp(hash_algo_name[i], sinfo->sig->hash_algo)) { + *hash_algo = i; + break; + } + + return 0; +} +#endif /* !__UBOOT__ */ + +/* + * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 + * uses the issuer's name and the issuing certificate serial number for + * matching purposes. These must match the certificate issuer's name (not + * subject's name) and the certificate serial number [RFC 2315 6.7]. + */ +static int pkcs7_find_key(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + struct x509_certificate *x509; + unsigned certix = 1; + + kenter("%u", sinfo->index); + + for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { + /* I'm _assuming_ that the generator of the PKCS#7 message will + * encode the fields from the X.509 cert in the same way in the + * PKCS#7 message - but I can't be 100% sure of that. It's + * possible this will need element-by-element comparison. + */ + if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0])) + continue; + pr_devel("Sig %u: Found cert serial match X.509[%u]\n", + sinfo->index, certix); + + if (strcmp(x509->pub->pkey_algo, sinfo->sig->pkey_algo) != 0) { + pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", + sinfo->index); + continue; + } + + sinfo->signer = x509; + return 0; + } + + /* The relevant X.509 cert isn't found here, but it might be found in + * the trust keyring. + */ + pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", + sinfo->index, + sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data); + return 0; +} + +/* + * Verify the internal certificate chain as best we can. + */ +static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + struct public_key_signature *sig; + struct x509_certificate *x509 = sinfo->signer, *p; + struct asymmetric_key_id *auth; + int ret; + + kenter(""); + + for (p = pkcs7->certs; p; p = p->next) + p->seen = false; + + for (;;) { + pr_debug("verify %s: %*phN\n", + x509->subject, + x509->raw_serial_size, x509->raw_serial); + x509->seen = true; + + if (x509->blacklisted) { + /* If this cert is blacklisted, then mark everything + * that depends on this as blacklisted too. + */ + sinfo->blacklisted = true; + for (p = sinfo->signer; p != x509; p = p->signer) + p->blacklisted = true; + pr_debug("- blacklisted\n"); + return 0; + } + + if (x509->unsupported_key) + goto unsupported_crypto_in_x509; + + pr_debug("- issuer %s\n", x509->issuer); + sig = x509->sig; + if (sig->auth_ids[0]) + pr_debug("- authkeyid.id %*phN\n", + sig->auth_ids[0]->len, sig->auth_ids[0]->data); + if (sig->auth_ids[1]) + pr_debug("- authkeyid.skid %*phN\n", + sig->auth_ids[1]->len, sig->auth_ids[1]->data); + + if (x509->self_signed) { + /* If there's no authority certificate specified, then + * the certificate must be self-signed and is the root + * of the chain. Likewise if the cert is its own + * authority. + */ + if (x509->unsupported_sig) + goto unsupported_crypto_in_x509; + x509->signer = x509; + pr_debug("- self-signed\n"); + return 0; + } + + /* Look through the X.509 certificates in the PKCS#7 message's + * list to see if the next one is there. + */ + auth = sig->auth_ids[0]; + if (auth) { + pr_debug("- want %*phN\n", auth->len, auth->data); + for (p = pkcs7->certs; p; p = p->next) { + pr_debug("- cmp [%u] %*phN\n", + p->index, p->id->len, p->id->data); + if (asymmetric_key_id_same(p->id, auth)) + goto found_issuer_check_skid; + } + } else if (sig->auth_ids[1]) { + auth = sig->auth_ids[1]; + pr_debug("- want %*phN\n", auth->len, auth->data); + for (p = pkcs7->certs; p; p = p->next) { + if (!p->skid) + continue; + pr_debug("- cmp [%u] %*phN\n", + p->index, p->skid->len, p->skid->data); + if (asymmetric_key_id_same(p->skid, auth)) + goto found_issuer; + } + } + + /* We didn't find the root of this chain */ + pr_debug("- top\n"); + return 0; + + found_issuer_check_skid: + /* We matched issuer + serialNumber, but if there's an + * authKeyId.keyId, that must match the CA subjKeyId also. + */ + if (sig->auth_ids[1] && + !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) { + pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n", + sinfo->index, x509->index, p->index); + return -EKEYREJECTED; + } + found_issuer: + pr_debug("- subject %s\n", p->subject); + if (p->seen) { + pr_warn("Sig %u: X.509 chain contains loop\n", + sinfo->index); + return 0; + } + ret = public_key_verify_signature(p->pub, x509->sig); + if (ret < 0) + return ret; + x509->signer = p; + if (x509 == p) { + pr_debug("- self-signed\n"); + return 0; + } + x509 = p; +#ifndef __UBOOT__ + might_sleep(); +#endif + } + +unsupported_crypto_in_x509: + /* Just prune the certificate chain at this point if we lack some + * crypto module to go further. Note, however, we don't want to set + * sinfo->unsupported_crypto as the signed info block may still be + * validatable against an X.509 cert lower in the chain that we have a + * trusted copy of. + */ + return 0; +} + +/* + * Verify one signed information block from a PKCS#7 message. + */ +#ifndef __UBOOT__ +static +#endif +int pkcs7_verify_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + int ret; + + kenter(",%u", sinfo->index); + + /* First of all, digest the data in the PKCS#7 message and the + * signed information block + */ + ret = pkcs7_digest(pkcs7, sinfo); + if (ret < 0) + return ret; + + /* Find the key for the signature if there is one */ + ret = pkcs7_find_key(pkcs7, sinfo); + if (ret < 0) + return ret; + + if (!sinfo->signer) + return 0; + + pr_devel("Using X.509[%u] for sig %u\n", + sinfo->signer->index, sinfo->index); + + /* Check that the PKCS#7 signing time is valid according to the X.509 + * certificate. We can't, however, check against the system clock + * since that may not have been set yet and may be wrong. + */ + if (test_bit(sinfo_has_signing_time, &sinfo->aa_set)) { + if (sinfo->signing_time < sinfo->signer->valid_from || + sinfo->signing_time > sinfo->signer->valid_to) { + pr_warn("Message signed outside of X.509 validity window\n"); + return -EKEYREJECTED; + } + } + + /* Verify the PKCS#7 binary against the key */ + ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig); + if (ret < 0) + return ret; + + pr_devel("Verified signature %u\n", sinfo->index); + + /* Verify the internal certificate chain */ + return pkcs7_verify_sig_chain(pkcs7, sinfo); +} + +#ifndef __UBOOT__ +/** + * pkcs7_verify - Verify a PKCS#7 message + * @pkcs7: The PKCS#7 message to be verified + * @usage: The use to which the key is being put + * + * Verify a PKCS#7 message is internally consistent - that is, the data digest + * matches the digest in the AuthAttrs and any signature in the message or one + * of the X.509 certificates it carries that matches another X.509 cert in the + * message can be verified. + * + * This does not look to match the contents of the PKCS#7 message against any + * external public keys. + * + * Returns, in order of descending priority: + * + * (*) -EKEYREJECTED if a key was selected that had a usage restriction at + * odds with the specified usage, or: + * + * (*) -EKEYREJECTED if a signature failed to match for which we found an + * appropriate X.509 certificate, or: + * + * (*) -EBADMSG if some part of the message was invalid, or: + * + * (*) 0 if a signature chain passed verification, or: + * + * (*) -EKEYREJECTED if a blacklisted key was encountered, or: + * + * (*) -ENOPKG if none of the signature chains are verifiable because suitable + * crypto modules couldn't be found. + */ +int pkcs7_verify(struct pkcs7_message *pkcs7, + enum key_being_used_for usage) +{ + struct pkcs7_signed_info *sinfo; + int actual_ret = -ENOPKG; + int ret; + + kenter(""); + + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + if (pkcs7->data_type != OID_data) { + pr_warn("Invalid module sig (not pkcs7-data)\n"); + return -EKEYREJECTED; + } + if (pkcs7->have_authattrs) { + pr_warn("Invalid module sig (has authattrs)\n"); + return -EKEYREJECTED; + } + break; + case VERIFYING_FIRMWARE_SIGNATURE: + if (pkcs7->data_type != OID_data) { + pr_warn("Invalid firmware sig (not pkcs7-data)\n"); + return -EKEYREJECTED; + } + if (!pkcs7->have_authattrs) { + pr_warn("Invalid firmware sig (missing authattrs)\n"); + return -EKEYREJECTED; + } + break; + case VERIFYING_KEXEC_PE_SIGNATURE: + if (pkcs7->data_type != OID_msIndirectData) { + pr_warn("Invalid kexec sig (not Authenticode)\n"); + return -EKEYREJECTED; + } + /* Authattr presence checked in parser */ + break; + case VERIFYING_UNSPECIFIED_SIGNATURE: + if (pkcs7->data_type != OID_data) { + pr_warn("Invalid unspecified sig (not pkcs7-data)\n"); + return -EKEYREJECTED; + } + break; + default: + return -EINVAL; + } + + for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { + ret = pkcs7_verify_one(pkcs7, sinfo); + if (sinfo->blacklisted) { + if (actual_ret == -ENOPKG) + actual_ret = -EKEYREJECTED; + continue; + } + if (ret < 0) { + if (ret == -ENOPKG) { + sinfo->unsupported_crypto = true; + continue; + } + kleave(" = %d", ret); + return ret; + } + actual_ret = 0; + } + + kleave(" = %d", actual_ret); + return actual_ret; +} +EXPORT_SYMBOL_GPL(pkcs7_verify); + +/** + * pkcs7_supply_detached_data - Supply the data needed to verify a PKCS#7 message + * @pkcs7: The PKCS#7 message + * @data: The data to be verified + * @datalen: The amount of data + * + * Supply the detached data needed to verify a PKCS#7 message. Note that no + * attempt to retain/pin the data is made. That is left to the caller. The + * data will not be modified by pkcs7_verify() and will not be freed when the + * PKCS#7 message is freed. + * + * Returns -EINVAL if data is already supplied in the message, 0 otherwise. + */ +int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7, + const void *data, size_t datalen) +{ + if (pkcs7->data) { + pr_debug("Data already supplied\n"); + return -EINVAL; + } + pkcs7->data = data; + pkcs7->data_len = datalen; + return 0; +} +#endif /* __UBOOT__ */ From 05329fa4c0c7774d01945d94ad2e9079a338baa8 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 21 Jul 2020 19:35:20 +0900 Subject: [PATCH 15/16] lib: crypto: add pkcs7_digest() This function was nullified when the file, pkcs7_verify.c, was imported because it calls further linux-specific interfaces inside, hence that could lead to more files being imported from linux. We need this function in pkcs7_verify_one() and so simply re-implement it here instead of re-using the code. Signed-off-by: AKASHI Takahiro --- lib/crypto/pkcs7_verify.c | 92 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/lib/crypto/pkcs7_verify.c b/lib/crypto/pkcs7_verify.c index 192e39bae90..706358f09b5 100644 --- a/lib/crypto/pkcs7_verify.c +++ b/lib/crypto/pkcs7_verify.c @@ -10,10 +10,12 @@ #define pr_fmt(fmt) "PKCS7: "fmt #ifdef __UBOOT__ +#include #include #include #include #include +#include #include #include #else @@ -29,15 +31,99 @@ #endif /* - * Digest the relevant parts of the PKCS#7 data + * pkcs7_digest - Digest the relevant parts of the PKCS#7 data + * @pkcs7: PKCS7 Signed Data + * @sinfo: PKCS7 Signed Info + * + * Digest the relevant parts of the PKCS#7 data, @pkcs7, using signature + * information in @sinfo. But if there are authentication attributes, + * i.e. signed image case, the digest must be calculated against + * the authentication attributes. + * + * Return: 0 - on success, non-zero error code - otherwise */ #ifdef __UBOOT__ static int pkcs7_digest(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { - return 0; + struct public_key_signature *sig = sinfo->sig; + struct image_region regions[2]; + int ret = 0; + + /* The digest was calculated already. */ + if (sig->digest) + return 0; + + if (!sinfo->sig->hash_algo) + return -ENOPKG; + if (!strcmp(sinfo->sig->hash_algo, "sha256")) + sig->digest_size = SHA256_SUM_LEN; + else if (!strcmp(sinfo->sig->hash_algo, "sha1")) + sig->digest_size = SHA1_SUM_LEN; + else + return -ENOPKG; + + sig->digest = calloc(1, sig->digest_size); + if (!sig->digest) { + pr_warn("Sig %u: Out of memory\n", sinfo->index); + return -ENOMEM; + } + + regions[0].data = pkcs7->data; + regions[0].size = pkcs7->data_len; + + /* Digest the message [RFC2315 9.3] */ + hash_calculate(sinfo->sig->hash_algo, regions, 1, sig->digest); + + /* However, if there are authenticated attributes, there must be a + * message digest attribute amongst them which corresponds to the + * digest we just calculated. + */ + if (sinfo->authattrs) { + u8 tag; + + if (!sinfo->msgdigest) { + pr_warn("Sig %u: No messageDigest\n", sinfo->index); + ret = -EKEYREJECTED; + goto error; + } + + if (sinfo->msgdigest_len != sig->digest_size) { + pr_debug("Sig %u: Invalid digest size (%u)\n", + sinfo->index, sinfo->msgdigest_len); + ret = -EBADMSG; + goto error; + } + + if (memcmp(sig->digest, sinfo->msgdigest, + sinfo->msgdigest_len) != 0) { + pr_debug("Sig %u: Message digest doesn't match\n", + sinfo->index); + ret = -EKEYREJECTED; + goto error; + } + + /* We then calculate anew, using the authenticated attributes + * as the contents of the digest instead. Note that we need to + * convert the attributes from a CONT.0 into a SET before we + * hash it. + */ + memset(sig->digest, 0, sig->digest_size); + + tag = 0x31; + regions[0].data = &tag; + regions[0].size = 1; + regions[1].data = sinfo->authattrs; + regions[1].size = sinfo->authattrs_len; + + hash_calculate(sinfo->sig->hash_algo, regions, 2, sig->digest); + + ret = 0; + } +error: + return ret; } -#else +#else /* !__UBOOT__ */ static int pkcs7_digest(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { From 5ee81c6e3f9f6f851c69b1e3d2661d96671d1dd1 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 21 Jul 2020 19:35:21 +0900 Subject: [PATCH 16/16] lib: crypto: export and enhance pkcs7_verify_one() The function, pkcs7_verify_one(), will be utilized to rework signature verification logic aiming to support intermediate certificates in "chain of trust." To do that, its function interface is expanded, adding an extra argument which is expected to return the last certificate in trusted chain. Then, this last one must further be verified with signature database, db and/or dbx. Signed-off-by: AKASHI Takahiro --- include/crypto/pkcs7.h | 9 +++++- lib/crypto/pkcs7_verify.c | 61 ++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8f5c8a7ee3b..ca35df29f6f 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -27,7 +27,14 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, size_t *_headerlen); -#ifndef __UBOOT__ +#ifdef __UBOOT__ +struct pkcs7_signed_info; +struct x509_certificate; + +int pkcs7_verify_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo, + struct x509_certificate **signer); +#else /* * pkcs7_trust.c */ diff --git a/lib/crypto/pkcs7_verify.c b/lib/crypto/pkcs7_verify.c index 706358f09b5..320ba49f79d 100644 --- a/lib/crypto/pkcs7_verify.c +++ b/lib/crypto/pkcs7_verify.c @@ -301,10 +301,27 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, } /* - * Verify the internal certificate chain as best we can. + * pkcs7_verify_sig_chain - Verify the internal certificate chain as best + * as we can. + * @pkcs7: PKCS7 Signed Data + * @sinfo: PKCS7 Signed Info + * @signer: Singer's certificate + * + * Build up and verify the internal certificate chain against a signature + * in @sinfo, using certificates contained in @pkcs7 as best as we can. + * If the chain reaches the end, the last certificate will be returned + * in @signer. + * + * Return: 0 - on success, non-zero error code - otherwise */ +#ifdef __UBOOT__ +static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo, + struct x509_certificate **signer) +#else static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) +#endif { struct public_key_signature *sig; struct x509_certificate *x509 = sinfo->signer, *p; @@ -313,6 +330,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, kenter(""); + *signer = NULL; + for (p = pkcs7->certs; p; p = p->next) p->seen = false; @@ -330,6 +349,9 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, for (p = sinfo->signer; p != x509; p = p->signer) p->blacklisted = true; pr_debug("- blacklisted\n"); +#ifdef __UBOOT__ + *signer = x509; +#endif return 0; } @@ -355,6 +377,9 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, goto unsupported_crypto_in_x509; x509->signer = x509; pr_debug("- self-signed\n"); +#ifdef __UBOOT__ + *signer = x509; +#endif return 0; } @@ -385,6 +410,9 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* We didn't find the root of this chain */ pr_debug("- top\n"); +#ifdef __UBOOT__ + *signer = x509; +#endif return 0; found_issuer_check_skid: @@ -402,6 +430,9 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, if (p->seen) { pr_warn("Sig %u: X.509 chain contains loop\n", sinfo->index); +#ifdef __UBOOT__ + *signer = p; +#endif return 0; } ret = public_key_verify_signature(p->pub, x509->sig); @@ -410,6 +441,9 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509->signer = p; if (x509 == p) { pr_debug("- self-signed\n"); +#ifdef __UBOOT__ + *signer = p; +#endif return 0; } x509 = p; @@ -429,13 +463,26 @@ unsupported_crypto_in_x509: } /* - * Verify one signed information block from a PKCS#7 message. + * pkcs7_verify_one - Verify one signed information block from a PKCS#7 + * message. + * @pkcs7: PKCS7 Signed Data + * @sinfo: PKCS7 Signed Info + * @signer: Signer's certificate + * + * Verify one signature in @sinfo and follow the certificate chain. + * If the chain reaches the end, the last certificate will be returned + * in @signer. + * + * Return: 0 - on success, non-zero error code - otherwise */ -#ifndef __UBOOT__ -static -#endif +#ifdef __UBOOT__ int pkcs7_verify_one(struct pkcs7_message *pkcs7, - struct pkcs7_signed_info *sinfo) + struct pkcs7_signed_info *sinfo, + struct x509_certificate **signer) +#else +static int pkcs7_verify_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +#endif { int ret; @@ -479,7 +526,7 @@ int pkcs7_verify_one(struct pkcs7_message *pkcs7, pr_devel("Verified signature %u\n", sinfo->index); /* Verify the internal certificate chain */ - return pkcs7_verify_sig_chain(pkcs7, sinfo); + return pkcs7_verify_sig_chain(pkcs7, sinfo, signer); } #ifndef __UBOOT__