From: "Daniel P. Berrange" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: Fam Zheng <famz@redhat.com>, qemu-block@nongnu.org
Subject: [Qemu-devel] [PATCH v4 23/26] qcow: convert QCow to use QCryptoBlock for encryption
Date: Mon, 29 Feb 2016 12:00:58 +0000 [thread overview]
Message-ID: <1456747261-22032-24-git-send-email-berrange@redhat.com> (raw)
In-Reply-To: <1456747261-22032-1-git-send-email-berrange@redhat.com>
This converts the qcow2 driver to make use of the QCryptoBlock
APIs for encrypting image content. This is only wired up to
permit use of the legacy QCow encryption format. Users who wish
to have the strong LUKS format should switch to qcow2 instead.
With this change it is now required to use the QCryptoSecret
object for providing passwords, instead of the current block
password APIs / interactive prompting.
$QEMU \
-object secret,id=sec0,filename=/home/berrange/encrypted.pw \
-drive file=/home/berrange/encrypted.qcow,key-secret=sec0
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
block/qcow.c | 176 +++++++++++++++++++++++----------------------------
qapi/block-core.json | 17 ++++-
2 files changed, 96 insertions(+), 97 deletions(-)
diff --git a/block/qcow.c b/block/qcow.c
index 096980b..5cb5297 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -27,7 +27,9 @@
#include "qemu/module.h"
#include <zlib.h>
#include "qapi/qmp/qerror.h"
-#include "crypto/cipher.h"
+#include "crypto/block.h"
+#include "qapi/opts-visitor.h"
+#include "qapi-visit.h"
#include "migration/migration.h"
/**************************************************************/
@@ -41,6 +43,8 @@
#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+#define QCOW_OPT_KEY_SECRET "key-secret"
+
typedef struct QCowHeader {
uint32_t magic;
uint32_t version;
@@ -73,7 +77,7 @@ typedef struct BDRVQcowState {
uint8_t *cluster_cache;
uint8_t *cluster_data;
uint64_t cluster_cache_offset;
- QCryptoCipher *cipher; /* NULL if no key yet */
+ QCryptoBlock *crypto; /* Disk encryption format driver */
uint32_t crypt_method_header;
CoMutex lock;
Error *migration_blocker;
@@ -93,6 +97,19 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0;
}
+static QemuOptsList qcow_runtime_opts = {
+ .name = "qcow",
+ .head = QTAILQ_HEAD_INITIALIZER(qcow_runtime_opts.head),
+ .desc = {
+ {
+ .name = QCOW_OPT_KEY_SECRET,
+ .type = QEMU_OPT_STRING,
+ .help = "ID of the secret that provides the encryption key",
+ },
+ { /* end of list */ }
+ },
+};
+
static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
@@ -100,6 +117,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
unsigned int len, i, shift;
int ret;
QCowHeader header;
+ QemuOpts *opts = NULL;
+ Error *local_err = NULL;
+ QCryptoBlockOpenOptions *crypto_opts = NULL;
+ unsigned int cflags = 0;
+ OptsVisitor *ov;
ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header));
if (ret < 0) {
@@ -148,19 +170,53 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- if (header.crypt_method > QCOW_CRYPT_AES) {
- error_setg(errp, "invalid encryption method in qcow header");
- ret = -EINVAL;
- goto fail;
- }
- if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) {
- error_setg(errp, "AES cipher not available");
+ opts = qemu_opts_create(&qcow_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
+
s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) {
+ if (s->crypt_method_header == QCOW_CRYPT_AES) {
+ QCryptoBlockOptionsQCow *tmp;
+ ov = opts_visitor_new(opts);
+
+ crypto_opts = g_new0(QCryptoBlockOpenOptions, 1);
+ crypto_opts->format = Q_CRYPTO_BLOCK_FORMAT_QCOW;
+ visit_type_QCryptoBlockOptionsQCow(opts_get_visitor(ov),
+ "qcow", &tmp,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ opts_visitor_cleanup(ov);
+ ret = -EINVAL;
+ goto fail;
+ } else {
+ memcpy(&crypto_opts->u.qcow, tmp, sizeof(*tmp));
+ g_free(tmp);
+ }
+ opts_visitor_cleanup(ov);
+
+ if (flags & BDRV_O_NO_IO) {
+ cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
+ }
+ s->crypto = qcrypto_block_open(crypto_opts, NULL, NULL,
+ cflags, errp);
+ if (!s->crypto) {
+ error_setg(errp, "Could not setup encryption layer");
+ ret = -EINVAL;
+ goto fail;
+ }
+ } else {
+ error_setg(errp, "invalid encryption method in qcow header");
+ ret = -EINVAL;
+ goto fail;
+ }
bs->encrypted = 1;
+ bs->valid_key = 1;
}
s->cluster_bits = header.cluster_bits;
s->cluster_size = 1 << s->cluster_bits;
@@ -239,6 +295,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
bdrv_get_device_or_node_name(bs));
migrate_add_blocker(s->migration_blocker);
+ qapi_free_QCryptoBlockOpenOptions(crypto_opts);
qemu_co_mutex_init(&s->lock);
return 0;
@@ -247,6 +304,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
qemu_vfree(s->l2_cache);
g_free(s->cluster_cache);
g_free(s->cluster_data);
+ qapi_free_QCryptoBlockOpenOptions(crypto_opts);
return ret;
}
@@ -259,79 +317,6 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
return 0;
}
-static int qcow_set_key(BlockDriverState *bs, const char *key)
-{
- BDRVQcowState *s = bs->opaque;
- uint8_t keybuf[16];
- int len, i;
- Error *err;
-
- memset(keybuf, 0, 16);
- len = strlen(key);
- if (len > 16)
- len = 16;
- /* XXX: we could compress the chars to 7 bits to increase
- entropy */
- for(i = 0;i < len;i++) {
- keybuf[i] = key[i];
- }
- assert(bs->encrypted);
-
- qcrypto_cipher_free(s->cipher);
- s->cipher = qcrypto_cipher_new(
- QCRYPTO_CIPHER_ALG_AES_128,
- QCRYPTO_CIPHER_MODE_CBC,
- keybuf, G_N_ELEMENTS(keybuf),
- &err);
-
- if (!s->cipher) {
- /* XXX would be nice if errors in this method could
- * be properly propagate to the caller. Would need
- * the bdrv_set_key() API signature to be fixed. */
- error_free(err);
- return -1;
- }
- return 0;
-}
-
-static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
- uint8_t *buf, int nb_sectors, bool enc,
- Error **errp)
-{
- union {
- uint64_t ll[2];
- uint8_t b[16];
- } ivec;
- int i;
- int ret;
-
- for(i = 0; i < nb_sectors; i++) {
- ivec.ll[0] = cpu_to_le64(sector_num);
- ivec.ll[1] = 0;
- if (qcrypto_cipher_setiv(s->cipher,
- ivec.b, G_N_ELEMENTS(ivec.b),
- errp) < 0) {
- return -1;
- }
- if (enc) {
- ret = qcrypto_cipher_encrypt(s->cipher,
- buf, buf,
- 512,
- errp);
- } else {
- ret = qcrypto_cipher_decrypt(s->cipher,
- buf, buf,
- 512,
- errp);
- }
- if (ret < 0) {
- return -1;
- }
- sector_num++;
- buf += 512;
- }
- return 0;
-}
/* 'allocate' is:
*
@@ -446,15 +431,15 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
if (bs->encrypted &&
(n_end - n_start) < s->cluster_sectors) {
uint64_t start_sect;
- assert(s->cipher);
+ assert(s->crypto);
start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
memset(s->cluster_data, 0x00, 512);
for(i = 0; i < s->cluster_sectors; i++) {
if (i < n_start || i >= n_end) {
Error *err = NULL;
- if (encrypt_sectors(s, start_sect + i,
- s->cluster_data, 1,
- true, &err) < 0) {
+ if (qcrypto_block_encrypt(s->crypto, start_sect + i,
+ s->cluster_data, 1,
+ &err) < 0) {
error_free(err);
errno = EIO;
return -1;
@@ -499,7 +484,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
if (!cluster_offset) {
return 0;
}
- if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) {
+ if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypto) {
return BDRV_BLOCK_DATA;
}
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
@@ -631,9 +616,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
break;
}
if (bs->encrypted) {
- assert(s->cipher);
- if (encrypt_sectors(s, sector_num, buf,
- n, false, &err) < 0) {
+ assert(s->crypto);
+ if (qcrypto_block_decrypt(s->crypto, sector_num, buf,
+ n, &err) < 0) {
goto fail;
}
}
@@ -704,9 +689,9 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
}
if (bs->encrypted) {
Error *err = NULL;
- assert(s->cipher);
- if (encrypt_sectors(s, sector_num, buf,
- n, true, &err) < 0) {
+ assert(s->crypto);
+ if (qcrypto_block_encrypt(s->crypto, sector_num, buf,
+ n, &err) < 0) {
error_free(err);
ret = -EIO;
break;
@@ -743,8 +728,8 @@ static void qcow_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
- qcrypto_cipher_free(s->cipher);
- s->cipher = NULL;
+ qcrypto_block_free(s->crypto);
+ s->crypto = NULL;
g_free(s->l1_table);
qemu_vfree(s->l2_cache);
g_free(s->cluster_cache);
@@ -1009,7 +994,6 @@ static BlockDriver bdrv_qcow = {
.bdrv_co_writev = qcow_co_writev,
.bdrv_co_get_block_status = qcow_co_get_block_status,
- .bdrv_set_key = qcow_set_key,
.bdrv_make_empty = qcow_make_empty,
.bdrv_write_compressed = qcow_write_compressed,
.bdrv_get_info = qcow_get_info,
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 8c6549f..df4bee2 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1851,6 +1851,21 @@
'mode': 'Qcow2OverlapCheckMode' } }
##
+# @BlockdevOptionsQcow
+#
+# Driver specific block device options for qcow.
+#
+# @key-secret: #optional ID of the "secret" object providing the
+# AES decryption key.
+#
+# Since: 2.6
+##
+{ 'struct': 'BlockdevOptionsQcow',
+ 'base': 'BlockdevOptionsGenericCOWFormat',
+ 'data': { '*key-secret': 'str' } }
+
+
+##
# @BlockdevOptionsQcow2
#
# Driver specific block device options for qcow2.
@@ -2120,7 +2135,7 @@
'null-co': 'BlockdevOptionsNull',
'parallels': 'BlockdevOptionsGenericFormat',
'qcow2': 'BlockdevOptionsQcow2',
- 'qcow': 'BlockdevOptionsGenericCOWFormat',
+ 'qcow': 'BlockdevOptionsQcow',
'qed': 'BlockdevOptionsGenericCOWFormat',
'quorum': 'BlockdevOptionsQuorum',
'raw': 'BlockdevOptionsGenericFormat',
--
2.5.0
next prev parent reply other threads:[~2016-02-29 12:01 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-29 12:00 [Qemu-devel] [PATCH v4 00/26] Support LUKS encryption in block devices Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 01/26] crypto: add cryptographic random byte source Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 02/26] crypto: add support for PBKDF2 algorithm Daniel P. Berrange
2016-03-03 0:20 ` Eric Blake
2016-03-07 5:18 ` Fam Zheng
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 03/26] crypto: add support for generating initialization vectors Daniel P. Berrange
2016-03-03 0:31 ` Eric Blake
2016-03-03 10:49 ` Daniel P. Berrange
2016-03-07 5:39 ` Fam Zheng
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 04/26] crypto: add support for anti-forensic split algorithm Daniel P. Berrange
2016-03-03 0:41 ` Eric Blake
2016-03-07 5:51 ` Fam Zheng
2016-03-11 16:55 ` Daniel P. Berrange
2016-03-14 3:23 ` Fam Zheng
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 05/26] crypto: skip testing of unsupported cipher algorithms Daniel P. Berrange
2016-03-07 5:52 ` Fam Zheng
2016-03-11 19:10 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 06/26] crypto: add support for the cast5-128 cipher algorithm Daniel P. Berrange
2016-03-07 5:56 ` Fam Zheng
2016-03-11 19:14 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 07/26] crypto: add support for the serpent " Daniel P. Berrange
2016-03-07 6:05 ` Fam Zheng
2016-03-11 19:18 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 08/26] crypto: add support for the twofish " Daniel P. Berrange
2016-03-08 6:01 ` Fam Zheng
2016-03-11 19:19 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 09/26] crypto: import an implementation of the XTS cipher mode Daniel P. Berrange
2016-03-11 19:51 ` Eric Blake
2016-03-14 14:22 ` Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 10/26] crypto: refactor code for dealing with AES cipher Daniel P. Berrange
2016-03-11 20:14 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 11/26] crypto: wire up XTS mode for cipher APIs Daniel P. Berrange
2016-03-11 20:23 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 12/26] crypto: add block encryption framework Daniel P. Berrange
2016-03-11 20:58 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 13/26] crypto: implement the LUKS block encryption format Daniel P. Berrange
2016-03-11 22:31 ` Eric Blake
2016-03-14 14:27 ` Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 14/26] block: add flag to indicate that no I/O will be performed Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 15/26] qemu-img/qemu-io: don't prompt for passwords if not required Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 16/26] tests: redirect stderr to stdout for iotests Daniel P. Berrange
2016-03-11 22:51 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 17/26] tests: refactor python I/O tests helper main method Daniel P. Berrange
2016-03-11 22:57 ` Eric Blake
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 18/26] tests: add output filter to python I/O tests helper Daniel P. Berrange
2016-03-14 17:57 ` Eric Blake
2016-03-14 18:33 ` Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 19/26] block: add generic full disk encryption driver Daniel P. Berrange
2016-03-15 13:59 ` Eric Blake
2016-03-15 14:03 ` Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 20/26] qcow2: make qcow2_encrypt_sectors encrypt in place Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 21/26] qcow2: convert QCow2 to use QCryptoBlock for encryption Daniel P. Berrange
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 22/26] qcow: make encrypt_sectors encrypt in place Daniel P. Berrange
2016-02-29 12:00 ` Daniel P. Berrange [this message]
2016-02-29 12:00 ` [Qemu-devel] [PATCH v4 24/26] block: rip out all traces of password prompting Daniel P. Berrange
2016-05-12 20:35 ` Eric Blake
2016-02-29 12:01 ` [Qemu-devel] [PATCH v4 25/26] block: remove all encryption handling APIs Daniel P. Berrange
2016-02-29 12:01 ` [Qemu-devel] [PATCH v4 26/26] block: remove support for legecy AES qcow/qcow2 encryption Daniel P. Berrange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1456747261-22032-24-git-send-email-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=famz@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.