[Skiboot] [RFC 4/6] libstb: add opal runtime services for secure boot key management
Oliver
oohall at gmail.com
Fri Mar 29 13:24:02 AEDT 2019
On Fri, Mar 29, 2019 at 9:19 AM Eric Richter <erichte at linux.ibm.com> wrote:
>
> 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.
>
> opal_secvar_info() has not been implemented yet. It will return
> status on the size of, and remaining storage space for secure variables.
>
> Signed-off-by: Eric Richter <erichte at linux.ibm.com>
> ---
> include/opal-api.h | 6 +-
> libstb/Makefile.inc | 2 +-
> libstb/secvar_api.c | 203 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 209 insertions(+), 2 deletions(-)
> create mode 100644 libstb/secvar_api.c
>
> diff --git a/include/opal-api.h b/include/opal-api.h
> index 73f86f9a..8e398073 100644
> --- a/include/opal-api.h
> +++ b/include/opal-api.h
> @@ -226,7 +226,11 @@
> #define OPAL_NX_COPROC_INIT 167
> #define OPAL_NPU_SET_RELAXED_ORDER 168
> #define OPAL_NPU_GET_RELAXED_ORDER 169
> -#define OPAL_LAST 169
> +#define OPAL_SECVAR_GET 170
> +#define OPAL_SECVAR_GET_NEXT 171
> +#define OPAL_SECVAR_ENQUEUE 172
Call it ENQUEUE_UPDATE or something. This is a bit vague.
> +#define OPAL_SECVAR_INFO 173
> +#define OPAL_LAST 173
>
> #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..c5de3bdd
> --- /dev/null
> +++ b/libstb/secvar_api.c
> @@ -0,0 +1,203 @@
> +/* 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 <opal.h>
> +#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; // TODO: make sure this size is correct
> + size_t *data_size = (size_t *) k_data_size;
> + void *data = (void *) k_data;
> +
> + if (!secvar_enabled)
> + return OPAL_HARDWARE;
> + 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 (!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;
> +
> + if (attributes)
> + *attributes = var->attributes;
Moving this above the !data check might be a good idea. That would
allow the kernel to query the size and attributes simultaneously.
> +
> +
> + 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_HARDWARE;
> + 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(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_HARDWARE;
> + if (!name)
> + return OPAL_PARAMETER;
> + if (!vendor)
> + return OPAL_PARAMETER;
> + if (!data)
> + return OPAL_PARAMETER;
> + if (data_size == 0)
> + return OPAL_UNSUPPORTED; // we don't support variable deletion this way
How does variable deletion work?
> + 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;
> + }
You can use a trailing struct member to do this in a single
allocation. Not a big deal, but it makes the code a bit cleaner.
> +
> +
> + 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);
> +
> + // TODO: Prevalidate here?
What does pre-validation involve?
> +
> + list_add_tail(&update_bank, &var->link);
> +
> + if (!secvar_write_update_bank)
> + return OPAL_HARDWARE;
> +
> + secvar_write_update_bank(&update_bank);
> +
> + return OPAL_SUCCESS;
> +}
> +opal_call(OPAL_SECVAR_ENQUEUE, opal_secvar_enqueue, 5);
> +
> +
> +// TODO: not implemented yet, out of scope for now
> +static int64_t opal_secvar_info(uint64_t attributes, uint64_t *storage_space, uint64_t *remaining_space, uint64_t *max_variable_size)
> +{
> + (void) attributes;
> + (void) storage_space;
> + (void) remaining_space;
> + (void) max_variable_size;
Is the max variable size a static value or does it depend on how much
space we have left? If it's static then this should be put in the
device-tree rather than being queried with an OPAL call.
> +
> + return OPAL_UNSUPPORTED;
> +}
> +opal_call(OPAL_SECVAR_INFO, opal_secvar_info, 4);
> --
> 2.17.2
>
> _______________________________________________
> Skiboot mailing list
> Skiboot at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/skiboot
More information about the Skiboot
mailing list