Path: blob/master/arch/powerpc/platforms/pseries/plpks-secvar.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only12// Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)3//4// Copyright 2022, 2023 IBM Corporation5// Authors: Russell Currey6// Andrew Donnellan7// Nayna Jain89#define pr_fmt(fmt) "secvar: "fmt1011#include <linux/printk.h>12#include <linux/init.h>13#include <linux/types.h>14#include <linux/slab.h>15#include <linux/string.h>16#include <linux/kobject.h>17#include <linux/nls.h>18#include <asm/machdep.h>19#include <asm/secvar.h>20#include <asm/plpks.h>2122// Config attributes for sysfs23#define PLPKS_CONFIG_ATTR(name, fmt, func) \24static ssize_t name##_show(struct kobject *kobj, \25struct kobj_attribute *attr, \26char *buf) \27{ \28return sysfs_emit(buf, fmt, func()); \29} \30static struct kobj_attribute attr_##name = __ATTR_RO(name)3132PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);33PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);34PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);35PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);36PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);37PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);3839static const struct attribute *config_attrs[] = {40&attr_version.attr,41&attr_max_object_size.attr,42&attr_total_size.attr,43&attr_used_space.attr,44&attr_supported_policies.attr,45&attr_signed_update_algorithms.attr,46NULL,47};4849static u32 get_policy(const char *name)50{51if ((strcmp(name, "db") == 0) ||52(strcmp(name, "dbx") == 0) ||53(strcmp(name, "grubdb") == 0) ||54(strcmp(name, "grubdbx") == 0) ||55(strcmp(name, "sbat") == 0))56return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);57else58return PLPKS_SIGNEDUPDATE;59}6061static const char * const plpks_var_names_static[] = {62"PK",63"moduledb",64"trustedcadb",65NULL,66};6768static const char * const plpks_var_names_dynamic[] = {69"PK",70"KEK",71"db",72"dbx",73"grubdb",74"grubdbx",75"sbat",76"moduledb",77"trustedcadb",78NULL,79};8081static int plpks_get_variable(const char *key, u64 key_len, u8 *data,82u64 *data_size)83{84struct plpks_var var = {0};85int rc = 0;8687// We subtract 1 from key_len because we don't need to include the88// null terminator at the end of the string89var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);90if (!var.name)91return -ENOMEM;92rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,93key_len - 1);94if (rc < 0)95goto err;96var.namelen = rc * 2;9798var.os = PLPKS_VAR_LINUX;99if (data) {100var.data = data;101var.datalen = *data_size;102}103rc = plpks_read_os_var(&var);104105if (rc)106goto err;107108*data_size = var.datalen;109110err:111kfree(var.name);112if (rc && rc != -ENOENT) {113pr_err("Failed to read variable '%s': %d\n", key, rc);114// Return -EIO since userspace probably doesn't care about the115// specific error116rc = -EIO;117}118return rc;119}120121static int plpks_set_variable(const char *key, u64 key_len, u8 *data,122u64 data_size)123{124struct plpks_var var = {0};125int rc = 0;126u64 flags;127128// Secure variables need to be prefixed with 8 bytes of flags.129// We only want to perform the write if we have at least one byte of data.130if (data_size <= sizeof(flags))131return -EINVAL;132133// We subtract 1 from key_len because we don't need to include the134// null terminator at the end of the string135var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);136if (!var.name)137return -ENOMEM;138rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,139key_len - 1);140if (rc < 0)141goto err;142var.namelen = rc * 2;143144// Flags are contained in the first 8 bytes of the buffer, and are always big-endian145flags = be64_to_cpup((__be64 *)data);146147var.datalen = data_size - sizeof(flags);148var.data = data + sizeof(flags);149var.os = PLPKS_VAR_LINUX;150var.policy = get_policy(key);151152// Unlike in the read case, the plpks error code can be useful to153// userspace on write, so we return it rather than just -EIO154rc = plpks_signed_update_var(&var, flags);155156err:157kfree(var.name);158return rc;159}160161/*162* Return the key management mode.163*164* SB_VERSION is defined as a "1 byte unsigned integer value", taking values165* starting from 1. It is owned by the Partition Firmware and its presence166* indicates that the key management mode is dynamic. Any failure in167* reading SB_VERSION defaults the key management mode to static. The error168* codes -ENOENT or -EPERM are expected in static key management mode. An169* unexpected error code will have to be investigated. Only signed variables170* have null bytes in their names, SB_VERSION does not.171*172* Return 0 to indicate that the key management mode is static. Otherwise173* return the SB_VERSION value to indicate that the key management mode is174* dynamic.175*/176static u8 plpks_get_sb_keymgmt_mode(void)177{178u8 mode;179ssize_t rc;180struct plpks_var var = {181.component = NULL,182.name = "SB_VERSION",183.namelen = 10,184.datalen = 1,185.data = &mode,186};187188rc = plpks_read_fw_var(&var);189if (rc) {190if (rc != -ENOENT && rc != -EPERM)191pr_info("Error %ld reading SB_VERSION from firmware\n", rc);192mode = 0;193}194return mode;195}196197/*198* PLPKS dynamic secure boot doesn't give us a format string in the same way199* OPAL does. Instead, report the format using the SB_VERSION variable in the200* keystore. The string, made up by us, takes the form of either201* "ibm,plpks-sb-v<n>" or "ibm,plpks-sb-v0", based on the key management mode,202* and return the length of the secvar format property.203*/204static ssize_t plpks_secvar_format(char *buf, size_t bufsize)205{206u8 mode;207208mode = plpks_get_sb_keymgmt_mode();209return snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", mode);210}211212static int plpks_max_size(u64 *max_size)213{214// The max object size reported by the hypervisor is accurate for the215// object itself, but we use the first 8 bytes of data on write as the216// signed update flags, so the max size a user can write is larger.217*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);218219return 0;220}221222static const struct secvar_operations plpks_secvar_ops_static = {223.get = plpks_get_variable,224.set = plpks_set_variable,225.format = plpks_secvar_format,226.max_size = plpks_max_size,227.config_attrs = config_attrs,228.var_names = plpks_var_names_static,229};230231static const struct secvar_operations plpks_secvar_ops_dynamic = {232.get = plpks_get_variable,233.set = plpks_set_variable,234.format = plpks_secvar_format,235.max_size = plpks_max_size,236.config_attrs = config_attrs,237.var_names = plpks_var_names_dynamic,238};239240static int plpks_secvar_init(void)241{242u8 mode;243244if (!plpks_is_available())245return -ENODEV;246247mode = plpks_get_sb_keymgmt_mode();248if (mode)249return set_secvar_ops(&plpks_secvar_ops_dynamic);250return set_secvar_ops(&plpks_secvar_ops_static);251}252machine_device_initcall(pseries, plpks_secvar_init);253254255