All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails
@ 2019-01-07  2:47 Eric Biggers
  2019-01-07  2:47 ` [PATCH 1/3] crypto: hash - " Eric Biggers
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Eric Biggers @ 2019-01-07  2:47 UTC (permalink / raw)
  To: linux-crypto, Herbert Xu

This series makes the crypto API mark shash, ahash, skcipher, and aead
tfms as needing a key again if setting a key fails, since on failure
many algorithms can leave the tfm in an intermediate state that is
neither the old key nor the new key -- and use of such tfms for hashing,
encryption, or decryption will produce bogus results or crashes.

Eric Biggers (3):
  crypto: hash - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
  crypto: skcipher - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
  crypto: aead - set CRYPTO_TFM_NEED_KEY if ->setkey() fails

 crypto/aead.c     |  4 +++-
 crypto/ahash.c    | 28 +++++++++++++++++++---------
 crypto/shash.c    | 18 +++++++++++++-----
 crypto/skcipher.c | 27 ++++++++++++++++++---------
 4 files changed, 53 insertions(+), 24 deletions(-)

-- 
2.20.1

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] crypto: hash - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
  2019-01-07  2:47 [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails Eric Biggers
@ 2019-01-07  2:47 ` Eric Biggers
  2019-01-07  2:47 ` [PATCH 2/3] crypto: skcipher " Eric Biggers
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2019-01-07  2:47 UTC (permalink / raw)
  To: linux-crypto, Herbert Xu

From: Eric Biggers <ebiggers@google.com>

Some algorithms have a ->setkey() method that is not atomic, in the
sense that setting a key can fail after changes were already made to the
tfm context.  In this case, if a key was already set the tfm can end up
in a state that corresponds to neither the old key nor the new key.

It's not feasible to make all ->setkey() methods atomic, especially ones
that have to key multiple sub-tfms.  Therefore, make the crypto API set
CRYPTO_TFM_NEED_KEY if ->setkey() fails and the algorithm requires a
key, to prevent the tfm from being used until a new key is set.

Note: we can't set CRYPTO_TFM_NEED_KEY for OPTIONAL_KEY algorithms, so
->setkey() for those must nevertheless be atomic.  That's fine for now
since only the crc32 and crc32c algorithms set OPTIONAL_KEY, and it's
not intended that OPTIONAL_KEY be used much.

[Cc stable mainly because when introducing the NEED_KEY flag I changed
 AF_ALG to rely on it; and unlike in-kernel crypto API users, AF_ALG
 previously didn't have this problem.  So these "incompletely keyed"
 states became theoretically accessible via AF_ALG -- though, the
 opportunities for causing real mischief seem pretty limited.]

Fixes: 9fa68f620041 ("crypto: hash - prevent using keyed hashes without setting key")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 crypto/ahash.c | 28 +++++++++++++++++++---------
 crypto/shash.c | 18 +++++++++++++-----
 2 files changed, 32 insertions(+), 14 deletions(-)

diff --git a/crypto/ahash.c b/crypto/ahash.c
index 5d320a811f750..ca0d3e281fefb 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -190,6 +190,21 @@ static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key,
 	return ret;
 }
 
+static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key,
+			  unsigned int keylen)
+{
+	return -ENOSYS;
+}
+
+static void ahash_set_needkey(struct crypto_ahash *tfm)
+{
+	const struct hash_alg_common *alg = crypto_hash_alg_common(tfm);
+
+	if (tfm->setkey != ahash_nosetkey &&
+	    !(alg->base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
+		crypto_ahash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
+}
+
 int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
 			unsigned int keylen)
 {
@@ -201,20 +216,16 @@ int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
 	else
 		err = tfm->setkey(tfm, key, keylen);
 
-	if (err)
+	if (unlikely(err)) {
+		ahash_set_needkey(tfm);
 		return err;
+	}
 
 	crypto_ahash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(crypto_ahash_setkey);
 
-static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key,
-			  unsigned int keylen)
-{
-	return -ENOSYS;
-}
-
 static inline unsigned int ahash_align_buffer_size(unsigned len,
 						   unsigned long mask)
 {
@@ -489,8 +500,7 @@ static int crypto_ahash_init_tfm(struct crypto_tfm *tfm)
 
 	if (alg->setkey) {
 		hash->setkey = alg->setkey;
-		if (!(alg->halg.base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
-			crypto_ahash_set_flags(hash, CRYPTO_TFM_NEED_KEY);
+		ahash_set_needkey(hash);
 	}
 
 	return 0;
diff --git a/crypto/shash.c b/crypto/shash.c
index 44d297b82a8fb..40311ccad3fae 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -53,6 +53,13 @@ static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
 	return err;
 }
 
+static void shash_set_needkey(struct crypto_shash *tfm, struct shash_alg *alg)
+{
+	if (crypto_shash_alg_has_setkey(alg) &&
+	    !(alg->base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
+		crypto_shash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
+}
+
 int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
 			unsigned int keylen)
 {
@@ -65,8 +72,10 @@ int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
 	else
 		err = shash->setkey(tfm, key, keylen);
 
-	if (err)
+	if (unlikely(err)) {
+		shash_set_needkey(tfm, shash);
 		return err;
+	}
 
 	crypto_shash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 	return 0;
@@ -373,7 +382,8 @@ int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
 	crt->final = shash_async_final;
 	crt->finup = shash_async_finup;
 	crt->digest = shash_async_digest;
-	crt->setkey = shash_async_setkey;
+	if (crypto_shash_alg_has_setkey(alg))
+		crt->setkey = shash_async_setkey;
 
 	crypto_ahash_set_flags(crt, crypto_shash_get_flags(shash) &
 				    CRYPTO_TFM_NEED_KEY);
@@ -395,9 +405,7 @@ static int crypto_shash_init_tfm(struct crypto_tfm *tfm)
 
 	hash->descsize = alg->descsize;
 
-	if (crypto_shash_alg_has_setkey(alg) &&
-	    !(alg->base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
-		crypto_shash_set_flags(hash, CRYPTO_TFM_NEED_KEY);
+	shash_set_needkey(hash, alg);
 
 	return 0;
 }
-- 
2.20.1

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] crypto: skcipher - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
  2019-01-07  2:47 [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails Eric Biggers
  2019-01-07  2:47 ` [PATCH 1/3] crypto: hash - " Eric Biggers
@ 2019-01-07  2:47 ` Eric Biggers
  2019-01-07  2:47 ` [PATCH 3/3] crypto: aead " Eric Biggers
  2019-01-18 10:55 ` [PATCH 0/3] crypto: " Herbert Xu
  3 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2019-01-07  2:47 UTC (permalink / raw)
  To: linux-crypto, Herbert Xu

From: Eric Biggers <ebiggers@google.com>

Some algorithms have a ->setkey() method that is not atomic, in the
sense that setting a key can fail after changes were already made to the
tfm context.  In this case, if a key was already set the tfm can end up
in a state that corresponds to neither the old key nor the new key.

For example, in lrw.c, if gf128mul_init_64k_bbe() fails due to lack of
memory, then priv::table will be left NULL.  After that, encryption with
that tfm will cause a NULL pointer dereference.

It's not feasible to make all ->setkey() methods atomic, especially ones
that have to key multiple sub-tfms.  Therefore, make the crypto API set
CRYPTO_TFM_NEED_KEY if ->setkey() fails and the algorithm requires a
key, to prevent the tfm from being used until a new key is set.

[Cc stable mainly because when introducing the NEED_KEY flag I changed
 AF_ALG to rely on it; and unlike in-kernel crypto API users, AF_ALG
 previously didn't have this problem.  So these "incompletely keyed"
 states became theoretically accessible via AF_ALG -- though, the
 opportunities for causing real mischief seem pretty limited.]

Fixes: f8d33fac8480 ("crypto: skcipher - prevent using skciphers without setting key")
Cc: <stable@vger.kernel.org> # v4.16+
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 crypto/skcipher.c | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/crypto/skcipher.c b/crypto/skcipher.c
index 2a969296bc248..de09ff60991e2 100644
--- a/crypto/skcipher.c
+++ b/crypto/skcipher.c
@@ -585,6 +585,12 @@ static unsigned int crypto_skcipher_extsize(struct crypto_alg *alg)
 	return crypto_alg_extsize(alg);
 }
 
+static void skcipher_set_needkey(struct crypto_skcipher *tfm)
+{
+	if (tfm->keysize)
+		crypto_skcipher_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
+}
+
 static int skcipher_setkey_blkcipher(struct crypto_skcipher *tfm,
 				     const u8 *key, unsigned int keylen)
 {
@@ -598,8 +604,10 @@ static int skcipher_setkey_blkcipher(struct crypto_skcipher *tfm,
 	err = crypto_blkcipher_setkey(blkcipher, key, keylen);
 	crypto_skcipher_set_flags(tfm, crypto_blkcipher_get_flags(blkcipher) &
 				       CRYPTO_TFM_RES_MASK);
-	if (err)
+	if (unlikely(err)) {
+		skcipher_set_needkey(tfm);
 		return err;
+	}
 
 	crypto_skcipher_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 	return 0;
@@ -677,8 +685,7 @@ static int crypto_init_skcipher_ops_blkcipher(struct crypto_tfm *tfm)
 	skcipher->ivsize = crypto_blkcipher_ivsize(blkcipher);
 	skcipher->keysize = calg->cra_blkcipher.max_keysize;
 
-	if (skcipher->keysize)
-		crypto_skcipher_set_flags(skcipher, CRYPTO_TFM_NEED_KEY);
+	skcipher_set_needkey(skcipher);
 
 	return 0;
 }
@@ -698,8 +705,10 @@ static int skcipher_setkey_ablkcipher(struct crypto_skcipher *tfm,
 	crypto_skcipher_set_flags(tfm,
 				  crypto_ablkcipher_get_flags(ablkcipher) &
 				  CRYPTO_TFM_RES_MASK);
-	if (err)
+	if (unlikely(err)) {
+		skcipher_set_needkey(tfm);
 		return err;
+	}
 
 	crypto_skcipher_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 	return 0;
@@ -776,8 +785,7 @@ static int crypto_init_skcipher_ops_ablkcipher(struct crypto_tfm *tfm)
 			    sizeof(struct ablkcipher_request);
 	skcipher->keysize = calg->cra_ablkcipher.max_keysize;
 
-	if (skcipher->keysize)
-		crypto_skcipher_set_flags(skcipher, CRYPTO_TFM_NEED_KEY);
+	skcipher_set_needkey(skcipher);
 
 	return 0;
 }
@@ -820,8 +828,10 @@ static int skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key,
 	else
 		err = cipher->setkey(tfm, key, keylen);
 
-	if (err)
+	if (unlikely(err)) {
+		skcipher_set_needkey(tfm);
 		return err;
+	}
 
 	crypto_skcipher_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 	return 0;
@@ -852,8 +862,7 @@ static int crypto_skcipher_init_tfm(struct crypto_tfm *tfm)
 	skcipher->ivsize = alg->ivsize;
 	skcipher->keysize = alg->max_keysize;
 
-	if (skcipher->keysize)
-		crypto_skcipher_set_flags(skcipher, CRYPTO_TFM_NEED_KEY);
+	skcipher_set_needkey(skcipher);
 
 	if (alg->exit)
 		skcipher->base.exit = crypto_skcipher_exit_tfm;
-- 
2.20.1

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] crypto: aead - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
  2019-01-07  2:47 [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails Eric Biggers
  2019-01-07  2:47 ` [PATCH 1/3] crypto: hash - " Eric Biggers
  2019-01-07  2:47 ` [PATCH 2/3] crypto: skcipher " Eric Biggers
@ 2019-01-07  2:47 ` Eric Biggers
  2019-01-18 10:55 ` [PATCH 0/3] crypto: " Herbert Xu
  3 siblings, 0 replies; 5+ messages in thread
From: Eric Biggers @ 2019-01-07  2:47 UTC (permalink / raw)
  To: linux-crypto, Herbert Xu

From: Eric Biggers <ebiggers@google.com>

Some algorithms have a ->setkey() method that is not atomic, in the
sense that setting a key can fail after changes were already made to the
tfm context.  In this case, if a key was already set the tfm can end up
in a state that corresponds to neither the old key nor the new key.

For example, in gcm.c, if the kzalloc() fails due to lack of memory,
then the CTR part of GCM will have the new key but GHASH will not.

It's not feasible to make all ->setkey() methods atomic, especially ones
that have to key multiple sub-tfms.  Therefore, make the crypto API set
CRYPTO_TFM_NEED_KEY if ->setkey() fails, to prevent the tfm from being
used until a new key is set.

[Cc stable mainly because when introducing the NEED_KEY flag I changed
 AF_ALG to rely on it; and unlike in-kernel crypto API users, AF_ALG
 previously didn't have this problem.  So these "incompletely keyed"
 states became theoretically accessible via AF_ALG -- though, the
 opportunities for causing real mischief seem pretty limited.]

Fixes: dc26c17f743a ("crypto: aead - prevent using AEADs without setting key")
Cc: <stable@vger.kernel.org> # v4.16+
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 crypto/aead.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/crypto/aead.c b/crypto/aead.c
index 189c52d1f63ab..4908b5e846f0e 100644
--- a/crypto/aead.c
+++ b/crypto/aead.c
@@ -61,8 +61,10 @@ int crypto_aead_setkey(struct crypto_aead *tfm,
 	else
 		err = crypto_aead_alg(tfm)->setkey(tfm, key, keylen);
 
-	if (err)
+	if (unlikely(err)) {
+		crypto_aead_set_flags(tfm, CRYPTO_TFM_NEED_KEY);
 		return err;
+	}
 
 	crypto_aead_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
 	return 0;
-- 
2.20.1

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails
  2019-01-07  2:47 [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails Eric Biggers
                   ` (2 preceding siblings ...)
  2019-01-07  2:47 ` [PATCH 3/3] crypto: aead " Eric Biggers
@ 2019-01-18 10:55 ` Herbert Xu
  3 siblings, 0 replies; 5+ messages in thread
From: Herbert Xu @ 2019-01-18 10:55 UTC (permalink / raw)
  To: Eric Biggers; +Cc: linux-crypto

On Sun, Jan 06, 2019 at 06:47:41PM -0800, Eric Biggers wrote:
> This series makes the crypto API mark shash, ahash, skcipher, and aead
> tfms as needing a key again if setting a key fails, since on failure
> many algorithms can leave the tfm in an intermediate state that is
> neither the old key nor the new key -- and use of such tfms for hashing,
> encryption, or decryption will produce bogus results or crashes.
> 
> Eric Biggers (3):
>   crypto: hash - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
>   crypto: skcipher - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
>   crypto: aead - set CRYPTO_TFM_NEED_KEY if ->setkey() fails
> 
>  crypto/aead.c     |  4 +++-
>  crypto/ahash.c    | 28 +++++++++++++++++++---------
>  crypto/shash.c    | 18 +++++++++++++-----
>  crypto/skcipher.c | 27 ++++++++++++++++++---------
>  4 files changed, 53 insertions(+), 24 deletions(-)

All applied.  Thanks.
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-01-18 10:55 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-07  2:47 [PATCH 0/3] crypto: set CRYPTO_TFM_NEED_KEY if ->setkey() fails Eric Biggers
2019-01-07  2:47 ` [PATCH 1/3] crypto: hash - " Eric Biggers
2019-01-07  2:47 ` [PATCH 2/3] crypto: skcipher " Eric Biggers
2019-01-07  2:47 ` [PATCH 3/3] crypto: aead " Eric Biggers
2019-01-18 10:55 ` [PATCH 0/3] crypto: " Herbert Xu

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.