From patchwork Tue Nov 5 07:37:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Richter X-Patchwork-Id: 1189379 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) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 476hQx53DHz9sNT for ; Tue, 5 Nov 2019 18:40:25 +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 476hQx2XvlzF3vG for ; Tue, 5 Nov 2019 18:40:25 +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 476hQq1ghpzF3tx for ; Tue, 5 Nov 2019 18:40:17 +1100 (AEDT) Received: from pps.filterd (m0098413.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id xA57e2jw046243 for ; Tue, 5 Nov 2019 02:40:15 -0500 Received: from e06smtp02.uk.ibm.com (e06smtp02.uk.ibm.com [195.75.94.98]) by mx0b-001b2d01.pphosted.com with ESMTP id 2w3477966g-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Tue, 05 Nov 2019 02:40:10 -0500 Received: from localhost by e06smtp02.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 5 Nov 2019 07:38:02 -0000 Received: from b06cxnps4076.portsmouth.uk.ibm.com (9.149.109.198) by e06smtp02.uk.ibm.com (192.168.101.132) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Tue, 5 Nov 2019 07:38:00 -0000 Received: from d06av23.portsmouth.uk.ibm.com (d06av23.portsmouth.uk.ibm.com [9.149.105.59]) by b06cxnps4076.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id xA57bxnn50659396 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 5 Nov 2019 07:37:59 GMT Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 1D09BA404D; Tue, 5 Nov 2019 07:37:59 +0000 (GMT) Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 630F8A4051; Tue, 5 Nov 2019 07:37:58 +0000 (GMT) Received: from ceres.ibmuc.com (unknown [9.80.237.45]) by d06av23.portsmouth.uk.ibm.com (Postfix) with ESMTP; Tue, 5 Nov 2019 07:37:58 +0000 (GMT) From: Eric Richter To: skiboot@lists.ozlabs.org Date: Tue, 5 Nov 2019 01:37:56 -0600 X-Mailer: git-send-email 2.21.0 MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19110507-0008-0000-0000-0000032ACD05 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19110507-0009-0000-0000-00004A4A25A3 Message-Id: <20191105073756.13371-1-erichte@linux.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-11-05_02:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=3 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-1911050063 Subject: [Skiboot] [PATCH RESEND v6 2/5] libstb/secvar: add secure variable internal abstraction 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: , Reply-To: 20191105042011.7357-1-erichte@linux.ibm.com Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" Resending this patch, as the original failed to fully remove the backend device tree node, causing a segfault when attempting to set the backend process status. ORIGINAL MESSAGE: This patch implements a platform-independent abstraction for storing and retrieving secure variables, as required for host OS secure boot. This serves as the main entry point for initializing the in-memory cache of the secure variables, which also kicks off any platform-specific logic that may be needed. This patch also provides core functions for the subsequent patches in this series to utilize. The base secure variable implementation makes use of two types of drivers, to be selected by the platform: "storage" drivers, and "backend" drivers. The storage driver implements the hooks required to write the secure variables to some form of non-volatile memory, and load the variables on boot. The backend driver defines how the variables should be interpreted, and processed. Secure variables are stored in two types of banks, the "variable" bank and the "update" bank. Variables that have been validated and processed are stored in the variable bank. This bank is effectively read-only after the base secvar initialization. Any proposed variable updates are instead stored in the update bank. During secvar initialization, the backend driver processes variables from the update bank, and if valid, adds the new variable to the variable bank. NOTE: The name "backend" is subject to change. It operates more like a scheme, so unless a better name comes along, it will likely change to "scheme" or "schema" in the future. V2: - added secvar device tree node as child of ibm,secureboot - added version and compatible properties to backend driver struct - added secvar_ready flag for the API to detect if secvar initialized successfully - moved pre-process step to after initial variable load - moved flags field from secvar struct to secvar node V3: - remove the metadata secvar field - add probe_secvar() to bump compatible flag - add device tree property for backend-agnostic secure mode setting - remove backend minor version field - remove static data allocation in secvar struct V4: - add alloc_secvar helpers - removed ibm,secureboot version bump to v3 - secvars now store their allocated size seperate from the data size (to permit overallocating) - split device tree functions into their own file - device tree changes: - secvar now a child of ibm,opal - compatible is "ibm,secvar-v1", backend creates its own node - secure-mode is now a boolean os-secure-enforcing property - storage and backends now have their own nodes V5: - removed storage device tree subnode - moved max-var-size to secvar node - added max-var-key-len - fixed SPDX header in include/secvar.h - removed obsolete enum - removed unused devtree wrappers - set secvar status prop earlier V6: - moved os-secureboot-enforcing to ibm,secureboot - set secvar compatible based on backend - removed backend node Signed-off-by: Eric Richter --- core/init.c | 4 ++ include/platform.h | 2 + include/secvar.h | 29 ++++++++ libstb/Makefile.inc | 3 +- libstb/secvar/Makefile.inc | 14 ++++ libstb/secvar/backend/Makefile.inc | 11 +++ libstb/secvar/secvar.h | 55 +++++++++++++++ libstb/secvar/secvar_devtree.c | 65 ++++++++++++++++++ libstb/secvar/secvar_devtree.h | 10 +++ libstb/secvar/secvar_main.c | 87 ++++++++++++++++++++++++ libstb/secvar/secvar_util.c | 105 +++++++++++++++++++++++++++++ libstb/secvar/storage/Makefile.inc | 11 +++ 12 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 include/secvar.h create mode 100644 libstb/secvar/Makefile.inc create mode 100644 libstb/secvar/backend/Makefile.inc create mode 100644 libstb/secvar/secvar.h create mode 100644 libstb/secvar/secvar_devtree.c create mode 100644 libstb/secvar/secvar_devtree.h create mode 100644 libstb/secvar/secvar_main.c create mode 100644 libstb/secvar/secvar_util.c create mode 100644 libstb/secvar/storage/Makefile.inc diff --git a/core/init.c b/core/init.c index cc1fdbc4..e6efb600 100644 --- a/core/init.c +++ b/core/init.c @@ -1211,6 +1211,10 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt) secureboot_init(); trustedboot_init(); + /* Secure variables init, handled by platform */ + if (platform.secvar_init) + platform.secvar_init(); + /* * BMC platforms load version information from flash after * secure/trustedboot init. diff --git a/include/platform.h b/include/platform.h index 0b043856..412f8fc8 100644 --- a/include/platform.h +++ b/include/platform.h @@ -210,6 +210,8 @@ struct platform { uint32_t len); int (*nvram_write)(uint32_t dst, void *src, uint32_t len); + int (*secvar_init)(void); + /* * OCC timeout. This return how long we should wait for the OCC * before timing out. This lets us use a high value on larger FSP diff --git a/include/secvar.h b/include/secvar.h new file mode 100644 index 00000000..c41fb739 --- /dev/null +++ b/include/secvar.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2019 IBM Corp. */ + +#ifndef _SECVAR_DRIVER_ +#define _SECVAR_DRIVER_ + +#include + +struct secvar; + +struct secvar_storage_driver { + int (*load_bank)(struct list_head *bank, int section); + int (*write_bank)(struct list_head *bank, int section); + int (*store_init)(void); + uint64_t max_var_size; +}; + +struct secvar_backend_driver { + int (*pre_process)(void); // Perform any pre-processing stuff (e.g. determine secure boot state) + int (*process)(void); // Process all updates + int (*post_process)(void); // Perform any post-processing stuff (e.g. derive/update variables) + int (*validate)(struct secvar *var); // Validate a single variable, return boolean + const char *compatible; // String to use for compatible in secvar node +}; + + +int secvar_main(struct secvar_storage_driver, struct secvar_backend_driver); + +#endif diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc index 6d54c5cd..d3f68496 100644 --- a/libstb/Makefile.inc +++ b/libstb/Makefile.inc @@ -8,11 +8,12 @@ LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o) LIBSTB = $(LIBSTB_DIR)/built-in.a +include $(SRC)/$(LIBSTB_DIR)/secvar/Makefile.inc include $(SRC)/$(LIBSTB_DIR)/mbedtls/Makefile.inc include $(SRC)/$(LIBSTB_DIR)/drivers/Makefile.inc include $(SRC)/$(LIBSTB_DIR)/tss/Makefile.inc -$(LIBSTB): $(LIBSTB_OBJS:%=$(LIBSTB_DIR)/%) $(DRIVERS) $(TSS) $(MBEDTLS) +$(LIBSTB): $(LIBSTB_OBJS:%=$(LIBSTB_DIR)/%) $(DRIVERS) $(TSS) $(SECVAR) $(MBEDTLS) libstb/create-container: libstb/create-container.c libstb/container-utils.c $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) \ diff --git a/libstb/secvar/Makefile.inc b/libstb/secvar/Makefile.inc new file mode 100644 index 00000000..e1e6e5c7 --- /dev/null +++ b/libstb/secvar/Makefile.inc @@ -0,0 +1,14 @@ +# -*-Makefile-*- + +SECVAR_DIR = libstb/secvar + +SUBDIRS += $(SECVAR_DIR) + +include $(SECVAR_DIR)/storage/Makefile.inc +include $(SECVAR_DIR)/backend/Makefile.inc + +SECVAR_SRCS = secvar_main.c secvar_util.c secvar_devtree.c +SECVAR_OBJS = $(SECVAR_SRCS:%.c=%.o) +SECVAR = $(SECVAR_DIR)/built-in.a + +$(SECVAR): $(SECVAR_OBJS:%=$(SECVAR_DIR)/%) $(SECVAR_STORAGE) $(SECVAR_BACKEND) diff --git a/libstb/secvar/backend/Makefile.inc b/libstb/secvar/backend/Makefile.inc new file mode 100644 index 00000000..7a7ca1f7 --- /dev/null +++ b/libstb/secvar/backend/Makefile.inc @@ -0,0 +1,11 @@ +# -*-Makefile-*- + +SECVAR_BACKEND_DIR = libstb/secvar/backend + +SUBDIRS += $(SECVAR_BACKEND_DIR) + +SECVAR_BACKEND_SRCS = +SECVAR_BACKEND_OBJS = $(SECVAR_BACKEND_SRCS:%.c=%.o) +SECVAR_BACKEND = $(SECVAR_BACKEND_DIR)/built-in.a + +$(SECVAR_BACKEND): $(SECVAR_BACKEND_OBJS:%=$(SECVAR_BACKEND_DIR)/%) diff --git a/libstb/secvar/secvar.h b/libstb/secvar/secvar.h new file mode 100644 index 00000000..6a208337 --- /dev/null +++ b/libstb/secvar/secvar.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2019 IBM Corp. */ + +#ifndef _SECVAR_H_ +#define _SECVAR_H_ + +#include +#include +#include + +#define SECVAR_MAX_KEY_LEN 1024 + +enum { + SECVAR_VARIABLE_BANK, + SECVAR_UPDATE_BANK, +}; + + +struct secvar_node { + struct list_node link; + struct secvar *var; + uint64_t flags; // Flag for how *var should be stored + uint64_t size; // How much space was allocated for data +}; + +#define SECVAR_FLAG_VOLATILE 0x1 // Instructs storage driver to ignore variable on writes +#define SECVAR_FLAG_SECURE_STORAGE 0x2 // Hint for storage driver to select storage location + +struct secvar { + uint64_t key_len; + uint64_t data_size; + char key[SECVAR_MAX_KEY_LEN]; + char data[0]; +}; + + +extern struct list_head variable_bank; +extern struct list_head update_bank; +extern int secvar_enabled; +extern int secvar_ready; +extern struct secvar_storage_driver secvar_storage; +extern struct secvar_backend_driver secvar_backend; + +// Check for secvar support, update secureboot DT compatible if so +int probe_secvar(void); + +// Helper functions +void clear_bank_list(struct list_head *bank); +struct secvar_node *alloc_secvar(uint64_t size); +int realloc_secvar(struct secvar_node *node, uint64_t size); +struct secvar_node *find_secvar(const char *key, uint64_t key_len, struct list_head *bank); +int is_key_empty(const char *key, uint64_t key_len); +int list_length(struct list_head *bank); + +#endif diff --git a/libstb/secvar/secvar_devtree.c b/libstb/secvar/secvar_devtree.c new file mode 100644 index 00000000..8ba34ae9 --- /dev/null +++ b/libstb/secvar/secvar_devtree.c @@ -0,0 +1,65 @@ +#include +#include +#include "secvar.h" +#include "secvar_devtree.h" + +struct dt_node *secvar_node; + +int secvar_set_secure_mode(void) +{ + struct dt_node *sb_root; + struct dt_property *prop; + + if (!secvar_node) + return -1; + + sb_root = dt_find_by_path(dt_root, "/ibm,secureboot/"); + + prop = (struct dt_property *) dt_find_property(sb_root, "os-secureboot-enforcing"); + if (prop) + return 0; + + prop = dt_add_property(sb_root, "os-secureboot-enforcing", 0, 0); + if (!prop) + return -2; + + return 0; +} + +void secvar_init_devnode(const char *compatible) +{ + struct dt_node *sb_root; + + sb_root = dt_find_by_path(dt_root, "/ibm,opal/"); + + secvar_node = dt_new(sb_root, "secvar"); + + dt_add_property_string(secvar_node, "compatible", compatible); + dt_add_property_u64(secvar_node, "max-var-size", secvar_storage.max_var_size); + dt_add_property_u64(secvar_node, "max-var-key-len", SECVAR_MAX_KEY_LEN); +} + +void secvar_set_status(const char *status) +{ + if (!secvar_node) + return; // Fail boot? + + if (dt_find_property(secvar_node, "status")) + return; + + dt_add_property_string(secvar_node, "status", status); + // Fail boot if not successful? +} + + +void secvar_set_update_status(uint64_t val) +{ + if (!secvar_node) + return; + + if (dt_find_property(secvar_node, "update-status")) + return; + + dt_add_property_u64(secvar_node, "update-status", val); +} + diff --git a/libstb/secvar/secvar_devtree.h b/libstb/secvar/secvar_devtree.h new file mode 100644 index 00000000..bf4296ed --- /dev/null +++ b/libstb/secvar/secvar_devtree.h @@ -0,0 +1,10 @@ +#ifndef _SECVAR_DEVTREE_H_ +#define _SECVAR_DEVTREE_H_ + +int secvar_set_secure_mode(void); +void secvar_init_devnode(const char *compatible); + +void secvar_set_status(const char *status); +void secvar_set_update_status(uint64_t val); + +#endif diff --git a/libstb/secvar/secvar_main.c b/libstb/secvar/secvar_main.c new file mode 100644 index 00000000..fc5527ab --- /dev/null +++ b/libstb/secvar/secvar_main.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2019 IBM Corp. */ + +#ifndef pr_fmt +#define pr_fmt(fmt) "SECVAR: " fmt +#endif + +#include +#include +#include +#include "secvar.h" +#include "secvar_devtree.h" + +struct list_head variable_bank; +struct list_head update_bank; + +int secvar_enabled = 0; // Set to 1 if secvar is supported +int secvar_ready = 0; // Set to 1 when base secvar inits correctly + +// To be filled in by platform.secvar_init +struct secvar_storage_driver secvar_storage = {0}; +struct secvar_backend_driver secvar_backend = {0}; + + +int secvar_main(struct secvar_storage_driver storage_driver, + struct secvar_backend_driver backend_driver) +{ + int rc = OPAL_UNSUPPORTED; + + secvar_storage = storage_driver; + secvar_backend = backend_driver; + + secvar_init_devnode(secvar_backend.compatible); + + secvar_enabled = 1; + + list_head_init(&variable_bank); + list_head_init(&update_bank); + + rc = secvar_storage.store_init(); + if (rc) + goto fail; + + // Failures here should indicate some kind of hardware problem + rc = secvar_storage.load_bank(&variable_bank, SECVAR_VARIABLE_BANK); + if (rc) + goto fail; + + rc = secvar_storage.load_bank(&update_bank, SECVAR_UPDATE_BANK); + if (rc) + goto fail; + + // At this point, base secvar is functional. Rest is up to the backend + secvar_ready = 1; + secvar_set_status("okay"); + + if (secvar_backend.pre_process) + rc = secvar_backend.pre_process(); + + // Process is required, error if it doesn't exist + if (!secvar_backend.process) + goto out; + + rc = secvar_backend.process(); + secvar_set_update_status(rc); + if (rc == OPAL_SUCCESS) { + rc = secvar_storage.write_bank(&variable_bank, SECVAR_VARIABLE_BANK); + if (rc) + goto out; + + rc = secvar_storage.write_bank(&update_bank, SECVAR_UPDATE_BANK); + if (rc) + goto out; + } + + if (secvar_backend.post_process) + rc = secvar_backend.post_process(); + if (rc) + goto out; + + return OPAL_SUCCESS; +fail: + secvar_set_status("fail"); +out: + printf("Secure Variables Status %04x\n", rc); + return rc; +} diff --git a/libstb/secvar/secvar_util.c b/libstb/secvar/secvar_util.c new file mode 100644 index 00000000..49a31817 --- /dev/null +++ b/libstb/secvar/secvar_util.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2019 IBM Corp. */ + +#ifndef pr_fmt +#define pr_fmt(fmt) "SECVAR: " fmt +#endif + +#include +#include +#include +#include +#include "secvar.h" + +void clear_bank_list(struct list_head *bank) +{ + struct secvar_node *node, *next; + + if (!bank) + return; + + list_for_each_safe(bank, node, next, link) { + list_del(&node->link); + + if (node->var) { + free(node->var); + } + + free(node); + } +} + +struct secvar_node *alloc_secvar(uint64_t size) +{ + struct secvar_node *ret; + + ret = zalloc(sizeof(struct secvar_node)); + if (!ret) + return NULL; + + ret->var = zalloc(sizeof(struct secvar) + size); + if (!ret->var) { + free(ret); + return NULL; + } + + ret->size = size; + + return ret; +} + +int realloc_secvar(struct secvar_node *node, uint64_t size) +{ + void *tmp; + + if (node->size >= size) + return 0; + + tmp = zalloc(sizeof(struct secvar) + size); + if (!tmp) + return -1; + + memcpy(tmp, node->var, sizeof(struct secvar) + node->size); + free(node->var); + node->var = tmp; + + return 0; +} + +struct secvar_node *find_secvar(const char *key, uint64_t key_len, struct list_head *bank) +{ + struct secvar_node *node = NULL; + + list_for_each(bank, node, link) { + // Prevent matching shorter key subsets / bail early + if (key_len != node->var->key_len) + continue; + if (!memcmp(key, node->var->key, key_len)) { + return node; + } + } + + return NULL; +} + +int is_key_empty(const char *key, uint64_t key_len) +{ + int i; + for (i = 0; i < key_len; i++) { + if (key[i] != 0) + return 0; + } + + return 1; +} + +int list_length(struct list_head *bank) +{ + int ret = 0; + struct secvar_node *node; + + list_for_each(bank, node, link) + ret++; + + return ret; +} diff --git a/libstb/secvar/storage/Makefile.inc b/libstb/secvar/storage/Makefile.inc new file mode 100644 index 00000000..c107736e --- /dev/null +++ b/libstb/secvar/storage/Makefile.inc @@ -0,0 +1,11 @@ +# -*-Makefile-*- + +SECVAR_STORAGE_DIR = libstb/secvar/storage + +SUBDIRS += $(SECVAR_STORAGE_DIR) + +SECVAR_STORAGE_SRCS = +SECVAR_STORAGE_OBJS = $(SECVAR_STORAGE_SRCS:%.c=%.o) +SECVAR_STORAGE = $(SECVAR_STORAGE_DIR)/built-in.a + +$(SECVAR_STORAGE): $(SECVAR_STORAGE_OBJS:%=$(SECVAR_STORAGE_DIR)/%)