Merge tag 'efi-2020-07-rc1' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
authorTom Rini <trini@konsulko.com>
Thu, 16 Apr 2020 20:41:40 +0000 (16:41 -0400)
committerTom Rini <trini@konsulko.com>
Thu, 16 Apr 2020 20:41:40 +0000 (16:41 -0400)
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

28 files changed:
.travis.yml
cmd/efidebug.c
cmd/nvedit.c
cmd/nvedit_efi.c
configs/sandbox64_defconfig
configs/sandbox_defconfig
doc/api/efi.rst
doc/uefi/uefi.rst
drivers/crypto/fsl/fsl_rsa.c
drivers/crypto/rsa_mod_exp/mod_exp_sw.c
include/efi_api.h
include/efi_loader.h
lib/efi_loader/Kconfig
lib/efi_loader/Makefile
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_disk.c
lib/efi_loader/efi_image_loader.c
lib/efi_loader/efi_setup.c
lib/efi_loader/efi_signature.c [new file with mode: 0644]
lib/efi_loader/efi_unicode_collation.c
lib/efi_loader/efi_variable.c
lib/efi_loader/efi_watchdog.c
test/py/README.md
test/py/tests/test_efi_secboot/conftest.py [new file with mode: 0644]
test/py/tests/test_efi_secboot/defs.py [new file with mode: 0644]
test/py/tests/test_efi_secboot/test_authvar.py [new file with mode: 0644]
test/py/tests/test_efi_secboot/test_signed.py [new file with mode: 0644]
test/py/tests/test_efi_secboot/test_unsigned.py [new file with mode: 0644]

index b3253da13c3bcdad8b5f15b65412460ea26a0509..de96b0e81d17f4bef2bb53e3f8ca2bbbd3dee757 100644 (file)
@@ -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"
index c1bb76477a6709a6f159c3294e4be109d6480bcb..02ef019694433fdbad9bd28220fc74a6b24f34a3 100644 (file)
@@ -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(
index 81d94cd193c6dd6c03d4c843cc6cae2a1284575a..966c134059562b90056465ff44802ae672e85691 100644 (file)
@@ -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"
index 8ea0da01283f7f7cf6fd8881c512b205e26cb402..837e39e021798876ddc9e05a230ca619642898c8 100644 (file)
@@ -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 *)
index a6183d9814643d4a16c424533f212dd8861ed431..9d092330efe29e8ff056ba8404131d38c46e5ac3 100644 (file)
@@ -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
index fa72c666c6eb0c686281496715b5ee472e22d50c..cc3831586838f2559fe3ca85914f2b770a4f0297 100644 (file)
@@ -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
index 631c0ceb1df1288824293499317dc8b668d29a28..0667c3aef7824673c1efe9b3b4f7363113c5f3d0 100644 (file)
@@ -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:
index cfe2d84a4c643196fdc0433325ec332db46c6d19..a35fbd331c1fa0ea137f5da49cc7b32c3011c47b 100644 (file)
@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index c5c76e319fa800fba4d6d50fb4dae040ef336862..0cb3c6b5f2f4297c4d491fd583bf7e4f1d93870a 100644 (file)
@@ -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) = {
index 46b9f1825cce94dc00f06fee5799cf6113328958..c9b571a461d0b644f61a9215c8b8d7db4b4d7a87 100644 (file)
@@ -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) = {
index 1c40ffc4f56c02cf661fd8d5757c34788416e47a..77d6bf2660b9f38060f8b5a2db41f45d7d3f7506 100644 (file)
@@ -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
index 3f2792892f34c72208df7795d8e03afa93ce4c72..0ba9a1f702a6224431d64238614c17b89ee1df0a 100644 (file)
@@ -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 */
index 9890144d4161117a4388c132acb0d515a2bcab6d..1cfa24ffcf727eed1e7aa9bb9794f916423541cf 100644 (file)
@@ -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
index 9b3b704473367ac78d5f4d1dc10c3e59b0520e16..eff3c25ec30161feebe46966904bf164dc254330 100644 (file)
@@ -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
index 3b79a88a480f6a7e294c2b1a65beff0b23fa0f4e..a3f11eaf6228db0c925654b6f2912b5a801adcf7 100644 (file)
@@ -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));
index fc0682bc48ca5f2da348e1edf4376d674fb10f43..fd8fe175670989086f28343b607d2dcee8658683 100644 (file)
@@ -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
index d5de6df16d84bdc149a881098549d5f8082d9738..6c270ce94f445feb8a2f038f522773dc6e61544f 100644 (file)
 #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;
 }
index b458093dfbd53adfe51db536020bfacd8a65b2ea..1b648c84673aa1eba9f08a078d3d7a43a947011b 100644 (file)
@@ -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 (file)
index 0000000..658e354
--- /dev/null
@@ -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 */
index c700be875600a43e052bbfe6cf1213605558e3f3..6655c68092e410dc226da389fd035a310b00d5c1 100644 (file)
@@ -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.
index fe2f26459136befd6190879b5b6416aa91610375..7df881a74b4496d24e44e930926c9d0e51ff4508 100644 (file)
 #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;
 }
index 6f69b76e4d94f3644b2d579686b7504afc18260e..61ea0f79260afcc9ad8a81167c496ee1136cb904 100644 (file)
@@ -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)
 {
index 2e5025258d717c97b12ee67b5f3ded94def7873b..fddc104b266fbb3953f1013296ec1488907f0033 100644 (file)
@@ -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 (file)
index 0000000..e542fef
--- /dev/null
@@ -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 (file)
index 0000000..d622280
--- /dev/null
@@ -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 (file)
index 0000000..55dcaa9
--- /dev/null
@@ -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 (file)
index 0000000..584282b
--- /dev/null
@@ -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 (file)
index 0000000..22d849a
--- /dev/null
@@ -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)))