summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pseries/plpks-secvar.c
blob: 257fd1f8bc191b22e5342040d50191155d2f9db9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// SPDX-License-Identifier: GPL-2.0-only

// Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
//
// Copyright 2022, 2023 IBM Corporation
// Authors: Russell Currey
//          Andrew Donnellan
//          Nayna Jain

#define pr_fmt(fmt) "secvar: "fmt

#include <linux/printk.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/nls.h>
#include <asm/machdep.h>
#include <asm/secvar.h>
#include <asm/plpks.h>

// Config attributes for sysfs
#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
	static ssize_t name##_show(struct kobject *kobj,	\
				   struct kobj_attribute *attr,	\
				   char *buf)			\
	{							\
		return sysfs_emit(buf, fmt, func());		\
	}							\
	static struct kobj_attribute attr_##name = __ATTR_RO(name)

PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);

static const struct attribute *config_attrs[] = {
	&attr_version.attr,
	&attr_max_object_size.attr,
	&attr_total_size.attr,
	&attr_used_space.attr,
	&attr_supported_policies.attr,
	&attr_signed_update_algorithms.attr,
	NULL,
};

static u32 get_policy(const char *name)
{
	if ((strcmp(name, "db") == 0) ||
	    (strcmp(name, "dbx") == 0) ||
	    (strcmp(name, "grubdb") == 0) ||
	    (strcmp(name, "grubdbx") == 0) ||
	    (strcmp(name, "sbat") == 0))
		return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);
	else
		return PLPKS_SIGNEDUPDATE;
}

static const char * const plpks_var_names[] = {
	"PK",
	"KEK",
	"db",
	"dbx",
	"grubdb",
	"grubdbx",
	"sbat",
	"moduledb",
	"trustedcadb",
	NULL,
};

static int plpks_get_variable(const char *key, u64 key_len, u8 *data,
			      u64 *data_size)
{
	struct plpks_var var = {0};
	int rc = 0;

	// We subtract 1 from key_len because we don't need to include the
	// null terminator at the end of the string
	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
	if (!var.name)
		return -ENOMEM;
	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
			     key_len - 1);
	if (rc < 0)
		goto err;
	var.namelen = rc * 2;

	var.os = PLPKS_VAR_LINUX;
	if (data) {
		var.data = data;
		var.datalen = *data_size;
	}
	rc = plpks_read_os_var(&var);

	if (rc)
		goto err;

	*data_size = var.datalen;

err:
	kfree(var.name);
	if (rc && rc != -ENOENT) {
		pr_err("Failed to read variable '%s': %d\n", key, rc);
		// Return -EIO since userspace probably doesn't care about the
		// specific error
		rc = -EIO;
	}
	return rc;
}

static int plpks_set_variable(const char *key, u64 key_len, u8 *data,
			      u64 data_size)
{
	struct plpks_var var = {0};
	int rc = 0;
	u64 flags;

	// Secure variables need to be prefixed with 8 bytes of flags.
	// We only want to perform the write if we have at least one byte of data.
	if (data_size <= sizeof(flags))
		return -EINVAL;

	// We subtract 1 from key_len because we don't need to include the
	// null terminator at the end of the string
	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
	if (!var.name)
		return -ENOMEM;
	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
			     key_len - 1);
	if (rc < 0)
		goto err;
	var.namelen = rc * 2;

	// Flags are contained in the first 8 bytes of the buffer, and are always big-endian
	flags = be64_to_cpup((__be64 *)data);

	var.datalen = data_size - sizeof(flags);
	var.data = data + sizeof(flags);
	var.os = PLPKS_VAR_LINUX;
	var.policy = get_policy(key);

	// Unlike in the read case, the plpks error code can be useful to
	// userspace on write, so we return it rather than just -EIO
	rc = plpks_signed_update_var(&var, flags);

err:
	kfree(var.name);
	return rc;
}

// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
// Instead, report the format using the SB_VERSION variable in the keystore.
// The string is made up by us, and takes the form "ibm,plpks-sb-v<n>" (or "ibm,plpks-sb-unknown"
// if the SB_VERSION variable doesn't exist). Hypervisor defines the SB_VERSION variable as a
// "1 byte unsigned integer value".
static ssize_t plpks_secvar_format(char *buf, size_t bufsize)
{
	struct plpks_var var = {0};
	ssize_t ret;
	u8 version;

	var.component = NULL;
	// Only the signed variables have null bytes in their names, this one doesn't
	var.name = "SB_VERSION";
	var.namelen = strlen(var.name);
	var.datalen = 1;
	var.data = &version;

	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
	ret = plpks_read_fw_var(&var);
	if (ret) {
		if (ret == -ENOENT) {
			ret = snprintf(buf, bufsize, "ibm,plpks-sb-unknown");
		} else {
			pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
			ret = -EIO;
		}
		goto err;
	}

	ret = snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", version);
err:
	return ret;
}

static int plpks_max_size(u64 *max_size)
{
	// The max object size reported by the hypervisor is accurate for the
	// object itself, but we use the first 8 bytes of data on write as the
	// signed update flags, so the max size a user can write is larger.
	*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);

	return 0;
}


static const struct secvar_operations plpks_secvar_ops = {
	.get = plpks_get_variable,
	.set = plpks_set_variable,
	.format = plpks_secvar_format,
	.max_size = plpks_max_size,
	.config_attrs = config_attrs,
	.var_names = plpks_var_names,
};

static int plpks_secvar_init(void)
{
	if (!plpks_is_available())
		return -ENODEV;

	return set_secvar_ops(&plpks_secvar_ops);
}
machine_device_initcall(pseries, plpks_secvar_init);