Files
linux-kernelorg-stable/security/keys/trusted-keys/trusted_core.c
T
Srish Srinivasan c99fcb0d73 keys/trusted_keys: establish PKWM as a trusted source
The wrapping key does not exist by default and is generated by the
hypervisor as a part of PKWM initialization. This key is then persisted by
the hypervisor and is used to wrap trusted keys. These are variable length
symmetric keys, which in the case of PowerVM Key Wrapping Module (PKWM) are
generated using the kernel RNG. PKWM can be used as a trust source through
the following example keyctl commands:

keyctl add trusted my_trusted_key "new 32" @u

Use the wrap_flags command option to set the secure boot requirement for
the wrapping request through the following keyctl commands

case1: no secure boot requirement. (default)
keyctl usage: keyctl add trusted my_trusted_key "new 32" @u
	      OR
	      keyctl add trusted my_trusted_key "new 32 wrap_flags=0x00" @u

case2: secure boot required to in either audit or enforce mode. set bit 0
keyctl usage: keyctl add trusted my_trusted_key "new 32 wrap_flags=0x01" @u

case3: secure boot required to be in enforce mode. set bit 1
keyctl usage: keyctl add trusted my_trusted_key "new 32 wrap_flags=0x02" @u

NOTE:
-> Setting the secure boot requirement is NOT a must.
-> Only either of the secure boot requirement options should be set. Not
both.
-> All the other bits are required to be not set.
-> Set the kernel parameter trusted.source=pkwm to choose PKWM as the
backend for trusted keys implementation.
-> CONFIG_PSERIES_PLPKS must be enabled to build PKWM.

Add PKWM, which is a combination of IBM PowerVM and Power LPAR Platform
KeyStore, as a new trust source for trusted keys.

Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Tested-by: Nayna Jain <nayna@linux.ibm.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Link: https://patch.msgid.link/20260127145228.48320-6-ssrish@linux.ibm.com
2026-01-30 09:27:26 +05:30

404 lines
9.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
* Copyright (c) 2019-2021, Linaro Limited
*
* See Documentation/security/keys/trusted-encrypted.rst
*/
#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <keys/trusted_tee.h>
#include <keys/trusted_caam.h>
#include <keys/trusted_dcp.h>
#include <keys/trusted_tpm.h>
#include <keys/trusted_pkwm.h>
#include <linux/capability.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/key-type.h>
#include <linux/module.h>
#include <linux/parser.h>
#include <linux/random.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/static_call.h>
#include <linux/string.h>
#include <linux/uaccess.h>
static char *trusted_rng = "default";
module_param_named(rng, trusted_rng, charp, 0);
MODULE_PARM_DESC(rng, "Select trusted key RNG");
static char *trusted_key_source;
module_param_named(source, trusted_key_source, charp, 0);
MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam, dcp or pkwm)");
static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_TPM)
{ "tpm", &trusted_key_tpm_ops },
#endif
#if defined(CONFIG_TRUSTED_KEYS_TEE)
{ "tee", &trusted_key_tee_ops },
#endif
#if defined(CONFIG_TRUSTED_KEYS_CAAM)
{ "caam", &trusted_key_caam_ops },
#endif
#if defined(CONFIG_TRUSTED_KEYS_DCP)
{ "dcp", &dcp_trusted_key_ops },
#endif
#if defined(CONFIG_TRUSTED_KEYS_PKWM)
{ "pkwm", &pkwm_trusted_key_ops },
#endif
};
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
DEFINE_STATIC_CALL_NULL(trusted_key_unseal,
*trusted_key_sources[0].ops->unseal);
DEFINE_STATIC_CALL_NULL(trusted_key_get_random,
*trusted_key_sources[0].ops->get_random);
static void (*trusted_key_exit)(void);
static unsigned char migratable;
enum {
Opt_err,
Opt_new, Opt_load, Opt_update,
};
static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
{Opt_update, "update"},
{Opt_err, NULL}
};
/*
* datablob_parse - parse the keyctl data and fill in the
* payload structure
*
* On success returns 0, otherwise -EINVAL.
*/
static int datablob_parse(char **datablob, struct trusted_key_payload *p)
{
substring_t args[MAX_OPT_ARGS];
long keylen;
int ret = -EINVAL;
int key_cmd;
char *c;
/* main command */
c = strsep(datablob, " \t");
if (!c)
return -EINVAL;
key_cmd = match_token(c, key_tokens, args);
switch (key_cmd) {
case Opt_new:
/* first argument is key size */
c = strsep(datablob, " \t");
if (!c)
return -EINVAL;
ret = kstrtol(c, 10, &keylen);
if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
return -EINVAL;
p->key_len = keylen;
ret = Opt_new;
break;
case Opt_load:
/* first argument is sealed blob */
c = strsep(datablob, " \t");
if (!c)
return -EINVAL;
p->blob_len = strlen(c) / 2;
if (p->blob_len > MAX_BLOB_SIZE)
return -EINVAL;
ret = hex2bin(p->blob, c, p->blob_len);
if (ret < 0)
return -EINVAL;
ret = Opt_load;
break;
case Opt_update:
ret = Opt_update;
break;
case Opt_err:
return -EINVAL;
}
return ret;
}
static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
{
struct trusted_key_payload *p = NULL;
int ret;
ret = key_payload_reserve(key, sizeof(*p));
if (ret < 0)
goto err;
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
goto err;
p->migratable = migratable;
err:
return p;
}
/*
* trusted_instantiate - create a new trusted key
*
* Unseal an existing trusted blob or, for a new key, get a
* random key, then seal and create a trusted key-type key,
* adding it to the specified keyring.
*
* On success, return 0. Otherwise return errno.
*/
static int trusted_instantiate(struct key *key,
struct key_preparsed_payload *prep)
{
struct trusted_key_payload *payload = NULL;
size_t datalen = prep->datalen;
char *datablob, *orig_datablob;
int ret = 0;
int key_cmd;
size_t key_len;
if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
memcpy(datablob, prep->data, datalen);
datablob[datalen] = '\0';
payload = trusted_payload_alloc(key);
if (!payload) {
ret = -ENOMEM;
goto out;
}
key_cmd = datablob_parse(&datablob, payload);
if (key_cmd < 0) {
ret = key_cmd;
goto out;
}
dump_payload(payload);
switch (key_cmd) {
case Opt_load:
ret = static_call(trusted_key_unseal)(payload, datablob);
dump_payload(payload);
if (ret < 0)
pr_info("key_unseal failed (%d)\n", ret);
break;
case Opt_new:
key_len = payload->key_len;
ret = static_call(trusted_key_get_random)(payload->key,
key_len);
if (ret < 0)
goto out;
if (ret != key_len) {
pr_info("key_create failed (%d)\n", ret);
ret = -EIO;
goto out;
}
ret = static_call(trusted_key_seal)(payload, datablob);
if (ret < 0)
pr_info("key_seal failed (%d)\n", ret);
break;
default:
ret = -EINVAL;
}
out:
kfree_sensitive(orig_datablob);
if (!ret)
rcu_assign_keypointer(key, payload);
else
kfree_sensitive(payload);
return ret;
}
static void trusted_rcu_free(struct rcu_head *rcu)
{
struct trusted_key_payload *p;
p = container_of(rcu, struct trusted_key_payload, rcu);
kfree_sensitive(p);
}
/*
* trusted_update - reseal an existing key with new PCR values
*/
static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
{
struct trusted_key_payload *p;
struct trusted_key_payload *new_p;
size_t datalen = prep->datalen;
char *datablob, *orig_datablob;
int ret = 0;
if (key_is_negative(key))
return -ENOKEY;
p = key->payload.data[0];
if (!p->migratable)
return -EPERM;
if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
new_p = trusted_payload_alloc(key);
if (!new_p) {
ret = -ENOMEM;
goto out;
}
memcpy(datablob, prep->data, datalen);
datablob[datalen] = '\0';
ret = datablob_parse(&datablob, new_p);
if (ret != Opt_update) {
ret = -EINVAL;
kfree_sensitive(new_p);
goto out;
}
/* copy old key values, and reseal with new pcrs */
new_p->migratable = p->migratable;
new_p->key_len = p->key_len;
memcpy(new_p->key, p->key, p->key_len);
dump_payload(p);
dump_payload(new_p);
ret = static_call(trusted_key_seal)(new_p, datablob);
if (ret < 0) {
pr_info("key_seal failed (%d)\n", ret);
kfree_sensitive(new_p);
goto out;
}
rcu_assign_keypointer(key, new_p);
call_rcu(&p->rcu, trusted_rcu_free);
out:
kfree_sensitive(orig_datablob);
return ret;
}
/*
* trusted_read - copy the sealed blob data to userspace in hex.
* On success, return to userspace the trusted key datablob size.
*/
static long trusted_read(const struct key *key, char *buffer,
size_t buflen)
{
const struct trusted_key_payload *p;
char *bufp;
int i;
p = dereference_key_locked(key);
if (!p)
return -EINVAL;
if (buffer && buflen >= 2 * p->blob_len) {
bufp = buffer;
for (i = 0; i < p->blob_len; i++)
bufp = hex_byte_pack(bufp, p->blob[i]);
}
return 2 * p->blob_len;
}
/*
* trusted_destroy - clear and free the key's payload
*/
static void trusted_destroy(struct key *key)
{
kfree_sensitive(key->payload.data[0]);
}
struct key_type key_type_trusted = {
.name = "trusted",
.instantiate = trusted_instantiate,
.update = trusted_update,
.destroy = trusted_destroy,
.describe = user_describe,
.read = trusted_read,
};
EXPORT_SYMBOL_GPL(key_type_trusted);
static int kernel_get_random(unsigned char *key, size_t key_len)
{
return get_random_bytes_wait(key, key_len) ?: key_len;
}
static int __init init_trusted(void)
{
int (*get_random)(unsigned char *key, size_t key_len);
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(trusted_key_sources); i++) {
if (trusted_key_source &&
strncmp(trusted_key_source, trusted_key_sources[i].name,
strlen(trusted_key_sources[i].name)))
continue;
/*
* We always support trusted.rng="kernel" and "default" as
* well as trusted.rng=$trusted.source if the trust source
* defines its own get_random callback.
*/
get_random = trusted_key_sources[i].ops->get_random;
if (trusted_rng && strcmp(trusted_rng, "default")) {
if (!strcmp(trusted_rng, "kernel")) {
get_random = kernel_get_random;
} else if (strcmp(trusted_rng, trusted_key_sources[i].name) ||
!get_random) {
pr_warn("Unsupported RNG. Supported: kernel");
if (get_random)
pr_cont(", %s", trusted_key_sources[i].name);
pr_cont(", default\n");
return -EINVAL;
}
}
if (!get_random)
get_random = kernel_get_random;
ret = trusted_key_sources[i].ops->init();
if (!ret) {
static_call_update(trusted_key_seal, trusted_key_sources[i].ops->seal);
static_call_update(trusted_key_unseal, trusted_key_sources[i].ops->unseal);
static_call_update(trusted_key_get_random, get_random);
trusted_key_exit = trusted_key_sources[i].ops->exit;
migratable = trusted_key_sources[i].ops->migratable;
}
if (!ret || ret != -ENODEV)
break;
}
/*
* encrypted_keys.ko depends on successful load of this module even if
* trusted key implementation is not found.
*/
if (ret == -ENODEV)
return 0;
return ret;
}
static void __exit cleanup_trusted(void)
{
if (trusted_key_exit)
(*trusted_key_exit)();
}
late_initcall(init_trusted);
module_exit(cleanup_trusted);
MODULE_DESCRIPTION("Trusted Key type");
MODULE_LICENSE("GPL");