aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-04-16 16:41:40 -0400
committerTom Rini <trini@konsulko.com>2020-04-16 16:41:40 -0400
commitcf87f7cd8cdb35761103720a102df9bf5b88c1b2 (patch)
tree68dca8e34ab5855a3655d057be09150739b09604 /lib
parentf51b4bcf61c9aa7994138a4a417488c1fbdb47cd (diff)
parentb2ace8753d0048487ab6e8955ae9067a6af91559 (diff)
downloadu-boot-cf87f7cd8cdb35761103720a102df9bf5b88c1b2.zip
u-boot-cf87f7cd8cdb35761103720a102df9bf5b88c1b2.tar.gz
u-boot-cf87f7cd8cdb35761103720a102df9bf5b88c1b2.tar.bz2
Merge tag 'efi-2020-07-rc1' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
Pull request for UEFI sub-system for efi-2020-07-rc1 This pull request * provides an implementation of UEFI secure booting * fixes a problem with the rsa_mod_exp driver which stops some boards from booting when CONFIG_RSA is enabled which is needed for UEFI secure booting * enables the EFI_RNG_PROTOCOL if DM_RNG is enabled * fixes some function comments
Diffstat (limited to 'lib')
-rw-r--r--lib/efi_loader/Kconfig19
-rw-r--r--lib/efi_loader/Makefile1
-rw-r--r--lib/efi_loader/efi_boottime.c10
-rw-r--r--lib/efi_loader/efi_disk.c52
-rw-r--r--lib/efi_loader/efi_image_loader.c462
-rw-r--r--lib/efi_loader/efi_setup.c38
-rw-r--r--lib/efi_loader/efi_signature.c804
-rw-r--r--lib/efi_loader/efi_unicode_collation.c6
-rw-r--r--lib/efi_loader/efi_variable.c952
-rw-r--r--lib/efi_loader/efi_watchdog.c18
10 files changed, 2227 insertions, 135 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 9890144..1cfa24f 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -126,6 +126,7 @@ config EFI_GRUB_ARM32_WORKAROUND
config EFI_RNG_PROTOCOL
bool "EFI_RNG_PROTOCOL support"
depends on DM_RNG
+ default y
help
Provide a EFI_RNG_PROTOCOL implementation using the hardware random
number generator of the platform.
@@ -145,4 +146,22 @@ config EFI_INITRD_FILESPEC
help
Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz.
+config EFI_SECURE_BOOT
+ bool "Enable EFI secure boot support"
+ depends on EFI_LOADER
+ select SHA256
+ select RSA
+ select RSA_VERIFY_WITH_PKEY
+ select IMAGE_SIGN_INFO
+ select ASYMMETRIC_KEY_TYPE
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select X509_CERTIFICATE_PARSER
+ select PKCS7_MESSAGE_PARSER
+ default n
+ help
+ Select this option to enable EFI secure boot support.
+ Once SecureBoot mode is enforced, any EFI binary can run only if
+ it is signed with a trusted key. To do that, you need to install,
+ at least, PK, KEK and db.
+
endif
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 9b3b704..eff3c25 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -44,3 +44,4 @@ obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o
obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
+obj-y += efi_signature.o
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 3b79a88..a3f11ea 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1882,12 +1882,12 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy,
efi_dp_split_file_path(file_path, &dp, &fp);
ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
if (ret == EFI_SUCCESS)
- ret = efi_load_pe(*image_obj, dest_buffer, info);
+ ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
if (!source_buffer)
/* Release buffer to which file was loaded */
efi_free_pages((uintptr_t)dest_buffer,
efi_size_in_pages(source_size));
- if (ret == EFI_SUCCESS) {
+ if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) {
info->system_table = &systab;
info->parent_handle = parent_image;
} else {
@@ -2885,10 +2885,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+ if (!efi_search_obj(image_handle))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
/* Check parameters */
if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE)
return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED)
+ return EFI_EXIT(EFI_SECURITY_VIOLATION);
+
ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image,
&info, NULL, NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL));
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index fc0682b..fd8fe17 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -108,6 +108,21 @@ static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this,
return EFI_SUCCESS;
}
+/**
+ * efi_disk_read_blocks() - reads blocks from device
+ *
+ * This function implements the ReadBlocks service of the EFI_BLOCK_IO_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be read from
+ * @lba: starting logical block for reading
+ * @buffer_size: size of the read buffer
+ * @buffer: pointer to the destination buffer
+ * Return: status code
+ */
static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
u32 media_id, u64 lba, efi_uintn_t buffer_size,
void *buffer)
@@ -157,6 +172,22 @@ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this,
return EFI_EXIT(r);
}
+/**
+ * efi_disk_write_blocks() - writes blocks to device
+ *
+ * This function implements the WriteBlocks service of the
+ * EFI_BLOCK_IO_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be written to
+ * @lba: starting logical block for writing
+ * @buffer_size: size of the write buffer
+ * @buffer: pointer to the source buffer
+ * Return: status code
+ */
static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
u32 media_id, u64 lba, efi_uintn_t buffer_size,
void *buffer)
@@ -208,9 +239,22 @@ static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this,
return EFI_EXIT(r);
}
+/**
+ * efi_disk_flush_blocks() - flushes modified data to the device
+ *
+ * This function implements the FlushBlocks service of the
+ * EFI_BLOCK_IO_PROTOCOL.
+ *
+ * As we always write synchronously nothing is done here.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * Return: status code
+ */
static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this)
{
- /* We always write synchronously */
EFI_ENTRY("%p", this);
return EFI_EXIT(EFI_SUCCESS);
}
@@ -286,7 +330,7 @@ static int efi_fs_exists(struct blk_desc *desc, int part)
return 1;
}
-/*
+/**
* efi_disk_add_dev() - create a handle for a partition or disk
*
* @parent: parent handle
@@ -295,6 +339,8 @@ static int efi_fs_exists(struct blk_desc *desc, int part)
* @desc: internal block device
* @dev_index: device index for block device
* @offset: offset into disk for simple partitions
+ * @part: partition
+ * @disk: pointer to receive the created handle
* Return: disk object
*/
static efi_status_t efi_disk_add_dev(
@@ -381,7 +427,7 @@ static efi_status_t efi_disk_add_dev(
* Create handles and protocols for the partitions of a block device.
*
* @parent: handle of the parent disk
- * @blk_desc: block device
+ * @desc: block device
* @if_typename: interface type
* @diskid: device number
* @pdevname: device name
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index d5de6df..6c270ce 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -10,7 +10,10 @@
#include <common.h>
#include <cpu_func.h>
#include <efi_loader.h>
+#include <malloc.h>
#include <pe.h>
+#include <sort.h>
+#include "../lib/crypto/pkcs7_parser.h"
const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
@@ -206,6 +209,386 @@ static void efi_set_code_and_data_type(
}
}
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * cmp_pe_section - compare two sections
+ * @arg1: Pointer to pointer to first section
+ * @arg2: Pointer to pointer to second section
+ *
+ * Compare two sections in PE image.
+ *
+ * Return: -1, 0, 1 respectively if arg1 < arg2, arg1 == arg2 or
+ * arg1 > arg2
+ */
+static int cmp_pe_section(const void *arg1, const void *arg2)
+{
+ const IMAGE_SECTION_HEADER *section1, *section2;
+
+ section1 = *((const IMAGE_SECTION_HEADER **)arg1);
+ section2 = *((const IMAGE_SECTION_HEADER **)arg2);
+
+ if (section1->VirtualAddress < section2->VirtualAddress)
+ return -1;
+ else if (section1->VirtualAddress == section2->VirtualAddress)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * efi_image_parse - parse a PE image
+ * @efi: Pointer to image
+ * @len: Size of @efi
+ * @regp: Pointer to a list of regions
+ * @auth: Pointer to a pointer to authentication data in PE
+ * @auth_len: Size of @auth
+ *
+ * Parse image binary in PE32(+) format, assuming that sanity of PE image
+ * has been checked by a caller.
+ * On success, an address of authentication data in @efi and its size will
+ * be returned in @auth and @auth_len, respectively.
+ *
+ * Return: true on success, false on error
+ */
+bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
+ WIN_CERTIFICATE **auth, size_t *auth_len)
+{
+ struct efi_image_regions *regs;
+ IMAGE_DOS_HEADER *dos;
+ IMAGE_NT_HEADERS32 *nt;
+ IMAGE_SECTION_HEADER *sections, **sorted;
+ int num_regions, num_sections, i;
+ int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY;
+ u32 align, size, authsz, authoff;
+ size_t bytes_hashed;
+
+ dos = (void *)efi;
+ nt = (void *)(efi + dos->e_lfanew);
+
+ /*
+ * Count maximum number of regions to be digested.
+ * We don't have to have an exact number here.
+ * See efi_image_region_add()'s in parsing below.
+ */
+ num_regions = 3; /* for header */
+ num_regions += nt->FileHeader.NumberOfSections;
+ num_regions++; /* for extra */
+
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions,
+ 1);
+ if (!regs)
+ goto err;
+ regs->max = num_regions;
+
+ /*
+ * Collect data regions for hash calculation
+ * 1. File headers
+ */
+ if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+ IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader;
+
+ /* Skip CheckSum */
+ efi_image_region_add(regs, efi, &opt->CheckSum, 0);
+ if (nt64->OptionalHeader.NumberOfRvaAndSizes <= ctidx) {
+ efi_image_region_add(regs,
+ &opt->CheckSum + 1,
+ efi + opt->SizeOfHeaders, 0);
+ } else {
+ /* Skip Certificates Table */
+ efi_image_region_add(regs,
+ &opt->CheckSum + 1,
+ &opt->DataDirectory[ctidx], 0);
+ efi_image_region_add(regs,
+ &opt->DataDirectory[ctidx] + 1,
+ efi + opt->SizeOfHeaders, 0);
+ }
+
+ bytes_hashed = opt->SizeOfHeaders;
+ align = opt->FileAlignment;
+ authoff = opt->DataDirectory[ctidx].VirtualAddress;
+ authsz = opt->DataDirectory[ctidx].Size;
+ } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
+
+ efi_image_region_add(regs, efi, &opt->CheckSum, 0);
+ efi_image_region_add(regs, &opt->CheckSum + 1,
+ &opt->DataDirectory[ctidx], 0);
+ efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1,
+ efi + opt->SizeOfHeaders, 0);
+
+ bytes_hashed = opt->SizeOfHeaders;
+ align = opt->FileAlignment;
+ authoff = opt->DataDirectory[ctidx].VirtualAddress;
+ authsz = opt->DataDirectory[ctidx].Size;
+ } else {
+ debug("%s: Invalid optional header magic %x\n", __func__,
+ nt->OptionalHeader.Magic);
+ goto err;
+ }
+
+ /* 2. Sections */
+ num_sections = nt->FileHeader.NumberOfSections;
+ sections = (void *)((uint8_t *)&nt->OptionalHeader +
+ nt->FileHeader.SizeOfOptionalHeader);
+ sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections);
+ if (!sorted) {
+ debug("%s: Out of memory\n", __func__);
+ goto err;
+ }
+
+ /*
+ * Make sure the section list is in ascending order.
+ */
+ for (i = 0; i < num_sections; i++)
+ sorted[i] = &sections[i];
+ qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section);
+
+ for (i = 0; i < num_sections; i++) {
+ if (!sorted[i]->SizeOfRawData)
+ continue;
+
+ size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1);
+ efi_image_region_add(regs, efi + sorted[i]->PointerToRawData,
+ efi + sorted[i]->PointerToRawData + size,
+ 0);
+ debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n",
+ i, sorted[i]->Name,
+ sorted[i]->PointerToRawData,
+ sorted[i]->PointerToRawData + size,
+ sorted[i]->VirtualAddress,
+ sorted[i]->VirtualAddress
+ + sorted[i]->Misc.VirtualSize);
+
+ bytes_hashed += size;
+ }
+ free(sorted);
+
+ /* 3. Extra data excluding Certificates Table */
+ if (bytes_hashed + authsz < len) {
+ debug("extra data for hash: %lu\n",
+ len - (bytes_hashed + authsz));
+ efi_image_region_add(regs, efi + bytes_hashed,
+ efi + len - authsz, 0);
+ }
+
+ /* Return Certificates Table */
+ if (authsz) {
+ if (len < authoff + authsz) {
+ debug("%s: Size for auth too large: %u >= %zu\n",
+ __func__, authsz, len - authoff);
+ goto err;
+ }
+ if (authsz < sizeof(*auth)) {
+ debug("%s: Size for auth too small: %u < %zu\n",
+ __func__, authsz, sizeof(*auth));
+ goto err;
+ }
+ *auth = efi + authoff;
+ *auth_len = authsz;
+ debug("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff, authsz);
+ } else {
+ *auth = NULL;
+ *auth_len = 0;
+ }
+
+ *regp = regs;
+
+ return true;
+
+err:
+ free(regs);
+
+ return false;
+}
+
+/**
+ * efi_image_unsigned_authenticate - authenticate unsigned image with
+ * SHA256 hash
+ * @regs: List of regions to be verified
+ *
+ * If an image is not signed, it doesn't have a signature. In this case,
+ * its message digest is calculated and it will be compared with one of
+ * hash values stored in signature databases.
+ *
+ * Return: true if authenticated, false if not
+ */
+static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
+{
+ struct efi_signature_store *db = NULL, *dbx = NULL;
+ bool ret = false;
+
+ dbx = efi_sigstore_parse_sigdb(L"dbx");
+ if (!dbx) {
+ debug("Getting signature database(dbx) failed\n");
+ goto out;
+ }
+
+ db = efi_sigstore_parse_sigdb(L"db");
+ if (!db) {
+ debug("Getting signature database(db) failed\n");
+ goto out;
+ }
+
+ /* try black-list first */
+ if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) {
+ debug("Image is not signed and rejected by \"dbx\"\n");
+ goto out;
+ }
+
+ /* try white-list */
+ if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL))
+ ret = true;
+ else
+ debug("Image is not signed and not found in \"db\" or \"dbx\"\n");
+
+out:
+ efi_sigstore_free(db);
+ efi_sigstore_free(dbx);
+
+ return ret;
+}
+
+/**
+ * efi_image_authenticate - verify a signature of signed image
+ * @efi: Pointer to image
+ * @efi_size: Size of @efi
+ *
+ * A signed image should have its signature stored in a table of its PE header.
+ * So if an image is signed and only if if its signature is verified using
+ * signature databases, an image is authenticated.
+ * If an image is not signed, its validity is checked by using
+ * efi_image_unsigned_authenticated().
+ * TODO:
+ * When AuditMode==0, if the image's signature is not found in
+ * the authorized database, or is found in the forbidden database,
+ * the image will not be started and instead, information about it
+ * will be placed in this table.
+ * When AuditMode==1, an EFI_IMAGE_EXECUTION_INFO element is created
+ * in the EFI_IMAGE_EXECUTION_INFO_TABLE for every certificate found
+ * in the certificate table of every image that is validated.
+ *
+ * Return: true if authenticated, false if not
+ */
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ struct efi_image_regions *regs = NULL;
+ WIN_CERTIFICATE *wincerts = NULL, *wincert;
+ size_t wincerts_len;
+ struct pkcs7_message *msg = NULL;
+ struct efi_signature_store *db = NULL, *dbx = NULL;
+ struct x509_certificate *cert = NULL;
+ void *new_efi = NULL;
+ size_t new_efi_size;
+ bool ret = false;
+
+ if (!efi_secure_boot_enabled())
+ return true;
+
+ /*
+ * Size must be 8-byte aligned and the trailing bytes must be
+ * zero'ed. Otherwise hash value may be incorrect.
+ */
+ if (efi_size & 0x7) {
+ new_efi_size = (efi_size + 0x7) & ~0x7ULL;
+ new_efi = calloc(new_efi_size, 1);
+ if (!new_efi)
+ return false;
+ memcpy(new_efi, efi, efi_size);
+ efi = new_efi;
+ efi_size = new_efi_size;
+ }
+
+ if (!efi_image_parse(efi, efi_size, &regs, &wincerts,
+ &wincerts_len)) {
+ debug("Parsing PE executable image failed\n");
+ goto err;
+ }
+
+ if (!wincerts) {
+ /* The image is not signed */
+ ret = efi_image_unsigned_authenticate(regs);
+
+ goto err;
+ }
+
+ /*
+ * verify signature using db and dbx
+ */
+ db = efi_sigstore_parse_sigdb(L"db");
+ if (!db) {
+ debug("Getting signature database(db) failed\n");
+ goto err;
+ }
+
+ dbx = efi_sigstore_parse_sigdb(L"dbx");
+ if (!dbx) {
+ debug("Getting signature database(dbx) failed\n");
+ goto err;
+ }
+
+ /* go through WIN_CERTIFICATE list */
+ for (wincert = wincerts;
+ (void *)wincert < (void *)wincerts + wincerts_len;
+ wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) {
+ if (wincert->dwLength < sizeof(*wincert)) {
+ debug("%s: dwLength too small: %u < %zu\n",
+ __func__, wincert->dwLength, sizeof(*wincert));
+ goto err;
+ }
+ msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert),
+ wincert->dwLength - sizeof(*wincert));
+ if (!msg) {
+ debug("Parsing image's signature failed\n");
+ goto err;
+ }
+
+ /* try black-list first */
+ if (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) {
+ debug("Signature was rejected by \"dbx\"\n");
+ goto err;
+ }
+
+ if (!efi_signature_verify_signers(msg, dbx)) {
+ debug("Signer was rejected by \"dbx\"\n");
+ goto err;
+ } else {
+ ret = true;
+ }
+
+ /* try white-list */
+ if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) {
+ debug("Verifying signature with \"db\" failed\n");
+ goto err;
+ } else {
+ ret = true;
+ }
+
+ if (!efi_signature_verify_cert(cert, dbx)) {
+ debug("Certificate was rejected by \"dbx\"\n");
+ goto err;
+ } else {
+ ret = true;
+ }
+ }
+
+err:
+ x509_free_certificate(cert);
+ efi_sigstore_free(db);
+ efi_sigstore_free(dbx);
+ pkcs7_free_message(msg);
+ free(regs);
+ free(new_efi);
+
+ return ret;
+}
+#else
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ return true;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
/**
* efi_load_pe() - relocate EFI binary
*
@@ -214,10 +597,12 @@ static void efi_set_code_and_data_type(
*
* @handle: loaded image handle
* @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
* @loaded_image_info: loaded image protocol
* Return: status code
*/
-efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
+ void *efi, size_t efi_size,
struct efi_loaded_image *loaded_image_info)
{
IMAGE_NT_HEADERS32 *nt;
@@ -232,17 +617,41 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
uint64_t image_base;
unsigned long virt_size = 0;
int supported = 0;
+ efi_status_t ret;
+
+ /* Sanity check for a file header */
+ if (efi_size < sizeof(*dos)) {
+ printf("%s: Truncated DOS Header\n", __func__);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
dos = efi;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
printf("%s: Invalid DOS Signature\n", __func__);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* assume sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64) */
+ if (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) {
+ printf("%s: Invalid offset for Extended Header\n", __func__);
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
nt = (void *) ((char *)efi + dos->e_lfanew);
+ if ((nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) &&
+ (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64))) {
+ printf("%s: Invalid offset for Extended Header\n", __func__);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
if (nt->Signature != IMAGE_NT_SIGNATURE) {
printf("%s: Invalid NT Signature\n", __func__);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
for (i = 0; machines[i]; i++)
@@ -254,14 +663,29 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
if (!supported) {
printf("%s: Machine type 0x%04x is not supported\n",
__func__, nt->FileHeader.Machine);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
- /* Calculate upper virtual address boundary */
num_sections = nt->FileHeader.NumberOfSections;
sections = (void *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader;
+ if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections
+ - efi)) {
+ printf("%s: Invalid number of sections: %d\n",
+ __func__, num_sections);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* Authenticate an image */
+ if (efi_image_authenticate(efi, efi_size))
+ handle->auth_status = EFI_IMAGE_AUTH_PASSED;
+ else
+ handle->auth_status = EFI_IMAGE_AUTH_FAILED;
+
+ /* Calculate upper virtual address boundary */
for (i = num_sections - 1; i >= 0; i--) {
IMAGE_SECTION_HEADER *sec = &sections[i];
virt_size = max_t(unsigned long, virt_size,
@@ -280,7 +704,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
if (!efi_reloc) {
printf("%s: Could not allocate %lu bytes\n",
__func__, virt_size);
- return EFI_OUT_OF_RESOURCES;
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
}
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
@@ -296,7 +721,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
if (!efi_reloc) {
printf("%s: Could not allocate %lu bytes\n",
__func__, virt_size);
- return EFI_OUT_OF_RESOURCES;
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
}
handle->entry = efi_reloc + opt->AddressOfEntryPoint;
rel_size = opt->DataDirectory[rel_idx].Size;
@@ -305,13 +731,16 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
} else {
printf("%s: Invalid optional header magic %x\n", __func__,
nt->OptionalHeader.Magic);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
/* Copy PE headers */
- memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt)
- + nt->FileHeader.SizeOfOptionalHeader
- + num_sections * sizeof(IMAGE_SECTION_HEADER));
+ memcpy(efi_reloc, efi,
+ sizeof(*dos)
+ + sizeof(*nt)
+ + nt->FileHeader.SizeOfOptionalHeader
+ + num_sections * sizeof(IMAGE_SECTION_HEADER));
/* Load sections into RAM */
for (i = num_sections - 1; i >= 0; i--) {
@@ -328,7 +757,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
(unsigned long)image_base) != EFI_SUCCESS) {
efi_free_pages((uintptr_t) efi_reloc,
(virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
- return EFI_LOAD_ERROR;
+ ret = EFI_LOAD_ERROR;
+ goto err;
}
/* Flush cache */
@@ -340,5 +770,11 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
loaded_image_info->image_base = efi_reloc;
loaded_image_info->image_size = virt_size;
- return EFI_SUCCESS;
+ if (handle->auth_status == EFI_IMAGE_AUTH_PASSED)
+ return EFI_SUCCESS;
+ else
+ return EFI_SECURITY_VIOLATION;
+
+err:
+ return ret;
}
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index b458093..1b648c8 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -82,6 +82,39 @@ out:
return ret;
}
+#ifdef CONFIG_EFI_SECURE_BOOT
+/**
+ * efi_init_secure_boot - initialize secure boot state
+ *
+ * Return: EFI_SUCCESS on success, status code (negative) on error
+ */
+static efi_status_t efi_init_secure_boot(void)
+{
+ efi_guid_t signature_types[] = {
+ EFI_CERT_SHA256_GUID,
+ EFI_CERT_X509_GUID,
+ };
+ efi_status_t ret;
+
+ /* TODO: read-only */
+ ret = EFI_CALL(efi_set_variable(L"SignatureSupport",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(signature_types),
+ &signature_types));
+ if (ret != EFI_SUCCESS)
+ printf("EFI: cannot initialize SignatureSupport variable\n");
+
+ return ret;
+}
+#else
+static efi_status_t efi_init_secure_boot(void)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
/**
* efi_init_obj_list() - Initialize and populate EFI object list
*
@@ -127,6 +160,11 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS)
goto out;
+ /* Secure boot */
+ ret = efi_init_secure_boot();
+ if (ret != EFI_SUCCESS)
+ goto out;
+
/* Indicate supported runtime services */
ret = efi_init_runtime_supported();
if (ret != EFI_SUCCESS)
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
new file mode 100644
index 0000000..658e354
--- /dev/null
+++ b/lib/efi_loader/efi_signature.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
+ * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <linux/compat.h>
+#include <linux/oid_registry.h>
+#include <u-boot/rsa.h>
+#include <u-boot/sha256.h>
+#include "../lib/crypto/pkcs7_parser.h"
+
+const efi_guid_t efi_guid_image_security_database =
+ EFI_IMAGE_SECURITY_DATABASE_GUID;
+const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
+const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
+const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
+const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+
+/**
+ * efi_hash_regions - calculate a hash value
+ * @regs: List of regions
+ * @hash: Pointer to a pointer to buffer holding a hash value
+ * @size: Size of buffer to be returned
+ *
+ * Calculate a sha256 value of @regs and return a value in @hash.
+ *
+ * Return: true on success, false on error
+ */
+static bool efi_hash_regions(struct efi_image_regions *regs, void **hash,
+ size_t *size)
+{
+ *size = 0;
+ *hash = calloc(1, SHA256_SUM_LEN);
+ if (!*hash) {
+ debug("Out of memory\n");
+ return false;
+ }
+ *size = SHA256_SUM_LEN;
+
+ hash_calculate("sha256", regs->reg, regs->num, *hash);
+#ifdef DEBUG
+ debug("hash calculated:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ *hash, SHA256_SUM_LEN, false);
+#endif
+
+ return true;
+}
+
+/**
+ * efi_hash_msg_content - calculate a hash value of contentInfo
+ * @msg: Signature
+ * @hash: Pointer to a pointer to buffer holding a hash value
+ * @size: Size of buffer to be returned
+ *
+ * Calculate a sha256 value of contentInfo in @msg and return a value in @hash.
+ *
+ * Return: true on success, false on error
+ */
+static bool efi_hash_msg_content(struct pkcs7_message *msg, void **hash,
+ size_t *size)
+{
+ struct image_region regtmp;
+
+ *size = 0;
+ *hash = calloc(1, SHA256_SUM_LEN);
+ if (!*hash) {
+ debug("Out of memory\n");
+ free(msg);
+ return false;
+ }
+ *size = SHA256_SUM_LEN;
+
+ regtmp.data = msg->data;
+ regtmp.size = msg->data_len;
+
+ hash_calculate("sha256", &regtmp, 1, *hash);
+#ifdef DEBUG
+ debug("hash calculated based on contentInfo:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ *hash, SHA256_SUM_LEN, false);
+#endif
+
+ return true;
+}
+
+/**
+ * efi_signature_verify - verify a signature with a certificate
+ * @regs: List of regions to be authenticated
+ * @signed_info: Pointer to PKCS7's signed_info
+ * @cert: x509 certificate
+ *
+ * Signature pointed to by @signed_info against image pointed to by @regs
+ * is verified by a certificate pointed to by @cert.
+ * @signed_info holds a signature, including a message digest which is to be
+ * compared with a hash value calculated from @regs.
+ *
+ * Return: true if signature is verified, false if not
+ */
+static bool efi_signature_verify(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct pkcs7_signed_info *ps_info,
+ struct x509_certificate *cert)
+{
+ struct image_sign_info info;
+ struct image_region regtmp[2];
+ void *hash;
+ size_t size;
+ char c;
+ bool verified;
+
+ debug("%s: Enter, %p, %p, %p(issuer: %s, subject: %s)\n", __func__,
+ regs, ps_info, cert, cert->issuer, cert->subject);
+
+ verified = false;
+
+ memset(&info, '\0', sizeof(info));
+ info.padding = image_get_padding_algo("pkcs-1.5");
+ /*
+ * Note: image_get_[checksum|crypto]_algo takes an string
+ * argument like "<checksum>,<crypto>"
+ * TODO: support other hash algorithms
+ */
+ if (!strcmp(ps_info->sig->hash_algo, "sha1")) {
+ info.checksum = image_get_checksum_algo("sha1,rsa2048");
+ info.name = "sha1,rsa2048";
+ } else if (!strcmp(ps_info->sig->hash_algo, "sha256")) {
+ info.checksum = image_get_checksum_algo("sha256,rsa2048");
+ info.name = "sha256,rsa2048";
+ } else {
+ debug("unknown msg digest algo: %s\n", ps_info->sig->hash_algo);
+ goto out;
+ }
+ info.crypto = image_get_crypto_algo(info.name);
+
+ info.key = cert->pub->key;
+ info.keylen = cert->pub->keylen;
+
+ /* verify signature */
+ debug("%s: crypto: %s, signature len:%x\n", __func__,
+ info.name, ps_info->sig->s_size);
+ if (ps_info->aa_set & (1UL << sinfo_has_message_digest)) {
+ debug("%s: RSA verify authentication attribute\n", __func__);
+ /*
+ * NOTE: This path will be executed only for
+ * PE image authentication
+ */
+
+ /* check if hash matches digest first */
+ debug("checking msg digest first, len:0x%x\n",
+ ps_info->msgdigest_len);
+
+#ifdef DEBUG
+ debug("hash in database:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ ps_info->msgdigest, ps_info->msgdigest_len,
+ false);
+#endif
+ /* against contentInfo first */
+ if ((msg->data && efi_hash_msg_content(msg, &hash, &size)) ||
+ /* for signed image */
+ efi_hash_regions(regs, &hash, &size)) {
+ /* for authenticated variable */
+ if (ps_info->msgdigest_len != size ||
+ memcmp(hash, ps_info->msgdigest, size)) {
+ debug("Digest doesn't match\n");
+ free(hash);
+ goto out;
+ }
+
+ free(hash);
+ } else {
+ debug("Digesting image failed\n");
+ goto out;
+ }
+
+ /* against digest */
+ c = 0x31;
+ regtmp[0].data = &c;
+ regtmp[0].size = 1;
+ regtmp[1].data = ps_info->authattrs;
+ regtmp[1].size = ps_info->authattrs_len;
+
+ if (!rsa_verify(&info, regtmp, 2,
+ ps_info->sig->s, ps_info->sig->s_size))
+ verified = true;
+ } else {
+ debug("%s: RSA verify content data\n", __func__);
+ /* against all data */
+ if (!rsa_verify(&info, regs->reg, regs->num,
+ ps_info->sig->s, ps_info->sig->s_size))
+ verified = true;
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_signature_verify_with_list - verify a signature with signature list
+ * @regs: List of regions to be authenticated
+ * @msg: Signature
+ * @signed_info: Pointer to PKCS7's signed_info
+ * @siglist: Signature list for certificates
+ * @valid_cert: x509 certificate that verifies this signature
+ *
+ * Signature pointed to by @signed_info against image pointed to by @regs
+ * is verified by signature list pointed to by @siglist.
+ * Signature database is a simple concatenation of one or more
+ * signature list(s).
+ *
+ * Return: true if signature is verified, false if not
+ */
+static
+bool efi_signature_verify_with_list(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct pkcs7_signed_info *signed_info,
+ struct efi_signature_store *siglist,
+ struct x509_certificate **valid_cert)
+{
+ struct x509_certificate *cert;
+ struct efi_sig_data *sig_data;
+ bool verified = false;
+
+ debug("%s: Enter, %p, %p, %p, %p\n", __func__,
+ regs, signed_info, siglist, valid_cert);
+
+ if (!signed_info) {
+ void *hash;
+ size_t size;
+
+ debug("%s: unsigned image\n", __func__);
+ /*
+ * verify based on calculated hash value
+ * TODO: support other hash algorithms
+ */
+ if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
+ debug("Digest algorithm is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ if (!efi_hash_regions(regs, &hash, &size)) {
+ debug("Digesting unsigned image failed\n");
+ goto out;
+ }
+
+ /* go through the list */
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+#ifdef DEBUG
+ debug("Msg digest in database:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ sig_data->data, sig_data->size, false);
+#endif
+ if ((sig_data->size == size) &&
+ !memcmp(sig_data->data, hash, size)) {
+ verified = true;
+ free(hash);
+ goto out;
+ }
+ }
+ free(hash);
+ goto out;
+ }
+
+ debug("%s: signed image\n", __func__);
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) {
+ debug("Signature type is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ /* go through the list */
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ /* TODO: support owner check based on policy */
+
+ cert = x509_cert_parse(sig_data->data, sig_data->size);
+ if (IS_ERR(cert)) {
+ debug("Parsing x509 certificate failed\n");
+ goto out;
+ }
+
+ verified = efi_signature_verify(regs, msg, signed_info, cert);
+
+ if (verified) {
+ if (valid_cert)
+ *valid_cert = cert;
+ else
+ x509_free_certificate(cert);
+ break;
+ }
+ x509_free_certificate(cert);
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_signature_verify_with_sigdb - verify a signature with db
+ * @regs: List of regions to be authenticated
+ * @msg: Signature
+ * @db: Signature database for trusted certificates
+ * @cert: x509 certificate that verifies this signature
+ *
+ * Signature pointed to by @msg against image pointed to by @regs
+ * is verified by signature database pointed to by @db.
+ *
+ * Return: true if signature is verified, false if not
+ */
+bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ struct efi_signature_store *db,
+ struct x509_certificate **cert)
+{
+ struct pkcs7_signed_info *info;
+ struct efi_signature_store *siglist;
+ bool verified = false;
+
+ debug("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, cert);
+
+ if (!db)
+ goto out;
+
+ if (!db->sig_data_list)
+ goto out;
+
+ /* for unsigned image */
+ if (!msg) {
+ debug("%s: Verify unsigned image with db\n", __func__);
+ for (siglist = db; siglist; siglist = siglist->next)
+ if (efi_signature_verify_with_list(regs, NULL, NULL,
+ siglist, cert)) {
+ verified = true;
+ goto out;
+ }
+
+ goto out;
+ }
+
+ /* for signed image or variable */
+ debug("%s: Verify signed image with db\n", __func__);
+ for (info = msg->signed_infos; info; info = info->next) {
+ debug("Signed Info: digest algo: %s, pkey algo: %s\n",
+ info->sig->hash_algo, info->sig->pkey_algo);
+
+ for (siglist = db; siglist; siglist = siglist->next) {
+ if (efi_signature_verify_with_list(regs, msg, info,
+ siglist, cert)) {
+ verified = true;
+ goto out;
+ }
+ }
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+/**
+ * efi_search_siglist - search signature list for a certificate
+ * @cert: x509 certificate
+ * @siglist: Signature list
+ * @revoc_time: Pointer to buffer for revocation time
+ *
+ * Search signature list pointed to by @siglist and find a certificate
+ * pointed to by @cert.
+ * If found, revocation time that is specified in signature database is
+ * returned in @revoc_time.
+ *
+ * Return: true if certificate is found, false if not
+ */
+static bool efi_search_siglist(struct x509_certificate *cert,
+ struct efi_signature_store *siglist,
+ time64_t *revoc_time)
+{
+ struct image_region reg[1];
+ void *hash = NULL, *msg = NULL;
+ struct efi_sig_data *sig_data;
+ bool found = false;
+
+ /* can be null */
+ if (!siglist->sig_data_list)
+ return false;
+
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) {
+ /* TODO: other hash algos */
+ debug("Certificate's digest type is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ /* calculate hash of TBSCertificate */
+ msg = calloc(1, SHA256_SUM_LEN);
+ if (!msg) {
+ debug("Out of memory\n");
+ goto out;
+ }
+
+ hash = calloc(1, SHA256_SUM_LEN);
+ if (!hash) {
+ debug("Out of memory\n");
+ goto out;
+ }
+
+ reg[0].data = cert->tbs;
+ reg[0].size = cert->tbs_size;
+ hash_calculate("sha256", reg, 1, msg);
+
+ /* go through signature list */
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ /*
+ * struct efi_cert_x509_sha256 {
+ * u8 tbs_hash[256/8];
+ * time64_t revocation_time;
+ * };
+ */
+ if ((sig_data->size == SHA256_SUM_LEN) &&
+ !memcmp(sig_data->data, hash, SHA256_SUM_LEN)) {
+ memcpy(revoc_time, sig_data->data + SHA256_SUM_LEN,
+ sizeof(*revoc_time));
+ found = true;
+ goto out;
+ }
+ }
+
+out:
+ free(hash);
+ free(msg);
+
+ return found;
+}
+
+/**
+ * efi_signature_verify_cert - verify a certificate with dbx
+ * @cert: x509 certificate
+ * @dbx: Signature database
+ *
+ * Search signature database pointed to by @dbx and find a certificate
+ * pointed to by @cert.
+ * This function is expected to be used against "dbx".
+ *
+ * Return: true if a certificate is not rejected, false otherwise.
+ */
+bool efi_signature_verify_cert(struct x509_certificate *cert,
+ struct efi_signature_store *dbx)
+{
+ struct efi_signature_store *siglist;
+ time64_t revoc_time;
+ bool found = false;
+
+ debug("%s: Enter, %p, %p\n", __func__, dbx, cert);
+
+ if (!cert)
+ return false;
+
+ for (siglist = dbx; siglist; siglist = siglist->next) {
+ if (efi_search_siglist(cert, siglist, &revoc_time)) {
+ /* TODO */
+ /* compare signing time with revocation time */
+
+ found = true;
+ break;
+ }
+ }
+
+ debug("%s: Exit, verified: %d\n", __func__, !found);
+ return !found;
+}
+
+/**
+ * efi_signature_verify_signers - verify signers' certificates with dbx
+ * @msg: Signature
+ * @dbx: Signature database
+ *
+ * Determine if any of signers' certificates in @msg may be verified
+ * by any of certificates in signature database pointed to by @dbx.
+ * This function is expected to be used against "dbx".
+ *
+ * Return: true if none of certificates is rejected, false otherwise.
+ */
+bool efi_signature_verify_signers(struct pkcs7_message *msg,
+ struct efi_signature_store *dbx)
+{
+ struct pkcs7_signed_info *info;
+ bool found = false;
+
+ debug("%s: Enter, %p, %p\n", __func__, msg, dbx);
+
+ if (!msg)
+ goto out;
+
+ for (info = msg->signed_infos; info; info = info->next) {
+ if (info->signer &&
+ !efi_signature_verify_cert(info->signer, dbx)) {
+ found = true;
+ goto out;
+ }
+ }
+out:
+ debug("%s: Exit, verified: %d\n", __func__, !found);
+ return !found;
+}
+
+/**
+ * efi_image_region_add - add an entry of region
+ * @regs: Pointer to array of regions
+ * @start: Start address of region
+ * @end: End address of region
+ * @nocheck: flag against overlapped regions
+ *
+ * Take one entry of region [@start, @end] and append it to the list
+ * pointed to by @regs. If @nocheck is false, overlapping among entries
+ * will be checked first.
+ *
+ * Return: 0 on success, status code (negative) on error
+ */
+efi_status_t efi_image_region_add(struct efi_image_regions *regs,
+ const void *start, const void *end,
+ int nocheck)
+{
+ struct image_region *reg;
+ int i, j;
+
+ if (regs->num >= regs->max) {
+ debug("%s: no more room for regions\n", __func__);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (end < start)
+ return EFI_INVALID_PARAMETER;
+
+ for (i = 0; i < regs->num; i++) {
+ reg = &regs->reg[i];
+ if (nocheck)
+ continue;
+
+ if (start > reg->data + reg->size)
+ continue;
+
+ if ((start >= reg->data && start < reg->data + reg->size) ||
+ (end > reg->data && end < reg->data + reg->size)) {
+ debug("%s: new region already part of another\n",
+ __func__);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (start < reg->data && end < reg->data + reg->size) {
+ for (j = regs->num - 1; j >= i; j--)
+ memcpy(&regs->reg[j], &regs->reg[j + 1],
+ sizeof(*reg));
+ break;
+ }
+ }
+
+ reg = &regs->reg[i];
+ reg->data = start;
+ reg->size = end - start;
+ regs->num++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_sigstore_free - free signature store
+ * @sigstore: Pointer to signature store structure
+ *
+ * Feee all the memories held in signature store and itself,
+ * which were allocated by efi_sigstore_parse_sigdb().
+ */
+void efi_sigstore_free(struct efi_signature_store *sigstore)
+{
+ struct efi_signature_store *sigstore_next;
+ struct efi_sig_data *sig_data, *sig_data_next;
+
+ while (sigstore) {
+ sigstore_next = sigstore->next;
+
+ sig_data = sigstore->sig_data_list;
+ while (sig_data) {
+ sig_data_next = sig_data->next;
+ free(sig_data->data);
+ free(sig_data);
+ sig_data = sig_data_next;
+ }
+
+ free(sigstore);
+ sigstore = sigstore_next;
+ }
+}
+
+/**
+ * efi_sigstore_parse_siglist - parse a signature list
+ * @name: Pointer to signature list
+ *
+ * Parse signature list and instantiate a signature store structure.
+ * Signature database is a simple concatenation of one or more
+ * signature list(s).
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+static struct efi_signature_store *
+efi_sigstore_parse_siglist(struct efi_signature_list *esl)
+{
+ struct efi_signature_store *siglist = NULL;
+ struct efi_sig_data *sig_data, *sig_data_next;
+ struct efi_signature_data *esd;
+ size_t left;
+
+ /*
+ * UEFI specification defines certificate types:
+ * for non-signed images,
+ * EFI_CERT_SHA256_GUID
+ * EFI_CERT_RSA2048_GUID
+ * EFI_CERT_RSA2048_SHA256_GUID
+ * EFI_CERT_SHA1_GUID
+ * EFI_CERT_RSA2048_SHA_GUID
+ * EFI_CERT_SHA224_GUID
+ * EFI_CERT_SHA384_GUID
+ * EFI_CERT_SHA512_GUID
+ *
+ * for signed images,
+ * EFI_CERT_X509_GUID
+ * NOTE: Each certificate will normally be in a separate
+ * EFI_SIGNATURE_LIST as the size may vary depending on
+ * its algo's.
+ *
+ * for timestamp revocation of certificate,
+ * EFI_CERT_X509_SHA512_GUID
+ * EFI_CERT_X509_SHA256_GUID
+ * EFI_CERT_X509_SHA384_GUID
+ */
+
+ if (esl->signature_list_size
+ <= (sizeof(*esl) + esl->signature_header_size)) {
+ debug("Siglist in wrong format\n");
+ return NULL;
+ }
+
+ /* Create a head */
+ siglist = calloc(sizeof(*siglist), 1);
+ if (!siglist) {
+ debug("Out of memory\n");
+ goto err;
+ }
+ memcpy(&siglist->sig_type, &esl->signature_type, sizeof(efi_guid_t));
+
+ /* Go through the list */
+ sig_data_next = NULL;
+ left = esl->signature_list_size
+ - (sizeof(*esl) + esl->signature_header_size);
+ esd = (struct efi_signature_data *)
+ ((u8 *)esl + sizeof(*esl) + esl->signature_header_size);
+
+ while ((left > 0) && left >= esl->signature_size) {
+ /* Signature must exist if there is remaining data. */
+ if (left < esl->signature_size) {
+ debug("Certificate is too small\n");
+ goto err;
+ }
+
+ sig_data = calloc(esl->signature_size
+ - sizeof(esd->signature_owner), 1);
+ if (!sig_data) {
+ debug("Out of memory\n");
+ goto err;
+ }
+
+ /* Append signature data */
+ memcpy(&sig_data->owner, &esd->signature_owner,
+ sizeof(efi_guid_t));
+ sig_data->size = esl->signature_size
+ - sizeof(esd->signature_owner);
+ sig_data->data = malloc(sig_data->size);
+ if (!sig_data->data) {
+ debug("Out of memory\n");
+ goto err;
+ }
+ memcpy(sig_data->data, esd->signature_data, sig_data->size);
+
+ sig_data->next = sig_data_next;
+ sig_data_next = sig_data;
+
+ /* Next */
+ esd = (struct efi_signature_data *)
+ ((u8 *)esd + esl->signature_size);
+ left -= esl->signature_size;
+ }
+ siglist->sig_data_list = sig_data_next;
+
+ return siglist;
+
+err:
+ efi_sigstore_free(siglist);
+
+ return NULL;
+}
+
+/**
+ * efi_sigstore_parse_sigdb - parse a signature database variable
+ * @name: Variable's name
+ *
+ * Read in a value of signature database variable pointed to by
+ * @name, parse it and instantiate a signature store structure.
+ *
+ * Return: Pointer to signature store on success, NULL on error
+ */
+struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
+{
+ struct efi_signature_store *sigstore = NULL, *siglist;
+ struct efi_signature_list *esl;
+ const efi_guid_t *vendor;
+ void *db;
+ efi_uintn_t db_size;
+ efi_status_t ret;
+
+ if (!u16_strcmp(name, L"PK") || !u16_strcmp(name, L"KEK")) {
+ vendor = &efi_global_variable_guid;
+ } else if (!u16_strcmp(name, L"db") || !u16_strcmp(name, L"dbx")) {
+ vendor = &efi_guid_image_security_database;
+ } else {
+ debug("unknown signature database, %ls\n", name);
+ return NULL;
+ }
+
+ /* retrieve variable data */
+ db_size = 0;
+ ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, NULL));
+ if (ret == EFI_NOT_FOUND) {
+ debug("variable, %ls, not found\n", name);
+ sigstore = calloc(sizeof(*sigstore), 1);
+ return sigstore;
+ } else if (ret != EFI_BUFFER_TOO_SMALL) {
+ debug("Getting variable, %ls, failed\n", name);
+ return NULL;
+ }
+
+ db = malloc(db_size);
+ if (!db) {
+ debug("Out of memory\n");
+ return NULL;
+ }
+
+ ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db));
+ if (ret != EFI_SUCCESS) {
+ debug("Getting variable, %ls, failed\n", name);
+ goto err;
+ }
+
+ /* Parse siglist list */
+ esl = db;
+ while (db_size > 0) {
+ /* List must exist if there is remaining data. */
+ if (db_size < sizeof(*esl)) {
+ debug("variable, %ls, in wrong format\n", name);
+ goto err;
+ }
+
+ if (db_size < esl->signature_list_size) {
+ debug("variable, %ls, in wrong format\n", name);
+ goto err;
+ }
+
+ /* Parse a single siglist. */
+ siglist = efi_sigstore_parse_siglist(esl);
+ if (!siglist) {
+ debug("Parsing signature list of %ls failed\n", name);
+ goto err;
+ }
+
+ /* Append siglist */
+ siglist->next = sigstore;
+ sigstore = siglist;
+
+ /* Next */
+ db_size -= esl->signature_list_size;
+ esl = (void *)esl + esl->signature_list_size;
+ }
+ free(db);
+
+ return sigstore;
+
+err:
+ efi_sigstore_free(sigstore);
+ free(db);
+
+ return NULL;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c
index c700be8..6655c68 100644
--- a/lib/efi_loader/efi_unicode_collation.c
+++ b/lib/efi_loader/efi_unicode_collation.c
@@ -169,8 +169,8 @@ static bool metai_match(const u16 *string, const u16 *pattern)
* case-insenitively
*
* @this: unicode collation protocol instance
- * @s: string to compare
- * @p: pattern string
+ * @string: string to compare
+ * @pattern: pattern string
*
* The pattern string may use these:
* - * matches >= 0 characters
@@ -199,7 +199,6 @@ static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
*
* @this: unicode collation protocol instance
* @string: string to convert
- * @p: pattern string
*
* The conversion is done in place. As long as upper and lower letters use the
* same number of words this does not pose a problem.
@@ -221,7 +220,6 @@ static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
*
* @this: unicode collation protocol instance
* @string: string to convert
- * @p: pattern string
*
* The conversion is done in place. As long as upper and lower letters use the
* same number of words this does not pose a problem.
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index fe2f264..7df881a 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -10,8 +10,23 @@
#include <env_internal.h>
#include <hexdump.h>
#include <malloc.h>
+#include <rtc.h>
#include <search.h>
+#include <linux/compat.h>
#include <u-boot/crc.h>
+#include "../lib/crypto/pkcs7_parser.h"
+
+enum efi_secure_mode {
+ EFI_MODE_SETUP,
+ EFI_MODE_USER,
+ EFI_MODE_AUDIT,
+ EFI_MODE_DEPLOYED,
+};
+
+const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+static bool efi_secure_boot;
+static int efi_secure_mode;
+static u8 efi_vendor_keys;
#define READ_ONLY BIT(31)
@@ -106,9 +121,10 @@ static const char *prefix(const char *str, const char *prefix)
*
* @str: value of U-Boot variable
* @attrp: pointer to UEFI attributes
+ * @timep: pointer to time attribute
* Return: pointer to remainder of U-Boot variable value
*/
-static const char *parse_attr(const char *str, u32 *attrp)
+static const char *parse_attr(const char *str, u32 *attrp, u64 *timep)
{
u32 attr = 0;
char sep = '{';
@@ -131,6 +147,12 @@ static const char *parse_attr(const char *str, u32 *attrp)
attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
} else if ((s = prefix(str, "run"))) {
attr |= EFI_VARIABLE_RUNTIME_ACCESS;
+ } else if ((s = prefix(str, "time="))) {
+ attr |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ hex2bin((u8 *)timep, s, sizeof(*timep));
+ s += sizeof(*timep) * 2;
+ } else if (*str == '}') {
+ break;
} else {
printf("invalid attribute: %s\n", str);
break;
@@ -147,49 +169,528 @@ static const char *parse_attr(const char *str, u32 *attrp)
return str;
}
+static efi_status_t efi_set_variable_internal(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data,
+ bool ro_check);
+
/**
- * efi_get_variable() - retrieve value of a UEFI variable
+ * efi_transfer_secure_state - handle a secure boot state transition
+ * @mode: new state
*
- * This function implements the GetVariable runtime service.
+ * Depending on @mode, secure boot related variables are updated.
+ * Those variables are *read-only* for users, efi_set_variable_internal()
+ * is called here.
*
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
+ * Return: EFI_SUCCESS on success, status code (negative) on error
+ */
+static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode)
+{
+ u32 attributes;
+ u8 val;
+ efi_status_t ret;
+
+ debug("Secure state from %d to %d\n", efi_secure_mode, mode);
+
+ attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ if (mode == EFI_MODE_DEPLOYED) {
+ val = 1;
+ ret = efi_set_variable_internal(L"SecureBoot",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"SetupMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"AuditMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 1;
+ ret = efi_set_variable_internal(L"DeployedMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ efi_secure_boot = true;
+ } else if (mode == EFI_MODE_AUDIT) {
+ ret = efi_set_variable_internal(L"PK",
+ &efi_global_variable_guid,
+ attributes,
+ 0, NULL,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"SecureBoot",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 1;
+ ret = efi_set_variable_internal(L"SetupMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 1;
+ ret = efi_set_variable_internal(L"AuditMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"DeployedMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ efi_secure_boot = true;
+ } else if (mode == EFI_MODE_USER) {
+ val = 1;
+ ret = efi_set_variable_internal(L"SecureBoot",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"SetupMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"AuditMode",
+ &efi_global_variable_guid,
+ attributes,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"DeployedMode",
+ &efi_global_variable_guid,
+ attributes,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ efi_secure_boot = true;
+ } else if (mode == EFI_MODE_SETUP) {
+ val = 0;
+ ret = efi_set_variable_internal(L"SecureBoot",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 1;
+ ret = efi_set_variable_internal(L"SetupMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"AuditMode",
+ &efi_global_variable_guid,
+ attributes,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ val = 0;
+ ret = efi_set_variable_internal(L"DeployedMode",
+ &efi_global_variable_guid,
+ attributes | READ_ONLY,
+ sizeof(val), &val,
+ false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ efi_secure_mode = mode;
+
+ return EFI_SUCCESS;
+
+err:
+ /* TODO: What action should be taken here? */
+ printf("ERROR: Secure state transition failed\n");
+ return ret;
+}
+
+/**
+ * efi_init_secure_state - initialize secure boot state
*
- * @variable_name: name of the variable
- * @vendor: vendor GUID
- * @attributes: attributes of the variable
- * @data_size: size of the buffer to which the variable value is copied
- * @data: buffer to which the variable value is copied
- * Return: status code
+ * Return: EFI_SUCCESS on success, status code (negative) on error
*/
-efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
- const efi_guid_t *vendor, u32 *attributes,
- efi_uintn_t *data_size, void *data)
+static efi_status_t efi_init_secure_state(void)
+{
+ enum efi_secure_mode mode;
+ efi_uintn_t size;
+ efi_status_t ret;
+
+ /*
+ * TODO:
+ * Since there is currently no "platform-specific" installation
+ * method of Platform Key, we can't say if VendorKeys is 0 or 1
+ * precisely.
+ */
+
+ size = 0;
+ ret = EFI_CALL(efi_get_variable(L"PK", &efi_global_variable_guid,
+ NULL, &size, NULL));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT))
+ mode = EFI_MODE_USER;
+ else
+ mode = EFI_MODE_SETUP;
+
+ efi_vendor_keys = 0;
+ } else if (ret == EFI_NOT_FOUND) {
+ mode = EFI_MODE_SETUP;
+ efi_vendor_keys = 1;
+ } else {
+ goto err;
+ }
+
+ ret = efi_transfer_secure_state(mode);
+ if (ret == EFI_SUCCESS)
+ ret = efi_set_variable_internal(L"VendorKeys",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS
+ | READ_ONLY,
+ sizeof(efi_vendor_keys),
+ &efi_vendor_keys,
+ false);
+
+err:
+ return ret;
+}
+
+/**
+ * efi_secure_boot_enabled - return if secure boot is enabled or not
+ *
+ * Return: true if enabled, false if disabled
+ */
+bool efi_secure_boot_enabled(void)
+{
+ return efi_secure_boot;
+}
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+static u8 pkcs7_hdr[] = {
+ /* SEQUENCE */
+ 0x30, 0x82, 0x05, 0xc7,
+ /* OID: pkcs7-signedData */
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
+ /* Context Structured? */
+ 0xa0, 0x82, 0x05, 0xb8,
+};
+
+/**
+ * efi_variable_parse_signature - parse a signature in variable
+ * @buf: Pointer to variable's value
+ * @buflen: Length of @buf
+ *
+ * Parse a signature embedded in variable's value and instantiate
+ * a pkcs7_message structure. Since pkcs7_parse_message() accepts only
+ * pkcs7's signedData, some header needed be prepended for correctly
+ * parsing authentication data, particularly for variable's.
+ *
+ * Return: Pointer to pkcs7_message structure on success, NULL on error
+ */
+static struct pkcs7_message *efi_variable_parse_signature(const void *buf,
+ size_t buflen)
+{
+ u8 *ebuf;
+ size_t ebuflen, len;
+ struct pkcs7_message *msg;
+
+ /*
+ * This is the best assumption to check if the binary is
+ * already in a form of pkcs7's signedData.
+ */
+ if (buflen > sizeof(pkcs7_hdr) &&
+ !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) {
+ msg = pkcs7_parse_message(buf, buflen);
+ goto out;
+ }
+
+ /*
+ * Otherwise, we should add a dummy prefix sequence for pkcs7
+ * message parser to be able to process.
+ * NOTE: EDK2 also uses similar hack in WrapPkcs7Data()
+ * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c
+ * TODO:
+ * The header should be composed in a more refined manner.
+ */
+ debug("Makeshift prefix added to authentication data\n");
+ ebuflen = sizeof(pkcs7_hdr) + buflen;
+ if (ebuflen <= 0x7f) {
+ debug("Data is too short\n");
+ return NULL;
+ }
+
+ ebuf = malloc(ebuflen);
+ if (!ebuf) {
+ debug("Out of memory\n");
+ return NULL;
+ }
+
+ memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr));
+ memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen);
+ len = ebuflen - 4;
+ ebuf[2] = (len >> 8) & 0xff;
+ ebuf[3] = len & 0xff;
+ len = ebuflen - 0x13;
+ ebuf[0x11] = (len >> 8) & 0xff;
+ ebuf[0x12] = len & 0xff;
+
+ msg = pkcs7_parse_message(ebuf, ebuflen);
+
+ free(ebuf);
+
+out:
+ if (IS_ERR(msg))
+ return NULL;
+
+ return msg;
+}
+
+/**
+ * efi_variable_authenticate - authenticate a variable
+ * @variable: Variable name in u16
+ * @vendor: Guid of variable
+ * @data_size: Size of @data
+ * @data: Pointer to variable's value
+ * @given_attr: Attributes to be given at SetVariable()
+ * @env_attr: Attributes that an existing variable holds
+ * @time: signed time that an existing variable holds
+ *
+ * Called by efi_set_variable() to verify that the input is correct.
+ * Will replace the given data pointer with another that points to
+ * the actual data to store in the internal memory.
+ * On success, @data and @data_size will be replaced with variable's
+ * actual data, excluding authentication data, and its size, and variable's
+ * attributes and signed time will also be returned in @env_attr and @time,
+ * respectively.
+ *
+ * Return: EFI_SUCCESS on success, status code (negative) on error
+ */
+static efi_status_t efi_variable_authenticate(u16 *variable,
+ const efi_guid_t *vendor,
+ efi_uintn_t *data_size,
+ const void **data, u32 given_attr,
+ u32 *env_attr, u64 *time)
+{
+ const struct efi_variable_authentication_2 *auth;
+ struct efi_signature_store *truststore, *truststore2;
+ struct pkcs7_message *var_sig;
+ struct efi_image_regions *regs;
+ struct efi_time timestamp;
+ struct rtc_time tm;
+ u64 new_time;
+ efi_status_t ret;
+
+ var_sig = NULL;
+ truststore = NULL;
+ truststore2 = NULL;
+ regs = NULL;
+ ret = EFI_SECURITY_VIOLATION;
+
+ if (*data_size < sizeof(struct efi_variable_authentication_2))
+ goto err;
+
+ /* authentication data */
+ auth = *data;
+ if (*data_size < (sizeof(auth->time_stamp)
+ + auth->auth_info.hdr.dwLength))
+ goto err;
+
+ if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
+ goto err;
+
+ *data += sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength;
+ *data_size -= (sizeof(auth->time_stamp)
+ + auth->auth_info.hdr.dwLength);
+
+ memcpy(&timestamp, &auth->time_stamp, sizeof(timestamp));
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = timestamp.year;
+ tm.tm_mon = timestamp.month;
+ tm.tm_mday = timestamp.day;
+ tm.tm_hour = timestamp.hour;
+ tm.tm_min = timestamp.minute;
+ tm.tm_sec = timestamp.second;
+ new_time = rtc_mktime(&tm);
+
+ if (!efi_secure_boot_enabled()) {
+ /* finished checking */
+ *time = new_time;
+ return EFI_SUCCESS;
+ }
+
+ if (new_time <= *time)
+ goto err;
+
+ /* data to be digested */
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 5, 1);
+ if (!regs)
+ goto err;
+ regs->max = 5;
+ efi_image_region_add(regs, (uint8_t *)variable,
+ (uint8_t *)variable
+ + u16_strlen(variable) * sizeof(u16), 1);
+ efi_image_region_add(regs, (uint8_t *)vendor,
+ (uint8_t *)vendor + sizeof(*vendor), 1);
+ efi_image_region_add(regs, (uint8_t *)&given_attr,
+ (uint8_t *)&given_attr + sizeof(given_attr), 1);
+ efi_image_region_add(regs, (uint8_t *)&timestamp,
+ (uint8_t *)&timestamp + sizeof(timestamp), 1);
+ efi_image_region_add(regs, (uint8_t *)*data,
+ (uint8_t *)*data + *data_size, 1);
+
+ /* variable's signature list */
+ if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info))
+ goto err;
+ var_sig = efi_variable_parse_signature(auth->auth_info.cert_data,
+ auth->auth_info.hdr.dwLength
+ - sizeof(auth->auth_info));
+ if (IS_ERR(var_sig)) {
+ debug("Parsing variable's signature failed\n");
+ var_sig = NULL;
+ goto err;
+ }
+
+ /* signature database used for authentication */
+ if (u16_strcmp(variable, L"PK") == 0 ||
+ u16_strcmp(variable, L"KEK") == 0) {
+ /* with PK */
+ truststore = efi_sigstore_parse_sigdb(L"PK");
+ if (!truststore)
+ goto err;
+ } else if (u16_strcmp(variable, L"db") == 0 ||
+ u16_strcmp(variable, L"dbx") == 0) {
+ /* with PK and KEK */
+ truststore = efi_sigstore_parse_sigdb(L"KEK");
+ truststore2 = efi_sigstore_parse_sigdb(L"PK");
+
+ if (!truststore) {
+ if (!truststore2)
+ goto err;
+
+ truststore = truststore2;
+ truststore2 = NULL;
+ }
+ } else {
+ /* TODO: support private authenticated variables */
+ goto err;
+ }
+
+ /* verify signature */
+ if (efi_signature_verify_with_sigdb(regs, var_sig, truststore, NULL)) {
+ debug("Verified\n");
+ } else {
+ if (truststore2 &&
+ efi_signature_verify_with_sigdb(regs, var_sig,
+ truststore2, NULL)) {
+ debug("Verified\n");
+ } else {
+ debug("Verifying variable's signature failed\n");
+ goto err;
+ }
+ }
+
+ /* finished checking */
+ *time = rtc_mktime(&tm);
+ ret = EFI_SUCCESS;
+
+err:
+ efi_sigstore_free(truststore);
+ efi_sigstore_free(truststore2);
+ pkcs7_free_message(var_sig);
+ free(regs);
+
+ return ret;
+}
+#else
+static efi_status_t efi_variable_authenticate(u16 *variable,
+ const efi_guid_t *vendor,
+ efi_uintn_t *data_size,
+ const void **data, u32 given_attr,
+ u32 *env_attr, u64 *time)
+{
+ return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
+static
+efi_status_t EFIAPI efi_get_variable_common(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes,
+ efi_uintn_t *data_size, void *data,
+ bool is_non_volatile)
{
char *native_name;
efi_status_t ret;
unsigned long in_size;
- const char *val, *s;
+ const char *val = NULL, *s;
+ u64 time = 0;
u32 attr;
- EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
- data_size, data);
-
if (!variable_name || !vendor || !data_size)
return EFI_EXIT(EFI_INVALID_PARAMETER);
ret = efi_to_native(&native_name, variable_name, vendor);
if (ret)
- return EFI_EXIT(ret);
+ return ret;
EFI_PRINT("get '%s'\n", native_name);
val = env_get(native_name);
free(native_name);
if (!val)
- return EFI_EXIT(EFI_NOT_FOUND);
+ return EFI_NOT_FOUND;
- val = parse_attr(val, &attr);
+ val = parse_attr(val, &attr, &time);
in_size = *data_size;
@@ -198,7 +699,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
/* number of hexadecimal digits must be even */
if (len & 1)
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_DEVICE_ERROR;
/* two characters per byte: */
len /= 2;
@@ -209,11 +710,13 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
goto out;
}
- if (!data)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!data) {
+ debug("Variable with no data shouldn't exist.\n");
+ return EFI_INVALID_PARAMETER;
+ }
if (hex2bin(data, s, len))
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_DEVICE_ERROR;
EFI_PRINT("got value: \"%s\"\n", s);
} else if ((s = prefix(val, "(utf8)"))) {
@@ -226,8 +729,10 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
goto out;
}
- if (!data)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!data) {
+ debug("Variable with no data shouldn't exist.\n");
+ return EFI_INVALID_PARAMETER;
+ }
memcpy(data, s, len);
((char *)data)[len] = '\0';
@@ -235,13 +740,67 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
EFI_PRINT("got value: \"%s\"\n", (char *)data);
} else {
EFI_PRINT("invalid value: '%s'\n", val);
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_DEVICE_ERROR;
}
out:
if (attributes)
*attributes = attr & EFI_VARIABLE_MASK;
+ return ret;
+}
+
+static
+efi_status_t EFIAPI efi_get_volatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes,
+ efi_uintn_t *data_size,
+ void *data)
+{
+ return efi_get_variable_common(variable_name, vendor, attributes,
+ data_size, data, false);
+}
+
+efi_status_t EFIAPI efi_get_nonvolatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes,
+ efi_uintn_t *data_size,
+ void *data)
+{
+ return efi_get_variable_common(variable_name, vendor, attributes,
+ data_size, data, true);
+}
+
+/**
+ * efi_efi_get_variable() - retrieve value of a UEFI variable
+ *
+ * This function implements the GetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
+ const efi_guid_t *vendor, u32 *attributes,
+ efi_uintn_t *data_size, void *data)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ ret = efi_get_volatile_variable(variable_name, vendor, attributes,
+ data_size, data);
+ if (ret == EFI_NOT_FOUND)
+ ret = efi_get_nonvolatile_variable(variable_name, vendor,
+ attributes, data_size, data);
+
return EFI_EXIT(ret);
}
@@ -275,6 +834,7 @@ static efi_status_t parse_uboot_variable(char *variable,
char *guid, *name, *end, c;
size_t name_len;
efi_uintn_t old_variable_name_size;
+ u64 time;
u16 *p;
guid = strchr(variable, '_');
@@ -309,7 +869,7 @@ static efi_status_t parse_uboot_variable(char *variable,
*(name - 1) = c;
/* attributes */
- parse_attr(end, attributes);
+ parse_attr(end, attributes, &time);
return EFI_SUCCESS;
}
@@ -391,7 +951,7 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
list_len = hexport_r(&env_htab, '\n',
H_MATCH_REGEX | H_MATCH_KEY,
&efi_variables_list, 0, 1, regexlist);
- /* 1 indicates that no match was found */
+
if (list_len <= 1)
return EFI_EXIT(EFI_NOT_FOUND);
@@ -404,143 +964,319 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
return EFI_EXIT(ret);
}
-/**
- * efi_set_variable() - set value of a UEFI variable
- *
- * This function implements the SetVariable runtime service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * @variable_name: name of the variable
- * @vendor: vendor GUID
- * @attributes: attributes of the variable
- * @data_size: size of the buffer with the variable value
- * @data: buffer with the variable value
- * Return: status code
- */
-efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
- const efi_guid_t *vendor, u32 attributes,
- efi_uintn_t data_size, const void *data)
+static
+efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data,
+ bool ro_check,
+ bool is_non_volatile)
{
- char *native_name = NULL, *val = NULL, *s;
- const char *old_val;
- size_t old_size;
- efi_status_t ret = EFI_SUCCESS;
+ char *native_name = NULL, *old_data = NULL, *val = NULL, *s;
+ efi_uintn_t old_size;
+ bool append, delete;
+ u64 time = 0;
u32 attr;
+ efi_status_t ret = EFI_SUCCESS;
- EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
- data_size, data);
+ debug("%s: set '%s'\n", __func__, native_name);
if (!variable_name || !*variable_name || !vendor ||
((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
!(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
ret = EFI_INVALID_PARAMETER;
- goto out;
+ goto err;
}
ret = efi_to_native(&native_name, variable_name, vendor);
if (ret)
- goto out;
+ goto err;
+
+ /* check if a variable exists */
+ old_size = 0;
+ attr = 0;
+ ret = EFI_CALL(efi_get_variable(variable_name, vendor, &attr,
+ &old_size, NULL));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ if ((is_non_volatile && !(attr & EFI_VARIABLE_NON_VOLATILE)) ||
+ (!is_non_volatile && (attr & EFI_VARIABLE_NON_VOLATILE))) {
+ ret = EFI_INVALID_PARAMETER;
+ goto err;
+ }
+ }
- old_val = env_get(native_name);
- if (old_val) {
- old_val = parse_attr(old_val, &attr);
+ append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
+ attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE;
+ delete = !append && (!data_size || !attributes);
- /* check read-only first */
- if (attr & READ_ONLY) {
+ /* check attributes */
+ if (old_size) {
+ if (ro_check && (attr & READ_ONLY)) {
ret = EFI_WRITE_PROTECTED;
- goto out;
- }
-
- if ((data_size == 0 &&
- !(attributes & EFI_VARIABLE_APPEND_WRITE)) ||
- !attributes) {
- /* delete the variable: */
- env_set(native_name, NULL);
- ret = EFI_SUCCESS;
- goto out;
+ goto err;
}
/* attributes won't be changed */
- if (attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) {
+ if (!delete &&
+ ((ro_check && attr != attributes) ||
+ (!ro_check && ((attr & ~(u32)READ_ONLY)
+ != (attributes & ~(u32)READ_ONLY))))) {
ret = EFI_INVALID_PARAMETER;
- goto out;
- }
-
- if (attributes & EFI_VARIABLE_APPEND_WRITE) {
- if (!prefix(old_val, "(blob)")) {
- ret = EFI_DEVICE_ERROR;
- goto out;
- }
- old_size = strlen(old_val);
- } else {
- old_size = 0;
+ goto err;
}
} else {
- if (data_size == 0 || !attributes ||
- (attributes & EFI_VARIABLE_APPEND_WRITE)) {
+ if (delete || append) {
/*
* Trying to delete or to update a non-existent
* variable.
*/
ret = EFI_NOT_FOUND;
- goto out;
+ goto err;
+ }
+ }
+
+ if (((!u16_strcmp(variable_name, L"PK") ||
+ !u16_strcmp(variable_name, L"KEK")) &&
+ !guidcmp(vendor, &efi_global_variable_guid)) ||
+ ((!u16_strcmp(variable_name, L"db") ||
+ !u16_strcmp(variable_name, L"dbx")) &&
+ !guidcmp(vendor, &efi_guid_image_security_database))) {
+ /* authentication is mandatory */
+ if (!(attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
+ debug("%ls: AUTHENTICATED_WRITE_ACCESS required\n",
+ variable_name);
+ ret = EFI_INVALID_PARAMETER;
+ goto err;
+ }
+ }
+
+ /* authenticate a variable */
+ if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) {
+ if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto err;
+ }
+ if (attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
+ ret = efi_variable_authenticate(variable_name, vendor,
+ &data_size, &data,
+ attributes, &attr,
+ &time);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ /* last chance to check for delete */
+ if (!data_size)
+ delete = true;
}
+ } else {
+ if (attributes &
+ (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
+ debug("Secure boot is not configured\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto err;
+ }
+ }
+
+ /* delete a variable */
+ if (delete) {
+ /* !old_size case has been handled before */
+ val = NULL;
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+ if (append) {
+ old_data = malloc(old_size);
+ if (!old_data) {
+ return EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ ret = EFI_CALL(efi_get_variable(variable_name, vendor,
+ &attr, &old_size, old_data));
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else {
old_size = 0;
}
- val = malloc(old_size + 2 * data_size
- + strlen("{ro,run,boot,nv}(blob)") + 1);
+ val = malloc(2 * old_size + 2 * data_size
+ + strlen("{ro,run,boot,nv,time=0123456701234567}(blob)")
+ + 1);
if (!val) {
ret = EFI_OUT_OF_RESOURCES;
- goto out;
+ goto err;
}
s = val;
- /* store attributes */
- attributes &= (EFI_VARIABLE_NON_VOLATILE |
+ /*
+ * store attributes
+ */
+ attributes &= (READ_ONLY |
+ EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
- EFI_VARIABLE_RUNTIME_ACCESS);
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS);
s += sprintf(s, "{");
while (attributes) {
- u32 attr = 1 << (ffs(attributes) - 1);
+ attr = 1 << (ffs(attributes) - 1);
- if (attr == EFI_VARIABLE_NON_VOLATILE)
+ if (attr == READ_ONLY) {
+ s += sprintf(s, "ro");
+ } else if (attr == EFI_VARIABLE_NON_VOLATILE) {
s += sprintf(s, "nv");
- else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
+ } else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) {
s += sprintf(s, "boot");
- else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
+ } else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) {
s += sprintf(s, "run");
+ } else if (attr ==
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
+ s += sprintf(s, "time=");
+ s = bin2hex(s, (u8 *)&time, sizeof(time));
+ }
attributes &= ~attr;
if (attributes)
s += sprintf(s, ",");
}
s += sprintf(s, "}");
-
- if (old_size)
- /* APPEND_WRITE */
- s += sprintf(s, old_val);
- else
- s += sprintf(s, "(blob)");
+ s += sprintf(s, "(blob)");
/* store payload: */
+ if (append)
+ s = bin2hex(s, old_data, old_size);
s = bin2hex(s, data, data_size);
*s = '\0';
EFI_PRINT("setting: %s=%s\n", native_name, val);
- if (env_set(native_name, val))
+out:
+ if (env_set(native_name, val)) {
ret = EFI_DEVICE_ERROR;
+ } else {
+ bool vendor_keys_modified = false;
+
+ if ((u16_strcmp(variable_name, L"PK") == 0 &&
+ guidcmp(vendor, &efi_global_variable_guid) == 0)) {
+ ret = efi_transfer_secure_state(
+ (delete ? EFI_MODE_SETUP :
+ EFI_MODE_USER));
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ if (efi_secure_mode != EFI_MODE_SETUP)
+ vendor_keys_modified = true;
+ } else if ((u16_strcmp(variable_name, L"KEK") == 0 &&
+ guidcmp(vendor, &efi_global_variable_guid) == 0)) {
+ if (efi_secure_mode != EFI_MODE_SETUP)
+ vendor_keys_modified = true;
+ }
-out:
+ /* update VendorKeys */
+ if (vendor_keys_modified & efi_vendor_keys) {
+ efi_vendor_keys = 0;
+ ret = efi_set_variable_internal(
+ L"VendorKeys",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS
+ | READ_ONLY,
+ sizeof(efi_vendor_keys),
+ &efi_vendor_keys,
+ false);
+ } else {
+ ret = EFI_SUCCESS;
+ }
+ }
+
+err:
free(native_name);
+ free(old_data);
free(val);
- return EFI_EXIT(ret);
+ return ret;
+}
+
+static
+efi_status_t EFIAPI efi_set_volatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data,
+ bool ro_check)
+{
+ return efi_set_variable_common(variable_name, vendor, attributes,
+ data_size, data, ro_check, false);
+}
+
+efi_status_t EFIAPI efi_set_nonvolatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data,
+ bool ro_check)
+{
+ efi_status_t ret;
+
+ ret = efi_set_variable_common(variable_name, vendor, attributes,
+ data_size, data, ro_check, true);
+
+ return ret;
+}
+
+static efi_status_t efi_set_variable_internal(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data,
+ bool ro_check)
+{
+ efi_status_t ret;
+
+ if (attributes & EFI_VARIABLE_NON_VOLATILE)
+ ret = efi_set_nonvolatile_variable(variable_name, vendor,
+ attributes,
+ data_size, data, ro_check);
+ else
+ ret = efi_set_volatile_variable(variable_name, vendor,
+ attributes, data_size, data,
+ ro_check);
+
+ return ret;
+}
+
+/**
+ * efi_set_variable() - set value of a UEFI variable
+ *
+ * This function implements the SetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ efi_uintn_t data_size, const void *data)
+{
+ EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ /* READ_ONLY bit is not part of API */
+ attributes &= ~(u32)READ_ONLY;
+
+ return EFI_EXIT(efi_set_variable_internal(variable_name, vendor,
+ attributes, data_size, data,
+ true));
}
/**
@@ -640,5 +1376,9 @@ void efi_variables_boot_exit_notify(void)
*/
efi_status_t efi_init_variables(void)
{
- return EFI_SUCCESS;
+ efi_status_t ret;
+
+ ret = efi_init_secure_state();
+
+ return ret;
}
diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c
index 6f69b76..61ea0f7 100644
--- a/lib/efi_loader/efi_watchdog.c
+++ b/lib/efi_loader/efi_watchdog.c
@@ -13,7 +13,9 @@
static struct efi_event *watchdog_timer_event;
-/*
+/**
+ * efi_watchdog_timer_notify() - resets system upon watchdog event
+ *
* Reset the system when the watchdog event is notified.
*
* @event: the watchdog event
@@ -31,13 +33,13 @@ static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
EFI_EXIT(EFI_UNSUPPORTED);
}
-/*
- * Reset the watchdog timer.
+/**
+ * efi_set_watchdog() - resets the watchdog timer
*
* This function is used by the SetWatchdogTimer service.
*
* @timeout: seconds before reset by watchdog
- * @return: status code
+ * Return: status code
*/
efi_status_t efi_set_watchdog(unsigned long timeout)
{
@@ -53,10 +55,12 @@ efi_status_t efi_set_watchdog(unsigned long timeout)
return r;
}
-/*
- * Initialize the EFI watchdog.
+/**
+ * efi_watchdog_register() - initializes the EFI watchdog
+ *
+ * This function is called by efi_init_obj_list().
*
- * This function is called by efi_init_obj_list()
+ * Return: status code
*/
efi_status_t efi_watchdog_register(void)
{