Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 95d3652e authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'fixes-v4.14-rc3' of...

Merge branch 'fixes-v4.14-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull keys fixes from James Morris:
 "Notable here is a rewrite of big_key crypto by Jason Donenfeld to
  address some issues in the original code.

  From Jason's commit log:
   "This started out as just replacing the use of crypto/rng with
    get_random_bytes_wait, so that we wouldn't use bad randomness at
    boot time. But, upon looking further, it appears that there were
    even deeper underlying cryptographic problems, and that this seems
    to have been committed with very little crypto review. So, I rewrote
    the whole thing, trying to keep to the conventions introduced by the
    previous author, to fix these cryptographic flaws."

  There has been positive review of the new code by Eric Biggers and
  Herbert Xu, and it passes basic testing via the keyutils test suite.
  Eric also manually tested it.

  Generally speaking, we likely need to improve the amount of crypto
  review for kernel crypto users including keys (I'll post a note
  separately to ksummit-discuss)"

* 'fixes-v4.14-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  security/keys: rewrite all of big_key crypto
  security/keys: properly zero out sensitive key material in big_key
  KEYS: use kmemdup() in request_key_auth_new()
  KEYS: restrict /proc/keys by credentials at open time
  KEYS: reset parent each time before searching key_user_tree
  KEYS: prevent KEYCTL_READ on negative key
  KEYS: prevent creating a different user's keyrings
  KEYS: fix writing past end of user-supplied buffer in keyring_read()
  KEYS: fix key refcount leak in keyctl_read_key()
  KEYS: fix key refcount leak in keyctl_assume_authority()
  KEYS: don't revoke uninstantiated key in request_key_auth_new()
  KEYS: fix cred refcount leak in request_key_auth_new()
parents 770b782f 2569e7e1
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ struct key {
#define KEY_FLAG_BUILTIN	8	/* set if key is built in to the kernel */
#define KEY_FLAG_ROOT_CAN_INVAL	9	/* set if key can be invalidated by root without permission */
#define KEY_FLAG_KEEP		10	/* set if key should not be removed */
#define KEY_FLAG_UID_KEYRING	11	/* set if key is a user or user session keyring */

	/* the key type and key description string
	 * - the desc is used to match a key against search criteria
@@ -243,6 +244,7 @@ extern struct key *key_alloc(struct key_type *type,
#define KEY_ALLOC_NOT_IN_QUOTA		0x0002	/* not in quota */
#define KEY_ALLOC_BUILT_IN		0x0004	/* Key is built into kernel */
#define KEY_ALLOC_BYPASS_RESTRICTION	0x0008	/* Override the check on restricted keyrings */
#define KEY_ALLOC_UID_KEYRING		0x0010	/* allocating a user or user session keyring */

extern void key_revoke(struct key *key);
extern void key_invalidate(struct key *key);
+1 −3
Original line number Diff line number Diff line
@@ -45,10 +45,8 @@ config BIG_KEYS
	bool "Large payload keys"
	depends on KEYS
	depends on TMPFS
	depends on (CRYPTO_ANSI_CPRNG = y || CRYPTO_DRBG = y)
	select CRYPTO_AES
	select CRYPTO_ECB
	select CRYPTO_RNG
	select CRYPTO_GCM
	help
	  This option provides support for holding large keys within the kernel
	  (for example Kerberos ticket caches).  The data may be stored out to
+65 −74
Original line number Diff line number Diff line
/* Large capacity key type
 *
 * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
 * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
@@ -16,10 +17,10 @@
#include <linux/shmem_fs.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/random.h>
#include <keys/user-type.h>
#include <keys/big_key-type.h>
#include <crypto/rng.h>
#include <crypto/skcipher.h>
#include <crypto/aead.h>

/*
 * Layout of key payload words.
@@ -49,7 +50,12 @@ enum big_key_op {
/*
 * Key size for big_key data encryption
 */
#define ENC_KEY_SIZE	16
#define ENC_KEY_SIZE 32

/*
 * Authentication tag length
 */
#define ENC_AUTHTAG_SIZE 16

/*
 * big_key defined keys take an arbitrary string as the description and an
@@ -64,57 +70,62 @@ struct key_type key_type_big_key = {
	.destroy		= big_key_destroy,
	.describe		= big_key_describe,
	.read			= big_key_read,
	/* no ->update(); don't add it without changing big_key_crypt() nonce */
};

/*
 * Crypto names for big_key data encryption
 * Crypto names for big_key data authenticated encryption
 */
static const char big_key_rng_name[] = "stdrng";
static const char big_key_alg_name[] = "ecb(aes)";
static const char big_key_alg_name[] = "gcm(aes)";

/*
 * Crypto algorithms for big_key data encryption
 * Crypto algorithms for big_key data authenticated encryption
 */
static struct crypto_rng *big_key_rng;
static struct crypto_skcipher *big_key_skcipher;
static struct crypto_aead *big_key_aead;

/*
 * Generate random key to encrypt big_key data
 * Since changing the key affects the entire object, we need a mutex.
 */
static inline int big_key_gen_enckey(u8 *key)
{
	return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
}
static DEFINE_MUTEX(big_key_aead_lock);

/*
 * Encrypt/decrypt big_key data
 */
static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
{
	int ret = -EINVAL;
	int ret;
	struct scatterlist sgio;
	SKCIPHER_REQUEST_ON_STACK(req, big_key_skcipher);
	struct aead_request *aead_req;
	/* We always use a zero nonce. The reason we can get away with this is
	 * because we're using a different randomly generated key for every
	 * different encryption. Notably, too, key_type_big_key doesn't define
	 * an .update function, so there's no chance we'll wind up reusing the
	 * key to encrypt updated data. Simply put: one key, one encryption.
	 */
	u8 zero_nonce[crypto_aead_ivsize(big_key_aead)];

	if (crypto_skcipher_setkey(big_key_skcipher, key, ENC_KEY_SIZE)) {
	aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL);
	if (!aead_req)
		return -ENOMEM;

	memset(zero_nonce, 0, sizeof(zero_nonce));
	sg_init_one(&sgio, data, datalen + (op == BIG_KEY_ENC ? ENC_AUTHTAG_SIZE : 0));
	aead_request_set_crypt(aead_req, &sgio, &sgio, datalen, zero_nonce);
	aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
	aead_request_set_ad(aead_req, 0);

	mutex_lock(&big_key_aead_lock);
	if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) {
		ret = -EAGAIN;
		goto error;
	}

	skcipher_request_set_tfm(req, big_key_skcipher);
	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
				      NULL, NULL);

	sg_init_one(&sgio, data, datalen);
	skcipher_request_set_crypt(req, &sgio, &sgio, datalen, NULL);

	if (op == BIG_KEY_ENC)
		ret = crypto_skcipher_encrypt(req);
		ret = crypto_aead_encrypt(aead_req);
	else
		ret = crypto_skcipher_decrypt(req);

	skcipher_request_zero(req);

		ret = crypto_aead_decrypt(aead_req);
error:
	mutex_unlock(&big_key_aead_lock);
	aead_request_free(aead_req);
	return ret;
}

@@ -146,16 +157,13 @@ int big_key_preparse(struct key_preparsed_payload *prep)
		 *
		 * File content is stored encrypted with randomly generated key.
		 */
		size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
		size_t enclen = datalen + ENC_AUTHTAG_SIZE;
		loff_t pos = 0;

		/* prepare aligned data to encrypt */
		data = kmalloc(enclen, GFP_KERNEL);
		if (!data)
			return -ENOMEM;

		memcpy(data, prep->data, datalen);
		memset(data + datalen, 0x00, enclen - datalen);

		/* generate random key */
		enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
@@ -163,13 +171,12 @@ int big_key_preparse(struct key_preparsed_payload *prep)
			ret = -ENOMEM;
			goto error;
		}

		ret = big_key_gen_enckey(enckey);
		if (ret)
		ret = get_random_bytes_wait(enckey, ENC_KEY_SIZE);
		if (unlikely(ret))
			goto err_enckey;

		/* encrypt aligned data */
		ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
		ret = big_key_crypt(BIG_KEY_ENC, data, datalen, enckey);
		if (ret)
			goto err_enckey;

@@ -195,7 +202,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
		*path = file->f_path;
		path_get(path);
		fput(file);
		kfree(data);
		kzfree(data);
	} else {
		/* Just store the data in a buffer */
		void *data = kmalloc(datalen, GFP_KERNEL);
@@ -211,9 +218,9 @@ int big_key_preparse(struct key_preparsed_payload *prep)
err_fput:
	fput(file);
err_enckey:
	kfree(enckey);
	kzfree(enckey);
error:
	kfree(data);
	kzfree(data);
	return ret;
}

@@ -227,7 +234,7 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)

		path_put(path);
	}
	kfree(prep->payload.data[big_key_data]);
	kzfree(prep->payload.data[big_key_data]);
}

/*
@@ -259,7 +266,7 @@ void big_key_destroy(struct key *key)
		path->mnt = NULL;
		path->dentry = NULL;
	}
	kfree(key->payload.data[big_key_data]);
	kzfree(key->payload.data[big_key_data]);
	key->payload.data[big_key_data] = NULL;
}

@@ -295,7 +302,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
		struct file *file;
		u8 *data;
		u8 *enckey = (u8 *)key->payload.data[big_key_data];
		size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
		size_t enclen = datalen + ENC_AUTHTAG_SIZE;
		loff_t pos = 0;

		data = kmalloc(enclen, GFP_KERNEL);
@@ -328,7 +335,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
err_fput:
		fput(file);
error:
		kfree(data);
		kzfree(data);
	} else {
		ret = datalen;
		if (copy_to_user(buffer, key->payload.data[big_key_data],
@@ -344,47 +351,31 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
 */
static int __init big_key_init(void)
{
	struct crypto_skcipher *cipher;
	struct crypto_rng *rng;
	int ret;

	rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
	if (IS_ERR(rng)) {
		pr_err("Can't alloc rng: %ld\n", PTR_ERR(rng));
		return PTR_ERR(rng);
	}

	big_key_rng = rng;

	/* seed RNG */
	ret = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng));
	if (ret) {
		pr_err("Can't reset rng: %d\n", ret);
		goto error_rng;
	}

	/* init block cipher */
	cipher = crypto_alloc_skcipher(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(cipher)) {
		ret = PTR_ERR(cipher);
	big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(big_key_aead)) {
		ret = PTR_ERR(big_key_aead);
		pr_err("Can't alloc crypto: %d\n", ret);
		goto error_rng;
		return ret;
	}
	ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE);
	if (ret < 0) {
		pr_err("Can't set crypto auth tag len: %d\n", ret);
		goto free_aead;
	}

	big_key_skcipher = cipher;

	ret = register_key_type(&key_type_big_key);
	if (ret < 0) {
		pr_err("Can't register type: %d\n", ret);
		goto error_cipher;
		goto free_aead;
	}

	return 0;

error_cipher:
	crypto_free_skcipher(big_key_skcipher);
error_rng:
	crypto_free_rng(big_key_rng);
free_aead:
	crypto_free_aead(big_key_aead);
	return ret;
}

+1 −1
Original line number Diff line number Diff line
@@ -141,7 +141,7 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);

extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);

extern int install_user_keyrings(void);
extern int install_thread_keyring_to_cred(struct cred *);
+4 −2
Original line number Diff line number Diff line
@@ -54,10 +54,10 @@ void __key_check(const struct key *key)
struct key_user *key_user_lookup(kuid_t uid)
{
	struct key_user *candidate = NULL, *user;
	struct rb_node *parent = NULL;
	struct rb_node **p;
	struct rb_node *parent, **p;

try_again:
	parent = NULL;
	p = &key_user_tree.rb_node;
	spin_lock(&key_user_lock);

@@ -302,6 +302,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
		key->flags |= 1 << KEY_FLAG_IN_QUOTA;
	if (flags & KEY_ALLOC_BUILT_IN)
		key->flags |= 1 << KEY_FLAG_BUILTIN;
	if (flags & KEY_ALLOC_UID_KEYRING)
		key->flags |= 1 << KEY_FLAG_UID_KEYRING;

#ifdef KEY_DEBUGGING
	key->magic = KEY_DEBUG_MAGIC;
Loading