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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEbcT5xx8ppvoGt20zxIHbvCwFGsQFAl6YjlcACgkQxIHbvCwF
 GsQ6Jg/+KVRQF0Dn2jiag8Me9ZhafZx4xGTI3LEzFD7V9kG3dmUzbO3m8a5dEevW
 bcPNA6EmcKAjyP09AZY4C8ns4sU7wzPu/GQvApD4S5+vcuNbEqRHfW6zzReNXq09
 t89DrFH+XLhGnh4uSh0hmfjrlIPVi86hs/f57vKRayrBu8W2yGPci3SNTO5Y0AY1
 to7sxP3hGlDCUhqIABWp5ylpWWUSG1MsVNcMOnHpj+jl0iWWYP46EQrGr95egQor
 gcGgUOLdOLLOHPYUIOBmo9INnS2MbvE9Mfgh5Bw7JW8DIu/Xp7r1qePQyMLsngc1
 pO5yzwMq6EQy6OjuYnXdIBXtHOlZ8rnE9pbTgyUR8T/2CdL2hqr9ddpqa4ryDABX
 iaQF8FG+KhY0XQ5EIJV7pEkvf9TMwGguJNlOkmJPeub2n9i0k1tPk0Rwq5ZWWBIn
 rHcgZXwl0On3O3f0HvfL0Oas5u9KE1HhA12h/EVmu2Muy36x2WxAAzDnsH19HzqU
 QnWBAo8VMyp3qTIZjb3y+qrxcU/joPGt9+hxmur03hzmKOJ9JNOv6vExb+yUIyXQ
 4ML6jvTvSVZbvkP++0zMKOjT8ks3xv5q84oFD5Gp4cG8ZpHpA9dj3mXM92Waepo4
 pAkko5IGQ4SqaGIBltEQzrG+cCiHHezdD/raCwAo6DgYSmfZUvc=
 =+ZDZ
 -----END PGP SIGNATURE-----

Merge tag 'efi-2020-07-rc1' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi

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
This commit is contained in:
Tom Rini 2020-04-16 16:41:40 -04:00
commit cf87f7cd8c
28 changed files with 3389 additions and 230 deletions

View 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"

View 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(

View 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"

View 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 *)

View 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

View 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

View 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:

View 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
~~~~~~~~~~~~~~~~~~~~~~~~~~

View 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) = {

View 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) = {

View 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

View 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 */

View 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

View 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

View 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));

View 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

View File

@ -10,7 +10,10 @@
#include <common.h>
#include <cpu_func.h>
#include <efi_loader.h>
#include <malloc.h>
#include <pe.h>
#include <sort.h>
#include "../lib/crypto/pkcs7_parser.h"
const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
@ -206,6 +209,386 @@ static void efi_set_code_and_data_type(
}
}
#ifdef CONFIG_EFI_SECURE_BOOT
/**
* cmp_pe_section - compare two sections
* @arg1: Pointer to pointer to first section
* @arg2: Pointer to pointer to second section
*
* Compare two sections in PE image.
*
* Return: -1, 0, 1 respectively if arg1 < arg2, arg1 == arg2 or
* arg1 > arg2
*/
static int cmp_pe_section(const void *arg1, const void *arg2)
{
const IMAGE_SECTION_HEADER *section1, *section2;
section1 = *((const IMAGE_SECTION_HEADER **)arg1);
section2 = *((const IMAGE_SECTION_HEADER **)arg2);
if (section1->VirtualAddress < section2->VirtualAddress)
return -1;
else if (section1->VirtualAddress == section2->VirtualAddress)
return 0;
else
return 1;
}
/**
* efi_image_parse - parse a PE image
* @efi: Pointer to image
* @len: Size of @efi
* @regp: Pointer to a list of regions
* @auth: Pointer to a pointer to authentication data in PE
* @auth_len: Size of @auth
*
* Parse image binary in PE32(+) format, assuming that sanity of PE image
* has been checked by a caller.
* On success, an address of authentication data in @efi and its size will
* be returned in @auth and @auth_len, respectively.
*
* Return: true on success, false on error
*/
bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
WIN_CERTIFICATE **auth, size_t *auth_len)
{
struct efi_image_regions *regs;
IMAGE_DOS_HEADER *dos;
IMAGE_NT_HEADERS32 *nt;
IMAGE_SECTION_HEADER *sections, **sorted;
int num_regions, num_sections, i;
int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY;
u32 align, size, authsz, authoff;
size_t bytes_hashed;
dos = (void *)efi;
nt = (void *)(efi + dos->e_lfanew);
/*
* Count maximum number of regions to be digested.
* We don't have to have an exact number here.
* See efi_image_region_add()'s in parsing below.
*/
num_regions = 3; /* for header */
num_regions += nt->FileHeader.NumberOfSections;
num_regions++; /* for extra */
regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions,
1);
if (!regs)
goto err;
regs->max = num_regions;
/*
* Collect data regions for hash calculation
* 1. File headers
*/
if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
/* Skip CheckSum */
efi_image_region_add(regs, efi, &opt->CheckSum, 0);
if (nt64->OptionalHeader.NumberOfRvaAndSizes <= ctidx) {
efi_image_region_add(regs,
&opt->CheckSum + 1,
efi + opt->SizeOfHeaders, 0);
} else {
/* Skip Certificates Table */
efi_image_region_add(regs,
&opt->CheckSum + 1,
&opt->DataDirectory[ctidx], 0);
efi_image_region_add(regs,
&opt->DataDirectory[ctidx] + 1,
efi + opt->SizeOfHeaders, 0);
}
bytes_hashed = opt->SizeOfHeaders;
align = opt->FileAlignment;
authoff = opt->DataDirectory[ctidx].VirtualAddress;
authsz = opt->DataDirectory[ctidx].Size;
} else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
efi_image_region_add(regs, efi, &opt->CheckSum, 0);
efi_image_region_add(regs, &opt->CheckSum + 1,
&opt->DataDirectory[ctidx], 0);
efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1,
efi + opt->SizeOfHeaders, 0);
bytes_hashed = opt->SizeOfHeaders;
align = opt->FileAlignment;
authoff = opt->DataDirectory[ctidx].VirtualAddress;
authsz = opt->DataDirectory[ctidx].Size;
} else {
debug("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
goto err;
}
/* 2. Sections */
num_sections = nt->FileHeader.NumberOfSections;
sections = (void *)((uint8_t *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader);
sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections);
if (!sorted) {
debug("%s: Out of memory\n", __func__);
goto err;
}
/*
* Make sure the section list is in ascending order.
*/
for (i = 0; i < num_sections; i++)
sorted[i] = &sections[i];
qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section);
for (i = 0; i < num_sections; i++) {
if (!sorted[i]->SizeOfRawData)
continue;
size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1);
efi_image_region_add(regs, efi + sorted[i]->PointerToRawData,
efi + sorted[i]->PointerToRawData + size,
0);
debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n",
i, sorted[i]->Name,
sorted[i]->PointerToRawData,
sorted[i]->PointerToRawData + size,
sorted[i]->VirtualAddress,
sorted[i]->VirtualAddress
+ sorted[i]->Misc.VirtualSize);
bytes_hashed += size;
}
free(sorted);
/* 3. Extra data excluding Certificates Table */
if (bytes_hashed + authsz < len) {
debug("extra data for hash: %lu\n",
len - (bytes_hashed + authsz));
efi_image_region_add(regs, efi + bytes_hashed,
efi + len - authsz, 0);
}
/* Return Certificates Table */
if (authsz) {
if (len < authoff + authsz) {
debug("%s: Size for auth too large: %u >= %zu\n",
__func__, authsz, len - authoff);
goto err;
}
if (authsz < sizeof(*auth)) {
debug("%s: Size for auth too small: %u < %zu\n",
__func__, authsz, sizeof(*auth));
goto err;
}
*auth = efi + authoff;
*auth_len = authsz;
debug("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff, authsz);
} else {
*auth = NULL;
*auth_len = 0;
}
*regp = regs;
return true;
err:
free(regs);
return false;
}
/**
* efi_image_unsigned_authenticate - authenticate unsigned image with
* SHA256 hash
* @regs: List of regions to be verified
*
* If an image is not signed, it doesn't have a signature. In this case,
* its message digest is calculated and it will be compared with one of
* hash values stored in signature databases.
*
* Return: true if authenticated, false if not
*/
static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
{
struct efi_signature_store *db = NULL, *dbx = NULL;
bool ret = false;
dbx = efi_sigstore_parse_sigdb(L"dbx");
if (!dbx) {
debug("Getting signature database(dbx) failed\n");
goto out;
}
db = efi_sigstore_parse_sigdb(L"db");
if (!db) {
debug("Getting signature database(db) failed\n");
goto out;
}
/* try black-list first */
if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) {
debug("Image is not signed and rejected by \"dbx\"\n");
goto out;
}
/* try white-list */
if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL))
ret = true;
else
debug("Image is not signed and not found in \"db\" or \"dbx\"\n");
out:
efi_sigstore_free(db);
efi_sigstore_free(dbx);
return ret;
}
/**
* efi_image_authenticate - verify a signature of signed image
* @efi: Pointer to image
* @efi_size: Size of @efi
*
* A signed image should have its signature stored in a table of its PE header.
* So if an image is signed and only if if its signature is verified using
* signature databases, an image is authenticated.
* If an image is not signed, its validity is checked by using
* efi_image_unsigned_authenticated().
* TODO:
* When AuditMode==0, if the image's signature is not found in
* the authorized database, or is found in the forbidden database,
* the image will not be started and instead, information about it
* will be placed in this table.
* When AuditMode==1, an EFI_IMAGE_EXECUTION_INFO element is created
* in the EFI_IMAGE_EXECUTION_INFO_TABLE for every certificate found
* in the certificate table of every image that is validated.
*
* Return: true if authenticated, false if not
*/
static bool efi_image_authenticate(void *efi, size_t efi_size)
{
struct efi_image_regions *regs = NULL;
WIN_CERTIFICATE *wincerts = NULL, *wincert;
size_t wincerts_len;
struct pkcs7_message *msg = NULL;
struct efi_signature_store *db = NULL, *dbx = NULL;
struct x509_certificate *cert = NULL;
void *new_efi = NULL;
size_t new_efi_size;
bool ret = false;
if (!efi_secure_boot_enabled())
return true;
/*
* Size must be 8-byte aligned and the trailing bytes must be
* zero'ed. Otherwise hash value may be incorrect.
*/
if (efi_size & 0x7) {
new_efi_size = (efi_size + 0x7) & ~0x7ULL;
new_efi = calloc(new_efi_size, 1);
if (!new_efi)
return false;
memcpy(new_efi, efi, efi_size);
efi = new_efi;
efi_size = new_efi_size;
}
if (!efi_image_parse(efi, efi_size, &regs, &wincerts,
&wincerts_len)) {
debug("Parsing PE executable image failed\n");
goto err;
}
if (!wincerts) {
/* The image is not signed */
ret = efi_image_unsigned_authenticate(regs);
goto err;
}
/*
* verify signature using db and dbx
*/
db = efi_sigstore_parse_sigdb(L"db");
if (!db) {
debug("Getting signature database(db) failed\n");
goto err;
}
dbx = efi_sigstore_parse_sigdb(L"dbx");
if (!dbx) {
debug("Getting signature database(dbx) failed\n");
goto err;
}
/* go through WIN_CERTIFICATE list */
for (wincert = wincerts;
(void *)wincert < (void *)wincerts + wincerts_len;
wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) {
if (wincert->dwLength < sizeof(*wincert)) {
debug("%s: dwLength too small: %u < %zu\n",
__func__, wincert->dwLength, sizeof(*wincert));
goto err;
}
msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert),
wincert->dwLength - sizeof(*wincert));
if (!msg) {
debug("Parsing image's signature failed\n");
goto err;
}
/* try black-list first */
if (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) {
debug("Signature was rejected by \"dbx\"\n");
goto err;
}
if (!efi_signature_verify_signers(msg, dbx)) {
debug("Signer was rejected by \"dbx\"\n");
goto err;
} else {
ret = true;
}
/* try white-list */
if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) {
debug("Verifying signature with \"db\" failed\n");
goto err;
} else {
ret = true;
}
if (!efi_signature_verify_cert(cert, dbx)) {
debug("Certificate was rejected by \"dbx\"\n");
goto err;
} else {
ret = true;
}
}
err:
x509_free_certificate(cert);
efi_sigstore_free(db);
efi_sigstore_free(dbx);
pkcs7_free_message(msg);
free(regs);
free(new_efi);
return ret;
}
#else
static bool efi_image_authenticate(void *efi, size_t efi_size)
{
return true;
}
#endif /* CONFIG_EFI_SECURE_BOOT */
/**
* efi_load_pe() - relocate EFI binary
*
@ -214,10 +597,12 @@ static void efi_set_code_and_data_type(
*
* @handle: loaded image handle
* @efi: pointer to the EFI binary
* @efi_size: size of @efi binary
* @loaded_image_info: loaded image protocol
* Return: status code
*/
efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
void *efi, size_t efi_size,
struct efi_loaded_image *loaded_image_info)
{
IMAGE_NT_HEADERS32 *nt;
@ -232,17 +617,41 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
uint64_t image_base;
unsigned long virt_size = 0;
int supported = 0;
efi_status_t ret;
/* Sanity check for a file header */
if (efi_size < sizeof(*dos)) {
printf("%s: Truncated DOS Header\n", __func__);
ret = EFI_LOAD_ERROR;
goto err;
}
dos = efi;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
printf("%s: Invalid DOS Signature\n", __func__);
return EFI_LOAD_ERROR;
ret = EFI_LOAD_ERROR;
goto err;
}
/* assume sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64) */
if (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) {
printf("%s: Invalid offset for Extended Header\n", __func__);
ret = EFI_LOAD_ERROR;
goto err;
}
nt = (void *) ((char *)efi + dos->e_lfanew);
if ((nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) &&
(efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64))) {
printf("%s: Invalid offset for Extended Header\n", __func__);
ret = EFI_LOAD_ERROR;
goto err;
}
if (nt->Signature != IMAGE_NT_SIGNATURE) {
printf("%s: Invalid NT Signature\n", __func__);
return EFI_LOAD_ERROR;
ret = EFI_LOAD_ERROR;
goto err;
}
for (i = 0; machines[i]; i++)
@ -254,14 +663,29 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
if (!supported) {
printf("%s: Machine type 0x%04x is not supported\n",
__func__, nt->FileHeader.Machine);
return EFI_LOAD_ERROR;
ret = EFI_LOAD_ERROR;
goto err;
}
/* Calculate upper virtual address boundary */
num_sections = nt->FileHeader.NumberOfSections;
sections = (void *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader;
if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections
- efi)) {
printf("%s: Invalid number of sections: %d\n",
__func__, num_sections);
ret = EFI_LOAD_ERROR;
goto err;
}
/* Authenticate an image */
if (efi_image_authenticate(efi, efi_size))
handle->auth_status = EFI_IMAGE_AUTH_PASSED;
else
handle->auth_status = EFI_IMAGE_AUTH_FAILED;
/* Calculate upper virtual address boundary */
for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = &sections[i];
virt_size = max_t(unsigned long, virt_size,
@ -280,7 +704,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
if (!efi_reloc) {
printf("%s: Could not allocate %lu bytes\n",
__func__, virt_size);
return EFI_OUT_OF_RESOURCES;
ret = EFI_OUT_OF_RESOURCES;
goto err;
}
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
@ -296,7 +721,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
if (!efi_reloc) {
printf("%s: Could not allocate %lu bytes\n",
__func__, virt_size);
return EFI_OUT_OF_RESOURCES;
ret = EFI_OUT_OF_RESOURCES;
goto err;
}
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
@ -305,13 +731,16 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
} else {
printf("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
return EFI_LOAD_ERROR;
ret = EFI_LOAD_ERROR;
goto err;
}
/* Copy PE headers */
memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt)
+ nt->FileHeader.SizeOfOptionalHeader
+ num_sections * sizeof(IMAGE_SECTION_HEADER));
memcpy(efi_reloc, efi,
sizeof(*dos)
+ sizeof(*nt)
+ nt->FileHeader.SizeOfOptionalHeader
+ num_sections * sizeof(IMAGE_SECTION_HEADER));
/* Load sections into RAM */
for (i = num_sections - 1; i >= 0; i--) {
@ -328,7 +757,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
(unsigned long)image_base) != EFI_SUCCESS) {
efi_free_pages((uintptr_t) efi_reloc,
(virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
return EFI_LOAD_ERROR;
ret = EFI_LOAD_ERROR;
goto err;
}
/* Flush cache */
@ -340,5 +770,11 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
loaded_image_info->image_base = efi_reloc;
loaded_image_info->image_size = virt_size;
return EFI_SUCCESS;
if (handle->auth_status == EFI_IMAGE_AUTH_PASSED)
return EFI_SUCCESS;
else
return EFI_SECURITY_VIOLATION;
err:
return ret;
}

View 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)

View File

@ -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 */

View 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.

File diff suppressed because it is too large Load Diff

View 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)
{

View 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.

View File

@ -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)

View File

@ -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=''

View File

@ -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)

View File

@ -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)))

View File

@ -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)))