mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 23:03:57 -04:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
798 lines
19 KiB
C
798 lines
19 KiB
C
// 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 __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, 0);
|
|
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_objs(struct pkey_apqn, nr_apqns);
|
|
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, 0);
|
|
if (rc == -ENODEV)
|
|
rc = pkey_handler_apqns_for_key(kfc.seckey.seckey,
|
|
sizeof(kfc.seckey.seckey),
|
|
PKEY_FLAGS_MATCH_ALT_MKVP,
|
|
apqns, &nr_apqns, 0);
|
|
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, 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_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, 0);
|
|
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, 0);
|
|
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, 0);
|
|
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, 0);
|
|
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, 0);
|
|
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, 0);
|
|
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, 0);
|
|
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, 0);
|
|
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_objs(struct pkey_apqn, nr_apqns);
|
|
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, 0);
|
|
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_objs(struct pkey_apqn, nr_apqns);
|
|
if (!apqns)
|
|
return -ENOMEM;
|
|
}
|
|
rc = pkey_handler_apqns_for_keytype(kat.type,
|
|
kat.cur_mkvp, kat.alt_mkvp,
|
|
kat.flags, apqns, &nr_apqns, 0);
|
|
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, 0);
|
|
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);
|
|
}
|