diff options
Diffstat (limited to 'roms/skiboot/doc/secvar')
-rw-r--r-- | roms/skiboot/doc/secvar/driver-api.rst | 312 | ||||
-rw-r--r-- | roms/skiboot/doc/secvar/edk2.rst | 49 | ||||
-rw-r--r-- | roms/skiboot/doc/secvar/secboot_tpm.rst | 175 |
3 files changed, 536 insertions, 0 deletions
diff --git a/roms/skiboot/doc/secvar/driver-api.rst b/roms/skiboot/doc/secvar/driver-api.rst new file mode 100644 index 000000000..32ca5785d --- /dev/null +++ b/roms/skiboot/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. diff --git a/roms/skiboot/doc/secvar/edk2.rst b/roms/skiboot/doc/secvar/edk2.rst new file mode 100644 index 000000000..1e4cc9e31 --- /dev/null +++ b/roms/skiboot/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/roms/skiboot/doc/secvar/secboot_tpm.rst b/roms/skiboot/doc/secvar/secboot_tpm.rst new file mode 100644 index 000000000..8da0c2f01 --- /dev/null +++ b/roms/skiboot/doc/secvar/secboot_tpm.rst @@ -0,0 +1,175 @@ +.. _secvar/secboot_tpm: + +secboot_tpm secvar storage driver for P9 platforms +================================================== + +Overview +-------- + +This storage driver utilizes the SECBOOT PNOR partition and TPM NV space to +persist secure variables across reboots in a tamper-resistant manner. While +writes to PNOR cannot be completely prevented, writes CAN be prevented to TPM +NV. On the other hand, there is limited available space in TPM NV. + +Therefore, this driver uses both in conjunction: large variable data is written +to SECBOOT, and a hash of the variable data is stored in TPM NV. When the +variables are loaded from SECBOOT, this hash is recalculated and compared +against the value stored in the TPM. If they do not match, then the variables +must have been altered and are not loaded. + +See the following sections for more information on the internals of the driver. + + +Storage Layouts +--------------- + +At a high-level, there are a few major logical components: + + - (PNOR) Variable storage (split in half, active/staging) + - (PNOR) Update storage + - (TPM) Protected variable storage + - (TPM) Bank hashes & active bit + +Variable storage consists of two smaller banks, variable bank 0 and variable +bank 1. Either of the banks may be designated "active" by setting the active +bank bit to either 0 or 1, indicating that the corresponding bank is now +"active". The other bank is then considered "staging". See the "Persisting +Variable Bank Updates" for more on the active/staging bank logic. + +Protected variable storage is stored in ``VARS`` TPM NV index. Unlike the other +variable storage, there is only one bank due to limited storage space. See the +TPM NV Indices section for more. + + +Persisting the Variable Bank +---------------------------- + +When writing a new variable bank to storage, this is (roughly) the procedure the +driver will follow: + +1. write variables to the staging bank +2. calculate hash of the staging bank +3. store the staging bank hash in the TPM NV +4. flip the active bank bit + +This procedure ensures that the switch-over from the old variables to the +new variables is as atomic as possible. This should prevent any possible +issues caused by an interruption during the writing process, such as power loss. + +The bank hashes are a SHA256 hash calculated over the whole region of +storage space allocated to the bank, including unused storage. For consistency, +unused space is always written as zeroes. Like the active/staging variable +banks, there are also two corresponding active/staging bank hashes stored in +the TPM. + + +TPM NV Indices +-------------- + +The driver utilizes two TPM NV indices: + +.. code-block:: c + + # size). datadefine SECBOOT_TPMNV_VARS_INDEX 0x01c10190 + #define SECBOOT_TPMNV_CONTROL_INDEX 0x01c10191 + +The ``VARS`` index stores variables flagged with ``SECVAR_FLAG_PROTECTED``. +These variables are critical to the state of OS secure boot, and therefore +cannot be safely stored in the SECBOOT partition. This index is defined to be +1024 bytes in size, which is enough for the current implementation on P9. It +is kept small by default to preserve the very limited NV index space. + +The ``CONTROL`` index stores the bank hashes, and the bit to determine which +bank is active. See the Active/Staging Bank Swapping section for more. + +Both indices are defined on first boot with the same set of attributes. If the +indices are already defined but not in the expected state, (different +attributes, size, etc), then the driver will halt the boot. Asserting physical +presence will redefine the indices in the correct state. + + +Locking +------- + +PNOR cannot be locked, however the TPM can be. The TPM NV indices are double +protected via two locking mechanisms: + + - The driver's ``.lock()`` hook sends the ``TSS_NV_WriteLock`` TPM command. +This sets the ``WRITELOCKED`` attribute, which is cleared on the next +TPM reset. + + - The TPM NV indices are defined under the platform hierarchy. Skiboot will add +a global lock to all the NV indices under this hierarchy prior to loading a +kernel. This is also reset on the next TPM reset. + +NOTE: The TPM is only reset during a cold reboot. Fast reboots or kexecs will +NOT unlock the TPM. + + +Resetting Storage / Physical Presence +------------------------------------- + +In the case that secure boot/secvar has been rendered unusable, (for example: +corrupted data, lost/compromised private key, improperly defined NV indices, etc) +this storage driver responds to physical presence assertion as a last-resort +method to recover the system. + +Asserting physical presence undefines, and immediately redefines the TPM NV +indices. Defining the NV indices then causes a cascading set of reformats for +the remaining components of storage, similar to a first-boot scenario. + +This driver considers physical presence to be asserted if any of the following +device tree nodes are present in ``ibm,secureboot``: + - ``clear-os-keys`` + - ``clear-all-keys`` + - ``clear-mfg-keys`` + + +Storage Formats/Layouts +======================= + +SECBOOT (PNOR) +-------------- + +Partition Format: + - 8b secboot header + - 4b: u32. magic number, always 0x5053424b + - 1b: u8. version, always 1 + - 3b: unused padding + - 32k: secvars. variable bank 0 + - 32k: secvars. variable bank 1 + - 32k: secvars. update bank + +Variable Format (secvar): + - 8b: u64. key length + - 8b: u64. data size + - 1k: string. key + - (data size). data + +TPM VARS (NV) +------------- + +NV Index Format: + - 8b secboot header + - 4b: u32. magic number, always 0x5053424b + - 1b: u8. version, always 1 + - 3b: unused padding + - 1016b: packed secvars. protected variable storage + +Variable Format (packed secvar): + - 8b: u64. key length + - 8b: u64. data size + - (key length): string. key + - (data size). data + +TPM CONTROL (NV) +---------------- + + - 8b secboot header + - 4b: u32. magic number, always 0x5053424b + - 1b: u8. version, always 1 + - 3b: unused padding + - 1b: u8. active bit, 0 or 1 + - 32b: sha256 hash of variable bank 0 + - 32b: sha256 hash of variable bank 1 + |