Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/pseries/plpks-secvar.c
26481 views
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
33
PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
34
PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
35
PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
36
PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
37
PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
38
PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
39
40
static 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
50
static 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
62
static const char * const plpks_var_names_static[] = {
63
"PK",
64
"moduledb",
65
"trustedcadb",
66
NULL,
67
};
68
69
static 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
82
static 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
111
err:
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
122
static 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
157
err:
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
*/
177
static 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
*/
205
static 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
213
static 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
223
static 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
232
static 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
241
static 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
}
253
machine_device_initcall(pseries, plpks_secvar_init);
254
255