File Index
Files and directories
Sign in
FILE

plpks-secvar.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/pseries/plpks-secvar.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3// Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
  4//
  5// Copyright 2022, 2023 IBM Corporation
  6// Authors: Russell Currey
  7//          Andrew Donnellan
  8//          Nayna Jain
  9
 10#define pr_fmt(fmt) "secvar: "fmt
 11
 12#include <linux/printk.h>
 13#include <linux/init.h>
 14#include <linux/types.h>
 15#include <linux/slab.h>
 16#include <linux/string.h>
 17#include <linux/kobject.h>
 18#include <linux/nls.h>
 19#include <asm/machdep.h>
 20#include <asm/secvar.h>
 21#include <asm/plpks.h>
 22
 23// Config attributes for sysfs
 24#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
 25	static ssize_t name##_show(struct kobject *kobj,	\
 26				   struct kobj_attribute *attr,	\
 27				   char *buf)			\
 28	{							\
 29		return sysfs_emit(buf, fmt, func());		\
 30	}							\
 31	static struct kobj_attribute attr_##name = __ATTR_RO(name)
 32
 33PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
 34PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
 35PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
 36PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
 37PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
 38PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
 39
 40static const struct attribute *config_attrs[] = {
 41	&attr_version.attr,
 42	&attr_max_object_size.attr,
 43	&attr_total_size.attr,
 44	&attr_used_space.attr,
 45	&attr_supported_policies.attr,
 46	&attr_signed_update_algorithms.attr,
 47	NULL,
 48};
 49
 50static u32 get_policy(const char *name)
 51{
 52	if ((strcmp(name, "db") == 0) ||
 53	    (strcmp(name, "dbx") == 0) ||
 54	    (strcmp(name, "grubdb") == 0) ||
 55	    (strcmp(name, "grubdbx") == 0) ||
 56	    (strcmp(name, "sbat") == 0))
 57		return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);
 58	else
 59		return PLPKS_SIGNEDUPDATE;
 60}
 61
 62static const char * const plpks_var_names_static[] = {
 63	"PK",
 64	"moduledb",
 65	"trustedcadb",
 66	NULL,
 67};
 68
 69static const char * const plpks_var_names_dynamic[] = {
 70	"PK",
 71	"KEK",
 72	"db",
 73	"dbx",
 74	"grubdb",
 75	"grubdbx",
 76	"sbat",
 77	"moduledb",
 78	"trustedcadb",
 79	NULL,
 80};
 81
 82static int plpks_get_variable(const char *key, u64 key_len, u8 *data,
 83			      u64 *data_size)
 84{
 85	struct plpks_var var = {0};
 86	int rc = 0;
 87
 88	// We subtract 1 from key_len because we don't need to include the
 89	// null terminator at the end of the string
 90	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
 91	if (!var.name)
 92		return -ENOMEM;
 93	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
 94			     key_len - 1);
 95	if (rc < 0)
 96		goto err;
 97	var.namelen = rc * 2;
 98
 99	var.os = PLPKS_VAR_LINUX;
100	if (data) {
101		var.data = data;
102		var.datalen = *data_size;
103	}
104	rc = plpks_read_os_var(&var);
105
106	if (rc)
107		goto err;
108
109	*data_size = var.datalen;
110
111err:
112	kfree(var.name);
113	if (rc && rc != -ENOENT) {
114		pr_err("Failed to read variable '%s': %d\n", key, rc);
115		// Return -EIO since userspace probably doesn't care about the
116		// specific error
117		rc = -EIO;
118	}
119	return rc;
120}
121
122static int plpks_set_variable(const char *key, u64 key_len, u8 *data,
123			      u64 data_size)
124{
125	struct plpks_var var = {0};
126	int rc = 0;
127	u64 flags;
128
129	// Secure variables need to be prefixed with 8 bytes of flags.
130	// We only want to perform the write if we have at least one byte of data.
131	if (data_size <= sizeof(flags))
132		return -EINVAL;
133
134	// We subtract 1 from key_len because we don't need to include the
135	// null terminator at the end of the string
136	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
137	if (!var.name)
138		return -ENOMEM;
139	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
140			     key_len - 1);
141	if (rc < 0)
142		goto err;
143	var.namelen = rc * 2;
144
145	// Flags are contained in the first 8 bytes of the buffer, and are always big-endian
146	flags = be64_to_cpup((__be64 *)data);
147
148	var.datalen = data_size - sizeof(flags);
149	var.data = data + sizeof(flags);
150	var.os = PLPKS_VAR_LINUX;
151	var.policy = get_policy(key);
152
153	// Unlike in the read case, the plpks error code can be useful to
154	// userspace on write, so we return it rather than just -EIO
155	rc = plpks_signed_update_var(&var, flags);
156
157err:
158	kfree(var.name);
159	return rc;
160}
161
162/*
163 * Return the key management mode.
164 *
165 * SB_VERSION is defined as a "1 byte unsigned integer value", taking values
166 * starting from 1. It is owned by the Partition Firmware and its presence
167 * indicates that the key management mode is dynamic. Any failure in
168 * reading SB_VERSION defaults the key management mode to static. The error
169 * codes -ENOENT or -EPERM are expected in static key management mode. An
170 * unexpected error code will have to be investigated. Only signed variables
171 * have null bytes in their names, SB_VERSION does not.
172 *
173 * Return 0 to indicate that the key management mode is static. Otherwise
174 * return the SB_VERSION value to indicate that the key management mode is
175 * dynamic.
176 */
177static u8 plpks_get_sb_keymgmt_mode(void)
178{
179	u8 mode;
180	ssize_t rc;
181	struct plpks_var var = {
182		.component = NULL,
183		.name = "SB_VERSION",
184		.namelen = 10,
185		.datalen = 1,
186		.data = &mode,
187	};
188
189	rc = plpks_read_fw_var(&var);
190	if (rc) {
191		if (rc != -ENOENT && rc != -EPERM)
192			pr_info("Error %ld reading SB_VERSION from firmware\n", rc);
193		mode = 0;
194	}
195	return mode;
196}
197
198/*
199 * PLPKS dynamic secure boot doesn't give us a format string in the same way
200 * OPAL does. Instead, report the format using the SB_VERSION variable in the
201 * keystore. The string, made up by us, takes the form of either
202 * "ibm,plpks-sb-v<n>" or "ibm,plpks-sb-v0", based on the key management mode,
203 * and return the length of the secvar format property.
204 */
205static ssize_t plpks_secvar_format(char *buf, size_t bufsize)
206{
207	u8 mode;
208
209	mode = plpks_get_sb_keymgmt_mode();
210	return snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", mode);
211}
212
213static int plpks_max_size(u64 *max_size)
214{
215	// The max object size reported by the hypervisor is accurate for the
216	// object itself, but we use the first 8 bytes of data on write as the
217	// signed update flags, so the max size a user can write is larger.
218	*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);
219
220	return 0;
221}
222
223static const struct secvar_operations plpks_secvar_ops_static = {
224	.get = plpks_get_variable,
225	.set = plpks_set_variable,
226	.format = plpks_secvar_format,
227	.max_size = plpks_max_size,
228	.config_attrs = config_attrs,
229	.var_names = plpks_var_names_static,
230};
231
232static const struct secvar_operations plpks_secvar_ops_dynamic = {
233	.get = plpks_get_variable,
234	.set = plpks_set_variable,
235	.format = plpks_secvar_format,
236	.max_size = plpks_max_size,
237	.config_attrs = config_attrs,
238	.var_names = plpks_var_names_dynamic,
239};
240
241static int plpks_secvar_init(void)
242{
243	u8 mode;
244
245	if (!plpks_is_available())
246		return -ENODEV;
247
248	mode = plpks_get_sb_keymgmt_mode();
249	if (mode)
250		return set_secvar_ops(&plpks_secvar_ops_dynamic);
251	return set_secvar_ops(&plpks_secvar_ops_static);
252}
253machine_device_initcall(pseries, plpks_secvar_init);
~/ Read-only file preview.