Commit 3cea5dcc authored by Milan Broz's avatar Milan Broz

* Add luksSuspend (freeze device and wipe key) and luksResume (with provided passphrase).

Signed-off-by: Milan Broz's avatarMilan Broz <mbroz@redhat.com>

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@104 36d66b0a-2a48-0410-832c-cd162a569da5
parent 52b0271a
2009-09-02 Milan Broz <mbroz@redhat.com>
* Add luksSuspend (freeze device and wipe key) and luksResume (with provided passphrase).
2009-08-30 Milan Broz <mbroz@redhat.com>
* Require device device-mapper to build and do not use backend wrapper for dm calls.
* Move memory locking and dm initialization to command layer.
......
......@@ -80,11 +80,16 @@ int dm_query_device(const char *name,
char **cipher,
int *key_size,
char **key,
int *read_only);
int *read_only,
int *suspended);
int dm_create_device(const char *name, const char *device, const char *cipher, const char *uuid,
uint64_t size, uint64_t skip, uint64_t offset,
size_t key_size, const char *key,
int read_only, int reload);
int dm_suspend_and_wipe_key(const char *name);
int dm_resume_and_reinstate_key(const char *name,
size_t key_size,
const char *key);
int sector_size_for_device(const char *device);
ssize_t write_blockwise(int fd, const void *buf, size_t count);
......
......@@ -14,12 +14,24 @@ struct crypt_device; /* crypt device handle */
* Returns 0 on success or negative errno value otherwise.
*
* @cd - returns pointer to crypt device handle
* @device - path to device
*
* Note that logging is not initialized here, possible messages uses
* default log function.
*/
int crypt_init(struct crypt_device **cd, const char *device);
/**
* Initialise crypt device handle from provided active device name
* and check if provided device exists.
*
* Returns 0 on success or negative errno value otherwise.
*
* @cd - crypt device handle
* @name - name of active crypt device
*/
int crypt_init_by_name(struct crypt_device **cd, const char *name);
/**
* Set log function.
*
......@@ -152,6 +164,51 @@ int crypt_load(struct crypt_device *cd,
const char *requested_type,
void *params);
/**
* Suspends crypt device.
*
* Returns 0 on success or negative errno value otherwise.
*
* @cd - crypt device handle, can be NULL
* @name - name of device to suspend
*/
int crypt_suspend(struct crypt_device *cd,
const char *name);
/**
* Resumes crypt device using passphrase.
*
* Returns unlocked key slot number or negative errno otherwise.
*
* @cd - crypt device handle
* @name - name of device to resume
* @keyslot - requested keyslot or CRYPT_ANY_SLOT
* @passphrase - passphrase used to unlock volume key, NULL for query
* @passphrase_size - size of @passphrase (binary data)
*/
int crypt_resume_by_passphrase(struct crypt_device *cd,
const char *name,
int keyslot,
const char *passphrase,
size_t passphrase_size);
/**
* Resumes crypt device using key file.
*
* Returns unlocked key slot number or negative errno otherwise.
*
* @cd - crypt device handle
* @name - name of device to resume
* @keyslot - requested keyslot or CRYPT_ANY_SLOT
* @keyfile - key file used to unlock volume key, NULL for passphrase query
* @keyfile_size - number of bytes to read from @keyfile, 0 is unlimited
*/
int crypt_resume_by_keyfile(struct crypt_device *cd,
const char *name,
int keyslot,
const char *keyfile,
size_t keyfile_size);
/**
* Releases crypt device context and used memory.
*
......
......@@ -143,19 +143,25 @@ static int _dev_read_ahead(const char *dev, uint32_t *read_ahead)
return r;
}
static void hex_key(char *hexkey, size_t key_size, const char *key)
{
int i;
for(i = 0; i < key_size; i++)
sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
}
static char *get_params(const char *device, uint64_t skip, uint64_t offset,
const char *cipher, size_t key_size, const char *key)
{
char *params;
char *hexkey;
int i;
hexkey = safe_alloc(key_size * 2 + 1);
if (!hexkey)
return NULL;
for(i = 0; i < key_size; i++)
sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
hex_key(hexkey, key_size, key);
params = safe_alloc(strlen(hexkey) + strlen(cipher) + strlen(device) + 64);
if (!params)
......@@ -405,7 +411,8 @@ int dm_query_device(const char *name,
char **cipher,
int *key_size,
char **key,
int *read_only)
int *read_only,
int *suspended)
{
struct dm_task *dmt;
struct dm_info dmi;
......@@ -495,10 +502,12 @@ int dm_query_device(const char *name,
}
memset(key_, 0, strlen(key_));
/* read_only */
if (read_only)
*read_only = dmi.read_only;
if (suspended)
*suspended = dmi.suspended;
r = (dmi.open_count > 0);
out:
if (dmt)
......@@ -507,6 +516,67 @@ out:
return r;
}
static int _dm_message(const char *name, const char *msg)
{
int r = 0;
struct dm_task *dmt;
if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
return 0;
if (name && !dm_task_set_name(dmt, name))
goto out;
if (!dm_task_set_sector(dmt, (uint64_t) 0))
goto out;
if (!dm_task_set_message(dmt, msg))
goto out;
r = dm_task_run(dmt);
out:
dm_task_destroy(dmt);
return r;
}
int dm_suspend_and_wipe_key(const char *name)
{
if (!_dm_simple(DM_DEVICE_SUSPEND, name))
return -EINVAL;
if (!_dm_message(name, "key wipe")) {
_dm_simple(DM_DEVICE_RESUME, name);
return -EINVAL;
}
return 0;
}
int dm_resume_and_reinstate_key(const char *name,
size_t key_size,
const char *key)
{
int msg_size = key_size * 2 + 10; // key set <key>
char *msg;
int r = 0;
msg = safe_alloc(msg_size);
if (!msg)
return -ENOMEM;
memset(msg, 0, msg_size);
strcpy(msg, "key set ");
hex_key(&msg[8], key_size, key);
if (!_dm_message(name, msg) ||
!_dm_simple(DM_DEVICE_RESUME, name))
r = -EINVAL;
safe_free(msg);
return r;
}
const char *dm_get_dir(void)
{
return dm_dir();
......
......@@ -443,6 +443,45 @@ static void key_from_terminal(struct crypt_device *cd, char *msg, char **key,
}
}
static int volume_key_by_terminal_passphrase(struct crypt_device *cd, int keyslot,
struct luks_masterkey **mk)
{
char *prompt = NULL, *passphrase_read = NULL;
unsigned int passphrase_size_read;
int r = -EINVAL, tries = cd->tries;
if(asprintf(&prompt, _("Enter passphrase for %s: "), cd->device) < 0)
return -ENOMEM;
*mk = NULL;
do {
if (*mk)
LUKS_dealloc_masterkey(*mk);
*mk = NULL;
key_from_terminal(cd, prompt, &passphrase_read,
&passphrase_size_read, 0);
if(!passphrase_read) {
r = -EINVAL;
break;
}
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase_read,
passphrase_size_read, &cd->hdr, mk, cd);
safe_free(passphrase_read);
passphrase_read = NULL;
} while (r == -EPERM && (--tries > 0));
if (r < 0 && *mk) {
LUKS_dealloc_masterkey(*mk);
*mk = NULL;
}
free(prompt);
return r;
}
static void key_from_file(struct crypt_device *cd, char *msg,
char **key, unsigned int *key_len,
const char *key_file, size_t key_size)
......@@ -572,7 +611,7 @@ int crypt_resize_device(struct crypt_options *options)
int key_size, read_only, r;
r = dm_query_device(options->name, &device, &size, &skip, &offset,
&cipher, &key_size, &key, &read_only);
&cipher, &key_size, &key, &read_only, NULL);
if (r < 0)
return r;
......@@ -606,7 +645,7 @@ int crypt_query_device(struct crypt_options *options)
r = dm_query_device(options->name, (char **)&options->device, &options->size,
&options->skip, &options->offset, (char **)&options->cipher,
&options->key_size, NULL, &read_only);
&options->key_size, NULL, &read_only, NULL);
if (r < 0)
return r;
......@@ -889,6 +928,29 @@ int crypt_init(struct crypt_device **cd, const char *device)
return 0;
}
int crypt_init_by_name(struct crypt_device **cd, const char *name)
{
crypt_status_info ci;
char *device = NULL;
int r;
log_dbg("Allocating crypt device context by device %s.", name);
ci = crypt_status(NULL, name);
if (ci < ACTIVE) {
log_err(NULL, _("Device %s is not active.\n"), name);
return -EINVAL;
}
r = dm_query_device(name, &device, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL);
if (!r)
r = crypt_init(cd, device);
free(device);
return r;
}
static int _crypt_format_plain(struct crypt_device *cd,
const char *cipher,
const char *cipher_mode,
......@@ -1046,6 +1108,137 @@ void crypt_free(struct crypt_device *cd)
}
}
int crypt_suspend(struct crypt_device *cd,
const char *name)
{
crypt_status_info ci;
int r, suspended = 0;
log_dbg("Suspending volume %s.", name);
ci = crypt_status(NULL, name);
if (ci < ACTIVE) {
log_err(cd, _("Volume %s is not active.\n"), name);
return -EINVAL;
}
r = dm_query_device(name, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &suspended);
if (r < 0)
return r;
if (suspended) {
log_err(cd, _("Volume %s is already suspended.\n"), name);
return -EINVAL;
}
r = dm_suspend_and_wipe_key(name);
if (r)
log_err(cd, "Error during suspending device %s.\n", name);
return r;
}
int crypt_resume_by_passphrase(struct crypt_device *cd,
const char *name,
int keyslot,
const char *passphrase,
size_t passphrase_size)
{
struct luks_masterkey *mk = NULL;
int r, suspended = 0;
log_dbg("Resuming volume %s.", name);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
r = -EINVAL;
goto out;
}
r = dm_query_device(name, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &suspended);
if (r < 0)
return r;
if (!suspended) {
log_err(cd, _("Volume %s is not suspended.\n"), name);
return -EINVAL;
}
if (passphrase) {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
} else
r = volume_key_by_terminal_passphrase(cd, keyslot, &mk);
if (r >= 0) {
keyslot = r;
r = dm_resume_and_reinstate_key(name, mk->keyLength, mk->key);
if (r)
log_err(cd, "Error during resuming device %s.\n", name);
} else
r = keyslot;
out:
LUKS_dealloc_masterkey(mk);
return r < 0 ? r : keyslot;
}
int crypt_resume_by_keyfile(struct crypt_device *cd,
const char *name,
int keyslot,
const char *keyfile,
size_t keyfile_size)
{
struct luks_masterkey *mk = NULL;
char *passphrase_read = NULL;
unsigned int passphrase_size_read;
int r, suspended = 0;
log_dbg("Resuming volume %s.", name);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
r = -EINVAL;
goto out;
}
r = dm_query_device(name, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &suspended);
if (r < 0)
return r;
if (!suspended) {
log_err(cd, _("Volume %s is not suspended.\n"), name);
return -EINVAL;
}
if (!keyfile)
return -EINVAL;
key_from_file(cd, _("Enter passphrase: "), &passphrase_read,
&passphrase_size_read, keyfile, keyfile_size);
if(!passphrase_read)
r = -EINVAL;
else {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase_read,
passphrase_size_read, &cd->hdr, &mk, cd);
safe_free(passphrase_read);
}
if (r >= 0) {
keyslot = r;
r = dm_resume_and_reinstate_key(name, mk->keyLength, mk->key);
if (r)
log_err(cd, "Error during resuming device %s.\n", name);
} else
r = keyslot;
out:
LUKS_dealloc_masterkey(mk);
return r < 0 ? r : keyslot;
}
// slot manipulation
int crypt_keyslot_add_by_passphrase(struct crypt_device *cd,
int keyslot, // -1 any
......@@ -1288,9 +1481,8 @@ int crypt_activate_by_passphrase(struct crypt_device *cd,
{
crypt_status_info ci;
struct luks_masterkey *mk = NULL;
char *prompt = NULL, *passphrase_read = NULL;
unsigned int passphrase_size_read;
int r, tries = cd->tries;
char *prompt = NULL;
int r;
log_dbg("%s volume %s [keyslot %d] using %spassphrase.",
name ? "Activating" : "Checking", name ?: "",
......@@ -1321,25 +1513,10 @@ int crypt_activate_by_passphrase(struct crypt_device *cd,
/* provided passphrase, do not retry */
if (passphrase) {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
} else do {
if (mk)
LUKS_dealloc_masterkey(mk);
mk = NULL;
key_from_terminal(cd, prompt, &passphrase_read,
&passphrase_size_read, 0);
if(!passphrase_read) {
r = -EINVAL;
break;
}
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase_read,
passphrase_size_read, &cd->hdr, &mk, cd);
safe_free(passphrase_read);
passphrase_read = NULL;
} while (r == -EPERM && (--tries > 0));
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
} else
r = volume_key_by_terminal_passphrase(cd, keyslot, &mk);
if (r >= 0) {
keyslot = r;
......
......@@ -50,6 +50,18 @@ opens the LUKS partition <device> and sets up a mapping <name> after successful
.IP
identical to \fIremove\fR.
.PP
\fIluksSuspend\fR <name>
.IP
suspends active device (all IO operations are frozen) and wipes encryption key from kernel. Kernel version 2.6.19 or later is required.
After that operation you have to use \fIluksResume\fR to reinstate encryption key (and resume device) or \fIluksClose\fR to remove mapped device.
\fBWARNING:\fR never try to suspend device where is the cryptsetup binary itself.
.PP
\fIluksResume\fR <name>
.IP
Resumes suspended device and reinstates encryption key. You will need provide passphrase identical to \fIluksOpen\fR command (using prompting or key file).
.PP
\fIluksAddKey\fR <device> [<new key file>]
.IP
add a new key file/passphrase. An existing passphrase or key file (via \-\-key-file) must be supplied. The key file with the new material is supplied as a positional argument. <options> can be [\-\-key-file, \-\-key-slot].
......
......@@ -52,6 +52,8 @@ static int action_luksRemoveKey(int arg);
static int action_isLuks(int arg);
static int action_luksUUID(int arg);
static int action_luksDump(int arg);
static int action_luksSuspend(int arg);
static int action_luksResume(int arg);
static struct action_type {
const char *type;
......@@ -77,6 +79,8 @@ static struct action_type {
{ "isLuks", action_isLuks, 0, 1, 0, 0, 0, N_("<device>"), N_("tests <device> for LUKS partition header") },
{ "luksClose", action_remove, 0, 1, 1, 0, 1, N_("<name>"), N_("remove LUKS mapping") },
{ "luksDump", action_luksDump, 0, 1, 0, 0, 1, N_("<device>"), N_("dump LUKS partition information") },
{ "luksSuspend",action_luksSuspend, 0, 1, 1, 1, 1, N_("<device>"), N_("Suspend LUKS device and wipe key (all IOs are frozen).") },
{ "luksResume", action_luksResume, 0, 1, 1, 1, 1, N_("<device>"), N_("Resume suspended LUKS device.") },
{ "luksDelKey", action_luksDelKey, 0, 2, 1, 1, 1, N_("<device> <key slot>"), N_("identical to luksKillSlot - DEPRECATED - see man page") },
{ "reload", action_create, 1, 2, 1, 1, 1, N_("<name> <device>"), N_("modify active device - DEPRECATED - see man page") },
{ NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL }
......@@ -490,6 +494,41 @@ static int action_luksDump(int arg)
return crypt_luksDump(&options);
}
static int action_luksSuspend(int arg)
{
struct crypt_device *cd = NULL;
int r;
r = crypt_init_by_name(&cd, action_argv[0]);
if (!r)
r = crypt_suspend(cd, action_argv[0]);
crypt_free(cd);
return r;
}
static int action_luksResume(int arg)
{
struct crypt_device *cd = NULL;
int r;
if ((r = crypt_init_by_name(&cd, action_argv[0])))
goto out;
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
goto out;
if (opt_key_file)
r = crypt_resume_by_keyfile(cd, action_argv[0], CRYPT_ANY_SLOT,
opt_key_file, opt_key_size / 8);
else
r = crypt_resume_by_passphrase(cd, action_argv[0], CRYPT_ANY_SLOT,
NULL, 0);
out:
crypt_free(cd);
return r;
}
static void usage(poptContext popt_context, int exitcode,
const char *error, const char *more)
{
......
......@@ -582,6 +582,35 @@ static void UseLuksDevice(void)
crypt_free(cd);
}
static void SuspendDevice(void)
{
struct crypt_device *cd;
char key[128];
size_t key_size;
int fd;
OK_(crypt_init(&cd, DEVICE_1));
OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
OK_(crypt_suspend(cd, CDEVICE_1));
FAIL_(crypt_suspend(cd, CDEVICE_1), "already suspended");
FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)-1), "wrong key");
OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)));
FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)), "not suspended");
OK_(_prepare_keyfile(KEYFILE1, KEY1));
OK_(crypt_suspend(cd, CDEVICE_1));
FAIL_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1 "blah", 0), "wrong keyfile");
OK_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0));
FAIL_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0), "not suspended");
_remove_keyfiles();
OK_(crypt_deactivate(cd, CDEVICE_1));
crypt_free(cd);
}
static void AddDeviceLuks(void)
{
struct crypt_device *cd;
......@@ -678,6 +707,7 @@ int main (int argc, char *argv[])
RUN_(AddDevicePlain, "plain device API creation exercise");
RUN_(AddDeviceLuks, "Format and use LUKS device");
RUN_(UseLuksDevice, "Use pre-formated LUKS device");
RUN_(SuspendDevice, "Suspend/Resume test");
_cleanup();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment