All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michael Halcrow <mhalcrow@google.com>
To: Eric Biggers <ebiggers3@gmail.com>
Cc: linux-fscrypt@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	linux-ext4@vger.kernel.org,
	linux-f2fs-devel@lists.sourceforge.net,
	linux-mtd@lists.infradead.org, linux-api@vger.kernel.org,
	keyrings@vger.kernel.org, "Theodore Y . Ts'o" <tytso@mit.edu>,
	Jaegeuk Kim <jaegeuk@kernel.org>,
	Gwendal Grignou <gwendal@chromium.org>,
	Ryo Hashimoto <hashimoto@chromium.org>,
	Sarthak Kukreti <sarthakkukreti@chromium.org>,
	Nick Desaulniers <ndesaulniers@google.com>,
	Eric Biggers <ebiggers@google.com>
Subject: Re: [RFC PATCH 04/25] fscrypt: refactor finding and deriving key
Date: Fri, 27 Oct 2017 11:23:03 -0700	[thread overview]
Message-ID: <20171027182303.GD10611@google.com> (raw)
In-Reply-To: <20171023214058.128121-5-ebiggers3@gmail.com>

On Mon, Oct 23, 2017 at 02:40:37PM -0700, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@google.com>
> 
> In preparation for introducing a new way to find the master keys and
> derive the per-file keys, clean up the current method.  This includes:
> 
> - Introduce a helper function find_and_derive_key() so that we don't
>   have to add more code directly to fscrypt_get_encryption_info().
> 
> - Don't pass the 'struct fscrypt_key' directly into derive_key_aes().
>   This is in preparation for the case where we find the master key in a
>   filesystem-level keyring, where (for good reasons) the key payload
>   will *not* be formatted as the UAPI 'struct fscrypt_key'.
> 
> - Separate finding the key from key derivation.  In particular, it
>   *only* makes sense to fall back to the alternate key description
>   prefix if searching for the "fscrypt:" prefix returns -ENOKEY.  It
>   doesn't make sense to do so when derive_key_aes() fails, for example.
> 
> - Improve the error messages for when the fscrypt_key is invalid.
> 
> - Rename 'raw_key' to 'derived_key' for clarity.

With all the crypto code I've delt with where a 'key' is actually just
a handle or a reference, I've developed a personal habit is to call
the buffer that contains the actual key bytes 'raw' to emphasize that
there's tangible secret material present.

That said, it's probably fine to call it 'derived_key' in this
instance.

> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>

Reviewed-by: Michael Halcrow <mhalcrow@google.com>

> ---
>  fs/crypto/keyinfo.c | 205 ++++++++++++++++++++++++++++------------------------
>  1 file changed, 109 insertions(+), 96 deletions(-)
> 
> diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
> index ac41f646e7b7..d3a97c2cd4dd 100644
> --- a/fs/crypto/keyinfo.c
> +++ b/fs/crypto/keyinfo.c
> @@ -28,113 +28,138 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
>  	complete(&ecr->completion);
>  }
>  
> -/**
> - * derive_key_aes() - Derive a key using AES-128-ECB
> - * @deriving_key: Encryption key used for derivation.
> - * @source_key:   Source key to which to apply derivation.
> - * @derived_raw_key:  Derived raw key.
> +/*
> + * Key derivation function.  This generates the derived key by encrypting the
> + * master key with AES-128-ECB using the nonce as the AES key.
>   *
> - * Return: Zero on success; non-zero otherwise.
> + * The master key must be at least as long as the derived key.  If the master
> + * key is longer, then only the first 'derived_keysize' bytes are used.
>   */
> -static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
> -				const struct fscrypt_key *source_key,
> -				u8 derived_raw_key[FSCRYPT_MAX_KEY_SIZE])
> +static int derive_key_aes(const u8 *master_key,
> +			  const struct fscrypt_context *ctx,
> +			  u8 *derived_key, unsigned int derived_keysize)
>  {
> -	int res = 0;
> +	int err;
>  	struct skcipher_request *req = NULL;
>  	DECLARE_FS_COMPLETION_RESULT(ecr);
>  	struct scatterlist src_sg, dst_sg;
> -	struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
> +	struct crypto_skcipher *tfm;
> +
> +	tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
> +	if (IS_ERR(tfm))
> +		return PTR_ERR(tfm);
>  
> -	if (IS_ERR(tfm)) {
> -		res = PTR_ERR(tfm);
> -		tfm = NULL;
> -		goto out;
> -	}
>  	crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
>  	req = skcipher_request_alloc(tfm, GFP_NOFS);
>  	if (!req) {
> -		res = -ENOMEM;
> +		err = -ENOMEM;
>  		goto out;
>  	}
>  	skcipher_request_set_callback(req,
>  			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
>  			derive_crypt_complete, &ecr);
> -	res = crypto_skcipher_setkey(tfm, deriving_key,
> -					FS_AES_128_ECB_KEY_SIZE);
> -	if (res < 0)
> +
> +	BUILD_BUG_ON(sizeof(ctx->nonce) != FS_AES_128_ECB_KEY_SIZE);
> +	err = crypto_skcipher_setkey(tfm, ctx->nonce, sizeof(ctx->nonce));
> +	if (err)
>  		goto out;
>  
> -	sg_init_one(&src_sg, source_key->raw, source_key->size);
> -	sg_init_one(&dst_sg, derived_raw_key, source_key->size);
> -	skcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size,
> +	sg_init_one(&src_sg, master_key, derived_keysize);
> +	sg_init_one(&dst_sg, derived_key, derived_keysize);
> +	skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize,
>  				   NULL);
> -	res = crypto_skcipher_encrypt(req);
> -	if (res == -EINPROGRESS || res == -EBUSY) {
> +	err = crypto_skcipher_encrypt(req);
> +	if (err == -EINPROGRESS || err == -EBUSY) {
>  		wait_for_completion(&ecr.completion);
> -		res = ecr.res;
> +		err = ecr.res;
>  	}
>  out:
>  	skcipher_request_free(req);
>  	crypto_free_skcipher(tfm);
> -	return res;
> +	return err;
>  }
>  
> -static int validate_user_key(struct fscrypt_info *crypt_info,
> -			struct fscrypt_context *ctx, u8 *raw_key,
> -			const char *prefix, int min_keysize)
> +/*
> + * Search the current task's subscribed keyrings for a "logon" key with
> + * description prefix:descriptor, and if found acquire a read lock on it and
> + * return a pointer to its validated payload in *payload_ret.
> + */
> +static struct key *
> +find_and_lock_process_key(const char *prefix,
> +			  const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE],
> +			  unsigned int min_keysize,
> +			  const struct fscrypt_key **payload_ret)

Since we've already crossed the threshold of returning at least one
object via a ptr-to-ptr param, maybe doing that for struct key too and
just making the return value of the function be an int err would be
cleaner than PTR_ERR/ERR_PTR?

>  {
>  	char *description;
> -	struct key *keyring_key;
> -	struct fscrypt_key *master_key;
> +	struct key *key;
>  	const struct user_key_payload *ukp;
> -	int res;
> +	const struct fscrypt_key *payload;
>  
>  	description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
> -				FSCRYPT_KEY_DESCRIPTOR_SIZE,
> -				ctx->master_key_descriptor);
> +				FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor);
>  	if (!description)
> -		return -ENOMEM;
> +		return ERR_PTR(-ENOMEM);
>  
> -	keyring_key = request_key(&key_type_logon, description, NULL);
> +	key = request_key(&key_type_logon, description, NULL);
>  	kfree(description);
> -	if (IS_ERR(keyring_key))
> -		return PTR_ERR(keyring_key);
> -	down_read(&keyring_key->sem);
> -
> -	if (keyring_key->type != &key_type_logon) {
> -		printk_once(KERN_WARNING
> -				"%s: key type must be logon\n", __func__);
> -		res = -ENOKEY;
> -		goto out;
> -	}
> -	ukp = user_key_payload_locked(keyring_key);
> -	if (!ukp) {
> -		/* key was revoked before we acquired its semaphore */
> -		res = -EKEYREVOKED;
> -		goto out;
> +	if (IS_ERR(key))
> +		return key;
> +
> +	down_read(&key->sem);
> +	ukp = user_key_payload_locked(key);
> +
> +	if (!ukp) /* was the key revoked before we acquired its semaphore? */
> +		goto invalid;
> +
> +	payload = (const struct fscrypt_key *)ukp->data;
> +
> +	if (ukp->datalen != sizeof(struct fscrypt_key) ||
> +	    payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) {
> +		pr_warn_ratelimited("fscrypt: key with description '%s' has invalid payload\n",
> +				    key->description);
> +		goto invalid;
>  	}
> -	if (ukp->datalen != sizeof(struct fscrypt_key)) {
> -		res = -EINVAL;
> -		goto out;
> +
> +	if (payload->size < min_keysize) {
> +		pr_warn_ratelimited("fscrypt: key with description '%s' is too short "
> +				    "(got %u bytes, need %u+ bytes)\n",
> +				    key->description,
> +				    payload->size, min_keysize);
> +		goto invalid;

A common (yet high-impact) mistake is to pass in only 256 bits of
entropic key material in the 512-bit buffer for AES-256-XTS, leaving
the second half all 0's.  I've actually seen that done in pre-release
code.  It would be an easy check just to see if userspace did that,
but then we're on the slippery slope of how much key strength
validation we should do on the key, if we're going to do any at all.

>  	}
> -	master_key = (struct fscrypt_key *)ukp->data;
> -	BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
> -
> -	if (master_key->size < min_keysize ||
> -	    master_key->size > FSCRYPT_MAX_KEY_SIZE
> -	    || master_key->size % AES_BLOCK_SIZE != 0) {
> -		printk_once(KERN_WARNING
> -				"%s: key size incorrect: %d\n",
> -				__func__, master_key->size);
> -		res = -ENOKEY;
> -		goto out;
> +
> +	*payload_ret = payload;
> +	return key;
> +
> +invalid:
> +	up_read(&key->sem);
> +	key_put(key);
> +	return ERR_PTR(-ENOKEY);
> +}
> +
> +/* Find the master key, then derive the inode's actual encryption key */
> +static int find_and_derive_key(const struct inode *inode,
> +			       const struct fscrypt_context *ctx,
> +			       u8 *derived_key, unsigned int derived_keysize)
> +{
> +	struct key *key;
> +	const struct fscrypt_key *payload;
> +	int err;
> +
> +	key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,
> +					ctx->master_key_descriptor,
> +					derived_keysize, &payload);
> +	if (key == ERR_PTR(-ENOKEY) && inode->i_sb->s_cop->key_prefix) {
> +		key = find_and_lock_process_key(inode->i_sb->s_cop->key_prefix,
> +						ctx->master_key_descriptor,
> +						derived_keysize, &payload);
>  	}
> -	res = derive_key_aes(ctx->nonce, master_key, raw_key);
> -out:
> -	up_read(&keyring_key->sem);
> -	key_put(keyring_key);
> -	return res;
> +	if (IS_ERR(key))
> +		return PTR_ERR(key);
> +	err = derive_key_aes(payload->raw, ctx, derived_key, derived_keysize);
> +	up_read(&key->sem);
> +	key_put(key);
> +	return err;
>  }
>  
>  static const struct {
> @@ -256,8 +281,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	struct fscrypt_context ctx;
>  	struct crypto_skcipher *ctfm;
>  	const char *cipher_str;
> -	int keysize;
> -	u8 *raw_key = NULL;
> +	unsigned int derived_keysize;
> +	u8 *derived_key = NULL;
>  	int res;
>  
>  	if (inode->i_crypt_info)
> @@ -301,7 +326,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
>  				sizeof(crypt_info->ci_master_key));
>  
> -	res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
> +	res = determine_cipher_type(crypt_info, inode,
> +				    &cipher_str, &derived_keysize);
>  	if (res)
>  		goto out;
>  
> @@ -310,24 +336,14 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	 * crypto API as part of key derivation.
>  	 */
>  	res = -ENOMEM;
> -	raw_key = kmalloc(FSCRYPT_MAX_KEY_SIZE, GFP_NOFS);
> -	if (!raw_key)
> +	derived_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
> +	if (!derived_key)
>  		goto out;
>  
> -	res = validate_user_key(crypt_info, &ctx, raw_key,
> -				FSCRYPT_KEY_DESC_PREFIX, keysize);
> -	if (res && inode->i_sb->s_cop->key_prefix) {
> -		int res2 = validate_user_key(crypt_info, &ctx, raw_key,
> -					     inode->i_sb->s_cop->key_prefix,
> -					     keysize);
> -		if (res2) {
> -			if (res2 == -ENOKEY)
> -				res = -ENOKEY;
> -			goto out;
> -		}
> -	} else if (res) {
> +	res = find_and_derive_key(inode, &ctx, derived_key, derived_keysize);
> +	if (res)
>  		goto out;
> -	}
> +
>  	ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
>  	if (!ctfm || IS_ERR(ctfm)) {
>  		res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
> @@ -338,17 +354,14 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	crypt_info->ci_ctfm = ctfm;
>  	crypto_skcipher_clear_flags(ctfm, ~0);
>  	crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
> -	/*
> -	 * if the provided key is longer than keysize, we use the first
> -	 * keysize bytes of the derived key only
> -	 */
> -	res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
> +	res = crypto_skcipher_setkey(ctfm, derived_key, derived_keysize);
>  	if (res)
>  		goto out;
>  
>  	if (S_ISREG(inode->i_mode) &&
>  	    crypt_info->ci_data_mode == FSCRYPT_MODE_AES_128_CBC) {
> -		res = init_essiv_generator(crypt_info, raw_key, keysize);
> +		res = init_essiv_generator(crypt_info, derived_key,
> +					   derived_keysize);
>  		if (res) {
>  			pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n",
>  				 __func__, res, inode->i_ino);
> @@ -361,7 +374,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	if (res == -ENOKEY)
>  		res = 0;
>  	put_crypt_info(crypt_info);
> -	kzfree(raw_key);
> +	kzfree(derived_key);
>  	return res;
>  }
>  EXPORT_SYMBOL(fscrypt_get_encryption_info);
> -- 
> 2.15.0.rc0.271.g36b669edcc-goog
> 

WARNING: multiple messages have this Message-ID (diff)
From: Michael Halcrow <mhalcrow@google.com>
To: Eric Biggers <ebiggers3@gmail.com>
Cc: linux-fscrypt@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	linux-ext4@vger.kernel.org,
	linux-f2fs-devel@lists.sourceforge.net,
	linux-mtd@lists.infradead.org, linux-api@vger.kernel.org,
	keyrings@vger.kernel.org, "Theodore Y . Ts'o" <tytso@mit.edu>,
	Jaegeuk Kim <jaegeuk@kernel.org>,
	Gwendal Grignou <gwendal@chromium.org>,
	Ryo Hashimoto <hashimoto@chromium.org>,
	Sarthak Kukreti <sarthakkukreti@chromium.org>,
	Nick Desaulniers <ndesaulniers@google.com>,
	Eric Biggers <ebiggers@google.com>
Subject: Re: [RFC PATCH 04/25] fscrypt: refactor finding and deriving key
Date: Fri, 27 Oct 2017 18:23:03 +0000	[thread overview]
Message-ID: <20171027182303.GD10611@google.com> (raw)
In-Reply-To: <20171023214058.128121-5-ebiggers3@gmail.com>

On Mon, Oct 23, 2017 at 02:40:37PM -0700, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@google.com>
> 
> In preparation for introducing a new way to find the master keys and
> derive the per-file keys, clean up the current method.  This includes:
> 
> - Introduce a helper function find_and_derive_key() so that we don't
>   have to add more code directly to fscrypt_get_encryption_info().
> 
> - Don't pass the 'struct fscrypt_key' directly into derive_key_aes().
>   This is in preparation for the case where we find the master key in a
>   filesystem-level keyring, where (for good reasons) the key payload
>   will *not* be formatted as the UAPI 'struct fscrypt_key'.
> 
> - Separate finding the key from key derivation.  In particular, it
>   *only* makes sense to fall back to the alternate key description
>   prefix if searching for the "fscrypt:" prefix returns -ENOKEY.  It
>   doesn't make sense to do so when derive_key_aes() fails, for example.
> 
> - Improve the error messages for when the fscrypt_key is invalid.
> 
> - Rename 'raw_key' to 'derived_key' for clarity.

With all the crypto code I've delt with where a 'key' is actually just
a handle or a reference, I've developed a personal habit is to call
the buffer that contains the actual key bytes 'raw' to emphasize that
there's tangible secret material present.

That said, it's probably fine to call it 'derived_key' in this
instance.

> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>

Reviewed-by: Michael Halcrow <mhalcrow@google.com>

> ---
>  fs/crypto/keyinfo.c | 205 ++++++++++++++++++++++++++++------------------------
>  1 file changed, 109 insertions(+), 96 deletions(-)
> 
> diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
> index ac41f646e7b7..d3a97c2cd4dd 100644
> --- a/fs/crypto/keyinfo.c
> +++ b/fs/crypto/keyinfo.c
> @@ -28,113 +28,138 @@ static void derive_crypt_complete(struct crypto_async_request *req, int rc)
>  	complete(&ecr->completion);
>  }
>  
> -/**
> - * derive_key_aes() - Derive a key using AES-128-ECB
> - * @deriving_key: Encryption key used for derivation.
> - * @source_key:   Source key to which to apply derivation.
> - * @derived_raw_key:  Derived raw key.
> +/*
> + * Key derivation function.  This generates the derived key by encrypting the
> + * master key with AES-128-ECB using the nonce as the AES key.
>   *
> - * Return: Zero on success; non-zero otherwise.
> + * The master key must be at least as long as the derived key.  If the master
> + * key is longer, then only the first 'derived_keysize' bytes are used.
>   */
> -static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
> -				const struct fscrypt_key *source_key,
> -				u8 derived_raw_key[FSCRYPT_MAX_KEY_SIZE])
> +static int derive_key_aes(const u8 *master_key,
> +			  const struct fscrypt_context *ctx,
> +			  u8 *derived_key, unsigned int derived_keysize)
>  {
> -	int res = 0;
> +	int err;
>  	struct skcipher_request *req = NULL;
>  	DECLARE_FS_COMPLETION_RESULT(ecr);
>  	struct scatterlist src_sg, dst_sg;
> -	struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
> +	struct crypto_skcipher *tfm;
> +
> +	tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
> +	if (IS_ERR(tfm))
> +		return PTR_ERR(tfm);
>  
> -	if (IS_ERR(tfm)) {
> -		res = PTR_ERR(tfm);
> -		tfm = NULL;
> -		goto out;
> -	}
>  	crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
>  	req = skcipher_request_alloc(tfm, GFP_NOFS);
>  	if (!req) {
> -		res = -ENOMEM;
> +		err = -ENOMEM;
>  		goto out;
>  	}
>  	skcipher_request_set_callback(req,
>  			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
>  			derive_crypt_complete, &ecr);
> -	res = crypto_skcipher_setkey(tfm, deriving_key,
> -					FS_AES_128_ECB_KEY_SIZE);
> -	if (res < 0)
> +
> +	BUILD_BUG_ON(sizeof(ctx->nonce) != FS_AES_128_ECB_KEY_SIZE);
> +	err = crypto_skcipher_setkey(tfm, ctx->nonce, sizeof(ctx->nonce));
> +	if (err)
>  		goto out;
>  
> -	sg_init_one(&src_sg, source_key->raw, source_key->size);
> -	sg_init_one(&dst_sg, derived_raw_key, source_key->size);
> -	skcipher_request_set_crypt(req, &src_sg, &dst_sg, source_key->size,
> +	sg_init_one(&src_sg, master_key, derived_keysize);
> +	sg_init_one(&dst_sg, derived_key, derived_keysize);
> +	skcipher_request_set_crypt(req, &src_sg, &dst_sg, derived_keysize,
>  				   NULL);
> -	res = crypto_skcipher_encrypt(req);
> -	if (res = -EINPROGRESS || res = -EBUSY) {
> +	err = crypto_skcipher_encrypt(req);
> +	if (err = -EINPROGRESS || err = -EBUSY) {
>  		wait_for_completion(&ecr.completion);
> -		res = ecr.res;
> +		err = ecr.res;
>  	}
>  out:
>  	skcipher_request_free(req);
>  	crypto_free_skcipher(tfm);
> -	return res;
> +	return err;
>  }
>  
> -static int validate_user_key(struct fscrypt_info *crypt_info,
> -			struct fscrypt_context *ctx, u8 *raw_key,
> -			const char *prefix, int min_keysize)
> +/*
> + * Search the current task's subscribed keyrings for a "logon" key with
> + * description prefix:descriptor, and if found acquire a read lock on it and
> + * return a pointer to its validated payload in *payload_ret.
> + */
> +static struct key *
> +find_and_lock_process_key(const char *prefix,
> +			  const u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE],
> +			  unsigned int min_keysize,
> +			  const struct fscrypt_key **payload_ret)

Since we've already crossed the threshold of returning at least one
object via a ptr-to-ptr param, maybe doing that for struct key too and
just making the return value of the function be an int err would be
cleaner than PTR_ERR/ERR_PTR?

>  {
>  	char *description;
> -	struct key *keyring_key;
> -	struct fscrypt_key *master_key;
> +	struct key *key;
>  	const struct user_key_payload *ukp;
> -	int res;
> +	const struct fscrypt_key *payload;
>  
>  	description = kasprintf(GFP_NOFS, "%s%*phN", prefix,
> -				FSCRYPT_KEY_DESCRIPTOR_SIZE,
> -				ctx->master_key_descriptor);
> +				FSCRYPT_KEY_DESCRIPTOR_SIZE, descriptor);
>  	if (!description)
> -		return -ENOMEM;
> +		return ERR_PTR(-ENOMEM);
>  
> -	keyring_key = request_key(&key_type_logon, description, NULL);
> +	key = request_key(&key_type_logon, description, NULL);
>  	kfree(description);
> -	if (IS_ERR(keyring_key))
> -		return PTR_ERR(keyring_key);
> -	down_read(&keyring_key->sem);
> -
> -	if (keyring_key->type != &key_type_logon) {
> -		printk_once(KERN_WARNING
> -				"%s: key type must be logon\n", __func__);
> -		res = -ENOKEY;
> -		goto out;
> -	}
> -	ukp = user_key_payload_locked(keyring_key);
> -	if (!ukp) {
> -		/* key was revoked before we acquired its semaphore */
> -		res = -EKEYREVOKED;
> -		goto out;
> +	if (IS_ERR(key))
> +		return key;
> +
> +	down_read(&key->sem);
> +	ukp = user_key_payload_locked(key);
> +
> +	if (!ukp) /* was the key revoked before we acquired its semaphore? */
> +		goto invalid;
> +
> +	payload = (const struct fscrypt_key *)ukp->data;
> +
> +	if (ukp->datalen != sizeof(struct fscrypt_key) ||
> +	    payload->size < 1 || payload->size > FSCRYPT_MAX_KEY_SIZE) {
> +		pr_warn_ratelimited("fscrypt: key with description '%s' has invalid payload\n",
> +				    key->description);
> +		goto invalid;
>  	}
> -	if (ukp->datalen != sizeof(struct fscrypt_key)) {
> -		res = -EINVAL;
> -		goto out;
> +
> +	if (payload->size < min_keysize) {
> +		pr_warn_ratelimited("fscrypt: key with description '%s' is too short "
> +				    "(got %u bytes, need %u+ bytes)\n",
> +				    key->description,
> +				    payload->size, min_keysize);
> +		goto invalid;

A common (yet high-impact) mistake is to pass in only 256 bits of
entropic key material in the 512-bit buffer for AES-256-XTS, leaving
the second half all 0's.  I've actually seen that done in pre-release
code.  It would be an easy check just to see if userspace did that,
but then we're on the slippery slope of how much key strength
validation we should do on the key, if we're going to do any at all.

>  	}
> -	master_key = (struct fscrypt_key *)ukp->data;
> -	BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
> -
> -	if (master_key->size < min_keysize ||
> -	    master_key->size > FSCRYPT_MAX_KEY_SIZE
> -	    || master_key->size % AES_BLOCK_SIZE != 0) {
> -		printk_once(KERN_WARNING
> -				"%s: key size incorrect: %d\n",
> -				__func__, master_key->size);
> -		res = -ENOKEY;
> -		goto out;
> +
> +	*payload_ret = payload;
> +	return key;
> +
> +invalid:
> +	up_read(&key->sem);
> +	key_put(key);
> +	return ERR_PTR(-ENOKEY);
> +}
> +
> +/* Find the master key, then derive the inode's actual encryption key */
> +static int find_and_derive_key(const struct inode *inode,
> +			       const struct fscrypt_context *ctx,
> +			       u8 *derived_key, unsigned int derived_keysize)
> +{
> +	struct key *key;
> +	const struct fscrypt_key *payload;
> +	int err;
> +
> +	key = find_and_lock_process_key(FSCRYPT_KEY_DESC_PREFIX,
> +					ctx->master_key_descriptor,
> +					derived_keysize, &payload);
> +	if (key = ERR_PTR(-ENOKEY) && inode->i_sb->s_cop->key_prefix) {
> +		key = find_and_lock_process_key(inode->i_sb->s_cop->key_prefix,
> +						ctx->master_key_descriptor,
> +						derived_keysize, &payload);
>  	}
> -	res = derive_key_aes(ctx->nonce, master_key, raw_key);
> -out:
> -	up_read(&keyring_key->sem);
> -	key_put(keyring_key);
> -	return res;
> +	if (IS_ERR(key))
> +		return PTR_ERR(key);
> +	err = derive_key_aes(payload->raw, ctx, derived_key, derived_keysize);
> +	up_read(&key->sem);
> +	key_put(key);
> +	return err;
>  }
>  
>  static const struct {
> @@ -256,8 +281,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	struct fscrypt_context ctx;
>  	struct crypto_skcipher *ctfm;
>  	const char *cipher_str;
> -	int keysize;
> -	u8 *raw_key = NULL;
> +	unsigned int derived_keysize;
> +	u8 *derived_key = NULL;
>  	int res;
>  
>  	if (inode->i_crypt_info)
> @@ -301,7 +326,8 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
>  				sizeof(crypt_info->ci_master_key));
>  
> -	res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
> +	res = determine_cipher_type(crypt_info, inode,
> +				    &cipher_str, &derived_keysize);
>  	if (res)
>  		goto out;
>  
> @@ -310,24 +336,14 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	 * crypto API as part of key derivation.
>  	 */
>  	res = -ENOMEM;
> -	raw_key = kmalloc(FSCRYPT_MAX_KEY_SIZE, GFP_NOFS);
> -	if (!raw_key)
> +	derived_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
> +	if (!derived_key)
>  		goto out;
>  
> -	res = validate_user_key(crypt_info, &ctx, raw_key,
> -				FSCRYPT_KEY_DESC_PREFIX, keysize);
> -	if (res && inode->i_sb->s_cop->key_prefix) {
> -		int res2 = validate_user_key(crypt_info, &ctx, raw_key,
> -					     inode->i_sb->s_cop->key_prefix,
> -					     keysize);
> -		if (res2) {
> -			if (res2 = -ENOKEY)
> -				res = -ENOKEY;
> -			goto out;
> -		}
> -	} else if (res) {
> +	res = find_and_derive_key(inode, &ctx, derived_key, derived_keysize);
> +	if (res)
>  		goto out;
> -	}
> +
>  	ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
>  	if (!ctfm || IS_ERR(ctfm)) {
>  		res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
> @@ -338,17 +354,14 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	crypt_info->ci_ctfm = ctfm;
>  	crypto_skcipher_clear_flags(ctfm, ~0);
>  	crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
> -	/*
> -	 * if the provided key is longer than keysize, we use the first
> -	 * keysize bytes of the derived key only
> -	 */
> -	res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
> +	res = crypto_skcipher_setkey(ctfm, derived_key, derived_keysize);
>  	if (res)
>  		goto out;
>  
>  	if (S_ISREG(inode->i_mode) &&
>  	    crypt_info->ci_data_mode = FSCRYPT_MODE_AES_128_CBC) {
> -		res = init_essiv_generator(crypt_info, raw_key, keysize);
> +		res = init_essiv_generator(crypt_info, derived_key,
> +					   derived_keysize);
>  		if (res) {
>  			pr_debug("%s: error %d (inode %lu) allocating essiv tfm\n",
>  				 __func__, res, inode->i_ino);
> @@ -361,7 +374,7 @@ int fscrypt_get_encryption_info(struct inode *inode)
>  	if (res = -ENOKEY)
>  		res = 0;
>  	put_crypt_info(crypt_info);
> -	kzfree(raw_key);
> +	kzfree(derived_key);
>  	return res;
>  }
>  EXPORT_SYMBOL(fscrypt_get_encryption_info);
> -- 
> 2.15.0.rc0.271.g36b669edcc-goog
> 

  reply	other threads:[~2017-10-27 18:23 UTC|newest]

Thread overview: 92+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-23 21:40 [RFC PATCH 00/25] fscrypt: filesystem-level keyring and v2 policy support Eric Biggers
2017-10-23 21:40 ` Eric Biggers
2017-10-23 21:40 ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 01/25] fs, fscrypt: move uapi definitions to new header <linux/fscrypt.h> Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 18:01   ` Michael Halcrow
2017-10-27 18:01     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 02/25] fscrypt: use FSCRYPT_ prefix for uapi constants Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 18:02   ` Michael Halcrow
2017-10-27 18:02     ` Michael Halcrow via Linux-f2fs-devel
2017-10-27 18:02     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 03/25] fscrypt: use FSCRYPT_* definitions, not FS_* Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 18:06   ` Michael Halcrow
2017-10-27 18:06     ` Michael Halcrow
2017-10-27 18:06     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 04/25] fscrypt: refactor finding and deriving key Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 18:23   ` Michael Halcrow [this message]
2017-10-27 18:23     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 05/25] fs: add ->s_master_keys to struct super_block Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 18:26   ` Michael Halcrow
2017-10-27 18:26     ` Michael Halcrow
2017-10-27 18:26     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 06/25] fscrypt: add FS_IOC_ADD_ENCRYPTION_KEY ioctl Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 20:14   ` Michael Halcrow
2017-10-27 20:14     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 07/25] fs/inode.c: export inode_lru_list_del() Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-27 20:28   ` Michael Halcrow
2017-10-27 20:28     ` Michael Halcrow
2017-10-27 20:28     ` Michael Halcrow
2017-10-23 21:40 ` [RFC PATCH 08/25] fs/inode.c: rename and export dispose_list() Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 09/25] fs/dcache.c: add shrink_dcache_inode() Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 10/25] fscrypt: add FS_IOC_REMOVE_ENCRYPTION_KEY ioctl Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 11/25] fscrypt: add FS_IOC_GET_ENCRYPTION_KEY_STATUS ioctl Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 12/25] ext4 crypto: wire up new ioctls for managing encryption keys Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 13/25] f2fs " Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 14/25] ubifs " Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 15/25] fscrypt: add UAPI definitions to get/set v2 encryption policies Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 16/25] fscrypt: implement basic handling of " Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 17/25] fscrypt: add an HKDF-SHA512 implementation Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 18/25] fscrypt: allow adding and removing keys for v2 encryption policies Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 19/25] fscrypt: use HKDF-SHA512 to derive the per-file keys for v2 policies Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 20/25] fscrypt: allow unprivileged users to add/remove " Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 21/25] fscrypt: require that key be added when setting a v2 encryption policy Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 22/25] ext4 crypto: wire up FS_IOC_GET_ENCRYPTION_POLICY_EX Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 23/25] f2fs " Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 24/25] ubifs " Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40 ` [RFC PATCH 25/25] fscrypt: document the new ioctls and policy version Eric Biggers
2017-10-23 21:40   ` Eric Biggers
2017-10-23 21:40   ` Eric Biggers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171027182303.GD10611@google.com \
    --to=mhalcrow@google.com \
    --cc=ebiggers3@gmail.com \
    --cc=ebiggers@google.com \
    --cc=gwendal@chromium.org \
    --cc=hashimoto@chromium.org \
    --cc=jaegeuk@kernel.org \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=linux-fscrypt@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=ndesaulniers@google.com \
    --cc=sarthakkukreti@chromium.org \
    --cc=tytso@mit.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.