RESEND [RFC] KEYS: add a new type "mktme" to kernel key services

David Howells dhowells at redhat.com
Fri Jun 8 16:33:04 UTC 2018


Alison Schofield <alison.schofield at 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 at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the Linux-security-module-archive mailing list