Files
linux_media/security/keys/trusted-keys/trusted_core.c
Ahmad Fatoum be07858fbf KEYS: trusted: allow use of TEE as backend without TCG_TPM support
With recent rework, trusted keys are no longer limited to TPM as trust
source. The Kconfig symbol is unchanged however leading to a few issues:

  - TCG_TPM is required, even if only TEE is to be used
  - Enabling TCG_TPM, but excluding it from available trusted sources
    is not possible
  - TEE=m && TRUSTED_KEYS=y will lead to TEE support being silently
    dropped, which is not the best user experience

Remedy these issues by introducing two new boolean Kconfig symbols:
TRUSTED_KEYS_TPM and TRUSTED_KEYS_TEE with the appropriate
dependencies.

Any new code depending on the TPM trusted key backend in particular
or symbols exported by it will now need to explicitly state that it

  depends on TRUSTED_KEYS && TRUSTED_KEYS_TPM

The latter to ensure the dependency is built and the former to ensure
it's reachable for module builds. There are no such users yet.

Reviewed-by: Sumit Garg <sumit.garg@linaro.org>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Reviewed-by: Pankaj Gupta <pankaj.gupta@nxp.com>
Tested-by: Pankaj Gupta <pankaj.gupta@nxp.com>
Tested-by: Andreas Rammhold <andreas@rammhold.de>
Tested-by: Tim Harvey <tharvey@gateworks.com>
Tested-by: Michael Walle <michael@walle.cc> # on ls1028a (non-E and E)
Tested-by: John Ernberg <john.ernberg@actia.se> # iMX8QXP
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
2022-05-23 18:47:50 +03:00

361 lines
8.1 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_tpm.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/rcupdate.h>
#include <linux/slab.h>
#include <linux/static_call.h>
#include <linux/string.h>
#include <linux/uaccess.h>
static char *trusted_key_source;
module_param_named(source, trusted_key_source, charp, 0);
MODULE_PARM_DESC(source, "Select trusted keys source (tpm or tee)");
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
};
DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init);
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);
DEFINE_STATIC_CALL_NULL(trusted_key_exit, *trusted_key_sources[0].ops->exit);
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 __init init_trusted(void)
{
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;
static_call_update(trusted_key_init,
trusted_key_sources[i].ops->init);
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,
trusted_key_sources[i].ops->get_random);
static_call_update(trusted_key_exit,
trusted_key_sources[i].ops->exit);
migratable = trusted_key_sources[i].ops->migratable;
ret = static_call(trusted_key_init)();
if (!ret)
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)
{
static_call_cond(trusted_key_exit)();
}
late_initcall(init_trusted);
module_exit(cleanup_trusted);
MODULE_LICENSE("GPL");