// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 IS_REACHABLE(CONFIG_TCG_TPM) { "tpm", &trusted_key_tpm_ops }, #endif #if IS_REACHABLE(CONFIG_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");