summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-04-16 16:41:40 -0400
committerTom Rini <trini@konsulko.com>2020-04-16 16:41:40 -0400
commitcf87f7cd8cdb35761103720a102df9bf5b88c1b2 (patch)
tree68dca8e34ab5855a3655d057be09150739b09604
parentf51b4bcf61c9aa7994138a4a417488c1fbdb47cd (diff)
parentb2ace8753d0048487ab6e8955ae9067a6af91559 (diff)
downloadu-boot-WIP/16Apr2020.tar.gz
Merge tag 'efi-2020-07-rc1' of https://gitlab.denx.de/u-boot/custodians/u-boot-efiWIP/16Apr2020
Pull request for UEFI sub-system for efi-2020-07-rc1 This pull request * provides an implementation of UEFI secure booting * fixes a problem with the rsa_mod_exp driver which stops some boards from booting when CONFIG_RSA is enabled which is needed for UEFI secure booting * enables the EFI_RNG_PROTOCOL if DM_RNG is enabled * fixes some function comments
-rw-r--r--.travis.yml11
-rw-r--r--cmd/efidebug.c78
-rw-r--r--cmd/nvedit.c5
-rw-r--r--cmd/nvedit_efi.c23
-rw-r--r--configs/sandbox64_defconfig2
-rw-r--r--configs/sandbox_defconfig3
-rw-r--r--doc/api/efi.rst12
-rw-r--r--doc/uefi/uefi.rst77
-rw-r--r--drivers/crypto/fsl/fsl_rsa.c1
-rw-r--r--drivers/crypto/rsa_mod_exp/mod_exp_sw.c1
-rw-r--r--include/efi_api.h87
-rw-r--r--include/efi_loader.h91
-rw-r--r--lib/efi_loader/Kconfig19
-rw-r--r--lib/efi_loader/Makefile1
-rw-r--r--lib/efi_loader/efi_boottime.c10
-rw-r--r--lib/efi_loader/efi_disk.c52
-rw-r--r--lib/efi_loader/efi_image_loader.c462
-rw-r--r--lib/efi_loader/efi_setup.c38
-rw-r--r--lib/efi_loader/efi_signature.c804
-rw-r--r--lib/efi_loader/efi_unicode_collation.c6
-rw-r--r--lib/efi_loader/efi_variable.c952
-rw-r--r--lib/efi_loader/efi_watchdog.c18
-rw-r--r--test/py/README.md8
-rw-r--r--test/py/tests/test_efi_secboot/conftest.py151
-rw-r--r--test/py/tests/test_efi_secboot/defs.py21
-rw-r--r--test/py/tests/test_efi_secboot/test_authvar.py282
-rw-r--r--test/py/tests/test_efi_secboot/test_signed.py117
-rw-r--r--test/py/tests/test_efi_secboot/test_unsigned.py121
28 files changed, 3306 insertions, 147 deletions
diff --git a/.travis.yml b/.travis.yml
index b3253da13c..de96b0e81d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -41,6 +41,14 @@ addons:
- clang-7
- srecord
- graphviz
+ - coreutils
+ - util-linux
+ - dosfstools
+ - gdisk
+ - mount
+ - mtools
+ - openssl
+ - sbsigntool
install:
# Clone uboot-test-hooks
@@ -57,10 +65,11 @@ install:
- grub-mkimage --prefix="" -o ~/grub_x86.efi -O i386-efi normal echo lsefimmap lsefi lsefisystab efinet tftp minicmd
- grub-mkimage --prefix="" -o ~/grub_x64.efi -O x86_64-efi normal echo lsefimmap lsefi lsefisystab efinet tftp minicmd
- wget http://mirrors.kernel.org/ubuntu/pool/main/m/mpfr4/libmpfr4_3.1.4-1_amd64.deb && sudo dpkg -i libmpfr4_3.1.4-1_amd64.deb && rm libmpfr4_3.1.4-1_amd64.deb
+ - wget http://mirrors.kernel.org/ubuntu/pool/universe/e/efitools/efitools_1.8.1-0ubuntu2_amd64.deb && sudo dpkg -i efitools_1.8.1-0ubuntu2_amd64.deb && rm efitools_1.8.1-0ubuntu2_amd64.deb
env:
global:
- - PATH=/tmp/qemu-install/bin:/tmp/uboot-test-hooks/bin:/usr/bin:/bin:/usr/local/bin
+ - PATH=/tmp/qemu-install/bin:/tmp/uboot-test-hooks/bin:/sbin:/usr/bin:/bin:/usr/local/bin
- PYTHONPATH=/tmp/uboot-test-hooks/py/travis-ci
- BUILD_DIR=build
- HOSTCC="cc"
diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index c1bb76477a..02ef019694 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -1089,6 +1089,78 @@ static int do_efi_boot_opt(cmd_tbl_t *cmdtp, int flag,
return cp->cmd(cmdtp, flag, argc, argv);
}
+/**
+ * do_efi_test_bootmgr() - run simple bootmgr for test
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "test bootmgr" sub-command.
+ * Run simple bootmgr for test.
+ *
+ * efidebug test bootmgr
+ */
+static int do_efi_test_bootmgr(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ efi_handle_t image;
+ efi_uintn_t exit_data_size = 0;
+ u16 *exit_data = NULL;
+ efi_status_t ret;
+
+ ret = efi_bootmgr_load(&image);
+ printf("efi_bootmgr_load() returned: %ld\n", ret & ~EFI_ERROR_MASK);
+
+ /* We call efi_start_image() even if error for test purpose. */
+ ret = EFI_CALL(efi_start_image(image, &exit_data_size, &exit_data));
+ printf("efi_start_image() returned: %ld\n", ret & ~EFI_ERROR_MASK);
+ if (ret && exit_data)
+ efi_free_pool(exit_data);
+
+ efi_restore_gd();
+
+ return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t cmd_efidebug_test_sub[] = {
+ U_BOOT_CMD_MKENT(bootmgr, CONFIG_SYS_MAXARGS, 1, do_efi_test_bootmgr,
+ "", ""),
+};
+
+/**
+ * do_efi_test() - manage UEFI load options
+ *
+ * @cmdtp: Command table
+ * @flag: Command flag
+ * @argc: Number of arguments
+ * @argv: Argument array
+ * Return: CMD_RET_SUCCESS on success,
+ * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "test" sub-command.
+ */
+static int do_efi_test(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ cmd_tbl_t *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ argc--; argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_efidebug_test_sub,
+ ARRAY_SIZE(cmd_efidebug_test_sub));
+ if (!cp)
+ return CMD_RET_USAGE;
+
+ return cp->cmd(cmdtp, flag, argc, argv);
+}
+
static cmd_tbl_t cmd_efidebug_sub[] = {
U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""),
U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices,
@@ -1103,6 +1175,8 @@ static cmd_tbl_t cmd_efidebug_sub[] = {
"", ""),
U_BOOT_CMD_MKENT(tables, CONFIG_SYS_MAXARGS, 1, do_efi_show_tables,
"", ""),
+ U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_efi_test,
+ "", ""),
};
/**
@@ -1172,7 +1246,9 @@ static char efidebug_help_text[] =
"efidebug memmap\n"
" - show UEFI memory map\n"
"efidebug tables\n"
- " - show UEFI configuration tables\n";
+ " - show UEFI configuration tables\n"
+ "efidebug test bootmgr\n"
+ " - run simple bootmgr for test\n";
#endif
U_BOOT_CMD(
diff --git a/cmd/nvedit.c b/cmd/nvedit.c
index 81d94cd193..966c134059 100644
--- a/cmd/nvedit.c
+++ b/cmd/nvedit.c
@@ -1417,7 +1417,7 @@ static char env_help_text[] =
#endif
#endif
#if defined(CONFIG_CMD_NVEDIT_EFI)
- "env set -e [-nv][-bs][-rt][-a][-i addr,size][-v] name [arg ...]\n"
+ "env set -e [-nv][-bs][-rt][-at][-a][-i addr,size][-v] name [arg ...]\n"
" - set UEFI variable; unset if '-i' or 'arg' not specified\n"
#endif
"env set [-f] name [arg ...]\n";
@@ -1479,13 +1479,14 @@ U_BOOT_CMD_COMPLETE(
setenv, CONFIG_SYS_MAXARGS, 0, do_env_set,
"set environment variables",
#if defined(CONFIG_CMD_NVEDIT_EFI)
- "-e [-guid guid][-nv][-bs][-rt][-a][-v]\n"
+ "-e [-guid guid][-nv][-bs][-rt][-at][-a][-v]\n"
" [-i addr,size name], or [name [value ...]]\n"
" - set UEFI variable 'name' to 'value' ...'\n"
" \"-guid\": set vendor guid\n"
" \"-nv\": set non-volatile attribute\n"
" \"-bs\": set boot-service attribute\n"
" \"-rt\": set runtime attribute\n"
+ " \"-at\": set time-based authentication attribute\n"
" \"-a\": append-write\n"
" \"-i addr,size\": use <addr,size> as variable's value\n"
" \"-v\": verbose message\n"
diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
index 8ea0da0128..837e39e021 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" */
@@ -453,7 +458,7 @@ out:
* Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
*
* This function is for "env set -e" or "setenv -e" command:
- * => env set -e [-guid guid][-nv][-bs][-rt][-a][-v]
+ * => env set -e [-guid guid][-nv][-bs][-rt][-at][-a][-v]
* [-i address,size] var, or
* var [value ...]
* Encode values specified and set given UEFI variable.
@@ -512,6 +517,9 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
} else if (!strcmp(argv[0], "-nv")) {
attributes |= EFI_VARIABLE_NON_VOLATILE;
+ } else if (!strcmp(argv[0], "-at")) {
+ attributes |=
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
} else if (!strcmp(argv[0], "-a")) {
attributes |= EFI_VARIABLE_APPEND_WRITE;
} else if (!strcmp(argv[0], "-i")) {
@@ -525,9 +533,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;
@@ -539,8 +547,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((const efi_guid_t *)
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index a6183d9814..9d092330ef 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -29,6 +29,7 @@ CONFIG_CMD_ASKENV=y
CONFIG_CMD_GREPENV=y
CONFIG_CMD_ENV_CALLBACK=y
CONFIG_CMD_ENV_FLAGS=y
+CONFIG_CMD_NVEDIT_EFI=y
CONFIG_LOOPW=y
CONFIG_CMD_MD5SUM=y
CONFIG_CMD_MEMINFO=y
@@ -206,6 +207,7 @@ CONFIG_RSA_VERIFY_WITH_PKEY=y
CONFIG_TPM=y
CONFIG_LZ4=y
CONFIG_ERRNO_STR=y
+CONFIG_EFI_SECURE_BOOT=y
CONFIG_TEST_FDTDEC=y
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index fa72c666c6..cc38315868 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -33,6 +33,7 @@ CONFIG_CMD_ASKENV=y
CONFIG_CMD_GREPENV=y
CONFIG_CMD_ENV_CALLBACK=y
CONFIG_CMD_ENV_FLAGS=y
+CONFIG_CMD_NVEDIT_EFI=y
CONFIG_LOOPW=y
CONFIG_CMD_MD5SUM=y
CONFIG_CMD_MEMINFO=y
@@ -64,6 +65,7 @@ CONFIG_CMD_LINK_LOCAL=y
CONFIG_CMD_ETHSW=y
CONFIG_CMD_BMP=y
CONFIG_CMD_BOOTCOUNT=y
+CONFIG_CMD_EFIDEBUG=y
CONFIG_CMD_TIME=y
CONFIG_CMD_TIMER=y
CONFIG_CMD_SOUND=y
@@ -231,6 +233,7 @@ CONFIG_RSA_VERIFY_WITH_PKEY=y
CONFIG_TPM=y
CONFIG_LZ4=y
CONFIG_ERRNO_STR=y
+CONFIG_EFI_SECURE_BOOT=y
CONFIG_TEST_FDTDEC=y
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
diff --git a/doc/api/efi.rst b/doc/api/efi.rst
index 631c0ceb1d..0667c3aef7 100644
--- a/doc/api/efi.rst
+++ b/doc/api/efi.rst
@@ -78,6 +78,12 @@ Memory services
.. kernel-doc:: lib/efi_loader/efi_memory.c
:internal:
+SetWatchdogTimer service
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: lib/efi_loader/efi_watchdog.c
+ :internal:
+
Runtime services
----------------
@@ -151,3 +157,9 @@ Text IO protocols
.. kernel-doc:: lib/efi_loader/efi_console.c
:internal:
+
+Unicode Collation protocol
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: lib/efi_loader/efi_unicode_collation.c
+ :internal:
diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst
index cfe2d84a4c..a35fbd331c 100644
--- a/doc/uefi/uefi.rst
+++ b/doc/uefi/uefi.rst
@@ -97,6 +97,83 @@ Below you find the output of an example session starting GRUB::
See doc/uImage.FIT/howto.txt for an introduction to FIT images.
+Configuring UEFI secure boot
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+UEFI specification[1] defines a secure way of executing UEFI images
+by verifying a signature (or message digest) of image with certificates.
+This feature on U-Boot is enabled with::
+
+ CONFIG_UEFI_SECURE_BOOT=y
+
+To make the boot sequence safe, you need to establish a chain of trust;
+In UEFI secure boot, you can make it with the UEFI variables, "PK"
+(Platform Key), "KEK" (Key Exchange Keys), "db" (white list database)
+and "dbx" (black list database).
+
+There are many online documents that describe what UEFI secure boot is
+and how it works. Please consult some of them for details.
+
+Here is a simple example that you can follow for your initial attempt
+(Please note that the actual steps would absolutely depend on your system
+and environment.):
+
+1. Install utility commands on your host
+ * openssl
+ * efitools
+ * sbsigntool
+
+2. Create signing keys and key database files on your host
+ for PK::
+
+ $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ \
+ -keyout PK.key -out PK.crt -nodes -days 365
+ $ cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \
+ PK.crt PK.esl;
+ $ sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth
+
+ for KEK::
+
+ $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ \
+ -keyout KEK.key -out KEK.crt -nodes -days 365
+ $ cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \
+ KEK.crt KEK.esl
+ $ sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth
+
+ for db::
+
+ $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ \
+ -keyout db.key -out db.crt -nodes -days 365
+ $ cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \
+ db.crt db.esl
+ $ sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth
+
+ Copy \*.auth to media, say mmc, that is accessible from U-Boot.
+
+3. Sign an image with one key in "db" on your host::
+
+ $ sbsign --key db.key --cert db.crt helloworld.efi
+
+4. Install keys on your board::
+
+ ==> fatload mmc 0:1 <tmpaddr> PK.auth
+ ==> setenv -e -nv -bs -rt -at -i <tmpaddr>,$filesize PK
+ ==> fatload mmc 0:1 <tmpaddr> KEK.auth
+ ==> setenv -e -nv -bs -rt -at -i <tmpaddr>,$filesize KEK
+ ==> fatload mmc 0:1 <tmpaddr> db.auth
+ ==> setenv -e -nv -bs -rt -at -i <tmpaddr>,$filesize db
+
+5. Set up boot parameters on your board::
+
+ ==> efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed ""
+
+Then your board runs that image from Boot manager (See below).
+You can also try this sequence by running Pytest, test_efi_secboot,
+on sandbox::
+
+ $ cd <U-Boot source directory>
+ $ pytest.py test/py/tests/test_efi_secboot/test_signed.py --bd sandbox
+
Executing the boot manager
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/drivers/crypto/fsl/fsl_rsa.c b/drivers/crypto/fsl/fsl_rsa.c
index c5c76e319f..0cb3c6b5f2 100644
--- a/drivers/crypto/fsl/fsl_rsa.c
+++ b/drivers/crypto/fsl/fsl_rsa.c
@@ -52,7 +52,6 @@ U_BOOT_DRIVER(fsl_rsa_mod_exp) = {
.name = "fsl_rsa_mod_exp",
.id = UCLASS_MOD_EXP,
.ops = &fsl_mod_exp_ops,
- .flags = DM_FLAG_PRE_RELOC,
};
U_BOOT_DEVICE(fsl_rsa) = {
diff --git a/drivers/crypto/rsa_mod_exp/mod_exp_sw.c b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c
index 46b9f1825c..c9b571a461 100644
--- a/drivers/crypto/rsa_mod_exp/mod_exp_sw.c
+++ b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c
@@ -31,7 +31,6 @@ U_BOOT_DRIVER(mod_exp_sw) = {
.name = "mod_exp_sw",
.id = UCLASS_MOD_EXP,
.ops = &mod_exp_ops_sw,
- .flags = DM_FLAG_PRE_RELOC,
};
U_BOOT_DEVICE(mod_exp_sw) = {
diff --git a/include/efi_api.h b/include/efi_api.h
index 1c40ffc4f5..77d6bf2660 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>
@@ -329,6 +330,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)
@@ -1682,4 +1687,86 @@ struct efi_load_file_protocol {
#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000
#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000
+/* Certificate types in signature database */
+#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)
+
+/**
+ * win_certificate_uefi_guid - A certificate that encapsulates
+ * a GUID-specific signature
+ *
+ * @hdr: Windows certificate header
+ * @cert_type: Certificate type
+ * @cert_data: Certificate data
+ */
+struct win_certificate_uefi_guid {
+ WIN_CERTIFICATE hdr;
+ efi_guid_t cert_type;
+ u8 cert_data[];
+} __attribute__((__packed__));
+
+/**
+ * efi_variable_authentication_2 - A time-based authentication method
+ * descriptor
+ *
+ * This structure describes an authentication information for
+ * a variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+ * and should be included as part of a variable's value.
+ * Only EFI_CERT_TYPE_PKCS7_GUID is accepted.
+ *
+ * @time_stamp: Descriptor's time stamp
+ * @auth_info: Authentication info
+ */
+struct efi_variable_authentication_2 {
+ struct efi_time time_stamp;
+ struct win_certificate_uefi_guid auth_info;
+} __attribute__((__packed__));
+
+/**
+ * efi_signature_data - A format of signature
+ *
+ * This structure describes a single signature in signature database.
+ *
+ * @signature_owner: Signature owner
+ * @signature_data: Signature data
+ */
+struct efi_signature_data {
+ efi_guid_t signature_owner;
+ u8 signature_data[];
+} __attribute__((__packed__));
+
+/**
+ * efi_signature_list - A format of signature database
+ *
+ * This structure describes a list of signatures with the same type.
+ * An authenticated variable's value is a concatenation of one or more
+ * efi_signature_list's.
+ *
+ * @signature_type: Signature type
+ * @signature_list_size: Size of signature list
+ * @signature_header_size: Size of signature header
+ * @signature_size: Size of signature
+ */
+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 3f2792892f..0ba9a1f702 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>
static inline int guidcmp(const void *g1, const void *g2)
{
@@ -26,6 +27,7 @@ static inline void *guidcpy(void *dst, const void *src)
#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
@@ -178,6 +180,12 @@ 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;
+/* GUIDs 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 const efi_guid_t efi_guid_cert_type_pkcs7;
/* GUID of RNG protocol */
extern const efi_guid_t efi_guid_rng_protocol;
@@ -256,6 +264,11 @@ struct efi_object {
enum efi_object_type type;
};
+enum efi_image_auth_status {
+ EFI_IMAGE_AUTH_FAILED = 0,
+ EFI_IMAGE_AUTH_PASSED,
+};
+
/**
* struct efi_loaded_image_obj - handle of a loaded image
*
@@ -275,6 +288,7 @@ struct efi_loaded_image_obj {
EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
struct efi_system_table *st);
u16 image_type;
+ enum efi_image_auth_status auth_status;
};
/**
@@ -408,7 +422,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);
@@ -680,6 +695,80 @@ 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>
+
+/**
+ * efi_image_regions - A list of memory regions
+ *
+ * @max: Maximum number of regions
+ * @num: Number of regions
+ * @reg: array of regions
+ */
+struct efi_image_regions {
+ int max;
+ int num;
+ struct image_region reg[];
+};
+
+/**
+ * efi_sig_data - A decoded data of struct efi_signature_data
+ *
+ * This structure represents an internal form of signature in
+ * signature database. A listed list may represent a signature list.
+ *
+ * @next: Pointer to next entry
+ * @onwer: Signature owner
+ * @data: Pointer to signature data
+ * @size: Size of signature data
+ */
+struct efi_sig_data {
+ struct efi_sig_data *next;
+ efi_guid_t owner;
+ void *data;
+ size_t size;
+};
+
+/**
+ * efi_signature_store - A decoded data of signature database
+ *
+ * This structure represents an internal form of signature database.
+ *
+ * @next: Pointer to next entry
+ * @sig_type: Signature type
+ * @sig_data_list: Pointer to signature list
+ */
+struct efi_signature_store {
+ struct efi_signature_store *next;
+ efi_guid_t sig_type;
+ struct efi_sig_data *sig_data_list;
+};
+
+struct x509_certificate;
+struct pkcs7_message;
+
+bool efi_signature_verify_cert(struct x509_certificate *cert,
+ struct efi_signature_store *dbx);
+bool efi_signature_verify_signers(struct pkcs7_message *msg,
+ struct efi_signature_store *dbx);
+bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct efi_signature_store *db,
+ struct x509_certificate **cert);
+
+efi_status_t efi_image_region_add(struct efi_image_regions *regs,
+ const void *start, const void *end,
+ int nocheck);
+
+void efi_sigstore_free(struct efi_signature_store *sigstore);
+struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name);
+
+bool efi_secure_boot_enabled(void);
+
+bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
+ WIN_CERTIFICATE **auth, size_t *auth_len);
+#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/Kconfig b/lib/efi_loader/Kconfig
index 9890144d41..1cfa24ffcf 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -126,6 +126,7 @@ config EFI_GRUB_ARM32_WORKAROUND
config EFI_RNG_PROTOCOL
bool "EFI_RNG_PROTOCOL support"
depends on DM_RNG
+ default y
help
Provide a EFI_RNG_PROTOCOL implementation using the hardware random
number generator of the platform.
@@ -145,4 +146,22 @@ config EFI_INITRD_FILESPEC
help
Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
+config EFI_SECURE_BOOT
+ bool "Enable EFI secure boot support"
+ depends on EFI_LOADER
+ select SHA256
+ select RSA
+ select RSA_VERIFY_WITH_PKEY
+ select IMAGE_SIGN_INFO
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select X509_CERTIFICATE_PARSER
+ select PKCS7_MESSAGE_PARSER
+ 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
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 9b3b704473..eff3c25ec3 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -44,3 +44,4 @@ obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o
obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
+obj-y += efi_signature.o
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 3b79a88a48..a3f11eaf62 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1882,12 +1882,12 @@ 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,
efi_size_in_pages(source_size));
- if (ret == EFI_SUCCESS) {
+ if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) {
info->system_table = &systab;
info->parent_handle = parent_image;
} else {
@@ -2885,10 +2885,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+ if (!efi_search_obj(image_handle))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
/* Check parameters */
if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE)
return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED)
+ return EFI_EXIT(EFI_SECURITY_VIOLATION);
+
ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
&info, NULL, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL));
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index fc0682bc48..fd8fe17567 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -108,6 +108,21 @@ static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
return EFI_SUCCESS;
}
+/**
+ * efi_disk_read_blocks() - reads blocks from device
+ *
+ * This function implements the ReadBlocks service of the EFI_BLOCK_IO_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be read from
+ * @lba: starting logical block for reading
+ * @buffer_size: size of the read buffer
+ * @buffer: pointer to the destination buffer
+ * Return: status code
+ */
static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
u32 media_id, u64 lba, efi_uintn_t buffer_size,
void *buffer)
@@ -157,6 +172,22 @@ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
return EFI_EXIT(r);
}
+/**
+ * efi_disk_write_blocks() - writes blocks to device
+ *
+ * This function implements the WriteBlocks service of the
+ * EFI_BLOCK_IO_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be written to
+ * @lba: starting logical block for writing
+ * @buffer_size: size of the write buffer
+ * @buffer: pointer to the source buffer
+ * Return: status code
+ */
static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
u32 media_id, u64 lba, efi_uintn_t buffer_size,
void *buffer)
@@ -208,9 +239,22 @@ static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
return EFI_EXIT(r);
}
+/**
+ * efi_disk_flush_blocks() - flushes modified data to the device
+ *
+ * This function implements the FlushBlocks service of the
+ * EFI_BLOCK_IO_PROTOCOL.
+ *
+ * As we always write synchronously nothing is done here.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * Return: status code
+ */
static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
{
- /* We always write synchronously */
EFI_ENTRY("%p", this);
return EFI_EXIT(EFI_SUCCESS);
}
@@ -286,7 +330,7 @@ static int efi_fs_exists(struct blk_desc *desc, int part)
return 1;
}
-/*
+/**
* efi_disk_add_dev() - create a handle for a partition or disk
*
* @parent: parent handle
@@ -295,6 +339,8 @@ static int efi_fs_exists(struct blk_desc *desc, int part)
* @desc: internal block device
* @dev_index: device index for block device
* @offset: offset into disk for simple partitions
+ * @part: partition
+ * @disk: pointer to receive the created handle
* Return: disk object
*/
static efi_status_t efi_disk_add_dev(
@@ -381,7 +427,7 @@ static efi_status_t efi_disk_add_dev(
* Create handles and protocols for the partitions of a block device.
*
* @parent: handle of the parent disk
- * @blk_desc: block device
+ * @desc: block device
* @if_typename: interface type
* @diskid: device number
* @pdevname: device name
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index d5de6df16d..6c270ce94f 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -10,7 +10,10 @@
#include <common.h>
#include <cpu_func.h>
#include <efi_loader.h>
+#include <malloc.h>
#include <pe.h>
+#include <sort.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;
@@ -206,6 +209,386 @@ static void efi_set_code_and_data_type(
}
}
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * cmp_pe_section - compare two sections
+ * @arg1: Pointer to pointer to first section
+ * @arg2: Pointer to pointer to second section
+ *
+ * Compare two sections in PE image.
+ *
+ * Return: -1, 0, 1 respectively if arg1 < arg2, arg1 == arg2 or
+ * arg1 > arg2
+ */
+static int cmp_pe_section(const void *arg1, const void *arg2)
+{
+ const IMAGE_SECTION_HEADER *section1, *section2;
+
+ section1 = *((const IMAGE_SECTION_HEADER **)arg1);
+ section2 = *((const IMAGE_SECTION_HEADER **)arg2);
+
+ if (section1->VirtualAddress < section2->VirtualAddress)
+ return -1;
+ else if (section1->VirtualAddress == section2->VirtualAddress)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * efi_image_parse - parse a PE image
+ * @efi: Pointer to image
+ * @len: Size of @efi
+ * @regp: Pointer to a list of regions
+ * @auth: Pointer to a pointer to authentication data in PE
+ * @auth_len: Size of @auth
+ *
+ * Parse image binary in PE32(+) format, assuming that sanity of PE image
+ * has been checked by a caller.
+ * On success, an address of authentication data in @efi and its size will
+ * be returned in @auth and @auth_len, respectively.
+ *
+ * Return: true on success, false on error
+ */
+bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
+ WIN_CERTIFICATE **auth, size_t *auth_len)
+{
+ struct efi_image_regions *regs;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_NT_HEADERS32 *nt;
+ IMAGE_SECTION_HEADER *sections, **sorted;
+ int num_regions, num_sections, i;
+ int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY;
+ u32 align, size, authsz, authoff;
+ size_t bytes_hashed;
+
+ dos = (void *)efi;
+ nt = (void *)(efi + dos->e_lfanew);
+
+ /*
+ * Count maximum number of regions to be digested.
+ * We don't have to have an exact number here.
+ * See efi_image_region_add()'s in parsing below.
+ */
+ num_regions = 3; /* for header */
+ num_regions += nt->FileHeader.NumberOfSections;
+ num_regions++; /* for extra */
+
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions,
+ 1);
+ if (!regs)
+ goto err;
+ regs->max = num_regions;
+
+ /*
+ * 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);
+ sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections);
+ if (!sorted) {
+ debug("%s: Out of memory\n", __func__);
+ goto err;
+ }
+
+ /*
+ * Make sure the section list is in ascending order.
+ */
+ for (i = 0; i < num_sections; i++)
+ sorted[i] = &sections[i];
+ qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section);
+
+ for (i = 0; i < num_sections; i++) {
+ if (!sorted[i]->SizeOfRawData)
+ continue;
+
+ size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1);
+ efi_image_region_add(regs, efi + sorted[i]->PointerToRawData,
+ efi + sorted[i]->PointerToRawData + size,
+ 0);
+ debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n",
+ i, sorted[i]->Name,
+ sorted[i]->PointerToRawData,
+ sorted[i]->PointerToRawData + size,
+ sorted[i]->VirtualAddress,
+ sorted[i]->VirtualAddress
+ + sorted[i]->Misc.VirtualSize);
+
+ bytes_hashed += size;
+ }
+ free(sorted);
+
+ /* 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;
+}
+
+/**
+ * efi_image_unsigned_authenticate - authenticate unsigned image with
+ * SHA256 hash
+ * @regs: List of regions to be verified
+ *
+ * If an image is not signed, it doesn't have a signature. In this case,
+ * its message digest is calculated and it will be compared with one of
+ * hash values stored in signature databases.
+ *
+ * Return: true if authenticated, false if not
+ */
+static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
+{
+ struct efi_signature_store *db = NULL, *dbx = NULL;
+ bool ret = false;
+
+ dbx = efi_sigstore_parse_sigdb(L"dbx");
+ if (!dbx) {
+ debug("Getting signature database(dbx) failed\n");
+ goto out;
+ }
+
+ db = efi_sigstore_parse_sigdb(L"db");
+ if (!db) {
+ debug("Getting signature database(db) failed\n");
+ goto out;
+ }
+
+ /* try black-list first */
+ if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) {
+ debug("Image is not signed and rejected by \"dbx\"\n");
+ goto out;
+ }
+
+ /* try white-list */
+ if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL))
+ ret = true;
+ else
+ debug("Image is not signed and not found in \"db\" or \"dbx\"\n");
+
+out:
+ efi_sigstore_free(db);
+ efi_sigstore_free(dbx);
+
+ return ret;
+}
+
+/**
+ * efi_image_authenticate - verify a signature of signed image
+ * @efi: Pointer to image
+ * @efi_size: Size of @efi
+ *
+ * A signed image should have its signature stored in a table of its PE header.
+ * So if an image is signed and only if if its signature is verified using
+ * signature databases, an image is authenticated.
+ * If an image is not signed, its validity is checked by using
+ * efi_image_unsigned_authenticated().
+ * 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 if authenticated, false if not
+ */
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ struct efi_image_regions *regs = NULL;
+ WIN_CERTIFICATE *wincerts = NULL, *wincert;
+ size_t wincerts_len;
+ struct pkcs7_message *msg = NULL;
+ struct efi_signature_store *db = NULL, *dbx = NULL;
+ struct x509_certificate *cert = NULL;
+ void *new_efi = NULL;
+ size_t new_efi_size;
+ bool ret = false;
+
+ if (!efi_secure_boot_enabled())
+ return true;
+
+ /*
+ * Size must be 8-byte aligned and the trailing bytes must be
+ * zero'ed. Otherwise hash value may be incorrect.
+ */
+ if (efi_size & 0x7) {
+ new_efi_size = (efi_size + 0x7) & ~0x7ULL;
+ new_efi = calloc(new_efi_size, 1);
+ if (!new_efi)
+ return false;
+ memcpy(new_efi, efi, efi_size);
+ efi = new_efi;
+ efi_size = new_efi_size;
+ }
+
+ if (!efi_image_parse(efi, efi_size, &regs, &wincerts,
+ &wincerts_len)) {
+ debug("Parsing PE executable image failed\n");
+ goto err;
+ }
+
+ if (!wincerts) {
+ /* The image is not signed */
+ ret = efi_image_unsigned_authenticate(regs);
+
+ goto err;
+ }
+
+ /*
+ * 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) {
+ 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 (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) {
+ debug("Signature was rejected by \"dbx\"\n");
+ goto err;
+ }
+
+ if (!efi_signature_verify_signers(msg, dbx)) {
+ debug("Signer was rejected by \"dbx\"\n");
+ goto err;
+ } else {
+ ret = true;
+ }
+
+ /* try white-list */
+ if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) {
+ debug("Verifying signature with \"db\" failed\n");
+ goto err;
+ } else {
+ ret = true;
+ }
+
+ if (!efi_signature_verify_cert(cert, dbx)) {
+ debug("Certificate was rejected by \"dbx\"\n");
+ goto err;
+ } else {
+ ret = true;
+ }
+ }
+
+err:
+ x509_free_certificate(cert);
+ efi_sigstore_free(db);
+ efi_sigstore_free(dbx);
+ pkcs7_free_message(msg);
+ free(regs);
+ free(new_efi);
+
+ return ret;
+}
+#else
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ return true;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
/**
* efi_load_pe() - relocate EFI binary
*
@@ -214,10 +597,12 @@ static void efi_set_code_and_data_type(
*
* @handle: loaded image handle
* @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
* @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,17 +617,41 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
uint64_t image_base;
unsigned long virt_size = 0;
int supported = 0;
+ efi_status_t ret;
+
+ /* Sanity check for a file header */
+ if (efi_size < sizeof(*dos)) {
+ printf("%s: Truncated DOS Header\n", __func__);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
dos = efi;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
printf("%s: Invalid DOS Signature\n", __func__);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* 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__);
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
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__);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
if (nt->Signature != IMAGE_NT_SIGNATURE) {
printf("%s: Invalid NT Signature\n", __func__);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
for (i = 0; machines[i]; i++)
@@ -254,14 +663,29 @@ 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;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
- /* 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);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* Authenticate an image */
+ if (efi_image_authenticate(efi, efi_size))
+ handle->auth_status = EFI_IMAGE_AUTH_PASSED;
+ else
+ handle->auth_status = EFI_IMAGE_AUTH_FAILED;
+
+ /* Calculate upper virtual address boundary */
for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = &sections[i];
virt_size = max_t(unsigned long, virt_size,
@@ -280,7 +704,8 @@ 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;
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
}
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
@@ -296,7 +721,8 @@ 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;
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
}
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
@@ -305,13 +731,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;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
/* 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--) {
@@ -328,7 +757,8 @@ 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;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
/* Flush cache */
@@ -340,5 +770,11 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
loaded_image_info->image_base = efi_reloc;
loaded_image_info->image_size = virt_size;
- return EFI_SUCCESS;
+ if (handle->auth_status == EFI_IMAGE_AUTH_PASSED)
+ return EFI_SUCCESS;
+ else
+ return EFI_SECURITY_VIOLATION;
+
+err:
+ return ret;
}
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index b458093dfb..1b648c8467 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -82,6 +82,39 @@ out:
return ret;
}
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * efi_init_secure_boot - initialize secure boot state
+ *
+ * Return: EFI_SUCCESS on success, status code (negative) on error
+ */
+static efi_status_t efi_init_secure_boot(void)
+{
+ efi_guid_t signature_types[] = {
+ EFI_CERT_SHA256_GUID,
+ EFI_CERT_X509_GUID,
+ };
+ efi_status_t ret;
+
+ /* TODO: read-only */
+ ret = EFI_CALL(efi_set_variable(L"SignatureSupport",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(signature_types),
+ &signature_types));
+ if (ret != EFI_SUCCESS)
+ printf("EFI: cannot initialize SignatureSupport variable\n");
+
+ return ret;
+}
+#else
+static efi_status_t efi_init_secure_boot(void)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
/**
* efi_init_obj_list() - Initialize and populate EFI object list
*
@@ -127,6 +160,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)
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
new file mode 100644
index 0000000000..658e3547da
--- /dev/null
+++ b/lib/efi_loader/efi_signature.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
+ * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <linux/compat.h>
+#include <linux/oid_registry.h>
+#include <u-boot/rsa.h>
+#include <u-boot/sha256.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
+
+/**
+ * efi_hash_regions - calculate a hash value
+ * @regs: List of regions
+ * @hash: Pointer to a pointer to buffer holding a hash value
+ * @size: Size of buffer to be returned
+ *
+ * Calculate a sha256 value of @regs and return a value in @hash.
+ *
+ * Return: true on success, false on error
+ */
+static bool efi_hash_regions(struct 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;
+}
+
+/**
+ * efi_hash_msg_content - calculate a hash value of contentInfo
+ * @msg: Signature
+ * @hash: Pointer to a pointer to buffer holding a hash value
+ * @size: Size of buffer to be returned
+ *
+ * Calculate a sha256 value of contentInfo in @msg and return a value in @hash.
+ *
+ * Return: true on success, false on error
+ */
+static bool efi_hash_msg_content(struct pkcs7_message *msg, void **hash,
+ size_t *size)
+{
+ struct image_region regtmp;
+
+ *size = 0;
+ *hash = calloc(1, SHA256_SUM_LEN);
+ if (!*hash) {
+ debug("Out of memory\n");
+ free(msg);
+ return false;
+ }
+ *size = SHA256_SUM_LEN;
+
+ regtmp.data = msg->data;
+ regtmp.size = msg->data_len;
+
+ hash_calculate("sha256", &regtmp, 1, *hash);
+#ifdef DEBUG
+ debug("hash calculated based on contentInfo:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ *hash, SHA256_SUM_LEN, false);
+#endif
+
+ return true;
+}
+
+/**
+ * efi_signature_verify - verify a signature with a certificate
+ * @regs: List of regions to be authenticated
+ * @signed_info: Pointer to PKCS7's signed_info
+ * @cert: x509 certificate
+ *
+ * Signature pointed to by @signed_info against image pointed to by @regs
+ * is verified by a certificate pointed to by @cert.
+ * @signed_info holds a signature, including a message digest which is to be
+ * compared with a hash value calculated from @regs.
+ *
+ * Return: true if signature is verified, false if not
+ */
+static bool efi_signature_verify(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ 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>"
+ * TODO: support other hash algorithms
+ */
+ 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->aa_set & (1UL << sinfo_has_message_digest)) {
+ 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);
+
+#ifdef DEBUG
+ debug("hash in database:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ ps_info->msgdigest, ps_info->msgdigest_len,
+ false);
+#endif
+ /* against contentInfo first */
+ if ((msg->data && efi_hash_msg_content(msg, &hash, &size)) ||
+ /* for signed image */
+ efi_hash_regions(regs, &hash, &size)) {
+ /* for authenticated variable */
+ 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;
+}
+
+/**
+ * efi_signature_verify_with_list - verify a signature with signature list
+ * @regs: List of regions to be authenticated
+ * @msg: Signature
+ * @signed_info: Pointer to PKCS7's signed_info
+ * @siglist: Signature list for certificates
+ * @valid_cert: x509 certificate that verifies this signature
+ *
+ * Signature pointed to by @signed_info against image pointed to by @regs
+ * is verified by signature list pointed to by @siglist.
+ * Signature database is a simple concatenation of one or more
+ * signature list(s).
+ *
+ * Return: true if signature is verified, false if not
+ */
+static
+bool efi_signature_verify_with_list(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct pkcs7_signed_info *signed_info,
+ struct efi_signature_store *siglist,
+ struct x509_certificate **valid_cert)
+{
+ struct x509_certificate *cert;
+ struct efi_sig_data *sig_data;
+ bool verified = false;
+
+ debug("%s: Enter, %p, %p, %p, %p\n", __func__,
+ regs, signed_info, siglist, valid_cert);
+
+ if (!signed_info) {
+ void *hash;
+ size_t size;
+
+ debug("%s: unsigned image\n", __func__);
+ /*
+ * verify based on calculated hash value
+ * TODO: support other hash algorithms
+ */
+ if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
+ debug("Digest algorithm is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ 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: support owner check based on 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, msg, signed_info, cert);
+
+ if (verified) {
+ if (valid_cert)
+ *valid_cert = cert;
+ else
+ x509_free_certificate(cert);
+ break;
+ }
+ x509_free_certificate(cert);
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_signature_verify_with_sigdb - verify a signature with db
+ * @regs: List of regions to be authenticated
+ * @msg: Signature
+ * @db: Signature database for trusted certificates
+ * @cert: x509 certificate that verifies this signature
+ *
+ * Signature pointed to by @msg against image pointed to by @regs
+ * is verified by signature database pointed to by @db.
+ *
+ * Return: true if signature is verified, false if not
+ */
+bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct efi_signature_store *db,
+ struct x509_certificate **cert)
+{
+ struct pkcs7_signed_info *info;
+ struct efi_signature_store *siglist;
+ bool verified = false;
+
+ debug("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, cert);
+
+ if (!db)
+ goto out;
+
+ if (!db->sig_data_list)
+ goto out;
+
+ /* for unsigned image */
+ if (!msg) {
+ debug("%s: Verify unsigned image with db\n", __func__);
+ for (siglist = db; siglist; siglist = siglist->next)
+ if (efi_signature_verify_with_list(regs, NULL, NULL,
+ siglist, cert)) {
+ verified = true;
+ goto out;
+ }
+
+ goto out;
+ }
+
+ /* for signed image or variable */
+ debug("%s: Verify signed image with db\n", __func__);
+ 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 = db; siglist; siglist = siglist->next) {
+ if (efi_signature_verify_with_list(regs, msg, info,
+ siglist, cert)) {
+ verified = true;
+ goto out;
+ }
+ }
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_search_siglist - search signature list for a certificate
+ * @cert: x509 certificate
+ * @siglist: Signature list
+ * @revoc_time: Pointer to buffer for revocation time
+ *
+ * Search signature list pointed to by @siglist and find a certificate
+ * pointed to by @cert.
+ * If found, revocation time that is specified in signature database is
+ * returned in @revoc_time.
+ *
+ * Return: true if certificate is found, false if not
+ */
+static bool efi_search_siglist(struct x509_certificate *cert,
+ struct efi_signature_store *siglist,
+ time64_t *revoc_time)
+{
+ struct image_region reg[1];
+ void *hash = NULL, *msg = NULL;
+ struct efi_sig_data *sig_data;
+ bool found = false;
+
+ /* can be null */
+ if (!siglist->sig_data_list)
+ return false;
+
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) {
+ /* TODO: other hash algos */
+ debug("Certificate's digest type is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ /* calculate hash of TBSCertificate */
+ msg = calloc(1, SHA256_SUM_LEN);
+ if (!msg) {
+ debug("Out of memory\n");
+ goto out;
+ }
+
+ hash = calloc(1, SHA256_SUM_LEN);
+ if (!hash) {
+ debug("Out of memory\n");
+ goto out;
+ }
+
+ reg[0].data = cert->tbs;
+ reg[0].size = cert->tbs_size;
+ hash_calculate("sha256", reg, 1, msg);
+
+ /* go through signature list */
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ /*
+ * struct efi_cert_x509_sha256 {
+ * u8 tbs_hash[256/8];
+ * time64_t revocation_time;
+ * };
+ */
+ if ((sig_data->size == SHA256_SUM_LEN) &&
+ !memcmp(sig_data->data, hash, SHA256_SUM_LEN)) {
+ memcpy(revoc_time, sig_data->data + SHA256_SUM_LEN,
+ sizeof(*revoc_time));
+ found = true;
+ goto out;
+ }
+ }
+
+out:
+ free(hash);
+ free(msg);
+
+ return found;
+}
+
+/**
+ * efi_signature_verify_cert - verify a certificate with dbx
+ * @cert: x509 certificate
+ * @dbx: Signature database
+ *
+ * Search signature database pointed to by @dbx and find a certificate
+ * pointed to by @cert.
+ * This function is expected to be used against "dbx".
+ *
+ * Return: true if a certificate is not rejected, false otherwise.
+ */
+bool efi_signature_verify_cert(struct x509_certificate *cert,
+ struct efi_signature_store *dbx)
+{
+ struct efi_signature_store *siglist;
+ time64_t revoc_time;
+ bool found = false;
+
+ debug("%s: Enter, %p, %p\n", __func__, dbx, cert);
+
+ if (!cert)
+ return false;
+
+ for (siglist = dbx; siglist; siglist = siglist->next) {
+ if (efi_search_siglist(cert, siglist, &revoc_time)) {
+ /* TODO */
+ /* compare signing time with revocation time */
+
+ found = true;
+ break;
+ }
+ }
+
+ debug("%s: Exit, verified: %d\n", __func__, !found);
+ return !found;
+}
+
+/**
+ * efi_signature_verify_signers - verify signers' certificates with dbx
+ * @msg: Signature
+ * @dbx: Signature database
+ *
+ * Determine if any of signers' certificates in @msg may be verified
+ * by any of certificates in signature database pointed to by @dbx.
+ * This function is expected to be used against "dbx".
+ *
+ * Return: true if none of certificates is rejected, false otherwise.
+ */
+bool efi_signature_verify_signers(struct pkcs7_message *msg,
+ struct efi_signature_store *dbx)
+{
+ struct pkcs7_signed_info *info;
+ bool found = false;
+
+ debug("%s: Enter, %p, %p\n", __func__, msg, dbx);
+
+ if (!msg)
+ goto out;
+
+ for (info = msg->signed_infos; info; info = info->next) {
+ if (info->signer &&
+ !efi_signature_verify_cert(info->signer, dbx)) {
+ found = true;
+ goto out;
+ }
+ }
+out:
+ debug("%s: Exit, verified: %d\n", __func__, !found);
+ return !found;
+}
+
+/**
+ * efi_image_region_add - add an entry of region
+ * @regs: Pointer to array of regions
+ * @start: Start address of region
+ * @end: End address of region
+ * @nocheck: flag against overlapped regions
+ *
+ * Take one entry of region [@start, @end] and append it to the list
+ * pointed to by @regs. If @nocheck is false, overlapping among entries
+ * will be checked first.
+ *
+ * Return: 0 on success, status code (negative) on error
+ */
+efi_status_t efi_image_region_add(struct efi_image_regions *regs,
+ const void *start, const void *end,
+ int nocheck)
+{
+ struct image_region *reg;
+ int i, j;
+
+ if (regs->num >= 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 < regs->num; i++) {
+ reg = &regs->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 = regs->num - 1; j >= i; j--)
+ memcpy(&regs->reg[j], &regs->reg[j + 1],
+ sizeof(*reg));
+ break;
+ }
+ }
+
+ reg = &regs->reg[i];
+ reg->data = start;
+ reg->size = end - start;
+ regs->num++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_sigstore_free - free signature store
+ * @sigstore: Pointer to signature store structure
+ *
+ * Feee all the memories held in signature store and itself,
+ * which were allocated by efi_sigstore_parse_sigdb().
+ */
+void efi_sigstore_free(struct efi_signature_store *sigstore)
+{
+ struct efi_signature_store *sigstore_next;
+ struct efi_sig_data *sig_data, *sig_data_next;
+
+ while (sigstore) {
+ sigstore_next = sigstore->next;
+
+ sig_data = sigstore->sig_data_list;
+ while (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;
+ }
+}
+
+/**
+ * efi_sigstore_parse_siglist - parse a signature list
+ * @name: Pointer to signature list
+ *
+ * Parse signature list and instantiate a signature store structure.
+ * Signature database is a simple concatenation of one or more
+ * signature list(s).
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+static struct efi_signature_store *
+efi_sigstore_parse_siglist(struct efi_signature_list *esl)
+{
+ struct efi_signature_store *siglist = 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 */
+ siglist = calloc(sizeof(*siglist), 1);
+ if (!siglist) {
+ debug("Out of memory\n");
+ goto err;
+ }
+ memcpy(&siglist->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;
+ }
+ siglist->sig_data_list = sig_data_next;
+
+ return siglist;
+
+err:
+ efi_sigstore_free(siglist);
+
+ return NULL;
+}
+
+/**
+ * efi_sigstore_parse_sigdb - parse a signature database variable
+ * @name: Variable's name
+ *
+ * Read in a value of signature database variable pointed to by
+ * @name, parse it and instantiate a signature store structure.
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
+{
+ struct efi_signature_store *sigstore = NULL, *siglist;
+ 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);
+ 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. */
+ siglist = efi_sigstore_parse_siglist(esl);
+ if (!siglist) {
+ debug("Parsing signature list of %ls failed\n", name);
+ goto err;
+ }
+
+ /* Append siglist */
+ siglist->next = sigstore;
+ sigstore = siglist;
+
+ /* 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 */
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
index c700be8756..6655c68092 100644
--- a/lib/efi_loader/efi_unicode_collation.c
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -169,8 +169,8 @@ static bool metai_match(const u16 *string, const u16 *pattern)
* case-insenitively
*
* @this: unicode collation protocol instance
- * @s: string to compare
- * @p: pattern string
+ * @string: string to compare
+ * @pattern: pattern string
*
* The pattern string may use these:
* - * matches >= 0 characters
@@ -199,7 +199,6 @@ static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
*
* @this: unicode collation protocol instance
* @string: string to convert
- * @p: pattern string
*
* The conversion is done in place. As long as upper and lower letters use the
* same number of words this does not pose a problem.
@@ -221,7 +220,6 @@ static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
*
* @this: unicode collation protocol instance
* @string: string to convert
- * @p: pattern string
*
* The conversion is done in place. As long as upper and lower letters use the
* same number of words this does not pose a problem.
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index fe2f264591..7df881a74b 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -10,8 +10,23 @@
#include <env_internal.h>
#include <hexdump.h>
#include <malloc.h>
+#include <rtc.h>
#include <search.h>
+#include <linux/compat.h>
#include <u-boot/crc.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;
+static int efi_secure_mode;
+static u8 efi_vendor_keys;
#define READ_ONLY BIT(31)
@@ -106,9 +121,10 @@ static const char *prefix(const char *str, const char *prefix)
*
* @str: value of U-Boot variable
* @attrp: pointer to UEFI attributes
+ * @timep: pointer to time attribute
* 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 = '{';
@@ -131,6 +147,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;
@@ -147,49 +169,528 @@ static const char *parse_attr(const char *str, u32 *attrp)
return str;
}
+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_get_variable() - retrieve value of a UEFI variable
+ * efi_transfer_secure_state - handle a secure boot state transition
+ * @mode: new state
*
- * This function implements the GetVariable runtime service.
+ * Depending on @mode, secure boot related variables are updated.
+ * Those variables are *read-only* for users, efi_set_variable_internal()
+ * is called here.
*
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
+ * Return: EFI_SUCCESS on success, status code (negative) on error
+ */
+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;
+ }
+
+ efi_secure_mode = mode;
+
+ return EFI_SUCCESS;
+
+err:
+ /* TODO: What action should be taken here? */
+ printf("ERROR: Secure state transition failed\n");
+ return ret;
+}
+
+/**
+ * efi_init_secure_state - initialize secure boot state
*
- * @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
+ * Return: EFI_SUCCESS on success, status code (negative) on error
*/
-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 efi_status_t efi_init_secure_state(void)
+{
+ enum efi_secure_mode mode;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ /*
+ * 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) {
+ 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;
+}
+
+/**
+ * efi_secure_boot_enabled - return if secure boot is enabled or not
+ *
+ * Return: true if enabled, false if disabled
+ */
+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,
+};
+
+/**
+ * efi_variable_parse_signature - parse a signature in variable
+ * @buf: Pointer to variable's value
+ * @buflen: Length of @buf
+ *
+ * Parse a signature embedded in variable's value and instantiate
+ * a pkcs7_message structure. Since pkcs7_parse_message() accepts only
+ * pkcs7's signedData, some header needed be prepended for correctly
+ * parsing authentication data, particularly for variable's.
+ *
+ * Return: Pointer to pkcs7_message structure on success, NULL on error
+ */
+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
+ * TODO:
+ * The header should be composed in a more refined manner.
+ */
+ debug("Makeshift 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;
+}
+
+/**
+ * efi_variable_authenticate - authenticate a variable
+ * @variable: Variable name in u16
+ * @vendor: Guid of variable
+ * @data_size: Size of @data
+ * @data: Pointer to variable's value
+ * @given_attr: Attributes to be given at SetVariable()
+ * @env_attr: Attributes that an existing variable holds
+ * @time: signed time that an existing variable holds
+ *
+ * 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.
+ * On success, @data and @data_size will be replaced with variable's
+ * actual data, excluding authentication data, and its size, and variable's
+ * attributes and signed time will also be returned in @env_attr and @time,
+ * respectively.
+ *
+ * Return: EFI_SUCCESS on success, status code (negative) on error
+ */
+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;
+ struct efi_signature_store *truststore, *truststore2;
+ struct pkcs7_message *var_sig;
+ struct efi_image_regions *regs;
+ struct efi_time timestamp;
+ struct rtc_time tm;
+ u64 new_time;
+ 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);
+
+ memcpy(&timestamp, &auth->time_stamp, sizeof(timestamp));
+ 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;
+ new_time = rtc_mktime(&tm);
+
+ if (!efi_secure_boot_enabled()) {
+ /* finished checking */
+ *time = new_time;
+ return EFI_SUCCESS;
+ }
+
+ if (new_time <= *time)
+ goto err;
+
+ /* data to be digested */
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 5, 1);
+ if (!regs)
+ goto err;
+ regs->max = 5;
+ 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: support private authenticated variables */
+ goto err;
+ }
+
+ /* verify signature */
+ if (efi_signature_verify_with_sigdb(regs, var_sig, truststore, NULL)) {
+ debug("Verified\n");
+ } else {
+ if (truststore2 &&
+ efi_signature_verify_with_sigdb(regs, var_sig,
+ truststore2, NULL)) {
+ debug("Verified\n");
+ } 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;
@@ -198,7 +699,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;
@@ -209,11 +710,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)"))) {
@@ -226,8 +729,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';
@@ -235,13 +740,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 +834,7 @@ static efi_status_t parse_uboot_variable(char *variable,
char *guid, *name, *end, c;
size_t name_len;
efi_uintn_t old_variable_name_size;
+ u64 time;
u16 *p;
guid = strchr(variable, '_');
@@ -309,7 +869,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 +951,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,143 +964,319 @@ 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, *val = NULL, *s;
- const char *old_val;
- size_t old_size;
- efi_status_t ret = EFI_SUCCESS;
+ char *native_name = NULL, *old_data = NULL, *val = NULL, *s;
+ efi_uintn_t old_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;
+ goto err;
+
+ /* check if a variable exists */
+ old_size = 0;
+ attr = 0;
+ ret = EFI_CALL(efi_get_variable(variable_name, vendor, &attr,
+ &old_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;
+ }
+ }
- old_val = env_get(native_name);
- if (old_val) {
- old_val = parse_attr(old_val, &attr);
+ append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
+ attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE;
+ delete = !append && (!data_size || !attributes);
- /* check read-only first */
- if (attr & READ_ONLY) {
+ /* check attributes */
+ if (old_size) {
+ if (ro_check && (attr & READ_ONLY)) {
ret = EFI_WRITE_PROTECTED;
- goto out;
- }
-
- if ((data_size == 0 &&
- !(attributes & EFI_VARIABLE_APPEND_WRITE)) ||
- !attributes) {
- /* delete the variable: */
- env_set(native_name, NULL);
- ret = EFI_SUCCESS;
- 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 out;
- }
-
- if (attributes & EFI_VARIABLE_APPEND_WRITE) {
- if (!prefix(old_val, "(blob)")) {
- ret = EFI_DEVICE_ERROR;
- goto out;
- }
- old_size = strlen(old_val);
- } else {
- old_size = 0;
+ goto err;
}
} else {
- if (data_size == 0 || !attributes ||
- (attributes & EFI_VARIABLE_APPEND_WRITE)) {
+ if (delete || append) {
/*
* Trying to delete or to update a non-existent
* variable.
*/
ret = EFI_NOT_FOUND;
- goto out;
+ 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) {
+ /* !old_size case has been handled before */
+ val = NULL;
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+ if (append) {
+ old_data = malloc(old_size);
+ if (!old_data) {
+ return EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ ret = EFI_CALL(efi_get_variable(variable_name, vendor,
+ &attr, &old_size, old_data));
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else {
old_size = 0;
}
- val = malloc(old_size + 2 * data_size
- + strlen("{ro,run,boot,nv}(blob)") + 1);
+ val = malloc(2 * old_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 (old_size)
- /* APPEND_WRITE */
- s += sprintf(s, old_val);
- else
- s += sprintf(s, "(blob)");
+ s += sprintf(s, "(blob)");
/* store payload: */
+ if (append)
+ s = bin2hex(s, old_data, old_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 {
+ 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(
+ (delete ? EFI_MODE_SETUP :
+ 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;
+ }
-out:
+ /* 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;
+ }
+ }
+
+err:
free(native_name);
+ free(old_data);
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));
}
/**
@@ -640,5 +1376,9 @@ void efi_variables_boot_exit_notify(void)
*/
efi_status_t efi_init_variables(void)
{
- return EFI_SUCCESS;
+ efi_status_t ret;
+
+ ret = efi_init_secure_state();
+
+ return ret;
}
diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c
index 6f69b76e4d..61ea0f7926 100644
--- a/lib/efi_loader/efi_watchdog.c
+++ b/lib/efi_loader/efi_watchdog.c
@@ -13,7 +13,9 @@
static struct efi_event *watchdog_timer_event;
-/*
+/**
+ * efi_watchdog_timer_notify() - resets system upon watchdog event
+ *
* Reset the system when the watchdog event is notified.
*
* @event: the watchdog event
@@ -31,13 +33,13 @@ static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
EFI_EXIT(EFI_UNSUPPORTED);
}
-/*
- * Reset the watchdog timer.
+/**
+ * efi_set_watchdog() - resets the watchdog timer
*
* This function is used by the SetWatchdogTimer service.
*
* @timeout: seconds before reset by watchdog
- * @return: status code
+ * Return: status code
*/
efi_status_t efi_set_watchdog(unsigned long timeout)
{
@@ -53,10 +55,12 @@ efi_status_t efi_set_watchdog(unsigned long timeout)
return r;
}
-/*
- * Initialize the EFI watchdog.
+/**
+ * efi_watchdog_register() - initializes the EFI watchdog
+ *
+ * This function is called by efi_init_obj_list().
*
- * This function is called by efi_init_obj_list()
+ * Return: status code
*/
efi_status_t efi_watchdog_register(void)
{
diff --git a/test/py/README.md b/test/py/README.md
index 2e5025258d..fddc104b26 100644
--- a/test/py/README.md
+++ b/test/py/README.md
@@ -37,7 +37,15 @@ will be required. The following is an incomplete list:
| openssl |
| sudo OR guestmount |
| e2fsprogs |
+| util-linux |
+| coreutils |
| dosfstools |
+| efitools |
+| mount |
+| mtools |
+| sbsigntool |
+| udisks2 |
+
Please use the apporirate commands for your distribution to match these tools
up with the package that provides them.
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 0000000000..e542fef6e8
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/conftest.py
@@ -0,0 +1,151 @@
+# 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 *
+
+# from test/py/conftest.py
+def tool_is_in_path(tool):
+ 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
+
+#
+# Fixture for UEFI secure boot test
+#
+@pytest.fixture(scope='session')
+def efi_boot_env(request, u_boot_config):
+ """Set up a file system to be used in UEFI secure boot test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ global HELLO_PATH
+
+ image_path = u_boot_config.persistent_data_dir
+ image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME
+ image_size = EFI_SECBOOT_IMAGE_SIZE
+ part_size = EFI_SECBOOT_PART_SIZE
+ fs_type = EFI_SECBOOT_FS_TYPE
+
+ if HELLO_PATH == '':
+ HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi'
+
+ try:
+ non_root = tool_is_in_path('udisksctl')
+
+ # create a disk/partition
+ check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
+ % (image_path, image_size), shell=True)
+ check_call('sgdisk %s -n 1:0:+%dMiB'
+ % (image_path, part_size), shell=True)
+ # create a file system
+ check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
+ % (image_path, part_size), shell=True)
+ check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True)
+ check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
+ % (image_path, image_path, 1), shell=True)
+ check_call('rm %s.tmp' % image_path, shell=True)
+ if non_root:
+ out_data = check_output('udisksctl loop-setup -f %s -o %d'
+ % (image_path, 1048576), shell=True).decode()
+ m = re.search('(?<= as )(.*)\.', out_data)
+ loop_dev = m.group(1)
+ # print 'loop device is: %s' % loop_dev
+ out_data = check_output('udisksctl info -b %s'
+ % loop_dev, shell=True).decode()
+ m = re.search('MountPoints:[ \t]+(.*)', out_data)
+ mnt_point = m.group(1)
+ else:
+ loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+ % (part_size, image_path), shell=True).decode()
+ mnt_point = '/mnt'
+ check_output('sudo mount -t %s -o umask=000 %s %s'
+ % (fs_type, loop_dev, mnt_point), shell=True)
+
+ # print 'mount point is: %s' % mnt_point
+
+ # suffix
+ # *.key: RSA private key in PEM
+ # *.crt: X509 certificate (self-signed) in PEM
+ # *.esl: signature list
+ # *.hash: message digest of image as signature list
+ # *.auth: signed signature list in signature database format
+ # *.efi: UEFI image
+ # *.efi.signed: signed UEFI image
+
+ # Create signature database
+ ## PK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ ## PK_null for deletion
+ check_call('cd %s; sleep 2; touch PK_null.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
+ % (mnt_point, EFITOOLS_PATH), shell=True)
+ ## KEK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ ## db
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ ## db1
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db1.esl db1.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ ## db1-update
+ check_call('cd %s; %ssign-efi-sig-list -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth'
+ % (mnt_point, EFITOOLS_PATH), shell=True)
+ ## dbx
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+
+ # Copy image
+ check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True)
+
+ ## Sign image
+ check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
+ % mnt_point, shell=True)
+ ## Digest image
+ check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
+ % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+ shell=True)
+
+ if non_root:
+ check_call('udisksctl unmount -b %s' % loop_dev, shell=True)
+ # not needed
+ # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True)
+ else:
+ check_call('sudo umount %s' % loop_dev, shell=True)
+ check_call('sudo losetup -d %s' % loop_dev, shell=True)
+
+ except CalledProcessError as e:
+ pytest.skip('Setup failed: %s' % e.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -f %s' % image_path, 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 0000000000..d6222809c5
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/defs.py
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+# Disk image name
+EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img'
+
+# Size in MiB
+EFI_SECBOOT_IMAGE_SIZE=16
+EFI_SECBOOT_PART_SIZE=8
+
+# Partition file system type
+EFI_SECBOOT_FS_TYPE='vfat'
+
+# Owner guid
+GUID='11111111-2222-3333-4444-123456789abc'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+EFITOOLS_PATH=''
+
+# Hello World application for sandbox
+HELLO_PATH=''
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 0000000000..55dcaa95f1
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_authvar.py
@@ -0,0 +1,282 @@
+# 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.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiAuthVar(object):
+ def test_efi_var_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - Install signature database
+ """
+ u_boot_console.restart_uboot()
+ 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 SecureBoot'])
+ assert('00000000: 00' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ 'printenv -e 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([
+ 'fatload host 0:1 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:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'printenv -e -n PK'])
+ assert(re.search('PK:', ''.join(output)))
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert('00000000: 01' in output)
+ output = u_boot_console.run_command(
+ 'printenv -e 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:1 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:1 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:1 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:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'printenv -e -n KEK'])
+ assert(re.search('KEK:', ''.join(output)))
+
+ output = u_boot_console.run_command(
+ 'printenv -e 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:1 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:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ output = u_boot_console.run_command(
+ 'printenv -e 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:1 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:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ output = u_boot_console.run_command(
+ 'printenv -e 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
+ """
+ u_boot_console.restart_uboot()
+ 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:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 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:1 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:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ def test_efi_var_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - Append database
+ """
+ u_boot_console.restart_uboot()
+ 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:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 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:1 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:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ def test_efi_var_auth4(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 4 - Delete database without authentication
+ """
+ u_boot_console.restart_uboot()
+ 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:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'setenv -e -nv -bs -rt db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.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 -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert(re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('db:', ''.join(output)))
+
+ def test_efi_var_auth5(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 5 - Uninstall(delete) PK
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 5a'):
+ # Test Case 5a, Uninstall PK without correct signature
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'printenv -e -n PK'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ assert(re.search('PK:', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK_null.esl',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'printenv -e -n 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:1 4000000 PK_null.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
+ 'printenv -e -n 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 SecureBoot')
+ assert('00000000: 00' in output)
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert('00000000: 01' in output)
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 0000000000..584282b338
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_signed.py
@@ -0,0 +1,117 @@
+# 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.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@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
+ """
+ u_boot_console.restart_uboot()
+ 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:1 /helloworld.efi.signed ""',
+ '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:1 /helloworld.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([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 2',
+ 'bootefi bootmgr'])
+ assert(re.search('\'HELLO2\' failed', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 2',
+ 'efidebug test bootmgr'])
+ assert(re.search('efi_start_image[(][)] returned: 26',
+ ''.join(output)))
+ 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
+ """
+ u_boot_console.restart_uboot()
+ 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,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert(re.search('\'HELLO\' failed', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert(re.search('efi_start_image[(][)] returned: 26',
+ ''.join(output)))
+ 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([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert(re.search('\'HELLO\' failed', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert(re.search('efi_start_image[(][)] returned: 26',
+ ''.join(output)))
+ 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 0000000000..22d849afb8
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_unsigned.py
@@ -0,0 +1,121 @@
+# 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.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@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 digest in db or dbx
+ """
+ u_boot_console.restart_uboot()
+ 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,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert(re.search('\'HELLO\' failed', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert(re.search('efi_start_image[(][)] returned: 26',
+ ''.join(output)))
+ assert(not re.search('Hello, world!', ''.join(output)))
+
+ def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - authenticated by digest in db
+ """
+ u_boot_console.restart_uboot()
+ 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,
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+ 'efidebug boot next 1',
+ '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 digest in dbx
+ """
+ u_boot_console.restart_uboot()
+ 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,
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert(re.search('\'HELLO\' failed', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert(re.search('efi_start_image[(][)] returned: 26',
+ ''.join(output)))
+ assert(not re.search('Hello, world!', ''.join(output)))
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, rejected by dbx even if db allows
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
+ assert(not re.search('Failed to set EFI variable', ''.join(output)))
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert(re.search('\'HELLO\' failed', ''.join(output)))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert(re.search('efi_start_image[(][)] returned: 26',
+ ''.join(output)))
+ assert(not re.search('Hello, world!', ''.join(output)))