diff options
author | Milan Broz <mbroz@redhat.com> | 2006-10-03 10:15:37 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-03 17:04:15 +0200 |
commit | e48d4bbf9697f4fee4f4e48c5e2586b332809519 (patch) | |
tree | eef2172f5730c17ace6ddb9a1cf6a4f5062176ae | |
parent | [PATCH] dm table: add target preresume (diff) | |
download | linux-e48d4bbf9697f4fee4f4e48c5e2586b332809519.tar.xz linux-e48d4bbf9697f4fee4f4e48c5e2586b332809519.zip |
[PATCH] dm crypt: add key msg
Add the facility to wipe the encryption key from memory (for example while a
laptop is suspended) and reinstate it later (when the laptop gets resumed).
Signed-off-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/md/dm-crypt.c | 97 |
1 files changed, 91 insertions, 6 deletions
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index bdbd34993a80..e1e8040f451a 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2003 Christophe Saout <christophe@saout.de> * Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org> + * Copyright (C) 2006 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. */ @@ -22,6 +23,7 @@ #include "dm.h" #define DM_MSG_PREFIX "crypt" +#define MESG_STR(x) x, sizeof(x) /* * per bio private data @@ -63,6 +65,7 @@ struct crypt_iv_operations { * Crypt: maps a linear range of a block device * and encrypts / decrypts at the same time. */ +enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID }; struct crypt_config { struct dm_dev *dev; sector_t start; @@ -86,6 +89,7 @@ struct crypt_config { char cipher[CRYPTO_MAX_ALG_NAME]; char chainmode[CRYPTO_MAX_ALG_NAME]; struct crypto_blkcipher *tfm; + unsigned long flags; unsigned int key_size; u8 key[0]; }; @@ -507,6 +511,31 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size) } } +static int crypt_set_key(struct crypt_config *cc, char *key) +{ + unsigned key_size = strlen(key) >> 1; + + if (cc->key_size && cc->key_size != key_size) + return -EINVAL; + + cc->key_size = key_size; /* initial settings */ + + if ((!key_size && strcmp(key, "-")) || + (key_size && crypt_decode_key(cc->key, key, key_size) < 0)) + return -EINVAL; + + set_bit(DM_CRYPT_KEY_VALID, &cc->flags); + + return 0; +} + +static int crypt_wipe_key(struct crypt_config *cc) +{ + clear_bit(DM_CRYPT_KEY_VALID, &cc->flags); + memset(&cc->key, 0, cc->key_size * sizeof(u8)); + return 0; +} + /* * Construct an encryption mapping: * <cipher> <key> <iv_offset> <dev_path> <start> @@ -539,16 +568,14 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) key_size = strlen(argv[1]) >> 1; - cc = kmalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL); + cc = kzalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL); if (cc == NULL) { ti->error = "Cannot allocate transparent encryption context"; return -ENOMEM; } - cc->key_size = key_size; - if ((!key_size && strcmp(argv[1], "-") != 0) || - (key_size && crypt_decode_key(cc->key, argv[1], key_size) < 0)) { + if (crypt_set_key(cc, argv[1])) { ti->error = "Error decoding key"; goto bad1; } @@ -780,13 +807,14 @@ static int crypt_map(struct dm_target *ti, struct bio *bio, union map_info *map_context) { struct crypt_config *cc = (struct crypt_config *) ti->private; - struct crypt_io *io = mempool_alloc(cc->io_pool, GFP_NOIO); + struct crypt_io *io; struct convert_context ctx; struct bio *clone; unsigned int remaining = bio->bi_size; sector_t sector = bio->bi_sector - ti->begin; unsigned int bvec_idx = 0; + io = mempool_alloc(cc->io_pool, GFP_NOIO); io->target = ti; io->bio = bio; io->first_clone = NULL; @@ -883,14 +911,71 @@ static int crypt_status(struct dm_target *ti, status_type_t type, return 0; } +static void crypt_postsuspend(struct dm_target *ti) +{ + struct crypt_config *cc = ti->private; + + set_bit(DM_CRYPT_SUSPENDED, &cc->flags); +} + +static int crypt_preresume(struct dm_target *ti) +{ + struct crypt_config *cc = ti->private; + + if (!test_bit(DM_CRYPT_KEY_VALID, &cc->flags)) { + DMERR("aborting resume - crypt key is not set."); + return -EAGAIN; + } + + return 0; +} + +static void crypt_resume(struct dm_target *ti) +{ + struct crypt_config *cc = ti->private; + + clear_bit(DM_CRYPT_SUSPENDED, &cc->flags); +} + +/* Message interface + * key set <key> + * key wipe + */ +static int crypt_message(struct dm_target *ti, unsigned argc, char **argv) +{ + struct crypt_config *cc = ti->private; + + if (argc < 2) + goto error; + + if (!strnicmp(argv[0], MESG_STR("key"))) { + if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) { + DMWARN("not suspended during key manipulation."); + return -EINVAL; + } + if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) + return crypt_set_key(cc, argv[2]); + if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) + return crypt_wipe_key(cc); + } + +error: + DMWARN("unrecognised message received."); + return -EINVAL; +} + static struct target_type crypt_target = { .name = "crypt", - .version= {1, 1, 0}, + .version= {1, 2, 0}, .module = THIS_MODULE, .ctr = crypt_ctr, .dtr = crypt_dtr, .map = crypt_map, .status = crypt_status, + .postsuspend = crypt_postsuspend, + .preresume = crypt_preresume, + .resume = crypt_resume, + .message = crypt_message, }; static int __init dm_crypt_init(void) |