diff options
author | Eric Biggers <ebiggers@google.com> | 2017-06-08 09:48:25 -0400 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2017-06-08 23:29:47 -0400 |
commit | 64d107d3acca1565c39c044c459fd18f70943534 (patch) | |
tree | 3de8ea038fc4373d65160503fc039d26aa84b129 /security/keys | |
parent | 794b4bc292f5d31739d89c0202c54e7dc9bc3add (diff) |
KEYS: encrypted: fix race causing incorrect HMAC calculations
The encrypted-keys module was using a single global HMAC transform,
which could be rekeyed by multiple threads concurrently operating on
different keys, causing incorrect HMAC values to be calculated. Fix
this by allocating a new HMAC transform whenever we need to calculate a
HMAC. Also simplify things a bit by allocating the shash_desc's using
SHASH_DESC_ON_STACK() for both the HMAC and unkeyed hashes.
The following script reproduces the bug:
keyctl new_session
keyctl add user master "abcdefghijklmnop" @s
for i in $(seq 2); do
(
set -e
for j in $(seq 1000); do
keyid=$(keyctl add encrypted desc$i "new user:master 25" @s)
datablob="$(keyctl pipe $keyid)"
keyctl unlink $keyid > /dev/null
keyid=$(keyctl add encrypted desc$i "load $datablob" @s)
keyctl unlink $keyid > /dev/null
done
) &
done
Output with bug:
[ 439.691094] encrypted_key: bad hmac (-22)
add_key: Invalid argument
add_key: Invalid argument
Cc: Mimi Zohar <zohar@linux.vnet.ibm.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <james.l.morris@oracle.com>
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/encrypted-keys/encrypted.c | 115 |
1 files changed, 32 insertions, 83 deletions
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 0f7b95de3b5f..702c80662069 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c | |||
@@ -54,13 +54,7 @@ static int blksize; | |||
54 | #define MAX_DATA_SIZE 4096 | 54 | #define MAX_DATA_SIZE 4096 |
55 | #define MIN_DATA_SIZE 20 | 55 | #define MIN_DATA_SIZE 20 |
56 | 56 | ||
57 | struct sdesc { | 57 | static struct crypto_shash *hash_tfm; |
58 | struct shash_desc shash; | ||
59 | char ctx[]; | ||
60 | }; | ||
61 | |||
62 | static struct crypto_shash *hashalg; | ||
63 | static struct crypto_shash *hmacalg; | ||
64 | 58 | ||
65 | enum { | 59 | enum { |
66 | Opt_err = -1, Opt_new, Opt_load, Opt_update | 60 | Opt_err = -1, Opt_new, Opt_load, Opt_update |
@@ -320,53 +314,38 @@ error: | |||
320 | return ukey; | 314 | return ukey; |
321 | } | 315 | } |
322 | 316 | ||
323 | static struct sdesc *alloc_sdesc(struct crypto_shash *alg) | 317 | static int calc_hash(struct crypto_shash *tfm, u8 *digest, |
324 | { | ||
325 | struct sdesc *sdesc; | ||
326 | int size; | ||
327 | |||
328 | size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); | ||
329 | sdesc = kmalloc(size, GFP_KERNEL); | ||
330 | if (!sdesc) | ||
331 | return ERR_PTR(-ENOMEM); | ||
332 | sdesc->shash.tfm = alg; | ||
333 | sdesc->shash.flags = 0x0; | ||
334 | return sdesc; | ||
335 | } | ||
336 | |||
337 | static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen, | ||
338 | const u8 *buf, unsigned int buflen) | 318 | const u8 *buf, unsigned int buflen) |
339 | { | 319 | { |
340 | struct sdesc *sdesc; | 320 | SHASH_DESC_ON_STACK(desc, tfm); |
341 | int ret; | 321 | int err; |
342 | 322 | ||
343 | sdesc = alloc_sdesc(hmacalg); | 323 | desc->tfm = tfm; |
344 | if (IS_ERR(sdesc)) { | 324 | desc->flags = 0; |
345 | pr_info("encrypted_key: can't alloc %s\n", hmac_alg); | ||
346 | return PTR_ERR(sdesc); | ||
347 | } | ||
348 | 325 | ||
349 | ret = crypto_shash_setkey(hmacalg, key, keylen); | 326 | err = crypto_shash_digest(desc, buf, buflen, digest); |
350 | if (!ret) | 327 | shash_desc_zero(desc); |
351 | ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest); | 328 | return err; |
352 | kfree(sdesc); | ||
353 | return ret; | ||
354 | } | 329 | } |
355 | 330 | ||
356 | static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen) | 331 | static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen, |
332 | const u8 *buf, unsigned int buflen) | ||
357 | { | 333 | { |
358 | struct sdesc *sdesc; | 334 | struct crypto_shash *tfm; |
359 | int ret; | 335 | int err; |
360 | 336 | ||
361 | sdesc = alloc_sdesc(hashalg); | 337 | tfm = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); |
362 | if (IS_ERR(sdesc)) { | 338 | if (IS_ERR(tfm)) { |
363 | pr_info("encrypted_key: can't alloc %s\n", hash_alg); | 339 | pr_err("encrypted_key: can't alloc %s transform: %ld\n", |
364 | return PTR_ERR(sdesc); | 340 | hmac_alg, PTR_ERR(tfm)); |
341 | return PTR_ERR(tfm); | ||
365 | } | 342 | } |
366 | 343 | ||
367 | ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest); | 344 | err = crypto_shash_setkey(tfm, key, keylen); |
368 | kfree(sdesc); | 345 | if (!err) |
369 | return ret; | 346 | err = calc_hash(tfm, digest, buf, buflen); |
347 | crypto_free_shash(tfm); | ||
348 | return err; | ||
370 | } | 349 | } |
371 | 350 | ||
372 | enum derived_key_type { ENC_KEY, AUTH_KEY }; | 351 | enum derived_key_type { ENC_KEY, AUTH_KEY }; |
@@ -394,7 +373,7 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, | |||
394 | 373 | ||
395 | memcpy(derived_buf + strlen(derived_buf) + 1, master_key, | 374 | memcpy(derived_buf + strlen(derived_buf) + 1, master_key, |
396 | master_keylen); | 375 | master_keylen); |
397 | ret = calc_hash(derived_key, derived_buf, derived_buf_len); | 376 | ret = calc_hash(hash_tfm, derived_key, derived_buf, derived_buf_len); |
398 | kfree(derived_buf); | 377 | kfree(derived_buf); |
399 | return ret; | 378 | return ret; |
400 | } | 379 | } |
@@ -998,47 +977,17 @@ struct key_type key_type_encrypted = { | |||
998 | }; | 977 | }; |
999 | EXPORT_SYMBOL_GPL(key_type_encrypted); | 978 | EXPORT_SYMBOL_GPL(key_type_encrypted); |
1000 | 979 | ||
1001 | static void encrypted_shash_release(void) | 980 | static int __init init_encrypted(void) |
1002 | { | ||
1003 | if (hashalg) | ||
1004 | crypto_free_shash(hashalg); | ||
1005 | if (hmacalg) | ||
1006 | crypto_free_shash(hmacalg); | ||
1007 | } | ||
1008 | |||
1009 | static int __init encrypted_shash_alloc(void) | ||
1010 | { | 981 | { |
1011 | int ret; | 982 | int ret; |
1012 | 983 | ||
1013 | hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); | 984 | hash_tfm = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); |
1014 | if (IS_ERR(hmacalg)) { | 985 | if (IS_ERR(hash_tfm)) { |
1015 | pr_info("encrypted_key: could not allocate crypto %s\n", | 986 | pr_err("encrypted_key: can't allocate %s transform: %ld\n", |
1016 | hmac_alg); | 987 | hash_alg, PTR_ERR(hash_tfm)); |
1017 | return PTR_ERR(hmacalg); | 988 | return PTR_ERR(hash_tfm); |
1018 | } | 989 | } |
1019 | 990 | ||
1020 | hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); | ||
1021 | if (IS_ERR(hashalg)) { | ||
1022 | pr_info("encrypted_key: could not allocate crypto %s\n", | ||
1023 | hash_alg); | ||
1024 | ret = PTR_ERR(hashalg); | ||
1025 | goto hashalg_fail; | ||
1026 | } | ||
1027 | |||
1028 | return 0; | ||
1029 | |||
1030 | hashalg_fail: | ||
1031 | crypto_free_shash(hmacalg); | ||
1032 | return ret; | ||
1033 | } | ||
1034 | |||
1035 | static int __init init_encrypted(void) | ||
1036 | { | ||
1037 | int ret; | ||
1038 | |||
1039 | ret = encrypted_shash_alloc(); | ||
1040 | if (ret < 0) | ||
1041 | return ret; | ||
1042 | ret = aes_get_sizes(); | 991 | ret = aes_get_sizes(); |
1043 | if (ret < 0) | 992 | if (ret < 0) |
1044 | goto out; | 993 | goto out; |
@@ -1047,14 +996,14 @@ static int __init init_encrypted(void) | |||
1047 | goto out; | 996 | goto out; |
1048 | return 0; | 997 | return 0; |
1049 | out: | 998 | out: |
1050 | encrypted_shash_release(); | 999 | crypto_free_shash(hash_tfm); |
1051 | return ret; | 1000 | return ret; |
1052 | 1001 | ||
1053 | } | 1002 | } |
1054 | 1003 | ||
1055 | static void __exit cleanup_encrypted(void) | 1004 | static void __exit cleanup_encrypted(void) |
1056 | { | 1005 | { |
1057 | encrypted_shash_release(); | 1006 | crypto_free_shash(hash_tfm); |
1058 | unregister_key_type(&key_type_encrypted); | 1007 | unregister_key_type(&key_type_encrypted); |
1059 | } | 1008 | } |
1060 | 1009 | ||