From patchwork Fri Jun 12 20:25:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Richter X-Patchwork-Id: 1308512 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 49kC4y0DwYz9sRN for ; Sat, 13 Jun 2020 06:29:38 +1000 (AEST) 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 49kC4x6DPtzDr2j for ; Sat, 13 Jun 2020 06:29:37 +1000 (AEST) 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 49kC0K2s2MzDqxL for ; Sat, 13 Jun 2020 06:25:37 +1000 (AEST) Received: from pps.filterd (m0098416.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 05CJ28XC181434 for ; Fri, 12 Jun 2020 16:25:35 -0400 Received: from ppma03ams.nl.ibm.com (62.31.33a9.ip4.static.sl-reverse.com [169.51.49.98]) by mx0b-001b2d01.pphosted.com with ESMTP id 31mcb3qvhx-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Fri, 12 Jun 2020 16:25:35 -0400 Received: from pps.filterd (ppma03ams.nl.ibm.com [127.0.0.1]) by ppma03ams.nl.ibm.com (8.16.0.42/8.16.0.42) with SMTP id 05CKLmce011970 for ; Fri, 12 Jun 2020 20:25:33 GMT Received: from b06cxnps4075.portsmouth.uk.ibm.com (d06relay12.portsmouth.uk.ibm.com [9.149.109.197]) by ppma03ams.nl.ibm.com with ESMTP id 31g2s83v1y-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Fri, 12 Jun 2020 20:25:33 +0000 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 05CKPUbL62718190 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 12 Jun 2020 20:25:30 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 825AF11C058; Fri, 12 Jun 2020 20:25:30 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id A5AB211C050; Fri, 12 Jun 2020 20:25:29 +0000 (GMT) Received: from ceres.ibmuc.com (unknown [9.163.59.202]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP; Fri, 12 Jun 2020 20:25:29 +0000 (GMT) From: Eric Richter To: skiboot@lists.ozlabs.org Date: Fri, 12 Jun 2020 15:25:05 -0500 Message-Id: <20200612202514.15032-12-erichte@linux.ibm.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200612202514.15032-1-erichte@linux.ibm.com> References: <20200612202514.15032-1-erichte@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-06-11_23:2020-06-11, 2020-06-11 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 malwarescore=0 cotscore=-2147483648 lowpriorityscore=0 bulkscore=0 adultscore=0 priorityscore=1501 impostorscore=0 spamscore=0 suspectscore=1 mlxscore=0 mlxlogscore=999 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2006110174 Subject: [Skiboot] [PATCH v5 11/20] doc/secvar: add document detailing secvar driver API 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" This patch adds a reference document that explains the intended use for each of the secvar driver API functions to aid in future secvar driver implementations. Signed-off-by: Eric Richter --- doc/secvar/driver-api.rst | 312 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 doc/secvar/driver-api.rst diff --git a/doc/secvar/driver-api.rst b/doc/secvar/driver-api.rst new file mode 100644 index 00000000..32ca5785 --- /dev/null +++ b/doc/secvar/driver-api.rst @@ -0,0 +1,312 @@ +.. _secvar-driver-api: + +Secvar Drivers +============== + +This document will attempt to define the expected behavior of the two secvar +drivers, and how a developer should implement a new one. + + +Storage vs Backend drivers +-------------------------- + +There are two types of drivers for secure variable support, storage and backend +drivers. Storage drivers are the most simple: they control how and where secure +variables are stored for a given platform. Backend drivers on the other hand, +can be bit more complex. They control the overall scheme of OS secureboot -- +from what variables are used, what format the variables are intended to be, how +they are updated, and how to determine the platform's OS secure boot state. + +These drivers are intended to be as self-contained as possible, so that ideally +any combination of storage and backend drivers in the future should be +compatible. + + +Storage Driver API +------------------ + +The storage driver is expected to: + * persist secure variables in a tamper-resistant manner + * handle two logical types of variable lists (referred to as "banks") + * the "variable bank" stores the active list of variables + * the "update bank" stores proposed updates to the variable bank + * handle variables using a specific secvar flag in a sensible manner + +Storage drivers must implement the following hooks for secvar to properly +utilize: + +.. code-block:: c + + 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); + void (*lock)(void); + uint64_t max_var_size; + }; + +The following subsections will give a summary of each hook, when they are used, +and their expected behavior. + + +store_init +^^^^^^^^^^ + +The ``store_init`` hook is called at the beginning of secure variable +intialization. This hook should perform any initialization logic required for +the other hooks to operate. + +IMPORTANT: If this hook returns an error (non-zero) code, secvar will +immediately halt the boot. When implementing this hook, consider the +implications of any errors in initialization, and whether they may affect the +secure state. For example, if secure state is indeterminable due to some +hardware failure, this is grounds for a halt. + +This hook should only be called once. Subsequent calls should have no effect, +or raise an error. + + +load_bank +^^^^^^^^^ + +The ``load_bank`` hook should load variables from persistent storage into the +in-memory linked lists, for the rest of secvar to operate on. + +The ``bank`` parameter should be an initialized linked list. This list may not +be empty, and this hook should only append variables to the list. + +The variables this hook loads should depend on the ``section`` flag: + * if ``SECVAR_VARIABLE_BANK``, load the active variables + * if ``SECVAR_UPDATE_BANK``, load the proposed updates + +This hook is called twice at the beginning of secure variable initialization, +one for loading each bank type into their respective lists. This hook may be +called again afterwards (e.g. a reset mechanism by a backend). + + +write_bank +^^^^^^^^^^ + +The ``write_bank`` hook should persist variables using some non-volatile +storage (e.g. flash). + +The ``bank`` parameter should be an initialized linked list. This list may be +empty. It is up to the storage driver to determine how to handle this, but it is +strongly recommended to zeroize the storage location. + +The ``section`` parameter indicates which list of variables is to be written +following the same pattern as in ``load_bank``. + +This hook is called for the variable bank if the backend driver reports that +updates were processed. This hook is called for the update bank in all cases +EXCEPT where no updates were found by the backend (this includes error cases). + +This hook should not be called more than once for the variable bank. This hook +is called once in the secvar initialization procedure, and then each time +``opal_secvar_enqueue_update()`` is successfully called. + + +lock +^^^^ + +The ``lock`` hook may perform any write-lock protections as necessary by the +platform. This hook is unconditionally called after the processing step +performed in the main secure variable logic, and should only be called once. +Subsequent calls should have no effect, or raise an error. + +This hook MUST also be called in any error cases that may interrupt the regular +secure variable initialization flow, to prevent leaving the storage mechanism +open to unauthorized writes. + +This hook MUST halt the boot if any internal errors arise that may compromise +the protection of the storage. + +If locking is not applicable to the storage mechanism, this hook may be +implemented as a no-op. + + +max_var_size +^^^^^^^^^^^^ + +The ``max_var_size`` field is not a function hook, but a value to be referenced +by other components to determine the maximum variable size. As this driver is +responsible for persisting variables somewhere, it has the option to determine +the maximum size to use. + + +A Quick Note on Secvar Flags +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While "communication" between the storage and backend drivers has been +minimized as best as possible, there are a few cases where the storage driver +may need to take a few hints from the backend. + +The ``flags`` field in ``struct secvar_node`` may contain one of the following +values: + +.. code-block:: c + + #define SECVAR_FLAG_VOLATILE 0x1 + #define SECVAR_FLAG_PROTECTED 0x2 + +At time of writing this document, the flags are mutually exclusive, however +this may change in the future. + +``VOLATILE`` indicates that the storage driver should NOT persist this variable +to storage. + +``PROTECTED`` indicates that this variable has a heightened importance than +other variables, and if applicable to the storage driver, stored in a more +secure/tamper-resistant region (e.g. store variables important to secureboot +state in TPM NV rather than PNOR on p9). + + +Backend Driver API +------------------ + +The backend driver at the core defines how secure variables are defined and +processed, and by extension, also how operate the platform's secure boot modes. + +.. code-block:: c + + struct secvar_backend_driver { + int (*pre_process)(struct list_head *variable_bank + struct list_head *update_bank); + int (*process)(struct list_head *variable_bank + struct list_head *update_bank); + int (*post_process)(struct list_head *variable_bank + struct list_head *update_bank); + int (*validate)(struct secvar *var); + const char *compatible; + }; + +The following subsections will give a summary of each hook, when they are used, +and their expected behaviors. + + +pre_process +^^^^^^^^^^^ + +The ``pre_process`` hook is an optional hook that a backend driver may implement +to handle any early logic prior to processing. If this hook is set to ``NULL``, +it is skipped. + +As this hook is called just after loading the variables from the storage driver +but just before ``process``, this hook is provided for convenience to do any +early initialization logic as necessary. + +Any error code returned by this hook will be treated as a failure, and halt +secure variable initialization. + +Example usage: + * initialize empty variables that were not loaded from storage + * allocate any internal structures that may be needed for processing + + +process +^^^^^^^ + +The ``process`` hook is the only required hook, and should contain all variable +update process logic. Unlike the other two hooks, this hook must be defined, or +secure variable initialization will halt. + +This hook is expected to iterate through any variables contained in the +``update_bank`` list argument, and perform any action on the +``variable_bank`` list argument as the backend seems appropriate for the given +update (e.g. add/remove/update variable) + +NOTE: the state of these bank lists will be written to persistent storage as-is, +so for example, if the update bank should be cleared, it should be done prior to +returning from this hook. + +Unlike the other two hooks, this hook may return a series of return codes +indicating various status situations. This return code is exposed in the device +tree at ``secvar/update-status``. See the table below for an expected definition +of the return code meanings. Backends SHOULD document any deviations or +extensions to these definitions for their specific implementation. + +To prevent excessive writes to flash, the main secure variable flow will only +perform writes when the ``process`` hook returns a status that declares +something has been changed. The variable bank is only written to storage if +``process`` returns ``OPAL_SUCCESS``. + +On the other hand, the update bank is written to storage if the return code is +anything other than ``OPAL_EMPTY`` (which signals that there were no updates to +process). This includes all error cases, therefore the backend is responsible +for emptying the update bank prior to exiting with an error, if the bank is to +be cleared. + + +Status codes +"""""""""""" + ++-----------------+-----------------------------------------------+ +| update-status | Generic Reason | ++-----------------+-----------------------------------------------+ +| OPAL_SUCCESS | Updates were found and processed successfully | ++-----------------+-----------------------------------------------+ +| OPAL_EMPTY | No updates were found, none processed | ++-----------------+-----------------------------------------------+ +| OPAL_PARAMETER | Malformed, or unexpected update data blob | ++-----------------+-----------------------------------------------+ +| OPAL_PERMISSION | Update failed to apply, possible auth failure | ++-----------------+-----------------------------------------------+ +| OPAL_HARDWARE | Misc. storage-related error | ++-----------------+-----------------------------------------------+ +| OPAL_RESOURCE | Out of space (reported by storage) | ++-----------------+-----------------------------------------------+ +| OPAL_NO_MEM | Out of memory | ++-----------------+-----------------------------------------------+ + +See also: ``device-tree/ibm,opal/secvar/secvar.rst``. + + +post_process +^^^^^^^^^^^^ + +The ``post_process`` hook is an optional hook that a backend driver may +implement to handle any additional logic after the processing step. Like +``pre_process``, it may be set to ``NULL`` if unused. + +This hook is called AFTER performing any writes to storage, and AFTER locking +the persistant storage. Any changes to the variable bank list in this hook will +NOT be persisted to storage. + +Any error code returned by this hook will be treated as a failure, and halt +secure variable initialization. + +Example usage: + * determine secure boot state (and set ``os-secure-enforcing``) + * remove any variables from the variable bank that do not need to be exposed + * append any additional volatile variables + + +validate +^^^^^^^^ + +!!NOTE!! This is not currently implemented, and the detail below is subject to +change. + +The ``validate`` hook is an optional hook that a backend may implement to check +if a single variable is valid. If implemented, this hook is called during +``opal_secvar_enqueue_update`` to provide more immediate feedback to the caller +on proposed variable validity. + +This hook should return ``OPAL_SUCCESS`` if the validity check passes. Any +other return code is treated as a failure, and will be passed through the +``enqueue_update`` call. + +Example usage: + * check for valid payload data structure + * check for valid signature format + * validate the signature against current variables + * implement a variable white/blacklist + + +compatible +^^^^^^^^^^ + +The compatible field is a required field that declares the compatibility of +this backend driver. This compatible field is exposed in the +``secvar/compatible`` device tree node for subsequent kernels, etc to +determine how to interact with the secure variables.