diff mbox series

[v6,11/20] doc/secvar: add document detailing secvar driver API

Message ID 20200916162131.22478-12-erichte@linux.ibm.com
State Accepted
Headers show
Series Add initial secure variable storage and backend drivers | expand

Checks

Context Check Description
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (d362ae4f4c521a7faffb1befe2fbba467f2c4d18)

Commit Message

Eric Richter Sept. 16, 2020, 4:21 p.m. UTC
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 <erichte@linux.ibm.com>
---
 doc/secvar/driver-api.rst | 312 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 312 insertions(+)
 create mode 100644 doc/secvar/driver-api.rst
diff mbox series

Patch

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.