Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/pseries/plpks-secvar.c
51935 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
static u32 get_policy(const char *name)
24
{
25
if ((strcmp(name, "db") == 0) ||
26
(strcmp(name, "dbx") == 0) ||
27
(strcmp(name, "grubdb") == 0) ||
28
(strcmp(name, "grubdbx") == 0) ||
29
(strcmp(name, "sbat") == 0))
30
return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);
31
else
32
return PLPKS_SIGNEDUPDATE;
33
}
34
35
static const char * const plpks_var_names_static[] = {
36
"PK",
37
"moduledb",
38
"trustedcadb",
39
NULL,
40
};
41
42
static const char * const plpks_var_names_dynamic[] = {
43
"PK",
44
"KEK",
45
"db",
46
"dbx",
47
"grubdb",
48
"grubdbx",
49
"sbat",
50
"moduledb",
51
"trustedcadb",
52
NULL,
53
};
54
55
static int plpks_get_variable(const char *key, u64 key_len, u8 *data,
56
u64 *data_size)
57
{
58
struct plpks_var var = {0};
59
int rc = 0;
60
61
// We subtract 1 from key_len because we don't need to include the
62
// null terminator at the end of the string
63
var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
64
if (!var.name)
65
return -ENOMEM;
66
rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
67
key_len - 1);
68
if (rc < 0)
69
goto err;
70
var.namelen = rc * 2;
71
72
var.os = PLPKS_VAR_LINUX;
73
if (data) {
74
var.data = data;
75
var.datalen = *data_size;
76
}
77
rc = plpks_read_os_var(&var);
78
79
if (rc)
80
goto err;
81
82
*data_size = var.datalen;
83
84
err:
85
kfree(var.name);
86
if (rc && rc != -ENOENT) {
87
pr_err("Failed to read variable '%s': %d\n", key, rc);
88
// Return -EIO since userspace probably doesn't care about the
89
// specific error
90
rc = -EIO;
91
}
92
return rc;
93
}
94
95
static int plpks_set_variable(const char *key, u64 key_len, u8 *data,
96
u64 data_size)
97
{
98
struct plpks_var var = {0};
99
int rc = 0;
100
u64 flags;
101
102
// Secure variables need to be prefixed with 8 bytes of flags.
103
// We only want to perform the write if we have at least one byte of data.
104
if (data_size <= sizeof(flags))
105
return -EINVAL;
106
107
// We subtract 1 from key_len because we don't need to include the
108
// null terminator at the end of the string
109
var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
110
if (!var.name)
111
return -ENOMEM;
112
rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
113
key_len - 1);
114
if (rc < 0)
115
goto err;
116
var.namelen = rc * 2;
117
118
// Flags are contained in the first 8 bytes of the buffer, and are always big-endian
119
flags = be64_to_cpup((__be64 *)data);
120
121
var.datalen = data_size - sizeof(flags);
122
var.data = data + sizeof(flags);
123
var.os = PLPKS_VAR_LINUX;
124
var.policy = get_policy(key);
125
126
// Unlike in the read case, the plpks error code can be useful to
127
// userspace on write, so we return it rather than just -EIO
128
rc = plpks_signed_update_var(&var, flags);
129
130
err:
131
kfree(var.name);
132
return rc;
133
}
134
135
/*
136
* Return the key management mode.
137
*
138
* SB_VERSION is defined as a "1 byte unsigned integer value", taking values
139
* starting from 1. It is owned by the Partition Firmware and its presence
140
* indicates that the key management mode is dynamic. Any failure in
141
* reading SB_VERSION defaults the key management mode to static. The error
142
* codes -ENOENT or -EPERM are expected in static key management mode. An
143
* unexpected error code will have to be investigated. Only signed variables
144
* have null bytes in their names, SB_VERSION does not.
145
*
146
* Return 0 to indicate that the key management mode is static. Otherwise
147
* return the SB_VERSION value to indicate that the key management mode is
148
* dynamic.
149
*/
150
static u8 plpks_get_sb_keymgmt_mode(void)
151
{
152
u8 mode;
153
ssize_t rc;
154
struct plpks_var var = {
155
.component = NULL,
156
.name = "SB_VERSION",
157
.namelen = 10,
158
.datalen = 1,
159
.data = &mode,
160
};
161
162
rc = plpks_read_fw_var(&var);
163
if (rc) {
164
if (rc != -ENOENT && rc != -EPERM)
165
pr_info("Error %ld reading SB_VERSION from firmware\n", rc);
166
mode = 0;
167
}
168
return mode;
169
}
170
171
/*
172
* PLPKS dynamic secure boot doesn't give us a format string in the same way
173
* OPAL does. Instead, report the format using the SB_VERSION variable in the
174
* keystore. The string, made up by us, takes the form of either
175
* "ibm,plpks-sb-v<n>" or "ibm,plpks-sb-v0", based on the key management mode,
176
* and return the length of the secvar format property.
177
*/
178
static ssize_t plpks_secvar_format(char *buf, size_t bufsize)
179
{
180
u8 mode;
181
182
mode = plpks_get_sb_keymgmt_mode();
183
return snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", mode);
184
}
185
186
static int plpks_max_size(u64 *max_size)
187
{
188
// The max object size reported by the hypervisor is accurate for the
189
// object itself, but we use the first 8 bytes of data on write as the
190
// signed update flags, so the max size a user can write is larger.
191
*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);
192
193
return 0;
194
}
195
196
static const struct secvar_operations plpks_secvar_ops_static = {
197
.get = plpks_get_variable,
198
.set = plpks_set_variable,
199
.format = plpks_secvar_format,
200
.max_size = plpks_max_size,
201
.var_names = plpks_var_names_static,
202
};
203
204
static const struct secvar_operations plpks_secvar_ops_dynamic = {
205
.get = plpks_get_variable,
206
.set = plpks_set_variable,
207
.format = plpks_secvar_format,
208
.max_size = plpks_max_size,
209
.var_names = plpks_var_names_dynamic,
210
};
211
212
static int plpks_secvar_init(void)
213
{
214
u8 mode;
215
216
if (!plpks_is_available())
217
return -ENODEV;
218
219
mode = plpks_get_sb_keymgmt_mode();
220
if (mode)
221
return set_secvar_ops(&plpks_secvar_ops_dynamic);
222
return set_secvar_ops(&plpks_secvar_ops_static);
223
}
224
machine_device_initcall(pseries, plpks_secvar_init);
225
226