Message ID | 20180525234328.GA2981@alison-desk.jf.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Alison Schofield <alison.schofield@intel.com> wrote: > +EXPORT_SYMBOL(key_lookup); *Twitch*. There's no protection on this whatsoever. > +struct mktme_mapping { > + struct mutex lock; /* protect mktme_map & hw state */ > + struct data { > + key_serial_t serial; Why key_serial_t rather than struct key *? Unless you pin it, there's no guarantee that serial will refer to the same key - or even the same type of key - next time you look for it. The kernel should not be using key IDs outside of code keyrings code, except for logging purposes. > +static int mktme_recover_key(void) > +{ > + int i = mktme_max_keyids; > + struct key *key; > + > + do { > + if (!mktme_map->id[i].serial) > + continue; > + > + key = key_lookup(mktme_map->id[i].serial); > + if (IS_ERR(key)) > + goto recover; This gets a ref on the key from key_lookup() and then simply drops it. Did you also want to check the key type? > + > + if (key_validate(key) < 0) { > + /* > + * Here the key ptr is good, but the > + * key may * be marked for removal. > + * Leave this here to watch for. > + */ > + pr_info("%s key validate fails\n", __func__); > + goto recover; > + } > + } while (i--); > + > + return 0; > +recover: > + mktme_unregister_key(i); > + return i; > +} What do you name your keys? Can they be named for the slot they're in? > +/* Parse the options from the datablob and fill in struct mktme_key_program. > + * After parsing, call mktme_check_options() for sanity checking. > + * > + * Success returns 0, otherwise -EINVAL. > + */ > +static int mktme_get_options(char *datablob, struct mktme_key_program *kprog) > +{ > + enum mktme_alg alg = MKTME_ALG__LAST; > + substring_t args[MAX_OPT_ARGS]; > + unsigned long token_mask = 0; > + int len, ret, token; > + char *p = datablob; > + > + while ((p = strsep(&datablob, " \t"))) { > + if (*p == '\0' || *p == ' ' || *p == '\t') > + continue; > + token = match_token(p, mktme_tokens, args); > + if (test_and_set_bit(token, &token_mask)) > + return -EINVAL; > + > + len = strlen(args[0].from) / 2; > + if (len > MKTME_MAX_OPTION_SIZE) > + return -EINVAL; > + > + switch (token) { > + case opt_userkey: > + ret = hex2bin(kprog->key_field_1, args[0].from, len); > + if (ret < 0) > + return -EINVAL; > + kprog->keyid_ctrl |= MKTME_KEYID_SET_KEY_DIRECT; > + break; You should perhaps use request_key() or lookup_user_key() here maybe? > + > + case opt_tweak: > + ret = hex2bin(kprog->key_field_2, args[0].from, len); > + if (ret < 0) > + return -EINVAL; > + break; > + > + case opt_entropy: > + ret = hex2bin(kprog->key_field_1, args[0].from, len); > + if (ret < 0) > + return -EINVAL; > + break; > + > + case opt_algorithm: > + alg = match_string(mktme_alg_name, MKTME_ALG__LAST, > + args[0].from); > + switch (alg) { > + case MKTME_ALG_AES_XTS_128: > + kprog->keyid_ctrl |= MKTME_AES_XTS_128; > + break; > + > + case MKTME_ALG_NO_ENCRYPT: > + kprog->keyid_ctrl |= MKTME_KEYID_NO_ENCRYPT; > + break; > + > + default: > + return -EINVAL; > + } > + break; > + > + default: > + return -EINVAL; > + } > + } > + dump_kprog(kprog); > + return mktme_check_options(kprog, token_mask); > +} > + > +/* Key Service Command: Creates a software key and programs hardware */ > + > +int mktme_instantiate(struct key *key, struct key_preparsed_payload *prep) Please use preparsing if you can. You shouldn't be returning ENOMEM from ->instantiate() if at all possible. > +{ > + struct mktme_key_program *kprog = NULL; > + size_t datalen = prep->datalen; > + char *datablob; > + int ret = 0; > + > + if (datalen <= 0 || datalen > 1024 || !prep->data) > + return -EINVAL; > + > + datablob = kmemdup(prep->data, datalen + 1, GFP_KERNEL); > + if (!datablob) > + return -ENOMEM; > + > + datablob[datalen] = '\0'; > + kprog = kzalloc(sizeof(*kprog), GFP_KERNEL); > + if (!kprog) { > + kzfree(datablob); > + return -ENOMEM; > + } > + ret = mktme_get_options(datablob, kprog); > + if (ret < 0) > + goto out; > + > + mutex_lock(&mktme_map->lock); > + ret = mktme_register_key(key->serial, kprog); > + mutex_unlock(&mktme_map->lock); > +out: > + kzfree(datablob); > + kzfree(kprog); > + dump_keys(); > + return ret; > +} > + > +/* Key Service Command: Clears the keys software and hardware */ > + > +void mktme_revoke(struct key *key) > +{ > + int keyid; > + > + keyid = mktme_get_keyid(key->serial); > + if (keyid < 0) > + return; > + > + mutex_lock(&mktme_map->lock); > + mktme_unregister_key(keyid); > + mutex_unlock(&mktme_map->lock); > + dump_keys(); > +} > + > +struct key_type key_type_mktme = { > + .name = "mktme", > + .instantiate = mktme_instantiate, > + .revoke = mktme_revoke, > + .describe = user_describe, > +}; > + > +/* > + * Get the maximum keyids reported from BIOS. > + * Allocate the mktme_map structure and register mktme key type. > + */ > +static int __init init_mktme(void) > +{ > + int ret; > + > + /* TODO: get real max hardware keyids */ > + /* mktme_max_keyids = nr_keyids; */ > + > + mktme_max_keyids = 100; > + mktme_mapped_keyids = 0; > + mktme_map = kzalloc((sizeof(mktme_map->id[0]) * mktme_max_keyids) > + + (sizeof(mktme_map->lock)), GFP_KERNEL); > + if (!mktme_map) > + return -ENOMEM; > + > + mutex_init(&mktme_map->lock); > + ret = register_key_type(&key_type_mktme); > + if (ret < 0) > + kfree(mktme_map); > + > + return ret; > +} > + > +static void __exit cleanup_mktme(void) > +{ > + unregister_key_type(&key_type_mktme); > + kfree(mktme_map); > +} > + > +late_initcall(init_mktme); -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Thanks for the feedback David. See comments inline... alisons On Fri, Jun 08, 2018 at 05:33:04PM +0100, David Howells wrote: > Alison Schofield <alison.schofield@intel.com> wrote: > > > +EXPORT_SYMBOL(key_lookup); > > *Twitch*. > > There's no protection on this whatsoever. got it. > > > +struct mktme_mapping { > > + struct mutex lock; /* protect mktme_map & hw state */ > > + struct data { > > + key_serial_t serial; > > Why key_serial_t rather than struct key *? > > Unless you pin it, there's no guarantee that serial will refer to the same > key - or even the same type of key - next time you look for it. > > The kernel should not be using key IDs outside of code keyrings code, except > for logging purposes. Perhaps I'm munging userspace vs kernel usage rules here. Userspace gets a key_serial_t when they add_key(). Later, they use key_serial_t in another system call to request an encrypted mapping. That system call does validity checking on key_serial_t, gets the corresponding keyid slot from the above struct, and performs the encrypted mapping. I see how I can save struct key* and use it here. I'm just not clear why key* is safer that key_serial_t. Either piece I save, key* or key_serial_t needs to be repeatedly validated. (key* would avoid need for key_lookup() ;)) Concerned I'm missing a bigger point here? > > > +static int mktme_recover_key(void) > > +{ > > + int i = mktme_max_keyids; > > + struct key *key; > > + > > + do { > > + if (!mktme_map->id[i].serial) > > + continue; > > + > > + key = key_lookup(mktme_map->id[i].serial); > > + if (IS_ERR(key)) > > + goto recover; > > This gets a ref on the key from key_lookup() and then simply drops it. Did > you also want to check the key type? Sure. This is an attempt at playing defense againt changes in keys that this key service is not directly told about. > > > + > > + if (key_validate(key) < 0) { > > + /* > > + * Here the key ptr is good, but the > > + * key may * be marked for removal. > > + * Leave this here to watch for. > > + */ > > + pr_info("%s key validate fails\n", __func__); > > + goto recover; > > + } > > + } while (i--); > > + > > + return 0; > > +recover: > > + mktme_unregister_key(i); > > + return i; > > +} > > What do you name your keys? Can they be named for the slot they're in? The service doesn't name keys. Userspace may use keyctl 'name' field or add_key() 'description' field. BTW - we never tell userspace what hardware slot they are mapped to. Maybe not the type of naming usage you were thinking of? > > > +/* Parse the options from the datablob and fill in struct mktme_key_program. > > + * After parsing, call mktme_check_options() for sanity checking. > > + * > > + * Success returns 0, otherwise -EINVAL. > > + */ > > +static int mktme_get_options(char *datablob, struct mktme_key_program *kprog) > > +{ > > + enum mktme_alg alg = MKTME_ALG__LAST; > > + substring_t args[MAX_OPT_ARGS]; > > + unsigned long token_mask = 0; > > + int len, ret, token; > > + char *p = datablob; > > + > > + while ((p = strsep(&datablob, " \t"))) { > > + if (*p == '\0' || *p == ' ' || *p == '\t') > > + continue; > > + token = match_token(p, mktme_tokens, args); > > + if (test_and_set_bit(token, &token_mask)) > > + return -EINVAL; > > + > > + len = strlen(args[0].from) / 2; > > + if (len > MKTME_MAX_OPTION_SIZE) > > + return -EINVAL; > > + > > + switch (token) { > > + case opt_userkey: > > + ret = hex2bin(kprog->key_field_1, args[0].from, len); > > + if (ret < 0) > > + return -EINVAL; > > + kprog->keyid_ctrl |= MKTME_KEYID_SET_KEY_DIRECT; > > + break; > > You should perhaps use request_key() or lookup_user_key() here maybe? This is the encryption key that the user passes as an option to add_key. Each of these options are a (maybe) required piece of their key setup. > > > + > > + case opt_tweak: > > + ret = hex2bin(kprog->key_field_2, args[0].from, len); > > + if (ret < 0) > > + return -EINVAL; > > + break; > > + > > + case opt_entropy: > > + ret = hex2bin(kprog->key_field_1, args[0].from, len); > > + if (ret < 0) > > + return -EINVAL; > > + break; > > + > > + case opt_algorithm: > > + alg = match_string(mktme_alg_name, MKTME_ALG__LAST, > > + args[0].from); > > + switch (alg) { > > + case MKTME_ALG_AES_XTS_128: > > + kprog->keyid_ctrl |= MKTME_AES_XTS_128; > > + break; > > + > > + case MKTME_ALG_NO_ENCRYPT: > > + kprog->keyid_ctrl |= MKTME_KEYID_NO_ENCRYPT; > > + break; > > + > > + default: > > + return -EINVAL; > > + } > > + break; > > + > > + default: > > + return -EINVAL; > > + } > > + } > > + dump_kprog(kprog); > > + return mktme_check_options(kprog, token_mask); > > +} > > + > > +/* Key Service Command: Creates a software key and programs hardware */ > > + > > +int mktme_instantiate(struct key *key, struct key_preparsed_payload *prep) > > Please use preparsing if you can. You shouldn't be returning ENOMEM from > ->instantiate() if at all possible. Got it, will pursue. > > > +{ > > + struct mktme_key_program *kprog = NULL; > > + size_t datalen = prep->datalen; > > + char *datablob; > > + int ret = 0; > > + > > + if (datalen <= 0 || datalen > 1024 || !prep->data) > > + return -EINVAL; > > + > > + datablob = kmemdup(prep->data, datalen + 1, GFP_KERNEL); > > + if (!datablob) > > + return -ENOMEM; > > + > > + datablob[datalen] = '\0'; > > + kprog = kzalloc(sizeof(*kprog), GFP_KERNEL); > > + if (!kprog) { > > + kzfree(datablob); > > + return -ENOMEM; > > + } > > + ret = mktme_get_options(datablob, kprog); > > + if (ret < 0) > > + goto out; > > + > > + mutex_lock(&mktme_map->lock); > > + ret = mktme_register_key(key->serial, kprog); > > + mutex_unlock(&mktme_map->lock); > > +out: > > + kzfree(datablob); > > + kzfree(kprog); > > + dump_keys(); > > + return ret; > > +} > > + > > +/* Key Service Command: Clears the keys software and hardware */ > > + > > +void mktme_revoke(struct key *key) > > +{ > > + int keyid; > > + > > + keyid = mktme_get_keyid(key->serial); > > + if (keyid < 0) > > + return; > > + > > + mutex_lock(&mktme_map->lock); > > + mktme_unregister_key(keyid); > > + mutex_unlock(&mktme_map->lock); > > + dump_keys(); > > +} > > + > > +struct key_type key_type_mktme = { > > + .name = "mktme", > > + .instantiate = mktme_instantiate, > > + .revoke = mktme_revoke, > > + .describe = user_describe, > > +}; > > + > > +/* > > + * Get the maximum keyids reported from BIOS. > > + * Allocate the mktme_map structure and register mktme key type. > > + */ > > +static int __init init_mktme(void) > > +{ > > + int ret; > > + > > + /* TODO: get real max hardware keyids */ > > + /* mktme_max_keyids = nr_keyids; */ > > + > > + mktme_max_keyids = 100; > > + mktme_mapped_keyids = 0; > > + mktme_map = kzalloc((sizeof(mktme_map->id[0]) * mktme_max_keyids) > > + + (sizeof(mktme_map->lock)), GFP_KERNEL); > > + if (!mktme_map) > > + return -ENOMEM; > > + > > + mutex_init(&mktme_map->lock); > > + ret = register_key_type(&key_type_mktme); > > + if (ret < 0) > > + kfree(mktme_map); > > + > > + return ret; > > +} > > + > > +static void __exit cleanup_mktme(void) > > +{ > > + unregister_key_type(&key_type_mktme); > > + kfree(mktme_map); > > +} > > + > > +late_initcall(init_mktme); -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/security/keys/mktme.rst b/Documentation/security/keys/mktme.rst new file mode 100644 index 0000000..bb9557e --- /dev/null +++ b/Documentation/security/keys/mktme.rst @@ -0,0 +1,69 @@ +========================================== +Keys for Multi-Key Total Memory Encryption +========================================== + +Keys for Multi-Key Total Memory Encryption (MKTME) are a new key type +added to the existing kernel key ring service. + +Allocating MKTME Keys via command line or system call:: + + keyctl add mktme name "[options]" ring + + key_serial_t add_key(const char *type, const char *description, + const void *payload, size_t plen, + key_serial_t keyring); + + +Revoking MKTME Keys via command line or system call:: + + keyctl revoke <key> + + long keyctl(KEYCTL_REVOKE, key_serial_t key); + + +Options Field Definition:: + + userkey= user provided encryption key. This key defaults to + a CPU generated (ephemeral) key if a userkey is not + defined here. + + algorithm= encryption algorithm to be used. Defaults to system + default TME algorithm. Select 'no_encrypt' for no + encryption. + + tweak= user provided tweak. This tweak will be added to the + user provided key. + + entropy= user provided entropy. This entropy will be used to + generated the CPU generated key. + +Algorithm Dependencies:: + + There will be algorithm dependencies that dictate which 'options' + actually make sense. For example, aes_xts_128 will require a + tweak key when a userkey is specified. Here we will document + these dependencies based on algorithms supported. At initial + release of the feature we will only support 2 algorithm choices: + aex_xts_128 and no_encrypt. + + +Sample usage MK-TME Key Service API with mktme_mprotect() API:: + + Add a key:: + key = add_key(mktme, name, "userkey=22 tweak=44", strlen(argv[3]), + KEY_SPEC_USER_KEYRING); + Map memory:: + ptr = mmap(NULL, size, prot, MAP_ANONYMOUS, -1, 0); + + Protect memory:: + ret = syscall(sys_mktme_mprotect, ptr, size, prot, key); + + Use protected memory:: + ................ + + Free memory:: + ret = munmap(ptr, size); + + Revoke key:: /* User may keep and resuse the key */ + ret = keyctl(KEYCTL_REVOKE, key); + diff --git a/include/keys/mktme-type.h b/include/keys/mktme-type.h new file mode 100644 index 0000000..4542606 --- /dev/null +++ b/include/keys/mktme-type.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Key service for Multi-KEY Total Memory Encryption + */ + +#ifndef _KEYS_MKTME_TYPE_H +#define _KEYS_MKTME_TYPE_H + +#include <linux/key.h> + +/* + * User can optionally provide encryption algorithm, encryption + * key, and tweak key. + */ + +#define MKTME_MAX_OPTION_SIZE 64 + +enum mktme_alg { + MKTME_ALG_AES_XTS_128, + MKTME_ALG_NO_ENCRYPT, /* do not encrypt */ + MKTME_ALG__LAST, +}; + +const char *const mktme_alg_name[MKTME_ALG__LAST] = { + [MKTME_ALG_AES_XTS_128] = "aes_xts_128", + [MKTME_ALG_NO_ENCRYPT] = "no_encrypt", +}; + +extern struct key_type key_type_mktme; + +#endif /* _KEYS_MKTME_TYPE_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 6462e66..3e5e619 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -101,3 +101,14 @@ config KEY_DH_OPERATIONS in the kernel. If you are unsure as to whether this is required, answer N. + +config MKTME_KEYS + bool "MKTME KEYS" + depends on KEYS + help + This option provides support for Multi-Key Total Memory + Encryption (MKTME). MKTME allows userspace to manage the + use of hardware programmed memory encryption keys for + encrypting any page of memory. + + If you are unsure as to whether this is required, answer N. diff --git a/security/keys/Makefile b/security/keys/Makefile index ef1581b..fa74bfc 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o obj-$(CONFIG_BIG_KEYS) += big_key.o obj-$(CONFIG_TRUSTED_KEYS) += trusted.o obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ +obj-$(CONFIG_MKTME_KEYS) += mktme.o diff --git a/security/keys/key.c b/security/keys/key.c index d97c939..5aa367b 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -679,6 +679,7 @@ struct key *key_lookup(key_serial_t id) spin_unlock(&key_serial_lock); return key; } +EXPORT_SYMBOL(key_lookup); /* * Find and lock the specified key type against removal. diff --git a/security/keys/mktme.c b/security/keys/mktme.c new file mode 100644 index 0000000..977a528 --- /dev/null +++ b/security/keys/mktme.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-3.0 + +/* See Documentation/security/keys/mktme.rst */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/key.h> +#include <linux/key-type.h> +#include <linux/init.h> +#include <linux/parser.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <asm/intel_pconfig.h> +#include <keys/mktme-type.h> +#include <keys/user-type.h> + +/** + * struct mktme_mapping - global mapping of MKTME software keys + * to hardware keyids. + * + * @lock: One lock used while (un)registering to protect the software + * map structure and the hardware state. + * + * @serial: Serial number associated with the software key. Userspace + * will use the serial number when invoking mktme_mprotect(). + * + * @count: Count is managed by mktme_mprotect(). Count is the number + * of mappings outstanding with this serial/keyid pair. + * + * The MKTME Key Service API manages the adding and removing of software + * keys. It maps software keys to hardware keyid slots and programs the + * hardware keyid slots with the user requested encryption options. + * + * API mktme_mprotect() references this structure when requests are + * made to protect memory with one of these mapped keys. + */ + +struct mktme_mapping { + struct mutex lock; /* protect mktme_map & hw state */ + struct data { + key_serial_t serial; + unsigned int count; + } id[]; +}; + +struct mktme_mapping *mktme_map; +unsigned int mktme_max_keyids; /* max hardware keyids */ +unsigned int mktme_mapped_keyids; /* number of keys mapped */ + +#define MKTME_DEBUG 0 +#if MKTME_DEBUG +static inline void dump_keys(void) +{ + int i = mktme_max_keyids + 1; + + while (i--) + pr_info(" [%d:%d:%d]\n", i, mktme_map->id[i].serial, + mktme_map->id[i].count); +} + +static inline void dump_kprog(struct mktme_key_program *kprog) +{ + print_hex_dump(KERN_INFO, "key_field_1: ", DUMP_PREFIX_NONE, 8, 1, + kprog->key_field_1, MKTME_MAX_OPTION_SIZE / 2, 1); + print_hex_dump(KERN_INFO, "key_field_2: ", DUMP_PREFIX_NONE, 8, 1, + kprog->key_field_2, MKTME_MAX_OPTION_SIZE / 2, 1); + pr_info("key_ctl [%x]\n", kprog->keyid_ctrl); +} +#else +static inline void dump_keys(void) +{ +} + +static inline void dump_kprog(struct mktme_key_program *kprog) +{ +} +#endif + +/* + * If a valid serial# is passed in, return the assigned keyid + * or EINVAL for invalid serial. + * If 0 is passed in, return an available keyid, or EINVAL if + * no more keyids are available. + */ + +int mktme_get_keyid(key_serial_t serial) +{ + int i = mktme_max_keyids; + + for (i = mktme_max_keyids; i > 0; i--) + if (mktme_map->id[i].serial == serial) + return i; + return -EINVAL; +} + +static int mktme_unregister_key(int keyid) +{ + struct mktme_key_program *kprog = NULL; + int ret; + + kprog = kzalloc(sizeof(*kprog), GFP_KERNEL); + if (!kprog) + return -ENOMEM; + + kprog->keyid = keyid; + kprog->keyid_ctrl = MKTME_KEYID_CLEAR_KEY; + + /* TODO ret = mktme_key_program(kprog); */ + ret = MKTME_PROG_SUCCESS; + + if (ret == MKTME_PROG_SUCCESS) { + mktme_map->id[kprog->keyid].serial = 0; + mktme_map->id[kprog->keyid].count = 0; + mktme_mapped_keyids--; + } + /* TODO pr the descriptive HW errors before passing up */ + + return ret; +} + +/* + * Check that for each keyid that is currently programmed, there is a + * valid userspace key associated. If the userspace key no longer exists, + * unregister it (clear it from both software and hardware) + * + * So far - it seems we can get here if a 'keyctl invalidate' is done. + * If we can find a way to block unwanted key control options, this defense + * could be removed. + * + * Call with mktme_map->lock held. + * + * Returns the keyid recovered, or 0 if no key is recovered. + */ + +static int mktme_recover_key(void) +{ + int i = mktme_max_keyids; + struct key *key; + + do { + if (!mktme_map->id[i].serial) + continue; + + key = key_lookup(mktme_map->id[i].serial); + if (IS_ERR(key)) + goto recover; + + if (key_validate(key) < 0) { + /* + * Here the key ptr is good, but the + * key may * be marked for removal. + * Leave this here to watch for. + */ + pr_info("%s key validate fails\n", __func__); + goto recover; + } + } while (i--); + + return 0; +recover: + mktme_unregister_key(i); + return i; +} + +/* Add the key to software map and progam key into the hardware. */ + +static int mktme_register_key(key_serial_t serial, + struct mktme_key_program *kprog) +{ + int keyid, ret; + + /* + * If it appears that we are out of keyid's, try to + * recover an abandoned keyid. Note that Keyid 0 is + * reserved for system level TME. + */ + + if (mktme_mapped_keyids < mktme_max_keyids) + keyid = mktme_get_keyid(0); + else + keyid = mktme_recover_key(); + + if (keyid == 0) + return -EDQUOT; + + kprog->keyid = keyid; + dump_kprog(kprog); + + /* TODO ret = mktme_key_program(kprog); */ + ret = MKTME_PROG_SUCCESS; + if (ret == MKTME_PROG_SUCCESS) { + mktme_map->id[keyid].serial = serial; + mktme_map->id[keyid].count = 0; + mktme_mapped_keyids++; + } + /* TODO pr the descriptive HW errors before passing up */ + + return ret; +} + +enum { + opt_err = -1, + opt_userkey, + opt_tweak, + opt_entropy, + opt_algorithm, +}; + +static const match_table_t mktme_tokens = { + {opt_userkey, "userkey=%s"}, + {opt_tweak, "tweak=%s"}, + {opt_entropy, "entropy=%s"}, + {opt_algorithm, "algorithm=%s"}, + {opt_err, NULL} +}; + +/* + * Sanity check the user specified options against each algorithms + * requirements. + * + * Success returns 0, otherwise -EINVAL. + */ + +static int mktme_check_options(struct mktme_key_program *kprog, + unsigned long token_mask) +{ + /* no userkey specified, use cpu generated key */ + if (!test_bit(opt_userkey, &token_mask)) + kprog->keyid_ctrl |= MKTME_KEYID_SET_KEY_RANDOM; + + /* no algorithm specified, use aes_xts_128 */ + if (!test_bit(opt_algorithm, &token_mask)) + kprog->keyid_ctrl |= MKTME_AES_XTS_128; + + /* userkey specified, no entropy allowed */ + if ((test_bit(opt_userkey, &token_mask)) && + (test_bit(opt_entropy, &token_mask))) { + pr_err("mktme: entropy not an option with userkey\n"); + return -EINVAL; + } + /* userkey specified with aes_xts_128, requires tweak */ + if ((test_bit(opt_userkey, &token_mask)) && + (kprog->keyid_ctrl & MKTME_AES_XTS_128)) { + if (!(test_bit(opt_tweak, &token_mask))) { + pr_err("mktme: algorithm requires a tweak key\n"); + return -EINVAL; + } + } + dump_kprog(kprog); + return 0; +} + +/* Parse the options from the datablob and fill in struct mktme_key_program. + * After parsing, call mktme_check_options() for sanity checking. + * + * Success returns 0, otherwise -EINVAL. + */ +static int mktme_get_options(char *datablob, struct mktme_key_program *kprog) +{ + enum mktme_alg alg = MKTME_ALG__LAST; + substring_t args[MAX_OPT_ARGS]; + unsigned long token_mask = 0; + int len, ret, token; + char *p = datablob; + + while ((p = strsep(&datablob, " \t"))) { + if (*p == '\0' || *p == ' ' || *p == '\t') + continue; + token = match_token(p, mktme_tokens, args); + if (test_and_set_bit(token, &token_mask)) + return -EINVAL; + + len = strlen(args[0].from) / 2; + if (len > MKTME_MAX_OPTION_SIZE) + return -EINVAL; + + switch (token) { + case opt_userkey: + ret = hex2bin(kprog->key_field_1, args[0].from, len); + if (ret < 0) + return -EINVAL; + kprog->keyid_ctrl |= MKTME_KEYID_SET_KEY_DIRECT; + break; + + case opt_tweak: + ret = hex2bin(kprog->key_field_2, args[0].from, len); + if (ret < 0) + return -EINVAL; + break; + + case opt_entropy: + ret = hex2bin(kprog->key_field_1, args[0].from, len); + if (ret < 0) + return -EINVAL; + break; + + case opt_algorithm: + alg = match_string(mktme_alg_name, MKTME_ALG__LAST, + args[0].from); + switch (alg) { + case MKTME_ALG_AES_XTS_128: + kprog->keyid_ctrl |= MKTME_AES_XTS_128; + break; + + case MKTME_ALG_NO_ENCRYPT: + kprog->keyid_ctrl |= MKTME_KEYID_NO_ENCRYPT; + break; + + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + } + dump_kprog(kprog); + return mktme_check_options(kprog, token_mask); +} + +/* Key Service Command: Creates a software key and programs hardware */ + +int mktme_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + struct mktme_key_program *kprog = NULL; + size_t datalen = prep->datalen; + char *datablob; + int ret = 0; + + if (datalen <= 0 || datalen > 1024 || !prep->data) + return -EINVAL; + + datablob = kmemdup(prep->data, datalen + 1, GFP_KERNEL); + if (!datablob) + return -ENOMEM; + + datablob[datalen] = '\0'; + kprog = kzalloc(sizeof(*kprog), GFP_KERNEL); + if (!kprog) { + kzfree(datablob); + return -ENOMEM; + } + ret = mktme_get_options(datablob, kprog); + if (ret < 0) + goto out; + + mutex_lock(&mktme_map->lock); + ret = mktme_register_key(key->serial, kprog); + mutex_unlock(&mktme_map->lock); +out: + kzfree(datablob); + kzfree(kprog); + dump_keys(); + return ret; +} + +/* Key Service Command: Clears the keys software and hardware */ + +void mktme_revoke(struct key *key) +{ + int keyid; + + keyid = mktme_get_keyid(key->serial); + if (keyid < 0) + return; + + mutex_lock(&mktme_map->lock); + mktme_unregister_key(keyid); + mutex_unlock(&mktme_map->lock); + dump_keys(); +} + +struct key_type key_type_mktme = { + .name = "mktme", + .instantiate = mktme_instantiate, + .revoke = mktme_revoke, + .describe = user_describe, +}; + +/* + * Get the maximum keyids reported from BIOS. + * Allocate the mktme_map structure and register mktme key type. + */ +static int __init init_mktme(void) +{ + int ret; + + /* TODO: get real max hardware keyids */ + /* mktme_max_keyids = nr_keyids; */ + + mktme_max_keyids = 100; + mktme_mapped_keyids = 0; + mktme_map = kzalloc((sizeof(mktme_map->id[0]) * mktme_max_keyids) + + (sizeof(mktme_map->lock)), GFP_KERNEL); + if (!mktme_map) + return -ENOMEM; + + mutex_init(&mktme_map->lock); + ret = register_key_type(&key_type_mktme); + if (ret < 0) + kfree(mktme_map); + + return ret; +} + +static void __exit cleanup_mktme(void) +{ + unregister_key_type(&key_type_mktme); + kfree(mktme_map); +} + +late_initcall(init_mktme);
Sorry all! Error in Davids email in that first email. Please use this one for replies. Hi David & Key Community, I'm requesting your comments on placing the MK-TME API in the Kernel Key Service API set. I'm hoping to get a thumbs-up on the general direction before going further down this path. Key Services seems to offer a tremendous amount of functionality. I'd like to hear if you think so too, address any concerns, or hear other suggestions for its placement. Here are the details... MK-TME (Multi-Key Total Memory Encryption) is a technology that allows transparent memory encryption in upcoming Intel platforms. Whereas TME allows encryption of the entire system memory using a single key, MK-TME allows mulitple encryption domains, each having their own key. The main use case for the feature is virtual machine isolation. The API, however, needs the flexibility to work for a wide range of uses. Kirill Shutemov has a patchset for the core kernel support that has been making its way upstream. Find that here: git://git.kernel.org/pub/scm/linux/kernel/git/kas/linux.git mktme/wip After considering alternatives (new API, crypto API) this POC adds "mktme" as a new key service to the existing kernel key services. The mktme key service will manage the adding and removing of software keys. It will map software keys to hardware keyid slots and program the hardware keyid slots with the user requested encryption options. The mktme key service will not store any encryption keys in the kernel. We program the hardware and basically throw away the key. We only retain a mapping of which software key is assigned to which hardware keyid. It is not even possible to read back any of the programming info (encryption algorithm, key, tweak, entropy) once programmed. The mktme key service is half of the API level solution. It will be paired with a new API that uses these keys to protect the memory. You will see reference to that mktme_mprotect() system call in the example documentation included in the patch. It doesn't exist yet. The first file in the patch is Documentation with sample usages. Signed-off-by: Alison Schofield <alison.schofield@intel.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Kirill Shutemov <kirill.shutemov@intel.com> --- Documentation/security/keys/mktme.rst | 69 ++++++ include/keys/mktme-type.h | 32 +++ security/keys/Kconfig | 11 + security/keys/Makefile | 1 + security/keys/key.c | 1 + security/keys/mktme.c | 413 ++++++++++++++++++++++++++++++++++ 6 files changed, 527 insertions(+) create mode 100644 Documentation/security/keys/mktme.rst create mode 100644 include/keys/mktme-type.h create mode 100644 security/keys/mktme.c