[U-Boot] [RFC 00/15] efi_loader: add secure boot support

One of major missing features in current UEFI implementation is "secure boot." The ultimate goal of my attempt is to implement image authentication based on signature and provide UEFI secure boot support which would be fully compliant with UEFI specification, section 32[1]. (The code was originally developed by Patrick Wildt.)
While this patch/RFC is still rough-edged, the aim here is to get early feedbacks from the community as the patch is quite huge (in total) and also as it's a security enhancement.
Please note, however, this patch doesn't work on its own; there are a couple of functional dependencies[2], [3] and [4], that I have submitted before, in addition to related preparatory patches[5], [6], [7] and [8] for pytest support. For complete workable patch set, see my repository[9], which also contains exeperimental timestamp-based revocation suuport.
My "non-volatile" support[10], which is under reviews now, is not mandatory and so not included here, but this inevitably implies that, for example, signature database variables, like db and dbx, won't be persistent unless you explicitly run "env save" command and that UEFI variables are not separated from U-Boot environment. Anyhow, Linaro is also working on implementing real "secure storage" solution based on TF-A and OP-TEE.
Supported features: * image authentication based on db and dbx * supported signature types are EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images) EFI_CERT_X509_GUID (x509 certificate for signed images) * SecureBoot/SignatureSupport variables * SetupMode and user mode * variable authentication based on PK and KEK EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS * pytest test cases
Unsupported features: * hash algorithms other than SHA256 * dbt: timestamp(RFC6131)-based certificate revocation * dbr: OS recovery * xxxDefault: default values for signature stores * transition to AuditMode and DeployedMode * recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE * variable authentication based on PK and KEK EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS * real secure storage, including hardware-specific PK (Platform Key) installation
Known issues: * [3] and [4] have not been well reviewed yet. * Some test case(test_efi_var_auth1:1g) still fails. * Extensive clean-ups * not bisect-ready (for easier code modification) for now
TODO: * implement "unsupported" features, in particular, timestamp-based revocation * fix some workarounds in the source (marked as TODO/FIXME) * extensive test suite (or more test cases) to confirm compatibility with EDK2
Hints about how to use: (Please see other documents, or my pytest scripts, for details.) * You can create your own certificates with openssl. * You can sign your application with pesign (on Ubuntu). * You can create raw data for signature database with efitools, and install/manage authenticated variables with "env -set -e" command or efitools' "UpdateVars.efi" application.
[1] https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf [2] https://lists.denx.de/pipermail/u-boot/2019-September/382911.html (support APPEND_WRITE) [3] https://lists.denx.de/pipermail/u-boot/2019-September/382573.html (import x509/pkcs7 parsers from linux) [4] https://lists.denx.de/pipermail/u-boot/2019-September/382917.html (extend rsa_verify() for UEFI secure boot) [5] https://lists.denx.de/pipermail/u-boot/2019-August/382027.html (sandbox: fix cpu property in test.dts for pytest) [6] https://lists.denx.de/pipermail/u-boot/2019-September/382914.html (extend "env [set|print] -e to manage UEFI variables v1) [7] https://lists.denx.de/pipermail/u-boot/2019-September/383343.html (install FILE_SYSTEM_PROTOCOL to a whole disk) [8] https://lists.denx.de/pipermail/u-boot/2019-September/383348.html (support Sandbox's "host" device) [9] http://git.linaro.org/people/takahiro.akashi/u-boot.git/ efi/secboot [10] https://lists.denx.de/pipermail/u-boot/2019-September/382835.html (non-volatile variables support)
AKASHI Takahiro (15): lib: charset: add u16_str<n>cmp() test: add tests for u16_str<n>cmp() include: pe.h: add image-signing-related definitions include: image.h: add key info to image_sign_info include: image.h: export hash algorithm helper functions secure boot: rename CONFIG_SECURE_BOOT efi_loader: add signature verification functions efi_loader: variable: support variable authentication efi_loader: variable: add VendorKeys and SignatureSupport variables efi_loader: image_loader: support image authentication efi_loader: initialize secure boot state efi_loader: add CONFIG_EFI_SECURE_BOOT cmd: env: provide appropriate guid for well-defined variable efi_loader, pytest: add UEFI secure boot tests (image) efi_loader, pytest: add UEFI secure boot tests (authenticated variables)
Kconfig | 7 + arch/arm/cpu/armv7/ls102xa/Kconfig | 3 +- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 +- arch/arm/mach-imx/Kconfig | 3 +- arch/powerpc/cpu/mpc85xx/Kconfig | 3 +- cmd/nvedit_efi.c | 31 +- include/charset.h | 15 + include/efi_api.h | 47 + include/efi_loader.h | 58 +- include/image.h | 17 +- include/pe.h | 16 + lib/charset.c | 25 + lib/efi_loader/Kconfig | 13 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_boottime.c | 2 +- lib/efi_loader/efi_image_loader.c | 364 ++++++- lib/efi_loader/efi_setup.c | 5 + lib/efi_loader/efi_signature.c | 602 ++++++++++++ lib/efi_loader/efi_variable.c | 928 ++++++++++++++++-- test/py/tests/test_efi_secboot/conftest.py | 168 ++++ test/py/tests/test_efi_secboot/defs.py | 7 + .../py/tests/test_efi_secboot/test_authvar.py | 287 ++++++ test/py/tests/test_efi_secboot/test_signed.py | 97 ++ .../tests/test_efi_secboot/test_unsigned.py | 126 +++ test/unicode_ut.c | 13 + 25 files changed, 2714 insertions(+), 127 deletions(-) create mode 100644 lib/efi_loader/efi_signature.c create mode 100644 test/py/tests/test_efi_secboot/conftest.py create mode 100644 test/py/tests/test_efi_secboot/defs.py create mode 100644 test/py/tests/test_efi_secboot/test_authvar.py create mode 100644 test/py/tests/test_efi_secboot/test_signed.py create mode 100644 test/py/tests/test_efi_secboot/test_unsigned.py

u16 version of strcmp(): u16_strncmp() works like u16_strcmp() but only at most n characters (in u16) are compared. This function will be used in my UEFI secure boot patch.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/charset.h | 15 +++++++++++++++ lib/charset.c | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+)
diff --git a/include/charset.h b/include/charset.h index 020f8a90df07..fde6bddbc2fb 100644 --- a/include/charset.h +++ b/include/charset.h @@ -168,6 +168,21 @@ s32 utf_to_lower(const s32 code); */ s32 utf_to_upper(const s32 code);
+/* + * u16_strncmp() - compare two u16 string + * + * @s1: first string to compare + * @s2: second string to compare + * @n: maximum number of u16 to compare + * Return: 0 if the first n u16 are the same in s1 and s2 + * < 0 if the first different u16 in s1 is less than the + * corresponding u16 in s2 + * > 0 if the first different u16 in s1 is greater than the + * corresponding u16 in s2 + */ +int u16_strncmp(const u16 *s1, const u16 *s2, size_t n); +#define u16_strcmp(s1, s2) u16_strncmp((s1), (s2), SIZE_MAX) + /** * u16_strlen - count non-zero words * diff --git a/lib/charset.c b/lib/charset.c index 72d745da4f4e..1c6a7f693de4 100644 --- a/lib/charset.c +++ b/lib/charset.c @@ -335,6 +335,31 @@ s32 utf_to_upper(const s32 code) return ret; }
+/* + * u16_strncmp() - compare two u16 string + * + * @s1: first string to compare + * @s2: second string to compare + * @n: maximum number of u16 to compare + * Return: 0 if the first n u16 are the same in s1 and s2 + * < 0 if the first different u16 in s1 is less than the + * corresponding u16 in s2 + * > 0 if the first different u16 in s1 is greater than the + * corresponding u16 in s2 + */ +int u16_strncmp(const u16 *s1, const u16 *s2, size_t n) +{ + int ret = 0; + + for (; n; --n, ++s1, ++s2) { + ret = *s1 - *s2; + if (ret || !*s1) + break; + } + + return ret; +} + size_t u16_strlen(const void *in) { const char *pos = in;

On 9/18/19 3:26 AM, AKASHI Takahiro wrote:
u16 version of strcmp(): u16_strncmp() works like u16_strcmp() but only at most n characters (in u16) are compared. This function will be used in my UEFI secure boot patch.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

New seven test cases for u16_str<n>cmp() are added under Unicode unit test, which should be executed by "ut unicode" command.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- test/unicode_ut.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/test/unicode_ut.c b/test/unicode_ut.c index 1ccd36e7c9e8..8875cdc6b2f5 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -567,6 +567,19 @@ static int unicode_test_utf_to_upper(struct unit_test_state *uts) } UNICODE_TEST(unicode_test_utf_to_upper);
+static int unicode_test_u16_strncmp(struct unit_test_state *uts) +{ + ut_assert(u16_strncmp(L"abc", L"abc", 3) == 0); + ut_assert(u16_strncmp(L"abcdef", L"abcghi", 3) == 0); + ut_assert(u16_strncmp(L"abcdef", L"abcghi", 6) < 0); + ut_assert(u16_strncmp(L"abcghi", L"abcdef", 6) > 0); + ut_assert(u16_strcmp(L"abc", L"abc") == 0); + ut_assert(u16_strcmp(L"abcdef", L"deghi") < 0); + ut_assert(u16_strcmp(L"deghi", L"abcdef") > 0); + return 0; +} +UNICODE_TEST(unicode_test_u16_strncmp); + int do_ut_unicode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct unit_test *tests = ll_entry_start(struct unit_test, unicode_test);

On 9/18/19 3:26 AM, AKASHI Takahiro wrote:
New seven test cases for u16_str<n>cmp() are added under Unicode unit test, which should be executed by "ut unicode" command.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Reviewed-by: Heinrich Schuchardt xypron.glpk@gmx.de

The index (IMAGE_DIRECTORY_ENTRY_CERTTABLE) in a table points to a region containing authentication information (image's signature) in PE format.
WIN_CERTIFICATE structure defines an embedded signature format.
Those definitions will be used in my UEFI secure boot patch.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/pe.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/include/pe.h b/include/pe.h index bff3b0aa7a6c..650478ca78af 100644 --- a/include/pe.h +++ b/include/pe.h @@ -155,6 +155,7 @@ typedef struct _IMAGE_SECTION_HEADER { uint32_t Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+#define IMAGE_DIRECTORY_ENTRY_CERTTABLE 4 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
typedef struct _IMAGE_BASE_RELOCATION @@ -252,4 +253,19 @@ typedef struct _IMAGE_RELOCATION #define IMAGE_REL_AMD64_PAIR 0x000F #define IMAGE_REL_AMD64_SSPAN32 0x0010
+/* certificate appended to PE image */ +typedef struct _WIN_CERTIFICATE { + uint32_t dwLength; + uint16_t wRevision; + uint16_t wCertificateType; + uint8_t bCertificate[]; +} WIN_CERTIFICATE, *LPWIN_CERTIFICATE; + +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 +#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +#define WIN_CERT_REVISION_1_0 0x0100 +#define WIN_CERT_REVISION_2_0 0x0200 + #endif /* _PE_H */

For FIT verification, all the properties of a public key come from "control fdt" pointed to by fdt_blob. In UEFI secure boot, on the other hand, a public key is located and retrieved from dedicated signature database stored as UEFI variables.
Added two fields may hold values of a public key if fdt_blob is NULL, and will be used in rsa_verify_with_pkey() to verify a signature in UEFI sub-system.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/image.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/image.h b/include/image.h index 97b6a82d9754..685f5181c829 100644 --- a/include/image.h +++ b/include/image.h @@ -1136,6 +1136,8 @@ struct image_sign_info { struct checksum_algo *checksum; /* Checksum algorithm information */ struct padding_algo *padding; /* Padding algorithm information */ struct crypto_algo *crypto; /* Crypto algorithm information */ + const void *key; + int keylen; const void *fdt_blob; /* FDT containing public keys */ int required_keynode; /* Node offset of key to use: -1=any */ const char *require_keys; /* Value for 'required' property */

Hi AKASHI,
On Tue, 17 Sep 2019 at 19:23, AKASHI Takahiro takahiro.akashi@linaro.org wrote:
For FIT verification, all the properties of a public key come from "control fdt" pointed to by fdt_blob. In UEFI secure boot, on the other hand, a public key is located and retrieved from dedicated signature database stored as UEFI variables.
Added two fields may hold values of a public key if fdt_blob is NULL, and will be used in rsa_verify_with_pkey() to verify a signature in UEFI sub-system.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
include/image.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/image.h b/include/image.h index 97b6a82d9754..685f5181c829 100644 --- a/include/image.h +++ b/include/image.h @@ -1136,6 +1136,8 @@ struct image_sign_info { struct checksum_algo *checksum; /* Checksum algorithm information */ struct padding_algo *padding; /* Padding algorithm information */ struct crypto_algo *crypto; /* Crypto algorithm information */
const void *key;
int keylen;
Please do add comments.
Also if these only relate to EFI they should have efi_ prefix and probably an #ifdef.
const void *fdt_blob; /* FDT containing public keys */ int required_keynode; /* Node offset of key to use: -1=any */ const char *require_keys; /* Value for 'required' property */
-- 2.21.0
Regards, Simon

This commit allows us to use common/image-sig.c even if CONFIG_FIT is disabled but CONFIG_EFI_LOADER is enabled.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/image.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/include/image.h b/include/image.h index 685f5181c829..c9fe1d8eaed8 100644 --- a/include/image.h +++ b/include/image.h @@ -53,7 +53,7 @@ struct fdt_region;
#endif /* USE_HOSTCC */
-#if IMAGE_ENABLE_FIT +#if IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT) #include <hash.h> #include <linux/libfdt.h> #include <fdt_support.h> @@ -86,13 +86,14 @@ struct fdt_region; #endif
#if defined(CONFIG_FIT_ENABLE_SHA256_SUPPORT) || \ - defined(CONFIG_SPL_SHA256_SUPPORT) + defined(CONFIG_SPL_SHA256_SUPPORT) || \ + defined(CONFIG_EFI_SECURE_BOOT) #define IMAGE_ENABLE_SHA256 1 #else #define IMAGE_ENABLE_SHA256 0 #endif
-#endif /* IMAGE_ENABLE_FIT */ +#endif /* IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT) */
#ifdef CONFIG_SYS_BOOT_GET_CMDLINE # define IMAGE_BOOT_GET_CMDLINE 1 @@ -1085,6 +1086,7 @@ int fit_conf_get_prop_node(const void *fit, int noffset,
int fit_check_ramdisk(const void *fit, int os_noffset, uint8_t arch, int verify); +#endif /* IMAGE_ENABLE_FIT */
int calculate_hash(const void *data, int data_len, const char *algo, uint8_t *value, int *value_len); @@ -1143,7 +1145,6 @@ struct image_sign_info { const char *require_keys; /* Value for 'required' property */ const char *engine_id; /* Engine to use for signing */ }; -#endif /* Allow struct image_region to always be defined for rsa.h */
/* A part of an image, used for hashing */ struct image_region { @@ -1151,7 +1152,7 @@ struct image_region { int size; };
-#if IMAGE_ENABLE_FIT +#if IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT)
#if IMAGE_ENABLE_VERIFY # include <u-boot/rsa-checksum.h> @@ -1252,7 +1253,9 @@ struct crypto_algo *image_get_crypto_algo(const char *full_name); * @return pointer to algorithm information, or NULL if not found */ struct padding_algo *image_get_padding_algo(const char *name); +#endif /* IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT) */
+#if IMAGE_ENABLE_FIT /** * fit_image_verify_required_sigs() - Verify signatures marked as 'required' * @@ -1328,7 +1331,7 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) #define fit_unsupported(msg) #define fit_unsupported_reset(msg) #endif /* CONFIG_FIT_VERBOSE */ -#endif /* CONFIG_FIT */ +#endif /* IMAGE_ENABLE_FIT */
#if defined(CONFIG_ANDROID_BOOT_IMAGE) struct andr_img_hdr;

On Tue, 17 Sep 2019 at 19:23, AKASHI Takahiro takahiro.akashi@linaro.org wrote:
This commit allows us to use common/image-sig.c even if CONFIG_FIT is disabled but CONFIG_EFI_LOADER is enabled.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
include/image.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org
BTW I think it should be possible to remove these things in the header file now. Please see below.
diff --git a/include/image.h b/include/image.h index 685f5181c829..c9fe1d8eaed8 100644 --- a/include/image.h +++ b/include/image.h @@ -53,7 +53,7 @@ struct fdt_region;
#endif /* USE_HOSTCC */
-#if IMAGE_ENABLE_FIT +#if IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT) #include <hash.h> #include <linux/libfdt.h> #include <fdt_support.h> @@ -86,13 +86,14 @@ struct fdt_region; #endif
#if defined(CONFIG_FIT_ENABLE_SHA256_SUPPORT) || \
defined(CONFIG_SPL_SHA256_SUPPORT)
defined(CONFIG_SPL_SHA256_SUPPORT) || \
defined(CONFIG_EFI_SECURE_BOOT)
#define IMAGE_ENABLE_SHA256 1 #else #define IMAGE_ENABLE_SHA256 0 #endif
We can probably use (in the C file):
if (IS_ENABLED(CONFIG_...) || IS_ENABLED(CONFIG_...) ...
-#endif /* IMAGE_ENABLE_FIT */ +#endif /* IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT) */
#ifdef CONFIG_SYS_BOOT_GET_CMDLINE # define IMAGE_BOOT_GET_CMDLINE 1 @@ -1085,6 +1086,7 @@ int fit_conf_get_prop_node(const void *fit, int noffset,
int fit_check_ramdisk(const void *fit, int os_noffset, uint8_t arch, int verify); +#endif /* IMAGE_ENABLE_FIT */
int calculate_hash(const void *data, int data_len, const char *algo, uint8_t *value, int *value_len); @@ -1143,7 +1145,6 @@ struct image_sign_info { const char *require_keys; /* Value for 'required' property */ const char *engine_id; /* Engine to use for signing */ }; -#endif /* Allow struct image_region to always be defined for rsa.h */
/* A part of an image, used for hashing */ struct image_region { @@ -1151,7 +1152,7 @@ struct image_region { int size; };
-#if IMAGE_ENABLE_FIT +#if IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT)
#if IMAGE_ENABLE_VERIFY # include <u-boot/rsa-checksum.h> @@ -1252,7 +1253,9 @@ struct crypto_algo *image_get_crypto_algo(const char *full_name);
- @return pointer to algorithm information, or NULL if not found
*/ struct padding_algo *image_get_padding_algo(const char *name); +#endif /* IMAGE_ENABLE_FIT || defined(CONFIG_EFI_SECURE_BOOT) */
+#if IMAGE_ENABLE_FIT /**
- fit_image_verify_required_sigs() - Verify signatures marked as 'required'
@@ -1328,7 +1331,7 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) #define fit_unsupported(msg) #define fit_unsupported_reset(msg) #endif /* CONFIG_FIT_VERBOSE */ -#endif /* CONFIG_FIT */ +#endif /* IMAGE_ENABLE_FIT */
#if defined(CONFIG_ANDROID_BOOT_IMAGE) struct andr_img_hdr; -- 2.21.0
Regards, Simon

The configuration, CONFIG_SECURE_BOOT, was scattered among different architecture directories for different implementation. This will prevent UEFI secure boot from being added later.
So let's rename them, giving each implementation to different configuration option. CONFIG_SECURE_BOOT still remains not to break existing implicit dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- Kconfig | 7 +++++++ arch/arm/cpu/armv7/ls102xa/Kconfig | 3 ++- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 ++- arch/arm/mach-imx/Kconfig | 3 ++- arch/powerpc/cpu/mpc85xx/Kconfig | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 1f0904f7045e..c11fc102a7d4 100644 --- a/Kconfig +++ b/Kconfig @@ -282,6 +282,13 @@ config SYS_LDSCRIPT
endmenu # General setup
+config SECURE_BOOT + bool "Secure Boot" + imply SHA256 + help + Enable Secure Boot feature. The actual behavior may vary + from architecture to architecture. + menu "Boot images"
config ANDROID_BOOT_IMAGE diff --git a/arch/arm/cpu/armv7/ls102xa/Kconfig b/arch/arm/cpu/armv7/ls102xa/Kconfig index 94fa68250ddf..ce1bc580d23d 100644 --- a/arch/arm/cpu/armv7/ls102xa/Kconfig +++ b/arch/arm/cpu/armv7/ls102xa/Kconfig @@ -50,8 +50,9 @@ config MAX_CPUS cores, count the reserved ports. This will allocate enough memory in spin table to properly handle all cores.
-config SECURE_BOOT +config FSL_ARMV7_ENABLE_SECURE_BOOT bool "Secure Boot" + depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change. diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 42d31fdab0a0..d4cfe31f8ebf 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -383,8 +383,9 @@ config EMC2305 Enable the EMC2305 fan controller for configuration of fan speed.
-config SECURE_BOOT +config FSI_ARMV8_ENABLE_SECURE_BOOT bool "Secure Boot" + depends on SECURE_BOOT help Enable Freescale Secure Boot feature
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index aeb54934888d..e1602fd5f0e8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -34,8 +34,9 @@ config USE_IMXIMG_PLUGIN i.MX6/7 supports DCD and Plugin. Enable this configuration to use Plugin, otherwise DCD will be used.
-config SECURE_BOOT +config FSL_IMX_ENABLE_SECURE_BOOT bool "Support i.MX HAB features" + depends on SECURE_BOOT depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 select FSL_CAAM if HAS_CAAM imply CMD_DEKBLOB diff --git a/arch/powerpc/cpu/mpc85xx/Kconfig b/arch/powerpc/cpu/mpc85xx/Kconfig index c038a6ddb0f4..9cf6ebbfe3ce 100644 --- a/arch/powerpc/cpu/mpc85xx/Kconfig +++ b/arch/powerpc/cpu/mpc85xx/Kconfig @@ -1208,8 +1208,9 @@ config FSL_LAW help Use Freescale common code for Local Access Window
-config SECURE_BOOT +config FSL_MPC_ENABLE_SECURE_BOOT bool "Secure Boot" + depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.

On Wed, Sep 18, 2019 at 10:26:34AM +0900, AKASHI Takahiro wrote:
The configuration, CONFIG_SECURE_BOOT, was scattered among different architecture directories for different implementation. This will prevent UEFI secure boot from being added later.
So let's rename them, giving each implementation to different configuration option. CONFIG_SECURE_BOOT still remains not to break existing implicit dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Kconfig | 7 +++++++ arch/arm/cpu/armv7/ls102xa/Kconfig | 3 ++- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 ++- arch/arm/mach-imx/Kconfig | 3 ++- arch/powerpc/cpu/mpc85xx/Kconfig | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 1f0904f7045e..c11fc102a7d4 100644 --- a/Kconfig +++ b/Kconfig @@ -282,6 +282,13 @@ config SYS_LDSCRIPT
endmenu # General setup
+config SECURE_BOOT
- bool "Secure Boot"
- imply SHA256
- help
Enable Secure Boot feature. The actual behavior may vary
from architecture to architecture.
menu "Boot images"
config ANDROID_BOOT_IMAGE diff --git a/arch/arm/cpu/armv7/ls102xa/Kconfig b/arch/arm/cpu/armv7/ls102xa/Kconfig index 94fa68250ddf..ce1bc580d23d 100644 --- a/arch/arm/cpu/armv7/ls102xa/Kconfig +++ b/arch/arm/cpu/armv7/ls102xa/Kconfig @@ -50,8 +50,9 @@ config MAX_CPUS cores, count the reserved ports. This will allocate enough memory in spin table to properly handle all cores.
-config SECURE_BOOT +config FSL_ARMV7_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 42d31fdab0a0..d4cfe31f8ebf 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -383,8 +383,9 @@ config EMC2305 Enable the EMC2305 fan controller for configuration of fan speed.
-config SECURE_BOOT +config FSI_ARMV8_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index aeb54934888d..e1602fd5f0e8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -34,8 +34,9 @@ config USE_IMXIMG_PLUGIN i.MX6/7 supports DCD and Plugin. Enable this configuration to use Plugin, otherwise DCD will be used.
-config SECURE_BOOT +config FSL_IMX_ENABLE_SECURE_BOOT bool "Support i.MX HAB features"
- depends on SECURE_BOOT depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 select FSL_CAAM if HAS_CAAM imply CMD_DEKBLOB
diff --git a/arch/powerpc/cpu/mpc85xx/Kconfig b/arch/powerpc/cpu/mpc85xx/Kconfig index c038a6ddb0f4..9cf6ebbfe3ce 100644 --- a/arch/powerpc/cpu/mpc85xx/Kconfig +++ b/arch/powerpc/cpu/mpc85xx/Kconfig @@ -1208,8 +1208,9 @@ config FSL_LAW help Use Freescale common code for Local Access Window
-config SECURE_BOOT +config FSL_MPC_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
I've added Priyanka Jain to the thread as the custodian for PowerPC and NXP stuff and Stefano Babic as the custodian for i.MX stuff. I don't want to see "CONFIG_SECURE_BOOT" continue on as a config option, it's too broad. Can we please rename and update the existing NXP CONFIG option (and I assume split it into a few ones to reflect better where things really changed fundamentally from one SoC/arch to the next) and update the help text? Thanks!

On 19/09/19 17:02, Tom Rini wrote:
On Wed, Sep 18, 2019 at 10:26:34AM +0900, AKASHI Takahiro wrote:
The configuration, CONFIG_SECURE_BOOT, was scattered among different architecture directories for different implementation. This will prevent UEFI secure boot from being added later.
So let's rename them, giving each implementation to different configuration option. CONFIG_SECURE_BOOT still remains not to break existing implicit dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Kconfig | 7 +++++++ arch/arm/cpu/armv7/ls102xa/Kconfig | 3 ++- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 ++- arch/arm/mach-imx/Kconfig | 3 ++- arch/powerpc/cpu/mpc85xx/Kconfig | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 1f0904f7045e..c11fc102a7d4 100644 --- a/Kconfig +++ b/Kconfig @@ -282,6 +282,13 @@ config SYS_LDSCRIPT
endmenu # General setup
+config SECURE_BOOT
- bool "Secure Boot"
- imply SHA256
- help
Enable Secure Boot feature. The actual behavior may vary
from architecture to architecture.
menu "Boot images"
config ANDROID_BOOT_IMAGE diff --git a/arch/arm/cpu/armv7/ls102xa/Kconfig b/arch/arm/cpu/armv7/ls102xa/Kconfig index 94fa68250ddf..ce1bc580d23d 100644 --- a/arch/arm/cpu/armv7/ls102xa/Kconfig +++ b/arch/arm/cpu/armv7/ls102xa/Kconfig @@ -50,8 +50,9 @@ config MAX_CPUS cores, count the reserved ports. This will allocate enough memory in spin table to properly handle all cores.
-config SECURE_BOOT +config FSL_ARMV7_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 42d31fdab0a0..d4cfe31f8ebf 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -383,8 +383,9 @@ config EMC2305 Enable the EMC2305 fan controller for configuration of fan speed.
-config SECURE_BOOT +config FSI_ARMV8_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index aeb54934888d..e1602fd5f0e8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -34,8 +34,9 @@ config USE_IMXIMG_PLUGIN i.MX6/7 supports DCD and Plugin. Enable this configuration to use Plugin, otherwise DCD will be used.
-config SECURE_BOOT +config FSL_IMX_ENABLE_SECURE_BOOT bool "Support i.MX HAB features"
- depends on SECURE_BOOT depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 select FSL_CAAM if HAS_CAAM imply CMD_DEKBLOB
diff --git a/arch/powerpc/cpu/mpc85xx/Kconfig b/arch/powerpc/cpu/mpc85xx/Kconfig index c038a6ddb0f4..9cf6ebbfe3ce 100644 --- a/arch/powerpc/cpu/mpc85xx/Kconfig +++ b/arch/powerpc/cpu/mpc85xx/Kconfig @@ -1208,8 +1208,9 @@ config FSL_LAW help Use Freescale common code for Local Access Window
-config SECURE_BOOT +config FSL_MPC_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
I've added Priyanka Jain to the thread as the custodian for PowerPC and NXP stuff and Stefano Babic as the custodian for i.MX stuff. I don't want to see "CONFIG_SECURE_BOOT" continue on as a config option, it's too broad. Can we please rename and update the existing NXP CONFIG option (and I assume split it into a few ones to reflect better where things really changed fundamentally from one SoC/arch to the next) and update the help text? Thanks!
Sure - SECURE_BOOT for NXP means enabling HAB, a config can be rename to identify the component itself (CONFIG_HAB for example).
Regards, Stefano

-----Original Message----- From: Stefano Babic sbabic@denx.de Sent: Thursday, September 19, 2019 8:40 PM To: Tom Rini trini@konsulko.com; AKASHI Takahiro takahiro.akashi@linaro.org; Priyanka Jain priyanka.jain@nxp.com; Stefano Babic sbabic@denx.de Cc: xypron.glpk@gmx.de; agraf@csgraf.de; u-boot@lists.denx.de Subject: Re: [U-Boot] [RFC 06/15] secure boot: rename CONFIG_SECURE_BOOT
On 19/09/19 17:02, Tom Rini wrote:
On Wed, Sep 18, 2019 at 10:26:34AM +0900, AKASHI Takahiro wrote:
The configuration, CONFIG_SECURE_BOOT, was scattered among different architecture directories for different implementation. This will prevent UEFI secure boot from being added later.
So let's rename them, giving each implementation to different configuration option. CONFIG_SECURE_BOOT still remains not to break existing implicit dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Kconfig | 7 +++++++ arch/arm/cpu/armv7/ls102xa/Kconfig | 3 ++- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 ++- arch/arm/mach-imx/Kconfig | 3 ++- arch/powerpc/cpu/mpc85xx/Kconfig | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 1f0904f7045e..c11fc102a7d4 100644 --- a/Kconfig +++ b/Kconfig @@ -282,6 +282,13 @@ config SYS_LDSCRIPT
endmenu # General setup
+config SECURE_BOOT
- bool "Secure Boot"
- imply SHA256
- help
Enable Secure Boot feature. The actual behavior may vary
from architecture to architecture.
menu "Boot images"
config ANDROID_BOOT_IMAGE diff --git a/arch/arm/cpu/armv7/ls102xa/Kconfig b/arch/arm/cpu/armv7/ls102xa/Kconfig index 94fa68250ddf..ce1bc580d23d 100644 --- a/arch/arm/cpu/armv7/ls102xa/Kconfig +++ b/arch/arm/cpu/armv7/ls102xa/Kconfig @@ -50,8 +50,9 @@ config MAX_CPUS cores, count the reserved ports. This will allocate enough memory in spin table to properly handle all cores.
-config SECURE_BOOT +config FSL_ARMV7_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 42d31fdab0a0..d4cfe31f8ebf 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -383,8 +383,9 @@ config EMC2305 Enable the EMC2305 fan controller for configuration of fan speed.
-config SECURE_BOOT +config FSI_ARMV8_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index aeb54934888d..e1602fd5f0e8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -34,8 +34,9 @@ config USE_IMXIMG_PLUGIN i.MX6/7 supports DCD and Plugin. Enable this configuration to use Plugin, otherwise DCD will be used.
-config SECURE_BOOT +config FSL_IMX_ENABLE_SECURE_BOOT bool "Support i.MX HAB features"
- depends on SECURE_BOOT depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 select FSL_CAAM if HAS_CAAM imply CMD_DEKBLOB
diff --git a/arch/powerpc/cpu/mpc85xx/Kconfig b/arch/powerpc/cpu/mpc85xx/Kconfig index c038a6ddb0f4..9cf6ebbfe3ce 100644 --- a/arch/powerpc/cpu/mpc85xx/Kconfig +++ b/arch/powerpc/cpu/mpc85xx/Kconfig @@ -1208,8 +1208,9 @@ config FSL_LAW help Use Freescale common code for Local Access Window
-config SECURE_BOOT +config FSL_MPC_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
I've added Priyanka Jain to the thread as the custodian for PowerPC and NXP stuff and Stefano Babic as the custodian for i.MX stuff. I don't want to see "CONFIG_SECURE_BOOT" continue on as a config option, it's too broad. Can we please rename and update the existing NXP CONFIG option (and I assume split it into a few ones to reflect better where things really changed fundamentally from one SoC/arch to the next) and update the help text? Thanks!
Sure - SECURE_BOOT for NXP means enabling HAB, a config can be rename to identify the component itself (CONFIG_HAB for example).
Regards, Stefano
Sure, We will look into this and update NXP CONFIG_SECURE_BOOT option. Priyanka
--
===== DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic@denx.de ================================================================ =====

Priyanka, Stefano and Tom,
On Wed, Sep 25, 2019 at 04:19:43AM +0000, Priyanka Jain wrote:
-----Original Message----- From: Stefano Babic sbabic@denx.de Sent: Thursday, September 19, 2019 8:40 PM To: Tom Rini trini@konsulko.com; AKASHI Takahiro takahiro.akashi@linaro.org; Priyanka Jain priyanka.jain@nxp.com; Stefano Babic sbabic@denx.de Cc: xypron.glpk@gmx.de; agraf@csgraf.de; u-boot@lists.denx.de Subject: Re: [U-Boot] [RFC 06/15] secure boot: rename CONFIG_SECURE_BOOT
On 19/09/19 17:02, Tom Rini wrote:
On Wed, Sep 18, 2019 at 10:26:34AM +0900, AKASHI Takahiro wrote:
The configuration, CONFIG_SECURE_BOOT, was scattered among different architecture directories for different implementation. This will prevent UEFI secure boot from being added later.
So let's rename them, giving each implementation to different configuration option. CONFIG_SECURE_BOOT still remains not to break existing implicit dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Kconfig | 7 +++++++ arch/arm/cpu/armv7/ls102xa/Kconfig | 3 ++- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 ++- arch/arm/mach-imx/Kconfig | 3 ++- arch/powerpc/cpu/mpc85xx/Kconfig | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 1f0904f7045e..c11fc102a7d4 100644 --- a/Kconfig +++ b/Kconfig @@ -282,6 +282,13 @@ config SYS_LDSCRIPT
endmenu # General setup
+config SECURE_BOOT
- bool "Secure Boot"
- imply SHA256
- help
Enable Secure Boot feature. The actual behavior may vary
from architecture to architecture.
menu "Boot images"
config ANDROID_BOOT_IMAGE diff --git a/arch/arm/cpu/armv7/ls102xa/Kconfig b/arch/arm/cpu/armv7/ls102xa/Kconfig index 94fa68250ddf..ce1bc580d23d 100644 --- a/arch/arm/cpu/armv7/ls102xa/Kconfig +++ b/arch/arm/cpu/armv7/ls102xa/Kconfig @@ -50,8 +50,9 @@ config MAX_CPUS cores, count the reserved ports. This will allocate enough memory in spin table to properly handle all cores.
-config SECURE_BOOT +config FSL_ARMV7_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 42d31fdab0a0..d4cfe31f8ebf 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -383,8 +383,9 @@ config EMC2305 Enable the EMC2305 fan controller for configuration of fan speed.
-config SECURE_BOOT +config FSI_ARMV8_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index aeb54934888d..e1602fd5f0e8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -34,8 +34,9 @@ config USE_IMXIMG_PLUGIN i.MX6/7 supports DCD and Plugin. Enable this configuration to use Plugin, otherwise DCD will be used.
-config SECURE_BOOT +config FSL_IMX_ENABLE_SECURE_BOOT bool "Support i.MX HAB features"
- depends on SECURE_BOOT depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 select FSL_CAAM if HAS_CAAM imply CMD_DEKBLOB
diff --git a/arch/powerpc/cpu/mpc85xx/Kconfig b/arch/powerpc/cpu/mpc85xx/Kconfig index c038a6ddb0f4..9cf6ebbfe3ce 100644 --- a/arch/powerpc/cpu/mpc85xx/Kconfig +++ b/arch/powerpc/cpu/mpc85xx/Kconfig @@ -1208,8 +1208,9 @@ config FSL_LAW help Use Freescale common code for Local Access Window
-config SECURE_BOOT +config FSL_MPC_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
I've added Priyanka Jain to the thread as the custodian for PowerPC and NXP stuff and Stefano Babic as the custodian for i.MX stuff. I don't want to see "CONFIG_SECURE_BOOT" continue on as a config option, it's too broad. Can we please rename and update the existing NXP CONFIG option (and I assume split it into a few ones to reflect better where things really changed fundamentally from one SoC/arch to the next) and update the help text? Thanks!
Sure - SECURE_BOOT for NXP means enabling HAB, a config can be rename to identify the component itself (CONFIG_HAB for example).
Regards, Stefano
Sure, We will look into this and update NXP CONFIG_SECURE_BOOT option. Priyanka
Can we expect this re-work on NXP/Freescal platforms to be done in the current release cycle, that is v2020.01?
If not, can I continue to use my match[1] as part of my UEFI secure boot patch set for the time being?
[1] https://lists.denx.de/pipermail/u-boot/2019-September/383908.html
Thanks, -Takahiro Akashi
--
===== DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic@denx.de ================================================================ =====

-----Original Message----- From: AKASHI Takahiro takahiro.akashi@linaro.org Sent: Tuesday, October 29, 2019 10:49 AM To: Priyanka Jain priyanka.jain@nxp.com; Stefano Babic sbabic@denx.de Cc: Tom Rini trini@konsulko.com; Udit Agarwal udit.agarwal@nxp.com; xypron.glpk@gmx.de; agraf@csgraf.de; u-boot@lists.denx.de Subject: Re: [U-Boot] [RFC 06/15] secure boot: rename CONFIG_SECURE_BOOT
Priyanka, Stefano and Tom,
On Wed, Sep 25, 2019 at 04:19:43AM +0000, Priyanka Jain wrote:
-----Original Message----- From: Stefano Babic sbabic@denx.de Sent: Thursday, September 19, 2019 8:40 PM To: Tom Rini trini@konsulko.com; AKASHI Takahiro takahiro.akashi@linaro.org; Priyanka Jain priyanka.jain@nxp.com; Stefano Babic sbabic@denx.de Cc: xypron.glpk@gmx.de; agraf@csgraf.de; u-boot@lists.denx.de Subject: Re: [U-Boot] [RFC 06/15] secure boot: rename CONFIG_SECURE_BOOT
On 19/09/19 17:02, Tom Rini wrote:
On Wed, Sep 18, 2019 at 10:26:34AM +0900, AKASHI Takahiro wrote:
The configuration, CONFIG_SECURE_BOOT, was scattered among different architecture directories for different implementation. This will prevent UEFI secure boot from being added later.
So let's rename them, giving each implementation to different configuration option. CONFIG_SECURE_BOOT still remains not to break existing implicit dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
Kconfig | 7 +++++++ arch/arm/cpu/armv7/ls102xa/Kconfig | 3 ++- arch/arm/cpu/armv8/fsl-layerscape/Kconfig | 3 ++- arch/arm/mach-imx/Kconfig | 3 ++- arch/powerpc/cpu/mpc85xx/Kconfig | 3 ++- 5 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/Kconfig b/Kconfig index 1f0904f7045e..c11fc102a7d4 100644 --- a/Kconfig +++ b/Kconfig @@ -282,6 +282,13 @@ config SYS_LDSCRIPT
endmenu # General setup
+config SECURE_BOOT
- bool "Secure Boot"
- imply SHA256
- help
Enable Secure Boot feature. The actual behavior may vary
from architecture to architecture.
menu "Boot images"
config ANDROID_BOOT_IMAGE diff --git a/arch/arm/cpu/armv7/ls102xa/Kconfig b/arch/arm/cpu/armv7/ls102xa/Kconfig index 94fa68250ddf..ce1bc580d23d 100644 --- a/arch/arm/cpu/armv7/ls102xa/Kconfig +++ b/arch/arm/cpu/armv7/ls102xa/Kconfig @@ -50,8 +50,9 @@ config MAX_CPUS cores, count the reserved ports. This will allocate enough memory in spin table to properly handle all cores.
-config SECURE_BOOT +config FSL_ARMV7_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig index 42d31fdab0a0..d4cfe31f8ebf 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/Kconfig +++ b/arch/arm/cpu/armv8/fsl-layerscape/Kconfig @@ -383,8 +383,9 @@ config EMC2305 Enable the EMC2305 fan controller for configuration of fan speed.
-config SECURE_BOOT +config FSI_ARMV8_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index aeb54934888d..e1602fd5f0e8 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -34,8 +34,9 @@ config USE_IMXIMG_PLUGIN i.MX6/7 supports DCD and Plugin. Enable this configuration to use Plugin, otherwise DCD will be used.
-config SECURE_BOOT +config FSL_IMX_ENABLE_SECURE_BOOT bool "Support i.MX HAB features"
- depends on SECURE_BOOT depends on ARCH_MX7 || ARCH_MX6 || ARCH_MX5 select FSL_CAAM if HAS_CAAM imply CMD_DEKBLOB
diff --git a/arch/powerpc/cpu/mpc85xx/Kconfig b/arch/powerpc/cpu/mpc85xx/Kconfig index c038a6ddb0f4..9cf6ebbfe3ce 100644 --- a/arch/powerpc/cpu/mpc85xx/Kconfig +++ b/arch/powerpc/cpu/mpc85xx/Kconfig @@ -1208,8 +1208,9 @@ config FSL_LAW help Use Freescale common code for Local Access Window
-config SECURE_BOOT +config FSL_MPC_ENABLE_SECURE_BOOT bool "Secure Boot"
- depends on SECURE_BOOT help Enable Freescale Secure Boot feature. Normally selected by defconfig. If unsure, do not change.
I've added Priyanka Jain to the thread as the custodian for PowerPC and NXP stuff and Stefano Babic as the custodian for i.MX stuff. I don't want to see "CONFIG_SECURE_BOOT" continue on as a config option, it's too broad. Can we please rename and update the existing NXP CONFIG option (and I assume split it into a few ones to reflect better where things really changed fundamentally from one SoC/arch to the next) and update the help text? Thanks!
Sure - SECURE_BOOT for NXP means enabling HAB, a config can be rename to identify the component itself (CONFIG_HAB for example).
Regards, Stefano
Sure, We will look into this and update NXP CONFIG_SECURE_BOOT option. Priyanka
Can we expect this re-work on NXP/Freescal platforms to be done in the current release cycle, that is v2020.01?
Yes, we are working on the changes for NXP ARM and mpc85xx platforms.
Regards Priyanka
If not, can I continue to use my match[1] as part of my UEFI secure boot patch set for the time being?
[1] https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.de nx.de%2Fpipermail%2Fu-boot%2F2019- September%2F383908.html&data=02%7C01%7Cpriyanka.jain%40nxp.com %7C00a34480e43c4950cb0808d75c2f836b%7C686ea1d3bc2b4c6fa92cd99c5c30 1635%7C0%7C0%7C637079231443969244&sdata=gvOKFn6Rt7sgbmrbMo Vq2cawyetW5z6H50Qhv0aX0rA%3D&reserved=0
Thanks, -Takahiro Akashi
--
================================================================
===== DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic@denx.de
================================================================
=====

In this commit, implemented are efi_signature_verify_with_db(), efi_signature_parse_sigdb() and a couple of helper functions which will be used for variable authentication as well as image authentication in UEFI secure boot.
efi_signature_verify_with_db() - authenticate an image with its hash value for unsigned image, and with its embedded pkcs7 signature with a given signature store if signed. This function will also be used to validate authentication data in authenticated variables.
efi_signature_parse_sigdb() - parse signature database variable and retrieve signature lists, which may consist of x509 certificates or message digests (SHA256 only for now).
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_api.h | 47 +++ include/efi_loader.h | 47 +++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_signature.c | 602 +++++++++++++++++++++++++++++++++ 4 files changed, 697 insertions(+) create mode 100644 lib/efi_loader/efi_signature.c
diff --git a/include/efi_api.h b/include/efi_api.h index 9f49a4575e07..72999f762515 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -18,6 +18,7 @@
#include <efi.h> #include <charset.h> +#include <pe.h>
#ifdef CONFIG_EFI_LOADER #include <asm/setjmp.h> @@ -307,6 +308,10 @@ struct efi_runtime_services { EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \ 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
+#define EFI_IMAGE_SECURITY_DATABASE_GUID \ + EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, \ + 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) + #define EFI_FDT_GUID \ EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \ 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) @@ -1597,4 +1602,46 @@ struct efi_unicode_collation_protocol { #define LOAD_OPTION_CATEGORY_BOOT 0x00000000 #define LOAD_OPTION_CATEGORY_APP 0x00000100
+/* Secure boot */ +#define EFI_CERT_SHA256_GUID \ + EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, \ + 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) +#define EFI_CERT_RSA2048_GUID \ + EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, \ + 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6) +#define EFI_CERT_X509_GUID \ + EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, \ + 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) +#define EFI_CERT_X509_SHA256_GUID \ + EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \ + 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed) +#define EFI_CERT_TYPE_PKCS7_GUID \ + EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \ + 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7) + +struct win_certificate_uefi_guid { + WIN_CERTIFICATE hdr; + efi_guid_t cert_type; + u8 cert_data[]; +} __attribute__((__packed__)); + +struct efi_variable_authentication_2 { + struct efi_time time_stamp; + struct win_certificate_uefi_guid auth_info; +} __attribute__((__packed__)); + +struct efi_signature_data { + efi_guid_t signature_owner; + u8 signature_data[]; +} __attribute__((__packed__)); + +struct efi_signature_list { + efi_guid_t signature_type; + u32 signature_list_size; + u32 signature_header_size; + u32 signature_size; +/* u8 signature_header[signature_header_size]; */ +/* struct efi_signature_data signatures[...][signature_size]; */ +} __attribute__((__packed__)); + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 5298ea7997f7..c75ee5fcb6ba 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -16,6 +16,7 @@ #if CONFIG_IS_ENABLED(EFI_LOADER)
#include <linux/list.h> +#include <linux/oid_registry.h>
/* Maximum number of configuration tables */ #define EFI_MAX_CONFIGURATION_TABLES 16 @@ -156,6 +157,11 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol; extern const efi_guid_t efi_guid_hii_config_access_protocol; extern const efi_guid_t efi_guid_hii_database_protocol; extern const efi_guid_t efi_guid_hii_string_protocol; +/* GUID for authentication */ +extern const efi_guid_t efi_guid_image_security_database; +extern const efi_guid_t efi_guid_sha256; +extern const efi_guid_t efi_guid_cert_x509; +extern const efi_guid_t efi_guid_cert_x509_sha256;
extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; @@ -654,6 +660,47 @@ void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data); unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data); efi_status_t efi_bootmgr_load(efi_handle_t *handle);
+#ifdef CONFIG_EFI_SECURE_BOOT +#include <image.h> + +#define EFI_REGS_MAX 16 /* currently good enough */ + +typedef struct { + int num; + struct image_region reg[EFI_REGS_MAX]; +} efi_image_regions; + +struct efi_sig_data { + struct efi_sig_data *next; + efi_guid_t owner; + void *data; + size_t size; +}; + +typedef struct efi_signature_store { + struct efi_signature_store *next; + efi_guid_t sig_type; + struct efi_sig_data *sig_data_list; +} efi_signature_store; + +struct pkcs7_message; + +bool efi_signature_verify_with_db(efi_image_regions *regs, + struct pkcs7_message *msg, + efi_signature_store *trusted); +bool efi_signature_revoke(efi_image_regions *regs, + struct pkcs7_message *msg, + efi_signature_store *untrusted, + efi_signature_store *tsa); + +efi_status_t efi_image_region_add(efi_image_regions *ctx, + const void *start, const void *end, + int nocheck); + +void efi_sigstore_free(efi_signature_store *ctx); +efi_signature_store *efi_sigstore_parse_sigdb(u16 *name); +#endif /* CONFIG_EFI_SECURE_BOOT */ + #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 01769ea58ba6..49c996c89052 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o +obj-y += efi_signature.o diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c new file mode 100644 index 000000000000..55a335cc44ae --- /dev/null +++ b/lib/efi_loader/efi_signature.c @@ -0,0 +1,602 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Patrick Wildt patrick@blueri.se + * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro + */ + +#include <charset.h> +#include <efi_loader.h> +#include <image.h> +#include <hexdump.h> +#include <malloc.h> +#include <pe.h> +#include <linux/compat.h> +#include <linux/oid_registry.h> +#include <u-boot/rsa.h> +#include <u-boot/sha256.h> +/* + * avoid duplicated inclusion: + * #include "../lib/crypto/x509_parser.h" + */ +#include "../lib/crypto/pkcs7_parser.h" + +const efi_guid_t efi_guid_image_security_database = + EFI_IMAGE_SECURITY_DATABASE_GUID; +const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID; +const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID; +const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID; +const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID; + +#ifdef CONFIG_EFI_SECURE_BOOT +/* TODO: generalized for other hash algos */ +static const unsigned char WinIndirectSha256[] = { + 0x30, 0x33, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, + 0x01, 0x0f, 0x30, 0x25, 0x03, 0x01, 0x00, 0xa0, 0x20, 0xa2, 0x1e, 0x80, + 0x1c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62, 0x00, + 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 +}; + +static bool efi_hash_regions(efi_image_regions *regs, void **hash, size_t *size) +{ + *size = 0; + *hash = calloc(1, SHA256_SUM_LEN); + if (!*hash) { + debug("Out of memory\n"); + return false; + } + *size = SHA256_SUM_LEN; + + hash_calculate("sha256", regs->reg, regs->num, *hash); +#ifdef DEBUG + debug("hash calculated:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + *hash, SHA256_SUM_LEN, false); +#endif + + return true; +} + +static bool efi_hash_regions_in_der(efi_image_regions *regs, void **hash, + size_t *size) +{ + void *msg; + size_t msg_size; + struct image_region regtmp[2]; + + if (!efi_hash_regions(regs, &msg, &msg_size)) { + debug("Hash calculation failed\n"); + return false; + ; + } + + *size = 0; + *hash = calloc(1, SHA256_SUM_LEN); + if (!*hash) { + debug("Out of memory\n"); + free(msg); + return false; + } + *size = SHA256_SUM_LEN; + + /* File image hash is digested with some DER wrapper. */ + regtmp[0].data = WinIndirectSha256; + regtmp[0].size = sizeof(WinIndirectSha256); + regtmp[1].data = msg; + regtmp[1].size = msg_size; + + hash_calculate("sha256", regtmp, 2, *hash); +#ifdef DEBUG + debug("hash calculated in der:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + *hash, SHA256_SUM_LEN, false); +#endif + + free(msg); + + return true; +} + +static bool efi_signature_verify(efi_image_regions *regs, + struct pkcs7_signed_info *ps_info, + struct x509_certificate *cert) +{ + struct image_sign_info info; + struct image_region regtmp[2]; + void *hash; + size_t size; + char c; + bool verified; + + debug("%s: Enter, %p, %p, %p(issuer: %s, subject: %s)\n", __func__, + regs, ps_info, cert, cert->issuer, cert->subject); + + verified = false; + + memset(&info, '\0', sizeof(info)); + info.padding = image_get_padding_algo("pkcs-1.5"); + /* + * Note: image_get_[checksum|crypto]_algo takes an string + * argument like "<checksum>,<crypto>" + */ + if (!strcmp(ps_info->sig->hash_algo, "sha1")) { + info.checksum = image_get_checksum_algo("sha1,rsa2048"); + info.name = "sha1,rsa2048"; + } else if (!strcmp(ps_info->sig->hash_algo, "sha256")) { + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.name = "sha256,rsa2048"; + } else { + debug("unknown msg digest algo: %s\n", ps_info->sig->hash_algo); + goto out; + } + info.crypto = image_get_crypto_algo(info.name); + + info.key = cert->pub->key; + info.keylen = cert->pub->keylen; + + /* verify signature */ + debug("%s: crypto: %s, signature len:%x\n", __func__, + info.name, ps_info->sig->s_size); + if (ps_info->authattrs_len) { + debug("%s: RSA verify authentication attribute\n", __func__); + /* + * NOTE: This path will be executed only for + * PE image authentication + */ + + /* check if hash matches digest first */ + debug("checking msg digest first, len:0x%x\n", + ps_info->msgdigest_len); + + if (efi_hash_regions_in_der(regs, &hash, &size)) { + if (ps_info->msgdigest_len != size || + memcmp(hash, ps_info->msgdigest, size)) { + debug("Digest doesn't match\n"); + free(hash); + goto out; + } + + free(hash); + } else { + debug("Digesting image failed\n"); + goto out; + } + + /* against digest */ + c = 0x31; + regtmp[0].data = &c; + regtmp[0].size = 1; + regtmp[1].data = ps_info->authattrs; + regtmp[1].size = ps_info->authattrs_len; + + if (!rsa_verify(&info, regtmp, 2, + ps_info->sig->s, ps_info->sig->s_size)) + verified = true; + } else { + debug("%s: RSA verify content data\n", __func__); + /* against all data */ + if (!rsa_verify(&info, regs->reg, regs->num, + ps_info->sig->s, ps_info->sig->s_size)) + verified = true; + } + +out: + debug("%s: Exit, verified: %d\n", __func__, verified); + return verified; +} + +static +bool efi_signature_verify_with_list(efi_image_regions *regs, + struct pkcs7_signed_info *signed_info, + efi_signature_store *siglist) +{ + struct x509_certificate *cert; + struct efi_sig_data *sig_data; + bool verified = false; + + debug("%s: Enter, %p, %p, %p\n", __func__, regs, signed_info, siglist); + + if (!signed_info) { + void *hash; + size_t size; + + debug("%s: unsigned image\n", __func__); + /* verify based on calculated hash value */ + if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) { + debug("Digest algorithm is not supported: %pUl\n", + &siglist->sig_type); + goto out; + } + + /* TODO: other than CERT_SHA256 */ + if (!efi_hash_regions(regs, &hash, &size)) { + debug("Digesting unsigned image failed\n"); + goto out; + } + + /* go through the list */ + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { +#ifdef DEBUG + debug("Msg digest in database:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + sig_data->data, sig_data->size, false); +#endif + if ((sig_data->size == size) && + !memcmp(sig_data->data, hash, size)) { + verified = true; + free(hash); + goto out; + } + } + free(hash); + goto out; + } + + debug("%s: signed image\n", __func__); + if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) { + debug("Signature type is not supported: %pUl\n", + &siglist->sig_type); + goto out; + } + + /* go through the list */ + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { + /* TODO: owner check by policy? */ + + cert = x509_cert_parse(sig_data->data, sig_data->size); + if (IS_ERR(cert)) { + debug("Parsing x509 certificate failed\n"); + goto out; + } + + verified = efi_signature_verify(regs, signed_info, cert); + x509_free_certificate(cert); + + if (verified) + break; + } + +out: + debug("%s: Exit, verified: %d\n", __func__, verified); + return verified; +} + +bool efi_signature_verify_with_db(efi_image_regions *regs, + struct pkcs7_message *msg, + efi_signature_store *trusted) +{ + struct pkcs7_signed_info *info; + efi_signature_store *siglist; + bool verified = false; + + if (!trusted) + goto out; + + if (!trusted->sig_data_list) + goto out; + + /* for unsigned image */ + if (!msg) { + for (siglist = trusted; siglist; siglist = siglist->next) + if (efi_signature_verify_with_list(regs, NULL, + siglist)) { + verified = true; + goto out; + } + + goto out; + } + + /* signed image or variable */ + for (info = msg->signed_infos; info; info = info->next) { + debug("Signed Info: digest algo: %s, pkey algo: %s\n", + info->sig->hash_algo, info->sig->pkey_algo); + + for (siglist = trusted; siglist; siglist = siglist->next) { + if (efi_signature_verify_with_list(regs, info, + siglist)) { + verified = true; + goto out; + } + } + } + +out: + return verified; +} + +/* TODO: TSA support */ +bool efi_signature_revoke(efi_image_regions *regs, + struct pkcs7_message *msg, + efi_signature_store *untrusted, + efi_signature_store *tsa) +{ + struct pkcs7_signed_info *info; + efi_signature_store *siglist; + bool rejected = false; + + if (!untrusted) + goto out; + + if (!untrusted->sig_data_list) + goto out; + + for (info = msg->signed_infos; info; info = info->next) { + debug("Signed Info: digest algo: %s, pkey algo: %s\n", + info->sig->hash_algo, info->sig->pkey_algo); + + for (siglist = untrusted; siglist; siglist = siglist->next) { + if (efi_signature_verify_with_list(regs, info, + siglist)) { + rejected = true; + goto out; + } + } + } + +out: + return rejected; +} + +/* + * Image region helper. With this it's easier to record what parts + * of an image should be checksummed and then do the checksumming + * later depending on the hash. + */ +efi_status_t efi_image_region_add(efi_image_regions *ctx, + const void *start, const void *end, + int nocheck) +{ + struct image_region *reg; + int i, j; + + if (ctx->num >= EFI_REGS_MAX) { + debug("%s: no more room for regions\n", __func__); + return EFI_OUT_OF_RESOURCES; + } + + if (end < start) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < ctx->num; i++) { + reg = &ctx->reg[i]; + if (nocheck) + continue; + + if (start > reg->data + reg->size) + continue; + + if ((start >= reg->data && start < reg->data + reg->size) || + (end > reg->data && end < reg->data + reg->size)) { + debug("%s: new region already part of another\n", + __func__); + return EFI_INVALID_PARAMETER; + } + + if (start < reg->data && end < reg->data + reg->size) { + for (j = ctx->num - 1; j >= i; j--) + memcpy(&ctx->reg[j], &ctx->reg[j + 1], + sizeof(*reg)); + break; + } + } + + reg = &ctx->reg[i]; + reg->data = start; + reg->size = end - start; + ctx->num++; + + return EFI_SUCCESS; +} + +void efi_sigstore_free(efi_signature_store *sigstore) +{ + efi_signature_store *sigstore_next; + struct efi_sig_data *sig_data, *sig_data_next; + + while (sigstore) { + sigstore_next = sigstore->next; + + /* TODO: more structured data? */ + sig_data = sigstore->sig_data_list; + while (sig_data) { + if (sig_data) + sig_data_next = sig_data->next; + free(sig_data->data); + free(sig_data); + sig_data = sig_data_next; + } + + free(sigstore); + sigstore = sigstore_next; + } +} + +static +efi_signature_store *efi_sigstore_parse_siglist(struct efi_signature_list *esl) +{ + efi_signature_store *sigstore = NULL; + struct efi_sig_data *sig_data, *sig_data_next; + struct efi_signature_data *esd; + size_t left; + + /* + * UEFI specification defines certificate types: + * for non-signed images, + * EFI_CERT_SHA256_GUID + * EFI_CERT_RSA2048_GUID + * EFI_CERT_RSA2048_SHA256_GUID + * EFI_CERT_SHA1_GUID + * EFI_CERT_RSA2048_SHA_GUID + * EFI_CERT_SHA224_GUID + * EFI_CERT_SHA384_GUID + * EFI_CERT_SHA512_GUID + * + * for signed images, + * EFI_CERT_X509_GUID + * NOTE: Each certificate will normally be in a separate + * EFI_SIGNATURE_LIST as the size may vary depending on + * its algo's. + * + * for timestamp revocation of certificate, + * EFI_CERT_X509_SHA512_GUID + * EFI_CERT_X509_SHA256_GUID + * EFI_CERT_X509_SHA384_GUID + */ + + if (esl->signature_list_size + <= (sizeof(*esl) + esl->signature_header_size)) { + debug("Siglist in wrong format\n"); + return NULL; + } + + /* Create a head */ + sigstore = calloc(sizeof(*sigstore), 1); + if (!sigstore) { + debug("Out of memory\n"); + goto err; + } + memcpy(&sigstore->sig_type, &esl->signature_type, sizeof(efi_guid_t)); + + /* Go through the list */ + sig_data_next = NULL; + left = esl->signature_list_size + - (sizeof(*esl) + esl->signature_header_size); + esd = (struct efi_signature_data *) + ((u8 *)esl + sizeof(*esl) + esl->signature_header_size); + + while ((left > 0) && left >= esl->signature_size) { + /* Signature must exist if there is remaining data. */ + if (left < esl->signature_size) { + debug("Certificate is too small\n"); + goto err; + } + + sig_data = calloc(esl->signature_size + - sizeof(esd->signature_owner), 1); + if (!sig_data) { + debug("Out of memory\n"); + goto err; + } + + /* Append signature data */ + memcpy(&sig_data->owner, &esd->signature_owner, + sizeof(efi_guid_t)); + sig_data->size = esl->signature_size + - sizeof(esd->signature_owner); + sig_data->data = malloc(sig_data->size); + if (!sig_data->data) { + debug("Out of memory\n"); + goto err; + } + memcpy(sig_data->data, esd->signature_data, sig_data->size); + + sig_data->next = sig_data_next; + sig_data_next = sig_data; + + /* Next */ + esd = (struct efi_signature_data *) + ((u8 *)esd + esl->signature_size); + left -= esl->signature_size; + } + sigstore->sig_data_list = sig_data_next; + + return sigstore; + +err: + efi_sigstore_free(sigstore); + + return NULL; +} + +efi_signature_store *efi_sigstore_parse_sigdb(u16 *name) +{ + efi_signature_store *sigstore = NULL, *sigstore_list; + struct efi_signature_list *esl; + const efi_guid_t *vendor; + void *db; + efi_uintn_t db_size; + efi_status_t ret; + + if (!u16_strcmp(name, L"PK") || !u16_strcmp(name, L"KEK")) { + vendor = &efi_global_variable_guid; + } else if (!u16_strcmp(name, L"db") || !u16_strcmp(name, L"dbx")) { + vendor = &efi_guid_image_security_database; + } else { + debug("unknown signature database, %ls\n", name); + return NULL; + } + + /* retrieve variable data */ + db_size = 0; + ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, NULL)); + if (ret == EFI_NOT_FOUND) { + debug("variable, %ls, not found\n", name); + /* + * TODO: + * how should this condition be notified of to caller? + * Returning empty sigstore won't harm anything. + */ + sigstore = calloc(sizeof(*sigstore), 1); + + return sigstore; + } else if (ret != EFI_BUFFER_TOO_SMALL) { + debug("Getting variable, %ls, failed\n", name); + return NULL; + } + + db = malloc(db_size); + if (!db) { + debug("Out of memory\n"); + return NULL; + } + + ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db)); + if (ret != EFI_SUCCESS) { + debug("Getting variable, %ls, failed\n", name); + goto err; + } + + /* Parse siglist list */ + esl = db; + while (db_size > 0) { + /* List must exist if there is remaining data. */ + if (db_size < sizeof(*esl)) { + debug("variable, %ls, in wrong format\n", name); + goto err; + } + + if (db_size < esl->signature_list_size) { + debug("variable, %ls, in wrong format\n", name); + goto err; + } + + /* Parse a single siglist. */ + sigstore_list = efi_sigstore_parse_siglist(esl); + if (!sigstore_list) { + debug("Parsing signature list of %ls failed\n", name); + goto err; + } + + /* Append siglist */ + sigstore_list->next = sigstore; + sigstore = sigstore_list; + + /* Next */ + db_size -= esl->signature_list_size; + esl = (void *)esl + esl->signature_list_size; + } + free(db); + + return sigstore; + +err: + efi_sigstore_free(sigstore); + free(db); + + return NULL; +} +#endif /* CONFIG_EFI_SECURE_BOOT */

With this commit, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is supported for authenticated variables and the system secure state will transfer between setup mode and user mode as UEFI specification section 32.3 describes.
Internally, authentication data is stored as part of authenticated variable's value. It is nothing but a pkcs7 message (but we need some wrapper, see efi_variable_parse_signature()) and will be validated by efi_variable_authenticate(), hence efi_signature_verify_with_db().
Associated time value will be encoded in "{...,time=...}" along with other UEFI variable's attributes.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_loader.h | 4 + lib/efi_loader/efi_variable.c | 855 ++++++++++++++++++++++++++++++---- 2 files changed, 757 insertions(+), 102 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index c75ee5fcb6ba..caac8efba89a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -162,6 +162,7 @@ extern const efi_guid_t efi_guid_image_security_database; extern const efi_guid_t efi_guid_sha256; extern const efi_guid_t efi_guid_cert_x509; extern const efi_guid_t efi_guid_cert_x509_sha256; +extern const efi_guid_t efi_guid_cert_type_pkcs7;
extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; @@ -699,6 +700,9 @@ efi_status_t efi_image_region_add(efi_image_regions *ctx,
void efi_sigstore_free(efi_signature_store *ctx); efi_signature_store *efi_sigstore_parse_sigdb(u16 *name); + +bool efi_secure_boot_enabled(void); +efi_status_t efi_init_secure_boot(void); #endif /* CONFIG_EFI_SECURE_BOOT */
#else /* CONFIG_IS_ENABLED(EFI_LOADER) */ diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 5d1ee50a606e..fa706f1ad6b8 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -11,8 +11,25 @@ #include <efi_loader.h> #include <hexdump.h> #include <env_internal.h> +#include <rtc.h> #include <search.h> #include <uuid.h> +#include <linux/compat.h> +/* + * avoid duplicated inclusion: + * #include "../lib/crypto/x509_parser.h" + */ +#include "../lib/crypto/pkcs7_parser.h" + +enum efi_secure_mode { + EFI_MODE_SETUP, + EFI_MODE_USER, + EFI_MODE_AUDIT, + EFI_MODE_DEPLOYED, +}; + +const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; +static bool efi_secure_boot;
#define READ_ONLY BIT(31)
@@ -109,7 +126,7 @@ static const char *prefix(const char *str, const char *prefix) * @attrp: pointer to UEFI attributes * Return: pointer to remainder of U-Boot variable value */ -static const char *parse_attr(const char *str, u32 *attrp) +static const char *parse_attr(const char *str, u32 *attrp, u64 *timep) { u32 attr = 0; char sep = '{'; @@ -132,6 +149,12 @@ static const char *parse_attr(const char *str, u32 *attrp) attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; } else if ((s = prefix(str, "run"))) { attr |= EFI_VARIABLE_RUNTIME_ACCESS; + } else if ((s = prefix(str, "time="))) { + attr |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + hex2bin((u8 *)timep, s, sizeof(*timep)); + s += sizeof(*timep) * 2; + } else if (*str == '}') { + break; } else { printf("invalid attribute: %s\n", str); break; @@ -148,49 +171,454 @@ static const char *parse_attr(const char *str, u32 *attrp) return str; }
-/** - * efi_get_variable() - retrieve value of a UEFI variable - * - * This function implements the GetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @variable_name: name of the variable - * @vendor: vendor GUID - * @attributes: attributes of the variable - * @data_size: size of the buffer to which the variable value is copied - * @data: buffer to which the variable value is copied - * Return: status code +static efi_status_t efi_set_variable_internal(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check); + +static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode) +{ + u32 attributes; + u8 val; + efi_status_t ret; + + debug("Secure state from %d to %d\n", efi_secure_mode, mode); + + attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + if (mode == EFI_MODE_DEPLOYED) { + val = 1; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + + efi_secure_boot = true; + } else if (mode == EFI_MODE_AUDIT) { + ret = efi_set_variable_internal(L"PK", + &efi_global_variable_guid, + attributes, + 0, NULL, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + + efi_secure_boot = true; + } else if (mode == EFI_MODE_USER) { + val = 1; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + + efi_secure_boot = true; + } else if (mode == EFI_MODE_SETUP) { + val = 0; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + } else { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; + +err: + /* TODO: failsafe */ + printf("ERROR: Secure state transition failed\n"); + return ret; +} + +efi_status_t efi_init_secure_boot(void) +{ + efi_uintn_t size = 0; + efi_status_t ret; + + ret = EFI_CALL(efi_get_variable(L"PK", &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL && IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) + ret = efi_transfer_secure_state(EFI_MODE_USER); + else + ret = efi_transfer_secure_state(EFI_MODE_SETUP); + + return ret; +} + +/* check if secure boot is enabled by trying to read PK */ +bool efi_secure_boot_enabled(void) +{ + return efi_secure_boot; +} + +#ifdef CONFIG_EFI_SECURE_BOOT +static u8 pkcs7_hdr[] = { + /* SEQUENCE */ + 0x30, 0x82, 0x05, 0xc7, + /* OID: pkcs7-signedData */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, + /* Context Structured? */ + 0xa0, 0x82, 0x05, 0xb8, +}; + +/* + * This function is a wrapper for pkcs7_parse_message() now. + * TODO: Any better way? */ -efi_status_t EFIAPI efi_get_variable(u16 *variable_name, - const efi_guid_t *vendor, u32 *attributes, - efi_uintn_t *data_size, void *data) +static struct pkcs7_message *efi_variable_parse_signature(const void *buf, + size_t buflen) +{ + u8 *ebuf; + size_t ebuflen, len; + struct pkcs7_message *msg; + + /* + * This is the best assumption to check if the binary is + * already in a form of pkcs7's signedData. + */ + if (buflen > sizeof(pkcs7_hdr) && + !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) { + msg = pkcs7_parse_message(buf, buflen); + goto out; + } + + /* + * Otherwise, we should add a dummy prefix sequence for pkcs7 + * message parser to be able to process. + * NOTE: EDK2 also uses similar hack in WrapPkcs7Data() + * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c + */ + debug("Fake prefix added to authentication data\n"); + ebuflen = sizeof(pkcs7_hdr) + buflen; + if (ebuflen <= 0x7f) { + debug("Data is too short\n"); + return NULL; + } + + ebuf = malloc(ebuflen); + if (!ebuf) { + debug("Out of memory\n"); + return NULL; + } + + memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr)); + memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen); + len = ebuflen - 4; + ebuf[2] = (len >> 8) & 0xff; + ebuf[3] = len & 0xff; + len = ebuflen - 0x13; + ebuf[0x11] = (len >> 8) & 0xff; + ebuf[0x12] = len & 0xff; + + msg = pkcs7_parse_message(ebuf, ebuflen); + + free(ebuf); + +out: + if (IS_ERR(msg)) + return NULL; + + return msg; +} + +/* + * Called by efi_set_variable() to verify that the input is correct. + * Will replace the given data pointer with another that points to + * the actual data to store in the internal memory. + */ +static efi_status_t efi_variable_authenticate(u16 *variable, + const efi_guid_t *vendor, + efi_uintn_t *data_size, + const void **data, u32 given_attr, + u32 *env_attr, u64 *time) +{ + const struct efi_variable_authentication_2 *auth; + efi_signature_store *truststore, *truststore2; + struct pkcs7_message *var_sig; + efi_image_regions *regs; + const struct efi_time *timestamp; + struct rtc_time tm; + efi_status_t ret; + + var_sig = NULL; + truststore = NULL; + truststore2 = NULL; + regs = NULL; + ret = EFI_SECURITY_VIOLATION; + + if (*data_size < sizeof(struct efi_variable_authentication_2)) + goto err; + + /* authentication data */ + auth = *data; + if (*data_size < (sizeof(auth->time_stamp) + + auth->auth_info.hdr.dwLength)) + goto err; + + if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7)) + goto err; + + *data += sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength; + *data_size -= (sizeof(auth->time_stamp) + + auth->auth_info.hdr.dwLength); + + /* FIXME: What to do when one attr has time but other one does not? */ + timestamp = &auth->time_stamp; + memset(&tm, 0, sizeof(tm)); + tm.tm_year = timestamp->year; + tm.tm_mon = timestamp->month; + tm.tm_mday = timestamp->day; + tm.tm_hour = timestamp->hour; + tm.tm_min = timestamp->minute; + tm.tm_sec = timestamp->second; + + if (!efi_secure_boot_enabled()) { + /* finished checking */ + *time = rtc_mktime(&tm); + return EFI_SUCCESS; + } + + if (rtc_mktime(&tm) <= *time) + goto err; + + /* data to be digested */ + regs = calloc(1, sizeof(*regs)); + if (!regs) + goto err; + efi_image_region_add(regs, (uint8_t *)variable, + (uint8_t *)variable + + u16_strlen(variable) * sizeof(u16), 1); + efi_image_region_add(regs, (uint8_t *)vendor, + (uint8_t *)vendor + sizeof(*vendor), 1); + efi_image_region_add(regs, (uint8_t *)&given_attr, + (uint8_t *)&given_attr + sizeof(given_attr), 1); + efi_image_region_add(regs, (uint8_t *)timestamp, + (uint8_t *)timestamp + sizeof(*timestamp), 1); + efi_image_region_add(regs, (uint8_t *)*data, + (uint8_t *)*data + *data_size, 1); + + /* variable's signature list */ + if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info)) + goto err; + var_sig = efi_variable_parse_signature(auth->auth_info.cert_data, + auth->auth_info.hdr.dwLength + - sizeof(auth->auth_info)); + if (IS_ERR(var_sig)) { + debug("Parsing variable's signature failed\n"); + var_sig = NULL; + goto err; + } + + /* signature database used for authentication */ + if (u16_strcmp(variable, L"PK") == 0 || + u16_strcmp(variable, L"KEK") == 0) { + /* with PK */ + truststore = efi_sigstore_parse_sigdb(L"PK"); + if (!truststore) + goto err; + } else if (u16_strcmp(variable, L"db") == 0 || + u16_strcmp(variable, L"dbx") == 0) { + /* with PK and KEK */ + truststore = efi_sigstore_parse_sigdb(L"KEK"); + truststore2 = efi_sigstore_parse_sigdb(L"PK"); + + if (!truststore) { + if (!truststore2) + goto err; + + truststore = truststore2; + truststore2 = NULL; + } + } else { + /* TODO: what UEFI spec says for other variables? */ + goto err; + } + + /* verify signature */ + if (efi_signature_verify_with_db(regs, var_sig, truststore)) { + debug("Verified\n"); + } else { + if (truststore2) { + if (efi_signature_verify_with_db(regs, var_sig, + truststore2)) { + debug("Verified\n"); + } else { + debug("Verifying variable's signature failed\n"); + goto err; + } + } else { + debug("Verifying variable's signature failed\n"); + goto err; + } + } + + /* finished checking */ + *time = rtc_mktime(&tm); + ret = EFI_SUCCESS; + +err: + efi_sigstore_free(truststore); + efi_sigstore_free(truststore2); + pkcs7_free_message(var_sig); + free(regs); + + return ret; +} +#else +static efi_status_t efi_variable_authenticate(u16 *variable, + const efi_guid_t *vendor, + efi_uintn_t *data_size, + const void **data, u32 given_attr, + u32 *env_attr, u64 *time) +{ + return EFI_SUCCESS; +} +#endif /* CONFIG_EFI_SECURE_BOOT */ + +static +efi_status_t EFIAPI efi_get_variable_common(u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, + efi_uintn_t *data_size, void *data, + bool is_non_volatile) { char *native_name; efi_status_t ret; unsigned long in_size; - const char *val, *s; + const char *val = NULL, *s; + u64 time = 0; u32 attr;
- EFI_ENTRY(""%ls" %pUl %p %p %p", variable_name, vendor, attributes, - data_size, data); - if (!variable_name || !vendor || !data_size) return EFI_EXIT(EFI_INVALID_PARAMETER);
ret = efi_to_native(&native_name, variable_name, vendor); if (ret) - return EFI_EXIT(ret); + return ret;
EFI_PRINT("get '%s'\n", native_name);
val = env_get(native_name); free(native_name); if (!val) - return EFI_EXIT(EFI_NOT_FOUND); + return EFI_NOT_FOUND;
- val = parse_attr(val, &attr); + val = parse_attr(val, &attr, &time);
in_size = *data_size;
@@ -199,7 +627,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
/* number of hexadecimal digits must be even */ if (len & 1) - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_DEVICE_ERROR;
/* two characters per byte: */ len /= 2; @@ -210,11 +638,13 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, goto out; }
- if (!data) - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!data) { + debug("Variable with no data shouldn't exist.\n"); + return EFI_INVALID_PARAMETER; + }
if (hex2bin(data, s, len)) - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_DEVICE_ERROR;
EFI_PRINT("got value: "%s"\n", s); } else if ((s = prefix(val, "(utf8)"))) { @@ -227,8 +657,10 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, goto out; }
- if (!data) - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!data) { + debug("Variable with no data shouldn't exist.\n"); + return EFI_INVALID_PARAMETER; + }
memcpy(data, s, len); ((char *)data)[len] = '\0'; @@ -236,13 +668,67 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, EFI_PRINT("got value: "%s"\n", (char *)data); } else { EFI_PRINT("invalid value: '%s'\n", val); - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_DEVICE_ERROR; }
out: if (attributes) *attributes = attr & EFI_VARIABLE_MASK;
+ return ret; +} + +static +efi_status_t EFIAPI efi_get_volatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, + efi_uintn_t *data_size, + void *data) +{ + return efi_get_variable_common(variable_name, vendor, attributes, + data_size, data, false); +} + +efi_status_t EFIAPI efi_get_nonvolatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, + efi_uintn_t *data_size, + void *data) +{ + return efi_get_variable_common(variable_name, vendor, attributes, + data_size, data, true); +} + +/** + * efi_efi_get_variable() - retrieve value of a UEFI variable + * + * This function implements the GetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer to which the variable value is copied + * @data: buffer to which the variable value is copied + * Return: status code + */ +efi_status_t EFIAPI efi_get_variable(u16 *variable_name, + const efi_guid_t *vendor, u32 *attributes, + efi_uintn_t *data_size, void *data) +{ + efi_status_t ret; + + EFI_ENTRY(""%ls" %pUl %p %p %p", variable_name, vendor, attributes, + data_size, data); + + ret = efi_get_volatile_variable(variable_name, vendor, attributes, + data_size, data); + if (ret == EFI_NOT_FOUND) + ret = efi_get_nonvolatile_variable(variable_name, vendor, + attributes, data_size, data); + return EFI_EXIT(ret); }
@@ -275,6 +761,7 @@ static efi_status_t parse_uboot_variable(char *variable, { char *guid, *name, *end, c; unsigned long name_len; + u64 time; u16 *p;
guid = strchr(variable, '_'); @@ -309,7 +796,7 @@ static efi_status_t parse_uboot_variable(char *variable, *(name - 1) = c;
/* attributes */ - parse_attr(end, attributes); + parse_attr(end, attributes, &time);
return EFI_SUCCESS; } @@ -391,7 +878,7 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, list_len = hexport_r(&env_htab, '\n', H_MATCH_REGEX | H_MATCH_KEY, &efi_variables_list, 0, 1, regexlist); - /* 1 indicates that no match was found */ + if (list_len <= 1) return EFI_EXIT(EFI_NOT_FOUND);
@@ -404,132 +891,296 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, return EFI_EXIT(ret); }
-/** - * efi_set_variable() - set value of a UEFI variable - * - * This function implements the SetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @variable_name: name of the variable - * @vendor: vendor GUID - * @attributes: attributes of the variable - * @data_size: size of the buffer with the variable value - * @data: buffer with the variable value - * Return: status code - */ -efi_status_t EFIAPI efi_set_variable(u16 *variable_name, - const efi_guid_t *vendor, u32 attributes, - efi_uintn_t data_size, const void *data) +static +efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check, + bool is_non_volatile) { - char *native_name = NULL, *oval, *val = NULL, *s; - size_t oval_size; - efi_status_t ret = EFI_SUCCESS; + char *native_name = NULL, *odata = NULL, *val = NULL, *s; + efi_uintn_t odata_size; + bool append, delete; + u64 time = 0; u32 attr; + efi_status_t ret = EFI_SUCCESS;
- EFI_ENTRY(""%ls" %pUl %x %zu %p", variable_name, vendor, attributes, - data_size, data); + debug("%s: set '%s'\n", __func__, native_name);
if (!variable_name || !*variable_name || !vendor || ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) && !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) { ret = EFI_INVALID_PARAMETER; - goto out; + goto err; }
ret = efi_to_native(&native_name, variable_name, vendor); if (ret) - goto out; - -#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) - - if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { - /* delete the variable: */ - env_set(native_name, NULL); - ret = EFI_SUCCESS; - goto out; + goto err; + + /* check if a variable exists */ + odata_size = 0; + attr = 0; + ret = EFI_CALL(efi_get_variable(variable_name, vendor, &attr, + &odata_size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + if ((is_non_volatile && !(attr & EFI_VARIABLE_NON_VOLATILE)) || + (!is_non_volatile && (attr & EFI_VARIABLE_NON_VOLATILE))) { + ret = EFI_INVALID_PARAMETER; + goto err; + } }
- oval = env_get(native_name); - if (oval) { - oval = parse_attr(oval, &attr); + append = !!(attributes & EFI_VARIABLE_APPEND_WRITE); + attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE; + delete = (!data_size || !(attributes & + (EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS)));
- if (attr & READ_ONLY) { + /* check attributes */ + if (odata_size) { + if (ro_check && (attr & READ_ONLY)) { ret = EFI_WRITE_PROTECTED; - goto out; + goto err; }
/* attributes won't be changed */ - if (attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) { + if (!delete && + ((ro_check && attr != attributes) || + (!ro_check && ((attr & ~(u32)READ_ONLY) + != (attributes & ~(u32)READ_ONLY))))) { ret = EFI_INVALID_PARAMETER; + goto err; + } + } else { + if (delete || append) { + ret = EFI_NOT_FOUND; + goto err; + } + } + + if (((!u16_strcmp(variable_name, L"PK") || + !u16_strcmp(variable_name, L"KEK")) && + !guidcmp(vendor, &efi_global_variable_guid)) || + ((!u16_strcmp(variable_name, L"db") || + !u16_strcmp(variable_name, L"dbx")) && + !guidcmp(vendor, &efi_guid_image_security_database))) { + /* authentication is mandatory */ + if (!(attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + debug("%ls: AUTHENTICATED_WRITE_ACCESS required\n", + variable_name); + ret = EFI_INVALID_PARAMETER; + goto err; + } + } + + /* authenticate a variable */ + if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) { + if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { + ret = EFI_INVALID_PARAMETER; + goto err; + } + if (attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + ret = efi_variable_authenticate(variable_name, vendor, + &data_size, &data, + attributes, &attr, + &time); + if (ret != EFI_SUCCESS) + goto err; + + /* last chance to check for delete */ + if (!data_size) + delete = true; + } + } else { + if (attributes & + (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + debug("Secure boot is not configured\n"); + ret = EFI_INVALID_PARAMETER; + goto err; + } + } + + /* delete a variable */ + if (delete) { + ret = EFI_SUCCESS; + if (odata_size) { + val = NULL; goto out; } + ret = EFI_SUCCESS; + goto err; /* not error, but nothing to do */ + }
- if (attributes & EFI_VARIABLE_APPEND_WRITE) { - if (!prefix(oval, "(blob)")) { - /* TODO: should support utf8? */ - return EFI_DEVICE_ERROR; - goto out; - } - oval_size = strlen(oval); - } else { - oval_size = 0; + if (append) { + odata = malloc(odata_size); + if (!odata) { + return EFI_OUT_OF_RESOURCES; + goto err; } + ret = EFI_CALL(efi_get_variable(variable_name, vendor, + &attr, &odata_size, odata)); + if (ret != EFI_SUCCESS) + goto err; } else { - oval_size = 0; + odata_size = 0; }
- val = malloc(oval_size + 2 * data_size - + strlen("{ro,run,boot,nv}(blob)") + 1); + val = malloc(2 * odata_size + 2 * data_size + + strlen("{ro,run,boot,nv,time=0123456701234567}(blob)") + + 1); if (!val) { ret = EFI_OUT_OF_RESOURCES; - goto out; + goto err; }
s = val;
- /* store attributes */ - attributes &= (EFI_VARIABLE_NON_VOLATILE | + /* + * store attributes + */ + attributes &= (READ_ONLY | + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS); + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS); s += sprintf(s, "{"); while (attributes) { - u32 attr = 1 << (ffs(attributes) - 1); + attr = 1 << (ffs(attributes) - 1);
- if (attr == EFI_VARIABLE_NON_VOLATILE) + if (attr == READ_ONLY) { + s += sprintf(s, "ro"); + } else if (attr == EFI_VARIABLE_NON_VOLATILE) { s += sprintf(s, "nv"); - else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) + } else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) { s += sprintf(s, "boot"); - else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) + } else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) { s += sprintf(s, "run"); + } else if (attr == + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + s += sprintf(s, "time="); + s = bin2hex(s, (u8 *)&time, sizeof(time)); + }
attributes &= ~attr; if (attributes) s += sprintf(s, ","); } s += sprintf(s, "}"); - - if (oval_size) - /* APPEND_WRITE */ - s += sprintf(s, oval); - else - s += sprintf(s, "(blob)"); + s += sprintf(s, "(blob)");
/* store payload: */ + if (append) + s = bin2hex(s, odata, odata_size); s = bin2hex(s, data, data_size); *s = '\0';
EFI_PRINT("setting: %s=%s\n", native_name, val);
- if (env_set(native_name, val)) +out: + if (env_set(native_name, val)) { ret = EFI_DEVICE_ERROR; + } else { + if ((u16_strcmp(variable_name, L"PK") == 0 && + guidcmp(vendor, &efi_global_variable_guid) == 0)) { + ret = efi_transfer_secure_state( + (delete ? EFI_MODE_SETUP : + EFI_MODE_USER)); + if (ret != EFI_SUCCESS) + goto err; + } + ret = EFI_SUCCESS; + }
-out: +err: free(native_name); + free(odata); free(val);
- return EFI_EXIT(ret); + return ret; +} + +static +efi_status_t EFIAPI efi_set_volatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check) +{ + return efi_set_variable_common(variable_name, vendor, attributes, + data_size, data, ro_check, false); +} + +efi_status_t EFIAPI efi_set_nonvolatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check) +{ + efi_status_t ret; + + ret = efi_set_variable_common(variable_name, vendor, attributes, + data_size, data, ro_check, true); + + return ret; +} + +static efi_status_t efi_set_variable_internal(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check) +{ + efi_status_t ret; + + if (attributes & EFI_VARIABLE_NON_VOLATILE) + ret = efi_set_nonvolatile_variable(variable_name, vendor, + attributes, + data_size, data, ro_check); + else + ret = efi_set_volatile_variable(variable_name, vendor, + attributes, data_size, data, + ro_check); + + return ret; +} + +/** + * efi_set_variable() - set value of a UEFI variable + * + * This function implements the SetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer with the variable value + * @data: buffer with the variable value + * Return: status code + */ +efi_status_t EFIAPI efi_set_variable(u16 *variable_name, + const efi_guid_t *vendor, u32 attributes, + efi_uintn_t data_size, const void *data) +{ + EFI_ENTRY(""%ls" %pUl %x %zu %p", variable_name, vendor, attributes, + data_size, data); + + /* READ_ONLY bit is not part of API */ + attributes &= ~(u32)READ_ONLY; + + return EFI_EXIT(efi_set_variable_internal(variable_name, vendor, + attributes, data_size, data, + true)); }
/**

Those two variables are well defined UEFI variables: SignatureSupport: array of GUIDs representing the type of signatures supported by the platform firmware VendorKeys: whether the system is configured to use only vendor-provided keys or not
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- lib/efi_loader/efi_variable.c | 85 ++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-)
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index fa706f1ad6b8..9b7654bde017 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -28,8 +28,14 @@ enum efi_secure_mode { EFI_MODE_DEPLOYED, };
+static const efi_guid_t signature_types[] = { + /* EFI_CERT_SHA256_GUID, */ + EFI_CERT_X509_GUID, +}; const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; static bool efi_secure_boot; +static int efi_secure_mode; +static u8 efi_vendor_keys;
#define READ_ONLY BIT(31)
@@ -337,6 +343,8 @@ static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode) return EFI_INVALID_PARAMETER; }
+ efi_secure_mode = mode; + return EFI_SUCCESS;
err: @@ -347,16 +355,57 @@ err:
efi_status_t efi_init_secure_boot(void) { - efi_uintn_t size = 0; + enum efi_secure_mode mode; + efi_uintn_t size; efi_status_t ret;
+ ret = efi_set_variable_internal(L"SignatureSupport", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS + | READ_ONLY, + sizeof(signature_types), + &signature_types, + false); + if (ret != EFI_SUCCESS) + goto err; + + /* + * TODO: + * Since there is currently no "platform-specific" installation + * method of Platform Key, we can't say if VendorKeys is 0 or 1 + * precisely. + */ + + size = 0; ret = EFI_CALL(efi_get_variable(L"PK", &efi_global_variable_guid, NULL, &size, NULL)); - if (ret == EFI_BUFFER_TOO_SMALL && IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) - ret = efi_transfer_secure_state(EFI_MODE_USER); - else - ret = efi_transfer_secure_state(EFI_MODE_SETUP); + if (ret == EFI_BUFFER_TOO_SMALL) { + if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) + mode = EFI_MODE_USER; + else + mode = EFI_MODE_SETUP; + + efi_vendor_keys = 0; + } else if (ret == EFI_NOT_FOUND) { + mode = EFI_MODE_SETUP; + efi_vendor_keys = 1; + } else { + goto err; + }
+ ret = efi_transfer_secure_state(mode); + if (ret == EFI_SUCCESS) + ret = efi_set_variable_internal(L"VendorKeys", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS + | READ_ONLY, + sizeof(efi_vendor_keys), + &efi_vendor_keys, + false); + +err: return ret; }
@@ -1086,6 +1135,8 @@ out: if (env_set(native_name, val)) { ret = EFI_DEVICE_ERROR; } else { + bool vendor_keys_modified = false; + if ((u16_strcmp(variable_name, L"PK") == 0 && guidcmp(vendor, &efi_global_variable_guid) == 0)) { ret = efi_transfer_secure_state( @@ -1093,8 +1144,30 @@ out: EFI_MODE_USER)); if (ret != EFI_SUCCESS) goto err; + + if (efi_secure_mode != EFI_MODE_SETUP) + vendor_keys_modified = true; + } else if ((u16_strcmp(variable_name, L"KEK") == 0 && + guidcmp(vendor, &efi_global_variable_guid) == 0)) { + if (efi_secure_mode != EFI_MODE_SETUP) + vendor_keys_modified = true; + } + + /* update VendorKeys */ + if (vendor_keys_modified & efi_vendor_keys) { + efi_vendor_keys = 0; + ret = efi_set_variable_internal( + L"VendorKeys", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS + | READ_ONLY, + sizeof(efi_vendor_keys), + &efi_vendor_keys, + false); + } else { + ret = EFI_SUCCESS; } - ret = EFI_SUCCESS; }
err:

With this commit, image validation can be enforced, as UEFI specification section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
Currently we support * authentication based on db and dbx, so dbx-validated image will always be rejected. * following signature types: EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images) EFI_CERT_X509_GUID (x509 certificate for signed images) Timestamp-based certifcate revocation is not supported here.
Internally, authentication data is stored in one of certificates tables of PE image (See efi_image_parse()) and will be verified by efi_image_authenticate(), hence efi_signature_verify_with_db(), before loading a given image.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_loader.h | 7 +- lib/efi_loader/efi_boottime.c | 2 +- lib/efi_loader/efi_image_loader.c | 364 +++++++++++++++++++++++++++++- 3 files changed, 366 insertions(+), 7 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index caac8efba89a..b0e1a3b7902d 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -11,6 +11,7 @@ #include <common.h> #include <part_efi.h> #include <efi_api.h> +#include <pe.h>
/* No need for efi loader support in SPL */ #if CONFIG_IS_ENABLED(EFI_LOADER) @@ -385,7 +386,8 @@ efi_status_t efi_set_watchdog(unsigned long timeout); /* Called from places to check whether a timer expired */ void efi_timer_check(void); /* PE loader implementation */ -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, + void *efi, size_t efi_size, struct efi_loaded_image *loaded_image_info); /* Called once to store the pristine gd pointer */ void efi_save_gd(void); @@ -703,6 +705,9 @@ efi_signature_store *efi_sigstore_parse_sigdb(u16 *name);
bool efi_secure_boot_enabled(void); efi_status_t efi_init_secure_boot(void); + +bool efi_image_parse(void *efi, size_t len, efi_image_regions **regp, + WIN_CERTIFICATE **auth, size_t *auth_len); #endif /* CONFIG_EFI_SECURE_BOOT */
#else /* CONFIG_IS_ENABLED(EFI_LOADER) */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b9bff894cbba..e27a52493291 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_dp_split_file_path(file_path, &dp, &fp); ret = efi_setup_loaded_image(dp, fp, image_obj, &info); if (ret == EFI_SUCCESS) - ret = efi_load_pe(*image_obj, dest_buffer, info); + ret = efi_load_pe(*image_obj, dest_buffer, source_size, info); if (!source_buffer) /* Release buffer to which file was loaded */ efi_free_pages((uintptr_t)dest_buffer, diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 13541cfa7a28..3004cb34a3cd 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -9,7 +9,13 @@
#include <common.h> #include <efi_loader.h> +#include <malloc.h> #include <pe.h> +/* + * avoid duplicated inclusion: + * #include "../lib/crypto/x509_parser.h" + */ +#include "../lib/crypto/pkcs7_parser.h"
const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; @@ -205,6 +211,288 @@ static void efi_set_code_and_data_type( } }
+#ifdef CONFIG_EFI_SECURE_BOOT +/* + * Parse image binary in PE32(+) format + * Assuming that sanity of PE image has been checked by a caller. + * + * Return: true on success, false on failure + */ +bool efi_image_parse(void *efi, size_t len, efi_image_regions **regp, + WIN_CERTIFICATE **auth, size_t *auth_len) +{ + efi_image_regions *regs; + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS32 *nt; + IMAGE_SECTION_HEADER *sections; + int num_sections, i; + int ctidx = IMAGE_DIRECTORY_ENTRY_CERTTABLE; + u32 align, size, authsz, authoff; + size_t bytes_hashed; + + regs = calloc(1, sizeof(*regs)); + if (!regs) + goto err; + + dos = (void *)efi; + nt = (void *)(efi + dos->e_lfanew); + + /* + * Collect data regions for hash calculation + * 1. File headers + */ + if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + IMAGE_NT_HEADERS64 *nt64 = (void *)nt; + IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; + + /* Skip CheckSum */ + efi_image_region_add(regs, efi, &opt->CheckSum, 0); + if (nt64->OptionalHeader.NumberOfRvaAndSizes <= ctidx) { + efi_image_region_add(regs, + &opt->CheckSum + 1, + efi + opt->SizeOfHeaders, 0); + } else { + /* Skip Certificates Table */ + efi_image_region_add(regs, + &opt->CheckSum + 1, + &opt->DataDirectory[ctidx], 0); + efi_image_region_add(regs, + &opt->DataDirectory[ctidx] + 1, + efi + opt->SizeOfHeaders, 0); + } + + bytes_hashed = opt->SizeOfHeaders; + align = opt->FileAlignment; + authoff = opt->DataDirectory[ctidx].VirtualAddress; + authsz = opt->DataDirectory[ctidx].Size; + } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; + + efi_image_region_add(regs, efi, &opt->CheckSum, 0); + efi_image_region_add(regs, &opt->CheckSum + 1, + &opt->DataDirectory[ctidx], 0); + efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1, + efi + opt->SizeOfHeaders, 0); + + bytes_hashed = opt->SizeOfHeaders; + align = opt->FileAlignment; + authoff = opt->DataDirectory[ctidx].VirtualAddress; + authsz = opt->DataDirectory[ctidx].Size; + } else { + debug("%s: Invalid optional header magic %x\n", __func__, + nt->OptionalHeader.Magic); + goto err; + } + + /* 2. Sections */ + num_sections = nt->FileHeader.NumberOfSections; + sections = (void *)((uint8_t *)&nt->OptionalHeader + + nt->FileHeader.SizeOfOptionalHeader); + for (i = 0; i < num_sections; i++) { + if (!sections[i].SizeOfRawData) + continue; + + /* TODO: ensure ascending order */ + size = (sections[i].SizeOfRawData + align - 1) & ~(align - 1); + efi_image_region_add(regs, efi + sections[i].PointerToRawData, + efi + sections[i].PointerToRawData + size, + 0); + debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n", + i, sections[i].Name, + sections[i].PointerToRawData, + sections[i].PointerToRawData + size, + sections[i].VirtualAddress, + sections[i].VirtualAddress + + sections[i].Misc.VirtualSize); + + bytes_hashed += size; + } + + /* 3. Extra data excluding Certificates Table */ + if (bytes_hashed + authsz < len) { + debug("extra data for hash: %lu\n", + len - (bytes_hashed + authsz)); + efi_image_region_add(regs, efi + bytes_hashed, + efi + len - authsz, 0); + } + + /* Return Certificates Table */ + if (authsz) { + if (len < authoff + authsz) { + debug("%s: Size for auth too large: %u >= %zu\n", + __func__, authsz, len - authoff); + goto err; + } + if (authsz < sizeof(*auth)) { + debug("%s: Size for auth too small: %u < %zu\n", + __func__, authsz, sizeof(*auth)); + goto err; + } + *auth = efi + authoff; + *auth_len = authsz; + debug("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff, authsz); + } else { + *auth = NULL; + *auth_len = 0; + } + + *regp = regs; + + return true; + +err: + free(regs); + + return false; +} + +/* + * Authenticate unsigned image with SHA256 hash + * + * Return: true on success, false on failure + */ +static bool efi_image_unsigned_authenticate(efi_image_regions *regs) +{ + efi_signature_store *db; + bool ret; + + /* try black-list */ + ret = true; + db = efi_sigstore_parse_sigdb(L"dbx"); + if (db) { + if (efi_signature_verify_with_db(regs, NULL, db)) { + debug("Image is not signed and rejected by "dbx"\n"); + ret = false; + } + + efi_sigstore_free(db); + } else { + debug("Getting signature database(dbx) failed\n"); + } + + if (!ret) + goto out; + + /* try white-list */ + ret = false; + db = efi_sigstore_parse_sigdb(L"db"); + if (db) { + if (efi_signature_verify_with_db(regs, NULL, db)) + ret = true; + else + debug("Image is not signed and not found in "db" or "dbx"\n"); + + efi_sigstore_free(db); + } else { + debug("Getting signature database(db) failed\n"); + } + +out: + return ret; +} + +/* + * TODO: + * When AuditMode==0, if the image's signature is not found in + * the authorized database, or is found in the forbidden database, + * the image will not be started and instead, information about it + * will be placed in this table. + * When AuditMode==1, an EFI_IMAGE_EXECUTION_INFO element is created + * in the EFI_IMAGE_EXECUTION_INFO_TABLE for every certificate found + * in the certificate table of every image that is validated. + * + * Return: true on success, false on failure + */ +static bool efi_image_authenticate(void *efi, size_t len) +{ + efi_image_regions *regs = NULL; + WIN_CERTIFICATE *wincerts = NULL, *wincert; + size_t wincerts_len; + struct pkcs7_message *msg = NULL; + efi_signature_store *db = NULL, *dbx = NULL; + bool ret = false; + + if (!efi_secure_boot_enabled()) + return true; + + if (!efi_image_parse(efi, len, ®s, &wincerts, + &wincerts_len)) { + debug("Parsing PE executable image failed\n"); + return false; + } + + if (!wincerts) { + /* The image is not signed */ + ret = efi_image_unsigned_authenticate(regs); + free(regs); + + return ret; + } + + /* + * verify signature using db and dbx + */ + db = efi_sigstore_parse_sigdb(L"db"); + if (!db) { + debug("Getting signature database(db) failed\n"); + goto err; + } + + dbx = efi_sigstore_parse_sigdb(L"dbx"); + if (!dbx) { + /* FIXME: error or not found? */ + debug("Getting signature database(dbx) failed\n"); + goto err; + } + + /* go through WIN_CERTIFICATE list */ + for (wincert = wincerts; + (void *)wincert < (void *)wincerts + wincerts_len; + wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) { + if (wincert->dwLength < sizeof(*wincert)) { + debug("%s: dwLength too small: %u < %zu\n", + __func__, wincert->dwLength, sizeof(*wincert)); + goto err; + } + msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert), + wincert->dwLength - sizeof(*wincert)); + if (!msg) { + debug("Parsing image's signature failed\n"); + goto err; + } + + /* try black-list first */ + if (dbx) { + if (efi_signature_revoke(regs, msg, dbx, NULL)) { + debug("Signature was revoked by "dbx"\n"); + goto err; + } + } + + /* try white-list */ + if (db) { + if (!efi_signature_verify_with_db(regs, msg, db)) + debug("Verifying signature with "db" failed\n"); + else + ret = true; + } + } + +err: + efi_sigstore_free(db); + efi_sigstore_free(dbx); + pkcs7_free_message(msg); + free(regs); + + return ret; +} +#else +static bool efi_image_authenticate(void *efi, size_t len) +{ + return true; +} +#endif /* CONFIG_EFI_SECURE_BOOT */ + /** * efi_load_pe() - relocate EFI binary * @@ -216,7 +504,8 @@ static void efi_set_code_and_data_type( * @loaded_image_info: loaded image protocol * Return: status code */ -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, + void *efi, size_t efi_size, struct efi_loaded_image *loaded_image_info) { IMAGE_NT_HEADERS32 *nt; @@ -232,15 +521,56 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, unsigned long virt_size = 0; int supported = 0;
+ /* + * Size must be 8-byte aligned and the trailing bytes must be + * zero'ed. Otherwise hash value may be incorrect. + */ + if (efi_size & 0x7) { + void *new_efi; + size_t new_efi_size; + + new_efi_size = (efi_size + 0x7) & ~0x7ULL; + new_efi = calloc(new_efi_size, 1); + if (!new_efi) + return EFI_OUT_OF_RESOURCES; + memcpy(new_efi, efi, efi_size); + efi = new_efi; + efi_size = new_efi_size; + /* TODO: free */ + } + + /* Sanity check for a file header */ + if (efi_size < sizeof(*dos)) { + printf("%s: Truncated DOS Header\n", __func__); + + return EFI_LOAD_ERROR; + } + dos = efi; if (dos->e_magic != IMAGE_DOS_SIGNATURE) { printf("%s: Invalid DOS Signature\n", __func__); + + return EFI_LOAD_ERROR; + } + + /* assume sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64) */ + if (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) { + printf("%s: Invalid offset for Extended Header\n", __func__); + return EFI_LOAD_ERROR; }
nt = (void *) ((char *)efi + dos->e_lfanew); + if ((nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && + (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64))) { + printf("%s: Invalid offset for Extended Header\n", __func__); + + return EFI_LOAD_ERROR; + } + if (nt->Signature != IMAGE_NT_SIGNATURE) { printf("%s: Invalid NT Signature\n", __func__); + return EFI_LOAD_ERROR; }
@@ -253,14 +583,32 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!supported) { printf("%s: Machine type 0x%04x is not supported\n", __func__, nt->FileHeader.Machine); + return EFI_LOAD_ERROR; }
- /* Calculate upper virtual address boundary */ num_sections = nt->FileHeader.NumberOfSections; sections = (void *)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader;
+ if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections + - efi)) { + printf("%s: Invalid number of sections: %d\n", + __func__, num_sections); + + return EFI_LOAD_ERROR; + } + + /* + * Authenticate an image + * TODO: + * If authentication fails, relevant information must be + * recorded in executable information table. + */ + if (!efi_image_authenticate(efi, efi_size)) + return EFI_ACCESS_DENIED; + + /* Calculate upper virtual address boundary */ for (i = num_sections - 1; i >= 0; i--) { IMAGE_SECTION_HEADER *sec = §ions[i]; virt_size = max_t(unsigned long, virt_size, @@ -279,6 +627,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!efi_reloc) { printf("%s: Could not allocate %lu bytes\n", __func__, virt_size); + return EFI_OUT_OF_RESOURCES; } handle->entry = efi_reloc + opt->AddressOfEntryPoint; @@ -295,6 +644,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!efi_reloc) { printf("%s: Could not allocate %lu bytes\n", __func__, virt_size); + return EFI_OUT_OF_RESOURCES; } handle->entry = efi_reloc + opt->AddressOfEntryPoint; @@ -304,13 +654,16 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, } else { printf("%s: Invalid optional header magic %x\n", __func__, nt->OptionalHeader.Magic); + return EFI_LOAD_ERROR; }
/* Copy PE headers */ - memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt) - + nt->FileHeader.SizeOfOptionalHeader - + num_sections * sizeof(IMAGE_SECTION_HEADER)); + memcpy(efi_reloc, efi, + sizeof(*dos) + + sizeof(*nt) + + nt->FileHeader.SizeOfOptionalHeader + + num_sections * sizeof(IMAGE_SECTION_HEADER));
/* Load sections into RAM */ for (i = num_sections - 1; i >= 0; i--) { @@ -327,6 +680,7 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, (unsigned long)image_base) != EFI_SUCCESS) { efi_free_pages((uintptr_t) efi_reloc, (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT); + return EFI_LOAD_ERROR; }

We call efi_init_secure_boot() after loading saved UEFI variables so that the initial secure boot status will be initialized.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- lib/efi_loader/efi_setup.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 0d915ba386da..de8f0bd7e68e 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -132,6 +132,11 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out;
+ /* Secure boot */ + ret = efi_init_secure_boot(); + if (ret != EFI_SUCCESS) + goto out; + /* Indicate supported runtime services */ ret = efi_init_runtime_supported(); if (ret != EFI_SUCCESS)

Now we can enable UEFI secure boot with this option.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- lib/efi_loader/Kconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c7027a967653..fb66766d2b7a 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -115,4 +115,17 @@ config EFI_GRUB_ARM32_WORKAROUND GRUB prior to version 2.04 requires U-Boot to disable caches. This workaround currently is also needed on systems with caches that cannot be managed via CP15. + +config EFI_SECURE_BOOT + bool "Enable EFI secure boot support" + depends on EFI_LOADER + depends on SECURE_BOOT + imply RSA_VERIFY_WITH_PKEY + default n + help + Select this option to enable EFI secure boot support. + Once SecureBoot mode is enforced, any EFI binary can run only if + it is signed with a trusted key. To do that, you need to install, + at least, PK, KEK and db. + endif

Any signature database variable is associated with a specific guid. For convenience, if user doesn't supply any guid info, "env set|print -e" should complement it.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- cmd/nvedit_efi.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c index 4532124c33d0..a4dbf37f3f2e 100644 --- a/cmd/nvedit_efi.c +++ b/cmd/nvedit_efi.c @@ -41,6 +41,11 @@ static const struct { } efi_guid_text[] = { /* signature database */ {EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"}, + {EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"}, + /* certificate type */ + {EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"}, + {EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"}, + {EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"}, };
/* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */ @@ -135,6 +140,7 @@ static int efi_dump_vars(int argc, char * const argv[], bool verbose) { u16 *var_name16, *p; efi_uintn_t buf_size, size; + efi_guid_t *guid;
buf_size = 128; var_name16 = malloc(buf_size); @@ -156,9 +162,13 @@ static int efi_dump_vars(int argc, char * const argv[], bool verbose) p = var_name16; utf8_utf16_strcpy(&p, argv[0]);
- efi_dump_single_var(var_name16, - (efi_guid_t *)&efi_global_variable_guid, - verbose); + if (!strcmp(argv[0], "db") || !strcmp(argv[0], "dbx") || + !strcmp(argv[0], "dbt")) + guid = (efi_guid_t *)&efi_guid_image_security_database; + else + guid = (efi_guid_t *)&efi_global_variable_guid; + + efi_dump_single_var(var_name16, guid, verbose); }
free(var_name16); @@ -167,7 +177,7 @@ static int efi_dump_vars(int argc, char * const argv[], bool verbose) }
/** - * efi_dump_vars() - show information about all the UEFI variables + * efi_dump_var_all() - show information about all the UEFI variables * * @verbose: if true, dump data * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE @@ -463,9 +473,9 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (*ep != ',') return CMD_RET_USAGE;
+ /* 0 should be allowed for delete */ size = simple_strtoul(++ep, NULL, 16); - if (!size) - return CMD_RET_FAILURE; + value_on_memory = true; } else if (!strcmp(argv[0], "-v")) { verbose = true; @@ -477,8 +487,13 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_USAGE;
var_name = argv[0]; - if (default_guid) - guid = efi_global_variable_guid; + if (default_guid) { + if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") || + !strcmp(var_name, "dbt")) + guid = efi_guid_image_security_database; + else + guid = efi_global_variable_guid; + }
if (verbose) { printf("GUID: %s\n", efi_guid_to_str(&guid));

test_efi_secboot/test_signed.py: provide test cases of image authentication for signed images test_efi_secboot/test_unsigned.py: provide test cases of image authentication for unsigned images
This test relies on efitools and user should compile it on his own. (Currently some local change has been applied.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- test/py/tests/test_efi_secboot/conftest.py | 168 ++++++++++++++++++ test/py/tests/test_efi_secboot/defs.py | 7 + test/py/tests/test_efi_secboot/test_signed.py | 97 ++++++++++ .../tests/test_efi_secboot/test_unsigned.py | 126 +++++++++++++ 4 files changed, 398 insertions(+) create mode 100644 test/py/tests/test_efi_secboot/conftest.py create mode 100644 test/py/tests/test_efi_secboot/defs.py create mode 100644 test/py/tests/test_efi_secboot/test_signed.py create mode 100644 test/py/tests/test_efi_secboot/test_unsigned.py
diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py new file mode 100644 index 000000000000..3806f305a138 --- /dev/null +++ b/test/py/tests/test_efi_secboot/conftest.py @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org + +import os +import os.path +import pytest +import re +from subprocess import call, check_call, check_output, CalledProcessError +from defs import * + +# +# Helper functions +# +def mk_fs(config, fs_type, size, id): + """Create a file system volume. + + Args: + fs_type: File system type. + size: Size of file system in MiB. + id: Prefix string of volume's file name. + + Return: + Nothing. + """ + fs_img = '%s.%s.img' % (id, fs_type) + fs_img = config.persistent_data_dir + '/' + fs_img + + if fs_type == 'fat16': + mkfs_opt = '-F 16' + elif fs_type == 'fat32': + mkfs_opt = '-F 32' + elif fs_type == 'ext4': + mkfs_opt = '-O ^metadata_csum' + else: + mkfs_opt = '' + + if re.match('fat', fs_type): + fs_lnxtype = 'vfat' + else: + fs_lnxtype = fs_type + + count = (size + 1048576 - 1) / 1048576 + + try: + check_call('rm -f %s' % fs_img, shell=True) + check_call('dd if=/dev/zero of=%s bs=1M count=%d' + % (fs_img, count), shell=True) + check_call('mkfs.%s %s %s' + % (fs_lnxtype, mkfs_opt, fs_img), shell=True) + return fs_img + except CalledProcessError: + call('rm -f %s' % fs_img, shell=True) + raise + +# from test/py/conftest.py +def tool_is_in_path(tool): + """Check whether a given command is available on host. + + Args: + tool: Command name. + + Return: + True if available, False if not. + """ + for path in os.environ['PATH'].split(os.pathsep): + fn = os.path.join(path, tool) + if os.path.isfile(fn) and os.access(fn, os.X_OK): + return True + return False + +fuse_mounted = False + +def mount_fs(fs_type, device, mount_point): + """Mount a volume. + + Args: + fs_type: File system type. + device: Volume's file name. + mount_point: Mount point. + + Return: + Nothing. + """ + global fuse_mounted + + fuse_mounted = False + try: + if tool_is_in_path('guestmount'): + fuse_mounted = True + check_call('guestmount -a %s -m /dev/sda %s' + % (device, mount_point), shell=True) + else: + mount_opt = 'loop,rw' + if re.match('fat', fs_type): + mount_opt += ',umask=0000' + + check_call('sudo mount -o %s %s %s' + % (mount_opt, device, mount_point), shell=True) + + # may not be effective for some file systems + check_call('sudo chmod a+rw %s' % mount_point, shell=True) + except CalledProcessError: + raise + +def umount_fs(mount_point): + """Unmount a volume. + + Args: + mount_point: Mount point. + + Return: + Nothing. + """ + if fuse_mounted: + call('sync') + call('guestunmount %s' % mount_point, shell=True) + else: + call('sudo umount %s' % mount_point, shell=True) + +# +# Fixture for TestEfiSignedImage test +# +# NOTE: yield_fixture was deprecated since pytest-3.0 +@pytest.yield_fixture() +def efi_boot_env(request, u_boot_config): + """Set up a file system to be used in basic fs test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A fixture for basic fs test, i.e. a triplet of file system type, + volume file name and a list of MD5 hashes. + """ + fs_type = 'fat32' + fs_img = '' + + mount_dir = u_boot_config.persistent_data_dir + '/mnt' + + try: + + # test volume + fs_img = mk_fs(u_boot_config, 'fat32', TEST_VOL_SIZE, 'test') + + # Mount the image so we can populate it. + check_output('mkdir -p %s' % mount_dir, shell=True) + mount_fs(fs_type, fs_img, mount_dir) + + # Copy files + check_output('cp %s/*.efi %s' % (EFITOOLS_PATH, mount_dir), shell=True) + check_output('cp %s/PK* %s' % (EFITOOLS_PATH, mount_dir), shell=True) + check_output('cp %s/noPK* %s' % (EFITOOLS_PATH, mount_dir), shell=True) + check_output('cp %s/KEK* %s' % (EFITOOLS_PATH, mount_dir), shell=True) + check_output('cp %s/DB* %s' % (EFITOOLS_PATH, mount_dir), shell=True) + + umount_fs(mount_dir) + except CalledProcessError as e: + pytest.skip('Setup failed: %s' % e.cmd) + return + else: + yield fs_img + finally: + umount_fs(mount_dir) + call('rmdir %s' % mount_dir, shell=True) + if fs_img: + call('rm -f %s' % fs_img, shell=True) diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py new file mode 100644 index 000000000000..cbe99ef3a6ba --- /dev/null +++ b/test/py/tests/test_efi_secboot/defs.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# 64MiB +TEST_VOL_SIZE=0x4000000 + +# efitools build directory +EFITOOLS_PATH='/home/akashi/x86/efitools' diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py new file mode 100644 index 000000000000..05b193a5266b --- /dev/null +++ b/test/py/tests/test_efi_secboot/test_signed.py @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org +# +# U-Boot UEFI: Signed Image Authentication Test + +""" +This test verifies image authentication for signed images. +""" + +import pytest +import re +from defs import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.slow +class TestEfiSignedImage(object): + def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - authenticated by DB + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, run signed image if no DB/DBX + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 HELLO1 host 0:0 /HelloWorld_simple-signed.efi ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('Hello World!', ''.join(output))) + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, run unsigned image if no DB/DBX + output = u_boot_console.run_command_list([ + 'efidebug boot add 2 HELLO2 host 0:0 /HelloWorld_simple.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search('Hello World!', ''.join(output))) + + with u_boot_console.log.section('Test Case 1c'): + # Test Case 1c, not authenticated by DB + output = u_boot_console.run_command_list([ + 'efidebug boot add 3 UPDV host 0:0 /UpdateVars.efi ""', + 'setenv bootargs "cmd db DB.auth"', + 'efidebug boot next 3', + 'bootefi bootmgr', + 'setenv bootargs "cmd KEK KEK.auth"', + 'efidebug boot next 3', + 'bootefi bootmgr', + 'setenv bootargs "cmd PK PK.auth"', + 'efidebug boot next 3', + 'bootefi bootmgr', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(not re.search('Hello World!', ''.join(output))) + + with u_boot_console.log.section('Test Case 1d'): + # Test Case 1d, authenticated by DB + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('Hello World!', ''.join(output))) + + def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - rejected by DBX + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, rejected by DBX + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 UPDV host 0:0 /UpdateVars-signed.efi ""', + 'setenv bootargs "cmd dbx DB.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd KEK KEK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd PK PK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'efidebug boot add 2 HELLO1 host 0:0 /HelloWorld_simple-signed.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(not re.search('Hello World!', ''.join(output))) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, rejected by DBX even if DB allows + output = u_boot_console.run_command_list([ + 'setenv bootargs "cmd db DB.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(not re.search('Hello World!', ''.join(output))) diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py new file mode 100644 index 000000000000..232793e431b3 --- /dev/null +++ b/test/py/tests/test_efi_secboot/test_unsigned.py @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org +# +# U-Boot UEFI: Signed Image Authentication Test + +""" +This test verifies image authentication for unsigned images. +""" + +import pytest +import re +from defs import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.slow +class TestEfiUnsignedImage(object): + def test_efi_unsigned_image_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - rejected when not hash in DB or DBX + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1'): + # Test Case 1 + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 UPDV host 0:0 /UpdateVars.efi ""', + 'setenv bootargs "cmd KEK KEK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd PK PK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(not re.search(''UPDV' failed', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 2 HELLO1 host 0:0 /HelloWorld_simple.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search(''HELLO1' failed', ''.join(output))) + + def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - authenticated by DB with hash + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2'): + # Test Case 2 + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 UPDV host 0:0 /UpdateVars.efi ""', + 'setenv bootargs "cmd db DB_HWs_hash.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd KEK KEK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd PK PK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(not re.search(''UPDV' failed', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 2 HELLO1 host 0:0 /HelloWorld_simple.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search('Hello World!', ''.join(output))) + + def test_efi_unsigned_image_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - rejected by DBX with hash + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, rejected by DBX + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 UPDV host 0:0 /UpdateVars-signed.efi ""', + 'setenv bootargs "cmd dbx DBX_HWs_hash.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd KEK KEK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd PK PK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(not re.search(''UPDV' failed', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 2 HELLO1 host 0:0 /HelloWorld_simple.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search(''HELLO1' failed', ''.join(output))) + + # TODO: can be merged with auth3 + def test_efi_unsigned_image_auth4(self, u_boot_console, efi_boot_env): + """ + Test Case 4 - rejected by DBX with hash + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 4a'): + # Test Case 4a, rejected by DBX even if DB allows + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 UPDV host 0:0 /UpdateVars-signed.efi ""', + 'setenv bootargs "cmd db DB_HWs_hash.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd dbx DBX_HWs_hash.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd KEK KEK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr', + 'setenv bootargs "cmd PK PK.auth"', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(not re.search(''UPDV' failed', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 2 HELLO1 host 0:0 /HelloWorld_simple.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search(''HELLO1' failed', ''.join(output)))

test_efi_secboot/test_authvar.py: provide test cases of variable authentication
This test relies on efitools and user should compile it on his own. (Currently some local change has been applied.)
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- .../py/tests/test_efi_secboot/test_authvar.py | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 test/py/tests/test_efi_secboot/test_authvar.py
diff --git a/test/py/tests/test_efi_secboot/test_authvar.py b/test/py/tests/test_efi_secboot/test_authvar.py new file mode 100644 index 000000000000..2ae25e8ce7eb --- /dev/null +++ b/test/py/tests/test_efi_secboot/test_authvar.py @@ -0,0 +1,287 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro takahiro.akashi@linaro.org +# +# U-Boot UEFI: Variable Authentication Test + +""" +This test verifies variable authentication +""" + +import pytest +import re +from defs import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.slow +class TestEfiAuthVar(object): + def test_efi_var_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - Install signature database + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, Initial secure state + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'printenv -e -v SecureBoot']) + assert(re.search('00000000: 00', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e -v SecureBoot') + assert('00000000: 00' in output) + output = u_boot_console.run_command( + 'printenv -e -v SetupMode') + assert('00000000: 01' in output) + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, PK without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:0 4000000 PK.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize PK']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 1c'): + # Test Case 1c, install PK + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'printenv -e PK']) + assert(re.search('PK:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e -v SecureBoot') + assert('00000000: 01' in output) + output = u_boot_console.run_command( + 'printenv -e -v SetupMode') + assert('00000000: 00' in output) + + with u_boot_console.log.section('Test Case 1d'): + # Test Case 1d, db/dbx without KEK + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 1e'): + # Test Case 1e, install KEK + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize KEK']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'printenv -e KEK']) + assert(re.search('KEK:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e -v SecureBoot') + assert('00000000: 01' in output) + + with u_boot_console.log.section('Test Case 1f'): + # Test Case 1f, install db + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e db']) + assert(re.search('db:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e -v SecureBoot') + assert('00000000: 01' in output) + + with u_boot_console.log.section('Test Case 1g'): + # Test Case 1g, install dbx + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB1.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize dbx']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB1.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx', + 'printenv -e dbx']) + # FIXME: currently failed + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('dbx:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e -v SecureBoot') + assert('00000000: 01' in output) + + def test_efi_var_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - Update database by overwriting + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:0 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:0 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x31d', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB1.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, update without correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB.esl', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 2c'): + # Test Case 2c, update with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB1.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x31f', ''.join(output))) + + def test_efi_var_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - Append database + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:0 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:0 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x31d', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB1.auth', + 'setenv -e -nv -bs -rt -a -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, update without correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB.esl', + 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 3c'): + # Test Case 3c, update with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 DB1.auth', + 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db', + 'printenv -e db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x63c', ''.join(output))) + + def test_efi_var_auth4(self, u_boot_console, efi_boot_env): + """ + Test Case 4 - Delete database without authentication + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 4a'): + # Test Case 4a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:0 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:0 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x31d', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'setenv -e -nv -bs -rt db', + 'printenv -e db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x31d', ''.join(output))) + + with u_boot_console.log.section('Test Case 4b'): + # Test Case 4b, update without correct signature/data + output = u_boot_console.run_command_list([ + 'setenv -e -nv -bs -rt -at db', + 'printenv -e db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + assert(re.search('DataSize = 0x31d', ''.join(output))) + + def test_efi_var_auth5(self, u_boot_console, efi_boot_env): + """ + Test Case 5 - Uninstall(delete) PK + """ + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 4a'): + # Test Case 5a, Uninstall PK without correct signature + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:0 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:0 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:0 4000000 DB.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('PK:', ''.join(output))) +# assert(re.search('DataSize = 0x31d', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 noPK.esl', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'printenv -e PK']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('PK:', ''.join(output))) + + with u_boot_console.log.section('Test Case 5b'): + # Test Case 5b, Uninstall PK with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:0 4000000 noPK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'printenv -e PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('"PK" not defined', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e -v SecureBoot') + assert('00000000: 00' in output) + output = u_boot_console.run_command( + 'printenv -e -v SetupMode') + assert('00000000: 01' in output)
participants (6)
-
AKASHI Takahiro
-
Heinrich Schuchardt
-
Priyanka Jain
-
Simon Glass
-
Stefano Babic
-
Tom Rini