// 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 #include #include #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, .llseek = no_llseek, .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); }