From patchwork Sat Oct 26 09:45:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Richter X-Patchwork-Id: 1184605 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 470bmL6S1rz9s7T for ; Sat, 26 Oct 2019 20:49:22 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from bilbo.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 470bmL53S1zDqxs for ; Sat, 26 Oct 2019 20:49:22 +1100 (AEDT) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=linux.ibm.com (client-ip=148.163.158.5; helo=mx0a-001b2d01.pphosted.com; envelope-from=erichte@linux.ibm.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from mx0a-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 470bht4N8FzDqlf for ; Sat, 26 Oct 2019 20:46:21 +1100 (AEDT) Received: from pps.filterd (m0098417.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x9Q9bXsO043371 for ; Sat, 26 Oct 2019 05:46:19 -0400 Received: from e06smtp05.uk.ibm.com (e06smtp05.uk.ibm.com [195.75.94.101]) by mx0a-001b2d01.pphosted.com with ESMTP id 2vvj10ajj1-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Sat, 26 Oct 2019 05:46:19 -0400 Received: from localhost by e06smtp05.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Sat, 26 Oct 2019 10:46:17 +0100 Received: from b06cxnps4075.portsmouth.uk.ibm.com (9.149.109.197) by e06smtp05.uk.ibm.com (192.168.101.135) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Sat, 26 Oct 2019 10:46:16 +0100 Received: from d06av22.portsmouth.uk.ibm.com (d06av22.portsmouth.uk.ibm.com [9.149.105.58]) by b06cxnps4075.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x9Q9kE6M52166832 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sat, 26 Oct 2019 09:46:14 GMT Received: from d06av22.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8B9AD4C044; Sat, 26 Oct 2019 09:46:14 +0000 (GMT) Received: from d06av22.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id D63B34C050; Sat, 26 Oct 2019 09:46:13 +0000 (GMT) Received: from ceres.ibmuc.com (unknown [9.80.231.2]) by d06av22.portsmouth.uk.ibm.com (Postfix) with ESMTP; Sat, 26 Oct 2019 09:46:13 +0000 (GMT) From: Eric Richter To: skiboot@lists.ozlabs.org Date: Sat, 26 Oct 2019 04:45:52 -0500 X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191026094553.26635-1-erichte@linux.ibm.com> References: <20191026094553.26635-1-erichte@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19102609-0020-0000-0000-0000037ECEE9 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19102609-0021-0000-0000-000021D51EC6 Message-Id: <20191026094553.26635-11-erichte@linux.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-10-26_02:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1908290000 definitions=main-1910260100 Subject: [Skiboot] [PATCH v4 10/11] secvar/backend: add edk2 derived key updates processing X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: nayna@linux.ibm.com Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" From: Nayna Jain As part of secureboot key management, the scheme for key updates processing is derived from tianocore reference implementation[1]. This includes the verification of key updates signed in the form of PKCS7 structure. This patch adds the PKCS7 verification support for the signed updates processed by the user. It also adds the preprocessing code which initializes the empty non-volatile variables. This patch is still a work-in-progress, for example. it still needs to add the support for post-processing steps and better failure handling. V2: - fixed memcpy based on sizeof(keylen) rather than the value - added version and compatible values to driver struct - renamed to edk2-compat V4: - added draft document for general concepts - stores PK in the tpm - empty variables don't attempt to preallocate space - many malloc->zalloc changes - removed hard coded example variables - removed most printfs, converted some to prlog - updated for new pkcs7 implementation - removed unnecessary "SecureBoot", and "SecureMode" variables, handled by devtree entry [1] https://github.com/tianocore/edk2-staging.git Signed-off-by: Nayna Jain --- doc/secvar/edk2.rst | 49 ++ include/secvar.h | 1 + libstb/secvar/backend/Makefile.inc | 4 +- .../secvar/backend/edk2-compat/edk2-compat.c | 555 ++++++++++++++++++ libstb/secvar/backend/edk2-compat/edk2.h | 249 ++++++++ 5 files changed, 857 insertions(+), 1 deletion(-) create mode 100644 doc/secvar/edk2.rst create mode 100644 libstb/secvar/backend/edk2-compat/edk2-compat.c create mode 100644 libstb/secvar/backend/edk2-compat/edk2.h diff --git a/doc/secvar/edk2.rst b/doc/secvar/edk2.rst new file mode 100644 index 00000000..e0c29457 --- /dev/null +++ b/doc/secvar/edk2.rst @@ -0,0 +1,49 @@ +.. _secvar/edk2: + +Skiboot edk2-compatible Secure Variable Backend +=============================================== + +Overview +-------- + +The edk2 secure variable backend for skiboot borrows from edk2 concepts +such as the three key hierarchy (PK, KEK, and db), and a similar +structure. In general, variable updates must be signed with a key +of a higher level. So, updates to the db must be signed with a key stored +in the KEK; updates to the KEK must be signed with the PK. Updates to the +PK must be signed with the previous PK (if any). + +Variables are stored in the efi signature list format, and updates are a +signed variant that includes an authentication header. + +If no PK is currently enrolled, the system is considered to be in "Setup +Mode". Any key can be enrolled without signature checks. However, once a +PK is enrolled, the system switches to "User Mode", and each update must +now be signed according to the hierarchy. Furthermore, when in "User +Mode", the backend initialized the ``os-secure-mode`` device tree flag, +signaling to the kernel that we are in secure mode. + +Updates are processed sequentially, in the order that they were provided +in the update queue. If any update fails to validate, appears to be +malformed, or any other error occurs, NO updates will not be applied. +This includes updates that may have successfully applied prior to the +error. The system will continue in an error state, reporting the error +reason via the ``update-status`` device tree property. + +P9 Special Case for the Platform Key +------------------------------------ + +Due to the powerful nature of the platform key and the lack of lockable +flash, the edk2 backend will store the PK in TPM NV rather than PNOR on +P9 systems. (TODO expand on this) + +Update Status Return Codes +-------------------------- + +TODO, edk2 driver needs to actually return these properly first + + +Device Tree Bindings +-------------------- + +TODO diff --git a/include/secvar.h b/include/secvar.h index cdc9b37d..87365445 100644 --- a/include/secvar.h +++ b/include/secvar.h @@ -38,6 +38,7 @@ struct secvar_backend_driver { }; extern struct secvar_storage_driver secboot_tpm_driver; +extern struct secvar_backend_driver edk2_compatible_v1; int secvar_main(struct secvar_storage_driver, struct secvar_backend_driver); diff --git a/libstb/secvar/backend/Makefile.inc b/libstb/secvar/backend/Makefile.inc index 7a7ca1f7..34ce59a9 100644 --- a/libstb/secvar/backend/Makefile.inc +++ b/libstb/secvar/backend/Makefile.inc @@ -4,8 +4,10 @@ SECVAR_BACKEND_DIR = libstb/secvar/backend SUBDIRS += $(SECVAR_BACKEND_DIR) -SECVAR_BACKEND_SRCS = +SECVAR_BACKEND_SRCS = ./edk2-compat/edk2-compat.c SECVAR_BACKEND_OBJS = $(SECVAR_BACKEND_SRCS:%.c=%.o) SECVAR_BACKEND = $(SECVAR_BACKEND_DIR)/built-in.a +SUBDIRS += $(SECVAR_BACKEND_DIR)/edk2-compat + $(SECVAR_BACKEND): $(SECVAR_BACKEND_OBJS:%=$(SECVAR_BACKEND_DIR)/%) diff --git a/libstb/secvar/backend/edk2-compat/edk2-compat.c b/libstb/secvar/backend/edk2-compat/edk2-compat.c new file mode 100644 index 00000000..f767b90a --- /dev/null +++ b/libstb/secvar/backend/edk2-compat/edk2-compat.c @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This + * program and the accompanying materials are licensed and made available + * under the terms and conditions of the 2-Clause BSD License which + * accompanies this distribution. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * Some of the concepts in this file are derived from the edk2-staging[1] repo + * of tianocore reference implementation + * [1] https://github.com/tianocore/edk2-staging + * Copyright 2019 IBM Corp. + */ + +#include +#include +#include +#include +#include +#include +#include "libstb/crypto/pkcs7/pkcs7.h" +#include "edk2.h" +#include "opal-api.h" +#include "../../secvar.h" +#include "../../secvar_devtree.h" +#include "../../secvar_tpmnv.h" + +#define TPMNV_ID_EDK2_PK 0xd1e81f2c + +static int esl_get_cert_size(unsigned char *buf) +{ + EFI_SIGNATURE_LIST list; + uint32_t sigsize; + + memcpy(&list, buf, sizeof(EFI_SIGNATURE_LIST)); + + sigsize = le32_to_cpu(list.SignatureListSize) - sizeof(list) + - le32_to_cpu(list.SignatureHeaderSize); + + return sigsize; +} + +static int esl_get_cert(unsigned char *buf, unsigned char *cert) +{ + int sig_data_offset; + int size; + EFI_SIGNATURE_LIST list; + + memcpy(&list, buf, sizeof(EFI_SIGNATURE_LIST)); + + sig_data_offset = sizeof(list.SignatureType) + + sizeof(list.SignatureListSize) + + sizeof(list.SignatureHeaderSize) + + sizeof(list.SignatureSize) + + le32_to_cpu(list.SignatureHeaderSize) + + 16 * sizeof(uint8_t); + + size = le32_to_cpu(list.SignatureSize) - sizeof(EFI_SIGNATURE_LIST); + memcpy(cert, buf + sig_data_offset, size); + + return 0; +} + + +/* + * PK needs to be stored in the TPMNV space if on p9 + * We store it using the form , the + * extra secvar headers are unnecessary + */ +static int edk2_p9_load_pk(void) +{ + struct secvar_node *pkvar; + uint64_t size; + int rc; + + // Ensure it exists + rc = secvar_tpmnv_alloc(TPMNV_ID_EDK2_PK, -1); + + // Peek to get the size + rc = secvar_tpmnv_read(TPMNV_ID_EDK2_PK, &size, sizeof(size), 0); + if (OPAL_EMPTY) + return 0; + else if (rc) + return -1; + + if (size > secvar_storage.max_var_size) + return OPAL_RESOURCE; + + pkvar = alloc_secvar(size); + pkvar->var->data_size = size; + pkvar->flags |= SECVAR_FLAG_VOLATILE; + + rc = secvar_tpmnv_read(TPMNV_ID_EDK2_PK, pkvar->var->data, pkvar->var->data_size, sizeof(pkvar->var->data_size)); + if (rc) + return -1; + + list_add_tail(&variable_bank, &pkvar->link); +} + +static int edk2_p9_write_pk(void) +{ + char *tmp; + int32_t tmpsize; + struct secvar_node *pkvar; + + pkvar = find_secvar("PK", 3, &variable_bank); + + // Should not happen + if (!pkvar) + return OPAL_INTERNAL_ERROR; + + // Reset the pk flag to volatile on p9 + pkvar->flags |= SECVAR_FLAG_VOLATILE; + + tmpsize = secvar_tpmnv_size(TPMNV_ID_EDK2_PK); + if (!tmpsize) + return OPAL_RESOURCE; + if (tmpsize < pkvar->var->data_size + sizeof(pkvar->var->data_size)) + return OPAL_RESOURCE; + + tmp = zalloc(tmpsize); + + memcpy(tmp, &pkvar->var->data_size, sizeof(pkvar->var->data_size)); + tmp += sizeof(pkvar->var->data_size); + memcpy(tmp, pkvar->var->data, pkvar->var->data_size); + + return secvar_tpmnv_write(TPMNV_ID_EDK2_PK, tmp, tmpsize, 0); +} + +/** + * Initializes supported variables as empty if not loaded from + * storage. Variables are initialized as volatile if not found. + * Updates should clear this flag. + * + * Returns OPAL Error if anything fails in initialization + */ +static int edk2_compat_pre_process(void) +{ + struct secvar_node *pkvar; + struct secvar_node *kekvar; + struct secvar_node *dbvar; + + // If we are on p9, we need to load the PK from TPM NV space + if (proc_gen == proc_gen_p9) + edk2_p9_load_pk(); + + pkvar = find_secvar((char *)"PK", 3, &variable_bank); + if (!pkvar) { + pkvar = alloc_secvar(0); + if (!pkvar) + return OPAL_NO_MEM; + + memcpy(pkvar->var->key, "PK", 3); + pkvar->var->key_len = 3; + pkvar->flags |= SECVAR_FLAG_VOLATILE; + list_add_tail(&variable_bank, &pkvar->link); + } + + kekvar = find_secvar((char *)"KEK", 4, &variable_bank); + if (!kekvar) { + kekvar = alloc_secvar(0); + if (!kekvar) + return OPAL_NO_MEM; + + memcpy(kekvar->var->key, "KEK", 4); + kekvar->var->key_len = 4; + kekvar->flags |= SECVAR_FLAG_VOLATILE; + list_add_tail(&variable_bank, &kekvar->link); + } + + dbvar = find_secvar((char *)"db", 3, &variable_bank); + if (!dbvar) { + dbvar = alloc_secvar(0); + if (!dbvar) + return OPAL_NO_MEM; + + memcpy(dbvar->var->key, "db", 3); + dbvar->var->key_len = 3; + dbvar->flags |= SECVAR_FLAG_VOLATILE; + list_add_tail(&variable_bank, &dbvar->link); + } + + + return OPAL_SUCCESS; +}; + +/** + * Extracts size of the PKCS7 signed data embedded in the + * struct Authentication Descriptor 2 Header + */ +static int get_pkcs7_len(struct efi_variable_authentication_2 *auth) +{ + uint32_t dw_length = le32_to_cpu(auth->auth_info.hdr.dw_length); + int size; + + size = dw_length - (sizeof(auth->auth_info.hdr.dw_length) + + sizeof(auth->auth_info.hdr.w_revision) + + sizeof(auth->auth_info.hdr.w_certificate_type) + + sizeof(auth->auth_info.cert_type)); + + return size; +} + +/** + * The data submitted by the user is + * auth_descriptor_2 + new ESL data + * This function returns the size of the auth_descriptor_2 + */ +static int get_auth_buffer_size(void *data) +{ + struct efi_variable_authentication_2 *auth; + uint64_t auth_buffer_size; + int len = 0; + + auth = (struct efi_variable_authentication_2 *)data; + + len = get_pkcs7_len(auth); + + auth_buffer_size = sizeof(struct efi_time) + + sizeof(u32) + + sizeof(u16) + + sizeof(u16) + + sizeof(uuid_t) + + len; + + return auth_buffer_size; +} + +/** + * Returns true if we are in Setup Mode + * + * Setup Mode is active if we have no PK. + * Otherwise, we are in deployed mode. + */ +static int is_setup_mode(void) +{ + struct secvar_node *setup; + + setup = find_secvar((char *)"PK", 3, &variable_bank); + + // Not sure why this wouldn't exist + if (!setup) + return 1; + + return !setup->var->data_size; +} + +/** + * Update the variable with the new value. + */ +static int add_to_variable_bank(struct secvar *secvar, void *data, uint64_t dsize) +{ + struct secvar_node *node; + + node = find_secvar(secvar->key, secvar->key_len, &variable_bank); + if (!node) + return OPAL_INTERNAL_ERROR; + + // Expand the secvar allocated memory if needed + if (node->size < dsize) + if (realloc_secvar(node, dsize)) + return OPAL_NO_MEM; + + node->var->data_size = dsize; + memcpy(node->var->data, data, dsize); + node->flags &= ~SECVAR_FLAG_VOLATILE; // Clear the volatile bit when updated + + return 0; +} + +/** + * Verifies the PKCS7 signature on the signed data. + */ +static int verify_update(void *auth_buffer, unsigned char *newcert, + uint64_t new_data_size, struct secvar *avar) +{ + struct efi_variable_authentication_2 *auth; + struct mbedtls_pkcs7 *pkcs7; + int len = 0; + int signing_cert_size = 0; + unsigned char *signing_cert; + unsigned char *x509_buf; + mbedtls_x509_crt x509; + int rc = 0; + + auth = auth_buffer; + + len = get_pkcs7_len(auth); + + pkcs7 = malloc(sizeof(struct mbedtls_pkcs7)); + mbedtls_pkcs7_init(pkcs7); + + rc = mbedtls_pkcs7_parse_der( + (const unsigned char *)auth->auth_info.cert_data, + (const unsigned int)len, pkcs7); + + signing_cert_size = esl_get_cert_size(avar->data); + signing_cert = zalloc(signing_cert_size); + esl_get_cert(avar->data, signing_cert); + + mbedtls_x509_crt_init(&x509); + rc = mbedtls_x509_crt_parse(&x509, signing_cert, signing_cert_size); + if(rc) { + prlog(PR_INFO, "X509 certificate parsing failed %04x\n", rc); + return rc; + } + + x509_buf = zalloc(2048); + mbedtls_x509_crt_info(x509_buf, 2048, "CRT:", &x509); + + rc = mbedtls_pkcs7_signed_data_verify(pkcs7, &x509, newcert, new_data_size); + + free(pkcs7); + + return rc; +} + +static char *utf8_to_ucs2(const char *key, const char keylen) +{ + int i; + char *str; + str = malloc(keylen * 2); + + for (i = 0; i < keylen*2; key++) { + str[i++] = *key; + str[i++] = '\0'; + } + return str; +} + +/** + * Create the single buffer (name, vendor guid, attributes,timestamp and + * newdata) which was originally signed by the user + */ +static int concatenate_data_tobehashed( unsigned char *key, unsigned char *new_data, + uint64_t new_data_size, + unsigned char **buffer, + uint64_t *buffer_size) +{ + unsigned char *tbh_buffer; + int tbh_buffer_size; + struct efi_time timestamp; + int size = 0; + int varlen = 0; + char *wkey; + uint32_t attr = 0x00000000; + //uuid_t guid = PLATFORM_SECVAR_ID; + char guid[16] = {0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,0x07, 0x08, 0x09}; + //uuid_t guid = UUID_INIT(0x11111111, 0x2222, 0x3333, 0x44, 0x44, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc); + + memset(×tamp, 0, sizeof(struct efi_time)); + + // Convert utf8 name to ucs2 width + varlen = strlen(key) * 2; + wkey = utf8_to_ucs2(key, strlen(key)+1); + + /** + * Hash is generated on: + * variablename || vendorguid || attributes || timestamp || newcontent + */ + + tbh_buffer_size = sizeof(struct efi_time) + varlen + UUID_SIZE + sizeof(attr) + new_data_size; + + tbh_buffer = malloc(tbh_buffer_size); + + memcpy(tbh_buffer + size, wkey, varlen); + size = size + varlen; + memcpy(tbh_buffer + size, &guid, sizeof(guid)); + size = size + sizeof(guid); + memcpy(tbh_buffer + size, &attr, sizeof(attr)); + size = size + sizeof(attr); + memcpy(tbh_buffer + size, ×tamp , sizeof(struct efi_time)); + size = size + sizeof(struct efi_time); + memcpy(tbh_buffer + size, new_data, new_data_size); + size = size + new_data_size; + + *buffer = malloc(size); + memcpy(*buffer, tbh_buffer, size); + *buffer_size = size; + + free(wkey); + + return 0; +} + +static int edk2_compat_process(void) +{ + unsigned char *auth_buffer; + uint64_t auth_buffer_size; + uint64_t new_data_size = 0; + unsigned char *dbcert = NULL; + struct secvar_node *anode = NULL; + struct secvar_node *node = NULL; + unsigned char *tbhbuffer; + uint64_t tbhbuffersize; + int rc; + int pk_updated = 0; + bool setupmode = is_setup_mode(); + + prlog(PR_DEBUG, "Setup mode = %d\n", setupmode); + + /* Loop through each command in the update bank. + * If any command fails, it just loops out of the update bank. + * It should also clear the update bank. + */ + list_for_each(&update_bank, node, link) { + + /* Submitted data is auth_descriptor_2 + new ESL data + * Extract the size of auth_descriptor_2 + */ + auth_buffer_size = get_auth_buffer_size(node->var->data); + auth_buffer = zalloc(auth_buffer_size); + memcpy(auth_buffer, node->var->data, auth_buffer_size); + + if (node->var->data_size < auth_buffer_size) { + rc = OPAL_PARAMETER; + goto out; + } + + /* Calculate the size of new ESL data */ + new_data_size = node->var->data_size - auth_buffer_size; + dbcert = zalloc(new_data_size); + memcpy(dbcert, node->var->data + auth_buffer_size, new_data_size); + + if (!setupmode) { + + /* If the update is for PK, verify it with existing PK */ + if (memcmp(node->var->key,"PK",node->var->key_len) == 0) { + anode = find_secvar((char *)"PK", 3, + &variable_bank); + if (anode && (anode->var->data_size == 0)) { + rc = -1; + goto out; + } + } + + /* If the update is for KEK/DB, verify it with PK */ + if ((memcmp(node->var->key,"KEK", node->var->key_len) == 0) + || (memcmp(node->var->key, "db", + node->var->key_len) == 0)) { + anode = find_secvar((char *)"PK", 3, + &variable_bank); + if ((anode && (anode->var->data_size == 0)) + && (memcmp(node->var->key, + "KEK", + node->var->key_len) == 0)) { + prlog(PR_INFO, "validation of %s failed\n", node->var->key); + rc = -1; + goto out; + } + } + + /* If the update is for db, and previous verification + * via PK fails, check if it is signed by any of the + * KEKs + */ + if (memcmp(node->var->key, "db", + node->var->key_len) == 0) { + anode = find_secvar((char *)"KEK", 4, + &variable_bank); + if (anode && (anode->var->data_size == 0)) { + prlog(PR_INFO, "validation of %s failed\n", node->var->key); + rc = -1; + goto out; + } + } + + /* Create the buffer on which signature was generated */ + rc = concatenate_data_tobehashed(node->var->key, + dbcert, + new_data_size, + &tbhbuffer, + &tbhbuffersize); + + /* Verify the signature */ + rc = verify_update(auth_buffer, tbhbuffer, + tbhbuffersize, anode->var); + if (rc) + goto out; + + } + + /* + * If reached here means, signature is verified so update the + * value in the variable bank + */ + add_to_variable_bank(node->var, dbcert, new_data_size); + + /* If the PK is updated, update the secure boot state of the + * system at the end of processing */ + if (memcmp(node->var->key, "PK", + node->var->key_len) == 0) { + pk_updated = 1; + } + } + + if (pk_updated) { + secvar_set_secure_mode(); + + // Store the updated pk in TPMNV on p9 to be safe + if (proc_gen == proc_gen_p9) + edk2_p9_write_pk(); + } + +out: + clear_bank_list(&update_bank); + + return rc; +} + + +static int edk2_compat_validate(struct secvar *var) +{ + + //Checks if the update is for supported + //Non-volatile secure variales + if (memcmp(var->key, "PK", 3) == 0) + return 1; + if (memcmp(var->key, "KEK", 4) == 0) + return 1; + if (memcmp(var->key, "db", 3) == 0) + return 1; + + //Some more checks needs to be added: + // - check guid + // - check auth struct + // - possibly check signature? can't add but can validate + + return 0; +}; + +struct secvar_backend_driver edk2_compatible_v1 = { + .pre_process = edk2_compat_pre_process, + .process = edk2_compat_process, + .validate = edk2_compat_validate, + .compatible = "ibm,edk2-compat-v1", +}; diff --git a/libstb/secvar/backend/edk2-compat/edk2.h b/libstb/secvar/backend/edk2-compat/edk2.h new file mode 100644 index 00000000..e36718c7 --- /dev/null +++ b/libstb/secvar/backend/edk2-compat/edk2.h @@ -0,0 +1,249 @@ +/* Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This + * program and the accompanying materials are licensed and made available + * under the terms and conditions of the 2-Clause BSD License which + * accompanies this distribution. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * This file is derived from the following files referred from edk2-staging[1] repo + * of tianocore + * + * MdePkg/Include/Guid/GlobalVariable.h + * MdePkg/Include/Guid/WinCertificate.h + * MdePkg/Include/Uefi/UefiMultiPhase.h + * MdePkg/Include/Uefi/UefiBaseType.h + * MdePkg/Include/Guid/ImageAuthentication.h + * + * [1] https://github.com/tianocore/edk2-staging + * + * Copyright 2019 IBM Corp. + */ + +#ifndef __EDK2_H__ +#define __EDK2_H__ + +#define UUID_SIZE 16 + +typedef struct { + u8 b[UUID_SIZE]; +} uuid_t; + +#define UUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ + ((uuid_t) {{ ((a) >> 24) & 0xff, ((a) >> 16) & 0xff, ((a) >> 8) & 0xff, (a) & 0xff, ((b) >> 8) & 0xff, (b) & 0xff, \ + ((c) >> 8) & 0xff, (c) & 0xff,(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) \ + }}); + +#define PLATFORM_SECVAR_ID \ +UUID_INIT(0x8be4df61,0x93ca,0x11d2,0xaa, 0x0d,0x00,0xe0,0x98,0x03,0x2b,0x8c) + +#define SECVAR_ATTRIBUTES 0x0029 + +/// +/// This identifies a signature based on an X.509 certificate. If the signature is an X.509 +/// certificate then verification of the signature of an image should validate the public +/// key certificate in the image using certificate path verification, up to this X.509 +/// certificate as a trusted root. The SignatureHeader size shall always be 0. The +/// SignatureSize may vary but shall always be 16 (size of the SignatureOwner component) + +/// the size of the certificate itself. +/// Note: This means that each certificate will normally be in a separate EFI_SIGNATURE_LIST. +/// + +#define EFI_CERT_RSA2048_GUID \ + (UUID_INIT) (0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6) + +#define EFI_CERT_TYPE_PKCS7_GUID \ + (UUID_INIT)(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7) + +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 +/* + * This attribute is identified by the mnemonic 'HR' elsewhere in this + * specification. + */ +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 +/* + * Attributes of Authenticated Variable + */ +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020 +#define EFI_VARIABLE_APPEND_WRITE 0x00000040 +/* + * NOTE: EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated and should be + * considered reserved. + */ +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 + +/* + * win_certificate.w_certificate_type + */ +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 + +#define SECURE_BOOT_MODE_ENABLE 1 +#define SECURE_BOOT_MODE_DISABLE 0 +/// +/// Depricated value definition for SetupMode variable +/// +#define SETUP_MODE 1 +#define USER_MODE 0 + +/* + * EFI Time Abstraction: + * Year: 1900 - 9999 + * Month: 1 - 12 + * Day: 1 - 31 + * Hour: 0 - 23 + * Minute: 0 - 59 + * Second: 0 - 59 + * Nanosecond: 0 - 999,999,999 + * TimeZone: -1440 to 1440 or 2047 + */ +struct efi_time { + u16 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; + u8 pad1; + u32 nanosecond; + s16 timezone; + u8 daylight; + u8 pad2; +}; +//*********************************************************************** +// Signature Database +//*********************************************************************** +/// +/// The format of a signature database. +/// +#pragma pack(1) + +typedef struct { + /// + /// An identifier which identifies the agent which added the signature to the list. + /// + uuid_t SignatureOwner; + /// + /// The format of the signature is defined by the SignatureType. + /// + unsigned char SignatureData[1]; +} EFI_SIGNATURE_DATA; + +typedef struct { + /// + /// Type of the signature. GUID signature types are defined in below. + /// + uuid_t SignatureType; + /// + /// Total size of the signature list, including this header. + /// + uint32_t SignatureListSize; + /// + /// Size of the signature header which precedes the array of signatures. + /// + uint32_t SignatureHeaderSize; + /// + /// Size of each signature. + /// + uint32_t SignatureSize; + /// + /// Header before the array of signatures. The format of this header is specified + /// by the SignatureType. + /// UINT8 SignatureHeader[SignatureHeaderSize]; + /// + /// An array of signatures. Each signature is SignatureSize bytes in length. + /// EFI_SIGNATURE_DATA Signatures[][SignatureSize]; + /// +} EFI_SIGNATURE_LIST; + + +/* + * The win_certificate structure is part of the PE/COFF specification. + */ +struct win_certificate { + /* + * The length of the entire certificate, including the length of the + * header, in bytes. + */ + u32 dw_length; + /* + * The revision level of the WIN_CERTIFICATE structure. The current + * revision level is 0x0200. + */ + u16 w_revision; + /* + * The certificate type. See WIN_CERT_TYPE_xxx for the UEFI certificate + * types. The UEFI specification reserves the range of certificate type + * values from 0x0EF0 to 0x0EFF. + */ + u16 w_certificate_type; + /* + * The following is the actual certificate. The format of + * the certificate depends on wCertificateType. + */ + /// UINT8 bCertificate[ANYSIZE_ARRAY]; +}; + +/* + * Certificate which encapsulates a GUID-specific digital signature + */ +struct win_certificate_uefi_guid { + /* + * This is the standard win_certificate header, where w_certificate_type + * is set to WIN_CERT_TYPE_EFI_GUID. + */ + struct win_certificate hdr; + /* + * This is the unique id which determines the format of the cert_data. + */ + uuid_t cert_type; + /* + * The following is the certificate data. The format of the data is + * determined by the @cert_type. If @cert_type is + * EFI_CERT_TYPE_RSA2048_SHA256_GUID, the @cert_data will be + * EFI_CERT_BLOCK_RSA_2048_SHA256 structure. + */ + u8 cert_data[1]; +}; +/* + * When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is set, + * then the Data buffer shall begin with an instance of a complete (and + * serialized) EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be + * followed by the new variable value and DataSize shall reflect the combined + * size of the descriptor and the new variable value. The authentication + * descriptor is not part of the variable data and is not returned by subsequent + * calls to GetVariable(). + */ +struct efi_variable_authentication_2 { + /* + * For the TimeStamp value, components Pad1, Nanosecond, TimeZone, Daylight and + * Pad2 shall be set to 0. This means that the time shall always be expressed in GMT. + */ + struct efi_time timestamp; + /* + * Only a CertType of EFI_CERT_TYPE_PKCS7_GUID is accepted. + */ + struct win_certificate_uefi_guid auth_info; +}; + +#endif