// SPDX-License-Identifier: GPL-2.0
/*
* pkey device driver
*
* Copyright IBM Corp. 2017, 2023
*
* Author(s): Harald Freudenberger
*/
#define pr_fmt(fmt) "pkey: " fmt
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/export.h>
#include <linux/slab.h>
#include "zcrypt_api.h"
#include "zcrypt_ccamisc.h"
#include "pkey_base.h"
/*
* Helper functions
*/
static int key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, size_t keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype,
u32 xflags)
{
int rc;
/* try the direct way */
rc = pkey_handler_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype, xflags);
/* if this did not work, try the slowpath way */
if (rc == -ENODEV) {
rc = pkey_handler_slowpath_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype, xflags);
if (rc)
rc = -ENODEV;
}
pr_debug("rc=%d\n", rc);
return rc;
}
/*
* In-Kernel function: Transform a key blob (of any type) into a protected key
*/
int pkey_key2protkey(const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype, u32 xflags)
{
int rc;
rc = key2protkey(NULL, 0, key, keylen,
protkey, protkeylen, protkeytype, xflags);
if (rc == -ENODEV) {
pkey_handler_request_modules();
rc = key2protkey(NULL, 0, key, keylen,
protkey, protkeylen, protkeytype, xflags);
}
return rc;
}
EXPORT_SYMBOL(pkey_key2protkey);
/*
* Ioctl functions
*/
static void *_copy_key_from_user(void __user *ukey, size_t keylen)
{
if (!ukey || keylen < MINKEYBLOBBUFSIZE || keylen > KEYBLOBBUFSIZE)
return ERR_PTR(-EINVAL);
return memdup_user(ukey, keylen);
}
static void *_copy_apqns_from_user(void __user *uapqns, size_t nr_apqns)
{
if (!uapqns || nr_apqns == 0)
return NULL;
return memdup_array_user(uapqns, nr_apqns, sizeof(struct pkey_apqn));
}
static int pkey_ioctl_genseck(struct pkey_genseck __user *ugs)
{
struct pkey_genseck kgs;
struct pkey_apqn apqn;
u32 keybuflen;
int rc;
if (copy_from_user(&kgs, ugs, sizeof(kgs)))
return -EFAULT;
apqn.card = kgs.cardnr;
apqn.domain = kgs.domain;
keybuflen = sizeof(kgs.seckey.seckey);
rc = pkey_handler_gen_key(&apqn, 1,
kgs.keytype, PKEY_TYPE_CCA_DATA, 0, 0,
kgs.seckey.seckey, &keybuflen, NULL, 0);
pr_debug("gen_key()=%d\n", rc);
if (!rc && copy_to_user(ugs, &kgs, sizeof(kgs)))
rc = -EFAULT;
memzero_explicit(&kgs, sizeof(kgs));
return rc;
}
static int pkey_ioctl_clr2seck(struct pkey_clr2seck __user *ucs)
{
struct pkey_clr2seck kcs;
struct pkey_apqn apqn;
u32 keybuflen;
int rc;
if (copy_from_user(&kcs, ucs, sizeof(kcs)))
return -EFAULT;
apqn.card = kcs.cardnr;
apqn.domain = kcs.domain;
keybuflen = sizeof(kcs.seckey.seckey);
rc = pkey_handler_clr_to_key(&apqn, 1,
kcs.keytype, PKEY_TYPE_CCA_DATA, 0, 0,
kcs.clrkey.clrkey,
pkey_keytype_aes_to_size(kcs.keytype),
kcs.seckey.seckey, &keybuflen, NULL, 0);
pr_debug("clr_to_key()=%d\n", rc);
if (!rc && copy_to_user(ucs, &kcs, sizeof(kcs)))
rc = -EFAULT;
memzero_explicit(&kcs, sizeof(kcs));
return rc;
}
static int pkey_ioctl_sec2protk(struct pkey_sec2protk __user *usp)
{
struct pkey_sec2protk ksp;
struct pkey_apqn apqn;
int rc;
if (copy_from_user(&ksp, usp, sizeof(ksp)))
return -EFAULT;
apqn.card = ksp.cardnr;
apqn.domain = ksp.domain;
ksp.protkey.len = sizeof(ksp.protkey.protkey);
rc = pkey_handler_key_to_protkey(&apqn, 1,
ksp.seckey.seckey,
sizeof(ksp.seckey.seckey),
ksp.protkey.protkey,
&ksp.protkey.len, &ksp.protkey.type,
0);
pr_debug("key_to_protkey()=%d\n", rc);
if (!rc && copy_to_user(usp, &ksp, sizeof(ksp)))
rc = -EFAULT;
memzero_explicit(&ksp, sizeof(ksp));
return rc;
}
static int pkey_ioctl_clr2protk(struct pkey_clr2protk