qcrypto/luks: implement encryption key management
Next few patches will expose that functionality to the user. Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20200608094030.670121-3-mlevitsk@redhat.com> Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
43cbd06df2
commit
557d2bdcca
@ -32,6 +32,7 @@
|
|||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
|
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
|
#include "qemu/bitmap.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reference for the LUKS format implemented here is
|
* Reference for the LUKS format implemented here is
|
||||||
@ -70,6 +71,9 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
|
|||||||
|
|
||||||
#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
|
#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
|
||||||
|
|
||||||
|
#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS 2000
|
||||||
|
#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
|
||||||
|
|
||||||
static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
|
static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
|
||||||
'L', 'U', 'K', 'S', 0xBA, 0xBE
|
'L', 'U', 'K', 'S', 0xBA, 0xBE
|
||||||
};
|
};
|
||||||
@ -219,6 +223,9 @@ struct QCryptoBlockLUKS {
|
|||||||
|
|
||||||
/* Hash algorithm used in pbkdf2 function */
|
/* Hash algorithm used in pbkdf2 function */
|
||||||
QCryptoHashAlgorithm hash_alg;
|
QCryptoHashAlgorithm hash_alg;
|
||||||
|
|
||||||
|
/* Name of the secret that was used to open the image */
|
||||||
|
char *secret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -720,7 +727,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QCryptoBlockLUKS *luks = block->opaque;
|
QCryptoBlockLUKS *luks = block->opaque;
|
||||||
QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
|
QCryptoBlockLUKSKeySlot *slot;
|
||||||
g_autofree uint8_t *splitkey = NULL;
|
g_autofree uint8_t *splitkey = NULL;
|
||||||
size_t splitkeylen;
|
size_t splitkeylen;
|
||||||
g_autofree uint8_t *slotkey = NULL;
|
g_autofree uint8_t *slotkey = NULL;
|
||||||
@ -730,6 +737,8 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
|
|||||||
uint64_t iters;
|
uint64_t iters;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
|
assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
|
||||||
|
slot = &luks->header.key_slots[slot_idx];
|
||||||
if (qcrypto_random_bytes(slot->salt,
|
if (qcrypto_random_bytes(slot->salt,
|
||||||
QCRYPTO_BLOCK_LUKS_SALT_LEN,
|
QCRYPTO_BLOCK_LUKS_SALT_LEN,
|
||||||
errp) < 0) {
|
errp) < 0) {
|
||||||
@ -890,7 +899,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
QCryptoBlockLUKS *luks = block->opaque;
|
QCryptoBlockLUKS *luks = block->opaque;
|
||||||
const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
|
const QCryptoBlockLUKSKeySlot *slot;
|
||||||
g_autofree uint8_t *splitkey = NULL;
|
g_autofree uint8_t *splitkey = NULL;
|
||||||
size_t splitkeylen;
|
size_t splitkeylen;
|
||||||
g_autofree uint8_t *possiblekey = NULL;
|
g_autofree uint8_t *possiblekey = NULL;
|
||||||
@ -900,6 +909,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
|
|||||||
g_autoptr(QCryptoIVGen) ivgen = NULL;
|
g_autoptr(QCryptoIVGen) ivgen = NULL;
|
||||||
size_t niv;
|
size_t niv;
|
||||||
|
|
||||||
|
assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
|
||||||
|
slot = &luks->header.key_slots[slot_idx];
|
||||||
if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
|
if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1069,6 +1080,126 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if a slot i is marked as active
|
||||||
|
* (contains encrypted copy of the master key)
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
qcrypto_block_luks_slot_active(const QCryptoBlockLUKS *luks,
|
||||||
|
unsigned int slot_idx)
|
||||||
|
{
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
|
assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
|
||||||
|
val = luks->header.key_slots[slot_idx].active;
|
||||||
|
return val == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of slots that are marked as active
|
||||||
|
* (slots that contain encrypted copy of the master key)
|
||||||
|
*/
|
||||||
|
static unsigned int
|
||||||
|
qcrypto_block_luks_count_active_slots(const QCryptoBlockLUKS *luks)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
unsigned int ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
|
||||||
|
if (qcrypto_block_luks_slot_active(luks, i)) {
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds first key slot which is not active
|
||||||
|
* Returns the key slot index, or -1 if it doesn't exist
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
qcrypto_block_luks_find_free_keyslot(const QCryptoBlockLUKS *luks)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
|
||||||
|
if (!qcrypto_block_luks_slot_active(luks, i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erases an keyslot given its index
|
||||||
|
* Returns:
|
||||||
|
* 0 if the keyslot was erased successfully
|
||||||
|
* -1 if a error occurred while erasing the keyslot
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
qcrypto_block_luks_erase_key(QCryptoBlock *block,
|
||||||
|
unsigned int slot_idx,
|
||||||
|
QCryptoBlockWriteFunc writefunc,
|
||||||
|
void *opaque,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QCryptoBlockLUKS *luks = block->opaque;
|
||||||
|
QCryptoBlockLUKSKeySlot *slot;
|
||||||
|
g_autofree uint8_t *garbagesplitkey = NULL;
|
||||||
|
size_t splitkeylen;
|
||||||
|
size_t i;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
|
||||||
|
slot = &luks->header.key_slots[slot_idx];
|
||||||
|
|
||||||
|
splitkeylen = luks->header.master_key_len * slot->stripes;
|
||||||
|
assert(splitkeylen > 0);
|
||||||
|
|
||||||
|
garbagesplitkey = g_new0(uint8_t, splitkeylen);
|
||||||
|
|
||||||
|
/* Reset the key slot header */
|
||||||
|
memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
|
||||||
|
slot->iterations = 0;
|
||||||
|
slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
|
||||||
|
|
||||||
|
ret = qcrypto_block_luks_store_header(block, writefunc,
|
||||||
|
opaque, &local_err);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Now try to erase the key material, even if the header
|
||||||
|
* update failed
|
||||||
|
*/
|
||||||
|
for (i = 0; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS; i++) {
|
||||||
|
if (qcrypto_random_bytes(garbagesplitkey,
|
||||||
|
splitkeylen, &local_err) < 0) {
|
||||||
|
/*
|
||||||
|
* If we failed to get the random data, still write
|
||||||
|
* at least zeros to the key slot at least once
|
||||||
|
*/
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (writefunc(block,
|
||||||
|
slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
|
||||||
|
garbagesplitkey,
|
||||||
|
splitkeylen,
|
||||||
|
opaque,
|
||||||
|
&local_err) != splitkeylen) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qcrypto_block_luks_open(QCryptoBlock *block,
|
qcrypto_block_luks_open(QCryptoBlock *block,
|
||||||
@ -1099,6 +1230,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
|
|||||||
|
|
||||||
luks = g_new0(QCryptoBlockLUKS, 1);
|
luks = g_new0(QCryptoBlockLUKS, 1);
|
||||||
block->opaque = luks;
|
block->opaque = luks;
|
||||||
|
luks->secret = g_strdup(options->u.luks.key_secret);
|
||||||
|
|
||||||
if (qcrypto_block_luks_load_header(block, readfunc, opaque, errp) < 0) {
|
if (qcrypto_block_luks_load_header(block, readfunc, opaque, errp) < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -1164,6 +1296,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
|
|||||||
fail:
|
fail:
|
||||||
qcrypto_block_free_cipher(block);
|
qcrypto_block_free_cipher(block);
|
||||||
qcrypto_ivgen_free(block->ivgen);
|
qcrypto_ivgen_free(block->ivgen);
|
||||||
|
g_free(luks->secret);
|
||||||
g_free(luks);
|
g_free(luks);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1204,7 +1337,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
|
|
||||||
memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
|
memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
|
||||||
if (!luks_opts.has_iter_time) {
|
if (!luks_opts.has_iter_time) {
|
||||||
luks_opts.iter_time = 2000;
|
luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
|
||||||
}
|
}
|
||||||
if (!luks_opts.has_cipher_alg) {
|
if (!luks_opts.has_cipher_alg) {
|
||||||
luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
|
luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
|
||||||
@ -1244,6 +1377,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
optprefix ? optprefix : "");
|
optprefix ? optprefix : "");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
luks->secret = g_strdup(options->u.luks.key_secret);
|
||||||
|
|
||||||
password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
|
password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
|
||||||
if (!password) {
|
if (!password) {
|
||||||
goto error;
|
goto error;
|
||||||
@ -1471,10 +1606,278 @@ qcrypto_block_luks_create(QCryptoBlock *block,
|
|||||||
qcrypto_block_free_cipher(block);
|
qcrypto_block_free_cipher(block);
|
||||||
qcrypto_ivgen_free(block->ivgen);
|
qcrypto_ivgen_free(block->ivgen);
|
||||||
|
|
||||||
|
g_free(luks->secret);
|
||||||
g_free(luks);
|
g_free(luks);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qcrypto_block_luks_amend_add_keyslot(QCryptoBlock *block,
|
||||||
|
QCryptoBlockReadFunc readfunc,
|
||||||
|
QCryptoBlockWriteFunc writefunc,
|
||||||
|
void *opaque,
|
||||||
|
QCryptoBlockAmendOptionsLUKS *opts_luks,
|
||||||
|
bool force,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QCryptoBlockLUKS *luks = block->opaque;
|
||||||
|
uint64_t iter_time = opts_luks->has_iter_time ?
|
||||||
|
opts_luks->iter_time :
|
||||||
|
QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
|
||||||
|
int keyslot;
|
||||||
|
g_autofree char *old_password = NULL;
|
||||||
|
g_autofree char *new_password = NULL;
|
||||||
|
g_autofree uint8_t *master_key = NULL;
|
||||||
|
|
||||||
|
char *secret = opts_luks->has_secret ? opts_luks->secret : luks->secret;
|
||||||
|
|
||||||
|
if (!opts_luks->has_new_secret) {
|
||||||
|
error_setg(errp, "'new-secret' is required to activate a keyslot");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (opts_luks->has_old_secret) {
|
||||||
|
error_setg(errp,
|
||||||
|
"'old-secret' must not be given when activating keyslots");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts_luks->has_keyslot) {
|
||||||
|
keyslot = opts_luks->keyslot;
|
||||||
|
if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Invalid keyslot %u specified, must be between 0 and %u",
|
||||||
|
keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keyslot = qcrypto_block_luks_find_free_keyslot(luks);
|
||||||
|
if (keyslot == -1) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Can't add a keyslot - all keyslots are in use");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && qcrypto_block_luks_slot_active(luks, keyslot)) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Refusing to overwrite active keyslot %i - "
|
||||||
|
"please erase it first",
|
||||||
|
keyslot);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locate the password that will be used to retrieve the master key */
|
||||||
|
old_password = qcrypto_secret_lookup_as_utf8(secret, errp);
|
||||||
|
if (!old_password) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the master key */
|
||||||
|
master_key = g_new0(uint8_t, luks->header.master_key_len);
|
||||||
|
|
||||||
|
if (qcrypto_block_luks_find_key(block, old_password, master_key,
|
||||||
|
readfunc, opaque, errp) < 0) {
|
||||||
|
error_append_hint(errp, "Failed to retrieve the master key");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locate the new password*/
|
||||||
|
new_password = qcrypto_secret_lookup_as_utf8(opts_luks->new_secret, errp);
|
||||||
|
if (!new_password) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now set the new keyslots */
|
||||||
|
if (qcrypto_block_luks_store_key(block, keyslot, new_password, master_key,
|
||||||
|
iter_time, writefunc, opaque, errp)) {
|
||||||
|
error_append_hint(errp, "Failed to write to keyslot %i", keyslot);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block,
|
||||||
|
QCryptoBlockReadFunc readfunc,
|
||||||
|
QCryptoBlockWriteFunc writefunc,
|
||||||
|
void *opaque,
|
||||||
|
QCryptoBlockAmendOptionsLUKS *opts_luks,
|
||||||
|
bool force,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QCryptoBlockLUKS *luks = block->opaque;
|
||||||
|
g_autofree uint8_t *tmpkey = NULL;
|
||||||
|
g_autofree char *old_password = NULL;
|
||||||
|
|
||||||
|
if (opts_luks->has_new_secret) {
|
||||||
|
error_setg(errp,
|
||||||
|
"'new-secret' must not be given when erasing keyslots");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (opts_luks->has_iter_time) {
|
||||||
|
error_setg(errp,
|
||||||
|
"'iter-time' must not be given when erasing keyslots");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (opts_luks->has_secret) {
|
||||||
|
error_setg(errp,
|
||||||
|
"'secret' must not be given when erasing keyslots");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load the old password if given */
|
||||||
|
if (opts_luks->has_old_secret) {
|
||||||
|
old_password = qcrypto_secret_lookup_as_utf8(opts_luks->old_secret,
|
||||||
|
errp);
|
||||||
|
if (!old_password) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a temporary key buffer that we will need when
|
||||||
|
* checking if slot matches the given old password
|
||||||
|
*/
|
||||||
|
tmpkey = g_new0(uint8_t, luks->header.master_key_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Erase an explicitly given keyslot */
|
||||||
|
if (opts_luks->has_keyslot) {
|
||||||
|
int keyslot = opts_luks->keyslot;
|
||||||
|
|
||||||
|
if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Invalid keyslot %i specified, must be between 0 and %i",
|
||||||
|
keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts_luks->has_old_secret) {
|
||||||
|
int rv = qcrypto_block_luks_load_key(block,
|
||||||
|
keyslot,
|
||||||
|
old_password,
|
||||||
|
tmpkey,
|
||||||
|
readfunc,
|
||||||
|
opaque,
|
||||||
|
errp);
|
||||||
|
if (rv == -1) {
|
||||||
|
return -1;
|
||||||
|
} else if (rv == 0) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Given keyslot %i doesn't contain the given "
|
||||||
|
"old password for erase operation",
|
||||||
|
keyslot);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && !qcrypto_block_luks_slot_active(luks, keyslot)) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Given keyslot %i is already erased (inactive) ",
|
||||||
|
keyslot);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && qcrypto_block_luks_count_active_slots(luks) == 1) {
|
||||||
|
error_setg(errp,
|
||||||
|
"Attempt to erase the only active keyslot %i "
|
||||||
|
"which will erase all the data in the image "
|
||||||
|
"irreversibly - refusing operation",
|
||||||
|
keyslot);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcrypto_block_luks_erase_key(block, keyslot,
|
||||||
|
writefunc, opaque, errp)) {
|
||||||
|
error_append_hint(errp, "Failed to erase keyslot %i", keyslot);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Erase all keyslots that match the given old password */
|
||||||
|
} else if (opts_luks->has_old_secret) {
|
||||||
|
|
||||||
|
unsigned long slots_to_erase_bitmap = 0;
|
||||||
|
size_t i;
|
||||||
|
int slot_count;
|
||||||
|
|
||||||
|
assert(QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS <=
|
||||||
|
sizeof(slots_to_erase_bitmap) * 8);
|
||||||
|
|
||||||
|
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
|
||||||
|
int rv = qcrypto_block_luks_load_key(block,
|
||||||
|
i,
|
||||||
|
old_password,
|
||||||
|
tmpkey,
|
||||||
|
readfunc,
|
||||||
|
opaque,
|
||||||
|
errp);
|
||||||
|
if (rv == -1) {
|
||||||
|
return -1;
|
||||||
|
} else if (rv == 1) {
|
||||||
|
bitmap_set(&slots_to_erase_bitmap, i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_count = bitmap_count_one(&slots_to_erase_bitmap,
|
||||||
|
QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
|
||||||
|
if (slot_count == 0) {
|
||||||
|
error_setg(errp,
|
||||||
|
"No keyslots match given (old) password for erase operation");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force &&
|
||||||
|
slot_count == qcrypto_block_luks_count_active_slots(luks)) {
|
||||||
|
error_setg(errp,
|
||||||
|
"All the active keyslots match the (old) password that "
|
||||||
|
"was given and erasing them will erase all the data in "
|
||||||
|
"the image irreversibly - refusing operation");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now apply the update */
|
||||||
|
for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
|
||||||
|
if (!test_bit(i, &slots_to_erase_bitmap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (qcrypto_block_luks_erase_key(block, i, writefunc,
|
||||||
|
opaque, errp)) {
|
||||||
|
error_append_hint(errp, "Failed to erase keyslot %zu", i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_setg(errp,
|
||||||
|
"To erase keyslot(s), either explicit keyslot index "
|
||||||
|
"or the password currently contained in them must be given");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qcrypto_block_luks_amend_options(QCryptoBlock *block,
|
||||||
|
QCryptoBlockReadFunc readfunc,
|
||||||
|
QCryptoBlockWriteFunc writefunc,
|
||||||
|
void *opaque,
|
||||||
|
QCryptoBlockAmendOptions *options,
|
||||||
|
bool force,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QCryptoBlockAmendOptionsLUKS *opts_luks = &options->u.luks;
|
||||||
|
|
||||||
|
switch (opts_luks->state) {
|
||||||
|
case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE:
|
||||||
|
return qcrypto_block_luks_amend_add_keyslot(block, readfunc,
|
||||||
|
writefunc, opaque,
|
||||||
|
opts_luks, force, errp);
|
||||||
|
case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE:
|
||||||
|
return qcrypto_block_luks_amend_erase_keyslots(block, readfunc,
|
||||||
|
writefunc, opaque,
|
||||||
|
opts_luks, force, errp);
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int qcrypto_block_luks_get_info(QCryptoBlock *block,
|
static int qcrypto_block_luks_get_info(QCryptoBlock *block,
|
||||||
QCryptoBlockInfo *info,
|
QCryptoBlockInfo *info,
|
||||||
@ -1523,7 +1926,11 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
|
|||||||
|
|
||||||
static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
|
static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
|
||||||
{
|
{
|
||||||
g_free(block->opaque);
|
QCryptoBlockLUKS *luks = block->opaque;
|
||||||
|
if (luks) {
|
||||||
|
g_free(luks->secret);
|
||||||
|
g_free(luks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1560,6 +1967,7 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
|
|||||||
const QCryptoBlockDriver qcrypto_block_driver_luks = {
|
const QCryptoBlockDriver qcrypto_block_driver_luks = {
|
||||||
.open = qcrypto_block_luks_open,
|
.open = qcrypto_block_luks_open,
|
||||||
.create = qcrypto_block_luks_create,
|
.create = qcrypto_block_luks_create,
|
||||||
|
.amend = qcrypto_block_luks_amend_options,
|
||||||
.get_info = qcrypto_block_luks_get_info,
|
.get_info = qcrypto_block_luks_get_info,
|
||||||
.cleanup = qcrypto_block_luks_cleanup,
|
.cleanup = qcrypto_block_luks_cleanup,
|
||||||
.decrypt = qcrypto_block_luks_decrypt,
|
.decrypt = qcrypto_block_luks_decrypt,
|
||||||
|
@ -297,7 +297,6 @@
|
|||||||
'uuid': 'str',
|
'uuid': 'str',
|
||||||
'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }}
|
'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }}
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# @QCryptoBlockInfo:
|
# @QCryptoBlockInfo:
|
||||||
#
|
#
|
||||||
@ -310,8 +309,64 @@
|
|||||||
'discriminator': 'format',
|
'discriminator': 'format',
|
||||||
'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
|
'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @QCryptoBlockLUKSKeyslotState:
|
||||||
|
#
|
||||||
|
# Defines state of keyslots that are affected by the update
|
||||||
|
#
|
||||||
|
# @active: The slots contain the given password and marked as active
|
||||||
|
# @inactive: The slots are erased (contain garbage) and marked as inactive
|
||||||
|
#
|
||||||
|
# Since: 5.1
|
||||||
|
##
|
||||||
|
{ 'enum': 'QCryptoBlockLUKSKeyslotState',
|
||||||
|
'data': [ 'active', 'inactive' ] }
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# @QCryptoBlockAmendOptionsLUKS:
|
||||||
|
#
|
||||||
|
# This struct defines the update parameters that activate/de-activate set
|
||||||
|
# of keyslots
|
||||||
|
#
|
||||||
|
# @state: the desired state of the keyslots
|
||||||
|
#
|
||||||
|
# @new-secret: The ID of a QCryptoSecret object providing the password to be
|
||||||
|
# written into added active keyslots
|
||||||
|
#
|
||||||
|
# @old-secret: Optional (for deactivation only)
|
||||||
|
# If given will deactive all keyslots that
|
||||||
|
# match password located in QCryptoSecret with this ID
|
||||||
|
#
|
||||||
|
# @iter-time: Optional (for activation only)
|
||||||
|
# Number of milliseconds to spend in
|
||||||
|
# PBKDF passphrase processing for the newly activated keyslot.
|
||||||
|
# Currently defaults to 2000.
|
||||||
|
#
|
||||||
|
# @keyslot: Optional. ID of the keyslot to activate/deactivate.
|
||||||
|
# For keyslot activation, keyslot should not be active already
|
||||||
|
# (this is unsafe to update an active keyslot),
|
||||||
|
# but possible if 'force' parameter is given.
|
||||||
|
# If keyslot is not given, first free keyslot will be written.
|
||||||
|
#
|
||||||
|
# For keyslot deactivation, this parameter specifies the exact
|
||||||
|
# keyslot to deactivate
|
||||||
|
#
|
||||||
|
# @secret: Optional. The ID of a QCryptoSecret object providing the
|
||||||
|
# password to use to retrive current master key.
|
||||||
|
# Defaults to the same secret that was used to open the image
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Since 5.1
|
||||||
|
##
|
||||||
|
{ 'struct': 'QCryptoBlockAmendOptionsLUKS',
|
||||||
|
'data': { 'state': 'QCryptoBlockLUKSKeyslotState',
|
||||||
|
'*new-secret': 'str',
|
||||||
|
'*old-secret': 'str',
|
||||||
|
'*keyslot': 'int',
|
||||||
|
'*iter-time': 'int',
|
||||||
|
'*secret': 'str' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @QCryptoBlockAmendOptions:
|
# @QCryptoBlockAmendOptions:
|
||||||
#
|
#
|
||||||
@ -324,4 +379,4 @@
|
|||||||
'base': 'QCryptoBlockOptionsBase',
|
'base': 'QCryptoBlockOptionsBase',
|
||||||
'discriminator': 'format',
|
'discriminator': 'format',
|
||||||
'data': {
|
'data': {
|
||||||
} }
|
'luks': 'QCryptoBlockAmendOptionsLUKS' } }
|
||||||
|
Loading…
Reference in New Issue
Block a user