Path: blob/master/arch/powerpc/platforms/pseries/plpks-secvar.c
51935 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>2122static u32 get_policy(const char *name)23{24if ((strcmp(name, "db") == 0) ||25(strcmp(name, "dbx") == 0) ||26(strcmp(name, "grubdb") == 0) ||27(strcmp(name, "grubdbx") == 0) ||28(strcmp(name, "sbat") == 0))29return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);30else31return PLPKS_SIGNEDUPDATE;32}3334static const char * const plpks_var_names_static[] = {35"PK",36"moduledb",37"trustedcadb",38NULL,39};4041static const char * const plpks_var_names_dynamic[] = {42"PK",43"KEK",44"db",45"dbx",46"grubdb",47"grubdbx",48"sbat",49"moduledb",50"trustedcadb",51NULL,52};5354static int plpks_get_variable(const char *key, u64 key_len, u8 *data,55u64 *data_size)56{57struct plpks_var var = {0};58int rc = 0;5960// We subtract 1 from key_len because we don't need to include the61// null terminator at the end of the string62var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);63if (!var.name)64return -ENOMEM;65rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,66key_len - 1);67if (rc < 0)68goto err;69var.namelen = rc * 2;7071var.os = PLPKS_VAR_LINUX;72if (data) {73var.data = data;74var.datalen = *data_size;75}76rc = plpks_read_os_var(&var);7778if (rc)79goto err;8081*data_size = var.datalen;8283err:84kfree(var.name);85if (rc && rc != -ENOENT) {86pr_err("Failed to read variable '%s': %d\n", key, rc);87// Return -EIO since userspace probably doesn't care about the88// specific error89rc = -EIO;90}91return rc;92}9394static int plpks_set_variable(const char *key, u64 key_len, u8 *data,95u64 data_size)96{97struct plpks_var var = {0};98int rc = 0;99u64 flags;100101// Secure variables need to be prefixed with 8 bytes of flags.102// We only want to perform the write if we have at least one byte of data.103if (data_size <= sizeof(flags))104return -EINVAL;105106// We subtract 1 from key_len because we don't need to include the107// null terminator at the end of the string108var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);109if (!var.name)110return -ENOMEM;111rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,112key_len - 1);113if (rc < 0)114goto err;115var.namelen = rc * 2;116117// Flags are contained in the first 8 bytes of the buffer, and are always big-endian118flags = be64_to_cpup((__be64 *)data);119120var.datalen = data_size - sizeof(flags);121var.data = data + sizeof(flags);122var.os = PLPKS_VAR_LINUX;123var.policy = get_policy(key);124125// Unlike in the read case, the plpks error code can be useful to126// userspace on write, so we return it rather than just -EIO127rc = plpks_signed_update_var(&var, flags);128129err:130kfree(var.name);131return rc;132}133134/*135* Return the key management mode.136*137* SB_VERSION is defined as a "1 byte unsigned integer value", taking values138* starting from 1. It is owned by the Partition Firmware and its presence139* indicates that the key management mode is dynamic. Any failure in140* reading SB_VERSION defaults the key management mode to static. The error141* codes -ENOENT or -EPERM are expected in static key management mode. An142* unexpected error code will have to be investigated. Only signed variables143* have null bytes in their names, SB_VERSION does not.144*145* Return 0 to indicate that the key management mode is static. Otherwise146* return the SB_VERSION value to indicate that the key management mode is147* dynamic.148*/149static u8 plpks_get_sb_keymgmt_mode(void)150{151u8 mode;152ssize_t rc;153struct plpks_var var = {154.component = NULL,155.name = "SB_VERSION",156.namelen = 10,157.datalen = 1,158.data = &mode,159};160161rc = plpks_read_fw_var(&var);162if (rc) {163if (rc != -ENOENT && rc != -EPERM)164pr_info("Error %ld reading SB_VERSION from firmware\n", rc);165mode = 0;166}167return mode;168}169170/*171* PLPKS dynamic secure boot doesn't give us a format string in the same way172* OPAL does. Instead, report the format using the SB_VERSION variable in the173* keystore. The string, made up by us, takes the form of either174* "ibm,plpks-sb-v<n>" or "ibm,plpks-sb-v0", based on the key management mode,175* and return the length of the secvar format property.176*/177static ssize_t plpks_secvar_format(char *buf, size_t bufsize)178{179u8 mode;180181mode = plpks_get_sb_keymgmt_mode();182return snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", mode);183}184185static int plpks_max_size(u64 *max_size)186{187// The max object size reported by the hypervisor is accurate for the188// object itself, but we use the first 8 bytes of data on write as the189// signed update flags, so the max size a user can write is larger.190*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);191192return 0;193}194195static const struct secvar_operations plpks_secvar_ops_static = {196.get = plpks_get_variable,197.set = plpks_set_variable,198.format = plpks_secvar_format,199.max_size = plpks_max_size,200.var_names = plpks_var_names_static,201};202203static const struct secvar_operations plpks_secvar_ops_dynamic = {204.get = plpks_get_variable,205.set = plpks_set_variable,206.format = plpks_secvar_format,207.max_size = plpks_max_size,208.var_names = plpks_var_names_dynamic,209};210211static int plpks_secvar_init(void)212{213u8 mode;214215if (!plpks_is_available())216return -ENODEV;217218mode = plpks_get_sb_keymgmt_mode();219if (mode)220return set_secvar_ops(&plpks_secvar_ops_dynamic);221return set_secvar_ops(&plpks_secvar_ops_static);222}223machine_device_initcall(pseries, plpks_secvar_init);224225226