All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/16] efi_loader: add secure boot support
@ 2019-12-18  0:44 AKASHI Takahiro
  2019-12-18  0:44 ` [PATCH v4 01/16] include: pe.h: add signature-related definitions AKASHI Takahiro
                   ` (17 more replies)
  0 siblings, 18 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:44 UTC (permalink / raw)
  To: u-boot

One of major missing features in current UEFI implementation is "secure boot."
The ultimate goal of my attempt is to implement image authentication based
on signature and provide UEFI secure boot support which would be fully
compliant with UEFI specification, section 32[1].
(The code was originally developed by Patrick Wildt.)

Please note, however, this patch doesn't work on its own; there are
a couple of functional dependencies[2] and [3], that I have submitted
before. For complete workable patch set, see my repository[4],
which also contains experimental timestamp-based revocation suuport.

My "non-volatile" support[5], which is under discussion, is not mandatory
and so not included here, but this inevitably implies that, for example,
signature database variables, like db and dbx, won't be persistent unless you
explicitly run "env save" command and that UEFI variables are not separated
from U-Boot environment. Anyhow, Linaro is also working on implementing
real "secure storage" solution based on TF-A and OP-TEE.


Supported features:
* image authentication based on db and dbx
* supported signature types are
    EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
    EFI_CERT_X509_GUID (x509 certificate for signed images)
* SecureBoot/SignatureSupport variables
* SetupMode and user mode
* variable authentication based on PK and KEK
    EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
* basic pytest test cases

Unsupported features: (marked as TODO in most cases in the source code,
			and won't be included in this series)
* hash algorithms other than SHA256
* dbt: timestamp(RFC6131)-based certificate revocation
* dbr: OS recovery 
* xxxDefault: default values for signature stores
* transition to AuditMode and DeployedMode
* recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
* verification "policy", in particular, check against signature's owner
* private authenticated variables
* variable authentication with EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS
* real secure storage support, including hardware-specific PK (Platform Key)
  installation

TODO's other than "Unsupported features": (won't be included in this series)
* fail recovery, in particular, in modifying authenticated variables
* support read-only attributes of well-defined global variables
  in particular, "SignatureSupport"
* Extensive test suite (or more test cases) to confirm compatibility
  with EDK2
	=> I requested EDK SCT community to add tests[6].

Test:
* my pytest, included in this patch set, passed.
* efi_selftest passed. (At least no regression.)
* Travis CI tests have passed.

Known issues:
* efitools is used in pytest, and its version must be v1.5.2 or later.
  (Solution: You can define EFITOOLS_PATH in defs.py for your own efitools.)
* Pytest depends on standalone "helloworld" app for sandbox
  (Solution: You can define HELLO_PATH in defs.py or Heinrich's [7].)


Hints about how to use:
(Please see other documents, or my pytest scripts, for details.)
* You can create your own certificates with openssl.
* You can sign your application with sbsign (on Ubuntu).
* You can create raw data for signature database with efitools, and
  install/manage authenticated variables with "env -set -e" command
  or efitools' "UpdateVars.efi" application.


[1] https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf
[2] https://lists.denx.de/pipermail/u-boot/2019-November/390127.html
    (import x509/pkcs7 parsers from linux)
[3] https://lists.denx.de/pipermail/u-boot/2019-December/393977.html
    (extend rsa_verify() for UEFI secure boot)
[4] http://git.linaro.org/people/takahiro.akashi/u-boot.git/ efi/secboot
[5] https://lists.denx.de/pipermail/u-boot/2019-September/382835.html
    (non-volatile variables support)
[6] https://bugzilla.tianocore.org/show_bug.cgi?id=2230
[7] https://lists.denx.de/pipermail/u-boot/2019-November/389593.html


Changes in v4 (Dec 18, 2019)
* adjust EFI_SECURE_BOOT dependencies due to a change of RSA extension patch v5
  (patch#2)
* change "imply" to "select" against kconfig dependencies (patch#2)
* otherwise, no functional changes

Changes in v3 (Dec 9, 2019)
* allow for arbitrary number of regions in efi_image_region_add()
  (patch#3, #5 and #8)
* remove a redundant check in a while loop at efi_sigstore_free() (patch#4)

Changes in v2 (Nov 26, 2019)
* rebased to v2020.01-rc3
* rename IMAGE_DIRECTORY_ENTRY_CERTTABLE to IMAGE_DIRECTORY_ENTRY_SECURITY
  (patch#1,#9)
* add comments (patch#1)
* drop v1's patch#2 as it is no longer necessary
* drop v1's patch#3 as other "SECURE_BOOT" architectures have renamed
  this option and no longer use it
* add structure descriptions (patch#3)
* rework hash calculation code in efi_signature_verify() and remove
  an odd constant, WinIndrectSha256 (patch#3)
* move travis.yml changes to a separate patch (patch#12, #16)
* yield_fixture() -> fixture() (patch#12)
* call console.restart_uboot() at every test case (13,#14)
* add patch#15; enable UEFI-related configurations by default on sandbox
* add patch#16; modify Travis CI environment to run UEFI secure boot test

Changes in v1 (Nov 13, 2019)
* rebased to v2020.01-rc
* remove already-merged patches
* re-work the patch set for easier reviews, including
  - move a config definition patch forward (patch#4)
  - refactor/rename verification functions (patch#5/#10)
  - split signature database parser as a separate patch (patch#6)
  - split secure state transition code as a separate patch (patch#8)
  - move most part of init_secure_boot() into init_variables() (patch#8)
  - split test environment setup from test patches (patch#14)
* add function descriptions (patch#5-#11)
* make sure the section list is sorted in ascending order in hash
  calculation of PE image (patch#10)
* add a new "-at" (authenticated access) option to "env -e" (patch#13)
* list required host packages, in particular udisks2, in pytest
  (patch#14)
* modify conftest.py to run under python3 (patch#14)
* use a partition on a disk instead of a whole disk without partition
  table (patch#14)
* reduce dependency on efitools, yet relying on its host tools (patch#14)
* modify pytests to catch up wth latest changes of "env -e" syntax
  (patch#15,#16)

RFC (Sept 18, 2019)

AKASHI Takahiro (16):
  include: pe.h: add signature-related definitions
  efi_loader: add CONFIG_EFI_SECURE_BOOT config option
  efi_loader: add signature verification functions
  efi_loader: add signature database parser
  efi_loader: variable: support variable authentication
  efi_loader: variable: add secure boot state transition
  efi_loader: variable: add VendorKeys variable
  efi_loader: image_loader: support image authentication
  efi_loader: set up secure boot
  cmd: env: use appropriate guid for authenticated UEFI variable
  cmd: env: add "-at" option to "env set -e" command
  efi_loader, pytest: set up secure boot environment
  efi_loader, pytest: add UEFI secure boot tests (authenticated
    variables)
  efi_loader, pytest: add UEFI secure boot tests (image)
  sandbox: add extra configurations for UEFI and related tests
  travis: add packages for UEFI secure boot test

 .travis.yml                                   |  11 +-
 cmd/nvedit.c                                  |   5 +-
 cmd/nvedit_efi.c                              |  23 +-
 configs/sandbox64_defconfig                   |   3 +
 configs/sandbox_defconfig                     |   3 +
 include/efi_api.h                             |  87 ++
 include/efi_loader.h                          |  85 +-
 include/pe.h                                  |  18 +
 lib/efi_loader/Kconfig                        |  19 +
 lib/efi_loader/Makefile                       |   1 +
 lib/efi_loader/efi_boottime.c                 |   2 +-
 lib/efi_loader/efi_image_loader.c             | 454 ++++++++-
 lib/efi_loader/efi_setup.c                    |  38 +
 lib/efi_loader/efi_signature.c                | 810 +++++++++++++++
 lib/efi_loader/efi_variable.c                 | 951 ++++++++++++++++--
 test/py/README.md                             |   8 +
 test/py/tests/test_efi_secboot/conftest.py    | 151 +++
 test/py/tests/test_efi_secboot/defs.py        |  21 +
 .../py/tests/test_efi_secboot/test_authvar.py | 282 ++++++
 test/py/tests/test_efi_secboot/test_signed.py |  99 ++
 .../tests/test_efi_secboot/test_unsigned.py   | 103 ++
 21 files changed, 3046 insertions(+), 128 deletions(-)
 create mode 100644 lib/efi_loader/efi_signature.c
 create mode 100644 test/py/tests/test_efi_secboot/conftest.py
 create mode 100644 test/py/tests/test_efi_secboot/defs.py
 create mode 100644 test/py/tests/test_efi_secboot/test_authvar.py
 create mode 100644 test/py/tests/test_efi_secboot/test_signed.py
 create mode 100644 test/py/tests/test_efi_secboot/test_unsigned.py

-- 
2.24.0

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 01/16] include: pe.h: add signature-related definitions
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
@ 2019-12-18  0:44 ` AKASHI Takahiro
  2019-12-18  0:44 ` [PATCH v4 02/16] efi_loader: add CONFIG_EFI_SECURE_BOOT config option AKASHI Takahiro
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:44 UTC (permalink / raw)
  To: u-boot

The index (IMAGE_DIRECTORY_ENTRY_SECURITY) in a table points to
a region containing authentication information (image's signature)
in PE format.

WIN_CERTIFICATE structure defines an embedded signature format.

Those definitions will be used in my UEFI secure boot patch.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
 include/pe.h | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/include/pe.h b/include/pe.h
index bff3b0aa7a6c..086f2b860e99 100644
--- a/include/pe.h
+++ b/include/pe.h
@@ -155,6 +155,8 @@ typedef struct _IMAGE_SECTION_HEADER {
 	uint32_t Characteristics;
 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
 
+/* Indices for Optional Header Data Directories */
+#define IMAGE_DIRECTORY_ENTRY_SECURITY		4
 #define IMAGE_DIRECTORY_ENTRY_BASERELOC         5
 
 typedef struct _IMAGE_BASE_RELOCATION
@@ -252,4 +254,20 @@ typedef struct _IMAGE_RELOCATION
 #define IMAGE_REL_AMD64_PAIR            0x000F
 #define IMAGE_REL_AMD64_SSPAN32         0x0010
 
+/* certificate appended to PE image */
+typedef struct _WIN_CERTIFICATE {
+	uint32_t dwLength;
+	uint16_t wRevision;
+	uint16_t wCertificateType;
+	uint8_t bCertificate[];
+} WIN_CERTIFICATE, *LPWIN_CERTIFICATE;
+
+/* Definitions for the contents of the certs data block */
+#define WIN_CERT_TYPE_PKCS_SIGNED_DATA	0x0002
+#define WIN_CERT_TYPE_EFI_OKCS115	0x0EF0
+#define WIN_CERT_TYPE_EFI_GUID		0x0EF1
+
+#define WIN_CERT_REVISION_1_0		0x0100
+#define WIN_CERT_REVISION_2_0		0x0200
+
 #endif /* _PE_H */
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 02/16] efi_loader: add CONFIG_EFI_SECURE_BOOT config option
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
  2019-12-18  0:44 ` [PATCH v4 01/16] include: pe.h: add signature-related definitions AKASHI Takahiro
@ 2019-12-18  0:44 ` AKASHI Takahiro
  2019-12-18  0:44 ` [PATCH v4 03/16] efi_loader: add signature verification functions AKASHI Takahiro
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:44 UTC (permalink / raw)
  To: u-boot

Under this configuration, UEFI secure boot support will be added
in later patches.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/Kconfig | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index c7027a967653..d1c092a1d80a 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -115,4 +115,23 @@ config EFI_GRUB_ARM32_WORKAROUND
 	  GRUB prior to version 2.04 requires U-Boot to disable caches. This
 	  workaround currently is also needed on systems with caches that
 	  cannot be managed via CP15.
+
+config EFI_SECURE_BOOT
+	bool "Enable EFI secure boot support"
+	depends on EFI_LOADER
+	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
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 03/16] efi_loader: add signature verification functions
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
  2019-12-18  0:44 ` [PATCH v4 01/16] include: pe.h: add signature-related definitions AKASHI Takahiro
  2019-12-18  0:44 ` [PATCH v4 02/16] efi_loader: add CONFIG_EFI_SECURE_BOOT config option AKASHI Takahiro
@ 2019-12-18  0:44 ` AKASHI Takahiro
  2020-01-14 23:43   ` Heinrich Schuchardt
  2019-12-18  0:45 ` [PATCH v4 04/16] efi_loader: add signature database parser AKASHI Takahiro
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:44 UTC (permalink / raw)
  To: u-boot

In this commit, implemented are a couple of helper functions which will be
used to materialize variable authentication as well as image authentication
in later patches.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h              |  87 +++++
 include/efi_loader.h           |  72 ++++
 lib/efi_loader/Makefile        |   1 +
 lib/efi_loader/efi_signature.c | 584 +++++++++++++++++++++++++++++++++
 4 files changed, 744 insertions(+)
 create mode 100644 lib/efi_loader/efi_signature.c

diff --git a/include/efi_api.h b/include/efi_api.h
index 22396172e15f..47f24fc90873 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -18,6 +18,7 @@
 
 #include <efi.h>
 #include <charset.h>
+#include <pe.h>
 
 #ifdef CONFIG_EFI_LOADER
 #include <asm/setjmp.h>
@@ -307,6 +308,10 @@ struct efi_runtime_services {
 	EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
 		 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
 
+#define EFI_IMAGE_SECURITY_DATABASE_GUID \
+	EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, \
+		 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
+
 #define EFI_FDT_GUID \
 	EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \
 		 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
@@ -1616,4 +1621,86 @@ struct efi_unicode_collation_protocol {
 #define LOAD_OPTION_CATEGORY_BOOT	0x00000000
 #define LOAD_OPTION_CATEGORY_APP	0x00000100
 
+/* Certificate types in signature database */
+#define EFI_CERT_SHA256_GUID \
+	EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, \
+		 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28)
+#define EFI_CERT_RSA2048_GUID \
+	EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, \
+		 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6)
+#define EFI_CERT_X509_GUID \
+	EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, \
+		 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
+#define EFI_CERT_X509_SHA256_GUID \
+	EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
+		 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed)
+#define EFI_CERT_TYPE_PKCS7_GUID \
+	EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
+		 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
+
+/**
+ * win_certificate_uefi_guid - A certificate that encapsulates
+ * a GUID-specific signature
+ *
+ * @hdr:	Windows certificate header
+ * @cert_type:	Certificate type
+ * @cert_data:	Certificate data
+ */
+struct win_certificate_uefi_guid {
+	WIN_CERTIFICATE	hdr;
+	efi_guid_t	cert_type;
+	u8		cert_data[];
+} __attribute__((__packed__));
+
+/**
+ * efi_variable_authentication_2 - A time-based authentication method
+ * descriptor
+ *
+ * This structure describes an authentication information for
+ * a variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+ * and should be included as part of a variable's value.
+ * Only EFI_CERT_TYPE_PKCS7_GUID is accepted.
+ *
+ * @time_stamp:	Descriptor's time stamp
+ * @auth_info:	Authentication info
+ */
+struct efi_variable_authentication_2 {
+	struct efi_time			 time_stamp;
+	struct win_certificate_uefi_guid auth_info;
+} __attribute__((__packed__));
+
+/**
+ * efi_signature_data - A format of signature
+ *
+ * This structure describes a single signature in signature database.
+ *
+ * @signature_owner:	Signature owner
+ * @signature_data:	Signature data
+ */
+struct efi_signature_data {
+	efi_guid_t	signature_owner;
+	u8		signature_data[];
+} __attribute__((__packed__));
+
+/**
+ * efi_signature_list - A format of signature database
+ *
+ * This structure describes a list of signatures with the same type.
+ * An authenticated variable's value is a concatenation of one or more
+ * efi_signature_list's.
+ *
+ * @signature_type:		Signature type
+ * @signature_list_size:	Size of signature list
+ * @signature_header_size:	Size of signature header
+ * @signature_size:		Size of signature
+ */
+struct efi_signature_list {
+	efi_guid_t	signature_type;
+	u32		signature_list_size;
+	u32		signature_header_size;
+	u32		signature_size;
+/*	u8		signature_header[signature_header_size]; */
+/*	struct efi_signature_data signatures[...][signature_size]; */
+} __attribute__((__packed__));
+
 #endif
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 381da80cdce0..3ca68f9bbb6e 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -21,6 +21,7 @@ static inline int guidcmp(const void *g1, const void *g2)
 #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
@@ -169,6 +170,11 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol;
 extern const efi_guid_t efi_guid_hii_config_access_protocol;
 extern const efi_guid_t efi_guid_hii_database_protocol;
 extern const efi_guid_t efi_guid_hii_string_protocol;
+/* 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 unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -650,6 +656,72 @@ 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);
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
 #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 7db406028618..3ffbfe78a46b 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o
 obj-$(CONFIG_NET) += efi_net.o
 obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
 obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
+obj-y += efi_signature.o
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
new file mode 100644
index 000000000000..823d3311e010
--- /dev/null
+++ b/lib/efi_loader/efi_signature.c
@@ -0,0 +1,584 @@
+// 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 <pe.h>
+#include <linux/compat.h>
+#include <linux/oid_registry.h>
+#include <u-boot/rsa.h>
+#include <u-boot/sha256.h>
+/*
+ * avoid duplicated inclusion:
+ * #include "../lib/crypto/x509_parser.h"
+ */
+#include "../lib/crypto/pkcs7_parser.h"
+
+const efi_guid_t efi_guid_image_security_database =
+		EFI_IMAGE_SECURITY_DATABASE_GUID;
+const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
+const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
+const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
+const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+
+/**
+ * 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;
+		} else {
+			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;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 04/16] efi_loader: add signature database parser
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2019-12-18  0:44 ` [PATCH v4 03/16] efi_loader: add signature verification functions AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 05/16] efi_loader: variable: support variable authentication AKASHI Takahiro
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

efi_signature_parse_sigdb() is a helper function will be used to parse
signature database variable and instantiate a signature store structure
in later patches.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h           |   3 +
 lib/efi_loader/efi_signature.c | 226 +++++++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 3ca68f9bbb6e..3b3618e0be24 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -720,6 +720,9 @@ bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
 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);
 #endif /* CONFIG_EFI_SECURE_BOOT */
 
 #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
index 823d3311e010..98302e1ab3d3 100644
--- a/lib/efi_loader/efi_signature.c
+++ b/lib/efi_loader/efi_signature.c
@@ -581,4 +581,230 @@ efi_status_t efi_image_region_add(struct efi_image_regions *regs,
 
 	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 */
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 05/16] efi_loader: variable: support variable authentication
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 04/16] efi_loader: add signature database parser AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2020-01-08 22:54   ` Heinrich Schuchardt
  2019-12-18  0:45 ` [PATCH v4 06/16] efi_loader: variable: add secure boot state transition AKASHI Takahiro
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

With this commit, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
is supported for authenticated variables and the system secure state
will transfer between setup mode and user mode as UEFI specification
section 32.3 describes.

Internally, authentication data is stored as part of authenticated
variable's value. It is nothing but a pkcs7 message (but we need some
wrapper, see efi_variable_parse_signature()) and will be validated by
efi_variable_authenticate(), hence efi_signature_verify_with_db().

Associated time value will be encoded in "{...,time=...}" along with
other UEFI variable's attributes.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h          |   3 +
 lib/efi_loader/efi_variable.c | 665 ++++++++++++++++++++++++++++------
 2 files changed, 564 insertions(+), 104 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 3b3618e0be24..1f88caf86709 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -175,6 +175,7 @@ extern const efi_guid_t efi_guid_image_security_database;
 extern const efi_guid_t efi_guid_sha256;
 extern const efi_guid_t efi_guid_cert_x509;
 extern const efi_guid_t efi_guid_cert_x509_sha256;
+extern const efi_guid_t efi_guid_cert_type_pkcs7;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -723,6 +724,8 @@ efi_status_t efi_image_region_add(struct efi_image_regions *regs,
 
 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);
 #endif /* CONFIG_EFI_SECURE_BOOT */
 
 #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 46f35bc60ba0..171b4abb4c58 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -10,7 +10,13 @@
 #include <env_internal.h>
 #include <hexdump.h>
 #include <malloc.h>
+#include <rtc.h>
 #include <search.h>
+#include <linux/compat.h>
+#include "../lib/crypto/pkcs7_parser.h"
+
+const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+static bool efi_secure_boot;
 
 #define READ_ONLY BIT(31)
 
@@ -107,7 +113,7 @@ static const char *prefix(const char *str, const char *prefix)
  * @attrp:	pointer to UEFI attributes
  * Return:	pointer to remainder of U-Boot variable value
  */
-static const char *parse_attr(const char *str, u32 *attrp)
+static const char *parse_attr(const char *str, u32 *attrp, u64 *timep)
 {
 	u32 attr = 0;
 	char sep = '{';
@@ -130,6 +136,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,48 +159,291 @@ static const char *parse_attr(const char *str, u32 *attrp)
 }
 
 /**
- * efi_get_variable() - retrieve value of a UEFI variable
+ * efi_secure_boot_enabled - return if secure boot is enabled or not
  *
- * This function implements the GetVariable runtime service.
+ * 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
  *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
+ * 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.
  *
- * @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:	Pointer to pkcs7_message structure on success, NULL 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 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;
+	const 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);
+
+	timestamp = &auth->time_stamp;
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = timestamp->year;
+	tm.tm_mon = timestamp->month;
+	tm.tm_mday = timestamp->day;
+	tm.tm_hour = timestamp->hour;
+	tm.tm_min = timestamp->minute;
+	tm.tm_sec = timestamp->second;
+	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;
 
@@ -197,7 +452,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;
@@ -208,11 +463,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)"))) {
@@ -225,8 +482,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';
@@ -234,13 +493,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);
 }
 
@@ -273,6 +586,7 @@ static efi_status_t parse_uboot_variable(char *variable,
 {
 	char *guid, *name, *end, c;
 	unsigned long name_len;
+	u64 time;
 	u16 *p;
 
 	guid = strchr(variable, '_');
@@ -307,7 +621,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;
 }
@@ -389,7 +703,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);
 
@@ -402,143 +716,286 @@ 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);
 
+out:
 	if (env_set(native_name, val))
 		ret = EFI_DEVICE_ERROR;
+	else
+		ret = EFI_SUCCESS;
 
-out:
+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));
 }
 
 /**
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 06/16] efi_loader: variable: add secure boot state transition
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 05/16] efi_loader: variable: support variable authentication AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 07/16] efi_loader: variable: add VendorKeys variable AKASHI Takahiro
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

UEFI specification defines several global variables which are related to
the current secure boot state. In this commit, those values will be
maintained according to operations. Currently, AuditMode and DeployedMode
are defined but not implemented.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/efi_variable.c | 231 +++++++++++++++++++++++++++++++++-
 1 file changed, 228 insertions(+), 3 deletions(-)

diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 171b4abb4c58..2e7570bc9b4f 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -15,8 +15,16 @@
 #include <linux/compat.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;
 
 #define READ_ONLY BIT(31)
 
@@ -158,6 +166,210 @@ static const char *parse_attr(const char *str, u32 *attrp, u64 *timep)
 	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_transfer_secure_state - handle a secure boot state transition
+ * @mode:	new state
+ *
+ * Depending on @mode, secure boot related variables are updated.
+ * Those variables are *read-only* for users, efi_set_variable_internal()
+ * is called here.
+ *
+ * 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;
+	}
+
+	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
+ *
+ * Return:	EFI_SUCCESS on success, status code (negative) on error
+ */
+static efi_status_t efi_init_secure_state(void)
+{
+	efi_uintn_t size = 0;
+	efi_status_t ret;
+
+	ret = EFI_CALL(efi_get_variable(L"PK", &efi_global_variable_guid,
+					NULL, &size, NULL));
+	if (ret == EFI_BUFFER_TOO_SMALL && IS_ENABLED(CONFIG_EFI_SECURE_BOOT))
+		ret = efi_transfer_secure_state(EFI_MODE_USER);
+	else
+		ret = efi_transfer_secure_state(EFI_MODE_SETUP);
+
+	return ret;
+}
+
 /**
  * efi_secure_boot_enabled - return if secure boot is enabled or not
  *
@@ -907,10 +1119,19 @@ efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
 	EFI_PRINT("setting: %s=%s\n", native_name, val);
 
 out:
-	if (env_set(native_name, val))
+	if (env_set(native_name, val)) {
 		ret = EFI_DEVICE_ERROR;
-	else
+	} else {
+		if ((u16_strcmp(variable_name, L"PK") == 0 &&
+		     guidcmp(vendor, &efi_global_variable_guid) == 0)) {
+			ret = efi_transfer_secure_state(
+					(delete ? EFI_MODE_SETUP :
+						  EFI_MODE_USER));
+			if (ret != EFI_SUCCESS)
+				goto err;
+		}
 		ret = EFI_SUCCESS;
+	}
 
 err:
 	free(native_name);
@@ -1095,5 +1316,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;
 }
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 07/16] efi_loader: variable: add VendorKeys variable
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 06/16] efi_loader: variable: add secure boot state transition AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 08/16] efi_loader: image_loader: support image authentication AKASHI Takahiro
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

The following variable is exported as UEFI specification defines:
VendorKeys: whether the system is configured to use only vendor-provided
	    keys or not
The value will have to be modified if a platform has its own way of
initializing signature database, in particular, PK.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/efi_variable.c | 69 ++++++++++++++++++++++++++++++++---
 1 file changed, 63 insertions(+), 6 deletions(-)

diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 2e7570bc9b4f..f967704fc2b6 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -25,6 +25,7 @@ enum efi_secure_mode {
 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)
 
@@ -342,6 +343,8 @@ static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode)
 		return EFI_INVALID_PARAMETER;
 	}
 
+	efi_secure_mode = mode;
+
 	return EFI_SUCCESS;
 
 err:
@@ -357,16 +360,46 @@ err:
  */
 static efi_status_t efi_init_secure_state(void)
 {
-	efi_uintn_t size = 0;
+	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 && IS_ENABLED(CONFIG_EFI_SECURE_BOOT))
-		ret = efi_transfer_secure_state(EFI_MODE_USER);
-	else
-		ret = efi_transfer_secure_state(EFI_MODE_SETUP);
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT))
+			mode = EFI_MODE_USER;
+		else
+			mode = EFI_MODE_SETUP;
+
+		efi_vendor_keys = 0;
+	} else if (ret == EFI_NOT_FOUND) {
+		mode = EFI_MODE_SETUP;
+		efi_vendor_keys = 1;
+	} else {
+		goto err;
+	}
 
+	ret = efi_transfer_secure_state(mode);
+	if (ret == EFI_SUCCESS)
+		ret = efi_set_variable_internal(L"VendorKeys",
+						&efi_global_variable_guid,
+						EFI_VARIABLE_BOOTSERVICE_ACCESS
+						 | EFI_VARIABLE_RUNTIME_ACCESS
+						 | READ_ONLY,
+						sizeof(efi_vendor_keys),
+						&efi_vendor_keys,
+						false);
+
+err:
 	return ret;
 }
 
@@ -1122,6 +1155,8 @@ out:
 	if (env_set(native_name, val)) {
 		ret = EFI_DEVICE_ERROR;
 	} else {
+		bool vendor_keys_modified = false;
+
 		if ((u16_strcmp(variable_name, L"PK") == 0 &&
 		     guidcmp(vendor, &efi_global_variable_guid) == 0)) {
 			ret = efi_transfer_secure_state(
@@ -1129,8 +1164,30 @@ out:
 						  EFI_MODE_USER));
 			if (ret != EFI_SUCCESS)
 				goto err;
+
+			if (efi_secure_mode != EFI_MODE_SETUP)
+				vendor_keys_modified = true;
+		} else if ((u16_strcmp(variable_name, L"KEK") == 0 &&
+		     guidcmp(vendor, &efi_global_variable_guid) == 0)) {
+			if (efi_secure_mode != EFI_MODE_SETUP)
+				vendor_keys_modified = true;
+		}
+
+		/* update VendorKeys */
+		if (vendor_keys_modified & efi_vendor_keys) {
+			efi_vendor_keys = 0;
+			ret = efi_set_variable_internal(
+						L"VendorKeys",
+						&efi_global_variable_guid,
+						EFI_VARIABLE_BOOTSERVICE_ACCESS
+						 | EFI_VARIABLE_RUNTIME_ACCESS
+						 | READ_ONLY,
+						sizeof(efi_vendor_keys),
+						&efi_vendor_keys,
+						false);
+		} else {
+			ret = EFI_SUCCESS;
 		}
-		ret = EFI_SUCCESS;
 	}
 
 err:
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (6 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 07/16] efi_loader: variable: add VendorKeys variable AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2020-01-08 23:55   ` Heinrich Schuchardt
  2019-12-18  0:45 ` [PATCH v4 09/16] efi_loader: set up secure boot AKASHI Takahiro
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

With this commit, image validation can be enforced, as UEFI specification
section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.

Currently we support
* authentication based on db and dbx,
  so dbx-validated image will always be rejected.
* following signature types:
    EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
    EFI_CERT_X509_GUID (x509 certificate for signed images)
Timestamp-based certificate revocation is not supported here.

Internally, authentication data is stored in one of certificates tables
of PE image (See efi_image_parse()) and will be verified by
efi_image_authenticate() before loading a given image.

It seems that UEFI specification defines the verification process
in a bit ambiguous way. I tried to implement it as closely to as
EDK2 does.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h              |   7 +-
 lib/efi_loader/efi_boottime.c     |   2 +-
 lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
 3 files changed, 449 insertions(+), 14 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 1f88caf86709..e12b49098fb0 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -11,6 +11,7 @@
 #include <common.h>
 #include <part_efi.h>
 #include <efi_api.h>
+#include <pe.h>
 
 static inline int guidcmp(const void *g1, const void *g2)
 {
@@ -398,7 +399,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);
@@ -726,6 +728,9 @@ 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) */
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 493d906c641d..311681764034 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
 	efi_dp_split_file_path(file_path, &dp, &fp);
 	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
 	if (ret == EFI_SUCCESS)
-		ret = efi_load_pe(*image_obj, dest_buffer, info);
+		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
 	if (!source_buffer)
 		/* Release buffer to which file was loaded */
 		efi_free_pages((uintptr_t)dest_buffer,
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index 13541cfa7a28..939758e61e3c 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -9,7 +9,9 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <malloc.h>
 #include <pe.h>
+#include "../lib/crypto/pkcs7_parser.h"
 
 const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
 const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
@@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
 	}
 }
 
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * efi_image_parse - parse a PE image
+ * @efi:	Pointer to image
+ * @len:	Size of @efi
+ * @regs:	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, j;
+	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.
+	 * As we can assume that it is already ordered in most cases,
+	 * the following code is optimized for this.
+	 */
+	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
+		if (sorted[i - 1]->VirtualAddress
+				<= sections[i].VirtualAddress) {
+			sorted[i] = &sections[i];
+		} else {
+			if (i == 1) {
+				sorted[1] = sorted[0];
+				sorted[0] = &sections[1];
+				continue;
+			}
+
+			sorted[i] = sorted[i - 1];
+			for (j = i - 2; j >= 0; j--) {
+				if (!j || sorted[j]->VirtualAddress
+						<= sections[i].VirtualAddress) {
+					sorted[j + 1] = &sections[i];
+					continue;
+				} else {
+					sorted[j + 1] = sorted[j];
+				}
+			}
+		}
+	}
+
+	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
+ * @len:	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 len)
+{
+	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;
+	bool ret = false;
+
+	if (!efi_secure_boot_enabled())
+		return true;
+
+	if (!efi_image_parse(efi, len, &regs, &wincerts,
+			     &wincerts_len)) {
+		debug("Parsing PE executable image failed\n");
+		return false;
+	}
+
+	if (!wincerts) {
+		/* The image is not signed */
+		ret = efi_image_unsigned_authenticate(regs);
+		free(regs);
+
+		return ret;
+	}
+
+	/*
+	 * verify signature using db and dbx
+	 */
+	db = efi_sigstore_parse_sigdb(L"db");
+	if (!db) {
+		debug("Getting signature database(db) failed\n");
+		goto err;
+	}
+
+	dbx = efi_sigstore_parse_sigdb(L"dbx");
+	if (!dbx) {
+		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);
+
+	return ret;
+}
+#else
+static bool efi_image_authenticate(void *efi, size_t len)
+{
+	return true;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
 /**
  * efi_load_pe() - relocate EFI binary
  *
@@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
  * @loaded_image_info:	loaded image protocol
  * Return:		status code
  */
-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
+			 void *efi, size_t efi_size,
 			 struct efi_loaded_image *loaded_image_info)
 {
 	IMAGE_NT_HEADERS32 *nt;
@@ -231,17 +595,57 @@ 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;
+	void *new_efi = NULL;
+	size_t new_efi_size;
+	efi_status_t ret;
+
+	/*
+	 * 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 EFI_OUT_OF_RESOURCES;
+		memcpy(new_efi, efi, efi_size);
+		efi = new_efi;
+		efi_size = new_efi_size;
+	}
+
+	/* 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++)
@@ -253,14 +657,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)) {
+		ret = EFI_ACCESS_DENIED;
+		goto err;
+	}
+
+	/* 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,
@@ -279,7 +698,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;
@@ -295,7 +715,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;
@@ -304,13 +725,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--) {
@@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
 	loaded_image_info->image_size = virt_size;
 
 	return EFI_SUCCESS;
+
+err:
+	free(new_efi);
+
+	return ret;
 }
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 09/16] efi_loader: set up secure boot
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (7 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 08/16] efi_loader: image_loader: support image authentication AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable AKASHI Takahiro
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

The following variable is exported as UEFI specification defines:
SignatureSupport: array of GUIDs representing the type of signatures
                      supported by the platform firmware

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/efi_setup.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index de7b616c6daa..50cef1b4dfde 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -82,6 +82,39 @@ out:
 	return ret;
 }
 
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * efi_init_secure_boot - initialize secure boot state
+ *
+ * Return:	EFI_SUCCESS on success, status code (negative) on error
+ */
+static efi_status_t efi_init_secure_boot(void)
+{
+	efi_guid_t signature_types[] = {
+		EFI_CERT_SHA256_GUID,
+		EFI_CERT_X509_GUID,
+	};
+	efi_status_t ret;
+
+	/* TODO: read-only */
+	ret = EFI_CALL(efi_set_variable(L"SignatureSupport",
+					&efi_global_variable_guid,
+					EFI_VARIABLE_BOOTSERVICE_ACCESS
+					 | EFI_VARIABLE_RUNTIME_ACCESS,
+					sizeof(signature_types),
+					&signature_types));
+	if (ret != EFI_SUCCESS)
+		printf("EFI: cannot initialize SignatureSupport variable\n");
+
+	return ret;
+}
+#else
+static efi_status_t efi_init_secure_boot(void)
+{
+	return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
 /**
  * efi_init_obj_list() - Initialize and populate EFI object list
  *
@@ -122,6 +155,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)
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (8 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 09/16] efi_loader: set up secure boot AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2020-01-21  7:13   ` Heinrich Schuchardt
  2019-12-18  0:45 ` [PATCH v4 11/16] cmd: env: add "-at" option to "env set -e" command AKASHI Takahiro
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

A signature database variable is associated with a specific guid.
For convenience, if user doesn't supply any guid info, "env set|print -e"
should complement it.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 cmd/nvedit_efi.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
index 8ea0da01283f..579cf430593c 100644
--- a/cmd/nvedit_efi.c
+++ b/cmd/nvedit_efi.c
@@ -41,6 +41,11 @@ static const struct {
 } efi_guid_text[] = {
 	/* signature database */
 	{EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"},
+	{EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"},
+	/* certificate type */
+	{EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"},
+	{EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"},
+	{EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"},
 };
 
 /* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */
@@ -525,9 +530,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 +544,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 *)
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 11/16] cmd: env: add "-at" option to "env set -e" command
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (9 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 12/16] efi_loader, pytest: set up secure boot environment AKASHI Takahiro
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

With "-at" option, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
will be passed to SetVariable() to authenticate the variable.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 cmd/nvedit.c     | 5 +++--
 cmd/nvedit_efi.c | 5 ++++-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/cmd/nvedit.c b/cmd/nvedit.c
index 99a3bc57b15f..30cd655b7260 100644
--- a/cmd/nvedit.c
+++ b/cmd/nvedit.c
@@ -1399,7 +1399,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";
@@ -1461,13 +1461,14 @@ U_BOOT_CMD_COMPLETE(
 	setenv, CONFIG_SYS_MAXARGS, 0,	do_env_set,
 	"set environment variables",
 #if defined(CONFIG_CMD_NVEDIT_EFI)
-	"-e [-guid guid][-nv][-bs][-rt][-a][-v]\n"
+	"-e [-guid guid][-nv][-bs][-rt][-at][-a][-v]\n"
 	"        [-i addr,size name], or [name [value ...]]\n"
 	"    - set UEFI variable 'name' to 'value' ...'\n"
 	"      \"-guid\": set vendor guid\n"
 	"      \"-nv\": set non-volatile attribute\n"
 	"      \"-bs\": set boot-service attribute\n"
 	"      \"-rt\": set runtime attribute\n"
+	"      \"-at\": set time-based authentication attribute\n"
 	"      \"-a\": append-write\n"
 	"      \"-i addr,size\": use <addr,size> as variable's value\n"
 	"      \"-v\": verbose message\n"
diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
index 579cf430593c..837e39e02179 100644
--- a/cmd/nvedit_efi.c
+++ b/cmd/nvedit_efi.c
@@ -458,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.
@@ -517,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")) {
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 12/16] efi_loader, pytest: set up secure boot environment
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (10 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 11/16] cmd: env: add "-at" option to "env set -e" command AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 13/16] efi_loader, pytest: add UEFI secure boot tests (authenticated variables) AKASHI Takahiro
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

A fixture for UEFI secure boot tests (image authentication and variable
authentication) is defined. A small file system with test data in a single
partition formatted in fat is created.

This test requires efitools v1.5.2 or later. If the system's efitools
is older, you have to build it on your own and define EFITOOLS_PATH.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 test/py/README.md                          |   8 ++
 test/py/tests/test_efi_secboot/conftest.py | 151 +++++++++++++++++++++
 test/py/tests/test_efi_secboot/defs.py     |  21 +++
 3 files changed, 180 insertions(+)
 create mode 100644 test/py/tests/test_efi_secboot/conftest.py
 create mode 100644 test/py/tests/test_efi_secboot/defs.py

diff --git a/test/py/README.md b/test/py/README.md
index 3cbe01b73e28..aa8a5607b064 100644
--- a/test/py/README.md
+++ b/test/py/README.md
@@ -37,7 +37,15 @@ will be required.  The following is an incomplete list:
 | openssl        |
 | sudo OR guestmount |
 | e2fsprogs      |
+| util-linux     |
+| coreutils      |
 | dosfstools     |
+| efitools       |
+| mount          |
+| mtools         |
+| sbsigntool     |
+| udisks2        |
+
 
 Please use the apporirate commands for your distribution to match these tools
 up with the package that provides them.
diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py
new file mode 100644
index 000000000000..e542fef6e819
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/conftest.py
@@ -0,0 +1,151 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import pytest
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+from defs import *
+
+# from test/py/conftest.py
+def tool_is_in_path(tool):
+    for path in os.environ["PATH"].split(os.pathsep):
+        fn = os.path.join(path, tool)
+        if os.path.isfile(fn) and os.access(fn, os.X_OK):
+            return True
+    return False
+
+#
+# Fixture for UEFI secure boot test
+#
+ at pytest.fixture(scope='session')
+def efi_boot_env(request, u_boot_config):
+    """Set up a file system to be used in UEFI secure boot test.
+
+    Args:
+        request: Pytest request object.
+	u_boot_config: U-boot configuration.
+
+    Return:
+        A path to disk image to be used for testing
+    """
+    global HELLO_PATH
+
+    image_path = u_boot_config.persistent_data_dir
+    image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME
+    image_size = EFI_SECBOOT_IMAGE_SIZE
+    part_size = EFI_SECBOOT_PART_SIZE
+    fs_type = EFI_SECBOOT_FS_TYPE
+
+    if HELLO_PATH == '':
+        HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi'
+
+    try:
+        non_root = tool_is_in_path('udisksctl')
+
+        # create a disk/partition
+        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
+                            % (image_path, image_size), shell=True)
+        check_call('sgdisk %s -n 1:0:+%dMiB'
+                            % (image_path, part_size), shell=True)
+        # create a file system
+        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
+                            % (image_path, part_size), shell=True)
+        check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True)
+        check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
+                            % (image_path, image_path, 1), shell=True)
+        check_call('rm %s.tmp' % image_path, shell=True)
+        if non_root:
+            out_data = check_output('udisksctl loop-setup -f %s -o %d'
+                                % (image_path, 1048576), shell=True).decode()
+            m = re.search('(?<= as )(.*)\.', out_data)
+            loop_dev = m.group(1)
+            # print 'loop device is: %s' % loop_dev
+            out_data = check_output('udisksctl info -b %s'
+                                % loop_dev, shell=True).decode()
+            m = re.search('MountPoints:[ \t]+(.*)', out_data)
+            mnt_point = m.group(1)
+        else:
+            loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
+                                % (part_size, image_path), shell=True).decode()
+            mnt_point = '/mnt'
+            check_output('sudo mount -t %s -o umask=000 %s %s'
+                                % (fs_type, loop_dev, mnt_point), shell=True)
+
+        # print 'mount point is: %s' % mnt_point
+
+        # suffix
+        # *.key: RSA private key in PEM
+        # *.crt: X509 certificate (self-signed) in PEM
+        # *.esl: signature list
+        # *.hash: message digest of image as signature list
+        # *.auth: signed signature list in signature database format
+        # *.efi: UEFI image
+        # *.efi.signed: signed UEFI image
+
+        # Create signature database
+        ## PK
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## PK_null for deletion
+        check_call('cd %s; sleep 2; touch PK_null.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
+                            % (mnt_point, EFITOOLS_PATH), shell=True)
+        ## KEK
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## db
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## db1
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db1.esl db1.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+        ## db1-update
+        check_call('cd %s; %ssign-efi-sig-list -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth'
+                            % (mnt_point, EFITOOLS_PATH), shell=True)
+        ## dbx
+        check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
+                            % mnt_point, shell=True)
+        check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
+                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                            shell=True)
+
+        # Copy image
+        check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True)
+
+        ## Sign image
+        check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
+                            % mnt_point, shell=True)
+        ## Digest image
+        check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
+                            % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+                            shell=True)
+
+        if non_root:
+            check_call('udisksctl unmount -b %s' % loop_dev, shell=True)
+            # not needed
+            # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True)
+        else:
+            check_call('sudo umount %s' % loop_dev, shell=True)
+            check_call('sudo losetup -d %s' % loop_dev, shell=True)
+
+    except CalledProcessError as e:
+        pytest.skip('Setup failed: %s' % e.cmd)
+        return
+    else:
+        yield image_path
+    finally:
+        call('rm -f %s' % image_path, shell=True)
diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py
new file mode 100644
index 000000000000..d6222809c547
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/defs.py
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier:      GPL-2.0+
+
+# Disk image name
+EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img'
+
+# Size in MiB
+EFI_SECBOOT_IMAGE_SIZE=16
+EFI_SECBOOT_PART_SIZE=8
+
+# Partition file system type
+EFI_SECBOOT_FS_TYPE='vfat'
+
+# Owner guid
+GUID='11111111-2222-3333-4444-123456789abc'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+EFITOOLS_PATH=''
+
+# Hello World application for sandbox
+HELLO_PATH=''
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 13/16] efi_loader, pytest: add UEFI secure boot tests (authenticated variables)
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (11 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 12/16] efi_loader, pytest: set up secure boot environment AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 14/16] efi_loader, pytest: add UEFI secure boot tests (image) AKASHI Takahiro
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

Provide a couple of test cases for variable authentication.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 .../py/tests/test_efi_secboot/test_authvar.py | 282 ++++++++++++++++++
 1 file changed, 282 insertions(+)
 create mode 100644 test/py/tests/test_efi_secboot/test_authvar.py

diff --git a/test/py/tests/test_efi_secboot/test_authvar.py b/test/py/tests/test_efi_secboot/test_authvar.py
new file mode 100644
index 000000000000..55dcaa95f1ea
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_authvar.py
@@ -0,0 +1,282 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Variable Authentication Test
+
+"""
+This test verifies variable authentication
+"""
+
+import pytest
+import re
+from defs import *
+
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('efi_secure_boot')
+ at pytest.mark.buildconfigspec('cmd_fat')
+ at pytest.mark.buildconfigspec('cmd_nvedit_efi')
+ at 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)
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 14/16] efi_loader, pytest: add UEFI secure boot tests (image)
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (12 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 13/16] efi_loader, pytest: add UEFI secure boot tests (authenticated variables) AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-18  0:45 ` [PATCH v4 15/16] sandbox: add extra configurations for UEFI and related tests AKASHI Takahiro
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

Provide test cases for
 * image authentication for signed images
   (test_efi_secboot/test_signed.py)
 * image authentication for unsigned images
   (test_efi_secboot/test_unsigned.py)

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 test/py/tests/test_efi_secboot/test_signed.py |  99 +++++++++++++++++
 .../tests/test_efi_secboot/test_unsigned.py   | 103 ++++++++++++++++++
 2 files changed, 202 insertions(+)
 create mode 100644 test/py/tests/test_efi_secboot/test_signed.py
 create mode 100644 test/py/tests/test_efi_secboot/test_unsigned.py

diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
new file mode 100644
index 000000000000..b185e36af2e3
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_signed.py
@@ -0,0 +1,99 @@
+# 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 *
+
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('efi_secure_boot')
+ at pytest.mark.buildconfigspec('cmd_efidebug')
+ at pytest.mark.buildconfigspec('cmd_fat')
+ at pytest.mark.buildconfigspec('cmd_nvedit_efi')
+ at 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)))
+
+        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)))
+
+        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)))
diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
new file mode 100644
index 000000000000..7e3727bbd6fe
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_unsigned.py
@@ -0,0 +1,103 @@
+# 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 *
+
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('efi_secure_boot')
+ at pytest.mark.buildconfigspec('cmd_efidebug')
+ at pytest.mark.buildconfigspec('cmd_fat')
+ at pytest.mark.buildconfigspec('cmd_nvedit_efi')
+ at 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)))
+
+    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)))
+
+        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)))
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 15/16] sandbox: add extra configurations for UEFI and related tests
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (13 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 14/16] efi_loader, pytest: add UEFI secure boot tests (image) AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2019-12-28  2:26   ` Simon Glass
  2019-12-18  0:45 ` [PATCH v4 16/16] travis: add packages for UEFI secure boot test AKASHI Takahiro
                   ` (2 subsequent siblings)
  17 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

Adding those extra configurations allows us to successfully run UEFI
secure boot pytest on Travis CI.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 configs/sandbox64_defconfig | 3 +++
 configs/sandbox_defconfig   | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index b0abf99386b9..c40e1d93465b 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -26,6 +26,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
@@ -52,6 +53,7 @@ CONFIG_CMD_DNS=y
 CONFIG_CMD_LINK_LOCAL=y
 CONFIG_CMD_ETHSW=y
 CONFIG_CMD_BMP=y
+CONFIG_CMD_EFIDEBUG=y
 CONFIG_CMD_TIME=y
 CONFIG_CMD_TIMER=y
 CONFIG_CMD_SOUND=y
@@ -194,6 +196,7 @@ CONFIG_CMD_DHRYSTONE=y
 CONFIG_TPM=y
 CONFIG_LZ4=y
 CONFIG_ERRNO_STR=y
+CONFIG_EFI_SECURE_BOOT=y
 CONFIG_TEST_FDTDEC=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ee0ec3f233d0..3246348341a4 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -29,6 +29,7 @@ CONFIG_CMD_ASKENV=y
 CONFIG_CMD_GREPENV=y
 CONFIG_CMD_ENV_CALLBACK=y
 CONFIG_CMD_ENV_FLAGS=y
+CONFIG_CMD_NVEDIT_EFI=y
 CONFIG_LOOPW=y
 CONFIG_CMD_MD5SUM=y
 CONFIG_CMD_MEMINFO=y
@@ -60,6 +61,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
@@ -221,6 +223,7 @@ CONFIG_CMD_DHRYSTONE=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
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 16/16] travis: add packages for UEFI secure boot test
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (14 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 15/16] sandbox: add extra configurations for UEFI and related tests AKASHI Takahiro
@ 2019-12-18  0:45 ` AKASHI Takahiro
  2020-01-08 23:11 ` [PATCH v4 00/16] efi_loader: add secure boot support Heinrich Schuchardt
  2020-01-09  0:08 ` Heinrich Schuchardt
  17 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2019-12-18  0:45 UTC (permalink / raw)
  To: u-boot

Pytest for UEFI secure boot will use several host commands.
In particular, Test setup relies on efitools, whose version must be v1.5.2
or later. So fetch a new version of deb package directly.
Please note it has a dependency on mtools, which must also be installed
along wih efitools.

In addition, the path, '/sbin', is added to PATH for use of sgdisk and
mkfs.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 .travis.yml | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index f6aec9677083..91b6d23ace98 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -38,6 +38,14 @@ addons:
     - libisl15
     - clang-7
     - srecord
+    - coreutils
+    - util-linux
+    - dosfstools
+    - gdisk
+    - mount
+    - mtools
+    - openssl
+    - sbsigntool
 
 install:
  # Clone uboot-test-hooks
@@ -58,10 +66,11 @@ install:
  - mkdir ~/grub2-arm64
  - ( cd ~/grub2-arm64; wget -O - http://download.opensuse.org/ports/aarch64/distribution/leap/42.2/repo/oss/suse/aarch64/grub2-arm64-efi-2.02~beta2-87.1.aarch64.rpm | rpm2cpio | cpio -di )
  - 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"
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 15/16] sandbox: add extra configurations for UEFI and related tests
  2019-12-18  0:45 ` [PATCH v4 15/16] sandbox: add extra configurations for UEFI and related tests AKASHI Takahiro
@ 2019-12-28  2:26   ` Simon Glass
  0 siblings, 0 replies; 45+ messages in thread
From: Simon Glass @ 2019-12-28  2:26 UTC (permalink / raw)
  To: u-boot

On Tue, 17 Dec 2019 at 17:44, AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
>
> Adding those extra configurations allows us to successfully run UEFI
> secure boot pytest on Travis CI.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  configs/sandbox64_defconfig | 3 +++
>  configs/sandbox_defconfig   | 3 +++
>  2 files changed, 6 insertions(+)

Reviewed-by: Simon Glass <sjg@chromium.org>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 05/16] efi_loader: variable: support variable authentication
  2019-12-18  0:45 ` [PATCH v4 05/16] efi_loader: variable: support variable authentication AKASHI Takahiro
@ 2020-01-08 22:54   ` Heinrich Schuchardt
  2020-01-17  5:35     ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-08 22:54 UTC (permalink / raw)
  To: u-boot

On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> With this commit, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
> is supported for authenticated variables and the system secure state
> will transfer between setup mode and user mode as UEFI specification
> section 32.3 describes.
>
> Internally, authentication data is stored as part of authenticated
> variable's value. It is nothing but a pkcs7 message (but we need some
> wrapper, see efi_variable_parse_signature()) and will be validated by
> efi_variable_authenticate(), hence efi_signature_verify_with_db().
>
> Associated time value will be encoded in "{...,time=...}" along with
> other UEFI variable's attributes.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_loader.h          |   3 +
>   lib/efi_loader/efi_variable.c | 665 ++++++++++++++++++++++++++++------
>   2 files changed, 564 insertions(+), 104 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 3b3618e0be24..1f88caf86709 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -175,6 +175,7 @@ extern const efi_guid_t efi_guid_image_security_database;
>   extern const efi_guid_t efi_guid_sha256;
>   extern const efi_guid_t efi_guid_cert_x509;
>   extern const efi_guid_t efi_guid_cert_x509_sha256;
> +extern const efi_guid_t efi_guid_cert_type_pkcs7;
>
>   extern unsigned int __efi_runtime_start, __efi_runtime_stop;
>   extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> @@ -723,6 +724,8 @@ efi_status_t efi_image_region_add(struct efi_image_regions *regs,
>
>   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);
>   #endif /* CONFIG_EFI_SECURE_BOOT */
>
>   #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
> diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> index 46f35bc60ba0..171b4abb4c58 100644
> --- a/lib/efi_loader/efi_variable.c
> +++ b/lib/efi_loader/efi_variable.c
> @@ -10,7 +10,13 @@
>   #include <env_internal.h>
>   #include <hexdump.h>
>   #include <malloc.h>
> +#include <rtc.h>
>   #include <search.h>
> +#include <linux/compat.h>
> +#include "../lib/crypto/pkcs7_parser.h"
> +
> +const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> +static bool efi_secure_boot;
>
>   #define READ_ONLY BIT(31)
>
> @@ -107,7 +113,7 @@ static const char *prefix(const char *str, const char *prefix)
>    * @attrp:	pointer to UEFI attributes
>    * Return:	pointer to remainder of U-Boot variable value
>    */
> -static const char *parse_attr(const char *str, u32 *attrp)
> +static const char *parse_attr(const char *str, u32 *attrp, u64 *timep)
>   {
>   	u32 attr = 0;
>   	char sep = '{';
> @@ -130,6 +136,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,48 +159,291 @@ static const char *parse_attr(const char *str, u32 *attrp)
>   }
>
>   /**
> - * efi_get_variable() - retrieve value of a UEFI variable
> + * efi_secure_boot_enabled - return if secure boot is enabled or not
>    *
> - * This function implements the GetVariable runtime service.
> + * 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
>    *
> - * See the Unified Extensible Firmware Interface (UEFI) specification for
> - * details.
> + * 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.
>    *
> - * @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:	Pointer to pkcs7_message structure on success, NULL 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 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;
> +	const 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);
> +
> +	timestamp = &auth->time_stamp;

This does not compile with GCC 9.2.1:

lib/efi_loader/efi_variable.c: In function ‘efi_variable_authenticate’:
lib/efi_loader/efi_variable.c:554:14: error: taking address of packed
member of ‘struct efi_variable_authentication_2’ may result in an
unaligned pointer value [-Werror=address-of-packed-member]
   554 |  timestamp = &auth->time_stamp;
       |              ^~~~~~~~~~~~~~~~~

Best regards

Heinrich

> +	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;
>
> @@ -197,7 +452,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;
> @@ -208,11 +463,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)"))) {
> @@ -225,8 +482,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';
> @@ -234,13 +493,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);
>   }
>
> @@ -273,6 +586,7 @@ static efi_status_t parse_uboot_variable(char *variable,
>   {
>   	char *guid, *name, *end, c;
>   	unsigned long name_len;
> +	u64 time;
>   	u16 *p;
>
>   	guid = strchr(variable, '_');
> @@ -307,7 +621,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;
>   }
> @@ -389,7 +703,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);
>
> @@ -402,143 +716,286 @@ 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);
>
> +out:
>   	if (env_set(native_name, val))
>   		ret = EFI_DEVICE_ERROR;
> +	else
> +		ret = EFI_SUCCESS;
>
> -out:
> +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));
>   }
>
>   /**
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (15 preceding siblings ...)
  2019-12-18  0:45 ` [PATCH v4 16/16] travis: add packages for UEFI secure boot test AKASHI Takahiro
@ 2020-01-08 23:11 ` Heinrich Schuchardt
  2020-01-09  0:08 ` Heinrich Schuchardt
  17 siblings, 0 replies; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-08 23:11 UTC (permalink / raw)
  To: u-boot

On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> One of major missing features in current UEFI implementation is "secure boot."
> The ultimate goal of my attempt is to implement image authentication based
> on signature and provide UEFI secure boot support which would be fully
> compliant with UEFI specification, section 32[1].
> (The code was originally developed by Patrick Wildt.)
>
> Please note, however, this patch doesn't work on its own; there are
> a couple of functional dependencies[2] and [3], that I have submitted
> before. For complete workable patch set, see my repository[4],
> which also contains experimental timestamp-based revocation suuport.
>
> My "non-volatile" support[5], which is under discussion, is not mandatory
> and so not included here, but this inevitably implies that, for example,
> signature database variables, like db and dbx, won't be persistent unless you
> explicitly run "env save" command and that UEFI variables are not separated
> from U-Boot environment. Anyhow, Linaro is also working on implementing
> real "secure storage" solution based on TF-A and OP-TEE.
>

I am missing a patch providing the necessary documentation explaining
how a user would:

* create a certificate to be used in conjunction with this patch series
* add the public key to U-Boot
* persist the public key
* sign a UEFI image
* which settings are needed for verified boot

Best regards

Heinrich

>
> Supported features:
> * image authentication based on db and dbx
> * supported signature types are
>      EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
>      EFI_CERT_X509_GUID (x509 certificate for signed images)
> * SecureBoot/SignatureSupport variables
> * SetupMode and user mode
> * variable authentication based on PK and KEK
>      EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
> * basic pytest test cases
>
> Unsupported features: (marked as TODO in most cases in the source code,
> 			and won't be included in this series)
> * hash algorithms other than SHA256
> * dbt: timestamp(RFC6131)-based certificate revocation
> * dbr: OS recovery
> * xxxDefault: default values for signature stores
> * transition to AuditMode and DeployedMode
> * recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
> * verification "policy", in particular, check against signature's owner
> * private authenticated variables
> * variable authentication with EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS
> * real secure storage support, including hardware-specific PK (Platform Key)
>    installation
>
> TODO's other than "Unsupported features": (won't be included in this series)
> * fail recovery, in particular, in modifying authenticated variables
> * support read-only attributes of well-defined global variables
>    in particular, "SignatureSupport"
> * Extensive test suite (or more test cases) to confirm compatibility
>    with EDK2
> 	=> I requested EDK SCT community to add tests[6].
>
> Test:
> * my pytest, included in this patch set, passed.
> * efi_selftest passed. (At least no regression.)
> * Travis CI tests have passed.
>
> Known issues:
> * efitools is used in pytest, and its version must be v1.5.2 or later.
>    (Solution: You can define EFITOOLS_PATH in defs.py for your own efitools.)
> * Pytest depends on standalone "helloworld" app for sandbox
>    (Solution: You can define HELLO_PATH in defs.py or Heinrich's [7].)
>
>
> Hints about how to use:
> (Please see other documents, or my pytest scripts, for details.)
> * You can create your own certificates with openssl.
> * You can sign your application with sbsign (on Ubuntu).
> * You can create raw data for signature database with efitools, and
>    install/manage authenticated variables with "env -set -e" command
>    or efitools' "UpdateVars.efi" application.
>
>
> [1] https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf
> [2] https://lists.denx.de/pipermail/u-boot/2019-November/390127.html
>      (import x509/pkcs7 parsers from linux)
> [3] https://lists.denx.de/pipermail/u-boot/2019-December/393977.html
>      (extend rsa_verify() for UEFI secure boot)
> [4] http://git.linaro.org/people/takahiro.akashi/u-boot.git/ efi/secboot
> [5] https://lists.denx.de/pipermail/u-boot/2019-September/382835.html
>      (non-volatile variables support)
> [6] https://bugzilla.tianocore.org/show_bug.cgi?id=2230
> [7] https://lists.denx.de/pipermail/u-boot/2019-November/389593.html
>
>
> Changes in v4 (Dec 18, 2019)
> * adjust EFI_SECURE_BOOT dependencies due to a change of RSA extension patch v5
>    (patch#2)
> * change "imply" to "select" against kconfig dependencies (patch#2)
> * otherwise, no functional changes
>
> Changes in v3 (Dec 9, 2019)
> * allow for arbitrary number of regions in efi_image_region_add()
>    (patch#3, #5 and #8)
> * remove a redundant check in a while loop at efi_sigstore_free() (patch#4)
>
> Changes in v2 (Nov 26, 2019)
> * rebased to v2020.01-rc3
> * rename IMAGE_DIRECTORY_ENTRY_CERTTABLE to IMAGE_DIRECTORY_ENTRY_SECURITY
>    (patch#1,#9)
> * add comments (patch#1)
> * drop v1's patch#2 as it is no longer necessary
> * drop v1's patch#3 as other "SECURE_BOOT" architectures have renamed
>    this option and no longer use it
> * add structure descriptions (patch#3)
> * rework hash calculation code in efi_signature_verify() and remove
>    an odd constant, WinIndrectSha256 (patch#3)
> * move travis.yml changes to a separate patch (patch#12, #16)
> * yield_fixture() -> fixture() (patch#12)
> * call console.restart_uboot() at every test case (13,#14)
> * add patch#15; enable UEFI-related configurations by default on sandbox
> * add patch#16; modify Travis CI environment to run UEFI secure boot test
>
> Changes in v1 (Nov 13, 2019)
> * rebased to v2020.01-rc
> * remove already-merged patches
> * re-work the patch set for easier reviews, including
>    - move a config definition patch forward (patch#4)
>    - refactor/rename verification functions (patch#5/#10)
>    - split signature database parser as a separate patch (patch#6)
>    - split secure state transition code as a separate patch (patch#8)
>    - move most part of init_secure_boot() into init_variables() (patch#8)
>    - split test environment setup from test patches (patch#14)
> * add function descriptions (patch#5-#11)
> * make sure the section list is sorted in ascending order in hash
>    calculation of PE image (patch#10)
> * add a new "-at" (authenticated access) option to "env -e" (patch#13)
> * list required host packages, in particular udisks2, in pytest
>    (patch#14)
> * modify conftest.py to run under python3 (patch#14)
> * use a partition on a disk instead of a whole disk without partition
>    table (patch#14)
> * reduce dependency on efitools, yet relying on its host tools (patch#14)
> * modify pytests to catch up wth latest changes of "env -e" syntax
>    (patch#15,#16)
>
> RFC (Sept 18, 2019)
>
> AKASHI Takahiro (16):
>    include: pe.h: add signature-related definitions
>    efi_loader: add CONFIG_EFI_SECURE_BOOT config option
>    efi_loader: add signature verification functions
>    efi_loader: add signature database parser
>    efi_loader: variable: support variable authentication
>    efi_loader: variable: add secure boot state transition
>    efi_loader: variable: add VendorKeys variable
>    efi_loader: image_loader: support image authentication
>    efi_loader: set up secure boot
>    cmd: env: use appropriate guid for authenticated UEFI variable
>    cmd: env: add "-at" option to "env set -e" command
>    efi_loader, pytest: set up secure boot environment
>    efi_loader, pytest: add UEFI secure boot tests (authenticated
>      variables)
>    efi_loader, pytest: add UEFI secure boot tests (image)
>    sandbox: add extra configurations for UEFI and related tests
>    travis: add packages for UEFI secure boot test
>
>   .travis.yml                                   |  11 +-
>   cmd/nvedit.c                                  |   5 +-
>   cmd/nvedit_efi.c                              |  23 +-
>   configs/sandbox64_defconfig                   |   3 +
>   configs/sandbox_defconfig                     |   3 +
>   include/efi_api.h                             |  87 ++
>   include/efi_loader.h                          |  85 +-
>   include/pe.h                                  |  18 +
>   lib/efi_loader/Kconfig                        |  19 +
>   lib/efi_loader/Makefile                       |   1 +
>   lib/efi_loader/efi_boottime.c                 |   2 +-
>   lib/efi_loader/efi_image_loader.c             | 454 ++++++++-
>   lib/efi_loader/efi_setup.c                    |  38 +
>   lib/efi_loader/efi_signature.c                | 810 +++++++++++++++
>   lib/efi_loader/efi_variable.c                 | 951 ++++++++++++++++--
>   test/py/README.md                             |   8 +
>   test/py/tests/test_efi_secboot/conftest.py    | 151 +++
>   test/py/tests/test_efi_secboot/defs.py        |  21 +
>   .../py/tests/test_efi_secboot/test_authvar.py | 282 ++++++
>   test/py/tests/test_efi_secboot/test_signed.py |  99 ++
>   .../tests/test_efi_secboot/test_unsigned.py   | 103 ++
>   21 files changed, 3046 insertions(+), 128 deletions(-)
>   create mode 100644 lib/efi_loader/efi_signature.c
>   create mode 100644 test/py/tests/test_efi_secboot/conftest.py
>   create mode 100644 test/py/tests/test_efi_secboot/defs.py
>   create mode 100644 test/py/tests/test_efi_secboot/test_authvar.py
>   create mode 100644 test/py/tests/test_efi_secboot/test_signed.py
>   create mode 100644 test/py/tests/test_efi_secboot/test_unsigned.py
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2019-12-18  0:45 ` [PATCH v4 08/16] efi_loader: image_loader: support image authentication AKASHI Takahiro
@ 2020-01-08 23:55   ` Heinrich Schuchardt
  2020-01-17  5:11     ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-08 23:55 UTC (permalink / raw)
  To: u-boot

On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> With this commit, image validation can be enforced, as UEFI specification
> section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
>
> Currently we support
> * authentication based on db and dbx,
>    so dbx-validated image will always be rejected.
> * following signature types:
>      EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
>      EFI_CERT_X509_GUID (x509 certificate for signed images)
> Timestamp-based certificate revocation is not supported here.
>
> Internally, authentication data is stored in one of certificates tables
> of PE image (See efi_image_parse()) and will be verified by
> efi_image_authenticate() before loading a given image.
>
> It seems that UEFI specification defines the verification process
> in a bit ambiguous way. I tried to implement it as closely to as
> EDK2 does.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_loader.h              |   7 +-
>   lib/efi_loader/efi_boottime.c     |   2 +-
>   lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
>   3 files changed, 449 insertions(+), 14 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 1f88caf86709..e12b49098fb0 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -11,6 +11,7 @@
>   #include <common.h>
>   #include <part_efi.h>
>   #include <efi_api.h>
> +#include <pe.h>
>
>   static inline int guidcmp(const void *g1, const void *g2)
>   {
> @@ -398,7 +399,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);
> @@ -726,6 +728,9 @@ 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) */
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 493d906c641d..311681764034 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
>   	efi_dp_split_file_path(file_path, &dp, &fp);
>   	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
>   	if (ret == EFI_SUCCESS)
> -		ret = efi_load_pe(*image_obj, dest_buffer, info);
> +		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
>   	if (!source_buffer)
>   		/* Release buffer to which file was loaded */
>   		efi_free_pages((uintptr_t)dest_buffer,
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> index 13541cfa7a28..939758e61e3c 100644
> --- a/lib/efi_loader/efi_image_loader.c
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -9,7 +9,9 @@
>
>   #include <common.h>
>   #include <efi_loader.h>
> +#include <malloc.h>
>   #include <pe.h>
> +#include "../lib/crypto/pkcs7_parser.h"
>
>   const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
>   const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
> @@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
>   	}
>   }
>
> +#ifdef CONFIG_EFI_SECURE_BOOT
> +/**
> + * efi_image_parse - parse a PE image
> + * @efi:	Pointer to image
> + * @len:	Size of @efi
> + * @regs:	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)


This function is way too long (> 100 lines). Please, cut it into logical
units.



> +{
> +	struct efi_image_regions *regs;
> +	IMAGE_DOS_HEADER *dos;
> +	IMAGE_NT_HEADERS32 *nt;
> +	IMAGE_SECTION_HEADER *sections, **sorted;
> +	int num_regions, num_sections, i, j;
> +	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.
> +	 * As we can assume that it is already ordered in most cases,
> +	 * the following code is optimized for this.
> +	 */
> +	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {

If sections[0] is not the lowest entry this function fails.

Please, use qsort() supplied in lib/qsort.c.

> +		if (sorted[i - 1]->VirtualAddress
> +				<= sections[i].VirtualAddress) {
> +			sorted[i] = &sections[i];
> +		} else {
> +			if (i == 1) {
> +				sorted[1] = sorted[0];
> +				sorted[0] = &sections[1];
> +				continue;
> +			}
> +
> +			sorted[i] = sorted[i - 1];
> +			for (j = i - 2; j >= 0; j--) {
> +				if (!j || sorted[j]->VirtualAddress
> +						<= sections[i].VirtualAddress) {
> +					sorted[j + 1] = &sections[i];
> +					continue;
> +				} else {
> +					sorted[j + 1] = sorted[j];
> +				}
> +			}
> +		}
> +	}
> +
> +	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
> + * @len:	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 len)
> +{
> +	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;
> +	bool ret = false;
> +
> +	if (!efi_secure_boot_enabled())
> +		return true;
> +
> +	if (!efi_image_parse(efi, len, &regs, &wincerts,
> +			     &wincerts_len)) {
> +		debug("Parsing PE executable image failed\n");
> +		return false;
> +	}
> +
> +	if (!wincerts) {
> +		/* The image is not signed */
> +		ret = efi_image_unsigned_authenticate(regs);
> +		free(regs);
> +
> +		return ret;
> +	}
> +
> +	/*
> +	 * verify signature using db and dbx
> +	 */
> +	db = efi_sigstore_parse_sigdb(L"db");
> +	if (!db) {
> +		debug("Getting signature database(db) failed\n");
> +		goto err;
> +	}
> +
> +	dbx = efi_sigstore_parse_sigdb(L"dbx");
> +	if (!dbx) {
> +		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);
> +
> +	return ret;
> +}
> +#else
> +static bool efi_image_authenticate(void *efi, size_t len)
> +{
> +	return true;
> +}
> +#endif /* CONFIG_EFI_SECURE_BOOT */
> +
>   /**
>    * efi_load_pe() - relocate EFI binary
>    *
> @@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
>    * @loaded_image_info:	loaded image protocol
>    * Return:		status code
>    */
> -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
> +			 void *efi, size_t efi_size,
>   			 struct efi_loaded_image *loaded_image_info)
>   {
>   	IMAGE_NT_HEADERS32 *nt;
> @@ -231,17 +595,57 @@ 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;
> +	void *new_efi = NULL;
> +	size_t new_efi_size;
> +	efi_status_t ret;
> +
> +	/*
> +	 * 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 EFI_OUT_OF_RESOURCES;
> +		memcpy(new_efi, efi, efi_size);
> +		efi = new_efi;
> +		efi_size = new_efi_size;
> +	}
> +
> +	/* 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++)
> @@ -253,14 +657,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)) {
> +		ret = EFI_ACCESS_DENIED;

According to the UEFI specification LoadImage() should return
EFI_SECURITY_VIOLATION in this case.

Further behaviour should depend on variables AuditMode and DeployedMode.

If authentication fails, you must update the configuration table
identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
Execution Information. If the authentication succeeds you may enter a
record.

It seems to me that in your patch series you are not creating the
configuration table at all.

The content of the Image Execution Information Table should be used to
decide if StartImage() may start an image. This is still missing in the
patch series.

Best regards

Heinrich

> +		goto err;
> +	}
> +
> +	/* 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,
> @@ -279,7 +698,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;
> @@ -295,7 +715,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;
> @@ -304,13 +725,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--) {
> @@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>   	loaded_image_info->image_size = virt_size;
>
>   	return EFI_SUCCESS;
> +
> +err:
> +	free(new_efi);
> +
> +	return ret;
>   }
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
                   ` (16 preceding siblings ...)
  2020-01-08 23:11 ` [PATCH v4 00/16] efi_loader: add secure boot support Heinrich Schuchardt
@ 2020-01-09  0:08 ` Heinrich Schuchardt
  2020-01-09  8:02   ` Ilias Apalodimas
  17 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-09  0:08 UTC (permalink / raw)
  To: u-boot

On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> One of major missing features in current UEFI implementation is "secure boot."
> The ultimate goal of my attempt is to implement image authentication based
> on signature and provide UEFI secure boot support which would be fully
> compliant with UEFI specification, section 32[1].
> (The code was originally developed by Patrick Wildt.)
>
> Please note, however, this patch doesn't work on its own; there are
> a couple of functional dependencies[2] and [3], that I have submitted
> before. For complete workable patch set, see my repository[4],
> which also contains experimental timestamp-based revocation suuport.
>
> My "non-volatile" support[5], which is under discussion, is not mandatory
> and so not included here, but this inevitably implies that, for example,
> signature database variables, like db and dbx, won't be persistent unless you
> explicitly run "env save" command and that UEFI variables are not separated
> from U-Boot environment. Anyhow, Linaro is also working on implementing
> real "secure storage" solution based on TF-A and OP-TEE.
>

Device trees can be used for denial of service or to destroy hardware.

How will you address the validation of device trees?

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2020-01-09  0:08 ` Heinrich Schuchardt
@ 2020-01-09  8:02   ` Ilias Apalodimas
  2020-01-09 19:09     ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2020-01-09  8:02 UTC (permalink / raw)
  To: u-boot

On Thu, Jan 09, 2020 at 01:08:35AM +0100, Heinrich Schuchardt wrote:
> On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> > One of major missing features in current UEFI implementation is "secure boot."
> > The ultimate goal of my attempt is to implement image authentication based
> > on signature and provide UEFI secure boot support which would be fully
> > compliant with UEFI specification, section 32[1].
> > (The code was originally developed by Patrick Wildt.)
> > 
> > Please note, however, this patch doesn't work on its own; there are
> > a couple of functional dependencies[2] and [3], that I have submitted
> > before. For complete workable patch set, see my repository[4],
> > which also contains experimental timestamp-based revocation suuport.
> > 
> > My "non-volatile" support[5], which is under discussion, is not mandatory
> > and so not included here, but this inevitably implies that, for example,
> > signature database variables, like db and dbx, won't be persistent unless you
> > explicitly run "env save" command and that UEFI variables are not separated
> > from U-Boot environment. Anyhow, Linaro is also working on implementing
> > real "secure storage" solution based on TF-A and OP-TEE.
> > 
> 
> Device trees can be used for denial of service or to destroy hardware.
> 
> How will you address the validation of device trees?

Although this is really simple to solve, factoring in the different vendor
needs makes it quite complex.
There's a couple of options we can consider and not all of them are sane.

1. U-Boot embeds the DTB. This is straightforward. On Arm devices TF-A
verifies U-boot so you natively end up with a verified DTB. If U-Boot does not
include the proper DTB (as is the case for several devices), one can always
complite the correct DTB and compile with EXT_DTB.
2. Use https://github.com/jiazhang0/SELoader which verifies non-PE files
3. Add some custom code and use UEFI keyring to verify non PE files. This is a
bad idea though since you'll 'polute' the UEFI keyring.
4. FIT for DTB (??)

In any case UEFI job is to verify PE/COFF executables and that's what this patch
provides. DTB verification is beyond UEFI secure boot patches imho.

Regards
/Ilias
> 
> Best regards
> 
> Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2020-01-09  8:02   ` Ilias Apalodimas
@ 2020-01-09 19:09     ` Heinrich Schuchardt
  2020-01-09 20:03       ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-09 19:09 UTC (permalink / raw)
  To: u-boot

On 1/9/20 9:02 AM, Ilias Apalodimas wrote:
> On Thu, Jan 09, 2020 at 01:08:35AM +0100, Heinrich Schuchardt wrote:
>> On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
>>> One of major missing features in current UEFI implementation is "secure boot."
>>> The ultimate goal of my attempt is to implement image authentication based
>>> on signature and provide UEFI secure boot support which would be fully
>>> compliant with UEFI specification, section 32[1].
>>> (The code was originally developed by Patrick Wildt.)
>>>
>>> Please note, however, this patch doesn't work on its own; there are
>>> a couple of functional dependencies[2] and [3], that I have submitted
>>> before. For complete workable patch set, see my repository[4],
>>> which also contains experimental timestamp-based revocation suuport.
>>>
>>> My "non-volatile" support[5], which is under discussion, is not mandatory
>>> and so not included here, but this inevitably implies that, for example,
>>> signature database variables, like db and dbx, won't be persistent unless you
>>> explicitly run "env save" command and that UEFI variables are not separated
>>> from U-Boot environment. Anyhow, Linaro is also working on implementing
>>> real "secure storage" solution based on TF-A and OP-TEE.
>>>
>>
>> Device trees can be used for denial of service or to destroy hardware.
>>
>> How will you address the validation of device trees?
>
> Although this is really simple to solve, factoring in the different vendor
> needs makes it quite complex.
> There's a couple of options we can consider and not all of them are sane.
>
> 1. U-Boot embeds the DTB. This is straightforward. On Arm devices TF-A
> verifies U-boot so you natively end up with a verified DTB. If U-Boot does not
> include the proper DTB (as is the case for several devices), one can always
> complite the correct DTB and compile with EXT_DTB.
> 2. Use https://github.com/jiazhang0/SELoader which verifies non-PE files
> 3. Add some custom code and use UEFI keyring to verify non PE files. This is a
> bad idea though since you'll 'polute' the UEFI keyring.
> 4. FIT for DTB (??)
>
> In any case UEFI job is to verify PE/COFF executables and that's what this patch
> provides. DTB verification is beyond UEFI secure boot patches imho.
>
> Regards
> /Ilias

The UEFI specification does not mention device trees at all. EDK2 is
based on ACPI tables.

We already have verified boot via signed UEFI FIT images which can
contain an UEFI image and a device tree. So for verified boot of Linux
you would simply package shim and the device tree into a FIT image. Shim
would verify GRUB and GRUB would verify the kernel and the ramdisk. In
this scenario we don't need the current patch series at all and it
integrates well with distributions like Debian which provide shim for
arm64, cf. https://packages.debian.org/de/bullseye/shim-signed .

If we implement secure boot according the UEFI specification, one option
would be to package the device tree as a UEFI driver image and let the
stub install it as a configuration table. The unload callback could be
used to remove the device tree.

Best regards

Heinrich

>>
>> Best regards
>>
>> Heinrich
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2020-01-09 19:09     ` Heinrich Schuchardt
@ 2020-01-09 20:03       ` Ilias Apalodimas
  2020-01-17  5:59         ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Ilias Apalodimas @ 2020-01-09 20:03 UTC (permalink / raw)
  To: u-boot

On Thu, Jan 09, 2020 at 08:09:27PM +0100, Heinrich Schuchardt wrote:
> On 1/9/20 9:02 AM, Ilias Apalodimas wrote:
> > On Thu, Jan 09, 2020 at 01:08:35AM +0100, Heinrich Schuchardt wrote:
> > > On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> > > > One of major missing features in current UEFI implementation is "secure boot."
> > > > The ultimate goal of my attempt is to implement image authentication based
> > > > on signature and provide UEFI secure boot support which would be fully
> > > > compliant with UEFI specification, section 32[1].
> > > > (The code was originally developed by Patrick Wildt.)
> > > > 
> > > > Please note, however, this patch doesn't work on its own; there are
> > > > a couple of functional dependencies[2] and [3], that I have submitted
> > > > before. For complete workable patch set, see my repository[4],
> > > > which also contains experimental timestamp-based revocation suuport.
> > > > 
> > > > My "non-volatile" support[5], which is under discussion, is not mandatory
> > > > and so not included here, but this inevitably implies that, for example,
> > > > signature database variables, like db and dbx, won't be persistent unless you
> > > > explicitly run "env save" command and that UEFI variables are not separated
> > > > from U-Boot environment. Anyhow, Linaro is also working on implementing
> > > > real "secure storage" solution based on TF-A and OP-TEE.
> > > > 
> > > 
> > > Device trees can be used for denial of service or to destroy hardware.
> > > 
> > > How will you address the validation of device trees?
> > 
> > Although this is really simple to solve, factoring in the different vendor
> > needs makes it quite complex.
> > There's a couple of options we can consider and not all of them are sane.
> > 
> > 1. U-Boot embeds the DTB. This is straightforward. On Arm devices TF-A
> > verifies U-boot so you natively end up with a verified DTB. If U-Boot does not
> > include the proper DTB (as is the case for several devices), one can always
> > complite the correct DTB and compile with EXT_DTB.
> > 2. Use https://github.com/jiazhang0/SELoader which verifies non-PE files
> > 3. Add some custom code and use UEFI keyring to verify non PE files. This is a
> > bad idea though since you'll 'polute' the UEFI keyring.
> > 4. FIT for DTB (??)
> > 
> > In any case UEFI job is to verify PE/COFF executables and that's what this patch
> > provides. DTB verification is beyond UEFI secure boot patches imho.
> > 
> > Regards
> > /Ilias
> 
> The UEFI specification does not mention device trees at all. EDK2 is
> based on ACPI tables.

On one of the platforms i kknow of (socionext synquacer), it also provides DTB
as part of the firmware, which is identical to proposeal (1) I mentioned.

> 
> We already have verified boot via signed UEFI FIT images which can
> contain an UEFI image and a device tree. So for verified boot of Linux
> you would simply package shim and the device tree into a FIT image. Shim
> would verify GRUB and GRUB would verify the kernel and the ramdisk. In
> this scenario we don't need the current patch series at all and it
> integrates well with distributions like Debian which provide shim for
> arm64, cf. https://packages.debian.org/de/bullseye/shim-signed .

Of course everything is verified, but that's not UEFI secure boot. It's similar
but the verification does not go through DB/DBX and there are no secure
variables, so the current patchset has value.

> 
> If we implement secure boot according the UEFI specification, one option
> would be to package the device tree as a UEFI driver image and let the
> stub install it as a configuration table. The unload callback could be
> used to remove the device tree.
> 

Sure but this is not in scope for the current patchset is it?
Similarly you can just include the DTB in U-Boot and naturally have it verified.

I am not arguing that DTB verification is needed. We absolutely agree on that.
All i am saying is that the extra functionality can be added in the future,
since we already have a valid way of providing it with the current patchset.

Regards
/Ilias
> 
> > > 
> > > Best regards
> > > 
> > > Heinrich
> > 
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 03/16] efi_loader: add signature verification functions
  2019-12-18  0:44 ` [PATCH v4 03/16] efi_loader: add signature verification functions AKASHI Takahiro
@ 2020-01-14 23:43   ` Heinrich Schuchardt
  2020-01-15  0:13     ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-14 23:43 UTC (permalink / raw)
  To: u-boot

On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> In this commit, implemented are a couple of helper functions which will be
> used to materialize variable authentication as well as image authentication
> in later patches.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_api.h              |  87 +++++
>   include/efi_loader.h           |  72 ++++
>   lib/efi_loader/Makefile        |   1 +
>   lib/efi_loader/efi_signature.c | 584 +++++++++++++++++++++++++++++++++
>   4 files changed, 744 insertions(+)
>   create mode 100644 lib/efi_loader/efi_signature.c
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 22396172e15f..47f24fc90873 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -18,6 +18,7 @@
>
>   #include <efi.h>
>   #include <charset.h>
> +#include <pe.h>
>
>   #ifdef CONFIG_EFI_LOADER
>   #include <asm/setjmp.h>
> @@ -307,6 +308,10 @@ struct efi_runtime_services {
>   	EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
>   		 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
>
> +#define EFI_IMAGE_SECURITY_DATABASE_GUID \
> +	EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, \
> +		 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
> +
>   #define EFI_FDT_GUID \
>   	EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \
>   		 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
> @@ -1616,4 +1621,86 @@ struct efi_unicode_collation_protocol {
>   #define LOAD_OPTION_CATEGORY_BOOT	0x00000000
>   #define LOAD_OPTION_CATEGORY_APP	0x00000100
>
> +/* Certificate types in signature database */
> +#define EFI_CERT_SHA256_GUID \
> +	EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, \
> +		 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28)
> +#define EFI_CERT_RSA2048_GUID \
> +	EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, \
> +		 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6)
> +#define EFI_CERT_X509_GUID \
> +	EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, \
> +		 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
> +#define EFI_CERT_X509_SHA256_GUID \
> +	EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
> +		 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed)
> +#define EFI_CERT_TYPE_PKCS7_GUID \
> +	EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
> +		 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
> +
> +/**
> + * win_certificate_uefi_guid - A certificate that encapsulates
> + * a GUID-specific signature
> + *
> + * @hdr:	Windows certificate header
> + * @cert_type:	Certificate type
> + * @cert_data:	Certificate data
> + */
> +struct win_certificate_uefi_guid {
> +	WIN_CERTIFICATE	hdr;
> +	efi_guid_t	cert_type;
> +	u8		cert_data[];
> +} __attribute__((__packed__));
> +
> +/**
> + * efi_variable_authentication_2 - A time-based authentication method
> + * descriptor
> + *
> + * This structure describes an authentication information for
> + * a variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
> + * and should be included as part of a variable's value.
> + * Only EFI_CERT_TYPE_PKCS7_GUID is accepted.
> + *
> + * @time_stamp:	Descriptor's time stamp
> + * @auth_info:	Authentication info
> + */
> +struct efi_variable_authentication_2 {
> +	struct efi_time			 time_stamp;
> +	struct win_certificate_uefi_guid auth_info;
> +} __attribute__((__packed__));
> +
> +/**
> + * efi_signature_data - A format of signature
> + *
> + * This structure describes a single signature in signature database.
> + *
> + * @signature_owner:	Signature owner
> + * @signature_data:	Signature data
> + */
> +struct efi_signature_data {
> +	efi_guid_t	signature_owner;
> +	u8		signature_data[];
> +} __attribute__((__packed__));
> +
> +/**
> + * efi_signature_list - A format of signature database
> + *
> + * This structure describes a list of signatures with the same type.
> + * An authenticated variable's value is a concatenation of one or more
> + * efi_signature_list's.
> + *
> + * @signature_type:		Signature type
> + * @signature_list_size:	Size of signature list
> + * @signature_header_size:	Size of signature header
> + * @signature_size:		Size of signature
> + */
> +struct efi_signature_list {
> +	efi_guid_t	signature_type;
> +	u32		signature_list_size;
> +	u32		signature_header_size;
> +	u32		signature_size;
> +/*	u8		signature_header[signature_header_size]; */
> +/*	struct efi_signature_data signatures[...][signature_size]; */
> +} __attribute__((__packed__));
> +
>   #endif
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 381da80cdce0..3ca68f9bbb6e 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -21,6 +21,7 @@ static inline int guidcmp(const void *g1, const void *g2)
>   #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
> @@ -169,6 +170,11 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol;
>   extern const efi_guid_t efi_guid_hii_config_access_protocol;
>   extern const efi_guid_t efi_guid_hii_database_protocol;
>   extern const efi_guid_t efi_guid_hii_string_protocol;
> +/* 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 unsigned int __efi_runtime_start, __efi_runtime_stop;
>   extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> @@ -650,6 +656,72 @@ 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

This constraint surrounds the whole code. So, please, move the
constraint to the Makefile.

Best regards

Heinrich

> +#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);
> +#endif /* CONFIG_EFI_SECURE_BOOT */
> +
>   #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
>
>   /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index 7db406028618..3ffbfe78a46b 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -42,3 +42,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o
>   obj-$(CONFIG_NET) += efi_net.o
>   obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
>   obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
> +obj-y += efi_signature.o
> diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
> new file mode 100644
> index 000000000000..823d3311e010
> --- /dev/null
> +++ b/lib/efi_loader/efi_signature.c
> @@ -0,0 +1,584 @@
> +// 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 <pe.h>
> +#include <linux/compat.h>
> +#include <linux/oid_registry.h>
> +#include <u-boot/rsa.h>
> +#include <u-boot/sha256.h>
> +/*
> + * avoid duplicated inclusion:
> + * #include "../lib/crypto/x509_parser.h"
> + */
> +#include "../lib/crypto/pkcs7_parser.h"
> +
> +const efi_guid_t efi_guid_image_security_database =
> +		EFI_IMAGE_SECURITY_DATABASE_GUID;
> +const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
> +const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
> +const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
> +const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
> +
> +#ifdef CONFIG_EFI_SECURE_BOOT
> +
> +/**
> + * 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;
> +		} else {
> +			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;
> +}
> +#endif /* CONFIG_EFI_SECURE_BOOT */
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 03/16] efi_loader: add signature verification functions
  2020-01-14 23:43   ` Heinrich Schuchardt
@ 2020-01-15  0:13     ` Heinrich Schuchardt
  2020-01-17  2:20       ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-15  0:13 UTC (permalink / raw)
  To: u-boot

On 1/15/20 12:43 AM, Heinrich Schuchardt wrote:
> On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
>> In this commit, implemented are a couple of helper functions which
>> will be
>> used to materialize variable authentication as well as image
>> authentication
>> in later patches.
>>
>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> ---
>>   include/efi_api.h              |  87 +++++
>>   include/efi_loader.h           |  72 ++++
>>   lib/efi_loader/Makefile        |   1 +
>>   lib/efi_loader/efi_signature.c | 584 +++++++++++++++++++++++++++++++++
>>   4 files changed, 744 insertions(+)
>>   create mode 100644 lib/efi_loader/efi_signature.c
>>
>> diff --git a/include/efi_api.h b/include/efi_api.h
>> index 22396172e15f..47f24fc90873 100644
>> --- a/include/efi_api.h
>> +++ b/include/efi_api.h
>> @@ -18,6 +18,7 @@
>>
>>   #include <efi.h>
>>   #include <charset.h>
>> +#include <pe.h>
>>
>>   #ifdef CONFIG_EFI_LOADER
>>   #include <asm/setjmp.h>
>> @@ -307,6 +308,10 @@ struct efi_runtime_services {
>>       EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
>>            0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
>>
>> +#define EFI_IMAGE_SECURITY_DATABASE_GUID \
>> +    EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, \
>> +         0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
>> +
>>   #define EFI_FDT_GUID \
>>       EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \
>>            0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
>> @@ -1616,4 +1621,86 @@ struct efi_unicode_collation_protocol {
>>   #define LOAD_OPTION_CATEGORY_BOOT    0x00000000
>>   #define LOAD_OPTION_CATEGORY_APP    0x00000100
>>
>> +/* Certificate types in signature database */
>> +#define EFI_CERT_SHA256_GUID \
>> +    EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, \
>> +         0x41, 0xf9, 0x36, 0x93, 0x43, 0x28)
>> +#define EFI_CERT_RSA2048_GUID \
>> +    EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, \
>> +         0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6)
>> +#define EFI_CERT_X509_GUID \
>> +    EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, \
>> +         0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
>> +#define EFI_CERT_X509_SHA256_GUID \
>> +    EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
>> +         0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed)
>> +#define EFI_CERT_TYPE_PKCS7_GUID \
>> +    EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
>> +         0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
>> +
>> +/**
>> + * win_certificate_uefi_guid - A certificate that encapsulates
>> + * a GUID-specific signature
>> + *
>> + * @hdr:    Windows certificate header
>> + * @cert_type:    Certificate type
>> + * @cert_data:    Certificate data
>> + */
>> +struct win_certificate_uefi_guid {
>> +    WIN_CERTIFICATE    hdr;
>> +    efi_guid_t    cert_type;
>> +    u8        cert_data[];
>> +} __attribute__((__packed__));
>> +
>> +/**
>> + * efi_variable_authentication_2 - A time-based authentication method
>> + * descriptor
>> + *
>> + * This structure describes an authentication information for
>> + * a variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
>> + * and should be included as part of a variable's value.
>> + * Only EFI_CERT_TYPE_PKCS7_GUID is accepted.
>> + *
>> + * @time_stamp:    Descriptor's time stamp
>> + * @auth_info:    Authentication info
>> + */
>> +struct efi_variable_authentication_2 {
>> +    struct efi_time             time_stamp;
>> +    struct win_certificate_uefi_guid auth_info;
>> +} __attribute__((__packed__));
>> +
>> +/**
>> + * efi_signature_data - A format of signature
>> + *
>> + * This structure describes a single signature in signature database.
>> + *
>> + * @signature_owner:    Signature owner
>> + * @signature_data:    Signature data
>> + */
>> +struct efi_signature_data {
>> +    efi_guid_t    signature_owner;
>> +    u8        signature_data[];
>> +} __attribute__((__packed__));
>> +
>> +/**
>> + * efi_signature_list - A format of signature database
>> + *
>> + * This structure describes a list of signatures with the same type.
>> + * An authenticated variable's value is a concatenation of one or more
>> + * efi_signature_list's.
>> + *
>> + * @signature_type:        Signature type
>> + * @signature_list_size:    Size of signature list
>> + * @signature_header_size:    Size of signature header
>> + * @signature_size:        Size of signature
>> + */
>> +struct efi_signature_list {
>> +    efi_guid_t    signature_type;
>> +    u32        signature_list_size;
>> +    u32        signature_header_size;
>> +    u32        signature_size;
>> +/*    u8        signature_header[signature_header_size]; */
>> +/*    struct efi_signature_data signatures[...][signature_size]; */
>> +} __attribute__((__packed__));
>> +
>>   #endif
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index 381da80cdce0..3ca68f9bbb6e 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -21,6 +21,7 @@ static inline int guidcmp(const void *g1, const void
>> *g2)
>>   #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
>> @@ -169,6 +170,11 @@ extern const efi_guid_t
>> efi_guid_hii_config_routing_protocol;
>>   extern const efi_guid_t efi_guid_hii_config_access_protocol;
>>   extern const efi_guid_t efi_guid_hii_database_protocol;
>>   extern const efi_guid_t efi_guid_hii_string_protocol;
>> +/* 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 unsigned int __efi_runtime_start, __efi_runtime_stop;
>>   extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
>> @@ -650,6 +656,72 @@ 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
>
> This constraint surrounds the whole code. So, please, move the
> constraint to the Makefile.
>
> Best regards
>
> Heinrich
>
>> +#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);
>> +#endif /* CONFIG_EFI_SECURE_BOOT */
>> +
>>   #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
>>
>>   /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub
>> it out */
>> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
>> index 7db406028618..3ffbfe78a46b 100644
>> --- a/lib/efi_loader/Makefile
>> +++ b/lib/efi_loader/Makefile
>> @@ -42,3 +42,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o
>>   obj-$(CONFIG_NET) += efi_net.o
>>   obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
>>   obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
>> +obj-y += efi_signature.o
>> diff --git a/lib/efi_loader/efi_signature.c
>> b/lib/efi_loader/efi_signature.c
>> new file mode 100644
>> index 000000000000..823d3311e010
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_signature.c
>> @@ -0,0 +1,584 @@
>> +// 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 <pe.h>
>> +#include <linux/compat.h>
>> +#include <linux/oid_registry.h>
>> +#include <u-boot/rsa.h>
>> +#include <u-boot/sha256.h>
>> +/*
>> + * avoid duplicated inclusion:
>> + * #include "../lib/crypto/x509_parser.h"
>> + */
>> +#include "../lib/crypto/pkcs7_parser.h"
>> +
>> +const efi_guid_t efi_guid_image_security_database =
>> +        EFI_IMAGE_SECURITY_DATABASE_GUID;
>> +const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
>> +const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
>> +const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
>> +const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
>> +
>> +#ifdef CONFIG_EFI_SECURE_BOOT

This is the #ifdef to move to the Makefile. In the previous mail I got
into the wrong line.

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 03/16] efi_loader: add signature verification functions
  2020-01-15  0:13     ` Heinrich Schuchardt
@ 2020-01-17  2:20       ` AKASHI Takahiro
  2020-01-17  5:37         ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-17  2:20 UTC (permalink / raw)
  To: u-boot

On Wed, Jan 15, 2020 at 01:13:36AM +0100, Heinrich Schuchardt wrote:
> On 1/15/20 12:43 AM, Heinrich Schuchardt wrote:
> >On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
(snip)
> >>diff --git a/lib/efi_loader/efi_signature.c
> >>b/lib/efi_loader/efi_signature.c
> >>new file mode 100644
> >>index 000000000000..823d3311e010
> >>--- /dev/null
> >>+++ b/lib/efi_loader/efi_signature.c
> >>@@ -0,0 +1,584 @@
> >>+// 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 <pe.h>
> >>+#include <linux/compat.h>
> >>+#include <linux/oid_registry.h>
> >>+#include <u-boot/rsa.h>
> >>+#include <u-boot/sha256.h>
> >>+/*
> >>+ * avoid duplicated inclusion:
> >>+ * #include "../lib/crypto/x509_parser.h"
> >>+ */
> >>+#include "../lib/crypto/pkcs7_parser.h"
> >>+
> >>+const efi_guid_t efi_guid_image_security_database =
> >>+        EFI_IMAGE_SECURITY_DATABASE_GUID;
> >>+const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
> >>+const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
> >>+const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
> >>+const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
> >>+
> >>+#ifdef CONFIG_EFI_SECURE_BOOT
> 
> This is the #ifdef to move to the Makefile. In the previous mail I got
> into the wrong line.

No.
As you can see, those guids may also be referred to by other files
(see efi_variable.c and cmd/nvedit_efi.c)
even in !EFI_SECURE_BOOT case, and I think that this file is best fit
for them.

-Takahiro Akashi


> Best regards
> 
> Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-08 23:55   ` Heinrich Schuchardt
@ 2020-01-17  5:11     ` AKASHI Takahiro
  2020-01-17  5:51       ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-17  5:11 UTC (permalink / raw)
  To: u-boot

On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >With this commit, image validation can be enforced, as UEFI specification
> >section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
> >
> >Currently we support
> >* authentication based on db and dbx,
> >   so dbx-validated image will always be rejected.
> >* following signature types:
> >     EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
> >     EFI_CERT_X509_GUID (x509 certificate for signed images)
> >Timestamp-based certificate revocation is not supported here.
> >
> >Internally, authentication data is stored in one of certificates tables
> >of PE image (See efi_image_parse()) and will be verified by
> >efi_image_authenticate() before loading a given image.
> >
> >It seems that UEFI specification defines the verification process
> >in a bit ambiguous way. I tried to implement it as closely to as
> >EDK2 does.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  include/efi_loader.h              |   7 +-
> >  lib/efi_loader/efi_boottime.c     |   2 +-
> >  lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
> >  3 files changed, 449 insertions(+), 14 deletions(-)
> >
> >diff --git a/include/efi_loader.h b/include/efi_loader.h
> >index 1f88caf86709..e12b49098fb0 100644
> >--- a/include/efi_loader.h
> >+++ b/include/efi_loader.h
> >@@ -11,6 +11,7 @@
> >  #include <common.h>
> >  #include <part_efi.h>
> >  #include <efi_api.h>
> >+#include <pe.h>
> >
> >  static inline int guidcmp(const void *g1, const void *g2)
> >  {
> >@@ -398,7 +399,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);
> >@@ -726,6 +728,9 @@ 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) */
> >diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> >index 493d906c641d..311681764034 100644
> >--- a/lib/efi_loader/efi_boottime.c
> >+++ b/lib/efi_loader/efi_boottime.c
> >@@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
> >  	efi_dp_split_file_path(file_path, &dp, &fp);
> >  	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
> >  	if (ret == EFI_SUCCESS)
> >-		ret = efi_load_pe(*image_obj, dest_buffer, info);
> >+		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
> >  	if (!source_buffer)
> >  		/* Release buffer to which file was loaded */
> >  		efi_free_pages((uintptr_t)dest_buffer,
> >diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> >index 13541cfa7a28..939758e61e3c 100644
> >--- a/lib/efi_loader/efi_image_loader.c
> >+++ b/lib/efi_loader/efi_image_loader.c
> >@@ -9,7 +9,9 @@
> >
> >  #include <common.h>
> >  #include <efi_loader.h>
> >+#include <malloc.h>
> >  #include <pe.h>
> >+#include "../lib/crypto/pkcs7_parser.h"
> >
> >  const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> >  const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
> >@@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
> >  	}
> >  }
> >
> >+#ifdef CONFIG_EFI_SECURE_BOOT
> >+/**
> >+ * efi_image_parse - parse a PE image
> >+ * @efi:	Pointer to image
> >+ * @len:	Size of @efi
> >+ * @regs:	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)
> 
> 
> This function is way too long (> 100 lines). Please, cut it into logical
> units.
> 
> 
> 
> >+{
> >+	struct efi_image_regions *regs;
> >+	IMAGE_DOS_HEADER *dos;
> >+	IMAGE_NT_HEADERS32 *nt;
> >+	IMAGE_SECTION_HEADER *sections, **sorted;
> >+	int num_regions, num_sections, i, j;
> >+	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.
> >+	 * As we can assume that it is already ordered in most cases,
> >+	 * the following code is optimized for this.
> >+	 */
> >+	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
> 
> If sections[0] is not the lowest entry this function fails.
> 
> Please, use qsort() supplied in lib/qsort.c.

I'd rather fix my code as it is much simpler than qsort.

> >+		if (sorted[i - 1]->VirtualAddress
> >+				<= sections[i].VirtualAddress) {
> >+			sorted[i] = &sections[i];
> >+		} else {
> >+			if (i == 1) {
> >+				sorted[1] = sorted[0];
> >+				sorted[0] = &sections[1];
> >+				continue;
> >+			}
> >+
> >+			sorted[i] = sorted[i - 1];
> >+			for (j = i - 2; j >= 0; j--) {
> >+				if (!j || sorted[j]->VirtualAddress
> >+						<= sections[i].VirtualAddress) {
> >+					sorted[j + 1] = &sections[i];
> >+					continue;
> >+				} else {
> >+					sorted[j + 1] = sorted[j];
> >+				}
> >+			}
> >+		}
> >+	}
> >+
> >+	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
> >+ * @len:	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 len)
> >+{
> >+	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;
> >+	bool ret = false;
> >+
> >+	if (!efi_secure_boot_enabled())
> >+		return true;
> >+
> >+	if (!efi_image_parse(efi, len, &regs, &wincerts,
> >+			     &wincerts_len)) {
> >+		debug("Parsing PE executable image failed\n");
> >+		return false;
> >+	}
> >+
> >+	if (!wincerts) {
> >+		/* The image is not signed */
> >+		ret = efi_image_unsigned_authenticate(regs);
> >+		free(regs);
> >+
> >+		return ret;
> >+	}
> >+
> >+	/*
> >+	 * verify signature using db and dbx
> >+	 */
> >+	db = efi_sigstore_parse_sigdb(L"db");
> >+	if (!db) {
> >+		debug("Getting signature database(db) failed\n");
> >+		goto err;
> >+	}
> >+
> >+	dbx = efi_sigstore_parse_sigdb(L"dbx");
> >+	if (!dbx) {
> >+		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);
> >+
> >+	return ret;
> >+}
> >+#else
> >+static bool efi_image_authenticate(void *efi, size_t len)
> >+{
> >+	return true;
> >+}
> >+#endif /* CONFIG_EFI_SECURE_BOOT */
> >+
> >  /**
> >   * efi_load_pe() - relocate EFI binary
> >   *
> >@@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
> >   * @loaded_image_info:	loaded image protocol
> >   * Return:		status code
> >   */
> >-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
> >+			 void *efi, size_t efi_size,
> >  			 struct efi_loaded_image *loaded_image_info)
> >  {
> >  	IMAGE_NT_HEADERS32 *nt;
> >@@ -231,17 +595,57 @@ 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;
> >+	void *new_efi = NULL;
> >+	size_t new_efi_size;
> >+	efi_status_t ret;
> >+
> >+	/*
> >+	 * 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 EFI_OUT_OF_RESOURCES;
> >+		memcpy(new_efi, efi, efi_size);
> >+		efi = new_efi;
> >+		efi_size = new_efi_size;
> >+	}
> >+
> >+	/* 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++)
> >@@ -253,14 +657,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)) {
> >+		ret = EFI_ACCESS_DENIED;
> 
> According to the UEFI specification LoadImage() should return
> EFI_SECURITY_VIOLATION in this case.

Will check.

> Further behaviour should depend on variables AuditMode and DeployedMode.
> 
> If authentication fails, you must update the configuration table
> identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
> Execution Information. If the authentication succeeds you may enter a
> record.
> 
> It seems to me that in your patch series you are not creating the
> configuration table at all.

From the very beginning of my submissions, I have clearly said
that this feature was *beyond the scope* in my current series.
===8<===
Unsupported features: (marked as TODO in most cases in the source code,
                        and won't be included in this series)
(snip)
* recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
===>8===

> The content of the Image Execution Information Table should be used to
> decide if StartImage() may start an image. This is still missing in the
> patch series.

No.
Whether such information be in configuration table or not,
non-authenticated image won't be started if secure boot is in force.

Thanks,
-Takahiro Akashi

> 
> Best regards
> 
> Heinrich
> 
> >+		goto err;
> >+	}
> >+
> >+	/* 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,
> >@@ -279,7 +698,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;
> >@@ -295,7 +715,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;
> >@@ -304,13 +725,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--) {
> >@@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >  	loaded_image_info->image_size = virt_size;
> >
> >  	return EFI_SUCCESS;
> >+
> >+err:
> >+	free(new_efi);
> >+
> >+	return ret;
> >  }
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 05/16] efi_loader: variable: support variable authentication
  2020-01-08 22:54   ` Heinrich Schuchardt
@ 2020-01-17  5:35     ` AKASHI Takahiro
  0 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-17  5:35 UTC (permalink / raw)
  To: u-boot

On Wed, Jan 08, 2020 at 11:54:26PM +0100, Heinrich Schuchardt wrote:
> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >With this commit, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
> >is supported for authenticated variables and the system secure state
> >will transfer between setup mode and user mode as UEFI specification
> >section 32.3 describes.
> >
> >Internally, authentication data is stored as part of authenticated
> >variable's value. It is nothing but a pkcs7 message (but we need some
> >wrapper, see efi_variable_parse_signature()) and will be validated by
> >efi_variable_authenticate(), hence efi_signature_verify_with_db().
> >
> >Associated time value will be encoded in "{...,time=...}" along with
> >other UEFI variable's attributes.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  include/efi_loader.h          |   3 +
> >  lib/efi_loader/efi_variable.c | 665 ++++++++++++++++++++++++++++------
> >  2 files changed, 564 insertions(+), 104 deletions(-)
> >
> >diff --git a/include/efi_loader.h b/include/efi_loader.h
> >index 3b3618e0be24..1f88caf86709 100644
> >--- a/include/efi_loader.h
> >+++ b/include/efi_loader.h
> >@@ -175,6 +175,7 @@ extern const efi_guid_t efi_guid_image_security_database;
> >  extern const efi_guid_t efi_guid_sha256;
> >  extern const efi_guid_t efi_guid_cert_x509;
> >  extern const efi_guid_t efi_guid_cert_x509_sha256;
> >+extern const efi_guid_t efi_guid_cert_type_pkcs7;
> >
> >  extern unsigned int __efi_runtime_start, __efi_runtime_stop;
> >  extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> >@@ -723,6 +724,8 @@ efi_status_t efi_image_region_add(struct efi_image_regions *regs,
> >
> >  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);
> >  #endif /* CONFIG_EFI_SECURE_BOOT */
> >
> >  #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
> >diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> >index 46f35bc60ba0..171b4abb4c58 100644
> >--- a/lib/efi_loader/efi_variable.c
> >+++ b/lib/efi_loader/efi_variable.c
> >@@ -10,7 +10,13 @@
> >  #include <env_internal.h>
> >  #include <hexdump.h>
> >  #include <malloc.h>
> >+#include <rtc.h>
> >  #include <search.h>
> >+#include <linux/compat.h>
> >+#include "../lib/crypto/pkcs7_parser.h"
> >+
> >+const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
> >+static bool efi_secure_boot;
> >
> >  #define READ_ONLY BIT(31)
> >
> >@@ -107,7 +113,7 @@ static const char *prefix(const char *str, const char *prefix)
> >   * @attrp:	pointer to UEFI attributes
> >   * Return:	pointer to remainder of U-Boot variable value
> >   */
> >-static const char *parse_attr(const char *str, u32 *attrp)
> >+static const char *parse_attr(const char *str, u32 *attrp, u64 *timep)
> >  {
> >  	u32 attr = 0;
> >  	char sep = '{';
> >@@ -130,6 +136,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,48 +159,291 @@ static const char *parse_attr(const char *str, u32 *attrp)
> >  }
> >
> >  /**
> >- * efi_get_variable() - retrieve value of a UEFI variable
> >+ * efi_secure_boot_enabled - return if secure boot is enabled or not
> >   *
> >- * This function implements the GetVariable runtime service.
> >+ * 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
> >   *
> >- * See the Unified Extensible Firmware Interface (UEFI) specification for
> >- * details.
> >+ * 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.
> >   *
> >- * @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:	Pointer to pkcs7_message structure on success, NULL 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 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;
> >+	const 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);
> >+
> >+	timestamp = &auth->time_stamp;
> 
> This does not compile with GCC 9.2.1:
> 
> lib/efi_loader/efi_variable.c: In function ‘efi_variable_authenticate’:
> lib/efi_loader/efi_variable.c:554:14: error: taking address of packed
> member of ‘struct efi_variable_authentication_2’ may result in an
> unaligned pointer value [-Werror=address-of-packed-member]
>   554 |  timestamp = &auth->time_stamp;
>       |              ^~~~~~~~~~~~~~~~~

Okay.
The line will be replaced with memcpy.

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >+	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;
> >
> >@@ -197,7 +452,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;
> >@@ -208,11 +463,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)"))) {
> >@@ -225,8 +482,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';
> >@@ -234,13 +493,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);
> >  }
> >
> >@@ -273,6 +586,7 @@ static efi_status_t parse_uboot_variable(char *variable,
> >  {
> >  	char *guid, *name, *end, c;
> >  	unsigned long name_len;
> >+	u64 time;
> >  	u16 *p;
> >
> >  	guid = strchr(variable, '_');
> >@@ -307,7 +621,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;
> >  }
> >@@ -389,7 +703,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);
> >
> >@@ -402,143 +716,286 @@ 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);
> >
> >+out:
> >  	if (env_set(native_name, val))
> >  		ret = EFI_DEVICE_ERROR;
> >+	else
> >+		ret = EFI_SUCCESS;
> >
> >-out:
> >+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));
> >  }
> >
> >  /**
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 03/16] efi_loader: add signature verification functions
  2020-01-17  2:20       ` AKASHI Takahiro
@ 2020-01-17  5:37         ` Heinrich Schuchardt
  2020-01-21  6:00           ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-17  5:37 UTC (permalink / raw)
  To: u-boot

On 1/17/20 3:20 AM, AKASHI Takahiro wrote:
> On Wed, Jan 15, 2020 at 01:13:36AM +0100, Heinrich Schuchardt wrote:
>> On 1/15/20 12:43 AM, Heinrich Schuchardt wrote:
>>> On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> (snip)
>>>> diff --git a/lib/efi_loader/efi_signature.c
>>>> b/lib/efi_loader/efi_signature.c
>>>> new file mode 100644
>>>> index 000000000000..823d3311e010
>>>> --- /dev/null
>>>> +++ b/lib/efi_loader/efi_signature.c
>>>> @@ -0,0 +1,584 @@
>>>> +// 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 <pe.h>
>>>> +#include <linux/compat.h>
>>>> +#include <linux/oid_registry.h>
>>>> +#include <u-boot/rsa.h>
>>>> +#include <u-boot/sha256.h>
>>>> +/*
>>>> + * avoid duplicated inclusion:
>>>> + * #include "../lib/crypto/x509_parser.h"
>>>> + */
>>>> +#include "../lib/crypto/pkcs7_parser.h"
>>>> +
>>>> +const efi_guid_t efi_guid_image_security_database =
>>>> +        EFI_IMAGE_SECURITY_DATABASE_GUID;
>>>> +const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
>>>> +const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
>>>> +const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
>>>> +const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
>>>> +
>>>> +#ifdef CONFIG_EFI_SECURE_BOOT
>>
>> This is the #ifdef to move to the Makefile. In the previous mail I got
>> into the wrong line.
>
> No.
> As you can see, those guids may also be referred to by other files
> (see efi_variable.c and cmd/nvedit_efi.c)
> even in !EFI_SECURE_BOOT case, and I think that this file is best fit
> for them.

I cannot find any of these guids in any other C file after applying all
patches from
https://patchwork.ozlabs.org/project/uboot/list/?series=&submitter=61166&state=&q=&archive=&delegate=

git grep -n efi_guid_sha256
include/efi_loader.h:185:extern const efi_guid_t efi_guid_sha256;
lib/efi_loader/efi_signature.c:26:const efi_guid_t efi_guid_sha256 =
EFI_CERT_SHA256_GUID;
lib/efi_loader/efi_signature.c:252:             if
(guidcmp(&siglist->sig_type, &efi_guid_sha256)) {

lib/efi_loader/efi_signature.c:27:const efi_guid_t efi_guid_cert_rsa2048
= EFI_CERT_RSA2048_GUID;

git grep -n efi_guid_cert_x509
include/efi_loader.h:186:extern const efi_guid_t efi_guid_cert_x509;
include/efi_loader.h:187:extern const efi_guid_t efi_guid_cert_x509_sha256;
lib/efi_loader/efi_signature.c:28:const efi_guid_t efi_guid_cert_x509 =
EFI_CERT_X509_GUID;
lib/efi_loader/efi_signature.c:29:const efi_guid_t
efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
lib/efi_loader/efi_signature.c:283:     if (guidcmp(&siglist->sig_type,
&efi_guid_cert_x509)) {
lib/efi_loader/efi_signature.c:406:     if (guidcmp(&siglist->sig_type,
&efi_guid_cert_x509_sha256)) {

include/efi_loader.h:187:extern const efi_guid_t efi_guid_cert_x509_sha256;
lib/efi_loader/efi_signature.c:29:const efi_guid_t
efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
lib/efi_loader/efi_signature.c:406:     if (guidcmp(&siglist->sig_type,
&efi_guid_cert_x509_sha256)) {

Best regards

Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-17  5:11     ` AKASHI Takahiro
@ 2020-01-17  5:51       ` Heinrich Schuchardt
  2020-01-21  6:12         ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-17  5:51 UTC (permalink / raw)
  To: u-boot

On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
> On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
>> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
>>> With this commit, image validation can be enforced, as UEFI specification
>>> section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
>>>
>>> Currently we support
>>> * authentication based on db and dbx,
>>>    so dbx-validated image will always be rejected.
>>> * following signature types:
>>>      EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
>>>      EFI_CERT_X509_GUID (x509 certificate for signed images)
>>> Timestamp-based certificate revocation is not supported here.
>>>
>>> Internally, authentication data is stored in one of certificates tables
>>> of PE image (See efi_image_parse()) and will be verified by
>>> efi_image_authenticate() before loading a given image.
>>>
>>> It seems that UEFI specification defines the verification process
>>> in a bit ambiguous way. I tried to implement it as closely to as
>>> EDK2 does.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>   include/efi_loader.h              |   7 +-
>>>   lib/efi_loader/efi_boottime.c     |   2 +-
>>>   lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
>>>   3 files changed, 449 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index 1f88caf86709..e12b49098fb0 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -11,6 +11,7 @@
>>>   #include <common.h>
>>>   #include <part_efi.h>
>>>   #include <efi_api.h>
>>> +#include <pe.h>
>>>
>>>   static inline int guidcmp(const void *g1, const void *g2)
>>>   {
>>> @@ -398,7 +399,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);
>>> @@ -726,6 +728,9 @@ 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) */
>>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>>> index 493d906c641d..311681764034 100644
>>> --- a/lib/efi_loader/efi_boottime.c
>>> +++ b/lib/efi_loader/efi_boottime.c
>>> @@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
>>>   	efi_dp_split_file_path(file_path, &dp, &fp);
>>>   	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
>>>   	if (ret == EFI_SUCCESS)
>>> -		ret = efi_load_pe(*image_obj, dest_buffer, info);
>>> +		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
>>>   	if (!source_buffer)
>>>   		/* Release buffer to which file was loaded */
>>>   		efi_free_pages((uintptr_t)dest_buffer,
>>> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
>>> index 13541cfa7a28..939758e61e3c 100644
>>> --- a/lib/efi_loader/efi_image_loader.c
>>> +++ b/lib/efi_loader/efi_image_loader.c
>>> @@ -9,7 +9,9 @@
>>>
>>>   #include <common.h>
>>>   #include <efi_loader.h>
>>> +#include <malloc.h>
>>>   #include <pe.h>
>>> +#include "../lib/crypto/pkcs7_parser.h"
>>>
>>>   const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
>>>   const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
>>> @@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
>>>   	}
>>>   }
>>>
>>> +#ifdef CONFIG_EFI_SECURE_BOOT
>>> +/**
>>> + * efi_image_parse - parse a PE image
>>> + * @efi:	Pointer to image
>>> + * @len:	Size of @efi
>>> + * @regs:	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)
>>
>>
>> This function is way too long (> 100 lines). Please, cut it into logical
>> units.
>>
>>
>>
>>> +{
>>> +	struct efi_image_regions *regs;
>>> +	IMAGE_DOS_HEADER *dos;
>>> +	IMAGE_NT_HEADERS32 *nt;
>>> +	IMAGE_SECTION_HEADER *sections, **sorted;
>>> +	int num_regions, num_sections, i, j;
>>> +	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.
>>> +	 * As we can assume that it is already ordered in most cases,
>>> +	 * the following code is optimized for this.
>>> +	 */
>>> +	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
>>
>> If sections[0] is not the lowest entry this function fails.
>>
>> Please, use qsort() supplied in lib/qsort.c.
>
> I'd rather fix my code as it is much simpler than qsort.

Using qsort will result in a smaller code size. With qsort your code
will also be much easier to read.

>
>>> +		if (sorted[i - 1]->VirtualAddress
>>> +				<= sections[i].VirtualAddress) {
>>> +			sorted[i] = &sections[i];
>>> +		} else {
>>> +			if (i == 1) {
>>> +				sorted[1] = sorted[0];
>>> +				sorted[0] = &sections[1];
>>> +				continue;
>>> +			}
>>> +
>>> +			sorted[i] = sorted[i - 1];
>>> +			for (j = i - 2; j >= 0; j--) {
>>> +				if (!j || sorted[j]->VirtualAddress
>>> +						<= sections[i].VirtualAddress) {
>>> +					sorted[j + 1] = &sections[i];
>>> +					continue;
>>> +				} else {
>>> +					sorted[j + 1] = sorted[j];
>>> +				}
>>> +			}
>>> +		}
>>> +	}
>>> +
>>> +	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
>>> + * @len:	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 len)
>>> +{
>>> +	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;
>>> +	bool ret = false;
>>> +
>>> +	if (!efi_secure_boot_enabled())
>>> +		return true;
>>> +
>>> +	if (!efi_image_parse(efi, len, &regs, &wincerts,
>>> +			     &wincerts_len)) {
>>> +		debug("Parsing PE executable image failed\n");
>>> +		return false;
>>> +	}
>>> +
>>> +	if (!wincerts) {
>>> +		/* The image is not signed */
>>> +		ret = efi_image_unsigned_authenticate(regs);
>>> +		free(regs);
>>> +
>>> +		return ret;
>>> +	}
>>> +
>>> +	/*
>>> +	 * verify signature using db and dbx
>>> +	 */
>>> +	db = efi_sigstore_parse_sigdb(L"db");
>>> +	if (!db) {
>>> +		debug("Getting signature database(db) failed\n");
>>> +		goto err;
>>> +	}
>>> +
>>> +	dbx = efi_sigstore_parse_sigdb(L"dbx");
>>> +	if (!dbx) {
>>> +		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);
>>> +
>>> +	return ret;
>>> +}
>>> +#else
>>> +static bool efi_image_authenticate(void *efi, size_t len)
>>> +{
>>> +	return true;
>>> +}
>>> +#endif /* CONFIG_EFI_SECURE_BOOT */
>>> +
>>>   /**
>>>    * efi_load_pe() - relocate EFI binary
>>>    *
>>> @@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
>>>    * @loaded_image_info:	loaded image protocol
>>>    * Return:		status code
>>>    */
>>> -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>>> +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
>>> +			 void *efi, size_t efi_size,
>>>   			 struct efi_loaded_image *loaded_image_info)
>>>   {
>>>   	IMAGE_NT_HEADERS32 *nt;
>>> @@ -231,17 +595,57 @@ 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;
>>> +	void *new_efi = NULL;
>>> +	size_t new_efi_size;
>>> +	efi_status_t ret;
>>> +
>>> +	/*
>>> +	 * 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 EFI_OUT_OF_RESOURCES;
>>> +		memcpy(new_efi, efi, efi_size);
>>> +		efi = new_efi;
>>> +		efi_size = new_efi_size;
>>> +	}
>>> +
>>> +	/* 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++)
>>> @@ -253,14 +657,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)) {
>>> +		ret = EFI_ACCESS_DENIED;
>>
>> According to the UEFI specification LoadImage() should return
>> EFI_SECURITY_VIOLATION in this case.
>
> Will check.
>
>> Further behaviour should depend on variables AuditMode and DeployedMode.
>>
>> If authentication fails, you must update the configuration table
>> identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
>> Execution Information. If the authentication succeeds you may enter a
>> record.
>>
>> It seems to me that in your patch series you are not creating the
>> configuration table at all.
>
>>From the very beginning of my submissions, I have clearly said
> that this feature was *beyond the scope* in my current series.
> ===8<===
> Unsupported features: (marked as TODO in most cases in the source code,
>                          and won't be included in this series)
> (snip)
> * recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
> ===>8===
>
>> The content of the Image Execution Information Table should be used to
>> decide if StartImage() may start an image. This is still missing in the
>> patch series.
>
> No.
> Whether such information be in configuration table or not,
> non-authenticated image won't be started if secure boot is in force.

I cannot find any such check in efi_start_image().

Please, provide an implementation that complies with the UEFI specification:

"The information is used to create the Image Execution Information
Table, which is added to the EFI System Configuration Table and assigned
the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."

Best regards

Heinrich

>
> Thanks,
> -Takahiro Akashi
>
>>
>> Best regards
>>
>> Heinrich
>>
>>> +		goto err;
>>> +	}
>>> +
>>> +	/* 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,
>>> @@ -279,7 +698,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;
>>> @@ -295,7 +715,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;
>>> @@ -304,13 +725,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--) {
>>> @@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>>>   	loaded_image_info->image_size = virt_size;
>>>
>>>   	return EFI_SUCCESS;
>>> +
>>> +err:
>>> +	free(new_efi);
>>> +
>>> +	return ret;
>>>   }
>>>
>>
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2020-01-09 20:03       ` Ilias Apalodimas
@ 2020-01-17  5:59         ` AKASHI Takahiro
  2020-01-17  6:39           ` Ilias Apalodimas
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-17  5:59 UTC (permalink / raw)
  To: u-boot

On Thu, Jan 09, 2020 at 10:03:33PM +0200, Ilias Apalodimas wrote:
> On Thu, Jan 09, 2020 at 08:09:27PM +0100, Heinrich Schuchardt wrote:
> > On 1/9/20 9:02 AM, Ilias Apalodimas wrote:
> > > On Thu, Jan 09, 2020 at 01:08:35AM +0100, Heinrich Schuchardt wrote:
> > > > On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> > > > > One of major missing features in current UEFI implementation is "secure boot."
> > > > > The ultimate goal of my attempt is to implement image authentication based
> > > > > on signature and provide UEFI secure boot support which would be fully
> > > > > compliant with UEFI specification, section 32[1].
> > > > > (The code was originally developed by Patrick Wildt.)
> > > > > 
> > > > > Please note, however, this patch doesn't work on its own; there are
> > > > > a couple of functional dependencies[2] and [3], that I have submitted
> > > > > before. For complete workable patch set, see my repository[4],
> > > > > which also contains experimental timestamp-based revocation suuport.
> > > > > 
> > > > > My "non-volatile" support[5], which is under discussion, is not mandatory
> > > > > and so not included here, but this inevitably implies that, for example,
> > > > > signature database variables, like db and dbx, won't be persistent unless you
> > > > > explicitly run "env save" command and that UEFI variables are not separated
> > > > > from U-Boot environment. Anyhow, Linaro is also working on implementing
> > > > > real "secure storage" solution based on TF-A and OP-TEE.
> > > > > 
> > > > 
> > > > Device trees can be used for denial of service or to destroy hardware.
> > > > 
> > > > How will you address the validation of device trees?
> > > 
> > > Although this is really simple to solve, factoring in the different vendor
> > > needs makes it quite complex.
> > > There's a couple of options we can consider and not all of them are sane.
> > > 
> > > 1. U-Boot embeds the DTB. This is straightforward. On Arm devices TF-A
> > > verifies U-boot so you natively end up with a verified DTB. If U-Boot does not
> > > include the proper DTB (as is the case for several devices), one can always
> > > complite the correct DTB and compile with EXT_DTB.
> > > 2. Use https://github.com/jiazhang0/SELoader which verifies non-PE files
> > > 3. Add some custom code and use UEFI keyring to verify non PE files. This is a
> > > bad idea though since you'll 'polute' the UEFI keyring.
> > > 4. FIT for DTB (??)
> > > 
> > > In any case UEFI job is to verify PE/COFF executables and that's what this patch
> > > provides. DTB verification is beyond UEFI secure boot patches imho.
> > > 
> > > Regards
> > > /Ilias
> > 
> > The UEFI specification does not mention device trees at all. EDK2 is
> > based on ACPI tables.
> 
> On one of the platforms i kknow of (socionext synquacer), it also provides DTB
> as part of the firmware, which is identical to proposeal (1) I mentioned.
> 
> > 
> > We already have verified boot via signed UEFI FIT images which can
> > contain an UEFI image and a device tree. So for verified boot of Linux
> > you would simply package shim and the device tree into a FIT image. Shim
> > would verify GRUB and GRUB would verify the kernel and the ramdisk. In
> > this scenario we don't need the current patch series at all and it
> > integrates well with distributions like Debian which provide shim for
> > arm64, cf. https://packages.debian.org/de/bullseye/shim-signed .
> 
> Of course everything is verified, but that's not UEFI secure boot. It's similar
> but the verification does not go through DB/DBX and there are no secure
> variables, so the current patchset has value.

I believe that db/dbx schemes give us, distributors as well as users,
more flexible manner of managing secure boot process.

> > 
> > If we implement secure boot according the UEFI specification, one option
> > would be to package the device tree as a UEFI driver image and let the
> > stub install it as a configuration table. The unload callback could be
> > used to remove the device tree.
> > 
> 
> Sure but this is not in scope for the current patchset is it?

Exactly.

> Similarly you can just include the DTB in U-Boot and naturally have it verified.
> 
> I am not arguing that DTB verification is needed. We absolutely agree on that.
> All i am saying is that the extra functionality can be added in the future,
> since we already have a valid way of providing it with the current patchset.

BTW, Ilias,
where should such a discussion about dtb verification be held,
Boot-arch ML, Linaro Connect, ELC or whatever else conference?
Otherwise just leave the decision in distributors' hands?

Thanks,
-Takahiro Akashi

> Regards
> /Ilias
> > 
> > > > 
> > > > Best regards
> > > > 
> > > > Heinrich
> > > 
> > 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 00/16] efi_loader: add secure boot support
  2020-01-17  5:59         ` AKASHI Takahiro
@ 2020-01-17  6:39           ` Ilias Apalodimas
  0 siblings, 0 replies; 45+ messages in thread
From: Ilias Apalodimas @ 2020-01-17  6:39 UTC (permalink / raw)
  To: u-boot


[...]
> > > If we implement secure boot according the UEFI specification, one option
> > > would be to package the device tree as a UEFI driver image and let the
> > > stub install it as a configuration table. The unload callback could be
> > > used to remove the device tree.
> > > 
> > 
> > Sure but this is not in scope for the current patchset is it?
> 
> Exactly.
> 
> > Similarly you can just include the DTB in U-Boot and naturally have it verified.
> > 
> > I am not arguing that DTB verification is needed. We absolutely agree on that.
> > All i am saying is that the extra functionality can be added in the future,
> > since we already have a valid way of providing it with the current patchset.
> 
> BTW, Ilias,
> where should such a discussion about dtb verification be held,
> Boot-arch ML, Linaro Connect, ELC or whatever else conference?
> Otherwise just leave the decision in distributors' hands?

We did send some e-mails on boot-arch ML in the past [1]. The subject is quite
controversial since there are a lot of opinions on this. 
I think Linaro is working on a device tree evolution project at the moment with
one of the subjects being device tree verification.
We can certainly discuss more during Linaro Connect.

[1] https://lists.linaro.org/pipermail/boot-architecture/2019-June/001053.html

Thanks
/Ilias
> 
> Thanks,
> -Takahiro Akashi
> 
> > Regards
> > /Ilias
> > > 
> > > > > 
> > > > > Best regards
> > > > > 
> > > > > Heinrich
> > > > 
> > > 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 03/16] efi_loader: add signature verification functions
  2020-01-17  5:37         ` Heinrich Schuchardt
@ 2020-01-21  6:00           ` AKASHI Takahiro
  0 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-21  6:00 UTC (permalink / raw)
  To: u-boot

On Fri, Jan 17, 2020 at 06:37:39AM +0100, Heinrich Schuchardt wrote:
> On 1/17/20 3:20 AM, AKASHI Takahiro wrote:
> >On Wed, Jan 15, 2020 at 01:13:36AM +0100, Heinrich Schuchardt wrote:
> >>On 1/15/20 12:43 AM, Heinrich Schuchardt wrote:
> >>>On 12/18/19 1:44 AM, AKASHI Takahiro wrote:
> >(snip)
> >>>>diff --git a/lib/efi_loader/efi_signature.c
> >>>>b/lib/efi_loader/efi_signature.c
> >>>>new file mode 100644
> >>>>index 000000000000..823d3311e010
> >>>>--- /dev/null
> >>>>+++ b/lib/efi_loader/efi_signature.c
> >>>>@@ -0,0 +1,584 @@
> >>>>+// 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 <pe.h>
> >>>>+#include <linux/compat.h>
> >>>>+#include <linux/oid_registry.h>
> >>>>+#include <u-boot/rsa.h>
> >>>>+#include <u-boot/sha256.h>
> >>>>+/*
> >>>>+ * avoid duplicated inclusion:
> >>>>+ * #include "../lib/crypto/x509_parser.h"
> >>>>+ */
> >>>>+#include "../lib/crypto/pkcs7_parser.h"
> >>>>+
> >>>>+const efi_guid_t efi_guid_image_security_database =
> >>>>+        EFI_IMAGE_SECURITY_DATABASE_GUID;
> >>>>+const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
> >>>>+const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
> >>>>+const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
> >>>>+const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
> >>>>+
> >>>>+#ifdef CONFIG_EFI_SECURE_BOOT
> >>
> >>This is the #ifdef to move to the Makefile. In the previous mail I got
> >>into the wrong line.
> >
> >No.
> >As you can see, those guids may also be referred to by other files
> >(see efi_variable.c and cmd/nvedit_efi.c)
> >even in !EFI_SECURE_BOOT case, and I think that this file is best fit
> >for them.
> 
> I cannot find any of these guids in any other C file after applying all
> patches from

???
Did you look for efi_guid_image_security_database in efi_variable.c
and cmd/nvedit_efi.c?

-Takahiro Akashi


> https://patchwork.ozlabs.org/project/uboot/list/?series=&submitter=61166&state=&q=&archive=&delegate=
> 
> git grep -n efi_guid_sha256
> include/efi_loader.h:185:extern const efi_guid_t efi_guid_sha256;
> lib/efi_loader/efi_signature.c:26:const efi_guid_t efi_guid_sha256 =
> EFI_CERT_SHA256_GUID;
> lib/efi_loader/efi_signature.c:252:             if
> (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
> 
> lib/efi_loader/efi_signature.c:27:const efi_guid_t efi_guid_cert_rsa2048
> = EFI_CERT_RSA2048_GUID;
> 
> git grep -n efi_guid_cert_x509
> include/efi_loader.h:186:extern const efi_guid_t efi_guid_cert_x509;
> include/efi_loader.h:187:extern const efi_guid_t efi_guid_cert_x509_sha256;
> lib/efi_loader/efi_signature.c:28:const efi_guid_t efi_guid_cert_x509 =
> EFI_CERT_X509_GUID;
> lib/efi_loader/efi_signature.c:29:const efi_guid_t
> efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
> lib/efi_loader/efi_signature.c:283:     if (guidcmp(&siglist->sig_type,
> &efi_guid_cert_x509)) {
> lib/efi_loader/efi_signature.c:406:     if (guidcmp(&siglist->sig_type,
> &efi_guid_cert_x509_sha256)) {
> 
> include/efi_loader.h:187:extern const efi_guid_t efi_guid_cert_x509_sha256;
> lib/efi_loader/efi_signature.c:29:const efi_guid_t
> efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
> lib/efi_loader/efi_signature.c:406:     if (guidcmp(&siglist->sig_type,
> &efi_guid_cert_x509_sha256)) {
> 
> Best regards
> 
> Heinrich

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-17  5:51       ` Heinrich Schuchardt
@ 2020-01-21  6:12         ` AKASHI Takahiro
  2020-01-21  7:15           ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-21  6:12 UTC (permalink / raw)
  To: u-boot

On Fri, Jan 17, 2020 at 06:51:50AM +0100, Heinrich Schuchardt wrote:
> On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
> >On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
> >>On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >>>With this commit, image validation can be enforced, as UEFI specification
> >>>section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
> >>>
> >>>Currently we support
> >>>* authentication based on db and dbx,
> >>>   so dbx-validated image will always be rejected.
> >>>* following signature types:
> >>>     EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
> >>>     EFI_CERT_X509_GUID (x509 certificate for signed images)
> >>>Timestamp-based certificate revocation is not supported here.
> >>>
> >>>Internally, authentication data is stored in one of certificates tables
> >>>of PE image (See efi_image_parse()) and will be verified by
> >>>efi_image_authenticate() before loading a given image.
> >>>
> >>>It seems that UEFI specification defines the verification process
> >>>in a bit ambiguous way. I tried to implement it as closely to as
> >>>EDK2 does.
> >>>
> >>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>---
> >>>  include/efi_loader.h              |   7 +-
> >>>  lib/efi_loader/efi_boottime.c     |   2 +-
> >>>  lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
> >>>  3 files changed, 449 insertions(+), 14 deletions(-)
> >>>
> >>>diff --git a/include/efi_loader.h b/include/efi_loader.h
> >>>index 1f88caf86709..e12b49098fb0 100644
> >>>--- a/include/efi_loader.h
> >>>+++ b/include/efi_loader.h
> >>>@@ -11,6 +11,7 @@
> >>>  #include <common.h>
> >>>  #include <part_efi.h>
> >>>  #include <efi_api.h>
> >>>+#include <pe.h>
> >>>
> >>>  static inline int guidcmp(const void *g1, const void *g2)
> >>>  {
> >>>@@ -398,7 +399,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);
> >>>@@ -726,6 +728,9 @@ 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) */
> >>>diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> >>>index 493d906c641d..311681764034 100644
> >>>--- a/lib/efi_loader/efi_boottime.c
> >>>+++ b/lib/efi_loader/efi_boottime.c
> >>>@@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
> >>>  	efi_dp_split_file_path(file_path, &dp, &fp);
> >>>  	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
> >>>  	if (ret == EFI_SUCCESS)
> >>>-		ret = efi_load_pe(*image_obj, dest_buffer, info);
> >>>+		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
> >>>  	if (!source_buffer)
> >>>  		/* Release buffer to which file was loaded */
> >>>  		efi_free_pages((uintptr_t)dest_buffer,
> >>>diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> >>>index 13541cfa7a28..939758e61e3c 100644
> >>>--- a/lib/efi_loader/efi_image_loader.c
> >>>+++ b/lib/efi_loader/efi_image_loader.c
> >>>@@ -9,7 +9,9 @@
> >>>
> >>>  #include <common.h>
> >>>  #include <efi_loader.h>
> >>>+#include <malloc.h>
> >>>  #include <pe.h>
> >>>+#include "../lib/crypto/pkcs7_parser.h"
> >>>
> >>>  const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> >>>  const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
> >>>@@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
> >>>  	}
> >>>  }
> >>>
> >>>+#ifdef CONFIG_EFI_SECURE_BOOT
> >>>+/**
> >>>+ * efi_image_parse - parse a PE image
> >>>+ * @efi:	Pointer to image
> >>>+ * @len:	Size of @efi
> >>>+ * @regs:	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)
> >>
> >>
> >>This function is way too long (> 100 lines). Please, cut it into logical
> >>units.
> >>
> >>
> >>
> >>>+{
> >>>+	struct efi_image_regions *regs;
> >>>+	IMAGE_DOS_HEADER *dos;
> >>>+	IMAGE_NT_HEADERS32 *nt;
> >>>+	IMAGE_SECTION_HEADER *sections, **sorted;
> >>>+	int num_regions, num_sections, i, j;
> >>>+	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.
> >>>+	 * As we can assume that it is already ordered in most cases,
> >>>+	 * the following code is optimized for this.
> >>>+	 */
> >>>+	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
> >>
> >>If sections[0] is not the lowest entry this function fails.
> >>
> >>Please, use qsort() supplied in lib/qsort.c.
> >
> >I'd rather fix my code as it is much simpler than qsort.
> 
> Using qsort will result in a smaller code size. With qsort your code
> will also be much easier to read.
> 
> >
> >>>+		if (sorted[i - 1]->VirtualAddress
> >>>+				<= sections[i].VirtualAddress) {
> >>>+			sorted[i] = &sections[i];
> >>>+		} else {
> >>>+			if (i == 1) {
> >>>+				sorted[1] = sorted[0];
> >>>+				sorted[0] = &sections[1];
> >>>+				continue;
> >>>+			}
> >>>+
> >>>+			sorted[i] = sorted[i - 1];
> >>>+			for (j = i - 2; j >= 0; j--) {
> >>>+				if (!j || sorted[j]->VirtualAddress
> >>>+						<= sections[i].VirtualAddress) {
> >>>+					sorted[j + 1] = &sections[i];
> >>>+					continue;
> >>>+				} else {
> >>>+					sorted[j + 1] = sorted[j];
> >>>+				}
> >>>+			}
> >>>+		}
> >>>+	}
> >>>+
> >>>+	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
> >>>+ * @len:	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 len)
> >>>+{
> >>>+	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;
> >>>+	bool ret = false;
> >>>+
> >>>+	if (!efi_secure_boot_enabled())
> >>>+		return true;
> >>>+
> >>>+	if (!efi_image_parse(efi, len, &regs, &wincerts,
> >>>+			     &wincerts_len)) {
> >>>+		debug("Parsing PE executable image failed\n");
> >>>+		return false;
> >>>+	}
> >>>+
> >>>+	if (!wincerts) {
> >>>+		/* The image is not signed */
> >>>+		ret = efi_image_unsigned_authenticate(regs);
> >>>+		free(regs);
> >>>+
> >>>+		return ret;
> >>>+	}
> >>>+
> >>>+	/*
> >>>+	 * verify signature using db and dbx
> >>>+	 */
> >>>+	db = efi_sigstore_parse_sigdb(L"db");
> >>>+	if (!db) {
> >>>+		debug("Getting signature database(db) failed\n");
> >>>+		goto err;
> >>>+	}
> >>>+
> >>>+	dbx = efi_sigstore_parse_sigdb(L"dbx");
> >>>+	if (!dbx) {
> >>>+		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);
> >>>+
> >>>+	return ret;
> >>>+}
> >>>+#else
> >>>+static bool efi_image_authenticate(void *efi, size_t len)
> >>>+{
> >>>+	return true;
> >>>+}
> >>>+#endif /* CONFIG_EFI_SECURE_BOOT */
> >>>+
> >>>  /**
> >>>   * efi_load_pe() - relocate EFI binary
> >>>   *
> >>>@@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
> >>>   * @loaded_image_info:	loaded image protocol
> >>>   * Return:		status code
> >>>   */
> >>>-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >>>+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
> >>>+			 void *efi, size_t efi_size,
> >>>  			 struct efi_loaded_image *loaded_image_info)
> >>>  {
> >>>  	IMAGE_NT_HEADERS32 *nt;
> >>>@@ -231,17 +595,57 @@ 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;
> >>>+	void *new_efi = NULL;
> >>>+	size_t new_efi_size;
> >>>+	efi_status_t ret;
> >>>+
> >>>+	/*
> >>>+	 * 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 EFI_OUT_OF_RESOURCES;
> >>>+		memcpy(new_efi, efi, efi_size);
> >>>+		efi = new_efi;
> >>>+		efi_size = new_efi_size;
> >>>+	}
> >>>+
> >>>+	/* 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++)
> >>>@@ -253,14 +657,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)) {
> >>>+		ret = EFI_ACCESS_DENIED;
> >>
> >>According to the UEFI specification LoadImage() should return
> >>EFI_SECURITY_VIOLATION in this case.
> >
> >Will check.
> >
> >>Further behaviour should depend on variables AuditMode and DeployedMode.
> >>
> >>If authentication fails, you must update the configuration table
> >>identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
> >>Execution Information. If the authentication succeeds you may enter a
> >>record.
> >>
> >>It seems to me that in your patch series you are not creating the
> >>configuration table at all.
> >
> >>From the very beginning of my submissions, I have clearly said
> >that this feature was *beyond the scope* in my current series.
> >===8<===
> >Unsupported features: (marked as TODO in most cases in the source code,
> >                         and won't be included in this series)
> >(snip)
> >* recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
> >===>8===
> >
> >>The content of the Image Execution Information Table should be used to
> >>decide if StartImage() may start an image. This is still missing in the
> >>patch series.
> >
> >No.
> >Whether such information be in configuration table or not,
> >non-authenticated image won't be started if secure boot is in force.
> 
> I cannot find any such check in efi_start_image().

Because my current code *rejects* any unauthenticated images being loaded
and return no valid pointer to image handle.
If an error code in this case is changed and efi_load_image() still returns
a valid handle, we will also have to modify efi_start_image().

> Please, provide an implementation that complies with the UEFI specification:
> 
> "The information is used to create the Image Execution Information
> Table, which is added to the EFI System Configuration Table and assigned
> the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."

Regarding "Image Execution Information Table", Heavily disagree.
The current UEFI code, not only mine but yours, has bunch of unimplemented
features.

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >
> >Thanks,
> >-Takahiro Akashi
> >
> >>
> >>Best regards
> >>
> >>Heinrich
> >>
> >>>+		goto err;
> >>>+	}
> >>>+
> >>>+	/* 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,
> >>>@@ -279,7 +698,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;
> >>>@@ -295,7 +715,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;
> >>>@@ -304,13 +725,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--) {
> >>>@@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >>>  	loaded_image_info->image_size = virt_size;
> >>>
> >>>  	return EFI_SUCCESS;
> >>>+
> >>>+err:
> >>>+	free(new_efi);
> >>>+
> >>>+	return ret;
> >>>  }
> >>>
> >>
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable
  2019-12-18  0:45 ` [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable AKASHI Takahiro
@ 2020-01-21  7:13   ` Heinrich Schuchardt
  2020-01-22  1:01     ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-21  7:13 UTC (permalink / raw)
  To: u-boot

On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> A signature database variable is associated with a specific guid.
> For convenience, if user doesn't supply any guid info, "env set|print -e"
> should complement it.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   cmd/nvedit_efi.c | 18 ++++++++++++++----
>   1 file changed, 14 insertions(+), 4 deletions(-)
>
> diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
> index 8ea0da01283f..579cf430593c 100644
> --- a/cmd/nvedit_efi.c
> +++ b/cmd/nvedit_efi.c
> @@ -41,6 +41,11 @@ static const struct {
>   } efi_guid_text[] = {
>   	/* signature database */
>   	{EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"},
> +	{EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"},
> +	/* certificate type */
> +	{EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"},
> +	{EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"},
> +	{EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"},
>   };
>
>   /* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */
> @@ -525,9 +530,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 +544,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"))

Why is "dbr" missing?

I guess dbDefault, dbrDefault, dbxDefault, dbtDefault use
EFI_GLOBAL_VARIABLE?

Best regards

Heinrich

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

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-21  6:12         ` AKASHI Takahiro
@ 2020-01-21  7:15           ` Heinrich Schuchardt
  2020-01-22  1:13             ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-21  7:15 UTC (permalink / raw)
  To: u-boot

On 1/21/20 7:12 AM, AKASHI Takahiro wrote:
> On Fri, Jan 17, 2020 at 06:51:50AM +0100, Heinrich Schuchardt wrote:
>> On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
>>> On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
>>>> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
>>>>> With this commit, image validation can be enforced, as UEFI specification
>>>>> section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
>>>>>
>>>>> Currently we support
>>>>> * authentication based on db and dbx,
>>>>>    so dbx-validated image will always be rejected.
>>>>> * following signature types:
>>>>>      EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
>>>>>      EFI_CERT_X509_GUID (x509 certificate for signed images)
>>>>> Timestamp-based certificate revocation is not supported here.
>>>>>
>>>>> Internally, authentication data is stored in one of certificates tables
>>>>> of PE image (See efi_image_parse()) and will be verified by
>>>>> efi_image_authenticate() before loading a given image.
>>>>>
>>>>> It seems that UEFI specification defines the verification process
>>>>> in a bit ambiguous way. I tried to implement it as closely to as
>>>>> EDK2 does.
>>>>>
>>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>>> ---
>>>>>   include/efi_loader.h              |   7 +-
>>>>>   lib/efi_loader/efi_boottime.c     |   2 +-
>>>>>   lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
>>>>>   3 files changed, 449 insertions(+), 14 deletions(-)
>>>>>
>>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>>> index 1f88caf86709..e12b49098fb0 100644
>>>>> --- a/include/efi_loader.h
>>>>> +++ b/include/efi_loader.h
>>>>> @@ -11,6 +11,7 @@
>>>>>   #include <common.h>
>>>>>   #include <part_efi.h>
>>>>>   #include <efi_api.h>
>>>>> +#include <pe.h>
>>>>>
>>>>>   static inline int guidcmp(const void *g1, const void *g2)
>>>>>   {
>>>>> @@ -398,7 +399,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);
>>>>> @@ -726,6 +728,9 @@ 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) */
>>>>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>>>>> index 493d906c641d..311681764034 100644
>>>>> --- a/lib/efi_loader/efi_boottime.c
>>>>> +++ b/lib/efi_loader/efi_boottime.c
>>>>> @@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
>>>>>   	efi_dp_split_file_path(file_path, &dp, &fp);
>>>>>   	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
>>>>>   	if (ret == EFI_SUCCESS)
>>>>> -		ret = efi_load_pe(*image_obj, dest_buffer, info);
>>>>> +		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
>>>>>   	if (!source_buffer)
>>>>>   		/* Release buffer to which file was loaded */
>>>>>   		efi_free_pages((uintptr_t)dest_buffer,
>>>>> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
>>>>> index 13541cfa7a28..939758e61e3c 100644
>>>>> --- a/lib/efi_loader/efi_image_loader.c
>>>>> +++ b/lib/efi_loader/efi_image_loader.c
>>>>> @@ -9,7 +9,9 @@
>>>>>
>>>>>   #include <common.h>
>>>>>   #include <efi_loader.h>
>>>>> +#include <malloc.h>
>>>>>   #include <pe.h>
>>>>> +#include "../lib/crypto/pkcs7_parser.h"
>>>>>
>>>>>   const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
>>>>>   const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
>>>>> @@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
>>>>>   	}
>>>>>   }
>>>>>
>>>>> +#ifdef CONFIG_EFI_SECURE_BOOT
>>>>> +/**
>>>>> + * efi_image_parse - parse a PE image
>>>>> + * @efi:	Pointer to image
>>>>> + * @len:	Size of @efi
>>>>> + * @regs:	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)
>>>>
>>>>
>>>> This function is way too long (> 100 lines). Please, cut it into logical
>>>> units.
>>>>
>>>>
>>>>
>>>>> +{
>>>>> +	struct efi_image_regions *regs;
>>>>> +	IMAGE_DOS_HEADER *dos;
>>>>> +	IMAGE_NT_HEADERS32 *nt;
>>>>> +	IMAGE_SECTION_HEADER *sections, **sorted;
>>>>> +	int num_regions, num_sections, i, j;
>>>>> +	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.
>>>>> +	 * As we can assume that it is already ordered in most cases,
>>>>> +	 * the following code is optimized for this.
>>>>> +	 */
>>>>> +	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
>>>>
>>>> If sections[0] is not the lowest entry this function fails.
>>>>
>>>> Please, use qsort() supplied in lib/qsort.c.
>>>
>>> I'd rather fix my code as it is much simpler than qsort.
>>
>> Using qsort will result in a smaller code size. With qsort your code
>> will also be much easier to read.
>>
>>>
>>>>> +		if (sorted[i - 1]->VirtualAddress
>>>>> +				<= sections[i].VirtualAddress) {
>>>>> +			sorted[i] = &sections[i];
>>>>> +		} else {
>>>>> +			if (i == 1) {
>>>>> +				sorted[1] = sorted[0];
>>>>> +				sorted[0] = &sections[1];
>>>>> +				continue;
>>>>> +			}
>>>>> +
>>>>> +			sorted[i] = sorted[i - 1];
>>>>> +			for (j = i - 2; j >= 0; j--) {
>>>>> +				if (!j || sorted[j]->VirtualAddress
>>>>> +						<= sections[i].VirtualAddress) {
>>>>> +					sorted[j + 1] = &sections[i];
>>>>> +					continue;
>>>>> +				} else {
>>>>> +					sorted[j + 1] = sorted[j];
>>>>> +				}
>>>>> +			}
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	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
>>>>> + * @len:	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 len)
>>>>> +{
>>>>> +	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;
>>>>> +	bool ret = false;
>>>>> +
>>>>> +	if (!efi_secure_boot_enabled())
>>>>> +		return true;
>>>>> +
>>>>> +	if (!efi_image_parse(efi, len, &regs, &wincerts,
>>>>> +			     &wincerts_len)) {
>>>>> +		debug("Parsing PE executable image failed\n");
>>>>> +		return false;
>>>>> +	}
>>>>> +
>>>>> +	if (!wincerts) {
>>>>> +		/* The image is not signed */
>>>>> +		ret = efi_image_unsigned_authenticate(regs);
>>>>> +		free(regs);
>>>>> +
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	/*
>>>>> +	 * verify signature using db and dbx
>>>>> +	 */
>>>>> +	db = efi_sigstore_parse_sigdb(L"db");
>>>>> +	if (!db) {
>>>>> +		debug("Getting signature database(db) failed\n");
>>>>> +		goto err;
>>>>> +	}
>>>>> +
>>>>> +	dbx = efi_sigstore_parse_sigdb(L"dbx");
>>>>> +	if (!dbx) {
>>>>> +		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);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +#else
>>>>> +static bool efi_image_authenticate(void *efi, size_t len)
>>>>> +{
>>>>> +	return true;
>>>>> +}
>>>>> +#endif /* CONFIG_EFI_SECURE_BOOT */
>>>>> +
>>>>>   /**
>>>>>    * efi_load_pe() - relocate EFI binary
>>>>>    *
>>>>> @@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
>>>>>    * @loaded_image_info:	loaded image protocol
>>>>>    * Return:		status code
>>>>>    */
>>>>> -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>>>>> +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
>>>>> +			 void *efi, size_t efi_size,
>>>>>   			 struct efi_loaded_image *loaded_image_info)
>>>>>   {
>>>>>   	IMAGE_NT_HEADERS32 *nt;
>>>>> @@ -231,17 +595,57 @@ 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;
>>>>> +	void *new_efi = NULL;
>>>>> +	size_t new_efi_size;
>>>>> +	efi_status_t ret;
>>>>> +
>>>>> +	/*
>>>>> +	 * 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 EFI_OUT_OF_RESOURCES;
>>>>> +		memcpy(new_efi, efi, efi_size);
>>>>> +		efi = new_efi;
>>>>> +		efi_size = new_efi_size;
>>>>> +	}
>>>>> +
>>>>> +	/* 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++)
>>>>> @@ -253,14 +657,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)) {
>>>>> +		ret = EFI_ACCESS_DENIED;
>>>>
>>>> According to the UEFI specification LoadImage() should return
>>>> EFI_SECURITY_VIOLATION in this case.
>>>
>>> Will check.
>>>
>>>> Further behaviour should depend on variables AuditMode and DeployedMode.
>>>>
>>>> If authentication fails, you must update the configuration table
>>>> identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
>>>> Execution Information. If the authentication succeeds you may enter a
>>>> record.
>>>>
>>>> It seems to me that in your patch series you are not creating the
>>>> configuration table at all.
>>>
>>> >From the very beginning of my submissions, I have clearly said
>>> that this feature was *beyond the scope* in my current series.
>>> ===8<===
>>> Unsupported features: (marked as TODO in most cases in the source code,
>>>                          and won't be included in this series)
>>> (snip)
>>> * recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
>>> ===>8===
>>>
>>>> The content of the Image Execution Information Table should be used to
>>>> decide if StartImage() may start an image. This is still missing in the
>>>> patch series.
>>>
>>> No.
>>> Whether such information be in configuration table or not,
>>> non-authenticated image won't be started if secure boot is in force.
>>
>> I cannot find any such check in efi_start_image().
>
> Because my current code *rejects* any unauthenticated images being loaded
> and return no valid pointer to image handle.
> If an error code in this case is changed and efi_load_image() still returns
> a valid handle, we will also have to modify efi_start_image().
>
>> Please, provide an implementation that complies with the UEFI specification:
>>
>> "The information is used to create the Image Execution Information
>> Table, which is added to the EFI System Configuration Table and assigned
>> the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."
>
> Regarding "Image Execution Information Table", Heavily disagree.
> The current UEFI code, not only mine but yours, has bunch of unimplemented
> features.

What is the point of this patch if efi_start_image() does not provide
any check?

Best regards

Heinrich

>
> -Takahiro Akashi
>
>
>> Best regards
>>
>> Heinrich
>>
>>>
>>> Thanks,
>>> -Takahiro Akashi
>>>
>>>>
>>>> Best regards
>>>>
>>>> Heinrich
>>>>
>>>>> +		goto err;
>>>>> +	}
>>>>> +
>>>>> +	/* 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,
>>>>> @@ -279,7 +698,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;
>>>>> @@ -295,7 +715,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;
>>>>> @@ -304,13 +725,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--) {
>>>>> @@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>>>>>   	loaded_image_info->image_size = virt_size;
>>>>>
>>>>>   	return EFI_SUCCESS;
>>>>> +
>>>>> +err:
>>>>> +	free(new_efi);
>>>>> +
>>>>> +	return ret;
>>>>>   }
>>>>>
>>>>
>>>
>>
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable
  2020-01-21  7:13   ` Heinrich Schuchardt
@ 2020-01-22  1:01     ` AKASHI Takahiro
  2020-01-22  6:38       ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-22  1:01 UTC (permalink / raw)
  To: u-boot

On Tue, Jan 21, 2020 at 08:13:06AM +0100, Heinrich Schuchardt wrote:
> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >A signature database variable is associated with a specific guid.
> >For convenience, if user doesn't supply any guid info, "env set|print -e"
> >should complement it.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  cmd/nvedit_efi.c | 18 ++++++++++++++----
> >  1 file changed, 14 insertions(+), 4 deletions(-)
> >
> >diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
> >index 8ea0da01283f..579cf430593c 100644
> >--- a/cmd/nvedit_efi.c
> >+++ b/cmd/nvedit_efi.c
> >@@ -41,6 +41,11 @@ static const struct {
> >  } efi_guid_text[] = {
> >  	/* signature database */
> >  	{EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"},
> >+	{EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"},
> >+	/* certificate type */
> >+	{EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"},
> >+	{EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"},
> >+	{EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"},
> >  };
> >
> >  /* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */
> >@@ -525,9 +530,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 +544,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"))
> 
> Why is "dbr" missing?

Because it is not yet supported and I have no plan to support it
in short term.

> I guess dbDefault, dbrDefault, dbxDefault, dbtDefault use
> EFI_GLOBAL_VARIABLE?

Yes.
I have a patch for supporting those *Default now, but will submit it
once my core secure boot patch is accepted.

Thanks,
-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >+			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 *)
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-21  7:15           ` Heinrich Schuchardt
@ 2020-01-22  1:13             ` AKASHI Takahiro
  2020-01-22  7:42               ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-22  1:13 UTC (permalink / raw)
  To: u-boot

On Tue, Jan 21, 2020 at 08:15:06AM +0100, Heinrich Schuchardt wrote:
> On 1/21/20 7:12 AM, AKASHI Takahiro wrote:
> >On Fri, Jan 17, 2020 at 06:51:50AM +0100, Heinrich Schuchardt wrote:
> >>On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
> >>>On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
> >>>>On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >>>>>With this commit, image validation can be enforced, as UEFI specification
> >>>>>section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
> >>>>>
> >>>>>Currently we support
> >>>>>* authentication based on db and dbx,
> >>>>>   so dbx-validated image will always be rejected.
> >>>>>* following signature types:
> >>>>>     EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
> >>>>>     EFI_CERT_X509_GUID (x509 certificate for signed images)
> >>>>>Timestamp-based certificate revocation is not supported here.
> >>>>>
> >>>>>Internally, authentication data is stored in one of certificates tables
> >>>>>of PE image (See efi_image_parse()) and will be verified by
> >>>>>efi_image_authenticate() before loading a given image.
> >>>>>
> >>>>>It seems that UEFI specification defines the verification process
> >>>>>in a bit ambiguous way. I tried to implement it as closely to as
> >>>>>EDK2 does.
> >>>>>
> >>>>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>>>---
> >>>>>  include/efi_loader.h              |   7 +-
> >>>>>  lib/efi_loader/efi_boottime.c     |   2 +-
> >>>>>  lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
> >>>>>  3 files changed, 449 insertions(+), 14 deletions(-)
> >>>>>
> >>>>>diff --git a/include/efi_loader.h b/include/efi_loader.h
> >>>>>index 1f88caf86709..e12b49098fb0 100644
> >>>>>--- a/include/efi_loader.h
> >>>>>+++ b/include/efi_loader.h
> >>>>>@@ -11,6 +11,7 @@
> >>>>>  #include <common.h>
> >>>>>  #include <part_efi.h>
> >>>>>  #include <efi_api.h>
> >>>>>+#include <pe.h>
> >>>>>
> >>>>>  static inline int guidcmp(const void *g1, const void *g2)
> >>>>>  {
> >>>>>@@ -398,7 +399,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);
> >>>>>@@ -726,6 +728,9 @@ 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) */
> >>>>>diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> >>>>>index 493d906c641d..311681764034 100644
> >>>>>--- a/lib/efi_loader/efi_boottime.c
> >>>>>+++ b/lib/efi_loader/efi_boottime.c
> >>>>>@@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
> >>>>>  	efi_dp_split_file_path(file_path, &dp, &fp);
> >>>>>  	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
> >>>>>  	if (ret == EFI_SUCCESS)
> >>>>>-		ret = efi_load_pe(*image_obj, dest_buffer, info);
> >>>>>+		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
> >>>>>  	if (!source_buffer)
> >>>>>  		/* Release buffer to which file was loaded */
> >>>>>  		efi_free_pages((uintptr_t)dest_buffer,
> >>>>>diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> >>>>>index 13541cfa7a28..939758e61e3c 100644
> >>>>>--- a/lib/efi_loader/efi_image_loader.c
> >>>>>+++ b/lib/efi_loader/efi_image_loader.c
> >>>>>@@ -9,7 +9,9 @@
> >>>>>
> >>>>>  #include <common.h>
> >>>>>  #include <efi_loader.h>
> >>>>>+#include <malloc.h>
> >>>>>  #include <pe.h>
> >>>>>+#include "../lib/crypto/pkcs7_parser.h"
> >>>>>
> >>>>>  const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> >>>>>  const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
> >>>>>@@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
> >>>>>  	}
> >>>>>  }
> >>>>>
> >>>>>+#ifdef CONFIG_EFI_SECURE_BOOT
> >>>>>+/**
> >>>>>+ * efi_image_parse - parse a PE image
> >>>>>+ * @efi:	Pointer to image
> >>>>>+ * @len:	Size of @efi
> >>>>>+ * @regs:	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)
> >>>>
> >>>>
> >>>>This function is way too long (> 100 lines). Please, cut it into logical
> >>>>units.
> >>>>
> >>>>
> >>>>
> >>>>>+{
> >>>>>+	struct efi_image_regions *regs;
> >>>>>+	IMAGE_DOS_HEADER *dos;
> >>>>>+	IMAGE_NT_HEADERS32 *nt;
> >>>>>+	IMAGE_SECTION_HEADER *sections, **sorted;
> >>>>>+	int num_regions, num_sections, i, j;
> >>>>>+	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.
> >>>>>+	 * As we can assume that it is already ordered in most cases,
> >>>>>+	 * the following code is optimized for this.
> >>>>>+	 */
> >>>>>+	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
> >>>>
> >>>>If sections[0] is not the lowest entry this function fails.
> >>>>
> >>>>Please, use qsort() supplied in lib/qsort.c.
> >>>
> >>>I'd rather fix my code as it is much simpler than qsort.
> >>
> >>Using qsort will result in a smaller code size. With qsort your code
> >>will also be much easier to read.
> >>
> >>>
> >>>>>+		if (sorted[i - 1]->VirtualAddress
> >>>>>+				<= sections[i].VirtualAddress) {
> >>>>>+			sorted[i] = &sections[i];
> >>>>>+		} else {
> >>>>>+			if (i == 1) {
> >>>>>+				sorted[1] = sorted[0];
> >>>>>+				sorted[0] = &sections[1];
> >>>>>+				continue;
> >>>>>+			}
> >>>>>+
> >>>>>+			sorted[i] = sorted[i - 1];
> >>>>>+			for (j = i - 2; j >= 0; j--) {
> >>>>>+				if (!j || sorted[j]->VirtualAddress
> >>>>>+						<= sections[i].VirtualAddress) {
> >>>>>+					sorted[j + 1] = &sections[i];
> >>>>>+					continue;
> >>>>>+				} else {
> >>>>>+					sorted[j + 1] = sorted[j];
> >>>>>+				}
> >>>>>+			}
> >>>>>+		}
> >>>>>+	}
> >>>>>+
> >>>>>+	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
> >>>>>+ * @len:	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 len)
> >>>>>+{
> >>>>>+	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;
> >>>>>+	bool ret = false;
> >>>>>+
> >>>>>+	if (!efi_secure_boot_enabled())
> >>>>>+		return true;
> >>>>>+
> >>>>>+	if (!efi_image_parse(efi, len, &regs, &wincerts,
> >>>>>+			     &wincerts_len)) {
> >>>>>+		debug("Parsing PE executable image failed\n");
> >>>>>+		return false;
> >>>>>+	}
> >>>>>+
> >>>>>+	if (!wincerts) {
> >>>>>+		/* The image is not signed */
> >>>>>+		ret = efi_image_unsigned_authenticate(regs);
> >>>>>+		free(regs);
> >>>>>+
> >>>>>+		return ret;
> >>>>>+	}
> >>>>>+
> >>>>>+	/*
> >>>>>+	 * verify signature using db and dbx
> >>>>>+	 */
> >>>>>+	db = efi_sigstore_parse_sigdb(L"db");
> >>>>>+	if (!db) {
> >>>>>+		debug("Getting signature database(db) failed\n");
> >>>>>+		goto err;
> >>>>>+	}
> >>>>>+
> >>>>>+	dbx = efi_sigstore_parse_sigdb(L"dbx");
> >>>>>+	if (!dbx) {
> >>>>>+		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);
> >>>>>+
> >>>>>+	return ret;
> >>>>>+}
> >>>>>+#else
> >>>>>+static bool efi_image_authenticate(void *efi, size_t len)
> >>>>>+{
> >>>>>+	return true;
> >>>>>+}
> >>>>>+#endif /* CONFIG_EFI_SECURE_BOOT */
> >>>>>+
> >>>>>  /**
> >>>>>   * efi_load_pe() - relocate EFI binary
> >>>>>   *
> >>>>>@@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
> >>>>>   * @loaded_image_info:	loaded image protocol
> >>>>>   * Return:		status code
> >>>>>   */
> >>>>>-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >>>>>+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
> >>>>>+			 void *efi, size_t efi_size,
> >>>>>  			 struct efi_loaded_image *loaded_image_info)
> >>>>>  {
> >>>>>  	IMAGE_NT_HEADERS32 *nt;
> >>>>>@@ -231,17 +595,57 @@ 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;
> >>>>>+	void *new_efi = NULL;
> >>>>>+	size_t new_efi_size;
> >>>>>+	efi_status_t ret;
> >>>>>+
> >>>>>+	/*
> >>>>>+	 * 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 EFI_OUT_OF_RESOURCES;
> >>>>>+		memcpy(new_efi, efi, efi_size);
> >>>>>+		efi = new_efi;
> >>>>>+		efi_size = new_efi_size;
> >>>>>+	}
> >>>>>+
> >>>>>+	/* 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++)
> >>>>>@@ -253,14 +657,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)) {
> >>>>>+		ret = EFI_ACCESS_DENIED;
> >>>>
> >>>>According to the UEFI specification LoadImage() should return
> >>>>EFI_SECURITY_VIOLATION in this case.
> >>>
> >>>Will check.
> >>>
> >>>>Further behaviour should depend on variables AuditMode and DeployedMode.
> >>>>
> >>>>If authentication fails, you must update the configuration table
> >>>>identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
> >>>>Execution Information. If the authentication succeeds you may enter a
> >>>>record.
> >>>>
> >>>>It seems to me that in your patch series you are not creating the
> >>>>configuration table at all.
> >>>
> >>>>From the very beginning of my submissions, I have clearly said
> >>>that this feature was *beyond the scope* in my current series.
> >>>===8<===
> >>>Unsupported features: (marked as TODO in most cases in the source code,
> >>>                         and won't be included in this series)
> >>>(snip)
> >>>* recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
> >>>===>8===
> >>>
> >>>>The content of the Image Execution Information Table should be used to
> >>>>decide if StartImage() may start an image. This is still missing in the
> >>>>patch series.
> >>>
> >>>No.
> >>>Whether such information be in configuration table or not,
> >>>non-authenticated image won't be started if secure boot is in force.
> >>
> >>I cannot find any such check in efi_start_image().
> >
> >Because my current code *rejects* any unauthenticated images being loaded
> >and return no valid pointer to image handle.
> >If an error code in this case is changed and efi_load_image() still returns
> >a valid handle, we will also have to modify efi_start_image().
> >
> >>Please, provide an implementation that complies with the UEFI specification:
> >>
> >>"The information is used to create the Image Execution Information
> >>Table, which is added to the EFI System Configuration Table and assigned
> >>the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."
> >
> >Regarding "Image Execution Information Table", Heavily disagree.
> >The current UEFI code, not only mine but yours, has bunch of unimplemented
> >features.
> 
> What is the point of this patch if efi_start_image() does not provide
> any check?

As I said in the previous reply,
efi_load_image() doesn't return any valid handle for unauthenticated binaries
in my *current* implementation, efi_start_image() has no chance to execute them.
No check is necessary. That's it.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >
> >-Takahiro Akashi
> >
> >
> >>Best regards
> >>
> >>Heinrich
> >>
> >>>
> >>>Thanks,
> >>>-Takahiro Akashi
> >>>
> >>>>
> >>>>Best regards
> >>>>
> >>>>Heinrich
> >>>>
> >>>>>+		goto err;
> >>>>>+	}
> >>>>>+
> >>>>>+	/* 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,
> >>>>>@@ -279,7 +698,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;
> >>>>>@@ -295,7 +715,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;
> >>>>>@@ -304,13 +725,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--) {
> >>>>>@@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >>>>>  	loaded_image_info->image_size = virt_size;
> >>>>>
> >>>>>  	return EFI_SUCCESS;
> >>>>>+
> >>>>>+err:
> >>>>>+	free(new_efi);
> >>>>>+
> >>>>>+	return ret;
> >>>>>  }
> >>>>>
> >>>>
> >>>
> >>
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable
  2020-01-22  1:01     ` AKASHI Takahiro
@ 2020-01-22  6:38       ` Heinrich Schuchardt
  2020-01-22  7:15         ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-22  6:38 UTC (permalink / raw)
  To: u-boot

On 1/22/20 2:01 AM, AKASHI Takahiro wrote:
> On Tue, Jan 21, 2020 at 08:13:06AM +0100, Heinrich Schuchardt wrote:
>> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
>>> A signature database variable is associated with a specific guid.
>>> For convenience, if user doesn't supply any guid info, "env set|print -e"
>>> should complement it.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>   cmd/nvedit_efi.c | 18 ++++++++++++++----
>>>   1 file changed, 14 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
>>> index 8ea0da01283f..579cf430593c 100644
>>> --- a/cmd/nvedit_efi.c
>>> +++ b/cmd/nvedit_efi.c
>>> @@ -41,6 +41,11 @@ static const struct {
>>>   } efi_guid_text[] = {
>>>   	/* signature database */
>>>   	{EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"},
>>> +	{EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"},
>>> +	/* certificate type */
>>> +	{EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"},
>>> +	{EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"},
>>> +	{EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"},
>>>   };
>>>
>>>   /* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */
>>> @@ -525,9 +530,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 +544,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"))
>>
>> Why is "dbr" missing?
>
> Because it is not yet supported and I have no plan to support it
> in short term.

This should not stop us from adding "dbr" here just to keep the setenv
command consistent even if the value of dbr will not yet be considered
in our secure boot implementation.

Best regards

Heinrich

>
>> I guess dbDefault, dbrDefault, dbxDefault, dbtDefault use
>> EFI_GLOBAL_VARIABLE?
>
> Yes.
> I have a patch for supporting those *Default now, but will submit it
> once my core secure boot patch is accepted.
>
> Thanks,
> -Takahiro Akashi
>
>> Best regards
>>
>> Heinrich
>>
>>> +			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 *)
>>>
>>
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable
  2020-01-22  6:38       ` Heinrich Schuchardt
@ 2020-01-22  7:15         ` AKASHI Takahiro
  0 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-22  7:15 UTC (permalink / raw)
  To: u-boot

On Wed, Jan 22, 2020 at 07:38:06AM +0100, Heinrich Schuchardt wrote:
> On 1/22/20 2:01 AM, AKASHI Takahiro wrote:
> >On Tue, Jan 21, 2020 at 08:13:06AM +0100, Heinrich Schuchardt wrote:
> >>On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >>>A signature database variable is associated with a specific guid.
> >>>For convenience, if user doesn't supply any guid info, "env set|print -e"
> >>>should complement it.
> >>>
> >>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>---
> >>>  cmd/nvedit_efi.c | 18 ++++++++++++++----
> >>>  1 file changed, 14 insertions(+), 4 deletions(-)
> >>>
> >>>diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
> >>>index 8ea0da01283f..579cf430593c 100644
> >>>--- a/cmd/nvedit_efi.c
> >>>+++ b/cmd/nvedit_efi.c
> >>>@@ -41,6 +41,11 @@ static const struct {
> >>>  } efi_guid_text[] = {
> >>>  	/* signature database */
> >>>  	{EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"},
> >>>+	{EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"},
> >>>+	/* certificate type */
> >>>+	{EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"},
> >>>+	{EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"},
> >>>+	{EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"},
> >>>  };
> >>>
> >>>  /* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */
> >>>@@ -525,9 +530,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 +544,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"))
> >>
> >>Why is "dbr" missing?
> >
> >Because it is not yet supported and I have no plan to support it
> >in short term.
> 
> This should not stop us from adding "dbr" here just to keep the setenv
> command consistent even if the value of dbr will not yet be considered
> in our secure boot implementation.

Your comments are inconsistent from time to time.
Please remember when I submitted the patch "cmd: env: extend "env
[set|print] -e" to manage UEFI variables".
In my early versions, "-at" (TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
was also supported. But you rejected it just because secure boot
was not merged yet at the moment.

So nak to your suggestion above.

-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >
> >>I guess dbDefault, dbrDefault, dbxDefault, dbtDefault use
> >>EFI_GLOBAL_VARIABLE?
> >
> >Yes.
> >I have a patch for supporting those *Default now, but will submit it
> >once my core secure boot patch is accepted.
> >
> >Thanks,
> >-Takahiro Akashi
> >
> >>Best regards
> >>
> >>Heinrich
> >>
> >>>+			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 *)
> >>>
> >>
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-22  1:13             ` AKASHI Takahiro
@ 2020-01-22  7:42               ` AKASHI Takahiro
  2020-01-23 17:41                 ` Heinrich Schuchardt
  0 siblings, 1 reply; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-22  7:42 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Wed, Jan 22, 2020 at 10:13:48AM +0900, AKASHI Takahiro wrote:
> On Tue, Jan 21, 2020 at 08:15:06AM +0100, Heinrich Schuchardt wrote:
> > On 1/21/20 7:12 AM, AKASHI Takahiro wrote:
> > >On Fri, Jan 17, 2020 at 06:51:50AM +0100, Heinrich Schuchardt wrote:
> > >>On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
> > >>>On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
> > >>>>On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> > >>>>>With this commit, image validation can be enforced, as UEFI specification
> > >>>>>section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
> > >>>>>
> > >>>>>Currently we support
> > >>>>>* authentication based on db and dbx,
> > >>>>>   so dbx-validated image will always be rejected.
> > >>>>>* following signature types:
> > >>>>>     EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
> > >>>>>     EFI_CERT_X509_GUID (x509 certificate for signed images)
> > >>>>>Timestamp-based certificate revocation is not supported here.
> > >>>>>
> > >>>>>Internally, authentication data is stored in one of certificates tables
> > >>>>>of PE image (See efi_image_parse()) and will be verified by
> > >>>>>efi_image_authenticate() before loading a given image.
> > >>>>>
> > >>>>>It seems that UEFI specification defines the verification process
> > >>>>>in a bit ambiguous way. I tried to implement it as closely to as
> > >>>>>EDK2 does.
> > >>>>>
> > >>>>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > >>>>>---
> > >>>>>  include/efi_loader.h              |   7 +-
> > >>>>>  lib/efi_loader/efi_boottime.c     |   2 +-
> > >>>>>  lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
> > >>>>>  3 files changed, 449 insertions(+), 14 deletions(-)
> > >>>>>
> > >>>>>diff --git a/include/efi_loader.h b/include/efi_loader.h
> > >>>>>index 1f88caf86709..e12b49098fb0 100644
> > >>>>>--- a/include/efi_loader.h
> > >>>>>+++ b/include/efi_loader.h
> > >>>>>@@ -11,6 +11,7 @@
> > >>>>>  #include <common.h>
> > >>>>>  #include <part_efi.h>
> > >>>>>  #include <efi_api.h>
> > >>>>>+#include <pe.h>
> > >>>>>
> > >>>>>  static inline int guidcmp(const void *g1, const void *g2)
> > >>>>>  {
> > >>>>>@@ -398,7 +399,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);
> > >>>>>@@ -726,6 +728,9 @@ 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) */
> > >>>>>diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> > >>>>>index 493d906c641d..311681764034 100644
> > >>>>>--- a/lib/efi_loader/efi_boottime.c
> > >>>>>+++ b/lib/efi_loader/efi_boottime.c
> > >>>>>@@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
> > >>>>>  	efi_dp_split_file_path(file_path, &dp, &fp);
> > >>>>>  	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
> > >>>>>  	if (ret == EFI_SUCCESS)
> > >>>>>-		ret = efi_load_pe(*image_obj, dest_buffer, info);
> > >>>>>+		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
> > >>>>>  	if (!source_buffer)
> > >>>>>  		/* Release buffer to which file was loaded */
> > >>>>>  		efi_free_pages((uintptr_t)dest_buffer,
> > >>>>>diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> > >>>>>index 13541cfa7a28..939758e61e3c 100644
> > >>>>>--- a/lib/efi_loader/efi_image_loader.c
> > >>>>>+++ b/lib/efi_loader/efi_image_loader.c
> > >>>>>@@ -9,7 +9,9 @@
> > >>>>>
> > >>>>>  #include <common.h>
> > >>>>>  #include <efi_loader.h>
> > >>>>>+#include <malloc.h>
> > >>>>>  #include <pe.h>
> > >>>>>+#include "../lib/crypto/pkcs7_parser.h"
> > >>>>>
> > >>>>>  const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> > >>>>>  const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
> > >>>>>@@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
> > >>>>>  	}
> > >>>>>  }
> > >>>>>
> > >>>>>+#ifdef CONFIG_EFI_SECURE_BOOT
> > >>>>>+/**
> > >>>>>+ * efi_image_parse - parse a PE image
> > >>>>>+ * @efi:	Pointer to image
> > >>>>>+ * @len:	Size of @efi
> > >>>>>+ * @regs:	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)
> > >>>>
> > >>>>
> > >>>>This function is way too long (> 100 lines). Please, cut it into logical
> > >>>>units.
> > >>>>
> > >>>>
> > >>>>
> > >>>>>+{
> > >>>>>+	struct efi_image_regions *regs;
> > >>>>>+	IMAGE_DOS_HEADER *dos;
> > >>>>>+	IMAGE_NT_HEADERS32 *nt;
> > >>>>>+	IMAGE_SECTION_HEADER *sections, **sorted;
> > >>>>>+	int num_regions, num_sections, i, j;
> > >>>>>+	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.
> > >>>>>+	 * As we can assume that it is already ordered in most cases,
> > >>>>>+	 * the following code is optimized for this.
> > >>>>>+	 */
> > >>>>>+	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
> > >>>>
> > >>>>If sections[0] is not the lowest entry this function fails.
> > >>>>
> > >>>>Please, use qsort() supplied in lib/qsort.c.
> > >>>
> > >>>I'd rather fix my code as it is much simpler than qsort.
> > >>
> > >>Using qsort will result in a smaller code size. With qsort your code
> > >>will also be much easier to read.
> > >>
> > >>>
> > >>>>>+		if (sorted[i - 1]->VirtualAddress
> > >>>>>+				<= sections[i].VirtualAddress) {
> > >>>>>+			sorted[i] = &sections[i];
> > >>>>>+		} else {
> > >>>>>+			if (i == 1) {
> > >>>>>+				sorted[1] = sorted[0];
> > >>>>>+				sorted[0] = &sections[1];
> > >>>>>+				continue;
> > >>>>>+			}
> > >>>>>+
> > >>>>>+			sorted[i] = sorted[i - 1];
> > >>>>>+			for (j = i - 2; j >= 0; j--) {
> > >>>>>+				if (!j || sorted[j]->VirtualAddress
> > >>>>>+						<= sections[i].VirtualAddress) {
> > >>>>>+					sorted[j + 1] = &sections[i];
> > >>>>>+					continue;
> > >>>>>+				} else {
> > >>>>>+					sorted[j + 1] = sorted[j];
> > >>>>>+				}
> > >>>>>+			}
> > >>>>>+		}
> > >>>>>+	}
> > >>>>>+
> > >>>>>+	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
> > >>>>>+ * @len:	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 len)
> > >>>>>+{
> > >>>>>+	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;
> > >>>>>+	bool ret = false;
> > >>>>>+
> > >>>>>+	if (!efi_secure_boot_enabled())
> > >>>>>+		return true;
> > >>>>>+
> > >>>>>+	if (!efi_image_parse(efi, len, &regs, &wincerts,
> > >>>>>+			     &wincerts_len)) {
> > >>>>>+		debug("Parsing PE executable image failed\n");
> > >>>>>+		return false;
> > >>>>>+	}
> > >>>>>+
> > >>>>>+	if (!wincerts) {
> > >>>>>+		/* The image is not signed */
> > >>>>>+		ret = efi_image_unsigned_authenticate(regs);
> > >>>>>+		free(regs);
> > >>>>>+
> > >>>>>+		return ret;
> > >>>>>+	}
> > >>>>>+
> > >>>>>+	/*
> > >>>>>+	 * verify signature using db and dbx
> > >>>>>+	 */
> > >>>>>+	db = efi_sigstore_parse_sigdb(L"db");
> > >>>>>+	if (!db) {
> > >>>>>+		debug("Getting signature database(db) failed\n");
> > >>>>>+		goto err;
> > >>>>>+	}
> > >>>>>+
> > >>>>>+	dbx = efi_sigstore_parse_sigdb(L"dbx");
> > >>>>>+	if (!dbx) {
> > >>>>>+		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);
> > >>>>>+
> > >>>>>+	return ret;
> > >>>>>+}
> > >>>>>+#else
> > >>>>>+static bool efi_image_authenticate(void *efi, size_t len)
> > >>>>>+{
> > >>>>>+	return true;
> > >>>>>+}
> > >>>>>+#endif /* CONFIG_EFI_SECURE_BOOT */
> > >>>>>+
> > >>>>>  /**
> > >>>>>   * efi_load_pe() - relocate EFI binary
> > >>>>>   *
> > >>>>>@@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
> > >>>>>   * @loaded_image_info:	loaded image protocol
> > >>>>>   * Return:		status code
> > >>>>>   */
> > >>>>>-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> > >>>>>+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
> > >>>>>+			 void *efi, size_t efi_size,
> > >>>>>  			 struct efi_loaded_image *loaded_image_info)
> > >>>>>  {
> > >>>>>  	IMAGE_NT_HEADERS32 *nt;
> > >>>>>@@ -231,17 +595,57 @@ 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;
> > >>>>>+	void *new_efi = NULL;
> > >>>>>+	size_t new_efi_size;
> > >>>>>+	efi_status_t ret;
> > >>>>>+
> > >>>>>+	/*
> > >>>>>+	 * 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 EFI_OUT_OF_RESOURCES;
> > >>>>>+		memcpy(new_efi, efi, efi_size);
> > >>>>>+		efi = new_efi;
> > >>>>>+		efi_size = new_efi_size;
> > >>>>>+	}
> > >>>>>+
> > >>>>>+	/* 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++)
> > >>>>>@@ -253,14 +657,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)) {
> > >>>>>+		ret = EFI_ACCESS_DENIED;
> > >>>>
> > >>>>According to the UEFI specification LoadImage() should return
> > >>>>EFI_SECURITY_VIOLATION in this case.
> > >>>
> > >>>Will check.
> > >>>
> > >>>>Further behaviour should depend on variables AuditMode and DeployedMode.
> > >>>>
> > >>>>If authentication fails, you must update the configuration table
> > >>>>identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
> > >>>>Execution Information. If the authentication succeeds you may enter a
> > >>>>record.
> > >>>>
> > >>>>It seems to me that in your patch series you are not creating the
> > >>>>configuration table at all.
> > >>>
> > >>>>From the very beginning of my submissions, I have clearly said
> > >>>that this feature was *beyond the scope* in my current series.
> > >>>===8<===
> > >>>Unsupported features: (marked as TODO in most cases in the source code,
> > >>>                         and won't be included in this series)
> > >>>(snip)
> > >>>* recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
> > >>>===>8===
> > >>>
> > >>>>The content of the Image Execution Information Table should be used to
> > >>>>decide if StartImage() may start an image. This is still missing in the
> > >>>>patch series.
> > >>>
> > >>>No.
> > >>>Whether such information be in configuration table or not,
> > >>>non-authenticated image won't be started if secure boot is in force.
> > >>
> > >>I cannot find any such check in efi_start_image().
> > >
> > >Because my current code *rejects* any unauthenticated images being loaded
> > >and return no valid pointer to image handle.
> > >If an error code in this case is changed and efi_load_image() still returns
> > >a valid handle, we will also have to modify efi_start_image().
> > >
> > >>Please, provide an implementation that complies with the UEFI specification:
> > >>
> > >>"The information is used to create the Image Execution Information
> > >>Table, which is added to the EFI System Configuration Table and assigned
> > >>the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."
> > >
> > >Regarding "Image Execution Information Table", Heavily disagree.
> > >The current UEFI code, not only mine but yours, has bunch of unimplemented
> > >features.
> > 
> > What is the point of this patch if efi_start_image() does not provide
> > any check?
> 
> As I said in the previous reply,
> efi_load_image() doesn't return any valid handle for unauthenticated binaries
> in my *current* implementation, efi_start_image() has no chance to execute them.
> No check is necessary. That's it.

I double-checked edk2 code as well as UEFI specification, and found
a couple of insights:

a. EDK2 code has several internal help functions for verifications.
   If they fail to find any valid signature in db's, the status (or
   internal error code) is set to ACCESS_DENIED.
   Then, at the end of verification, if the status is not EFI_SUCCESS,
   the return code is anyhow rewritten to SECURITY_VIOLATION.
   (That is why my code returns ACCESS_DENIED right now.)
b. While UEFI specification requires that efiLoadImage() return
   SECURITY_VIOLATION if "the image signature is not valid,"
   it doesn't mention if a handle to the image be returned or not.
c. "Status Codes Returned" can also read that it depends on
   "platform policy" if we return ACCESS_DENIED or SECURITY_VIOLATION.
   But the policy may be vendor/platform, or even U-Boot specific as
   UEFI specification doesn't mention anything about that.

Thinking of the fact that we don't have any consensus nor implementation
of "policy" yet, I believe that the best solution for now is:
   efi_load_image() return EFI_SECURITY_VIOLATION if the image signature
   is not verified and does *not* return a handle to image.

This behavior is safe and yet won't prevent us from additionally implementing
"policy" framework as well as image information table when adding
Audit/DeployedMode support in the future.
Can you agree?

-Takahiro Akashi


> -Takahiro Akashi
> 
> > Best regards
> > 
> > Heinrich
> > 
> > >
> > >-Takahiro Akashi
> > >
> > >
> > >>Best regards
> > >>
> > >>Heinrich
> > >>
> > >>>
> > >>>Thanks,
> > >>>-Takahiro Akashi
> > >>>
> > >>>>
> > >>>>Best regards
> > >>>>
> > >>>>Heinrich
> > >>>>
> > >>>>>+		goto err;
> > >>>>>+	}
> > >>>>>+
> > >>>>>+	/* 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,
> > >>>>>@@ -279,7 +698,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;
> > >>>>>@@ -295,7 +715,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;
> > >>>>>@@ -304,13 +725,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--) {
> > >>>>>@@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> > >>>>>  	loaded_image_info->image_size = virt_size;
> > >>>>>
> > >>>>>  	return EFI_SUCCESS;
> > >>>>>+
> > >>>>>+err:
> > >>>>>+	free(new_efi);
> > >>>>>+
> > >>>>>+	return ret;
> > >>>>>  }
> > >>>>>
> > >>>>
> > >>>
> > >>
> > >
> > 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-22  7:42               ` AKASHI Takahiro
@ 2020-01-23 17:41                 ` Heinrich Schuchardt
  2020-01-27  6:52                   ` AKASHI Takahiro
  0 siblings, 1 reply; 45+ messages in thread
From: Heinrich Schuchardt @ 2020-01-23 17:41 UTC (permalink / raw)
  To: u-boot

On 1/22/20 8:42 AM, AKASHI Takahiro wrote:
> Heinrich,
>
> On Wed, Jan 22, 2020 at 10:13:48AM +0900, AKASHI Takahiro wrote:
>> On Tue, Jan 21, 2020 at 08:15:06AM +0100, Heinrich Schuchardt wrote:
>>> On 1/21/20 7:12 AM, AKASHI Takahiro wrote:
>>>> On Fri, Jan 17, 2020 at 06:51:50AM +0100, Heinrich Schuchardt wrote:
>>>>> On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
>>>>>> On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
>>>>>>> On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
>>>>>>>> With this commit, image validation can be enforced, as UEFI specification
>>>>>>>> section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
>>>>>>>>
>>>>>>>> Currently we support
>>>>>>>> * authentication based on db and dbx,
>>>>>>>>    so dbx-validated image will always be rejected.
>>>>>>>> * following signature types:
>>>>>>>>      EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
>>>>>>>>      EFI_CERT_X509_GUID (x509 certificate for signed images)
>>>>>>>> Timestamp-based certificate revocation is not supported here.
>>>>>>>>
>>>>>>>> Internally, authentication data is stored in one of certificates tables
>>>>>>>> of PE image (See efi_image_parse()) and will be verified by
>>>>>>>> efi_image_authenticate() before loading a given image.
>>>>>>>>
>>>>>>>> It seems that UEFI specification defines the verification process
>>>>>>>> in a bit ambiguous way. I tried to implement it as closely to as
>>>>>>>> EDK2 does.
>>>>>>>>
>>>>>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>>>>>> ---
>>>>>>>>   include/efi_loader.h              |   7 +-
>>>>>>>>   lib/efi_loader/efi_boottime.c     |   2 +-
>>>>>>>>   lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
>>>>>>>>   3 files changed, 449 insertions(+), 14 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>>>>>> index 1f88caf86709..e12b49098fb0 100644
>>>>>>>> --- a/include/efi_loader.h
>>>>>>>> +++ b/include/efi_loader.h
>>>>>>>> @@ -11,6 +11,7 @@
>>>>>>>>   #include <common.h>
>>>>>>>>   #include <part_efi.h>
>>>>>>>>   #include <efi_api.h>
>>>>>>>> +#include <pe.h>
>>>>>>>>
>>>>>>>>   static inline int guidcmp(const void *g1, const void *g2)
>>>>>>>>   {
>>>>>>>> @@ -398,7 +399,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);
>>>>>>>> @@ -726,6 +728,9 @@ 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) */
>>>>>>>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>>>>>>>> index 493d906c641d..311681764034 100644
>>>>>>>> --- a/lib/efi_loader/efi_boottime.c
>>>>>>>> +++ b/lib/efi_loader/efi_boottime.c
>>>>>>>> @@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
>>>>>>>>   	efi_dp_split_file_path(file_path, &dp, &fp);
>>>>>>>>   	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
>>>>>>>>   	if (ret == EFI_SUCCESS)
>>>>>>>> -		ret = efi_load_pe(*image_obj, dest_buffer, info);
>>>>>>>> +		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
>>>>>>>>   	if (!source_buffer)
>>>>>>>>   		/* Release buffer to which file was loaded */
>>>>>>>>   		efi_free_pages((uintptr_t)dest_buffer,
>>>>>>>> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
>>>>>>>> index 13541cfa7a28..939758e61e3c 100644
>>>>>>>> --- a/lib/efi_loader/efi_image_loader.c
>>>>>>>> +++ b/lib/efi_loader/efi_image_loader.c
>>>>>>>> @@ -9,7 +9,9 @@
>>>>>>>>
>>>>>>>>   #include <common.h>
>>>>>>>>   #include <efi_loader.h>
>>>>>>>> +#include <malloc.h>
>>>>>>>>   #include <pe.h>
>>>>>>>> +#include "../lib/crypto/pkcs7_parser.h"
>>>>>>>>
>>>>>>>>   const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
>>>>>>>>   const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
>>>>>>>> @@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
>>>>>>>>   	}
>>>>>>>>   }
>>>>>>>>
>>>>>>>> +#ifdef CONFIG_EFI_SECURE_BOOT
>>>>>>>> +/**
>>>>>>>> + * efi_image_parse - parse a PE image
>>>>>>>> + * @efi:	Pointer to image
>>>>>>>> + * @len:	Size of @efi
>>>>>>>> + * @regs:	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)
>>>>>>>
>>>>>>>
>>>>>>> This function is way too long (> 100 lines). Please, cut it into logical
>>>>>>> units.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> +{
>>>>>>>> +	struct efi_image_regions *regs;
>>>>>>>> +	IMAGE_DOS_HEADER *dos;
>>>>>>>> +	IMAGE_NT_HEADERS32 *nt;
>>>>>>>> +	IMAGE_SECTION_HEADER *sections, **sorted;
>>>>>>>> +	int num_regions, num_sections, i, j;
>>>>>>>> +	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.
>>>>>>>> +	 * As we can assume that it is already ordered in most cases,
>>>>>>>> +	 * the following code is optimized for this.
>>>>>>>> +	 */
>>>>>>>> +	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
>>>>>>>
>>>>>>> If sections[0] is not the lowest entry this function fails.
>>>>>>>
>>>>>>> Please, use qsort() supplied in lib/qsort.c.
>>>>>>
>>>>>> I'd rather fix my code as it is much simpler than qsort.
>>>>>
>>>>> Using qsort will result in a smaller code size. With qsort your code
>>>>> will also be much easier to read.
>>>>>
>>>>>>
>>>>>>>> +		if (sorted[i - 1]->VirtualAddress
>>>>>>>> +				<= sections[i].VirtualAddress) {
>>>>>>>> +			sorted[i] = &sections[i];
>>>>>>>> +		} else {
>>>>>>>> +			if (i == 1) {
>>>>>>>> +				sorted[1] = sorted[0];
>>>>>>>> +				sorted[0] = &sections[1];
>>>>>>>> +				continue;
>>>>>>>> +			}
>>>>>>>> +
>>>>>>>> +			sorted[i] = sorted[i - 1];
>>>>>>>> +			for (j = i - 2; j >= 0; j--) {
>>>>>>>> +				if (!j || sorted[j]->VirtualAddress
>>>>>>>> +						<= sections[i].VirtualAddress) {
>>>>>>>> +					sorted[j + 1] = &sections[i];
>>>>>>>> +					continue;
>>>>>>>> +				} else {
>>>>>>>> +					sorted[j + 1] = sorted[j];
>>>>>>>> +				}
>>>>>>>> +			}
>>>>>>>> +		}
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	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
>>>>>>>> + * @len:	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 len)
>>>>>>>> +{
>>>>>>>> +	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;
>>>>>>>> +	bool ret = false;
>>>>>>>> +
>>>>>>>> +	if (!efi_secure_boot_enabled())
>>>>>>>> +		return true;
>>>>>>>> +
>>>>>>>> +	if (!efi_image_parse(efi, len, &regs, &wincerts,
>>>>>>>> +			     &wincerts_len)) {
>>>>>>>> +		debug("Parsing PE executable image failed\n");
>>>>>>>> +		return false;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	if (!wincerts) {
>>>>>>>> +		/* The image is not signed */
>>>>>>>> +		ret = efi_image_unsigned_authenticate(regs);
>>>>>>>> +		free(regs);
>>>>>>>> +
>>>>>>>> +		return ret;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	/*
>>>>>>>> +	 * verify signature using db and dbx
>>>>>>>> +	 */
>>>>>>>> +	db = efi_sigstore_parse_sigdb(L"db");
>>>>>>>> +	if (!db) {
>>>>>>>> +		debug("Getting signature database(db) failed\n");
>>>>>>>> +		goto err;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	dbx = efi_sigstore_parse_sigdb(L"dbx");
>>>>>>>> +	if (!dbx) {
>>>>>>>> +		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);
>>>>>>>> +
>>>>>>>> +	return ret;
>>>>>>>> +}
>>>>>>>> +#else
>>>>>>>> +static bool efi_image_authenticate(void *efi, size_t len)
>>>>>>>> +{
>>>>>>>> +	return true;
>>>>>>>> +}
>>>>>>>> +#endif /* CONFIG_EFI_SECURE_BOOT */
>>>>>>>> +
>>>>>>>>   /**
>>>>>>>>    * efi_load_pe() - relocate EFI binary
>>>>>>>>    *
>>>>>>>> @@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
>>>>>>>>    * @loaded_image_info:	loaded image protocol
>>>>>>>>    * Return:		status code
>>>>>>>>    */
>>>>>>>> -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>>>>>>>> +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
>>>>>>>> +			 void *efi, size_t efi_size,
>>>>>>>>   			 struct efi_loaded_image *loaded_image_info)
>>>>>>>>   {
>>>>>>>>   	IMAGE_NT_HEADERS32 *nt;
>>>>>>>> @@ -231,17 +595,57 @@ 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;
>>>>>>>> +	void *new_efi = NULL;
>>>>>>>> +	size_t new_efi_size;
>>>>>>>> +	efi_status_t ret;
>>>>>>>> +
>>>>>>>> +	/*
>>>>>>>> +	 * 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 EFI_OUT_OF_RESOURCES;
>>>>>>>> +		memcpy(new_efi, efi, efi_size);
>>>>>>>> +		efi = new_efi;
>>>>>>>> +		efi_size = new_efi_size;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	/* 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++)
>>>>>>>> @@ -253,14 +657,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)) {
>>>>>>>> +		ret = EFI_ACCESS_DENIED;
>>>>>>>
>>>>>>> According to the UEFI specification LoadImage() should return
>>>>>>> EFI_SECURITY_VIOLATION in this case.
>>>>>>
>>>>>> Will check.
>>>>>>
>>>>>>> Further behaviour should depend on variables AuditMode and DeployedMode.
>>>>>>>
>>>>>>> If authentication fails, you must update the configuration table
>>>>>>> identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
>>>>>>> Execution Information. If the authentication succeeds you may enter a
>>>>>>> record.
>>>>>>>
>>>>>>> It seems to me that in your patch series you are not creating the
>>>>>>> configuration table at all.
>>>>>>
>>>>>> >From the very beginning of my submissions, I have clearly said
>>>>>> that this feature was *beyond the scope* in my current series.
>>>>>> ===8<===
>>>>>> Unsupported features: (marked as TODO in most cases in the source code,
>>>>>>                          and won't be included in this series)
>>>>>> (snip)
>>>>>> * recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
>>>>>> ===>8===
>>>>>>
>>>>>>> The content of the Image Execution Information Table should be used to
>>>>>>> decide if StartImage() may start an image. This is still missing in the
>>>>>>> patch series.
>>>>>>
>>>>>> No.
>>>>>> Whether such information be in configuration table or not,
>>>>>> non-authenticated image won't be started if secure boot is in force.
>>>>>
>>>>> I cannot find any such check in efi_start_image().
>>>>
>>>> Because my current code *rejects* any unauthenticated images being loaded
>>>> and return no valid pointer to image handle.
>>>> If an error code in this case is changed and efi_load_image() still returns
>>>> a valid handle, we will also have to modify efi_start_image().
>>>>
>>>>> Please, provide an implementation that complies with the UEFI specification:
>>>>>
>>>>> "The information is used to create the Image Execution Information
>>>>> Table, which is added to the EFI System Configuration Table and assigned
>>>>> the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."
>>>>
>>>> Regarding "Image Execution Information Table", Heavily disagree.
>>>> The current UEFI code, not only mine but yours, has bunch of unimplemented
>>>> features.
>>>
>>> What is the point of this patch if efi_start_image() does not provide
>>> any check?
>>
>> As I said in the previous reply,
>> efi_load_image() doesn't return any valid handle for unauthenticated binaries
>> in my *current* implementation, efi_start_image() has no chance to execute them.
>> No check is necessary. That's it.
>
> I double-checked edk2 code as well as UEFI specification, and found
> a couple of insights:
>
> a. EDK2 code has several internal help functions for verifications.
>     If they fail to find any valid signature in db's, the status (or
>     internal error code) is set to ACCESS_DENIED.
>     Then, at the end of verification, if the status is not EFI_SUCCESS,
>     the return code is anyhow rewritten to SECURITY_VIOLATION.
>     (That is why my code returns ACCESS_DENIED right now.)
> b. While UEFI specification requires that efiLoadImage() return
>     SECURITY_VIOLATION if "the image signature is not valid,"
>     it doesn't mention if a handle to the image be returned or not.

Please, have look at the return values for LoadImage() in the UEFI 2.8 spec.

If you return ACCESS_DENIED, <cite>Image was not loaded because the
platform policy prohibits the image from being loaded. NULL is returned
in *ImageHandle</cite>

If you return SECURITY_VIOLATION, <cite>Image was loaded and an
ImageHandle was created with a valid EFI_LOADED_IMAGE_PROTOCOL.</cite>

In the case of SECURITY_VIOLATION we have to ensure that
efi_start_image() does not start the image. This is why we need the
EFI_IMAGE_SECURITY_DATABASE.

> c. "Status Codes Returned" can also read that it depends on
>     "platform policy" if we return ACCESS_DENIED or SECURITY_VIOLATION.
>     But the policy may be vendor/platform, or even U-Boot specific as
>     UEFI specification doesn't mention anything about that.
>
> Thinking of the fact that we don't have any consensus nor implementation
> of "policy" yet, I believe that the best solution for now is:
>     efi_load_image() return EFI_SECURITY_VIOLATION if the image signature
>     is not verified and does *not* return a handle to image.

This would violate the UEFI spec.

Best regards

Heinrich

>
> This behavior is safe and yet won't prevent us from additionally implementing
> "policy" framework as well as image information table when adding
> Audit/DeployedMode support in the future.
> Can you agree?
>
> -Takahiro Akashi
>
>
>> -Takahiro Akashi
>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>>
>>>> -Takahiro Akashi
>>>>
>>>>
>>>>> Best regards
>>>>>
>>>>> Heinrich
>>>>>
>>>>>>
>>>>>> Thanks,
>>>>>> -Takahiro Akashi
>>>>>>
>>>>>>>
>>>>>>> Best regards
>>>>>>>
>>>>>>> Heinrich
>>>>>>>
>>>>>>>> +		goto err;
>>>>>>>> +	}
>>>>>>>> +
>>>>>>>> +	/* 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,
>>>>>>>> @@ -279,7 +698,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;
>>>>>>>> @@ -295,7 +715,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;
>>>>>>>> @@ -304,13 +725,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--) {
>>>>>>>> @@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
>>>>>>>>   	loaded_image_info->image_size = virt_size;
>>>>>>>>
>>>>>>>>   	return EFI_SUCCESS;
>>>>>>>> +
>>>>>>>> +err:
>>>>>>>> +	free(new_efi);
>>>>>>>> +
>>>>>>>> +	return ret;
>>>>>>>>   }
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 08/16] efi_loader: image_loader: support image authentication
  2020-01-23 17:41                 ` Heinrich Schuchardt
@ 2020-01-27  6:52                   ` AKASHI Takahiro
  0 siblings, 0 replies; 45+ messages in thread
From: AKASHI Takahiro @ 2020-01-27  6:52 UTC (permalink / raw)
  To: u-boot

On Thu, Jan 23, 2020 at 06:41:53PM +0100, Heinrich Schuchardt wrote:
> On 1/22/20 8:42 AM, AKASHI Takahiro wrote:
> >Heinrich,
> >
> >On Wed, Jan 22, 2020 at 10:13:48AM +0900, AKASHI Takahiro wrote:
> >>On Tue, Jan 21, 2020 at 08:15:06AM +0100, Heinrich Schuchardt wrote:
> >>>On 1/21/20 7:12 AM, AKASHI Takahiro wrote:
> >>>>On Fri, Jan 17, 2020 at 06:51:50AM +0100, Heinrich Schuchardt wrote:
> >>>>>On 1/17/20 6:11 AM, AKASHI Takahiro wrote:
> >>>>>>On Thu, Jan 09, 2020 at 12:55:17AM +0100, Heinrich Schuchardt wrote:
> >>>>>>>On 12/18/19 1:45 AM, AKASHI Takahiro wrote:
> >>>>>>>>With this commit, image validation can be enforced, as UEFI specification
> >>>>>>>>section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
> >>>>>>>>
> >>>>>>>>Currently we support
> >>>>>>>>* authentication based on db and dbx,
> >>>>>>>>   so dbx-validated image will always be rejected.
> >>>>>>>>* following signature types:
> >>>>>>>>     EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
> >>>>>>>>     EFI_CERT_X509_GUID (x509 certificate for signed images)
> >>>>>>>>Timestamp-based certificate revocation is not supported here.
> >>>>>>>>
> >>>>>>>>Internally, authentication data is stored in one of certificates tables
> >>>>>>>>of PE image (See efi_image_parse()) and will be verified by
> >>>>>>>>efi_image_authenticate() before loading a given image.
> >>>>>>>>
> >>>>>>>>It seems that UEFI specification defines the verification process
> >>>>>>>>in a bit ambiguous way. I tried to implement it as closely to as
> >>>>>>>>EDK2 does.
> >>>>>>>>
> >>>>>>>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>>>>>>---
> >>>>>>>>  include/efi_loader.h              |   7 +-
> >>>>>>>>  lib/efi_loader/efi_boottime.c     |   2 +-
> >>>>>>>>  lib/efi_loader/efi_image_loader.c | 454 +++++++++++++++++++++++++++++-
> >>>>>>>>  3 files changed, 449 insertions(+), 14 deletions(-)
> >>>>>>>>
> >>>>>>>>diff --git a/include/efi_loader.h b/include/efi_loader.h
> >>>>>>>>index 1f88caf86709..e12b49098fb0 100644
> >>>>>>>>--- a/include/efi_loader.h
> >>>>>>>>+++ b/include/efi_loader.h
> >>>>>>>>@@ -11,6 +11,7 @@
> >>>>>>>>  #include <common.h>
> >>>>>>>>  #include <part_efi.h>
> >>>>>>>>  #include <efi_api.h>
> >>>>>>>>+#include <pe.h>
> >>>>>>>>
> >>>>>>>>  static inline int guidcmp(const void *g1, const void *g2)
> >>>>>>>>  {
> >>>>>>>>@@ -398,7 +399,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);
> >>>>>>>>@@ -726,6 +728,9 @@ 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) */
> >>>>>>>>diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> >>>>>>>>index 493d906c641d..311681764034 100644
> >>>>>>>>--- a/lib/efi_loader/efi_boottime.c
> >>>>>>>>+++ b/lib/efi_loader/efi_boottime.c
> >>>>>>>>@@ -1879,7 +1879,7 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
> >>>>>>>>  	efi_dp_split_file_path(file_path, &dp, &fp);
> >>>>>>>>  	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
> >>>>>>>>  	if (ret == EFI_SUCCESS)
> >>>>>>>>-		ret = efi_load_pe(*image_obj, dest_buffer, info);
> >>>>>>>>+		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
> >>>>>>>>  	if (!source_buffer)
> >>>>>>>>  		/* Release buffer to which file was loaded */
> >>>>>>>>  		efi_free_pages((uintptr_t)dest_buffer,
> >>>>>>>>diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> >>>>>>>>index 13541cfa7a28..939758e61e3c 100644
> >>>>>>>>--- a/lib/efi_loader/efi_image_loader.c
> >>>>>>>>+++ b/lib/efi_loader/efi_image_loader.c
> >>>>>>>>@@ -9,7 +9,9 @@
> >>>>>>>>
> >>>>>>>>  #include <common.h>
> >>>>>>>>  #include <efi_loader.h>
> >>>>>>>>+#include <malloc.h>
> >>>>>>>>  #include <pe.h>
> >>>>>>>>+#include "../lib/crypto/pkcs7_parser.h"
> >>>>>>>>
> >>>>>>>>  const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> >>>>>>>>  const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
> >>>>>>>>@@ -205,6 +207,367 @@ static void efi_set_code_and_data_type(
> >>>>>>>>  	}
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>>+#ifdef CONFIG_EFI_SECURE_BOOT
> >>>>>>>>+/**
> >>>>>>>>+ * efi_image_parse - parse a PE image
> >>>>>>>>+ * @efi:	Pointer to image
> >>>>>>>>+ * @len:	Size of @efi
> >>>>>>>>+ * @regs:	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)
> >>>>>>>
> >>>>>>>
> >>>>>>>This function is way too long (> 100 lines). Please, cut it into logical
> >>>>>>>units.
> >>>>>>>
> >>>>>>>
> >>>>>>>
> >>>>>>>>+{
> >>>>>>>>+	struct efi_image_regions *regs;
> >>>>>>>>+	IMAGE_DOS_HEADER *dos;
> >>>>>>>>+	IMAGE_NT_HEADERS32 *nt;
> >>>>>>>>+	IMAGE_SECTION_HEADER *sections, **sorted;
> >>>>>>>>+	int num_regions, num_sections, i, j;
> >>>>>>>>+	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.
> >>>>>>>>+	 * As we can assume that it is already ordered in most cases,
> >>>>>>>>+	 * the following code is optimized for this.
> >>>>>>>>+	 */
> >>>>>>>>+	for (sorted[0] = &sections[0], i = 1; i < num_sections; i++) {
> >>>>>>>
> >>>>>>>If sections[0] is not the lowest entry this function fails.
> >>>>>>>
> >>>>>>>Please, use qsort() supplied in lib/qsort.c.
> >>>>>>
> >>>>>>I'd rather fix my code as it is much simpler than qsort.
> >>>>>
> >>>>>Using qsort will result in a smaller code size. With qsort your code
> >>>>>will also be much easier to read.
> >>>>>
> >>>>>>
> >>>>>>>>+		if (sorted[i - 1]->VirtualAddress
> >>>>>>>>+				<= sections[i].VirtualAddress) {
> >>>>>>>>+			sorted[i] = &sections[i];
> >>>>>>>>+		} else {
> >>>>>>>>+			if (i == 1) {
> >>>>>>>>+				sorted[1] = sorted[0];
> >>>>>>>>+				sorted[0] = &sections[1];
> >>>>>>>>+				continue;
> >>>>>>>>+			}
> >>>>>>>>+
> >>>>>>>>+			sorted[i] = sorted[i - 1];
> >>>>>>>>+			for (j = i - 2; j >= 0; j--) {
> >>>>>>>>+				if (!j || sorted[j]->VirtualAddress
> >>>>>>>>+						<= sections[i].VirtualAddress) {
> >>>>>>>>+					sorted[j + 1] = &sections[i];
> >>>>>>>>+					continue;
> >>>>>>>>+				} else {
> >>>>>>>>+					sorted[j + 1] = sorted[j];
> >>>>>>>>+				}
> >>>>>>>>+			}
> >>>>>>>>+		}
> >>>>>>>>+	}
> >>>>>>>>+
> >>>>>>>>+	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
> >>>>>>>>+ * @len:	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 len)
> >>>>>>>>+{
> >>>>>>>>+	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;
> >>>>>>>>+	bool ret = false;
> >>>>>>>>+
> >>>>>>>>+	if (!efi_secure_boot_enabled())
> >>>>>>>>+		return true;
> >>>>>>>>+
> >>>>>>>>+	if (!efi_image_parse(efi, len, &regs, &wincerts,
> >>>>>>>>+			     &wincerts_len)) {
> >>>>>>>>+		debug("Parsing PE executable image failed\n");
> >>>>>>>>+		return false;
> >>>>>>>>+	}
> >>>>>>>>+
> >>>>>>>>+	if (!wincerts) {
> >>>>>>>>+		/* The image is not signed */
> >>>>>>>>+		ret = efi_image_unsigned_authenticate(regs);
> >>>>>>>>+		free(regs);
> >>>>>>>>+
> >>>>>>>>+		return ret;
> >>>>>>>>+	}
> >>>>>>>>+
> >>>>>>>>+	/*
> >>>>>>>>+	 * verify signature using db and dbx
> >>>>>>>>+	 */
> >>>>>>>>+	db = efi_sigstore_parse_sigdb(L"db");
> >>>>>>>>+	if (!db) {
> >>>>>>>>+		debug("Getting signature database(db) failed\n");
> >>>>>>>>+		goto err;
> >>>>>>>>+	}
> >>>>>>>>+
> >>>>>>>>+	dbx = efi_sigstore_parse_sigdb(L"dbx");
> >>>>>>>>+	if (!dbx) {
> >>>>>>>>+		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);
> >>>>>>>>+
> >>>>>>>>+	return ret;
> >>>>>>>>+}
> >>>>>>>>+#else
> >>>>>>>>+static bool efi_image_authenticate(void *efi, size_t len)
> >>>>>>>>+{
> >>>>>>>>+	return true;
> >>>>>>>>+}
> >>>>>>>>+#endif /* CONFIG_EFI_SECURE_BOOT */
> >>>>>>>>+
> >>>>>>>>  /**
> >>>>>>>>   * efi_load_pe() - relocate EFI binary
> >>>>>>>>   *
> >>>>>>>>@@ -216,7 +579,8 @@ static void efi_set_code_and_data_type(
> >>>>>>>>   * @loaded_image_info:	loaded image protocol
> >>>>>>>>   * Return:		status code
> >>>>>>>>   */
> >>>>>>>>-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >>>>>>>>+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
> >>>>>>>>+			 void *efi, size_t efi_size,
> >>>>>>>>  			 struct efi_loaded_image *loaded_image_info)
> >>>>>>>>  {
> >>>>>>>>  	IMAGE_NT_HEADERS32 *nt;
> >>>>>>>>@@ -231,17 +595,57 @@ 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;
> >>>>>>>>+	void *new_efi = NULL;
> >>>>>>>>+	size_t new_efi_size;
> >>>>>>>>+	efi_status_t ret;
> >>>>>>>>+
> >>>>>>>>+	/*
> >>>>>>>>+	 * 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 EFI_OUT_OF_RESOURCES;
> >>>>>>>>+		memcpy(new_efi, efi, efi_size);
> >>>>>>>>+		efi = new_efi;
> >>>>>>>>+		efi_size = new_efi_size;
> >>>>>>>>+	}
> >>>>>>>>+
> >>>>>>>>+	/* 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++)
> >>>>>>>>@@ -253,14 +657,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)) {
> >>>>>>>>+		ret = EFI_ACCESS_DENIED;
> >>>>>>>
> >>>>>>>According to the UEFI specification LoadImage() should return
> >>>>>>>EFI_SECURITY_VIOLATION in this case.
> >>>>>>
> >>>>>>Will check.
> >>>>>>
> >>>>>>>Further behaviour should depend on variables AuditMode and DeployedMode.
> >>>>>>>
> >>>>>>>If authentication fails, you must update the configuration table
> >>>>>>>identified by EFI_IMAGE_SECURITY_DATABASE_GUID containing the Image
> >>>>>>>Execution Information. If the authentication succeeds you may enter a
> >>>>>>>record.
> >>>>>>>
> >>>>>>>It seems to me that in your patch series you are not creating the
> >>>>>>>configuration table at all.
> >>>>>>
> >>>>>>>From the very beginning of my submissions, I have clearly said
> >>>>>>that this feature was *beyond the scope* in my current series.
> >>>>>>===8<===
> >>>>>>Unsupported features: (marked as TODO in most cases in the source code,
> >>>>>>                         and won't be included in this series)
> >>>>>>(snip)
> >>>>>>* recording rejected images in EFI_IMAGE_EXECUTION_INFO_TABLE
> >>>>>>===>8===
> >>>>>>
> >>>>>>>The content of the Image Execution Information Table should be used to
> >>>>>>>decide if StartImage() may start an image. This is still missing in the
> >>>>>>>patch series.
> >>>>>>
> >>>>>>No.
> >>>>>>Whether such information be in configuration table or not,
> >>>>>>non-authenticated image won't be started if secure boot is in force.
> >>>>>
> >>>>>I cannot find any such check in efi_start_image().
> >>>>
> >>>>Because my current code *rejects* any unauthenticated images being loaded
> >>>>and return no valid pointer to image handle.
> >>>>If an error code in this case is changed and efi_load_image() still returns
> >>>>a valid handle, we will also have to modify efi_start_image().
> >>>>
> >>>>>Please, provide an implementation that complies with the UEFI specification:
> >>>>>
> >>>>>"The information is used to create the Image Execution Information
> >>>>>Table, which is added to the EFI System Configuration Table and assigned
> >>>>>the GUID EFI_IMAGE_SECURITY_DATABASE_GUID."
> >>>>
> >>>>Regarding "Image Execution Information Table", Heavily disagree.
> >>>>The current UEFI code, not only mine but yours, has bunch of unimplemented
> >>>>features.
> >>>
> >>>What is the point of this patch if efi_start_image() does not provide
> >>>any check?
> >>
> >>As I said in the previous reply,
> >>efi_load_image() doesn't return any valid handle for unauthenticated binaries
> >>in my *current* implementation, efi_start_image() has no chance to execute them.
> >>No check is necessary. That's it.
> >
> >I double-checked edk2 code as well as UEFI specification, and found
> >a couple of insights:
> >
> >a. EDK2 code has several internal help functions for verifications.
> >    If they fail to find any valid signature in db's, the status (or
> >    internal error code) is set to ACCESS_DENIED.
> >    Then, at the end of verification, if the status is not EFI_SUCCESS,
> >    the return code is anyhow rewritten to SECURITY_VIOLATION.
> >    (That is why my code returns ACCESS_DENIED right now.)
> >b. While UEFI specification requires that efiLoadImage() return
> >    SECURITY_VIOLATION if "the image signature is not valid,"
> >    it doesn't mention if a handle to the image be returned or not.
> 
> Please, have look at the return values for LoadImage() in the UEFI 2.8 spec.
> 
> If you return ACCESS_DENIED, <cite>Image was not loaded because the
> platform policy prohibits the image from being loaded. NULL is returned
> in *ImageHandle</cite>
> 
> If you return SECURITY_VIOLATION, <cite>Image was loaded and an
> ImageHandle was created with a valid EFI_LOADED_IMAGE_PROTOCOL.</cite>
> 
> In the case of SECURITY_VIOLATION we have to ensure that
> efi_start_image() does not start the image.

Okay.

> This is why we need the
> EFI_IMAGE_SECURITY_DATABASE.

No. Disagree.
Whether we support Image Execution Information Table or not,
we should and can prevent the image from being executed.
It's just a matter of implementation of efi_start_image().

The information is nothing but for UEFI applications/drivers
like other tables in configuration table.

-Takahiro Akashi


> >c. "Status Codes Returned" can also read that it depends on
> >    "platform policy" if we return ACCESS_DENIED or SECURITY_VIOLATION.
> >    But the policy may be vendor/platform, or even U-Boot specific as
> >    UEFI specification doesn't mention anything about that.
> >
> >Thinking of the fact that we don't have any consensus nor implementation
> >of "policy" yet, I believe that the best solution for now is:
> >    efi_load_image() return EFI_SECURITY_VIOLATION if the image signature
> >    is not verified and does *not* return a handle to image.
> 
> This would violate the UEFI spec.
> 
> Best regards
> 
> Heinrich
> 
> >
> >This behavior is safe and yet won't prevent us from additionally implementing
> >"policy" framework as well as image information table when adding
> >Audit/DeployedMode support in the future.
> >Can you agree?
> >
> >-Takahiro Akashi
> >
> >
> >>-Takahiro Akashi
> >>
> >>>Best regards
> >>>
> >>>Heinrich
> >>>
> >>>>
> >>>>-Takahiro Akashi
> >>>>
> >>>>
> >>>>>Best regards
> >>>>>
> >>>>>Heinrich
> >>>>>
> >>>>>>
> >>>>>>Thanks,
> >>>>>>-Takahiro Akashi
> >>>>>>
> >>>>>>>
> >>>>>>>Best regards
> >>>>>>>
> >>>>>>>Heinrich
> >>>>>>>
> >>>>>>>>+		goto err;
> >>>>>>>>+	}
> >>>>>>>>+
> >>>>>>>>+	/* 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,
> >>>>>>>>@@ -279,7 +698,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;
> >>>>>>>>@@ -295,7 +715,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;
> >>>>>>>>@@ -304,13 +725,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--) {
> >>>>>>>>@@ -327,7 +751,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,4 +765,9 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
> >>>>>>>>  	loaded_image_info->image_size = virt_size;
> >>>>>>>>
> >>>>>>>>  	return EFI_SUCCESS;
> >>>>>>>>+
> >>>>>>>>+err:
> >>>>>>>>+	free(new_efi);
> >>>>>>>>+
> >>>>>>>>+	return ret;
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>
> >>>>>>
> >>>>>
> >>>>
> >>>
> >
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2020-01-27  6:52 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-18  0:44 [PATCH v4 00/16] efi_loader: add secure boot support AKASHI Takahiro
2019-12-18  0:44 ` [PATCH v4 01/16] include: pe.h: add signature-related definitions AKASHI Takahiro
2019-12-18  0:44 ` [PATCH v4 02/16] efi_loader: add CONFIG_EFI_SECURE_BOOT config option AKASHI Takahiro
2019-12-18  0:44 ` [PATCH v4 03/16] efi_loader: add signature verification functions AKASHI Takahiro
2020-01-14 23:43   ` Heinrich Schuchardt
2020-01-15  0:13     ` Heinrich Schuchardt
2020-01-17  2:20       ` AKASHI Takahiro
2020-01-17  5:37         ` Heinrich Schuchardt
2020-01-21  6:00           ` AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 04/16] efi_loader: add signature database parser AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 05/16] efi_loader: variable: support variable authentication AKASHI Takahiro
2020-01-08 22:54   ` Heinrich Schuchardt
2020-01-17  5:35     ` AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 06/16] efi_loader: variable: add secure boot state transition AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 07/16] efi_loader: variable: add VendorKeys variable AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 08/16] efi_loader: image_loader: support image authentication AKASHI Takahiro
2020-01-08 23:55   ` Heinrich Schuchardt
2020-01-17  5:11     ` AKASHI Takahiro
2020-01-17  5:51       ` Heinrich Schuchardt
2020-01-21  6:12         ` AKASHI Takahiro
2020-01-21  7:15           ` Heinrich Schuchardt
2020-01-22  1:13             ` AKASHI Takahiro
2020-01-22  7:42               ` AKASHI Takahiro
2020-01-23 17:41                 ` Heinrich Schuchardt
2020-01-27  6:52                   ` AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 09/16] efi_loader: set up secure boot AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 10/16] cmd: env: use appropriate guid for authenticated UEFI variable AKASHI Takahiro
2020-01-21  7:13   ` Heinrich Schuchardt
2020-01-22  1:01     ` AKASHI Takahiro
2020-01-22  6:38       ` Heinrich Schuchardt
2020-01-22  7:15         ` AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 11/16] cmd: env: add "-at" option to "env set -e" command AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 12/16] efi_loader, pytest: set up secure boot environment AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 13/16] efi_loader, pytest: add UEFI secure boot tests (authenticated variables) AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 14/16] efi_loader, pytest: add UEFI secure boot tests (image) AKASHI Takahiro
2019-12-18  0:45 ` [PATCH v4 15/16] sandbox: add extra configurations for UEFI and related tests AKASHI Takahiro
2019-12-28  2:26   ` Simon Glass
2019-12-18  0:45 ` [PATCH v4 16/16] travis: add packages for UEFI secure boot test AKASHI Takahiro
2020-01-08 23:11 ` [PATCH v4 00/16] efi_loader: add secure boot support Heinrich Schuchardt
2020-01-09  0:08 ` Heinrich Schuchardt
2020-01-09  8:02   ` Ilias Apalodimas
2020-01-09 19:09     ` Heinrich Schuchardt
2020-01-09 20:03       ` Ilias Apalodimas
2020-01-17  5:59         ` AKASHI Takahiro
2020-01-17  6:39           ` Ilias Apalodimas

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.