mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
cb787f4ac0
no_llseek had been defined to NULL two years ago, in commit 868941b144
("fs: remove no_llseek")
To quote that commit,
At -rc1 we'll need do a mechanical removal of no_llseek -
git grep -l -w no_llseek | grep -v porting.rst | while read i; do
sed -i '/\<no_llseek\>/d' $i
done
would do it.
Unfortunately, that hadn't been done. Linus, could you do that now, so
that we could finally put that thing to rest? All instances are of the
form
.llseek = no_llseek,
so it's obviously safe.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
800 lines
19 KiB
C
800 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* pkey device driver
|
|
*
|
|
* Copyright IBM Corp. 2017, 2023
|
|
*
|
|
* Author(s): Harald Freudenberger
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "pkey"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/miscdevice.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)
|
|
{
|
|
int rc;
|
|
|
|
/* try the direct way */
|
|
rc = pkey_handler_key_to_protkey(apqns, nr_apqns,
|
|
key, keylen,
|
|
protkey, protkeylen,
|
|
protkeytype);
|
|
|
|
/* 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);
|
|
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)
|
|
{
|
|
int rc;
|
|
|
|
rc = key2protkey(NULL, 0, key, keylen,
|
|
protkey, protkeylen, protkeytype);
|
|
if (rc == -ENODEV) {
|
|
pkey_handler_request_modules();
|
|
rc = key2protkey(NULL, 0, key, keylen,
|
|
protkey, protkeylen, protkeytype);
|
|
}
|
|
|
|
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_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);
|
|
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);
|
|
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);
|
|
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 __user *ucp)
|
|
{
|
|
struct pkey_clr2protk kcp;
|
|
struct clearkeytoken *t;
|
|
u32 keylen;
|
|
u8 *tmpbuf;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kcp, ucp, sizeof(kcp)))
|
|
return -EFAULT;
|
|
|
|
/* build a 'clear key token' from the clear key value */
|
|
keylen = pkey_keytype_aes_to_size(kcp.keytype);
|
|
if (!keylen) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n",
|
|
__func__, kcp.keytype);
|
|
memzero_explicit(&kcp, sizeof(kcp));
|
|
return -EINVAL;
|
|
}
|
|
tmpbuf = kzalloc(sizeof(*t) + keylen, GFP_KERNEL);
|
|
if (!tmpbuf) {
|
|
memzero_explicit(&kcp, sizeof(kcp));
|
|
return -ENOMEM;
|
|
}
|
|
t = (struct clearkeytoken *)tmpbuf;
|
|
t->type = TOKTYPE_NON_CCA;
|
|
t->version = TOKVER_CLEAR_KEY;
|
|
t->keytype = (keylen - 8) >> 3;
|
|
t->len = keylen;
|
|
memcpy(t->clearkey, kcp.clrkey.clrkey, keylen);
|
|
kcp.protkey.len = sizeof(kcp.protkey.protkey);
|
|
|
|
rc = key2protkey(NULL, 0,
|
|
tmpbuf, sizeof(*t) + keylen,
|
|
kcp.protkey.protkey,
|
|
&kcp.protkey.len, &kcp.protkey.type);
|
|
pr_debug("key2protkey()=%d\n", rc);
|
|
|
|
kfree_sensitive(tmpbuf);
|
|
|
|
if (!rc && copy_to_user(ucp, &kcp, sizeof(kcp)))
|
|
rc = -EFAULT;
|
|
memzero_explicit(&kcp, sizeof(kcp));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_findcard(struct pkey_findcard __user *ufc)
|
|
{
|
|
struct pkey_findcard kfc;
|
|
struct pkey_apqn *apqns;
|
|
size_t nr_apqns;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kfc, ufc, sizeof(kfc)))
|
|
return -EFAULT;
|
|
|
|
nr_apqns = MAXAPQNSINLIST;
|
|
apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn), GFP_KERNEL);
|
|
if (!apqns)
|
|
return -ENOMEM;
|
|
|
|
rc = pkey_handler_apqns_for_key(kfc.seckey.seckey,
|
|
sizeof(kfc.seckey.seckey),
|
|
PKEY_FLAGS_MATCH_CUR_MKVP,
|
|
apqns, &nr_apqns);
|
|
if (rc == -ENODEV)
|
|
rc = pkey_handler_apqns_for_key(kfc.seckey.seckey,
|
|
sizeof(kfc.seckey.seckey),
|
|
PKEY_FLAGS_MATCH_ALT_MKVP,
|
|
apqns, &nr_apqns);
|
|
pr_debug("apqns_for_key()=%d\n", rc);
|
|
if (rc) {
|
|
kfree(apqns);
|
|
return rc;
|
|
}
|
|
kfc.cardnr = apqns[0].card;
|
|
kfc.domain = apqns[0].domain;
|
|
kfree(apqns);
|
|
if (copy_to_user(ufc, &kfc, sizeof(kfc)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pkey_ioctl_skey2pkey(struct pkey_skey2pkey __user *usp)
|
|
{
|
|
struct pkey_skey2pkey ksp;
|
|
int rc;
|
|
|
|
if (copy_from_user(&ksp, usp, sizeof(ksp)))
|
|
return -EFAULT;
|
|
|
|
ksp.protkey.len = sizeof(ksp.protkey.protkey);
|
|
rc = pkey_handler_key_to_protkey(NULL, 0,
|
|
ksp.seckey.seckey,
|
|
sizeof(ksp.seckey.seckey),
|
|
ksp.protkey.protkey,
|
|
&ksp.protkey.len,
|
|
&ksp.protkey.type);
|
|
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_verifykey(struct pkey_verifykey __user *uvk)
|
|
{
|
|
u32 keytype, keybitsize, flags;
|
|
struct pkey_verifykey kvk;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kvk, uvk, sizeof(kvk)))
|
|
return -EFAULT;
|
|
|
|
kvk.cardnr = 0xFFFF;
|
|
kvk.domain = 0xFFFF;
|
|
rc = pkey_handler_verify_key(kvk.seckey.seckey,
|
|
sizeof(kvk.seckey.seckey),
|
|
&kvk.cardnr, &kvk.domain,
|
|
&keytype, &keybitsize, &flags);
|
|
pr_debug("verify_key()=%d\n", rc);
|
|
if (!rc && keytype != PKEY_TYPE_CCA_DATA)
|
|
rc = -EINVAL;
|
|
kvk.attributes = PKEY_VERIFY_ATTR_AES;
|
|
kvk.keysize = (u16)keybitsize;
|
|
if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
|
|
kvk.attributes |= PKEY_VERIFY_ATTR_OLD_MKVP;
|
|
if (!rc && copy_to_user(uvk, &kvk, sizeof(kvk)))
|
|
rc = -EFAULT;
|
|
memzero_explicit(&kvk, sizeof(kvk));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_genprotk(struct pkey_genprotk __user *ugp)
|
|
{
|
|
struct pkey_genprotk kgp;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kgp, ugp, sizeof(kgp)))
|
|
return -EFAULT;
|
|
|
|
kgp.protkey.len = sizeof(kgp.protkey.protkey);
|
|
rc = pkey_handler_gen_key(NULL, 0, kgp.keytype,
|
|
PKEY_TYPE_PROTKEY, 0, 0,
|
|
kgp.protkey.protkey, &kgp.protkey.len,
|
|
&kgp.protkey.type);
|
|
pr_debug("gen_key()=%d\n", rc);
|
|
if (!rc && copy_to_user(ugp, &kgp, sizeof(kgp)))
|
|
rc = -EFAULT;
|
|
memzero_explicit(&kgp, sizeof(kgp));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_verifyprotk(struct pkey_verifyprotk __user *uvp)
|
|
{
|
|
struct pkey_verifyprotk kvp;
|
|
struct protaeskeytoken *t;
|
|
u32 keytype;
|
|
u8 *tmpbuf;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kvp, uvp, sizeof(kvp)))
|
|
return -EFAULT;
|
|
|
|
keytype = pkey_aes_bitsize_to_keytype(8 * kvp.protkey.len);
|
|
if (!keytype) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported protkey length %u\n",
|
|
__func__, kvp.protkey.len);
|
|
memzero_explicit(&kvp, sizeof(kvp));
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* build a 'protected key token' from the raw protected key */
|
|
tmpbuf = kzalloc(sizeof(*t), GFP_KERNEL);
|
|
if (!tmpbuf) {
|
|
memzero_explicit(&kvp, sizeof(kvp));
|
|
return -ENOMEM;
|
|
}
|
|
t = (struct protaeskeytoken *)tmpbuf;
|
|
t->type = TOKTYPE_NON_CCA;
|
|
t->version = TOKVER_PROTECTED_KEY;
|
|
t->keytype = keytype;
|
|
t->len = kvp.protkey.len;
|
|
memcpy(t->protkey, kvp.protkey.protkey, kvp.protkey.len);
|
|
|
|
rc = pkey_handler_verify_key(tmpbuf, sizeof(*t),
|
|
NULL, NULL, NULL, NULL, NULL);
|
|
pr_debug("verify_key()=%d\n", rc);
|
|
|
|
kfree_sensitive(tmpbuf);
|
|
memzero_explicit(&kvp, sizeof(kvp));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_kblob2protk(struct pkey_kblob2pkey __user *utp)
|
|
{
|
|
struct pkey_kblob2pkey ktp;
|
|
u8 *kkey;
|
|
int rc;
|
|
|
|
if (copy_from_user(&ktp, utp, sizeof(ktp)))
|
|
return -EFAULT;
|
|
kkey = _copy_key_from_user(ktp.key, ktp.keylen);
|
|
if (IS_ERR(kkey))
|
|
return PTR_ERR(kkey);
|
|
ktp.protkey.len = sizeof(ktp.protkey.protkey);
|
|
rc = key2protkey(NULL, 0, kkey, ktp.keylen,
|
|
ktp.protkey.protkey, &ktp.protkey.len,
|
|
&ktp.protkey.type);
|
|
pr_debug("key2protkey()=%d\n", rc);
|
|
kfree_sensitive(kkey);
|
|
if (!rc && copy_to_user(utp, &ktp, sizeof(ktp)))
|
|
rc = -EFAULT;
|
|
memzero_explicit(&ktp, sizeof(ktp));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_genseck2(struct pkey_genseck2 __user *ugs)
|
|
{
|
|
u32 klen = KEYBLOBBUFSIZE;
|
|
struct pkey_genseck2 kgs;
|
|
struct pkey_apqn *apqns;
|
|
u8 *kkey;
|
|
int rc;
|
|
u32 u;
|
|
|
|
if (copy_from_user(&kgs, ugs, sizeof(kgs)))
|
|
return -EFAULT;
|
|
u = pkey_aes_bitsize_to_keytype(kgs.size);
|
|
if (!u) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
|
|
__func__, kgs.size);
|
|
return -EINVAL;
|
|
}
|
|
apqns = _copy_apqns_from_user(kgs.apqns, kgs.apqn_entries);
|
|
if (IS_ERR(apqns))
|
|
return PTR_ERR(apqns);
|
|
kkey = kzalloc(klen, GFP_KERNEL);
|
|
if (!kkey) {
|
|
kfree(apqns);
|
|
return -ENOMEM;
|
|
}
|
|
rc = pkey_handler_gen_key(apqns, kgs.apqn_entries,
|
|
u, kgs.type, kgs.size, kgs.keygenflags,
|
|
kkey, &klen, NULL);
|
|
pr_debug("gen_key()=%d\n", rc);
|
|
kfree(apqns);
|
|
if (rc) {
|
|
kfree_sensitive(kkey);
|
|
return rc;
|
|
}
|
|
if (kgs.key) {
|
|
if (kgs.keylen < klen) {
|
|
kfree_sensitive(kkey);
|
|
return -EINVAL;
|
|
}
|
|
if (copy_to_user(kgs.key, kkey, klen)) {
|
|
kfree_sensitive(kkey);
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
kgs.keylen = klen;
|
|
if (copy_to_user(ugs, &kgs, sizeof(kgs)))
|
|
rc = -EFAULT;
|
|
kfree_sensitive(kkey);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_clr2seck2(struct pkey_clr2seck2 __user *ucs)
|
|
{
|
|
u32 klen = KEYBLOBBUFSIZE;
|
|
struct pkey_clr2seck2 kcs;
|
|
struct pkey_apqn *apqns;
|
|
u8 *kkey;
|
|
int rc;
|
|
u32 u;
|
|
|
|
if (copy_from_user(&kcs, ucs, sizeof(kcs)))
|
|
return -EFAULT;
|
|
u = pkey_aes_bitsize_to_keytype(kcs.size);
|
|
if (!u) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
|
|
__func__, kcs.size);
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
return -EINVAL;
|
|
}
|
|
apqns = _copy_apqns_from_user(kcs.apqns, kcs.apqn_entries);
|
|
if (IS_ERR(apqns)) {
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
return PTR_ERR(apqns);
|
|
}
|
|
kkey = kzalloc(klen, GFP_KERNEL);
|
|
if (!kkey) {
|
|
kfree(apqns);
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
return -ENOMEM;
|
|
}
|
|
rc = pkey_handler_clr_to_key(apqns, kcs.apqn_entries,
|
|
u, kcs.type, kcs.size, kcs.keygenflags,
|
|
kcs.clrkey.clrkey, kcs.size / 8,
|
|
kkey, &klen, NULL);
|
|
pr_debug("clr_to_key()=%d\n", rc);
|
|
kfree(apqns);
|
|
if (rc) {
|
|
kfree_sensitive(kkey);
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
return rc;
|
|
}
|
|
if (kcs.key) {
|
|
if (kcs.keylen < klen) {
|
|
kfree_sensitive(kkey);
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
return -EINVAL;
|
|
}
|
|
if (copy_to_user(kcs.key, kkey, klen)) {
|
|
kfree_sensitive(kkey);
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
kcs.keylen = klen;
|
|
if (copy_to_user(ucs, &kcs, sizeof(kcs)))
|
|
rc = -EFAULT;
|
|
memzero_explicit(&kcs, sizeof(kcs));
|
|
kfree_sensitive(kkey);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_verifykey2(struct pkey_verifykey2 __user *uvk)
|
|
{
|
|
struct pkey_verifykey2 kvk;
|
|
u8 *kkey;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kvk, uvk, sizeof(kvk)))
|
|
return -EFAULT;
|
|
kkey = _copy_key_from_user(kvk.key, kvk.keylen);
|
|
if (IS_ERR(kkey))
|
|
return PTR_ERR(kkey);
|
|
|
|
rc = pkey_handler_verify_key(kkey, kvk.keylen,
|
|
&kvk.cardnr, &kvk.domain,
|
|
&kvk.type, &kvk.size, &kvk.flags);
|
|
pr_debug("verify_key()=%d\n", rc);
|
|
|
|
kfree_sensitive(kkey);
|
|
if (!rc && copy_to_user(uvk, &kvk, sizeof(kvk)))
|
|
return -EFAULT;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_kblob2protk2(struct pkey_kblob2pkey2 __user *utp)
|
|
{
|
|
struct pkey_apqn *apqns = NULL;
|
|
struct pkey_kblob2pkey2 ktp;
|
|
u8 *kkey;
|
|
int rc;
|
|
|
|
if (copy_from_user(&ktp, utp, sizeof(ktp)))
|
|
return -EFAULT;
|
|
apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries);
|
|
if (IS_ERR(apqns))
|
|
return PTR_ERR(apqns);
|
|
kkey = _copy_key_from_user(ktp.key, ktp.keylen);
|
|
if (IS_ERR(kkey)) {
|
|
kfree(apqns);
|
|
return PTR_ERR(kkey);
|
|
}
|
|
ktp.protkey.len = sizeof(ktp.protkey.protkey);
|
|
rc = key2protkey(apqns, ktp.apqn_entries, kkey, ktp.keylen,
|
|
ktp.protkey.protkey, &ktp.protkey.len,
|
|
&ktp.protkey.type);
|
|
pr_debug("key2protkey()=%d\n", rc);
|
|
kfree(apqns);
|
|
kfree_sensitive(kkey);
|
|
if (!rc && copy_to_user(utp, &ktp, sizeof(ktp)))
|
|
rc = -EFAULT;
|
|
memzero_explicit(&ktp, sizeof(ktp));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_apqns4k(struct pkey_apqns4key __user *uak)
|
|
{
|
|
struct pkey_apqn *apqns = NULL;
|
|
struct pkey_apqns4key kak;
|
|
size_t nr_apqns, len;
|
|
u8 *kkey;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kak, uak, sizeof(kak)))
|
|
return -EFAULT;
|
|
nr_apqns = kak.apqn_entries;
|
|
if (nr_apqns) {
|
|
apqns = kmalloc_array(nr_apqns,
|
|
sizeof(struct pkey_apqn),
|
|
GFP_KERNEL);
|
|
if (!apqns)
|
|
return -ENOMEM;
|
|
}
|
|
kkey = _copy_key_from_user(kak.key, kak.keylen);
|
|
if (IS_ERR(kkey)) {
|
|
kfree(apqns);
|
|
return PTR_ERR(kkey);
|
|
}
|
|
rc = pkey_handler_apqns_for_key(kkey, kak.keylen, kak.flags,
|
|
apqns, &nr_apqns);
|
|
pr_debug("apqns_for_key()=%d\n", rc);
|
|
kfree_sensitive(kkey);
|
|
if (rc && rc != -ENOSPC) {
|
|
kfree(apqns);
|
|
return rc;
|
|
}
|
|
if (!rc && kak.apqns) {
|
|
if (nr_apqns > kak.apqn_entries) {
|
|
kfree(apqns);
|
|
return -EINVAL;
|
|
}
|
|
len = nr_apqns * sizeof(struct pkey_apqn);
|
|
if (len) {
|
|
if (copy_to_user(kak.apqns, apqns, len)) {
|
|
kfree(apqns);
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
}
|
|
kak.apqn_entries = nr_apqns;
|
|
if (copy_to_user(uak, &kak, sizeof(kak)))
|
|
rc = -EFAULT;
|
|
kfree(apqns);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_apqns4kt(struct pkey_apqns4keytype __user *uat)
|
|
{
|
|
struct pkey_apqn *apqns = NULL;
|
|
struct pkey_apqns4keytype kat;
|
|
size_t nr_apqns, len;
|
|
int rc;
|
|
|
|
if (copy_from_user(&kat, uat, sizeof(kat)))
|
|
return -EFAULT;
|
|
nr_apqns = kat.apqn_entries;
|
|
if (nr_apqns) {
|
|
apqns = kmalloc_array(nr_apqns,
|
|
sizeof(struct pkey_apqn),
|
|
GFP_KERNEL);
|
|
if (!apqns)
|
|
return -ENOMEM;
|
|
}
|
|
rc = pkey_handler_apqns_for_keytype(kat.type,
|
|
kat.cur_mkvp, kat.alt_mkvp,
|
|
kat.flags, apqns, &nr_apqns);
|
|
pr_debug("apqns_for_keytype()=%d\n", rc);
|
|
if (rc && rc != -ENOSPC) {
|
|
kfree(apqns);
|
|
return rc;
|
|
}
|
|
if (!rc && kat.apqns) {
|
|
if (nr_apqns > kat.apqn_entries) {
|
|
kfree(apqns);
|
|
return -EINVAL;
|
|
}
|
|
len = nr_apqns * sizeof(struct pkey_apqn);
|
|
if (len) {
|
|
if (copy_to_user(kat.apqns, apqns, len)) {
|
|
kfree(apqns);
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
}
|
|
kat.apqn_entries = nr_apqns;
|
|
if (copy_to_user(uat, &kat, sizeof(kat)))
|
|
rc = -EFAULT;
|
|
kfree(apqns);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int pkey_ioctl_kblob2protk3(struct pkey_kblob2pkey3 __user *utp)
|
|
{
|
|
u32 protkeylen = PROTKEYBLOBBUFSIZE;
|
|
struct pkey_apqn *apqns = NULL;
|
|
struct pkey_kblob2pkey3 ktp;
|
|
u8 *kkey, *protkey;
|
|
int rc;
|
|
|
|
if (copy_from_user(&ktp, utp, sizeof(ktp)))
|
|
return -EFAULT;
|
|
apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries);
|
|
if (IS_ERR(apqns))
|
|
return PTR_ERR(apqns);
|
|
kkey = _copy_key_from_user(ktp.key, ktp.keylen);
|
|
if (IS_ERR(kkey)) {
|
|
kfree(apqns);
|
|
return PTR_ERR(kkey);
|
|
}
|
|
protkey = kmalloc(protkeylen, GFP_KERNEL);
|
|
if (!protkey) {
|
|
kfree(apqns);
|
|
kfree_sensitive(kkey);
|
|
return -ENOMEM;
|
|
}
|
|
rc = key2protkey(apqns, ktp.apqn_entries, kkey, ktp.keylen,
|
|
protkey, &protkeylen, &ktp.pkeytype);
|
|
pr_debug("key2protkey()=%d\n", rc);
|
|
kfree(apqns);
|
|
kfree_sensitive(kkey);
|
|
if (rc) {
|
|
kfree_sensitive(protkey);
|
|
return rc;
|
|
}
|
|
if (ktp.pkey && ktp.pkeylen) {
|
|
if (protkeylen > ktp.pkeylen) {
|
|
kfree_sensitive(protkey);
|
|
return -EINVAL;
|
|
}
|
|
if (copy_to_user(ktp.pkey, protkey, protkeylen)) {
|
|
kfree_sensitive(protkey);
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
kfree_sensitive(protkey);
|
|
ktp.pkeylen = protkeylen;
|
|
if (copy_to_user(utp, &ktp, sizeof(ktp)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int rc;
|
|
|
|
switch (cmd) {
|
|
case PKEY_GENSECK:
|
|
rc = pkey_ioctl_genseck((struct pkey_genseck __user *)arg);
|
|
break;
|
|
case PKEY_CLR2SECK:
|
|
rc = pkey_ioctl_clr2seck((struct pkey_clr2seck __user *)arg);
|
|
break;
|
|
case PKEY_SEC2PROTK:
|
|
rc = pkey_ioctl_sec2protk((struct pkey_sec2protk __user *)arg);
|
|
break;
|
|
case PKEY_CLR2PROTK:
|
|
rc = pkey_ioctl_clr2protk((struct pkey_clr2protk __user *)arg);
|
|
break;
|
|
case PKEY_FINDCARD:
|
|
rc = pkey_ioctl_findcard((struct pkey_findcard __user *)arg);
|
|
break;
|
|
case PKEY_SKEY2PKEY:
|
|
rc = pkey_ioctl_skey2pkey((struct pkey_skey2pkey __user *)arg);
|
|
break;
|
|
case PKEY_VERIFYKEY:
|
|
rc = pkey_ioctl_verifykey((struct pkey_verifykey __user *)arg);
|
|
break;
|
|
case PKEY_GENPROTK:
|
|
rc = pkey_ioctl_genprotk((struct pkey_genprotk __user *)arg);
|
|
break;
|
|
case PKEY_VERIFYPROTK:
|
|
rc = pkey_ioctl_verifyprotk((struct pkey_verifyprotk __user *)arg);
|
|
break;
|
|
case PKEY_KBLOB2PROTK:
|
|
rc = pkey_ioctl_kblob2protk((struct pkey_kblob2pkey __user *)arg);
|
|
break;
|
|
case PKEY_GENSECK2:
|
|
rc = pkey_ioctl_genseck2((struct pkey_genseck2 __user *)arg);
|
|
break;
|
|
case PKEY_CLR2SECK2:
|
|
rc = pkey_ioctl_clr2seck2((struct pkey_clr2seck2 __user *)arg);
|
|
break;
|
|
case PKEY_VERIFYKEY2:
|
|
rc = pkey_ioctl_verifykey2((struct pkey_verifykey2 __user *)arg);
|
|
break;
|
|
case PKEY_KBLOB2PROTK2:
|
|
rc = pkey_ioctl_kblob2protk2((struct pkey_kblob2pkey2 __user *)arg);
|
|
break;
|
|
case PKEY_APQNS4K:
|
|
rc = pkey_ioctl_apqns4k((struct pkey_apqns4key __user *)arg);
|
|
break;
|
|
case PKEY_APQNS4KT:
|
|
rc = pkey_ioctl_apqns4kt((struct pkey_apqns4keytype __user *)arg);
|
|
break;
|
|
case PKEY_KBLOB2PROTK3:
|
|
rc = pkey_ioctl_kblob2protk3((struct pkey_kblob2pkey3 __user *)arg);
|
|
break;
|
|
default:
|
|
/* unknown/unsupported ioctl cmd */
|
|
return -ENOTTY;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* File io operations
|
|
*/
|
|
|
|
static const struct file_operations pkey_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = nonseekable_open,
|
|
.unlocked_ioctl = pkey_unlocked_ioctl,
|
|
};
|
|
|
|
static struct miscdevice pkey_dev = {
|
|
.name = "pkey",
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.mode = 0666,
|
|
.fops = &pkey_fops,
|
|
.groups = pkey_attr_groups,
|
|
};
|
|
|
|
int __init pkey_api_init(void)
|
|
{
|
|
/* register as a misc device */
|
|
return misc_register(&pkey_dev);
|
|
}
|
|
|
|
void __exit pkey_api_exit(void)
|
|
{
|
|
misc_deregister(&pkey_dev);
|
|
}
|