mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-18 19:18:57 +00: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>
319 lines
6.9 KiB
C
319 lines
6.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* pkey uv specific code
|
|
*
|
|
* 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/module.h>
|
|
#include <asm/uv.h>
|
|
|
|
#include "zcrypt_ccamisc.h"
|
|
#include "pkey_base.h"
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("IBM Corporation");
|
|
MODULE_DESCRIPTION("s390 protected key UV handler");
|
|
|
|
/*
|
|
* One pre-allocated uv_secret_list for use with uv_find_secret()
|
|
*/
|
|
static struct uv_secret_list *uv_list;
|
|
static DEFINE_MUTEX(uv_list_mutex);
|
|
|
|
/*
|
|
* UV secret token struct and defines.
|
|
*/
|
|
|
|
#define TOKVER_UV_SECRET 0x09
|
|
|
|
struct uvsecrettoken {
|
|
u8 type; /* 0x00 = TOKTYPE_NON_CCA */
|
|
u8 res0[3];
|
|
u8 version; /* 0x09 = TOKVER_UV_SECRET */
|
|
u8 res1[3];
|
|
u16 secret_type; /* one of enum uv_secret_types from uv.h */
|
|
u16 secret_len; /* length in bytes of the secret */
|
|
u8 secret_id[UV_SECRET_ID_LEN]; /* the secret id for this secret */
|
|
} __packed;
|
|
|
|
/*
|
|
* Check key blob for known and supported UV key.
|
|
*/
|
|
static bool is_uv_key(const u8 *key, u32 keylen)
|
|
{
|
|
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
|
|
|
|
if (keylen < sizeof(*t))
|
|
return false;
|
|
|
|
switch (t->type) {
|
|
case TOKTYPE_NON_CCA:
|
|
switch (t->version) {
|
|
case TOKVER_UV_SECRET:
|
|
switch (t->secret_type) {
|
|
case UV_SECRET_AES_128:
|
|
case UV_SECRET_AES_192:
|
|
case UV_SECRET_AES_256:
|
|
case UV_SECRET_AES_XTS_128:
|
|
case UV_SECRET_AES_XTS_256:
|
|
case UV_SECRET_HMAC_SHA_256:
|
|
case UV_SECRET_HMAC_SHA_512:
|
|
case UV_SECRET_ECDSA_P256:
|
|
case UV_SECRET_ECDSA_P384:
|
|
case UV_SECRET_ECDSA_P521:
|
|
case UV_SECRET_ECDSA_ED25519:
|
|
case UV_SECRET_ECDSA_ED448:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_uv_keytype(enum pkey_key_type keytype)
|
|
{
|
|
switch (keytype) {
|
|
case PKEY_TYPE_UVSECRET:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int get_secret_metadata(const u8 secret_id[UV_SECRET_ID_LEN],
|
|
struct uv_secret_list_item_hdr *secret)
|
|
{
|
|
int rc;
|
|
|
|
mutex_lock(&uv_list_mutex);
|
|
memset(uv_list, 0, sizeof(*uv_list));
|
|
rc = uv_find_secret(secret_id, uv_list, secret);
|
|
mutex_unlock(&uv_list_mutex);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int retrieve_secret(const u8 secret_id[UV_SECRET_ID_LEN],
|
|
u16 *secret_type, u8 *buf, u32 *buflen)
|
|
{
|
|
struct uv_secret_list_item_hdr secret_meta_data;
|
|
int rc;
|
|
|
|
rc = get_secret_metadata(secret_id, &secret_meta_data);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (*buflen < secret_meta_data.length)
|
|
return -EINVAL;
|
|
|
|
rc = uv_retrieve_secret(secret_meta_data.index,
|
|
buf, secret_meta_data.length);
|
|
if (rc)
|
|
return rc;
|
|
|
|
*secret_type = secret_meta_data.type;
|
|
*buflen = secret_meta_data.length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int uv_get_size_and_type(u16 secret_type, u32 *pkeysize, u32 *pkeytype)
|
|
{
|
|
int rc = 0;
|
|
|
|
switch (secret_type) {
|
|
case UV_SECRET_AES_128:
|
|
*pkeysize = 16 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_AES_128;
|
|
break;
|
|
case UV_SECRET_AES_192:
|
|
*pkeysize = 24 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_AES_192;
|
|
break;
|
|
case UV_SECRET_AES_256:
|
|
*pkeysize = 32 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_AES_256;
|
|
break;
|
|
case UV_SECRET_AES_XTS_128:
|
|
*pkeysize = 16 + 16 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_AES_XTS_128;
|
|
break;
|
|
case UV_SECRET_AES_XTS_256:
|
|
*pkeysize = 32 + 32 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_AES_XTS_256;
|
|
break;
|
|
case UV_SECRET_HMAC_SHA_256:
|
|
*pkeysize = 64 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_HMAC_512;
|
|
break;
|
|
case UV_SECRET_HMAC_SHA_512:
|
|
*pkeysize = 128 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_HMAC_1024;
|
|
break;
|
|
case UV_SECRET_ECDSA_P256:
|
|
*pkeysize = 32 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_ECC_P256;
|
|
break;
|
|
case UV_SECRET_ECDSA_P384:
|
|
*pkeysize = 48 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_ECC_P384;
|
|
break;
|
|
case UV_SECRET_ECDSA_P521:
|
|
*pkeysize = 80 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_ECC_P521;
|
|
break;
|
|
case UV_SECRET_ECDSA_ED25519:
|
|
*pkeysize = 32 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_ECC_ED25519;
|
|
break;
|
|
case UV_SECRET_ECDSA_ED448:
|
|
*pkeysize = 64 + AES_WK_VP_SIZE;
|
|
*pkeytype = PKEY_KEYTYPE_ECC_ED448;
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int uv_key2protkey(const struct pkey_apqn *_apqns __always_unused,
|
|
size_t _nr_apqns __always_unused,
|
|
const u8 *key, u32 keylen,
|
|
u8 *protkey, u32 *protkeylen, u32 *keyinfo,
|
|
u32 _xflags __always_unused)
|
|
{
|
|
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
|
|
u32 pkeysize, pkeytype;
|
|
u16 secret_type;
|
|
int rc;
|
|
|
|
rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
|
|
if (rc)
|
|
goto out;
|
|
|
|
if (*protkeylen < pkeysize) {
|
|
PKEY_DBF_ERR("%s prot key buffer size too small: %u < %u\n",
|
|
__func__, *protkeylen, pkeysize);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
rc = retrieve_secret(t->secret_id, &secret_type, protkey, protkeylen);
|
|
if (rc) {
|
|
PKEY_DBF_ERR("%s retrieve_secret() failed with %d\n",
|
|
__func__, rc);
|
|
goto out;
|
|
}
|
|
if (secret_type != t->secret_type) {
|
|
PKEY_DBF_ERR("%s retrieved secret type %u != expected type %u\n",
|
|
__func__, secret_type, t->secret_type);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (keyinfo)
|
|
*keyinfo = pkeytype;
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int uv_verifykey(const u8 *key, u32 keylen,
|
|
u16 *_card __always_unused,
|
|
u16 *_dom __always_unused,
|
|
u32 *keytype, u32 *keybitsize, u32 *flags,
|
|
u32 xflags __always_unused)
|
|
{
|
|
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
|
|
struct uv_secret_list_item_hdr secret_meta_data;
|
|
u32 pkeysize, pkeytype, bitsize;
|
|
int rc;
|
|
|
|
rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
|
|
if (rc)
|
|
goto out;
|
|
|
|
rc = get_secret_metadata(t->secret_id, &secret_meta_data);
|
|
if (rc)
|
|
goto out;
|
|
|
|
if (secret_meta_data.type != t->secret_type) {
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* set keytype; keybitsize and flags are not supported */
|
|
if (keytype)
|
|
*keytype = PKEY_TYPE_UVSECRET;
|
|
if (keybitsize) {
|
|
bitsize = 8 * pkey_keytype_to_size(pkeytype);
|
|
*keybitsize = bitsize ?: PKEY_SIZE_UNKNOWN;
|
|
}
|
|
if (flags)
|
|
*flags = pkeytype;
|
|
|
|
out:
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static struct pkey_handler uv_handler = {
|
|
.module = THIS_MODULE,
|
|
.name = "PKEY UV handler",
|
|
.is_supported_key = is_uv_key,
|
|
.is_supported_keytype = is_uv_keytype,
|
|
.key_to_protkey = uv_key2protkey,
|
|
.verify_key = uv_verifykey,
|
|
};
|
|
|
|
/*
|
|
* Module init
|
|
*/
|
|
static int __init pkey_uv_init(void)
|
|
{
|
|
int rc;
|
|
|
|
if (!is_prot_virt_guest())
|
|
return -ENODEV;
|
|
|
|
if (!test_bit_inv(BIT_UVC_CMD_RETR_SECRET, uv_info.inst_calls_list))
|
|
return -ENODEV;
|
|
|
|
uv_list = kmalloc(sizeof(*uv_list), GFP_KERNEL);
|
|
if (!uv_list)
|
|
return -ENOMEM;
|
|
|
|
rc = pkey_handler_register(&uv_handler);
|
|
if (rc)
|
|
kfree(uv_list);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Module exit
|
|
*/
|
|
static void __exit pkey_uv_exit(void)
|
|
{
|
|
pkey_handler_unregister(&uv_handler);
|
|
mutex_lock(&uv_list_mutex);
|
|
kvfree(uv_list);
|
|
mutex_unlock(&uv_list_mutex);
|
|
}
|
|
|
|
module_cpu_feature_match(S390_CPU_FEATURE_UV, pkey_uv_init);
|
|
module_exit(pkey_uv_exit);
|