mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

Provide and pass the xflag parameter from pkey ioctls through the pkey handler and further down to the implementations (CCA, EP11, PCKMO and UV). So all the code is now prepared and ready to support xflags ("execution flag"). The pkey layer supports the xflag PKEY_XFLAG_NOMEMALLOC: If this flag is given in the xflags parameter, the pkey implementation is not allowed to allocate memory but instead should fall back to use preallocated memory or simple fail with -ENOMEM. This flag is for protected key derive within a cipher or similar which must not allocate memory which would cause io operations - see also the CRYPTO_ALG_ALLOCATES_MEMORY flag in crypto.h. Within the pkey handlers this flag is then to be translated to appropriate zcrypt xflags before any zcrypt related functions are called. So the PKEY_XFLAG_NOMEMALLOC translates to ZCRYPT_XFLAG_NOMEMALLOC - If this flag is set, no memory allocations which may trigger any IO operations are done. The pkey in-kernel pkey API still does not provide this xflag param. That's intended to come with a separate patch which enables this functionality. Signed-off-by: Harald Freudenberger <freude@linux.ibm.com> Reviewed-by: Holger Dengler <dengler@linux.ibm.com> Link: https://lore.kernel.org/r/20250424133619.16495-25-freude@linux.ibm.com Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
381 lines
8.3 KiB
C
381 lines
8.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* pkey base: debug feature, pkey handler registry
|
|
*
|
|
* Copyright IBM Corp. 2024
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "pkey"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <linux/cpufeature.h>
|
|
#include <linux/init.h>
|
|
#include <linux/list.h>
|
|
#include <linux/module.h>
|
|
#include <linux/rculist.h>
|
|
|
|
#include "pkey_base.h"
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("IBM Corporation");
|
|
MODULE_DESCRIPTION("s390 protected key base and api");
|
|
|
|
/*
|
|
* pkey debug feature
|
|
*/
|
|
debug_info_t *pkey_dbf_info;
|
|
EXPORT_SYMBOL(pkey_dbf_info);
|
|
|
|
/*
|
|
* pkey handler registry
|
|
*/
|
|
|
|
static DEFINE_SPINLOCK(handler_list_write_lock);
|
|
static LIST_HEAD(handler_list);
|
|
|
|
int pkey_handler_register(struct pkey_handler *handler)
|
|
{
|
|
const struct pkey_handler *h;
|
|
|
|
if (!handler ||
|
|
!handler->is_supported_key ||
|
|
!handler->is_supported_keytype)
|
|
return -EINVAL;
|
|
|
|
if (!try_module_get(handler->module))
|
|
return -ENXIO;
|
|
|
|
spin_lock(&handler_list_write_lock);
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(h, &handler_list, list) {
|
|
if (h == handler) {
|
|
rcu_read_unlock();
|
|
spin_unlock(&handler_list_write_lock);
|
|
module_put(handler->module);
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
list_add_rcu(&handler->list, &handler_list);
|
|
spin_unlock(&handler_list_write_lock);
|
|
synchronize_rcu();
|
|
|
|
module_put(handler->module);
|
|
|
|
PKEY_DBF_INFO("%s pkey handler '%s' registered\n", __func__,
|
|
handler->name ?: "<no name>");
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_register);
|
|
|
|
int pkey_handler_unregister(struct pkey_handler *handler)
|
|
{
|
|
spin_lock(&handler_list_write_lock);
|
|
list_del_rcu(&handler->list);
|
|
INIT_LIST_HEAD_RCU(&handler->list);
|
|
spin_unlock(&handler_list_write_lock);
|
|
synchronize_rcu();
|
|
|
|
PKEY_DBF_INFO("%s pkey handler '%s' unregistered\n", __func__,
|
|
handler->name ?: "<no name>");
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_unregister);
|
|
|
|
/*
|
|
* Handler invocation functions.
|
|
*/
|
|
|
|
const struct pkey_handler *pkey_handler_get_keybased(const u8 *key, u32 keylen)
|
|
{
|
|
const struct pkey_handler *h;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(h, &handler_list, list) {
|
|
if (!try_module_get(h->module))
|
|
continue;
|
|
if (h->is_supported_key(key, keylen)) {
|
|
rcu_read_unlock();
|
|
return h;
|
|
}
|
|
module_put(h->module);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_get_keybased);
|
|
|
|
const struct pkey_handler *pkey_handler_get_keytypebased(enum pkey_key_type kt)
|
|
{
|
|
const struct pkey_handler *h;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(h, &handler_list, list) {
|
|
if (!try_module_get(h->module))
|
|
continue;
|
|
if (h->is_supported_keytype(kt)) {
|
|
rcu_read_unlock();
|
|
return h;
|
|
}
|
|
module_put(h->module);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_get_keytypebased);
|
|
|
|
void pkey_handler_put(const struct pkey_handler *handler)
|
|
{
|
|
const struct pkey_handler *h;
|
|
|
|
if (!handler)
|
|
return;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(h, &handler_list, list) {
|
|
if (h == handler) {
|
|
module_put(h->module);
|
|
break;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_put);
|
|
|
|
int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
|
|
const u8 *key, u32 keylen,
|
|
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
|
|
u32 xflags)
|
|
{
|
|
const struct pkey_handler *h;
|
|
int rc = -ENODEV;
|
|
|
|
h = pkey_handler_get_keybased(key, keylen);
|
|
if (h && h->key_to_protkey) {
|
|
rc = h->key_to_protkey(apqns, nr_apqns, key, keylen,
|
|
protkey, protkeylen,
|
|
protkeytype, xflags);
|
|
}
|
|
pkey_handler_put(h);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_key_to_protkey);
|
|
|
|
/*
|
|
* This handler invocation is special as there may be more than
|
|
* one handler providing support for the very same key (type).
|
|
* And the handler may not respond true on is_supported_key(),
|
|
* so simple try and check return value here.
|
|
*/
|
|
int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns,
|
|
size_t nr_apqns,
|
|
const u8 *key, u32 keylen,
|
|
u8 *protkey, u32 *protkeylen,
|
|
u32 *protkeytype, u32 xflags)
|
|
{
|
|
const struct pkey_handler *h, *htmp[10];
|
|
int i, n = 0, rc = -ENODEV;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(h, &handler_list, list) {
|
|
if (!try_module_get(h->module))
|
|
continue;
|
|
if (h->slowpath_key_to_protkey && n < ARRAY_SIZE(htmp))
|
|
htmp[n++] = h;
|
|
else
|
|
module_put(h->module);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
for (i = 0; i < n; i++) {
|
|
h = htmp[i];
|
|
if (rc)
|
|
rc = h->slowpath_key_to_protkey(apqns, nr_apqns,
|
|
key, keylen,
|
|
protkey, protkeylen,
|
|
protkeytype, xflags);
|
|
module_put(h->module);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_slowpath_key_to_protkey);
|
|
|
|
int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
|
|
u32 keytype, u32 keysubtype,
|
|
u32 keybitsize, u32 flags,
|
|
u8 *keybuf, u32 *keybuflen, u32 *keyinfo, u32 xflags)
|
|
{
|
|
const struct pkey_handler *h;
|
|
int rc = -ENODEV;
|
|
|
|
h = pkey_handler_get_keytypebased(keysubtype);
|
|
if (h && h->gen_key) {
|
|
rc = h->gen_key(apqns, nr_apqns, keytype, keysubtype,
|
|
keybitsize, flags,
|
|
keybuf, keybuflen, keyinfo, xflags);
|
|
}
|
|
pkey_handler_put(h);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_gen_key);
|
|
|
|
int pkey_handler_clr_to_key(const struct pkey_apqn *apqns, size_t nr_apqns,
|
|
u32 keytype, u32 keysubtype,
|
|
u32 keybitsize, u32 flags,
|
|
const u8 *clrkey, u32 clrkeylen,
|
|
u8 *keybuf, u32 *keybuflen, u32 *keyinfo,
|
|
u32 xflags)
|
|
{
|
|
const struct pkey_handler *h;
|
|
int rc = -ENODEV;
|
|
|
|
h = pkey_handler_get_keytypebased(keysubtype);
|
|
if (h && h->clr_to_key) {
|
|
rc = h->clr_to_key(apqns, nr_apqns, keytype, keysubtype,
|
|
keybitsize, flags, clrkey, clrkeylen,
|
|
keybuf, keybuflen, keyinfo, xflags);
|
|
}
|
|
pkey_handler_put(h);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_clr_to_key);
|
|
|
|
int pkey_handler_verify_key(const u8 *key, u32 keylen,
|
|
u16 *card, u16 *dom,
|
|
u32 *keytype, u32 *keybitsize, u32 *flags,
|
|
u32 xflags)
|
|
{
|
|
const struct pkey_handler *h;
|
|
int rc = -ENODEV;
|
|
|
|
h = pkey_handler_get_keybased(key, keylen);
|
|
if (h && h->verify_key) {
|
|
rc = h->verify_key(key, keylen, card, dom,
|
|
keytype, keybitsize, flags, xflags);
|
|
}
|
|
pkey_handler_put(h);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_verify_key);
|
|
|
|
int pkey_handler_apqns_for_key(const u8 *key, u32 keylen, u32 flags,
|
|
struct pkey_apqn *apqns, size_t *nr_apqns,
|
|
u32 xflags)
|
|
{
|
|
const struct pkey_handler *h;
|
|
int rc = -ENODEV;
|
|
|
|
h = pkey_handler_get_keybased(key, keylen);
|
|
if (h && h->apqns_for_key)
|
|
rc = h->apqns_for_key(key, keylen, flags, apqns, nr_apqns,
|
|
xflags);
|
|
pkey_handler_put(h);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_apqns_for_key);
|
|
|
|
int pkey_handler_apqns_for_keytype(enum pkey_key_type keysubtype,
|
|
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
|
|
struct pkey_apqn *apqns, size_t *nr_apqns,
|
|
u32 xflags)
|
|
{
|
|
const struct pkey_handler *h;
|
|
int rc = -ENODEV;
|
|
|
|
h = pkey_handler_get_keytypebased(keysubtype);
|
|
if (h && h->apqns_for_keytype) {
|
|
rc = h->apqns_for_keytype(keysubtype,
|
|
cur_mkvp, alt_mkvp, flags,
|
|
apqns, nr_apqns, xflags);
|
|
}
|
|
pkey_handler_put(h);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_apqns_for_keytype);
|
|
|
|
void pkey_handler_request_modules(void)
|
|
{
|
|
#ifdef CONFIG_MODULES
|
|
static const char * const pkey_handler_modules[] = {
|
|
#if IS_MODULE(CONFIG_PKEY_CCA)
|
|
"pkey_cca",
|
|
#endif
|
|
#if IS_MODULE(CONFIG_PKEY_EP11)
|
|
"pkey_ep11",
|
|
#endif
|
|
#if IS_MODULE(CONFIG_PKEY_PCKMO)
|
|
"pkey_pckmo",
|
|
#endif
|
|
#if IS_MODULE(CONFIG_PKEY_UV)
|
|
"pkey_uv",
|
|
#endif
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(pkey_handler_modules); i++) {
|
|
const struct pkey_handler *h;
|
|
bool found = false;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(h, &handler_list, list) {
|
|
if (h->module &&
|
|
!strcmp(h->module->name, pkey_handler_modules[i])) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
if (!found) {
|
|
pr_debug("request_module(%s)\n", pkey_handler_modules[i]);
|
|
request_module(pkey_handler_modules[i]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
EXPORT_SYMBOL(pkey_handler_request_modules);
|
|
|
|
/*
|
|
* Module init
|
|
*/
|
|
static int __init pkey_init(void)
|
|
{
|
|
int rc;
|
|
|
|
/* init debug feature */
|
|
pkey_dbf_info = debug_register("pkey", 1, 1, 5 * sizeof(long));
|
|
debug_register_view(pkey_dbf_info, &debug_sprintf_view);
|
|
debug_set_level(pkey_dbf_info, 4);
|
|
|
|
/* the handler registry does not need any init */
|
|
|
|
rc = pkey_api_init();
|
|
if (rc)
|
|
debug_unregister(pkey_dbf_info);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Module exit
|
|
*/
|
|
static void __exit pkey_exit(void)
|
|
{
|
|
pkey_api_exit();
|
|
}
|
|
|
|
module_cpu_feature_match(S390_CPU_FEATURE_MSA, pkey_init);
|
|
module_exit(pkey_exit);
|