From patchwork Thu Apr 11 22:45:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Richter X-Patchwork-Id: 1084347 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 44gGPP1H51z9s5c for ; Fri, 12 Apr 2019 08:47:21 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 44gGPP06WkzDqTX for ; Fri, 12 Apr 2019 08:47:21 +1000 (AEST) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) 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 44gGN33dbMzDqSq for ; Fri, 12 Apr 2019 08:46:11 +1000 (AEST) Received: from pps.filterd (m0098419.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x3BMitW4136074 for ; Thu, 11 Apr 2019 18:46:09 -0400 Received: from e06smtp04.uk.ibm.com (e06smtp04.uk.ibm.com [195.75.94.100]) by mx0b-001b2d01.pphosted.com with ESMTP id 2rtdcutvpw-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Thu, 11 Apr 2019 18:46:08 -0400 Received: from localhost by e06smtp04.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 11 Apr 2019 23:46:07 +0100 Received: from b06cxnps4075.portsmouth.uk.ibm.com (9.149.109.197) by e06smtp04.uk.ibm.com (192.168.101.134) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Thu, 11 Apr 2019 23:46:04 +0100 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06cxnps4075.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x3BMk36c51118302 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 11 Apr 2019 22:46:03 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 2290C11C06C; Thu, 11 Apr 2019 22:46:03 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 98E8D11C071; Thu, 11 Apr 2019 22:46:02 +0000 (GMT) Received: from yorha.ibmuc.com (unknown [9.80.238.6]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP; Thu, 11 Apr 2019 22:46:02 +0000 (GMT) From: Eric Richter To: skiboot@lists.ozlabs.org Date: Thu, 11 Apr 2019 17:45:48 -0500 X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190411224551.29401-1-erichte@linux.ibm.com> References: <20190411224551.29401-1-erichte@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19041122-0016-0000-0000-0000026DE8A5 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19041122-0017-0000-0000-000032CA214E Message-Id: <20190411224551.29401-6-erichte@linux.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-04-11_14:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=4 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-1810050000 definitions=main-1904110145 Subject: [Skiboot] [RFC v2 5/8] libstb: add opal runtime services for secure boot key management 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: , Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" This patch provides the OPAL runtime service frontend for the host OS to retrieve secure variables, and append new ones for processing on the next reboot. Included are the four minimum required services for basic manipulation: - opal_secvar_read() - opal_secvar_read_next() - opal_secvar_enqueue() - opal_secvar_info() opal_secvar_read() takes in an name string and a vendor buffer (likely a GUID), and returns the corresponding blob of data if there is a matching name + vendor pair in the variable bank. This runtime service only operates on the variable bank. opal_secvar_read_next() provides a method to iterate through the variables in the variable bank. If provided a name and vendor that match a variable in the variable bank, the function will return the name and vendor of the next variable in the bank in sequence. When provided an empty string, it will begin from the top of the list. This runtime service only operates on the variable bank. opal_secvar_enqueue() provides a method for the host OS to submit a new secure variable for processing on next boot, by appending it to the update bank, or "update queue". As this does not affect the variable bank, appending a variable via this runtime service will not affect the output of the previous two. The update queue will only ever be processed during the initialization of skiboot, and is part of a forthcoming patch series. v2: - removed opal_secvar_info(), not sure if neeeded yet - runtime services return OPAL_UNSUPPORTED if secure variables are not enabled - renamed opal_secvar_enqueue to opal_secvar_enqueue_update - moved list_next to ccan/list.h - updated to use new platform hooks Signed-off-by: Eric Richter --- ccan/list/list.h | 38 +++++++++ include/opal-api.h | 5 +- libstb/Makefile.inc | 2 +- libstb/secvar_api.c | 189 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 libstb/secvar_api.c diff --git a/ccan/list/list.h b/ccan/list/list.h index 7cd3a83e..47ea8b28 100644 --- a/ccan/list/list.h +++ b/ccan/list/list.h @@ -523,4 +523,42 @@ static inline struct list_node *list_node_from_off_(void *ptr, size_t off) (container_off_var(var, member) + \ check_type(var->member, struct list_node)) + +#if HAVE_TYPEOF +#define list_typeof(var) typeof(var) +#else +#define list_typeof(var) void * +#endif + + +/* Returns member, or NULL if at end of list. */ +static inline void *list_entry_or_null(const struct list_head *h, + const struct list_node *n, + size_t off) +{ + if (n == &h->n) + return NULL; + return (char *)n - off; +} + +/** + * list_next - get the next entry in a list + * @h: the list_head + * @i: a pointer to an entry in the list. + * @member: the list_node member of the structure + * + * If @i was the last entry in the list, returns NULL. + * + * Example: + * struct child *second; + * second = list_next(&parent->children, first, list); + * if (!second) + * printf("No second child!\n"); + */ +#define list_next(h, i, member) \ + ((list_typeof(i))list_entry_or_null(list_debug(h), \ + (i)->member.next, \ + list_off_var_((i), member))) + + #endif /* CCAN_LIST_H */ diff --git a/include/opal-api.h b/include/opal-api.h index e461c9d2..83bcb0cc 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -229,7 +229,10 @@ #define OPAL_XIVE_GET_VP_STATE 170 /* Get NVT state */ #define OPAL_NPU_RESERVED1 171 /* LPC Allocate */ #define OPAL_NPU_RESERVED2 172 /* LPC Release */ -#define OPAL_LAST 172 +#define OPAL_SECVAR_GET 173 +#define OPAL_SECVAR_GET_NEXT 174 +#define OPAL_SECVAR_ENQUEUE_UPDATE 175 +#define OPAL_LAST 175 #define QUIESCE_HOLD 1 /* Spin all calls at entry */ #define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */ diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc index 2bc9e91b..f3a7e716 100644 --- a/libstb/Makefile.inc +++ b/libstb/Makefile.inc @@ -4,7 +4,7 @@ LIBSTB_DIR = libstb SUBDIRS += $(LIBSTB_DIR) -LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c +LIBSTB_SRCS = container.c tpm_chip.c cvc.c secureboot.c trustedboot.c secvar.c secvar_api.c LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o) LIBSTB = $(LIBSTB_DIR)/built-in.a diff --git a/libstb/secvar_api.c b/libstb/secvar_api.c new file mode 100644 index 00000000..651f96c8 --- /dev/null +++ b/libstb/secvar_api.c @@ -0,0 +1,189 @@ +/* Copyright 2019 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef pr_fmt +#define pr_fmt(fmt) "SECVAR_API: " fmt +#endif + +#include +#include "secvar.h" + + +static int64_t opal_secvar_get(uint64_t k_name, uint64_t k_vendor, uint64_t k_attributes, uint64_t k_data_size, uint64_t k_data) +{ + struct secvar *var; + + char16_t *name = (char16_t *) k_name; + guid_t *vendor = (guid_t *) k_vendor; + uint32_t *attributes = (uint32_t *) k_attributes; + size_t *data_size = (size_t *) k_data_size; + void *data = (void *) k_data; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!name) + return OPAL_PARAMETER; + if (!vendor) + return OPAL_PARAMETER; + if (!data_size) + return OPAL_PARAMETER; + if ((*data_size == 0) && (data)) // Data should be NULL when size is zero + return OPAL_PARAMETER; + if ((*data_size) && (!data)) + return OPAL_PARAMETER; // Data can't be NULL with nonzero size + if (str16nlen(name, SECVAR_MAX_NAME_SIZE) <= 0) + return OPAL_PARAMETER; // Empty or erroneous name is useless + + var = find_secvar_by_name_vendor(name, vendor, &variable_bank); + + if (!var) + return OPAL_EMPTY; + + if (attributes) + *attributes = var->attributes; + + if (!data) { + *data_size = var->data_size; + return OPAL_SUCCESS; // Size check + } + + if (var->data_size > *data_size) { + *data_size = var->data_size; + return OPAL_PARTIAL; + } + + memcpy(data, var->data, var->data_size); + *data_size = var->data_size; + + + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_GET, opal_secvar_get, 5); + + +static int64_t opal_secvar_get_next(uint64_t k_name_size, uint64_t k_name, uint64_t k_vendor) +{ + struct secvar *var; + int namelen; + + size_t *name_size = (size_t *) k_name_size; + char16_t *name = (char16_t *) k_name; + guid_t *vendor = (guid_t *) k_vendor; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!name_size || (*name_size == 0)) + return OPAL_PARAMETER; + if (!name) + return OPAL_PARAMETER; + if (!vendor) + return OPAL_PARAMETER; + + namelen = str16nlen(name, SECVAR_MAX_NAME_SIZE); + if (namelen < 0) + return OPAL_PARAMETER; // Erroneous name buffer + + if (namelen != 0) { + // Non-empty string + var = find_secvar_by_name_vendor(name, vendor, &variable_bank); + if (!var) + return OPAL_PARAMETER; + + var = list_next(&variable_bank, var, link); + } + else { + // Empty string was found, returning first entry + var = list_top(&variable_bank, struct secvar, link); + } + + if (!var) + return OPAL_EMPTY; + + // Return name buffer is too small + namelen = str16nlen(var->name, SECVAR_MAX_NAME_SIZE); + if (namelen <= 0) + return OPAL_HARDWARE; // This should not be reached + + if ((namelen*2) > *name_size) { + *name_size = namelen*2; + return OPAL_PARTIAL; + } + + + memcpy(name, var->name, namelen*2); + memcpy(vendor, &var->vendor, SECVAR_VENDOR_SIZE); + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_GET_NEXT, opal_secvar_get_next, 3); + +static int64_t opal_secvar_enqueue_update(uint64_t k_name, uint64_t k_vendor, uint64_t k_attributes, uint64_t k_data_size, uint64_t k_data) +{ + struct secvar *var; + int namelen; + + char16_t *name = (char16_t *) k_name; + guid_t *vendor = (guid_t *) k_vendor; + uint32_t attributes = (uint32_t) k_attributes; + size_t data_size = (size_t) k_data_size; + void *data = (void *) k_data; + + if (!secvar_enabled) + return OPAL_UNSUPPORTED; + if (!name) + return OPAL_PARAMETER; + if (!vendor) + return OPAL_PARAMETER; + if (!data) + return OPAL_PARAMETER; + + if (data_size == 0) + return OPAL_PARAMETER; // we don't support variable deletion this way + if (data_size > SECVAR_MAX_DATA_SIZE) + return OPAL_PARAMETER; + + namelen = str16nlen(name, SECVAR_MAX_NAME_SIZE); + if (namelen <= 0) + return OPAL_PARAMETER; // empty or erroneous name is useless + + var = zalloc(sizeof(struct secvar)); + if (!var) + return OPAL_NO_MEM; + + var->data = malloc(data_size); + if (!var->data) { + free(var); + return OPAL_NO_MEM; + } + + + memcpy(var->name, name, namelen*2); + memcpy(&var->vendor, vendor, SECVAR_VENDOR_SIZE); + var->attributes = attributes; + var->data_size = data_size; + memcpy(var->data, data, data_size); + + list_add_tail(&update_bank, &var->link); + + if (!platform.secvar_write_bank) + return OPAL_HARDWARE; + + platform.secvar_write_bank(&update_bank, SECVAR_UPDATE_BANK); + + return OPAL_SUCCESS; +} +opal_call(OPAL_SECVAR_ENQUEUE_UPDATE, opal_secvar_enqueue_update, 5);