Skip to content

Commit 9fa68f6

Browse files
ebiggersherbertx
authored andcommitted
crypto: hash - prevent using keyed hashes without setting key
Currently, almost none of the keyed hash algorithms check whether a key has been set before proceeding. Some algorithms are okay with this and will effectively just use a key of all 0's or some other bogus default. However, others will severely break, as demonstrated using "hmac(sha3-512-generic)", the unkeyed use of which causes a kernel crash via a (potentially exploitable) stack buffer overflow. A while ago, this problem was solved for AF_ALG by pairing each hash transform with a 'has_key' bool. However, there are still other places in the kernel where userspace can specify an arbitrary hash algorithm by name, and the kernel uses it as unkeyed hash without checking whether it is really unkeyed. Examples of this include: - KEYCTL_DH_COMPUTE, via the KDF extension - dm-verity - dm-crypt, via the ESSIV support - dm-integrity, via the "internal hash" mode with no key given - drbd (Distributed Replicated Block Device) This bug is especially bad for KEYCTL_DH_COMPUTE as that requires no privileges to call. Fix the bug for all users by adding a flag CRYPTO_TFM_NEED_KEY to the ->crt_flags of each hash transform that indicates whether the transform still needs to be keyed or not. Then, make the hash init, import, and digest functions return -ENOKEY if the key is still needed. The new flag also replaces the 'has_key' bool which algif_hash was previously using, thereby simplifying the algif_hash implementation. Reported-by: syzbot <syzkaller@googlegroups.com> Cc: stable@vger.kernel.org Signed-off-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
1 parent a208fa8 commit 9fa68f6

5 files changed

Lines changed: 75 additions & 60 deletions

File tree

crypto/ahash.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,18 @@ int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
193193
unsigned int keylen)
194194
{
195195
unsigned long alignmask = crypto_ahash_alignmask(tfm);
196+
int err;
196197

197198
if ((unsigned long)key & alignmask)
198-
return ahash_setkey_unaligned(tfm, key, keylen);
199+
err = ahash_setkey_unaligned(tfm, key, keylen);
200+
else
201+
err = tfm->setkey(tfm, key, keylen);
202+
203+
if (err)
204+
return err;
199205

200-
return tfm->setkey(tfm, key, keylen);
206+
crypto_ahash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
207+
return 0;
201208
}
202209
EXPORT_SYMBOL_GPL(crypto_ahash_setkey);
203210

@@ -368,7 +375,12 @@ EXPORT_SYMBOL_GPL(crypto_ahash_finup);
368375

369376
int crypto_ahash_digest(struct ahash_request *req)
370377
{
371-
return crypto_ahash_op(req, crypto_ahash_reqtfm(req)->digest);
378+
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
379+
380+
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
381+
return -ENOKEY;
382+
383+
return crypto_ahash_op(req, tfm->digest);
372384
}
373385
EXPORT_SYMBOL_GPL(crypto_ahash_digest);
374386

@@ -450,7 +462,6 @@ static int crypto_ahash_init_tfm(struct crypto_tfm *tfm)
450462
struct ahash_alg *alg = crypto_ahash_alg(hash);
451463

452464
hash->setkey = ahash_nosetkey;
453-
hash->has_setkey = false;
454465
hash->export = ahash_no_export;
455466
hash->import = ahash_no_import;
456467

@@ -465,7 +476,8 @@ static int crypto_ahash_init_tfm(struct crypto_tfm *tfm)
465476

466477
if (alg->setkey) {
467478
hash->setkey = alg->setkey;
468-
hash->has_setkey = true;
479+
if (!(alg->halg.base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
480+
crypto_ahash_set_flags(hash, CRYPTO_TFM_NEED_KEY);
469481
}
470482
if (alg->export)
471483
hash->export = alg->export;

crypto/algif_hash.c

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ struct hash_ctx {
3434
struct ahash_request req;
3535
};
3636

37-
struct algif_hash_tfm {
38-
struct crypto_ahash *hash;
39-
bool has_key;
40-
};
41-
4237
static int hash_alloc_result(struct sock *sk, struct hash_ctx *ctx)
4338
{
4439
unsigned ds;
@@ -307,7 +302,7 @@ static int hash_check_key(struct socket *sock)
307302
int err = 0;
308303
struct sock *psk;
309304
struct alg_sock *pask;
310-
struct algif_hash_tfm *tfm;
305+
struct crypto_ahash *tfm;
311306
struct sock *sk = sock->sk;
312307
struct alg_sock *ask = alg_sk(sk);
313308

@@ -321,7 +316,7 @@ static int hash_check_key(struct socket *sock)
321316

322317
err = -ENOKEY;
323318
lock_sock_nested(psk, SINGLE_DEPTH_NESTING);
324-
if (!tfm->has_key)
319+
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
325320
goto unlock;
326321

327322
if (!pask->refcnt++)
@@ -412,41 +407,17 @@ static struct proto_ops algif_hash_ops_nokey = {
412407

413408
static void *hash_bind(const char *name, u32 type, u32 mask)
414409
{
415-
struct algif_hash_tfm *tfm;
416-
struct crypto_ahash *hash;
417-
418-
tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
419-
if (!tfm)
420-
return ERR_PTR(-ENOMEM);
421-
422-
hash = crypto_alloc_ahash(name, type, mask);
423-
if (IS_ERR(hash)) {
424-
kfree(tfm);
425-
return ERR_CAST(hash);
426-
}
427-
428-
tfm->hash = hash;
429-
430-
return tfm;
410+
return crypto_alloc_ahash(name, type, mask);
431411
}
432412

433413
static void hash_release(void *private)
434414
{
435-
struct algif_hash_tfm *tfm = private;
436-
437-
crypto_free_ahash(tfm->hash);
438-
kfree(tfm);
415+
crypto_free_ahash(private);
439416
}
440417

441418
static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
442419
{
443-
struct algif_hash_tfm *tfm = private;
444-
int err;
445-
446-
err = crypto_ahash_setkey(tfm->hash, key, keylen);
447-
tfm->has_key = !err;
448-
449-
return err;
420+
return crypto_ahash_setkey(private, key, keylen);
450421
}
451422

452423
static void hash_sock_destruct(struct sock *sk)
@@ -461,11 +432,10 @@ static void hash_sock_destruct(struct sock *sk)
461432

462433
static int hash_accept_parent_nokey(void *private, struct sock *sk)
463434
{
464-
struct hash_ctx *ctx;
435+
struct crypto_ahash *tfm = private;
465436
struct alg_sock *ask = alg_sk(sk);
466-
struct algif_hash_tfm *tfm = private;
467-
struct crypto_ahash *hash = tfm->hash;
468-
unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(hash);
437+
struct hash_ctx *ctx;
438+
unsigned int len = sizeof(*ctx) + crypto_ahash_reqsize(tfm);
469439

470440
ctx = sock_kmalloc(sk, len, GFP_KERNEL);
471441
if (!ctx)
@@ -478,7 +448,7 @@ static int hash_accept_parent_nokey(void *private, struct sock *sk)
478448

479449
ask->private = ctx;
480450

481-
ahash_request_set_tfm(&ctx->req, hash);
451+
ahash_request_set_tfm(&ctx->req, tfm);
482452
ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
483453
crypto_req_done, &ctx->wait);
484454

@@ -489,9 +459,9 @@ static int hash_accept_parent_nokey(void *private, struct sock *sk)
489459

490460
static int hash_accept_parent(void *private, struct sock *sk)
491461
{
492-
struct algif_hash_tfm *tfm = private;
462+
struct crypto_ahash *tfm = private;
493463

494-
if (!tfm->has_key && crypto_ahash_has_setkey(tfm->hash))
464+
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
495465
return -ENOKEY;
496466

497467
return hash_accept_parent_nokey(private, sk);

crypto/shash.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,18 @@ int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
5858
{
5959
struct shash_alg *shash = crypto_shash_alg(tfm);
6060
unsigned long alignmask = crypto_shash_alignmask(tfm);
61+
int err;
6162

6263
if ((unsigned long)key & alignmask)
63-
return shash_setkey_unaligned(tfm, key, keylen);
64+
err = shash_setkey_unaligned(tfm, key, keylen);
65+
else
66+
err = shash->setkey(tfm, key, keylen);
67+
68+
if (err)
69+
return err;
6470

65-
return shash->setkey(tfm, key, keylen);
71+
crypto_shash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);
72+
return 0;
6673
}
6774
EXPORT_SYMBOL_GPL(crypto_shash_setkey);
6875

@@ -181,6 +188,9 @@ int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
181188
struct shash_alg *shash = crypto_shash_alg(tfm);
182189
unsigned long alignmask = crypto_shash_alignmask(tfm);
183190

191+
if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
192+
return -ENOKEY;
193+
184194
if (((unsigned long)data | (unsigned long)out) & alignmask)
185195
return shash_digest_unaligned(desc, data, len, out);
186196

@@ -360,7 +370,8 @@ int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
360370
crt->digest = shash_async_digest;
361371
crt->setkey = shash_async_setkey;
362372

363-
crt->has_setkey = alg->setkey != shash_no_setkey;
373+
crypto_ahash_set_flags(crt, crypto_shash_get_flags(shash) &
374+
CRYPTO_TFM_NEED_KEY);
364375

365376
if (alg->export)
366377
crt->export = shash_async_export;
@@ -375,8 +386,14 @@ int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
375386
static int crypto_shash_init_tfm(struct crypto_tfm *tfm)
376387
{
377388
struct crypto_shash *hash = __crypto_shash_cast(tfm);
389+
struct shash_alg *alg = crypto_shash_alg(hash);
390+
391+
hash->descsize = alg->descsize;
392+
393+
if (crypto_shash_alg_has_setkey(alg) &&
394+
!(alg->base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY))
395+
crypto_shash_set_flags(hash, CRYPTO_TFM_NEED_KEY);
378396

379-
hash->descsize = crypto_shash_alg(hash)->descsize;
380397
return 0;
381398
}
382399

include/crypto/hash.h

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ struct crypto_ahash {
210210
unsigned int keylen);
211211

212212
unsigned int reqsize;
213-
bool has_setkey;
214213
struct crypto_tfm base;
215214
};
216215

@@ -410,11 +409,6 @@ static inline void *ahash_request_ctx(struct ahash_request *req)
410409
int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
411410
unsigned int keylen);
412411

413-
static inline bool crypto_ahash_has_setkey(struct crypto_ahash *tfm)
414-
{
415-
return tfm->has_setkey;
416-
}
417-
418412
/**
419413
* crypto_ahash_finup() - update and finalize message digest
420414
* @req: reference to the ahash_request handle that holds all information
@@ -487,7 +481,12 @@ static inline int crypto_ahash_export(struct ahash_request *req, void *out)
487481
*/
488482
static inline int crypto_ahash_import(struct ahash_request *req, const void *in)
489483
{
490-
return crypto_ahash_reqtfm(req)->import(req, in);
484+
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
485+
486+
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
487+
return -ENOKEY;
488+
489+
return tfm->import(req, in);
491490
}
492491

493492
/**
@@ -503,7 +502,12 @@ static inline int crypto_ahash_import(struct ahash_request *req, const void *in)
503502
*/
504503
static inline int crypto_ahash_init(struct ahash_request *req)
505504
{
506-
return crypto_ahash_reqtfm(req)->init(req);
505+
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
506+
507+
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
508+
return -ENOKEY;
509+
510+
return tfm->init(req);
507511
}
508512

509513
/**
@@ -855,7 +859,12 @@ static inline int crypto_shash_export(struct shash_desc *desc, void *out)
855859
*/
856860
static inline int crypto_shash_import(struct shash_desc *desc, const void *in)
857861
{
858-
return crypto_shash_alg(desc->tfm)->import(desc, in);
862+
struct crypto_shash *tfm = desc->tfm;
863+
864+
if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
865+
return -ENOKEY;
866+
867+
return crypto_shash_alg(tfm)->import(desc, in);
859868
}
860869

861870
/**
@@ -871,7 +880,12 @@ static inline int crypto_shash_import(struct shash_desc *desc, const void *in)
871880
*/
872881
static inline int crypto_shash_init(struct shash_desc *desc)
873882
{
874-
return crypto_shash_alg(desc->tfm)->init(desc);
883+
struct crypto_shash *tfm = desc->tfm;
884+
885+
if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
886+
return -ENOKEY;
887+
888+
return crypto_shash_alg(tfm)->init(desc);
875889
}
876890

877891
/**

include/linux/crypto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
/*
116116
* Transform masks and values (for crt_flags).
117117
*/
118+
#define CRYPTO_TFM_NEED_KEY 0x00000001
119+
118120
#define CRYPTO_TFM_REQ_MASK 0x000fff00
119121
#define CRYPTO_TFM_RES_MASK 0xfff00000
120122

0 commit comments

Comments
 (0)