Re: [Qemu-block] [PATCH] block: posix: Always allocate the first block

2019-08-25 Thread Maxim Levitsky
On Sun, 2019-08-25 at 22:51 +0300, Nir Soffer wrote:
> On Sun, Aug 25, 2019 at 10:44 AM Maxim Levitsky  wrote:
> > On Sat, 2019-08-17 at 00:21 +0300, Nir Soffer wrote:
> > > When creating an image with preallocation "off" or "falloc", the first
> > > block of the image is typically not allocated. When using Gluster
> > > storage backed by XFS filesystem, reading this block using direct I/O
> > > succeeds regardless of request length, fooling alignment detection.
> > > 
> > > In this case we fallback to a safe value (4096) instead of the optimal
> > > value (512), which may lead to unneeded data copying when aligning
> > > requests.  Allocating the first block avoids the fallback.
> > > 
> > > When using preallocation=off, we always allocate at least one filesystem
> > > block:
> > > 
> > > $ ./qemu-img create -f raw test.raw 1g
> > > Formatting 'test.raw', fmt=raw size=1073741824
> > > 
> > > $ ls -lhs test.raw
> > > 4.0K -rw-r--r--. 1 nsoffer nsoffer 1.0G Aug 16 23:48 test.raw
> > 
> > Are you sure about this?
> 
> This is the new behaviour with this change...
> 
> > [mlevitsk@maximlenovopc ~/work/test_area/posix-file 0]$ qemu-img create -f 
> > raw test.raw 1g -o preallocation=off
> > Formatting 'test.raw', fmt=raw size=1073741824 preallocation=off
> > [mlevitsk@maximlenovopc ~/work/test_area/posix-file 0]$ls -lhs ./test.raw 
> > 0 -rw-r--r--. 1 mlevitsk mlevitsk 1.0G Aug 25 10:38 ./test.raw
> > 
> > ext4, tested on qemu-4.0.0 and qemu git master.
> 
> And this is the old behavior. I guess the commit message does not make it 
> clear.

Ah, thanks!


> > From what I remember, the only case when posix-raw touches the first block 
> > is to zero it out
> > when running on top of kernel block device, to erase whatever header might 
> > be there, and this
> > is also kind of a backward compat hack which might be one day removed.
> 
> This change is only for file, on block storage we use BLKSSZGET.
>  
> > [...]
> > 
> > Best regards,
> > Maxim Levitsky
> > 
> > 

Best regards,
Maxim Levitsky





Re: [Qemu-block] [Qemu-devel] [PATCH 06/13] qcrypto-luks: implement more rigorous header checking

2019-08-26 Thread Maxim Levitsky
On Mon, 2019-08-26 at 08:31 -0500, Eric Blake wrote:
> On 8/25/19 11:08 AM, Maxim Levitsky wrote:
> 
> > > > I'd do a separate check for stripes and active fields, and then give a
> > > > specific error message for each. That way if this does ever trigger
> > > > in practice will immediately understand which check failed.
> > > > 
> > > > Also using '%d' rather than '%i' is more common convention
> > > 
> > > Done.
> > 
> > Note that I switched i,j to be size_t since you said that you prefer this,
> > and to print this I apparently need %lu.
> 
> Actually, for size_t, you need %zu. %lu/size_t will cause warnings on
> 32-bit platforms.
> 
> 
Thank you!
I have read something like that on the internet, but I wondered,
what actually is the most portable way.

Best regards,
Maxim Levitsky




[Qemu-block] [PATCH v2 02/13] block-crypto: misc refactoring

2019-08-26 Thread Maxim Levitsky
* rename the write_func to create_write_func,
  and init_func to create_init_func
  this is  preparation for other write_func that will
  be used to update the encryption keys.

No functional changes

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 block/crypto.c | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index 7eb698774e..6e822c6e50 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -78,7 +78,7 @@ struct BlockCryptoCreateData {
 };
 
 
-static ssize_t block_crypto_write_func(QCryptoBlock *block,
+static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
size_t offset,
const uint8_t *buf,
size_t buflen,
@@ -96,8 +96,7 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
 return ret;
 }
 
-
-static ssize_t block_crypto_init_func(QCryptoBlock *block,
+static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
   size_t headerlen,
   void *opaque,
   Error **errp)
@@ -109,7 +108,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
 return -EFBIG;
 }
 
-/* User provided size should reflect amount of space made
+/*
+ * User provided size should reflect amount of space made
  * available to the guest, so we must take account of that
  * which will be used by the crypto header
  */
@@ -279,8 +279,8 @@ static int block_crypto_co_create_generic(BlockDriverState 
*bs,
 };
 
 crypto = qcrypto_block_create(opts, NULL,
-  block_crypto_init_func,
-  block_crypto_write_func,
+  block_crypto_create_init_func,
+  block_crypto_create_write_func,
   &data,
   errp);
 
-- 
2.17.2




[Qemu-block] [PATCH v2 03/13] qcrypto-luks: rename some fields in QCryptoBlockLUKSHeader

2019-08-26 Thread Maxim Levitsky
* key_bytes -> master_key_len
* payload_offset = payload_offset_sector (to emphasise that this isn't byte 
offset)
* key_offset -> key_offset_sector - same as above for luks slots

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 91 +++--
 1 file changed, 47 insertions(+), 44 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 743949adbf..f12fa2d270 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -143,7 +143,7 @@ struct QCryptoBlockLUKSKeySlot {
 /* salt for PBKDF2 */
 uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN];
 /* start sector of key material */
-uint32_t key_offset;
+uint32_t key_offset_sector;
 /* number of anti-forensic stripes */
 uint32_t stripes;
 };
@@ -172,10 +172,10 @@ struct QCryptoBlockLUKSHeader {
 char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN];
 
 /* start offset of the volume data (in 512 byte sectors) */
-uint32_t payload_offset;
+uint32_t payload_offset_sector;
 
 /* Number of key bytes */
-uint32_t key_bytes;
+uint32_t master_key_len;
 
 /* master key checksum after PBKDF2 */
 uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN];
@@ -466,7 +466,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * then encrypted.
  */
 rv = readfunc(block,
-  slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+  slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
   splitkey, splitkeylen,
   opaque,
   errp);
@@ -584,8 +584,8 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 size_t i;
 int rv;
 
-*masterkey = g_new0(uint8_t, luks->header.key_bytes);
-*masterkeylen = luks->header.key_bytes;
+*masterkey = g_new0(uint8_t, luks->header.master_key_len);
+*masterkeylen = luks->header.master_key_len;
 
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 rv = qcrypto_block_luks_load_key(block,
@@ -677,14 +677,14 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 /* The header is always stored in big-endian format, so
  * convert everything to native */
 be16_to_cpus(&luks->header.version);
-be32_to_cpus(&luks->header.payload_offset);
-be32_to_cpus(&luks->header.key_bytes);
+be32_to_cpus(&luks->header.payload_offset_sector);
+be32_to_cpus(&luks->header.master_key_len);
 be32_to_cpus(&luks->header.master_key_iterations);
 
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 be32_to_cpus(&luks->header.key_slots[i].active);
 be32_to_cpus(&luks->header.key_slots[i].iterations);
-be32_to_cpus(&luks->header.key_slots[i].key_offset);
+be32_to_cpus(&luks->header.key_slots[i].key_offset_sector);
 be32_to_cpus(&luks->header.key_slots[i].stripes);
 }
 
@@ -743,10 +743,11 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 goto fail;
 }
 
-cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
-  ciphermode,
-  luks->header.key_bytes,
-  &local_err);
+cipheralg =
+qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+  ciphermode,
+  luks->header.master_key_len,
+  &local_err);
 if (local_err) {
 ret = -ENOTSUP;
 error_propagate(errp, local_err);
@@ -838,7 +839,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 }
 
 block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
-block->payload_offset = luks->header.payload_offset *
+block->payload_offset = luks->header.payload_offset_sector *
 block->sector_size;
 
 luks->cipher_alg = cipheralg;
@@ -993,9 +994,11 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 strcpy(luks->header.cipher_mode, cipher_mode_spec);
 strcpy(luks->header.hash_spec, hash_alg);
 
-luks->header.key_bytes = qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+luks->header.master_key_len =
+qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+
 if (luks_opts.cipher_mode == QCRYPTO_CIPHER_MODE_XTS) {
-luks->header.key_bytes *= 2;
+luks->header.master_key_len *= 2;
 }
 
 /* Generate the salt used for hashing the master key
@@ -1008,9 +1011,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 }
 
 /* Generate random master key */
-masterkey = g_new0(uint8_t, luks->header.key_bytes);
+masterkey = g_new0(uint8_t, luks->header.master_key_len);
 if (qcrypto_random_bytes(masterkey,
- 

[Qemu-block] [PATCH v2 07/13] qcrypto-luks: use the parsed encryption settings in QCryptoBlockLUKS

2019-08-26 Thread Maxim Levitsky
Prior to that patch, the parsed encryptio settings
were alrady stored into the QCryptoBlockLUKS but not
used anywhere but in qcrypto_block_luks_get_info

Using them simplifies the code

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 169 +---
 1 file changed, 79 insertions(+), 90 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 0d81f2ac61..cad65ae0aa 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -199,13 +199,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) 
!= 592);
 struct QCryptoBlockLUKS {
 QCryptoBlockLUKSHeader header;
 
-/* Cache parsed versions of what's in header fields,
- * as we can't rely on QCryptoBlock.cipher being
- * non-NULL */
+/* Main encryption algorithm used for encryption*/
 QCryptoCipherAlgorithm cipher_alg;
+
+/* Mode of encryption for the selected encryption algorithm */
 QCryptoCipherMode cipher_mode;
+
+/* Initialization vector generation algorithm */
 QCryptoIVGenAlgorithm ivgen_alg;
+
+/* Hash algorithm used for IV generation*/
 QCryptoHashAlgorithm ivgen_hash_alg;
+
+/*
+ * Encryption algorithm used for IV generation.
+ * Usually the same as main encryption algorithm
+ */
+QCryptoCipherAlgorithm ivgen_cipher_alg;
+
+/* Hash algorithm used in pbkdf2 function */
 QCryptoHashAlgorithm hash_alg;
 };
 
@@ -412,12 +424,6 @@ static int
 qcrypto_block_luks_load_key(QCryptoBlock *block,
 unsigned int slot_idx,
 const char *password,
-QCryptoCipherAlgorithm cipheralg,
-QCryptoCipherMode ciphermode,
-QCryptoHashAlgorithm hash,
-QCryptoIVGenAlgorithm ivalg,
-QCryptoCipherAlgorithm ivcipheralg,
-QCryptoHashAlgorithm ivhash,
 uint8_t *masterkey,
 QCryptoBlockReadFunc readfunc,
 void *opaque,
@@ -449,7 +455,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * the key is correct and validate the results of
  * decryption later.
  */
-if (qcrypto_pbkdf2(hash,
+if (qcrypto_pbkdf2(luks->hash_alg,
(const uint8_t *)password, strlen(password),
slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
slot->iterations,
@@ -477,19 +483,23 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 
 /* Setup the cipher/ivgen that we'll use to try to decrypt
  * the split master key material */
-cipher = qcrypto_cipher_new(cipheralg, ciphermode,
-possiblekey, luks->header.master_key_len,
+cipher = qcrypto_cipher_new(luks->cipher_alg,
+luks->cipher_mode,
+possiblekey,
+luks->header.master_key_len,
 errp);
 if (!cipher) {
 return -1;
 }
 
-niv = qcrypto_cipher_get_iv_len(cipheralg,
-ciphermode);
-ivgen = qcrypto_ivgen_new(ivalg,
-  ivcipheralg,
-  ivhash,
-  possiblekey, luks->header.master_key_len,
+niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
+luks->cipher_mode);
+
+ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+  luks->ivgen_cipher_alg,
+  luks->ivgen_hash_alg,
+  possiblekey,
+  luks->header.master_key_len,
   errp);
 if (!ivgen) {
 return -1;
@@ -518,7 +528,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * Now we've decrypted the split master key, join
  * it back together to get the actual master key.
  */
-if (qcrypto_afsplit_decode(hash,
+if (qcrypto_afsplit_decode(luks->hash_alg,
luks->header.master_key_len,
slot->stripes,
splitkey,
@@ -536,7 +546,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * then comparing that to the hash stored in the key slot
  * header
  */
-if (qcrypto_pbkdf2(hash,
+if (qcrypto_pbkdf2(luks->hash_alg,
masterkey,
luks->header.master_key_len,
luks->header.master_key_salt,
@@ -570,12 +580,6 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 static int
 qcrypto_block_luks_find_key(QCryptoBlock *block,
 const char *password,
-QCryptoCiph

[Qemu-block] [PATCH v2 00/13] RFC crypto/luks: preparation for encryption key managment

2019-08-26 Thread Maxim Levitsky
Hi!

This patch series is the refactoring/preparation part of the
former patch series I had sent which adds support for luks
key management.

I tried my best to address all the comments that were given
during the review, and I would like to use that opportunity
to thanks again for the review I was given.

Best regards,
Maxim Levitsky

Maxim Levitsky (13):
  introduce g_autowipe
  block-crypto: misc refactoring
  qcrypto-luks: rename some fields in QCryptoBlockLUKSHeader
  qcrypto-luks: don't overwrite cipher_mode in header
  qcrypto-luks: simplify masterkey and masterkey length
  qcrypto-block: pass keyslot index rather that pointer to the keyslot
  qcrypto-luks: use the parsed encryption settings in QCryptoBlockLUKS
  qcrypto-luks: extract store and load header
  qcrypto-block: extract check and parse header
  qcrypto-luks: refactoring: extract store key function
  qcrypto-luks: refactoring: simplify the math used for keyslot
locations
  qcrypto-luks: use g_autowipe
  qcrypto-luks: implement more rigorous header checking

 block/crypto.c  |   12 +-
 crypto/block-luks.c | 1046 +--
 include/autowipe.h  |   52 +++
 3 files changed, 666 insertions(+), 444 deletions(-)
 create mode 100644 include/autowipe.h

-- 
2.17.2




[Qemu-block] [PATCH v2 04/13] qcrypto-luks: don't overwrite cipher_mode in header

2019-08-26 Thread Maxim Levitsky
This way we can store the header we loaded, which
will be used in key management code

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index f12fa2d270..e9ae3f6baa 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -645,6 +645,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 QCryptoHashAlgorithm hash;
 QCryptoHashAlgorithm ivhash;
 g_autofree char *password = NULL;
+g_autofree char *cipher_mode = NULL;
 
 if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
 if (!options->u.luks.key_secret) {
@@ -701,6 +702,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 goto fail;
 }
 
+cipher_mode = g_strdup(luks->header.cipher_mode);
+
 /*
  * The cipher_mode header contains a string that we have
  * to further parse, of the format
@@ -709,11 +712,11 @@ qcrypto_block_luks_open(QCryptoBlock *block,
  *
  * eg  cbc-essiv:sha256, cbc-plain64
  */
-ivgen_name = strchr(luks->header.cipher_mode, '-');
+ivgen_name = strchr(cipher_mode, '-');
 if (!ivgen_name) {
 ret = -EINVAL;
 error_setg(errp, "Unexpected cipher mode string format %s",
-   luks->header.cipher_mode);
+cipher_mode);
 goto fail;
 }
 *ivgen_name = '\0';
@@ -735,7 +738,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 }
 }
 
-ciphermode = 
qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
+ciphermode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
&local_err);
 if (local_err) {
 ret = -ENOTSUP;
-- 
2.17.2




[Qemu-block] [PATCH v2 01/13] introduce g_autowipe

2019-08-26 Thread Maxim Levitsky
Marking a pointer with g_autowipe, will
not only free it at the scope exit, but also
erase the data it points to just prior to freeing it.

This is first attempt to implement this feature,
as suggested by Daniel and Nir.

The things that need to be verified prior to merging this is

1. Can we just always use memset_s (defined in C++)
 or some alternative.

2. is it portable enought for us to use malloc_usable_size
to get the size of malloced pointer in the autofree callback?
This function is aviable in glibc (but no wrapper in glib).

Thanks for Daniel for the g_autowipe and to Nir for the
information about the fact that plain memset is usually
optimized away.

Suggested-by: Daniel P. Berrangé 
Suggested-by: Nir Soffer 
Signed-off-by: Maxim Levitsky 
---
 include/autowipe.h | 52 ++
 1 file changed, 52 insertions(+)
 create mode 100644 include/autowipe.h

diff --git a/include/autowipe.h b/include/autowipe.h
new file mode 100644
index 00..1ed4eaf3ba
--- /dev/null
+++ b/include/autowipe.h
@@ -0,0 +1,52 @@
+/*
+ * g_autowipe implementation for crypto secret wiping
+ *
+ * Copyright (c) 2019 Red Hat, Inc.
+ * Copyright (c) 2019 Maxim Levitsky
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include 
+#include 
+#include 
+
+
+/*
+ * based on
+ * 
https://www.cryptologie.net/article/419/zeroing-memory-compiler-optimizations-and-memset_s/
+ */
+
+static inline void memerase(void *pointer, size_t size)
+{
+#ifdef __STDC_LIB_EXT1__
+memset_s(pointer, size, 0, size);
+#else
+/*volatile used to force compiler to not optimize the code away*/
+volatile unsigned char *p = pointer;
+while (size--) {
+*p++ = 0;
+}
+#endif
+}
+
+static void g_autoptr_cleanup_generic_wipe_gfree(void *p)
+{
+void **pp = (void **)p;
+size_t size = malloc_usable_size(*pp);
+memerase(*pp, size);
+g_free(*pp);
+}
+
+#define g_autowipe _GLIB_CLEANUP(g_autoptr_cleanup_generic_wipe_gfree)
-- 
2.17.2




[Qemu-block] [PATCH v2 10/13] qcrypto-luks: refactoring: extract store key function

2019-08-26 Thread Maxim Levitsky
This function will be used later to store
new keys to the luks metadata

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 310 ++--
 1 file changed, 184 insertions(+), 126 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index cc9a52c9af..d713125925 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -650,6 +650,176 @@ out:
 
 }
 
+/*
+ * Given a key slot,  user password, and the master key,
+ * will store the encrypted master key there, and update the
+ * in-memory header. User must then write the in-memory header
+ *
+ * Returns:
+ *0 if the keyslot was written successfully
+ *  with the provided password
+ *   -1 if a fatal error occurred while storing the key
+ */
+static int
+qcrypto_block_luks_store_key(QCryptoBlock *block,
+ unsigned int slot_idx,
+ const char *password,
+ uint8_t *masterkey,
+ uint64_t iter_time,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+QCryptoBlockLUKS *luks = block->opaque;
+QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
+g_autofree uint8_t *splitkey = NULL;
+size_t splitkeylen;
+g_autofree uint8_t *slotkey = NULL;
+g_autoptr(QCryptoCipher) cipher = NULL;
+g_autoptr(QCryptoIVGen) ivgen = NULL;
+Error *local_err = NULL;
+uint64_t iters;
+int ret = -1;
+
+if (qcrypto_random_bytes(slot->salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ errp) < 0) {
+goto cleanup;
+}
+
+splitkeylen = luks->header.master_key_len * slot->stripes;
+
+/*
+ * Determine how many iterations are required to
+ * hash the user password while consuming 1 second of compute
+ * time
+ */
+iters = qcrypto_pbkdf2_count_iters(luks->hash_alg,
+   (uint8_t *)password, strlen(password),
+   slot->salt,
+   QCRYPTO_BLOCK_LUKS_SALT_LEN,
+   luks->header.master_key_len,
+   &local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+goto cleanup;
+}
+
+if (iters > (ULLONG_MAX / iter_time)) {
+error_setg_errno(errp, ERANGE,
+ "PBKDF iterations %llu too large to scale",
+ (unsigned long long)iters);
+goto cleanup;
+}
+
+/* iter_time was in millis, but count_iters reported for secs */
+iters = iters * iter_time / 1000;
+
+if (iters > UINT32_MAX) {
+error_setg_errno(errp, ERANGE,
+ "PBKDF iterations %llu larger than %u",
+ (unsigned long long)iters, UINT32_MAX);
+goto cleanup;
+}
+
+slot->iterations =
+MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+
+
+/*
+ * Generate a key that we'll use to encrypt the master
+ * key, from the user's password
+ */
+slotkey = g_new0(uint8_t, luks->header.master_key_len);
+if (qcrypto_pbkdf2(luks->hash_alg,
+   (uint8_t *)password, strlen(password),
+   slot->salt,
+   QCRYPTO_BLOCK_LUKS_SALT_LEN,
+   slot->iterations,
+   slotkey, luks->header.master_key_len,
+   errp) < 0) {
+goto cleanup;
+}
+
+
+/*
+ * Setup the encryption objects needed to encrypt the
+ * master key material
+ */
+cipher = qcrypto_cipher_new(luks->cipher_alg,
+luks->cipher_mode,
+slotkey, luks->header.master_key_len,
+errp);
+if (!cipher) {
+goto cleanup;
+}
+
+ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+  luks->ivgen_cipher_alg,
+  luks->ivgen_hash_alg,
+  slotkey, luks->header.master_key_len,
+  errp);
+if (!ivgen) {
+goto cleanup;
+}
+
+/*
+ * Before storing the master key, we need to vastly
+ * increase its size, as protection against forensic
+ * disk data recovery
+ */
+splitkey = g_new0(uint8_t, splitkeylen);
+
+if (qcrypto_afsplit_encode(luks->hash_alg,
+   luks->header.master_key_len,
+   slot->stripes,
+   masterkey,
+   splitkey,
+   errp) < 0) {
+goto cleanup;
+}
+
+

[Qemu-block] [PATCH v2 08/13] qcrypto-luks: extract store and load header

2019-08-26 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 166 +++-
 1 file changed, 102 insertions(+), 64 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index cad65ae0aa..b4dc6fc899 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -409,6 +409,105 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
cipher,
 }
 }
 
+/*
+ * Stores the main LUKS header, taking care of endianess
+ */
+static int
+qcrypto_block_luks_store_header(QCryptoBlock *block,
+QCryptoBlockWriteFunc writefunc,
+void *opaque,
+Error **errp)
+{
+const QCryptoBlockLUKS *luks = block->opaque;
+Error *local_err = NULL;
+size_t i;
+QCryptoBlockLUKSHeader *hdr_copy;
+
+/* Create a copy of the header */
+hdr_copy = g_new0(QCryptoBlockLUKSHeader, 1);
+memcpy(hdr_copy, &luks->header, sizeof(QCryptoBlockLUKSHeader));
+
+/*
+ * Everything on disk uses Big Endian (tm), so flip header fields
+ * before writing them
+ */
+cpu_to_be16s(&hdr_copy->version);
+cpu_to_be32s(&hdr_copy->payload_offset_sector);
+cpu_to_be32s(&hdr_copy->master_key_len);
+cpu_to_be32s(&hdr_copy->master_key_iterations);
+
+for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+cpu_to_be32s(&hdr_copy->key_slots[i].active);
+cpu_to_be32s(&hdr_copy->key_slots[i].iterations);
+cpu_to_be32s(&hdr_copy->key_slots[i].key_offset_sector);
+cpu_to_be32s(&hdr_copy->key_slots[i].stripes);
+}
+
+/* Write out the partition header and key slot headers */
+writefunc(block, 0, (const uint8_t *)hdr_copy, sizeof(*hdr_copy),
+  opaque, &local_err);
+
+g_free(hdr_copy);
+
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+return 0;
+}
+
+/*
+ * Loads the main LUKS header,and byteswaps it to native endianess
+ * And run basic sanity checks on it
+ */
+static int
+qcrypto_block_luks_load_header(QCryptoBlock *block,
+QCryptoBlockReadFunc readfunc,
+void *opaque,
+Error **errp)
+{
+ssize_t rv;
+size_t i;
+int ret = 0;
+QCryptoBlockLUKS *luks = block->opaque;
+
+/*
+ * Read the entire LUKS header, minus the key material from
+ * the underlying device
+ */
+
+rv = readfunc(block, 0,
+  (uint8_t *)&luks->header,
+  sizeof(luks->header),
+  opaque,
+  errp);
+if (rv < 0) {
+ret = rv;
+goto fail;
+}
+
+/*
+ * The header is always stored in big-endian format, so
+ * convert everything to native
+ */
+be16_to_cpus(&luks->header.version);
+be32_to_cpus(&luks->header.payload_offset_sector);
+be32_to_cpus(&luks->header.master_key_len);
+be32_to_cpus(&luks->header.master_key_iterations);
+
+for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+be32_to_cpus(&luks->header.key_slots[i].active);
+be32_to_cpus(&luks->header.key_slots[i].iterations);
+be32_to_cpus(&luks->header.key_slots[i].key_offset_sector);
+be32_to_cpus(&luks->header.key_slots[i].stripes);
+}
+
+
+return 0;
+fail:
+return ret;
+}
+
 /*
  * Given a key slot, and user password, this will attempt to unlock
  * the master encryption key from the key slot.
@@ -623,8 +722,6 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 QCryptoBlockLUKS *luks = NULL;
 Error *local_err = NULL;
 int ret = 0;
-size_t i;
-ssize_t rv;
 g_autofree uint8_t *masterkey = NULL;
 char *ivgen_name, *ivhash_name;
 g_autofree char *password = NULL;
@@ -646,31 +743,11 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 luks = g_new0(QCryptoBlockLUKS, 1);
 block->opaque = luks;
 
-/* Read the entire LUKS header, minus the key material from
- * the underlying device */
-rv = readfunc(block, 0,
-  (uint8_t *)&luks->header,
-  sizeof(luks->header),
-  opaque,
-  errp);
-if (rv < 0) {
-ret = rv;
+ret = qcrypto_block_luks_load_header(block, readfunc, opaque, errp);
+if (ret) {
 goto fail;
 }
 
-/* The header is always stored in big-endian format, so
- * convert everything to native */
-be16_to_cpus(&luks->header.version);
-be32_to_cpus(&luks->header.payload_offset_sector);
-be32_to_cpus(&luks->header.master_key_len);
-be32_to_cpus(&luks->header.master_key_iterations);
-
-for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-be32_to_cpus(&luks->header.key_slo

[Qemu-block] [PATCH v2 11/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations

2019-08-26 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 64 +
 1 file changed, 41 insertions(+), 23 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index d713125925..6a43d97ce5 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -409,6 +409,32 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
cipher,
 }
 }
 
+/*
+ * Returns number of sectors needed to store the key material
+ * given number of anti forensic stripes
+ */
+static int
+qcrypto_block_luks_splitkeylen_sectors(const QCryptoBlockLUKS *luks,
+   unsigned int stripes)
+{
+/*
+ * This calculation doesn't match that shown in the spec,
+ * but instead follows the cryptsetup implementation.
+ */
+
+size_t header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+size_t splitkeylen = luks->header.master_key_len * stripes;
+
+/* First align the key material size to block size*/
+size_t splitkeylen_sectors =
+DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);
+
+/* Then also align the key material size to the size of the header */
+return ROUND_UP(splitkeylen_sectors, header_sectors);
+}
+
 /*
  * Stores the main LUKS header, taking care of endianess
  */
@@ -1151,7 +1177,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 QCryptoBlockCreateOptionsLUKS luks_opts;
 Error *local_err = NULL;
 g_autofree uint8_t *masterkey = NULL;
-size_t splitkeylen = 0;
+size_t header_sectors;
+size_t split_key_sectors;
 size_t i;
 g_autofree char *password;
 const char *cipher_alg;
@@ -1370,37 +1397,28 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 goto error;
 }
 
+/* start with the sector that follows the header*/
+header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+split_key_sectors =
+qcrypto_block_luks_splitkeylen_sectors(luks,
+   QCRYPTO_BLOCK_LUKS_STRIPES);
 
-/* Although LUKS has multiple key slots, we're just going
- * to use the first key slot */
-splitkeylen = luks->header.master_key_len * QCRYPTO_BLOCK_LUKS_STRIPES;
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-luks->header.key_slots[i].active = 
QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
-luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
+QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
+slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
 
-/* This calculation doesn't match that shown in the spec,
- * but instead follows the cryptsetup implementation.
- */
-luks->header.key_slots[i].key_offset_sector =
-(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
-(ROUND_UP(DIV_ROUND_UP(splitkeylen, 
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
-  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
+slot->key_offset_sector = header_sectors + i * split_key_sectors;
+slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
 }
 
-
 /* The total size of the LUKS headers is the partition header + key
  * slot headers, rounded up to the nearest sector, combined with
  * the size of each master key material region, also rounded up
  * to the nearest sector */
-luks->header.payload_offset_sector =
-(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
-(ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
-  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
- QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+luks->header.payload_offset_sector = header_sectors +
+QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors;
 
 block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
 block->payload_offset = luks->header.payload_offset_sector *
-- 
2.17.2




[Qemu-block] [PATCH v2 09/13] qcrypto-block: extract check and parse header

2019-08-26 Thread Maxim Levitsky
This is just to make qcrypto_block_luks_open more
reasonable in size.

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 254 +---
 1 file changed, 146 insertions(+), 108 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index b4dc6fc899..cc9a52c9af 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -508,6 +508,148 @@ fail:
 return ret;
 }
 
+/*
+ * Does basic sanity checks on the LUKS header
+ */
+static int
+qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
+{
+int ret;
+
+if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
+   QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
+error_setg(errp, "Volume is not in LUKS format");
+ret = -EINVAL;
+goto fail;
+}
+
+if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
+error_setg(errp, "LUKS version %" PRIu32 " is not supported",
+   luks->header.version);
+ret = -ENOTSUP;
+goto fail;
+}
+
+return 0;
+fail:
+return ret;
+}
+
+/*
+ * Parses the crypto parameters that are stored in the LUKS header
+ */
+
+static int
+qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp)
+{
+g_autofree char *cipher_mode = g_strdup(luks->header.cipher_mode);
+char *ivgen_name, *ivhash_name;
+int ret = -1;
+Error *local_err = NULL;
+
+/*
+ * The cipher_mode header contains a string that we have
+ * to further parse, of the format
+ *
+ *-[:]
+ *
+ * eg  cbc-essiv:sha256, cbc-plain64
+ */
+ivgen_name = strchr(cipher_mode, '-');
+if (!ivgen_name) {
+ret = -EINVAL;
+error_setg(errp, "Unexpected cipher mode string format %s",
+   luks->header.cipher_mode);
+goto out;
+}
+*ivgen_name = '\0';
+ivgen_name++;
+
+ivhash_name = strchr(ivgen_name, ':');
+if (!ivhash_name) {
+luks->ivgen_hash_alg = 0;
+} else {
+*ivhash_name = '\0';
+ivhash_name++;
+
+luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
+   &local_err);
+if (local_err) {
+ret = -ENOTSUP;
+error_propagate(errp, local_err);
+goto out;
+}
+}
+
+luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
+  &local_err);
+if (local_err) {
+ret = -ENOTSUP;
+error_propagate(errp, local_err);
+goto out;
+}
+
+luks->cipher_alg =
+qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+  luks->cipher_mode,
+  luks->header.master_key_len,
+  &local_err);
+if (local_err) {
+ret = -ENOTSUP;
+error_propagate(errp, local_err);
+goto out;
+}
+
+luks->hash_alg =
+qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
+   &local_err);
+if (local_err) {
+ret = -ENOTSUP;
+error_propagate(errp, local_err);
+goto out;
+}
+
+luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
+   &local_err);
+if (local_err) {
+ret = -ENOTSUP;
+error_propagate(errp, local_err);
+goto out;
+}
+
+if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
+if (!ivhash_name) {
+ret = -EINVAL;
+error_setg(errp, "Missing IV generator hash specification");
+goto out;
+}
+luks->ivgen_cipher_alg =
+qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
+luks->ivgen_hash_alg,
+&local_err);
+if (local_err) {
+ret = -ENOTSUP;
+error_propagate(errp, local_err);
+goto out;
+}
+} else {
+
+/*
+ * Note we parsed the ivhash_name earlier in the cipher_mode
+ * spec string even with plain/plain64 ivgens, but we
+ * will ignore it, since it is irrelevant for these ivgens.
+ * This is for compat with dm-crypt which will silently
+ * ignore hash names with these ivgens rather than report
+ * an error about the invalid usage
+ */
+luks->ivgen_cipher_alg = luks->cipher_alg;
+}
+ret = 0;
+out:
+return ret;
+
+}
+
 /*
  * Given a key slot, and user password, this will attempt to unlock
  * the master

[Qemu-block] [PATCH v2 13/13] qcrypto-luks: implement more rigorous header checking

2019-08-26 Thread Maxim Levitsky
Check that keyslots don't overlap with the data,
and check that keyslots don't overlap with each other.
(this is done using naive O(n^2) nested loops,
but since there are just 8 keyslots, this doesn't really matter.

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 46 +++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index db0fb764b4..fdf4c41f8a 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -541,12 +541,12 @@ fail:
 static int
 qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
 {
-int ret;
+int ret = -EINVAL;
+size_t i, j;
 
 if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
 error_setg(errp, "Volume is not in LUKS format");
-ret = -EINVAL;
 goto fail;
 }
 
@@ -557,6 +557,48 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS 
*luks, Error **errp)
 goto fail;
 }
 
+/* Check all keyslots for corruption  */
+for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
+
+const QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
+unsigned int start1 = slot1->key_offset_sector;
+unsigned int len1 =
+qcrypto_block_luks_splitkeylen_sectors(luks, slot1->stripes);
+
+if (slot1->stripes == 0) {
+error_setg(errp, "Keyslot %zu is corrupted (stripes == 0)", i);
+goto fail;
+}
+
+if (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
+slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
+error_setg(errp,
+   "Keyslot %zu state (active/disable) is corrupted", i);
+goto fail;
+}
+
+if (start1 + len1 > luks->header.payload_offset_sector) {
+error_setg(errp,
+   "Keyslot %zu is overlapping with the encrypted payload",
+   i);
+goto fail;
+}
+
+for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
+const QCryptoBlockLUKSKeySlot *slot2 = &luks->header.key_slots[j];
+unsigned int start2 = slot2->key_offset_sector;
+unsigned int len2 =
+qcrypto_block_luks_splitkeylen_sectors(luks, slot2->stripes);
+
+if (start1 + len1 > start2 && start2 + len2 > start1) {
+error_setg(errp,
+   "Keyslots %zu and %zu are overlapping in the 
header",
+   i, j);
+goto fail;
+}
+}
+
+}
 return 0;
 fail:
 return ret;
-- 
2.17.2




[Qemu-block] [PATCH v2 05/13] qcrypto-luks: simplify masterkey and masterkey length

2019-08-26 Thread Maxim Levitsky
Let the caller allocate masterkey
Always use master key len from the header

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 44 +---
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index e9ae3f6baa..331377293d 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -419,7 +419,6 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 QCryptoCipherAlgorithm ivcipheralg,
 QCryptoHashAlgorithm ivhash,
 uint8_t *masterkey,
-size_t masterkeylen,
 QCryptoBlockReadFunc readfunc,
 void *opaque,
 Error **errp)
@@ -438,9 +437,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 return 0;
 }
 
-splitkeylen = masterkeylen * slot->stripes;
+splitkeylen = luks->header.master_key_len * slot->stripes;
 splitkey = g_new0(uint8_t, splitkeylen);
-possiblekey = g_new0(uint8_t, masterkeylen);
+possiblekey = g_new0(uint8_t, luks->header.master_key_len);
 
 /*
  * The user password is used to generate a (possible)
@@ -453,7 +452,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
(const uint8_t *)password, strlen(password),
slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
slot->iterations,
-   possiblekey, masterkeylen,
+   possiblekey, luks->header.master_key_len,
errp) < 0) {
 return -1;
 }
@@ -478,7 +477,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 /* Setup the cipher/ivgen that we'll use to try to decrypt
  * the split master key material */
 cipher = qcrypto_cipher_new(cipheralg, ciphermode,
-possiblekey, masterkeylen,
+possiblekey, luks->header.master_key_len,
 errp);
 if (!cipher) {
 return -1;
@@ -489,7 +488,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 ivgen = qcrypto_ivgen_new(ivalg,
   ivcipheralg,
   ivhash,
-  possiblekey, masterkeylen,
+  possiblekey, luks->header.master_key_len,
   errp);
 if (!ivgen) {
 return -1;
@@ -519,7 +518,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * it back together to get the actual master key.
  */
 if (qcrypto_afsplit_decode(hash,
-   masterkeylen,
+   luks->header.master_key_len,
slot->stripes,
splitkey,
masterkey,
@@ -537,11 +536,13 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * header
  */
 if (qcrypto_pbkdf2(hash,
-   masterkey, masterkeylen,
+   masterkey,
+   luks->header.master_key_len,
luks->header.master_key_salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.master_key_iterations,
-   keydigest, G_N_ELEMENTS(keydigest),
+   keydigest,
+   G_N_ELEMENTS(keydigest),
errp) < 0) {
 return -1;
 }
@@ -574,8 +575,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 QCryptoIVGenAlgorithm ivalg,
 QCryptoCipherAlgorithm ivcipheralg,
 QCryptoHashAlgorithm ivhash,
-uint8_t **masterkey,
-size_t *masterkeylen,
+uint8_t *masterkey,
 QCryptoBlockReadFunc readfunc,
 void *opaque,
 Error **errp)
@@ -584,9 +584,6 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 size_t i;
 int rv;
 
-*masterkey = g_new0(uint8_t, luks->header.master_key_len);
-*masterkeylen = luks->header.master_key_len;
-
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 rv = qcrypto_block_luks_load_key(block,
  &luks->header.key_slots[i],
@@ -597,8 +594,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
  ivalg,
  ivcipheralg,
  ivhash,
- *masterkey,
- *masterkeylen,
+ 

[Qemu-block] [PATCH v2 06/13] qcrypto-block: pass keyslot index rather that pointer to the keyslot

2019-08-26 Thread Maxim Levitsky
Another minor refactoring

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 331377293d..0d81f2ac61 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -410,7 +410,7 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
cipher,
  */
 static int
 qcrypto_block_luks_load_key(QCryptoBlock *block,
-QCryptoBlockLUKSKeySlot *slot,
+unsigned int slot_idx,
 const char *password,
 QCryptoCipherAlgorithm cipheralg,
 QCryptoCipherMode ciphermode,
@@ -424,6 +424,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 Error **errp)
 {
 QCryptoBlockLUKS *luks = block->opaque;
+const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
 g_autofree uint8_t *splitkey = NULL;
 size_t splitkeylen;
 g_autofree uint8_t *possiblekey = NULL;
@@ -580,13 +581,12 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 void *opaque,
 Error **errp)
 {
-QCryptoBlockLUKS *luks = block->opaque;
 size_t i;
 int rv;
 
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 rv = qcrypto_block_luks_load_key(block,
- &luks->header.key_slots[i],
+ i,
  password,
  cipheralg,
  ciphermode,
-- 
2.17.2




[Qemu-block] [PATCH v2 12/13] qcrypto-luks: use g_autowipe

2019-08-26 Thread Maxim Levitsky
This patch makes the luks crypto driver use the g_autowipe to erase the master 
keys,
and the passwords from the memory.

Note that this is not a complete solution, since these keys are also present in 
the
chipers, and in the secrets.

Some of them still can be erased, at least at driver instance close.

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 61 -
 1 file changed, 22 insertions(+), 39 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 6a43d97ce5..db0fb764b4 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -32,6 +32,7 @@
 #include "qemu/uuid.h"
 
 #include "qemu/coroutine.h"
+#include "autowipe.h"
 
 /*
  * Reference for the LUKS format implemented here is
@@ -698,19 +699,18 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
 {
 QCryptoBlockLUKS *luks = block->opaque;
 QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
-g_autofree uint8_t *splitkey = NULL;
+g_autowipe uint8_t *splitkey = NULL;
 size_t splitkeylen;
-g_autofree uint8_t *slotkey = NULL;
+g_autowipe uint8_t *slotkey = NULL;
 g_autoptr(QCryptoCipher) cipher = NULL;
 g_autoptr(QCryptoIVGen) ivgen = NULL;
 Error *local_err = NULL;
 uint64_t iters;
-int ret = -1;
 
 if (qcrypto_random_bytes(slot->salt,
  QCRYPTO_BLOCK_LUKS_SALT_LEN,
  errp) < 0) {
-goto cleanup;
+return -1;
 }
 
 splitkeylen = luks->header.master_key_len * slot->stripes;
@@ -728,14 +728,14 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
&local_err);
 if (local_err) {
 error_propagate(errp, local_err);
-goto cleanup;
+return -1;
 }
 
 if (iters > (ULLONG_MAX / iter_time)) {
 error_setg_errno(errp, ERANGE,
  "PBKDF iterations %llu too large to scale",
  (unsigned long long)iters);
-goto cleanup;
+return -1;
 }
 
 /* iter_time was in millis, but count_iters reported for secs */
@@ -745,7 +745,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
 error_setg_errno(errp, ERANGE,
  "PBKDF iterations %llu larger than %u",
  (unsigned long long)iters, UINT32_MAX);
-goto cleanup;
+return -1;
 }
 
 slot->iterations =
@@ -764,7 +764,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
slot->iterations,
slotkey, luks->header.master_key_len,
errp) < 0) {
-goto cleanup;
+return -1;
 }
 
 
@@ -777,7 +777,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
 slotkey, luks->header.master_key_len,
 errp);
 if (!cipher) {
-goto cleanup;
+return -1;
 }
 
 ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
@@ -786,7 +786,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
   slotkey, luks->header.master_key_len,
   errp);
 if (!ivgen) {
-goto cleanup;
+return -1;
 }
 
 /*
@@ -802,7 +802,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
masterkey,
splitkey,
errp) < 0) {
-goto cleanup;
+return -1;
 }
 
 /*
@@ -815,7 +815,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
 splitkey,
 splitkeylen,
 errp) < 0) {
-goto cleanup;
+return -1;
 }
 
 /* Write out the slot's master key material. */
@@ -825,25 +825,16 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
   splitkey, splitkeylen,
   opaque,
   errp) != splitkeylen) {
-goto cleanup;
+return -1;
 }
 
 slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
 
 if (qcrypto_block_luks_store_header(block,  writefunc, opaque, errp)) {
-goto cleanup;
+return -1;
 }
 
-ret = 0;
-
-cleanup:
-if (slotkey) {
-memset(slotkey, 0, luks->header.master_key_len);
-}
-if (splitkey) {
-memset(splitkey, 0, splitkeylen);
-}
-return ret;
+return 0;
 }
 
 /*
@@ -868,9 +859,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 {
 QCryptoBlockLUKS *luks = block->opaque;
 const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
-g_autofree uint8_t *splitkey = NULL;
+g_autowipe uint8_t *splitkey = NULL;
 size_t splitkeylen;
-g_autofree uint8_t

Re: [Qemu-block] [Qemu-devel] [PATCH v2 01/13] introduce g_autowipe

2019-08-27 Thread Maxim Levitsky
On Tue, 2019-08-27 at 11:52 +0100, Daniel P. Berrangé wrote:
> On Mon, Aug 26, 2019 at 04:50:51PM +0300, Maxim Levitsky wrote:
> > Marking a pointer with g_autowipe, will
> > not only free it at the scope exit, but also
> > erase the data it points to just prior to freeing it.
> > 
> > This is first attempt to implement this feature,
> > as suggested by Daniel and Nir.
> > 
> > The things that need to be verified prior to merging this is
> > 
> > 1. Can we just always use memset_s (defined in C++)
> >  or some alternative.
> > 
> > 2. is it portable enought for us to use malloc_usable_size
> > to get the size of malloced pointer in the autofree callback?
> > This function is aviable in glibc (but no wrapper in glib).
> 
> Urgh, no, we can't rely on that.
> 
> I completely forgot that we would need to know the size during
> the deallocate function.  The portable way to deal with this
> will be to change all our code that handles passwords to use
> GString instead, since that is a struct carrying around the
> allocated size.
> 
> As mentioned in v1, I'm fine if you just let this series use
> memset as this is a pre-existing problem & we can fix it
> in separate yseries.

All right, I *was* afraid of that, but I guess it was worth a try anyway.
So I think I'll keep that patch that adds few missing memsets, 
just to consistency/documentation purposes since anyway
in addtion to these there are lot of other places that keys are kept,
like the ciphers itself, secrets (which aren't even freed  usually
as long as VM is running)

The purpose was that I just that memsetting caught my eye and
I wanted to make it at least consistent.

Thanks for the review,
Best regards,
Maxim Levitsky





Re: [Qemu-block] [Qemu-devel] [PATCH 1/2] block/nvme: add support for write zeros

2019-08-28 Thread Maxim Levitsky
On Tue, 2019-08-27 at 18:22 -0400, John Snow wrote:
> Without a commit message, I have no real hope of reviewing this. I was
> CC'd, though, so I'll give it a blind shot.
> 
> We want to add write_zeroes support for block/nvme, but I can't really
> verify any of that is correct or working without a unit test, a spec, or
> some instructions to help me verify any of this does what it looks like
> it does.

Its a bit problematic to have unit tests for this specific driver,
because it is the first and the only driver which is a driver in the
real sense of the word, that is it works directly with host hardware,
replacing the in-kernel driver.

I guess some unit tests could be done using a nested VM or so.


Yea, I was initially confused as well as what write zeros does, but it
does _exactly_ what it says, just writes zeros to the block you ask
for. So basically just like a write command, but without data.

Plus optionally if the drive  declares that after discard,
the blocks read as zeros, you can enable 'DEALOC' bit in write zeros

command to specify that in addition to filling the block with zeros
it would be nice to discard its flash data as well (this is still a hint
though).


> 
> On 8/25/19 3:15 AM, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/nvme.c | 72 +++-
> >  block/trace-events   |  1 +
> >  include/block/nvme.h | 19 +++-
> >  3 files changed, 90 insertions(+), 2 deletions(-)
> > 
> > diff --git a/block/nvme.c b/block/nvme.c
> > index 5be3a39b63..f8bd11e19a 100644
> > --- a/block/nvme.c
> > +++ b/block/nvme.c
> > @@ -111,6 +111,8 @@ typedef struct {
> >  uint64_t max_transfer;
> >  bool plugged;
> >  
> > +bool supports_write_zeros;
> > +
> 
> I suppose the spelling of "zeroes" is not as established as I thought it
> was. Actually, what's worse is that the NVME writers apparently couldn't
> decide what to name it themselves either:
> 
> "Write Zeroes" has 23 hits.
> "Write Zeros" has just two, in Figure 114 for the Identify NS Data.
> 
> Oh, in QEMU we're not much better:
> 
> jhuston@probe (review) ~/s/qemu> git grep -i zeros | wc -l
> 265
> jhuston@probe (review) ~/s/qemu> git grep -i zeroes | wc -l
> 747
> 
> I'm going to suggest that we use 'zeroes' as the spelling here, to match
> the existing 'pwrite_zeroes', and then otherwise to match the NVME
> spec's usual spelling.
No problem I didn't notice the two spelling to be honest.


> 
> >  CoMutex dma_map_lock;
> >  CoQueue dma_flush_queue;
> >  
> > @@ -421,6 +423,7 @@ static void nvme_identify(BlockDriverState *bs, int 
> > namespace, Error **errp)
> >  NvmeIdNs *idns;
> >  NvmeLBAF *lbaf;
> >  uint8_t *resp;
> > +uint16_t oncs;
> >  int r;
> >  uint64_t iova;
> >  NvmeCmd cmd = {
> > @@ -458,6 +461,9 @@ static void nvme_identify(BlockDriverState *bs, int 
> > namespace, Error **errp)
> >  s->max_transfer = MIN_NON_ZERO(s->max_transfer,
> >s->page_size / sizeof(uint64_t) * s->page_size);
> >  
> > +oncs = le16_to_cpu(idctrl->oncs);
> > +s->supports_write_zeros = (oncs & NVME_ONCS_WRITE_ZEROS) != 0;
> > +
> 
> For other reviewers: oncs is "Optional NVM Command Support".
> 
> I think it's better to say `!!(oncs & NVME_ONCS_WRITE_ZEROES)` to remove
> doubt over the width of the bitmask.
No problem.


> 
> >  memset(resp, 0, 4096);
> >  
> >  cmd.cdw10 = 0;
> > @@ -470,6 +476,12 @@ static void nvme_identify(BlockDriverState *bs, int 
> > namespace, Error **errp)
> >  s->nsze = le64_to_cpu(idns->nsze);
> >  lbaf = &idns->lbaf[NVME_ID_NS_FLBAS_INDEX(idns->flbas)];
> >  
> > +if (NVME_ID_NS_DLFEAT_WRITE_ZEROS(idns->dlfeat) &&
> > +NVME_ID_NS_DLFEAT_READ_BEHAVIOR(idns->dlfeat) ==
> > +NVME_ID_NS_DLFEAT_READ_BEHAVIOR_ZEROS) {
> > +bs->supported_write_flags |= BDRV_REQ_MAY_UNMAP;
> > +}
> > +
> >  if (lbaf->ms) {
> >  error_setg(errp, "Namespaces with metadata are not yet supported");
> >  goto out;
> > @@ -764,6 +776,8 @@ static int nvme_file_open(BlockDriverState *bs, QDict 
> > *options, int flags,
> >  int ret;
> >  BDRVNVMeState *s = bs->opaque;
> >  
> > +bs->supported_write_flags = BDRV_REQ_FUA;
> > +
> 
>

Re: [Qemu-block] [Qemu-devel] [PATCH 2/2] block/nvme: add support for discard

2019-08-28 Thread Maxim Levitsky
On Tue, 2019-08-27 at 18:29 -0400, John Snow wrote:
> 
> On 8/25/19 3:15 AM, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/nvme.c   | 83 ++
> >  block/trace-events |  2 ++
> >  2 files changed, 85 insertions(+)
> > 
> > diff --git a/block/nvme.c b/block/nvme.c
> > index f8bd11e19a..dd041f39c9 100644
> > --- a/block/nvme.c
> > +++ b/block/nvme.c
> > @@ -112,6 +112,7 @@ typedef struct {
> >  bool plugged;
> >  
> >  bool supports_write_zeros;
> > +bool supports_discard;
> >  
> >  CoMutex dma_map_lock;
> >  CoQueue dma_flush_queue;
> > @@ -463,6 +464,7 @@ static void nvme_identify(BlockDriverState *bs, int 
> > namespace, Error **errp)
> >  
> >  oncs = le16_to_cpu(idctrl->oncs);
> >  s->supports_write_zeros = (oncs & NVME_ONCS_WRITE_ZEROS) != 0;
> > +s->supports_discard = (oncs & NVME_ONCS_DSM) != 0;
> 
> Same comment -- checking !!(register & FIELD) is nicer than the
> negative. (I'm actually not sure even the !! is needed, but it seems to
> be a QEMU-ism and I've caught myself using it...)

All right, no problem to use !!

> 
> Rest looks good to me on a skim, but I'm not very well-versed in NVME.
Thanks!


> 
> >  
> >  memset(resp, 0, 4096);
> >  
> > @@ -1153,6 +1155,86 @@ static coroutine_fn int 
> > nvme_co_pwrite_zeroes(BlockDriverState *bs,
> >  }
> >  
> >  
> > +static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs,
> > + int64_t offset,
> > + int bytes)
> > +{
> > +BDRVNVMeState *s = bs->opaque;
> > +NVMeQueuePair *ioq = s->queues[1];
> > +NVMeRequest *req;
> > +NvmeDsmRange *buf;
> > +QEMUIOVector local_qiov;
> > +int ret;
> > +
> > +NvmeCmd cmd = {
> > +.opcode = NVME_CMD_DSM,
> > +.nsid = cpu_to_le32(s->nsid),
> > +.cdw10 = cpu_to_le32(0), /*number of ranges - 0 based*/
> > +.cdw11 = cpu_to_le32(1 << 2), /*deallocate bit*/
> > +};
> > +
> > +NVMeCoData data = {
> > +.ctx = bdrv_get_aio_context(bs),
> > +.ret = -EINPROGRESS,
> > +};
> > +
> > +if (!s->supports_discard) {
> > +return -ENOTSUP;
> > +}
> > +
> > +assert(s->nr_queues > 1);
> > +
> > +buf = qemu_try_blockalign0(bs, s->page_size);
> > +if (!buf) {
> > +return -ENOMEM;
> > +}
> > +
> > +buf->nlb = cpu_to_le32(bytes >> s->blkshift);
> > +buf->slba = cpu_to_le64(offset >> s->blkshift);
> > +buf->cattr = 0;
> > +
> > +qemu_iovec_init(&local_qiov, 1);
> > +qemu_iovec_add(&local_qiov, buf, 4096);
> > +
> > +req = nvme_get_free_req(ioq);
> > +assert(req);
> > +
> > +qemu_co_mutex_lock(&s->dma_map_lock);
> > +ret = nvme_cmd_map_qiov(bs, &cmd, req, &local_qiov);
> > +qemu_co_mutex_unlock(&s->dma_map_lock);
> > +
> > +if (ret) {
> > +req->busy = false;
> > +goto out;
> > +}
> > +
> > +trace_nvme_dsm(s, offset, bytes);
> > +
> > +nvme_submit_command(s, ioq, req, &cmd, nvme_rw_cb, &data);
> > +
> > +data.co = qemu_coroutine_self();
> > +while (data.ret == -EINPROGRESS) {
> > +qemu_coroutine_yield();
> > +}
> > +
> > +qemu_co_mutex_lock(&s->dma_map_lock);
> > +ret = nvme_cmd_unmap_qiov(bs, &local_qiov);
> > +qemu_co_mutex_unlock(&s->dma_map_lock);
> > +
> > +if (ret) {
> > +goto out;
> > +}
> > +
> > +ret = data.ret;
> > +trace_nvme_dsm_done(s, offset, bytes, ret);
> > +out:
> > +qemu_iovec_destroy(&local_qiov);
> > +qemu_vfree(buf);
> > +return ret;
> > +
> > +}
> > +
> > +
> >  static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
> > BlockReopenQueue *queue, Error **errp)
> >  {
> > @@ -1259,6 +1341,7 @@ static BlockDriver bdrv_nvme = {
> >  .bdrv_co_pwritev  = nvme_co_pwritev,
> >  
> >  .bdrv_co_pwrite_zeroes= nvme_co_pwrite_zeroes,
> > +.bdrv_co_pdiscard = nvme_co_pdiscard,
> >  
> >  .bdrv_co_flush_to_

[Qemu-block] [PATCH 00/10] RFC crypto/luks: encryption key managment using amend interface

2019-08-30 Thread Maxim Levitsky
This patch series is continuation of my work to add encryption
key managment to luks/qcow2 with luks.

This patch series is based on patch series I sent earlier
called 'RFC crypto/luks: preparation for encryption key managment'

Let me hear what you think. This is still an RFC, so please
don't kill if I did something obviously wrong.

I did run the iotests - all luks and qcow2 tests, including
3 that I added.

Only test 162 seems pretty much always to fail,regardless of my changes
I suspect something nbd related / or an enviroment issue

Best regards,
    Maxim Levitsky

Maxim Levitsky (10):
  qcrypto: add suport for amend options
  qcrypto-luks: extend the create options for upcoming encryption key
management
  qcrypto-luks: implement the encryption key management
  block: amend: add 'force' option
  block/crypto: implement the encryption key management
  qcow2: implement crypto amend options
  block: add x-blockdev-amend qmp command
  block/crypto: implement blockdev-amend
  block/qcow2: implement blockdev-amend
  iotests : add tests for encryption key management

 block.c  |   4 +-
 block/Makefile.objs  |   2 +-
 block/amend.c| 116 +
 block/crypto.c   | 154 +++-
 block/crypto.h   |  16 ++
 block/qcow2.c| 153 ++--
 crypto/block-luks.c  | 392 ++-
 crypto/block.c   |  31 +++
 crypto/blockpriv.h   |   8 +
 include/block/block.h|   1 +
 include/block/block_int.h|  22 +-
 include/crypto/block.h   |  22 ++
 qapi/block-core.json |  34 ++-
 qapi/crypto.json |  19 ++
 qapi/job.json|   4 +-
 qemu-img-cmds.hx |   4 +-
 qemu-img.c   |   8 +-
 qemu-img.texi|   6 +-
 tests/qemu-iotests/082.out   |  54 +
 tests/qemu-iotests/087.out   |   6 +-
 tests/qemu-iotests/134.out   |   2 +-
 tests/qemu-iotests/158.out   |   4 +-
 tests/qemu-iotests/188.out   |   2 +-
 tests/qemu-iotests/189.out   |   4 +-
 tests/qemu-iotests/198.out   |   4 +-
 tests/qemu-iotests/300   | 202 
 tests/qemu-iotests/300.out   |  98 
 tests/qemu-iotests/301   |  90 +++
 tests/qemu-iotests/301.out   |  30 +++
 tests/qemu-iotests/302   | 247 +++
 tests/qemu-iotests/302.out   |  18 ++
 tests/qemu-iotests/common.filter |   6 +-
 tests/qemu-iotests/group |   8 +
 33 files changed, 1717 insertions(+), 54 deletions(-)
 create mode 100644 block/amend.c
 create mode 100755 tests/qemu-iotests/300
 create mode 100644 tests/qemu-iotests/300.out
 create mode 100755 tests/qemu-iotests/301
 create mode 100644 tests/qemu-iotests/301.out
 create mode 100644 tests/qemu-iotests/302
 create mode 100644 tests/qemu-iotests/302.out

-- 
2.17.2




[Qemu-block] [PATCH 05/10] block/crypto: implement the encryption key management

2019-08-30 Thread Maxim Levitsky
This implements the encryption key management
using the generic code in qcrypto layer
(currently only for qemu-img amend)

This code adds another 'write_func' because the initialization
write_func works directly on the underlying file,
because during the creation, there is no open instance
of the luks driver, but during regular use, we have it,
and should use it instead.

Signed-off-by: Maxim Levitsky 
---
 block/crypto.c | 106 +++--
 1 file changed, 103 insertions(+), 3 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index a6a3e1f1d8..dbd95a99ba 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -36,6 +36,7 @@ typedef struct BlockCrypto BlockCrypto;
 
 struct BlockCrypto {
 QCryptoBlock *block;
+bool updating_keys;
 };
 
 
@@ -70,6 +71,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
 return ret;
 }
 
+static ssize_t block_crypto_write_func(QCryptoBlock *block,
+  size_t offset,
+  const uint8_t *buf,
+  size_t buflen,
+  void *opaque,
+  Error **errp)
+{
+BlockDriverState *bs = opaque;
+ssize_t ret;
+
+ret = bdrv_pwrite(bs->file, offset, buf, buflen);
+if (ret < 0) {
+error_setg_errno(errp, -ret, "Could not write encryption header");
+return ret;
+}
+return ret;
+}
+
 
 struct BlockCryptoCreateData {
 BlockBackend *blk;
@@ -647,6 +666,88 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, 
Error **errp)
 return spec_info;
 }
 
+
+static int
+block_crypto_amend_options(BlockDriverState *bs,
+   QemuOpts *opts,
+   BlockDriverAmendStatusCB *status_cb,
+   void *cb_opaque,
+   bool force,
+   Error **errp)
+{
+BlockCrypto *crypto = bs->opaque;
+QDict *cryptoopts = NULL;
+QCryptoBlockCreateOptions *amend_options = NULL;
+int ret;
+
+assert(crypto);
+assert(crypto->block);
+
+crypto->updating_keys = true;
+
+ret = bdrv_child_refresh_perms(bs, bs->file, errp);
+if (ret) {
+goto cleanup;
+}
+
+cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
+ &block_crypto_create_opts_luks,
+ true);
+
+qdict_put_str(cryptoopts, "format", "luks");
+amend_options = block_crypto_create_opts_init(cryptoopts, errp);
+if (!amend_options) {
+ret = -EINVAL;
+goto cleanup;
+}
+
+ret = qcrypto_block_amend_options(crypto->block,
+  block_crypto_read_func,
+  block_crypto_write_func,
+  bs,
+  amend_options,
+  force,
+  errp);
+cleanup:
+crypto->updating_keys = false;
+bdrv_child_refresh_perms(bs, bs->file, errp);
+qapi_free_QCryptoBlockCreateOptions(amend_options);
+qobject_unref(cryptoopts);
+return ret;
+}
+
+
+static void
+block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
+ const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+
+BlockCrypto *crypto = bs->opaque;
+
+/*
+ * This driver doesn't modify LUKS metadata except
+ * when updating the encryption slots.
+ * Allow share-rw=on as a special case.
+ *
+ * Encryption update will set the crypto->updating_keys
+ * during that period and refresh permissions
+ *
+ * */
+
+if (crypto->updating_keys) {
+/*need exclusive write access for header update  */
+perm |= BLK_PERM_WRITE;
+shared &= ~BLK_PERM_WRITE;
+}
+
+bdrv_filter_default_perms(bs, c, role, reopen_queue,
+perm, shared, nperm, nshared);
+}
+
+
 static const char *const block_crypto_strong_runtime_opts[] = {
 BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
 
@@ -659,9 +760,7 @@ static BlockDriver bdrv_crypto_luks = {
 .bdrv_probe = block_crypto_probe_luks,
 .bdrv_open  = block_crypto_open_luks,
 .bdrv_close = block_crypto_close,
-/* This driver doesn't modify LUKS metadata except when creating image.
- * Allow share-rw=on as a special case. */
-.bdrv_child_perm= bdrv_filter_default_perms,
+.bdrv_child_perm= block_crypto_child_perms,
 .bdrv_co_create = block_crypto_co_create_luks,
 .bdrv_co_create_opts = block_crypto_co_create_opts_luks,
 .bdrv_co_

[Qemu-block] [PATCH 03/10] qcrypto-luks: implement the encryption key management

2019-08-30 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 366 +++-
 1 file changed, 364 insertions(+), 2 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index ba20d55246..21325fbc79 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -70,6 +70,9 @@ typedef struct QCryptoBlockLUKSKeySlot 
QCryptoBlockLUKSKeySlot;
 
 #define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
 
+#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
+#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
+
 static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
 'L', 'U', 'K', 'S', 0xBA, 0xBE
 };
@@ -219,6 +222,9 @@ struct QCryptoBlockLUKS {
 
 /* Hash algorithm used in pbkdf2 function */
 QCryptoHashAlgorithm hash_alg;
+
+/* Name of the secret that was used to open the image */
+char *secret;
 };
 
 
@@ -1089,6 +1095,175 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 }
 
 
+
+/*
+ * 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 = 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
+ * (contains 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 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 = &luks->header.key_slots[slot_idx];
+g_autofree uint8_t *garbagesplitkey = NULL;
+size_t splitkeylen = luks->header.master_key_len * slot->stripes;
+size_t i;
+
+assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+assert(splitkeylen > 0);
+
+garbagesplitkey = g_malloc0(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;
+
+qcrypto_block_luks_store_header(block,  writefunc, opaque, errp);
+
+/*
+ * 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, errp) < 0) {
+/*
+ * If we failed to get the random data, still write
+ * at least zeros to the key slot at least once
+ */
+
+if (i > 0) {
+return -1;
+}
+}
+
+if (writefunc(block,
+  slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+  garbagesplitkey,
+  splitkeylen,
+  opaque,
+  errp) != splitkeylen) {
+return -1;
+}
+}
+return 0;
+}
+
+
+/*
+ * Erase all the keys that match the given password
+ * Will stop when only one keyslot is remaining
+ * Returns number of slots that were erased or -1 on failure
+ */
+
+static int
+qcrypto_block_luks_erase_matching_keys(QCryptoBlock *block,
+   const char *password,
+   QCryptoBlockReadFunc readfunc,
+   QCryptoBlockWriteFunc writefunc,
+   void *opaque,
+   bool force,
+   Error **errp)
+{
+QCryptoBlockLUKS *luks = block->opaque;
+size_t i;
+int rv;
+g_autofree uint8_t *masterkey = NULL;
+unsigned int erased_cnt = 0;
+unsigned int ac

[Qemu-block] [PATCH 01/10] qcrypto: add suport for amend options

2019-08-30 Thread Maxim Levitsky
This adds the qcrypto_amend_options and corresponding
crypto driver callbacks for the  for encrypted
key managedment

Signed-off-by: Maxim Levitsky 
---
 crypto/block.c | 31 +++
 crypto/blockpriv.h |  8 
 include/crypto/block.h | 22 ++
 3 files changed, 61 insertions(+)

diff --git a/crypto/block.c b/crypto/block.c
index 325752871c..14b684de7f 100644
--- a/crypto/block.c
+++ b/crypto/block.c
@@ -115,6 +115,37 @@ QCryptoBlock 
*qcrypto_block_create(QCryptoBlockCreateOptions *options,
 }
 
 
+int qcrypto_block_amend_options(QCryptoBlock *block,
+QCryptoBlockReadFunc readfunc,
+QCryptoBlockWriteFunc writefunc,
+void *opaque,
+QCryptoBlockCreateOptions *options,
+bool force,
+Error **errp)
+{
+if (options->format != block->format) {
+error_setg(errp,
+   "Its not possible to change encryption format with amend 
interface");
+return -1;
+}
+
+if (!block->driver->amend) {
+error_setg(errp,
+   "Crypto format %s doesn't support format options amendment",
+   QCryptoBlockFormat_str(block->format));
+return -1;
+}
+
+return block->driver->amend(block,
+readfunc,
+writefunc,
+opaque,
+options,
+force,
+errp);
+}
+
+
 QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block,
  Error **errp)
 {
diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
index 71c59cb542..c18a4e0b43 100644
--- a/crypto/blockpriv.h
+++ b/crypto/blockpriv.h
@@ -62,6 +62,14 @@ struct QCryptoBlockDriver {
   void *opaque,
   Error **errp);
 
+int (*amend)(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockCreateOptions *options,
+ bool force,
+ Error **errp);
+
 int (*get_info)(QCryptoBlock *block,
 QCryptoBlockInfo *info,
 Error **errp);
diff --git a/include/crypto/block.h b/include/crypto/block.h
index d49d2c2da9..777fd51ebe 100644
--- a/include/crypto/block.h
+++ b/include/crypto/block.h
@@ -144,6 +144,28 @@ QCryptoBlock 
*qcrypto_block_create(QCryptoBlockCreateOptions *options,
void *opaque,
Error **errp);
 
+/**
+ * qcrypto_block_amend_options:
+ * @block: the block encryption object
+ *
+ * @readfunc: callback for reading data from the volume header
+ * @writefunc: callback for writing data to the volume header
+ * @opaque: data to pass to @readfunc and @writefunc
+ * @options: the new/amended encryption options
+ * @force: hint for the driver to allow unsafe operation
+ * @errp: error pointer
+ *
+ * Changes the crypto options of the encryption format
+ *
+ */
+int qcrypto_block_amend_options(QCryptoBlock *block,
+QCryptoBlockReadFunc readfunc,
+QCryptoBlockWriteFunc writefunc,
+void *opaque,
+QCryptoBlockCreateOptions *options,
+bool force,
+Error **errp);
+
 
 /**
  * qcrypto_block_get_info:
-- 
2.17.2




[Qemu-block] [PATCH 06/10] qcow2: implement crypto amend options

2019-08-30 Thread Maxim Levitsky
---
 block/qcow2.c | 79 ---
 1 file changed, 63 insertions(+), 16 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 376bb416fd..8dff4c6b5f 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -171,6 +171,25 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock 
*block, size_t offset,
 return ret;
 }
 
+static QCryptoBlockCreateOptions*
+qcow2_extract_crypto_create_opts(QemuOpts *opts, const char *fmt, Error **errp)
+{
+QDict *cryptoopts_qdict;
+QCryptoBlockCreateOptions *cryptoopts;
+QDict *opts_qdict;
+
+/* Extract "encrypt." options into a qdict */
+opts_qdict = qemu_opts_to_qdict(opts, NULL);
+qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
+qobject_unref(opts_qdict);
+
+/* Build QCryptoBlockCreateOptions object from qdict */
+qdict_put_str(cryptoopts_qdict, "format", "luks");
+cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
+qobject_unref(cryptoopts_qdict);
+return cryptoopts;
+}
+
 
 /* 
  * read qcow2 extension and fill bs
@@ -4366,20 +4385,10 @@ static ssize_t 
qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
 static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
  Error **errp)
 {
-QDict *opts_qdict;
-QDict *cryptoopts_qdict;
 QCryptoBlockCreateOptions *cryptoopts;
 QCryptoBlock *crypto;
 
-/* Extract "encrypt." options into a qdict */
-opts_qdict = qemu_opts_to_qdict(opts, NULL);
-qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
-qobject_unref(opts_qdict);
-
-/* Build QCryptoBlockCreateOptions object from qdict */
-qdict_put_str(cryptoopts_qdict, "format", "luks");
-cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
-qobject_unref(cryptoopts_qdict);
+cryptoopts = qcow2_extract_crypto_create_opts(opts, "luks", errp);
 if (!cryptoopts) {
 return false;
 }
@@ -4756,6 +4765,7 @@ typedef enum Qcow2AmendOperation {
  * invocation from an operation change */
 QCOW2_NO_OPERATION = 0,
 
+QCOW2_UPDATING_ENCRYPTION,
 QCOW2_CHANGING_REFCOUNT_ORDER,
 QCOW2_DOWNGRADING,
 } Qcow2AmendOperation;
@@ -4840,6 +4850,7 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 int ret;
 QemuOptDesc *desc = opts->list->desc;
 Qcow2AmendHelperCBInfo helper_cb_info;
+bool encryption_update = false;
 
 while (desc && desc->name) {
 if (!qemu_opt_find(opts, desc->name)) {
@@ -4888,9 +4899,22 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 return -ENOTSUP;
 }
 } else if (g_str_has_prefix(desc->name, "encrypt.")) {
-error_setg(errp,
-   "Changing the encryption parameters is not supported");
-return -ENOTSUP;
+
+if (!s->crypto) {
+error_setg(errp,
+   "Can't amend encryption options - encryption not 
supported");
+return -ENOTSUP;
+
+}
+
+if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
+error_setg(errp,
+   "Only LUKS encryption options can be amended");
+return -ENOTSUP;
+}
+
+encryption_update = true;
+
 } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
 cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
  cluster_size);
@@ -4927,7 +4951,7 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
  "images");
 return -EINVAL;
 }
-} else {
+} else  {
 /* if this point is reached, this probably means a new option was
  * added without having it covered here */
 abort();
@@ -4940,7 +4964,8 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 .original_status_cb = status_cb,
 .original_cb_opaque = cb_opaque,
 .total_operations = (new_version < old_version)
-  + (s->refcount_bits != refcount_bits)
+  + (s->refcount_bits != refcount_bits) +
+  (encryption_update == true)
 };
 
 /* Upgrade first (some features may require compat=1.1) */
@@ -4954,6 +4979,28 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 }
 }
 
+if (encryption_update) {
+
+QCryptoBlockCreateOptions *cryptoopts;
+
+cryptoopts = qcow2_extract_crypto_create_opts(opts, "luks", errp);
+if (!cryptoopts)
+return -EINVAL;
+
+helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
+
+ret = qcrypto_block_amend_options(s->crypto,
+  qcow2_crypto_hdr_read_fun

[Qemu-block] [PATCH 09/10] block/qcow2: implement blockdev-amend

2019-08-30 Thread Maxim Levitsky
Currently only for changing crypto parameters

Signed-off-by: Maxim Levitsky 
---
 block/qcow2.c| 71 
 qapi/block-core.json |  4 +--
 2 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 8dff4c6b5f..327d2afd9f 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3082,6 +3082,18 @@ qcow2_co_create(BlockdevCreateOptions *create_options, 
Error **errp)
 assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
 qcow2_opts = &create_options->u.qcow2;
 
+if (!qcow2_opts->has_size) {
+error_setg(errp, "Size is manadatory for image creation");
+return -EINVAL;
+
+}
+
+if (!qcow2_opts->has_file) {
+error_setg(errp, "'file' is manadatory for image creation");
+return -EINVAL;
+
+}
+
 bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
 if (bs == NULL) {
 return -EIO;
@@ -5112,6 +5124,64 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 return 0;
 }
 
+
+static int coroutine_fn qcow2_co_amend(BlockDriverState *bs,
+   BlockdevCreateOptions *opts,
+   bool force,
+   Error **errp)
+{
+BlockdevCreateOptionsQcow2 *qopts = &opts->u.qcow2;
+BDRVQcow2State *s = bs->opaque;
+int ret;
+
+/*
+ * This is ugly as hell, in later versions of this patch
+ * something has to be done about this
+ */
+if (qopts->has_file || qopts->has_size || qopts->has_data_file ||
+qopts->has_data_file_raw || qopts->has_version ||
+qopts->has_backing_file || qopts->has_backing_fmt ||
+qopts->has_cluster_size || qopts->has_preallocation ||
+qopts->has_lazy_refcounts || qopts->has_refcount_bits) {
+
+error_setg(errp,
+"Only LUKS encryption options can be amended for qcow2 with 
blockdev-amend");
+return -EOPNOTSUPP;
+
+}
+
+if (qopts->has_encrypt) {
+if (!s->crypto) {
+error_setg(errp, "QCOW2 image is not encrypted, can't amend");
+return -EOPNOTSUPP;
+}
+
+if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
+error_setg(errp,
+   "Amend can't be used to change the qcow2 encryption 
format");
+return -EOPNOTSUPP;
+}
+
+if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
+error_setg(errp,
+   "Only LUKS encryption options can be amended for qcow2 
with blockdev-amend");
+return -EOPNOTSUPP;
+}
+
+ret = qcrypto_block_amend_options(s->crypto,
+  qcow2_crypto_hdr_read_func,
+  qcow2_crypto_hdr_write_func,
+  bs,
+  qopts->encrypt,
+  force,
+  errp);
+if (ret) {
+return ret;
+}
+}
+return 0;
+}
+
 /*
  * If offset or size are negative, respectively, they will not be included in
  * the BLOCK_IMAGE_CORRUPTED event emitted.
@@ -5304,6 +5374,7 @@ BlockDriver bdrv_qcow2 = {
 .mutable_opts= mutable_opts,
 .bdrv_co_check   = qcow2_co_check,
 .bdrv_amend_options  = qcow2_amend_options,
+.bdrv_co_amend   = qcow2_co_amend,
 
 .bdrv_detach_aio_context  = qcow2_detach_aio_context,
 .bdrv_attach_aio_context  = qcow2_attach_aio_context,
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 02375fb59a..ba41744427 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -4312,10 +4312,10 @@
 # Since: 2.12
 ##
 { 'struct': 'BlockdevCreateOptionsQcow2',
-  'data': { 'file': 'BlockdevRef',
+  'data': { '*file':'BlockdevRef',
 '*data-file':   'BlockdevRef',
 '*data-file-raw':   'bool',
-'size': 'size',
+'*size':'size',
 '*version': 'BlockdevQcow2Version',
 '*backing-file':'str',
 '*backing-fmt': 'BlockdevDriver',
-- 
2.17.2




[Qemu-block] [PATCH 08/10] block/crypto: implement blockdev-amend

2019-08-30 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 block/crypto.c   | 86 +---
 qapi/block-core.json |  4 +--
 2 files changed, 68 insertions(+), 22 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index dbd95a99ba..9cb668ff0e 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -534,6 +534,17 @@ block_crypto_co_create_luks(BlockdevCreateOptions 
*create_options, Error **errp)
 assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
 luks_opts = &create_options->u.luks;
 
+if (!luks_opts->has_size) {
+error_setg(errp, "'size' is manadatory for image creation");
+return -EINVAL;
+}
+
+if (!luks_opts->has_file) {
+error_setg(errp, "'file' is manadatory for image creation");
+return -EINVAL;
+}
+
+
 bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
 if (bs == NULL) {
 return -EIO;
@@ -667,6 +678,39 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, 
Error **errp)
 }
 
 
+static int
+block_crypto_amend_options_generic(BlockDriverState *bs,
+   QCryptoBlockCreateOptions *amend_options,
+   bool force,
+   Error **errp)
+{
+BlockCrypto *crypto = bs->opaque;
+int ret = -1;
+
+assert(crypto);
+assert(crypto->block);
+
+/* apply for exclusive write permissions to the underlying file*/
+crypto->updating_keys = true;
+ret = bdrv_child_refresh_perms(bs, bs->file, errp);
+if (ret) {
+goto cleanup;
+}
+
+ret = qcrypto_block_amend_options(crypto->block,
+  block_crypto_read_func,
+  block_crypto_write_func,
+  bs,
+  amend_options,
+  force,
+  errp);
+cleanup:
+/* release exclusive write permissions to the underlying file*/
+crypto->updating_keys = false;
+bdrv_child_refresh_perms(bs, bs->file, errp);
+return ret;
+}
+
 static int
 block_crypto_amend_options(BlockDriverState *bs,
QemuOpts *opts,
@@ -678,44 +722,45 @@ block_crypto_amend_options(BlockDriverState *bs,
 BlockCrypto *crypto = bs->opaque;
 QDict *cryptoopts = NULL;
 QCryptoBlockCreateOptions *amend_options = NULL;
-int ret;
+int ret= -EINVAL;
 
 assert(crypto);
 assert(crypto->block);
 
-crypto->updating_keys = true;
-
-ret = bdrv_child_refresh_perms(bs, bs->file, errp);
-if (ret) {
-goto cleanup;
-}
-
 cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
  &block_crypto_create_opts_luks,
  true);
 
 qdict_put_str(cryptoopts, "format", "luks");
 amend_options = block_crypto_create_opts_init(cryptoopts, errp);
+
 if (!amend_options) {
-ret = -EINVAL;
-goto cleanup;
+goto out;
 }
 
-ret = qcrypto_block_amend_options(crypto->block,
-  block_crypto_read_func,
-  block_crypto_write_func,
-  bs,
-  amend_options,
-  force,
-  errp);
-cleanup:
-crypto->updating_keys = false;
-bdrv_child_refresh_perms(bs, bs->file, errp);
+ret = block_crypto_amend_options_generic(bs, amend_options, force, errp);
+out:
 qapi_free_QCryptoBlockCreateOptions(amend_options);
 qobject_unref(cryptoopts);
 return ret;
 }
 
+static int
+coroutine_fn block_crypto_co_amend(BlockDriverState *bs,
+   BlockdevCreateOptions *opts,
+   bool force,
+   Error **errp)
+{
+QCryptoBlockCreateOptions amend_opts;
+
+amend_opts = (QCryptoBlockCreateOptions) {
+.format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
+.u.luks = *qapi_BlockdevCreateOptionsLUKS_base(&opts->u.luks),
+};
+
+return block_crypto_amend_options_generic(bs, &amend_opts, force, errp);
+}
+
 
 static void
 block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
@@ -774,6 +819,7 @@ static BlockDriver bdrv_crypto_luks = {
 .bdrv_get_info  = block_crypto_get_info_luks,
 .bdrv_get_specific_info = block_crypto_get_specific_info_luks,
 .bdrv_amend_options = block_crypto_amend_options,
+.bdrv_co_amend  = block_crypto_co_amend,
 
 .strong_runtime_opts = block_crypto_strong_runtime_opts,
 };
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 7900914506..02375fb59a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core

[Qemu-block] [PATCH 02/10] qcrypto-luks: extend the create options for upcoming encryption key management

2019-08-30 Thread Maxim Levitsky
Now you can specify which slot to put the encryption key to
Plus add 'active' option which will let  user erase the key secret
instead of adding it.
Check that it is true for creation

Signed-off-by: Maxim Levitsky 
---
 block/crypto.c |  2 ++
 block/crypto.h | 16 +++
 block/qcow2.c  |  2 ++
 crypto/block-luks.c| 26 +++---
 qapi/crypto.json   | 19 ++
 tests/qemu-iotests/082.out | 54 ++
 6 files changed, 115 insertions(+), 4 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index 6e822c6e50..a6a3e1f1d8 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -144,6 +144,8 @@ static QemuOptsList block_crypto_create_opts_luks = {
 BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
 BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
 BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
+BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT(""),
+BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE(""),
 { /* end of list */ }
 },
 };
diff --git a/block/crypto.h b/block/crypto.h
index b935695e79..05cc43d9bc 100644
--- a/block/crypto.h
+++ b/block/crypto.h
@@ -35,12 +35,14 @@
 "ID of the secret that provides the AES encryption key")
 
 #define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret"
+#define BLOCK_CRYPTO_OPT_LUKS_SLOT "slot"
 #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode"
 #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
+#define BLOCK_CRYPTO_OPT_LUKS_ACTIVE "active"
 
 #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix)\
 BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \
@@ -88,6 +90,20 @@
 .help = "Time to spend in PBKDF in milliseconds", \
 }
 
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT(prefix)   \
+{ \
+.name = prefix BLOCK_CRYPTO_OPT_LUKS_SLOT,   \
+.type = QEMU_OPT_NUMBER,  \
+.help = "Controls the slot where the secret is added/erased", \
+}
+
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE(prefix)   \
+{ \
+.name = prefix BLOCK_CRYPTO_OPT_LUKS_ACTIVE,   \
+.type = QEMU_OPT_BOOL,  \
+.help = "Controls if the added secret is added or erased", \
+}
+
 QCryptoBlockCreateOptions *
 block_crypto_create_opts_init(QDict *opts, Error **errp);
 
diff --git a/block/qcow2.c b/block/qcow2.c
index 7c5a4859f7..be4a5063e5 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -5167,6 +5167,8 @@ static QemuOptsList qcow2_create_opts = {
 BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
 BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
 BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
+BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT("encrypt."),
+BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE("encrypt."),
 {
 .name = BLOCK_OPT_CLUSTER_SIZE,
 .type = QEMU_OPT_SIZE,
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 3af137e364..ba20d55246 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -1230,6 +1230,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 const char *hash_alg;
 g_autofree char *cipher_mode_spec = NULL;
 uint64_t iters;
+unsigned int slot_idx = 0;
 
 memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
 if (!luks_opts.has_iter_time) {
@@ -1263,12 +1264,30 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
 luks->hash_alg = luks_opts.hash_alg;
 
+if (luks_opts.has_active && !luks_opts.active) {
+error_setg(errp,
+   "For image creation, the added secret must be active!");
+goto error;
+
+}
+
+if (luks_opts.has_slot) {
+if (luks_opts.slot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ||
+luks_opts.slot < 0) {
+error_setg(errp,
+   "Invalid slot %" PRId64 " is specified",
+   luks_opts.slot);
+goto error;
+}
+slot_idx = (unsigned int)luks_opts.slot;
+}
+
 
 /* Note we're allowing ivgen_hash_alg to be set even for
  * non-essiv iv generators that don't need a hash. It will
  * be silently ignored, for compatibility with dm-crypt */
 
-   

[Qemu-block] [PATCH 10/10] iotests : add tests for encryption key management

2019-08-30 Thread Maxim Levitsky
Note that currently I add tests 300-302, which are
placeholders to ease the rebase. In final version
of these patches I will update these.

Signed-off-by: Maxim Levitsky 
---
 tests/qemu-iotests/087.out   |   6 +-
 tests/qemu-iotests/134.out   |   2 +-
 tests/qemu-iotests/158.out   |   4 +-
 tests/qemu-iotests/188.out   |   2 +-
 tests/qemu-iotests/189.out   |   4 +-
 tests/qemu-iotests/198.out   |   4 +-
 tests/qemu-iotests/300   | 202 +
 tests/qemu-iotests/300.out   |  98 
 tests/qemu-iotests/301   |  90 +++
 tests/qemu-iotests/301.out   |  30 
 tests/qemu-iotests/302   | 247 +++
 tests/qemu-iotests/302.out   |  18 +++
 tests/qemu-iotests/common.filter |   6 +-
 tests/qemu-iotests/group |   8 +
 14 files changed, 708 insertions(+), 13 deletions(-)
 create mode 100755 tests/qemu-iotests/300
 create mode 100644 tests/qemu-iotests/300.out
 create mode 100755 tests/qemu-iotests/301
 create mode 100644 tests/qemu-iotests/301.out
 create mode 100644 tests/qemu-iotests/302
 create mode 100644 tests/qemu-iotests/302.out

diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index 2d92ea847b..b61ba638af 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -34,7 +34,7 @@ QMP_VERSION
 
 === Encrypted image QCow ===
 
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
 Testing:
 QMP_VERSION
 {"return": {}}
@@ -46,7 +46,7 @@ QMP_VERSION
 
 === Encrypted image LUKS ===
 
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encrypt.format=luks 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
 Testing:
 QMP_VERSION
 {"return": {}}
@@ -58,7 +58,7 @@ QMP_VERSION
 
 === Missing driver ===
 
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
 Testing: -S
 QMP_VERSION
 {"return": {}}
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
index 09d46f6b17..4abc5b5f7d 100644
--- a/tests/qemu-iotests/134.out
+++ b/tests/qemu-iotests/134.out
@@ -1,5 +1,5 @@
 QA output created by 134
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
 
 == reading whole image ==
 read 134217728/134217728 bytes at offset 0
diff --git a/tests/qemu-iotests/158.out b/tests/qemu-iotests/158.out
index 6def216e55..f28a17626b 100644
--- a/tests/qemu-iotests/158.out
+++ b/tests/qemu-iotests/158.out
@@ -1,6 +1,6 @@
 QA output created by 158
 == create base ==
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on
 
 == writing whole image ==
 wrote 134217728/134217728 bytes at offset 0
@@ -10,7 +10,7 @@ wrote 134217728/134217728 bytes at offset 0
 read 134217728/134217728 bytes at offset 0
 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 == create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 
backing_file=TEST_DIR/t.IMGFMT.base encryption=on encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 
backing_file=TEST_DIR/t.IMGFMT.base encryption=on
 
 == writing part of a cluster ==
 wrote 1024/1024 bytes at offset 0
diff --git a/tests/qemu-iotests/188.out b/tests/qemu-iotests/188.out
index c568ef3701..5426861b18 100644
--- a/tests/qemu-iotests/188.out
+++ b/tests/qemu-iotests/188.out
@@ -1,5 +1,5 @@
 QA output created by 188
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 encrypt.format=luks 
encrypt.key-secret=sec0 encrypt.iter-time=10
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216
 
 == reading whole image ==
 read 16777216/16777216 bytes at offset 0
diff --git a/tests/qemu-iotests/189.out b/tests/qemu-iotests/189.out
index a0b7c9c24c..bc213cbe14 100644
--- a/tests/qemu-iotests/189.out
+++ b/tests/qemu-iotests/189.out
@@ -1,6 +1,6 @@
 QA output created by 189
 == create base ==
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 
encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216
 
 == writing whole image ==
 wrote 16777216/16777216 bytes at offset 0
@@ -10,7 +10,7 @@ wrote 16777216/16777216 bytes at offset 0
 read 16777216/16777216 bytes at offset 0
 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 == create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGF

[Qemu-block] [PATCH 04/10] block: amend: add 'force' option

2019-08-30 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 block.c   | 4 +++-
 block/qcow2.c | 1 +
 include/block/block.h | 1 +
 include/block/block_int.h | 1 +
 qemu-img-cmds.hx  | 4 ++--
 qemu-img.c| 8 +++-
 qemu-img.texi | 6 +-
 7 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/block.c b/block.c
index 874a29a983..df6707677a 100644
--- a/block.c
+++ b/block.c
@@ -6142,6 +6142,7 @@ void bdrv_remove_aio_context_notifier(BlockDriverState 
*bs,
 
 int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+   bool force,
Error **errp)
 {
 if (!bs->drv) {
@@ -6153,7 +6154,8 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts 
*opts,
bs->drv->format_name);
 return -ENOTSUP;
 }
-return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
+return bs->drv->bdrv_amend_options(bs, opts, status_cb,
+   cb_opaque, force, errp);
 }
 
 /* This function will be called by the bdrv_recurse_is_first_non_filter method
diff --git a/block/qcow2.c b/block/qcow2.c
index be4a5063e5..376bb416fd 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -4823,6 +4823,7 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
 static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque,
+   bool force,
Error **errp)
 {
 BDRVQcow2State *s = bs->opaque;
diff --git a/include/block/block.h b/include/block/block.h
index 124ad40809..6bc89c7667 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -400,6 +400,7 @@ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, 
int64_t offset,
   int64_t total_work_size, void *opaque);
 int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+   bool force,
Error **errp);
 
 /* external snapshots */
diff --git a/include/block/block_int.h b/include/block/block_int.h
index ceec8c2f56..c6aa05214f 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -402,6 +402,7 @@ struct BlockDriver {
 int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
   BlockDriverAmendStatusCB *status_cb,
   void *cb_opaque,
+  bool force,
   Error **errp);
 
 void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 1c93e6d185..323ea10ad0 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -14,9 +14,9 @@ STEXI
 ETEXI
 
 DEF("amend", img_amend,
-"amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] 
-o options filename")
+"amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] 
[--force] -o options filename")
 STEXI
-@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] 
[-t @var{cache}] -o @var{options} @var{filename}
+@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] 
[-t @var{cache}] [--force] -o @var{options} @var{filename}
 ETEXI
 
 DEF("bench", img_bench,
diff --git a/qemu-img.c b/qemu-img.c
index 7daa05e51a..4533a44c1d 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -70,6 +70,7 @@ enum {
 OPTION_PREALLOCATION = 265,
 OPTION_SHRINK = 266,
 OPTION_SALVAGE = 267,
+OPTION_FORCE = 268,
 };
 
 typedef enum OutputFormat {
@@ -3915,6 +3916,7 @@ static int img_amend(int argc, char **argv)
 BlockBackend *blk = NULL;
 BlockDriverState *bs = NULL;
 bool image_opts = false;
+bool force = false;
 
 cache = BDRV_DEFAULT_CACHE;
 for (;;) {
@@ -3922,6 +3924,7 @@ static int img_amend(int argc, char **argv)
 {"help", no_argument, 0, 'h'},
 {"object", required_argument, 0, OPTION_OBJECT},
 {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+{"force", no_argument, 0, OPTION_FORCE},
 {0, 0, 0, 0}
 };
 c = getopt_long(argc, argv, ":ho:f:t:pq",
@@ -3977,6 +3980,9 @@ static int img_amend(int argc, char **argv)
 case OPTION_IMAGE_OPTS:
 image_opts = true;
 break;
+case OPTION_FORCE:
+force = true;
+break;
 }
 }
 
@@ -4054,7 +4060,7 @@ static int img_amend(int argc, char **argv)
 
 /* In case the driver does not call amend_status_cb() */
 qemu_progress_print(0.f, 0);
-  

[Qemu-block] [PATCH 07/10] block: add x-blockdev-amend qmp command

2019-08-30 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 block/Makefile.objs   |   2 +-
 block/amend.c | 116 ++
 include/block/block_int.h |  23 ++--
 qapi/block-core.json  |  26 +
 qapi/job.json |   4 +-
 5 files changed, 163 insertions(+), 8 deletions(-)
 create mode 100644 block/amend.c

diff --git a/block/Makefile.objs b/block/Makefile.objs
index 35f3bca4d9..10d0308792 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -18,7 +18,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += file-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
-block-obj-y += null.o mirror.o commit.o io.o create.o
+block-obj-y += null.o mirror.o commit.o io.o create.o amend.o
 block-obj-y += throttle-groups.o
 block-obj-$(CONFIG_LINUX) += nvme.o
 
diff --git a/block/amend.c b/block/amend.c
new file mode 100644
index 00..9bd28e08e7
--- /dev/null
+++ b/block/amend.c
@@ -0,0 +1,116 @@
+/*
+ * Block layer code related to image options amend
+ *
+ * Copyright (c) 2018 Kevin Wolf 
+ * Copyright (c) 2019 Maxim Levitsky 
+ *
+ * Heavily based on create.c
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block_int.h"
+#include "qemu/job.h"
+#include "qemu/main-loop.h"
+#include "qapi/qapi-commands-block-core.h"
+#include "qapi/qapi-visit-block-core.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/error.h"
+
+typedef struct BlockdevAmendJob {
+Job common;
+BlockdevCreateOptions *opts;
+BlockDriverState *bs;
+bool force;
+} BlockdevAmendJob;
+
+static int coroutine_fn blockdev_amend_run(Job *job, Error **errp)
+{
+BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common);
+int ret;
+
+job_progress_set_remaining(&s->common, 1);
+ret = s->bs->drv->bdrv_co_amend(s->bs, s->opts, s->force, errp);
+job_progress_update(&s->common, 1);
+
+qapi_free_BlockdevCreateOptions(s->opts);
+
+return ret;
+}
+
+static const JobDriver blockdev_amend_job_driver = {
+.instance_size = sizeof(BlockdevAmendJob),
+.job_type  = JOB_TYPE_AMEND,
+.run   = blockdev_amend_run,
+};
+
+void qmp_x_blockdev_amend(const char *job_id,
+const char *node_name,
+BlockdevCreateOptions *options,
+bool has_force,
+bool force,
+Error **errp)
+{
+BlockdevAmendJob *s;
+const char *fmt = BlockdevDriver_str(options->driver);
+BlockDriver *drv = bdrv_find_format(fmt);
+BlockDriverState *bs = bdrv_find_node(node_name);
+
+/*
+ * If the driver is in the schema, we know that it exists. But it may not
+ * be whitelisted.
+ */
+assert(drv);
+if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
+error_setg(errp, "Driver is not whitelisted");
+return;
+}
+
+if (bs->drv != drv) {
+error_setg(errp,
+   "x-blockdev-amend doesn't support changing the block 
driver");
+return;
+
+}
+
+/* Error out if the driver doesn't support .bdrv_co_amend */
+if (!drv->bdrv_co_amend) {
+error_setg(errp, "Driver does not support x-blockdev-amend");
+return;
+}
+
+/*
+ * Create the block job
+ * TODO Running in the main context. Block drivers need to error out or add
+ * locking when they use a BDS in a different AioContext.
+ */
+s = job_create(job_id, &blockdev_amend_job_driver, NULL,
+   qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
+   NULL, NULL, errp);
+if (!s) {
+   

Re: [Qemu-block] [Qemu-devel] [PATCH 2/2] block/nvme: add support for discard

2019-09-05 Thread Maxim Levitsky
On Wed, 2019-08-28 at 12:03 +0300, Maxim Levitsky wrote:
> On Tue, 2019-08-27 at 18:29 -0400, John Snow wrote:
> > 
> > On 8/25/19 3:15 AM, Maxim Levitsky wrote:
> > > Signed-off-by: Maxim Levitsky 
> > > ---
> > >  block/nvme.c   | 83 ++
> > >  block/trace-events |  2 ++
> > >  2 files changed, 85 insertions(+)
> > > 
> > > diff --git a/block/nvme.c b/block/nvme.c
> > > index f8bd11e19a..dd041f39c9 100644
> > > --- a/block/nvme.c
> > > +++ b/block/nvme.c
> > > @@ -112,6 +112,7 @@ typedef struct {
> > >  bool plugged;
> > >  
> > >  bool supports_write_zeros;
> > > +bool supports_discard;
> > >  
> > >  CoMutex dma_map_lock;
> > >  CoQueue dma_flush_queue;
> > > @@ -463,6 +464,7 @@ static void nvme_identify(BlockDriverState *bs, int 
> > > namespace, Error **errp)
> > >  
> > >  oncs = le16_to_cpu(idctrl->oncs);
> > >  s->supports_write_zeros = (oncs & NVME_ONCS_WRITE_ZEROS) != 0;
> > > +s->supports_discard = (oncs & NVME_ONCS_DSM) != 0;
> > 
> > Same comment -- checking !!(register & FIELD) is nicer than the
> > negative. (I'm actually not sure even the !! is needed, but it seems to
> > be a QEMU-ism and I've caught myself using it...)
> 
> All right, no problem to use !!
> 
> > 
> > Rest looks good to me on a skim, but I'm not very well-versed in NVME.
> 
> Thanks!
> 

Kind ping about this patch series.

Apart from using !!, do you think that this patch series
can be merged, or should I do anything else?
Which tree do you think this should be committed to?

I kind of want to see that merged before the freeze
starts, if there are no objections,
to reduce the amount of pending stuff in my queue.


[...]



Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH 0/7] block: Generic file creation fallback

2019-09-05 Thread Maxim Levitsky
On Fri, 2019-07-12 at 19:35 +0200, Max Reitz wrote:
> Hi,
> 
> Kevin commented on my RFC, so I got what an RFC wants, and he didn’t
> object to the creation fallback part.  So I suppose I can go down that
> route at least.  (Which was actually the more important part of the
> series.)
> 
> So as in the RFC, this series adds a fallback path for creating files
> (on the protocol layer) if the protocol driver does not support file
> creation, but the file already exists.
> 

Hi!
Do you have any update on this patch series by a chance?

Best regards,
Maxim Levitsky




Re: [Qemu-block] [Qemu-devel] [PATCH 2/2] block/nvme: add support for discard

2019-09-05 Thread Maxim Levitsky
On Thu, 2019-09-05 at 13:27 -0400, John Snow wrote:
> 
> On 9/5/19 9:24 AM, Maxim Levitsky wrote:
> > On Wed, 2019-08-28 at 12:03 +0300, Maxim Levitsky wrote:
> > > On Tue, 2019-08-27 at 18:29 -0400, John Snow wrote:
> > > > 
> > > > On 8/25/19 3:15 AM, Maxim Levitsky wrote:
> > > > > Signed-off-by: Maxim Levitsky 
> > > > > ---
> > > > >  block/nvme.c   | 83 
> > > > > ++
> > > > >  block/trace-events |  2 ++
> > > > >  2 files changed, 85 insertions(+)
> > > > > 
> > > > > diff --git a/block/nvme.c b/block/nvme.c
> > > > > index f8bd11e19a..dd041f39c9 100644
> > > > > --- a/block/nvme.c
> > > > > +++ b/block/nvme.c
> > > > > @@ -112,6 +112,7 @@ typedef struct {
> > > > >  bool plugged;
> > > > >  
> > > > >  bool supports_write_zeros;
> > > > > +bool supports_discard;
> > > > >  
> > > > >  CoMutex dma_map_lock;
> > > > >  CoQueue dma_flush_queue;
> > > > > @@ -463,6 +464,7 @@ static void nvme_identify(BlockDriverState *bs, 
> > > > > int namespace, Error **errp)
> > > > >  
> > > > >  oncs = le16_to_cpu(idctrl->oncs);
> > > > >  s->supports_write_zeros = (oncs & NVME_ONCS_WRITE_ZEROS) != 0;
> > > > > +s->supports_discard = (oncs & NVME_ONCS_DSM) != 0;
> > > > 
> > > > Same comment -- checking !!(register & FIELD) is nicer than the
> > > > negative. (I'm actually not sure even the !! is needed, but it seems to
> > > > be a QEMU-ism and I've caught myself using it...)
> > > 
> > > All right, no problem to use !!
> > > 
> > > > 
> > > > Rest looks good to me on a skim, but I'm not very well-versed in NVME.
> > > 
> > > Thanks!
> > > 
> > 
> > Kind ping about this patch series.
> > 
> > Apart from using !!, do you think that this patch series
> > can be merged, or should I do anything else?
> > Which tree do you think this should be committed to?
> > 
> > I kind of want to see that merged before the freeze
> > starts, if there are no objections,
> > to reduce the amount of pending stuff in my queue.
> > 
> 
> Didn't I ask a few other things?
> 
> like not using "res30" because you've moved the fields around, and
> trying to be consistent about "zeros" vs "zeroes".
> 
> Removing "+#define NVME_ID_NS_DLFEAT_GUARD_CRC(dlfeat)   ((dlfeat) &
> 0x10)" because it's unused.


All right I forgot about it, that's one of they joys of duplication of a kernel
driver in the userspace...


> 
> You also probably require review (or at least an ACK) from Keith Busch
> who maintains this file.
> 
> --js

All right,
Best regards,
Maxim Levitsky





Re: [Qemu-block] [PATCH 02/10] qcrypto-luks: extend the create options for upcoming encryption key management

2019-09-06 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:49 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:00PM +0300, Maxim Levitsky wrote:
> > Now you can specify which slot to put the encryption key to
> > Plus add 'active' option which will let  user erase the key secret
> > instead of adding it.
> > Check that it is true for creation
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/crypto.c |  2 ++
> >  block/crypto.h | 16 +++
> >  block/qcow2.c  |  2 ++
> >  crypto/block-luks.c| 26 +++---
> >  qapi/crypto.json   | 19 ++
> >  tests/qemu-iotests/082.out | 54 ++
> >  6 files changed, 115 insertions(+), 4 deletions(-)
> > 
> > diff --git a/block/crypto.c b/block/crypto.c
> > index 6e822c6e50..a6a3e1f1d8 100644
> > --- a/block/crypto.c
> > +++ b/block/crypto.c
> > @@ -144,6 +144,8 @@ static QemuOptsList block_crypto_create_opts_luks = {
> >  BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
> >  BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
> >  BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
> > +BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT(""),
> > +BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE(""),
> >  { /* end of list */ }
> >  },
> >  };
> > diff --git a/block/crypto.h b/block/crypto.h
> > index b935695e79..05cc43d9bc 100644
> > --- a/block/crypto.h
> > +++ b/block/crypto.h
> > @@ -35,12 +35,14 @@
> >  "ID of the secret that provides the AES encryption key")
> >  
> >  #define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret"
> > +#define BLOCK_CRYPTO_OPT_LUKS_SLOT "slot"
> >  #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg"
> >  #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode"
> >  #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg"
> >  #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
> >  #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
> >  #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
> > +#define BLOCK_CRYPTO_OPT_LUKS_ACTIVE "active"
> >  
> >  #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix)\
> >  BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \
> > @@ -88,6 +90,20 @@
> >  .help = "Time to spend in PBKDF in milliseconds", \
> >  }
> >  
> > +#define BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT(prefix)   \
> > +{ \
> > +.name = prefix BLOCK_CRYPTO_OPT_LUKS_SLOT,   \
> > +.type = QEMU_OPT_NUMBER,  \
> > +.help = "Controls the slot where the secret is added/erased", \
> > +}
> > +
> > +#define BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE(prefix)   \
> > +{ \
> > +.name = prefix BLOCK_CRYPTO_OPT_LUKS_ACTIVE,   \
> > +.type = QEMU_OPT_BOOL,  \
> > +.help = "Controls if the added secret is added or erased", \
> > +    }
> 
> Do we actually need the "active" property for initial
> creation. I think its only needed for amend, so perhaps
> we shuold not register this at all ?

Sadly we kind of do, since both amend and create use the same option list 
currently.
I tried to duplicate it, and it is possible, but then you end up
with significant code duplication in qcow2 with its huge create option list.

I am now thinking that we could have had , 'create only' option list, 'amend 
only' option list,
and 'common' option list.
What do you think?


[...]

Best regards,
Maxim Levitsky







Re: [Qemu-block] [PATCH 10/10] iotests : add tests for encryption key management

2019-09-06 Thread Maxim Levitsky
On Fri, 2019-09-06 at 15:14 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:08PM +0300, Maxim Levitsky wrote:
> > Note that currently I add tests 300-302, which are
> > placeholders to ease the rebase. In final version
> > of these patches I will update these.
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  tests/qemu-iotests/087.out   |   6 +-
> >  tests/qemu-iotests/134.out   |   2 +-
> >  tests/qemu-iotests/158.out   |   4 +-
> >  tests/qemu-iotests/188.out   |   2 +-
> >  tests/qemu-iotests/189.out   |   4 +-
> >  tests/qemu-iotests/198.out   |   4 +-
> >  tests/qemu-iotests/300   | 202 +
> >  tests/qemu-iotests/300.out   |  98 
> >  tests/qemu-iotests/301   |  90 +++
> >  tests/qemu-iotests/301.out   |  30 
> >  tests/qemu-iotests/302   | 247 +++
> >  tests/qemu-iotests/302.out   |  18 +++
> >  tests/qemu-iotests/common.filter |   6 +-
> >  tests/qemu-iotests/group |   8 +
> >  14 files changed, 708 insertions(+), 13 deletions(-)
> >  create mode 100755 tests/qemu-iotests/300
> >  create mode 100644 tests/qemu-iotests/300.out
> >  create mode 100755 tests/qemu-iotests/301
> >  create mode 100644 tests/qemu-iotests/301.out
> >  create mode 100644 tests/qemu-iotests/302
> >  create mode 100644 tests/qemu-iotests/302.out
> > 
> > diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
> > index 2d92ea847b..b61ba638af 100644
> > --- a/tests/qemu-iotests/087.out
> > +++ b/tests/qemu-iotests/087.out
> > @@ -34,7 +34,7 @@ QMP_VERSION
> >  
> >  === Encrypted image QCow ===
> >  
> > -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
> > encrypt.key-secret=sec0
> > +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
> 
> Why is the output format changing ? There's no code changes in
> this patch. If the change is due to an earlier patch, then this
> patch chunk should be put in the earlier patch that causes it.
> 
> Likewise for the changed output to other files in this patch.
> 
> >  

I tweaked the common.filter to filter more luks specific create options
so that a test could have same output for both qcow2 and plain raw luks 
encryption.
(due to the "encrypt.*" prefix)


I can move this in a separate patch if you think this is worth it.

Best regards,
Maxim Levitsky




[Qemu-block] [PATCH 0/3] Fix qcow2+luks corruption introduced by commit 8ac0f15f335

2019-09-06 Thread Maxim Levitsky
Commit 8ac0f15f335 accidently broke the COW of non changed areas
of newly allocated clusters, when the write spans multiple clusters,
and needs COW both prior and after the write.
This results in 'after' COW area beeing encrypted with wrong
sector address, which render it corrupted.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

CC: qemu-stable 

Best regards,
    Maxim Levitsky

Maxim Levitsky (3):
  block/qcow2: refactoring of threaded encryption code
  block/qcow2: fix the corruption when rebasing luks encrypted files
  qemu-iotests: test for bz #1745922

 block/qcow2-cluster.c  | 26 +++--
 block/qcow2-threads.c  | 53 --
 tests/qemu-iotests/263 | 76 ++
 tests/qemu-iotests/263.out | 19 ++
 tests/qemu-iotests/group   |  1 +
 5 files changed, 153 insertions(+), 22 deletions(-)
 create mode 100755 tests/qemu-iotests/263
 create mode 100644 tests/qemu-iotests/263.out

-- 
2.17.2




[Qemu-block] [PATCH 3/3] qemu-iotests: test for bz #1745922

2019-09-06 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 tests/qemu-iotests/263 | 76 ++
 tests/qemu-iotests/263.out | 19 ++
 tests/qemu-iotests/group   |  1 +
 3 files changed, 96 insertions(+)
 create mode 100755 tests/qemu-iotests/263
 create mode 100644 tests/qemu-iotests/263.out

diff --git a/tests/qemu-iotests/263 b/tests/qemu-iotests/263
new file mode 100755
index 00..9cb23aa81e
--- /dev/null
+++ b/tests/qemu-iotests/263
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+#
+# Test encrypted write that crosses cluster boundary of two unallocated 
clusters
+# Based on 188
+#
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mlevi...@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1   # failure is the default!
+
+_cleanup()
+{
+   _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+
+size=1M
+
+SECRET="secret,id=sec0,data=astrochicken"
+SECRETALT="secret,id=sec0,data=platypus"
+
+_make_test_img --object $SECRET -o 
"encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=64K"
 $size
+
+IMGSPEC="driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG"
+
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
+
+echo
+echo "== reading whole image =="
+$QEMU_IO --object $SECRET -c "read -P 0 0 $size" --image-opts $IMGSPEC | 
_filter_qemu_io | _filter_testdir
+
+echo
+echo "== write two 512 byte sectors on a cluster boundary =="
+$QEMU_IO --object $SECRET -c "write -P 0xAA 0xFE00 0x400" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== verify that the rest of the image is not changed =="
+$QEMU_IO --object $SECRET -c "read -P 0x00 0x0 0xFE00" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET -c "read -P 0xAA 0x0FE00 0x400" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET -c "read -P 0x00 0x10200 0xEFE00" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+
+_cleanup_test_img
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/263.out b/tests/qemu-iotests/263.out
new file mode 100644
index 00..fa4e4e0e4a
--- /dev/null
+++ b/tests/qemu-iotests/263.out
@@ -0,0 +1,19 @@
+QA output created by 263
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks 
encrypt.key-secret=sec0 encrypt.iter-time=10
+
+== reading whole image ==
+read 1048576/1048576 bytes at offset 0
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== write two 512 byte sectors on a cluster boundary ==
+wrote 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify that the rest of the image is not changed ==
+read 65024/65024 bytes at offset 0
+63.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 982528/982528 bytes at offset 66048
+959.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index d95d556414..be1c4a3baa 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -274,3 +274,4 @@
 257 rw
 258 rw quick
 262 rw quick migration
+263 rw quick
-- 
2.17.2




[Qemu-block] [PATCH 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-06 Thread Maxim Levitsky
This commit tries to clarify few function arguments,
and add comments describing the encrypt/decrypt interface

Signed-off-by: Maxim Levitsky 
---
 block/qcow2-cluster.c |  8 +++
 block/qcow2-threads.c | 53 ++-
 2 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index f09cc992af..b95e64c237 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -463,8 +463,8 @@ static int coroutine_fn 
do_perform_cow_read(BlockDriverState *bs,
 }
 
 static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
-uint64_t src_cluster_offset,
-uint64_t cluster_offset,
+uint64_t guest_cluster_offset,
+uint64_t host_cluster_offset,
 unsigned offset_in_cluster,
 uint8_t *buffer,
 unsigned bytes)
@@ -474,8 +474,8 @@ static bool coroutine_fn 
do_perform_cow_encrypt(BlockDriverState *bs,
 assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
 assert((bytes & ~BDRV_SECTOR_MASK) == 0);
 assert(s->crypto);
-if (qcow2_co_encrypt(bs, cluster_offset,
- src_cluster_offset + offset_in_cluster,
+if (qcow2_co_encrypt(bs, host_cluster_offset,
+ guest_cluster_offset + offset_in_cluster,
  buffer, bytes) < 0) {
 return false;
 }
diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
index 3b1e63fe41..8bc339690f 100644
--- a/block/qcow2-threads.c
+++ b/block/qcow2-threads.c
@@ -234,15 +234,19 @@ static int qcow2_encdec_pool_func(void *opaque)
 }
 
 static int coroutine_fn
-qcow2_co_encdec(BlockDriverState *bs, uint64_t file_cluster_offset,
-  uint64_t offset, void *buf, size_t len, Qcow2EncDecFunc func)
+qcow2_co_encdec(BlockDriverState *bs, uint64_t host_cluster_offset,
+  uint64_t guest_offset, void *buf, size_t len,
+  Qcow2EncDecFunc func)
 {
 BDRVQcow2State *s = bs->opaque;
+
+uint64_t offset = s->crypt_physical_offset ?
+host_cluster_offset + offset_into_cluster(s, guest_offset) :
+guest_offset;
+
 Qcow2EncDecData arg = {
 .block = s->crypto,
-.offset = s->crypt_physical_offset ?
-  file_cluster_offset + offset_into_cluster(s, offset) :
-  offset,
+.offset = offset,
 .buf = buf,
 .len = len,
 .func = func,
@@ -251,18 +255,45 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t 
file_cluster_offset,
 return qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
 }
 
+
+/*
+ * qcow2_co_encrypt()
+ *
+ * Encrypts a sector size aligned contiguous area
+ *
+ * @host_cluster_offset - on disk offset of the cluster in which
+ *the buffer resides
+ *
+ * @guest_offset - guest (virtual) offset of the buffer
+ * @buf - buffer with the data to encrypt
+ * @len - length of the buffer
+ *
+ * Note that the area is not cluster aligned and might cross a cluster
+ * boundary
+ *
+ *
+ */
 int coroutine_fn
-qcow2_co_encrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
- uint64_t offset, void *buf, size_t len)
+qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
+ uint64_t guest_offset, void *buf, size_t len)
 {
-return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
+return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
  qcrypto_block_encrypt);
 }
 
+
+/*
+ * qcow2_co_decrypt()
+ *
+ * Decrypts a sector size aligned contiguous area
+ * Same function as qcow2_co_encrypt
+ *
+ */
+
 int coroutine_fn
-qcow2_co_decrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
- uint64_t offset, void *buf, size_t len)
+qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
+ uint64_t guest_offset, void *buf, size_t len)
 {
-return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
+return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
  qcrypto_block_decrypt);
 }
-- 
2.17.2




[Qemu-block] [PATCH 2/3] block/qcow2: fix the corruption when rebasing luks encrypted files

2019-09-06 Thread Maxim Levitsky
This fixes subltle corruption introduced by luks threaded encryption
in commit 8ac0f15f335

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

The corruption happens when we do
   * write to two or more unallocated clusters at once
   * write doesn't fully cover nether first nor last cluster

In this case, when allocating the new clusters we COW both area
prior to the write and after the write, and we encrypt them.

The above mentioned commit accidently made it so, we encrypt the
second COW are using the physical cluster offset of the first area.

Fix this by:
 * remove the offset_in_cluster parameter of do_perform_cow_encrypt
   since it is misleading. That offset can be larger that cluster size.
   instead just add the start and end COW are offsets to both host and guest 
offsets
   that do_perform_cow_encrypt receives.

*  in do_perform_cow_encrypt, remove the cluster offset from the host_offset
   And thus pass correctly to the qcow2_co_encrypt, the host cluster offset and 
full guest offset


Signed-off-by: Maxim Levitsky 
---
 block/qcow2-cluster.c | 26 +++---
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index b95e64c237..32477f0156 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -463,20 +463,20 @@ static int coroutine_fn 
do_perform_cow_read(BlockDriverState *bs,
 }
 
 static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
-uint64_t guest_cluster_offset,
-uint64_t host_cluster_offset,
-unsigned offset_in_cluster,
+uint64_t guest_offset,
+uint64_t host_offset,
 uint8_t *buffer,
 unsigned bytes)
 {
 if (bytes && bs->encrypted) {
 BDRVQcow2State *s = bs->opaque;
-assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
+assert((guest_offset & ~BDRV_SECTOR_MASK) == 0);
+assert((host_offset & ~BDRV_SECTOR_MASK) == 0);
 assert((bytes & ~BDRV_SECTOR_MASK) == 0);
 assert(s->crypto);
-if (qcow2_co_encrypt(bs, host_cluster_offset,
- guest_cluster_offset + offset_in_cluster,
- buffer, bytes) < 0) {
+
+if (qcow2_co_encrypt(bs, start_of_cluster(s, host_offset),
+ guest_offset, buffer, bytes) < 0) {
 return false;
 }
 }
@@ -890,11 +890,15 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta 
*m)
 
 /* Encrypt the data if necessary before writing it */
 if (bs->encrypted) {
-if (!do_perform_cow_encrypt(bs, m->offset, m->alloc_offset,
-start->offset, start_buffer,
+if (!do_perform_cow_encrypt(bs,
+m->offset + start->offset,
+m->alloc_offset + start->offset,
+start_buffer,
 start->nb_bytes) ||
-!do_perform_cow_encrypt(bs, m->offset, m->alloc_offset,
-end->offset, end_buffer, end->nb_bytes)) {
+!do_perform_cow_encrypt(bs,
+m->offset + end->offset,
+m->alloc_offset + end->offset,
+end_buffer, end->nb_bytes)) {
 ret = -EIO;
 goto fail;
 }
-- 
2.17.2




Re: [Qemu-block] [Qemu-devel] [PATCH 0/3] Fix qcow2+luks corruption introduced by commit 8ac0f15f335

2019-09-06 Thread Maxim Levitsky
On Fri, 2019-09-06 at 20:31 +0300, Maxim Levitsky wrote:
> Commit 8ac0f15f335 accidently broke the COW of non changed areas
> of newly allocated clusters, when the write spans multiple clusters,
> and needs COW both prior and after the write.
> This results in 'after' COW area beeing encrypted with wrong
> sector address, which render it corrupted.
> 
> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922
> 
> CC: qemu-stable 

I forgot to mention, huge thanks to Kevin Wolf,
for helping me to define the proper fix for this bug.

Best regards,
Maxim Levitsky




Re: [Qemu-block] [Qemu-devel] [PATCH 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-06 Thread Maxim Levitsky
On Fri, 2019-09-06 at 13:00 -0500, Eric Blake wrote:
> On 9/6/19 12:31 PM, Maxim Levitsky wrote:
> > This commit tries to clarify few function arguments,
> > and add comments describing the encrypt/decrypt interface
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/qcow2-cluster.c |  8 +++
> >  block/qcow2-threads.c | 53 ++-
> >  2 files changed, 46 insertions(+), 15 deletions(-)
> > 
> > +++ b/block/qcow2-threads.c
> > @@ -234,15 +234,19 @@ static int qcow2_encdec_pool_func(void *opaque)
> >  }
> >  
> >  static int coroutine_fn
> > -qcow2_co_encdec(BlockDriverState *bs, uint64_t file_cluster_offset,
> > -  uint64_t offset, void *buf, size_t len, Qcow2EncDecFunc 
> > func)
> 
> Pre-existing bug in alignment...
No problem will fix.

> 
> > +qcow2_co_encdec(BlockDriverState *bs, uint64_t host_cluster_offset,
> > +  uint64_t guest_offset, void *buf, size_t len,
> > +  Qcow2EncDecFunc func)
> 
> ...so this would be a great time to fix it.
> 
> >  {
> >  BDRVQcow2State *s = bs->opaque;
> > +
> > +uint64_t offset = s->crypt_physical_offset ?
> > +host_cluster_offset + offset_into_cluster(s, guest_offset) :
> > +guest_offset;
> > +
> >  Qcow2EncDecData arg = {
> >  .block = s->crypto,
> > -.offset = s->crypt_physical_offset ?
> > -  file_cluster_offset + offset_into_cluster(s, offset) 
> > :
> > -  offset,
> > +.offset = offset,
> 
> I'm ambivalent on whether the new 'offset' variable gains us anything.
> But it doesn't hurt.
I added it, so that I won't need to wrap the lines even more that 
they are wrapped already since I increased the length of
the parameter names a bit.

> 
> 
> >  .buf = buf,
> >  .len = len,
> >  .func = func,
> > @@ -251,18 +255,45 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t 
> > file_cluster_offset,
> >  return qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
> >  }
> >  
> > +
> > +/*
> > + * qcow2_co_encrypt()
> > + *
> > + * Encrypts a sector size aligned contiguous area
> > + *
> > + * @host_cluster_offset - on disk offset of the cluster in which
> > + *the buffer resides
> > + *
> > + * @guest_offset - guest (virtual) offset of the buffer
> > + * @buf - buffer with the data to encrypt
> > + * @len - length of the buffer
> > + *
> > + * Note that the area is not cluster aligned and might cross a cluster
> > + * boundary
> 
> Umm, how is it possible for a sector to cross a cluster boundary?  All
> clusters are sector-aligned, and encryption only works on aligned
> sectors.  Oh, I see - if @len is a multiple larger than sector size,
> then we have multiple sectors, and then indeed we may cross clusters.
> But then the docs about being 'a sector size aligned contiguous area' is
> not quite right.

Why? the written area is always both aligned on _sector_ boundary
and multiple of the sector size. At least that what I see from
the existing asserts.


> 
> > + *
> > + *
> > + */
> >  int coroutine_fn
> > -qcow2_co_encrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
> > - uint64_t offset, void *buf, size_t len)
> > +qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
> > + uint64_t guest_offset, void *buf, size_t len)
> >  {
> > -return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
> > +return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
> >   qcrypto_block_encrypt);
> 
> 
> Another alignment worth fixing up while in the area.
> 
> >  }
> >  
> > +
> > +/*
> > + * qcow2_co_decrypt()
> > + *
> > + * Decrypts a sector size aligned contiguous area
> > + * Same function as qcow2_co_encrypt
> > + *
> > + */
> > +
> >  int coroutine_fn
> > -qcow2_co_decrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
> > - uint64_t offset, void *buf, size_t len)
> > +qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
> > + uint64_t guest_offset, void *buf, size_t len)
> >  {
> > -return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
> > +return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
> >   qcrypto_block_decrypt);
> 
> and again.
> 
> >  }
> > 
> 
> 


Best regards,
Thanks for the review,
Maxim Levitsky





Re: [Qemu-block] [Qemu-devel] [PATCH 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-06 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:00 -0500, Eric Blake wrote:
> On 9/6/19 1:55 PM, Maxim Levitsky wrote:
> 
> > > > +/*
> > > > + * qcow2_co_encrypt()
> > > > + *
> > > > + * Encrypts a sector size aligned contiguous area
> > > > + *
> > > > + * @host_cluster_offset - on disk offset of the cluster in which
> > > > + *the buffer resides
> > > > + *
> > > > + * @guest_offset - guest (virtual) offset of the buffer
> > > > + * @buf - buffer with the data to encrypt
> > > > + * @len - length of the buffer
> > > > + *
> > > > + * Note that the area is not cluster aligned and might cross a cluster
> > > > + * boundary
> > > 
> > > Umm, how is it possible for a sector to cross a cluster boundary?  All
> > > clusters are sector-aligned, and encryption only works on aligned
> > > sectors.  Oh, I see - if @len is a multiple larger than sector size,
> > > then we have multiple sectors, and then indeed we may cross clusters.
> > > But then the docs about being 'a sector size aligned contiguous area' is
> > > not quite right.
> > 
> > Why? the written area is always both aligned on _sector_ boundary
> > and multiple of the sector size. At least that what I see from
> > the existing asserts.
> 
> I'm thinking it should read something like:
> 
> Encrypts one or more contiguous aligned sectors
> 
> to make it obvious that because there can be multiple sectors, there may
> indeed be a cluster boundary mid-length.  My complaint was that the
> original text made it sound like there was exactly one sector (at which
> point @len is redundant, unless sectors are variably-sized)
> 
I agree with you completely, I will change that.

Best regards,
Maxim Levitsky






Re: [Qemu-block] [Qemu-devel] [PATCH 2/3] block/qcow2: fix the corruption when rebasing luks encrypted files

2019-09-06 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:17 -0500, Eric Blake wrote:
> On 9/6/19 12:32 PM, Maxim Levitsky wrote:
> > This fixes subltle corruption introduced by luks threaded encryption
> 
> subtle

I usually put the commit messages to a spellchecker, but this time
I forgot to do this. I will try not to in the future.

> 
> > in commit 8ac0f15f335
> > 
> > Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922
> > 
> > The corruption happens when we do
> >* write to two or more unallocated clusters at once
> >* write doesn't fully cover nether first nor last cluster
> 
> s/nether/neither/
> 
> or even:
> 
> write doesn't fully cover either the first or the last cluster
I think I didn't wrote the double negative correctly here.
I meant a write that doesn't cover first sector fully and doesn't cover second 
sector.
I'll just write it like that I guess.

> 
> > 
> > In this case, when allocating the new clusters we COW both area
> 
> areas
> 
> > prior to the write and after the write, and we encrypt them.
> > 
> > The above mentioned commit accidently made it so, we encrypt the
> 
> accidentally
> 
> s/made it so, we encrypt/changed the encryption of/
> 
> > second COW are using the physical cluster offset of the first area.
> 
> s/are using/to use/
I actually meant to write 'area' here. I just haven't proofed the commit
message at all I confess. Next time I do better.

> 
> > 
> > Fix this by:
> >  * remove the offset_in_cluster parameter of do_perform_cow_encrypt
> >since it is misleading. That offset can be larger that cluster size.
> >instead just add the start and end COW are offsets to both host and 
> > guest offsets
> >that do_perform_cow_encrypt receives.
> > 
> > *  in do_perform_cow_encrypt, remove the cluster offset from the host_offset
> >And thus pass correctly to the qcow2_co_encrypt, the host cluster offset 
> > and full guest offset
> > 
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/qcow2-cluster.c | 26 +++---
> >  1 file changed, 15 insertions(+), 11 deletions(-)
> > 
> > +++ b/block/qcow2-cluster.c
> > @@ -463,20 +463,20 @@ static int coroutine_fn 
> > do_perform_cow_read(BlockDriverState *bs,
> >  }
> >  
> >  static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
> > -uint64_t 
> > guest_cluster_offset,
> > -uint64_t 
> > host_cluster_offset,
> > -unsigned offset_in_cluster,
> > +uint64_t guest_offset,
> > +uint64_t host_offset,
> >  uint8_t *buffer,
> >  unsigned bytes)
> >  {
> >  if (bytes && bs->encrypted) {
> >  BDRVQcow2State *s = bs->opaque;
> > -assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
> > +assert((guest_offset & ~BDRV_SECTOR_MASK) == 0);
> > +assert((host_offset & ~BDRV_SECTOR_MASK) == 0);
> >  assert((bytes & ~BDRV_SECTOR_MASK) == 0);
> 
> Pre-existing, but we could use QEMU_IS_ALIGNED(x, BDRV_SECTOR_SIZE) for
> slightly more legibility than open-coding the bit operation.
> 
> Neat trick about power-of-2 alignment checks:
> 
> assert(QEMU_IS_ALIGNED(offset_in_cluster | guest_offset |
>host_offset | bytes, BDRV_SECTOR_SIZE));

In my book, a shorter code is almost always better, so why not.
> 
> gives the same result in one assertion.  (I've used it elsewhere in the
> code base, but I'm not opposed to one assert per variable if you think
> batching is too dense.)
> 
> I'll let Dan review the actual code change, but offhand it makes sense
> to me.
> 

Best regards,
Thanks for the review,
Maxim Levitsky





[Qemu-block] [PATCH v2 0/3] Fix qcow2+luks corruption introduced by commit 8ac0f15f335

2019-09-06 Thread Maxim Levitsky
Commit 8ac0f15f335 accidently broke the COW of non changed areas
of newly allocated clusters, when the write spans multiple clusters,
and needs COW both prior and after the write.
This results in 'after' COW area being encrypted with wrong
sector address, which render it corrupted.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

CC: qemu-stable 

V2: grammar, spelling and code style fixes.

Best regards,
    Maxim Levitsky

Maxim Levitsky (3):
  block/qcow2: refactoring of threaded encryption code
  block/qcow2: fix the corruption when rebasing luks encrypted files
  qemu-iotests: Add test for bz #1745922

 block/qcow2-cluster.c  | 30 ---
 block/qcow2-threads.c  | 61 ---
 tests/qemu-iotests/263 | 75 ++
 tests/qemu-iotests/263.out | 19 ++
 tests/qemu-iotests/group   |  1 +
 5 files changed, 160 insertions(+), 26 deletions(-)
 create mode 100755 tests/qemu-iotests/263
 create mode 100644 tests/qemu-iotests/263.out

-- 
2.17.2




[Qemu-block] [PATCH v2 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-06 Thread Maxim Levitsky
This commit tries to clarify few function arguments,
and add comments describing the encrypt/decrypt interface

Signed-off-by: Maxim Levitsky 
---
 block/qcow2-cluster.c | 10 +++
 block/qcow2-threads.c | 61 ++-
 2 files changed, 53 insertions(+), 18 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index f09cc992af..1989b423da 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -463,8 +463,8 @@ static int coroutine_fn 
do_perform_cow_read(BlockDriverState *bs,
 }
 
 static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
-uint64_t src_cluster_offset,
-uint64_t cluster_offset,
+uint64_t guest_cluster_offset,
+uint64_t host_cluster_offset,
 unsigned offset_in_cluster,
 uint8_t *buffer,
 unsigned bytes)
@@ -474,8 +474,8 @@ static bool coroutine_fn 
do_perform_cow_encrypt(BlockDriverState *bs,
 assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
 assert((bytes & ~BDRV_SECTOR_MASK) == 0);
 assert(s->crypto);
-if (qcow2_co_encrypt(bs, cluster_offset,
- src_cluster_offset + offset_in_cluster,
+if (qcow2_co_encrypt(bs, host_cluster_offset,
+ guest_cluster_offset + offset_in_cluster,
  buffer, bytes) < 0) {
 return false;
 }
@@ -496,7 +496,7 @@ static int coroutine_fn 
do_perform_cow_write(BlockDriverState *bs,
 }
 
 ret = qcow2_pre_write_overlap_check(bs, 0,
-cluster_offset + offset_in_cluster, qiov->size, true);
+  cluster_offset + offset_in_cluster, qiov->size, true);
 if (ret < 0) {
 return ret;
 }
diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
index 3b1e63fe41..c3cda0c6a5 100644
--- a/block/qcow2-threads.c
+++ b/block/qcow2-threads.c
@@ -234,15 +234,19 @@ static int qcow2_encdec_pool_func(void *opaque)
 }
 
 static int coroutine_fn
-qcow2_co_encdec(BlockDriverState *bs, uint64_t file_cluster_offset,
-  uint64_t offset, void *buf, size_t len, Qcow2EncDecFunc func)
+qcow2_co_encdec(BlockDriverState *bs, uint64_t host_cluster_offset,
+uint64_t guest_offset, void *buf, size_t len,
+Qcow2EncDecFunc func)
 {
 BDRVQcow2State *s = bs->opaque;
+
+uint64_t offset = s->crypt_physical_offset ?
+host_cluster_offset + offset_into_cluster(s, guest_offset) :
+guest_offset;
+
 Qcow2EncDecData arg = {
 .block = s->crypto,
-.offset = s->crypt_physical_offset ?
-  file_cluster_offset + offset_into_cluster(s, offset) :
-  offset,
+.offset = offset,
 .buf = buf,
 .len = len,
 .func = func,
@@ -251,18 +255,49 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t 
file_cluster_offset,
 return qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
 }
 
+
+/*
+ * qcow2_co_encrypt()
+ *
+ * Encrypts one or more contiguous aligned sectors
+ *
+ * @host_cluster_offset - on disk offset of the first cluster in which
+ * the encrypted data will be written
+ * Used as an initialization vector for encryption
+ *
+ * @guest_offset - guest (virtual) offset of the first sector of the
+ * data to be encrypted
+ * Used as an initialization vector for older, qcow2 native encryption
+ *
+ * @buf - buffer with the data to encrypt
+ * @len - length of the buffer (in sector size multiplies)
+ *
+ * Note that the group of the sectors, don't have to be aligned
+ * on cluster boundary and can also cross a cluster boundary.
+ *
+ *
+ */
 int coroutine_fn
-qcow2_co_encrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
- uint64_t offset, void *buf, size_t len)
+qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
+ uint64_t guest_offset, void *buf, size_t len)
 {
-return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
- qcrypto_block_encrypt);
+return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
+   qcrypto_block_encrypt);
 }
 
+
+/*
+ * qcow2_co_decrypt()
+ *
+ * Decrypts one or more contiguous aligned sectors
+ * Same function as qcow2_co_encrypt
+ *
+ */
+
 int coroutine_fn
-qcow2_co_decrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
- uint64_t offset, void *buf, size_t len)
+qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
+ uint64_t guest_offset, void *buf, size_t len)
 {
-return qcow2_co_encd

[Qemu-block] [PATCH v2 3/3] qemu-iotests: Add test for bz #1745922

2019-09-06 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 tests/qemu-iotests/263 | 75 ++
 tests/qemu-iotests/263.out | 19 ++
 tests/qemu-iotests/group   |  1 +
 3 files changed, 95 insertions(+)
 create mode 100755 tests/qemu-iotests/263
 create mode 100644 tests/qemu-iotests/263.out

diff --git a/tests/qemu-iotests/263 b/tests/qemu-iotests/263
new file mode 100755
index 00..36951ff7b4
--- /dev/null
+++ b/tests/qemu-iotests/263
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+#
+# Test encrypted write that crosses cluster boundary of two unallocated 
clusters
+# Based on 188
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mlevi...@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1   # failure is the default!
+
+_cleanup()
+{
+   _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+
+size=1M
+
+SECRET="secret,id=sec0,data=astrochicken"
+
+_make_test_img --object $SECRET -o 
"encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=64K"
 $size
+
+IMGSPEC="driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG"
+
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
+
+echo
+echo "== reading the whole image =="
+$QEMU_IO --object $SECRET -c "read -P 0 0 $size" --image-opts $IMGSPEC | 
_filter_qemu_io | _filter_testdir
+
+echo
+echo "== write two 512 byte sectors on a cluster boundary =="
+$QEMU_IO --object $SECRET -c "write -P 0xAA 0xFE00 0x400" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== verify that the rest of the image is not changed =="
+$QEMU_IO --object $SECRET -c "read -P 0x00 0x0 0xFE00" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET -c "read -P 0xAA 0x0FE00 0x400" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+$QEMU_IO --object $SECRET -c "read -P 0x00 0x10200 0xEFE00" --image-opts 
$IMGSPEC | _filter_qemu_io | _filter_testdir
+
+_cleanup_test_img
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/263.out b/tests/qemu-iotests/263.out
new file mode 100644
index 00..fa4e4e0e4a
--- /dev/null
+++ b/tests/qemu-iotests/263.out
@@ -0,0 +1,19 @@
+QA output created by 263
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks 
encrypt.key-secret=sec0 encrypt.iter-time=10
+
+== reading whole image ==
+read 1048576/1048576 bytes at offset 0
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== write two 512 byte sectors on a cluster boundary ==
+wrote 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify that the rest of the image is not changed ==
+read 65024/65024 bytes at offset 0
+63.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 982528/982528 bytes at offset 66048
+959.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index d95d556414..be1c4a3baa 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -274,3 +274,4 @@
 257 rw
 258 rw quick
 262 rw quick migration
+263 rw quick
-- 
2.17.2




[Qemu-block] [PATCH v2 2/3] block/qcow2: fix the corruption when rebasing luks encrypted files

2019-09-06 Thread Maxim Levitsky
This fixes subtle corruption introduced by luks threaded encryption
in commit 8ac0f15f335

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

The corruption happens when we do a write that
   * writes to two or more unallocated clusters at once
   * doesn't fully cover the first sector
   * doesn't fully cover the last sector

In this case, when allocating the new clusters we COW both areas
prior to the write and after the write, and we encrypt them.

The above mentioned commit accidentally made it so we encrypt the
second COW area using the physical cluster offset of the first area.

Fix this by:
 * Remove the offset_in_cluster parameter of do_perform_cow_encrypt,
   since it is misleading. That offset can be larger than cluster size
   currently.

   Instead just add the start and the end COW area offsets to both host
   and guest offsets that do_perform_cow_encrypt receives.

*  in do_perform_cow_encrypt, remove the cluster offset from the host_offset,
   and thus pass correctly to the qcow2_co_encrypt, the host cluster offset
   and full guest offset

In the bugreport that was triggered by rebasing a luks image to new,
zero filled base, which lot of such writes, and causes some files
with zero areas to contain garbage there instead.
But as described above it can happen elsewhere as well


Signed-off-by: Maxim Levitsky 
---
 block/qcow2-cluster.c | 28 
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 1989b423da..6df17dd296 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -463,20 +463,20 @@ static int coroutine_fn 
do_perform_cow_read(BlockDriverState *bs,
 }
 
 static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
-uint64_t guest_cluster_offset,
-uint64_t host_cluster_offset,
-unsigned offset_in_cluster,
+uint64_t guest_offset,
+uint64_t host_offset,
 uint8_t *buffer,
 unsigned bytes)
 {
 if (bytes && bs->encrypted) {
 BDRVQcow2State *s = bs->opaque;
-assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
-assert((bytes & ~BDRV_SECTOR_MASK) == 0);
+
+assert(QEMU_IS_ALIGNED(guest_offset | host_offset | bytes,
+   BDRV_SECTOR_SIZE));
 assert(s->crypto);
-if (qcow2_co_encrypt(bs, host_cluster_offset,
- guest_cluster_offset + offset_in_cluster,
- buffer, bytes) < 0) {
+
+if (qcow2_co_encrypt(bs, start_of_cluster(s, host_offset),
+ guest_offset, buffer, bytes) < 0) {
 return false;
 }
 }
@@ -890,11 +890,15 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta 
*m)
 
 /* Encrypt the data if necessary before writing it */
 if (bs->encrypted) {
-if (!do_perform_cow_encrypt(bs, m->offset, m->alloc_offset,
-start->offset, start_buffer,
+if (!do_perform_cow_encrypt(bs,
+m->offset + start->offset,
+m->alloc_offset + start->offset,
+start_buffer,
 start->nb_bytes) ||
-!do_perform_cow_encrypt(bs, m->offset, m->alloc_offset,
-end->offset, end_buffer, end->nb_bytes)) {
+!do_perform_cow_encrypt(bs,
+m->offset + end->offset,
+m->alloc_offset + end->offset,
+end_buffer, end->nb_bytes)) {
 ret = -EIO;
 goto fail;
 }
-- 
2.17.2




Re: [Qemu-block] [Qemu-devel] IOTEST 162

2019-09-10 Thread Maxim Levitsky
On Mon, 2019-09-09 at 13:24 -0400, John Snow wrote:
> 
> On 9/6/19 1:25 PM, Maxim Levitsky wrote:
> > Hi!
> > 
> > I just had a very fun rabbit hole dive, and I want to share it with you.
> > 
> > I notice for some time that iotest 162 fails with that:
> > 
> > -qemu-img: Could not open 'json:{"driver": "nbd", "host": 42}': Failed to 
> > connect socket: Invalid argument
> > +qemu-img: Could not open 'json:{"driver": "nbd", "host": 42}': Failed to 
> > connect socket:
> > Connection timed out
> > 
> > 
> > I didn't bother to report it yet as it started failing more or less when 
> > qemu 4.2 merge window opened,
> > so I thought that maybe something got broken, or maybe something broken in 
> > my environment 
> > (ahem, AF_UNIX path is too long, ahem)
> > 
> > I just didn't had enough time to pay attention to this. Until today.
> > 
> > So I asked Kevin Wolf today, just by the way why I see for some time that
> > iotest 162 fails. for me.
> > 
> > 
> > He told me that it works for him.
> > So I look at the test and I see that it just does
> > 
> > qemu-img info 'json:{"driver": "nbd", "host": 42}'
> > 
> > Supposedly it should fail because 42 is not quoted, and it 
> > doesn't look like a valid host name.
> > 
> > I try with disto's qemu-img (2.11) and I still see that I get the 
> > 'Connection timed out'
> > 
> > Then I ask him to try on his system with '42' quoted and it still passes.
> > 
> > All right - this means that this 42 is parsed. He also mentions that he 
> > uses fedora 29 and I still
> > use fedora 28. So I start a VM with fedora 30, and yep, there it does work 
> > correctly.
> > 
> > All right, that 42 must be parsed as a host name. Yep. 'ping 42' on my 
> > machine tries to ping 0.0.0.42,
> > and in VM exits immediately with 'Invalid argument'
> > All right, lets ping 0.0.0.42. Maybe something in hostname parsing changed, 
> > maybe something parses this on DNS level?
> > Nope, ping 0.0.0.42 works on my machine, fails with invalid argument in 
> > VM...
> > 
> > All right lets strace it
> > 
> > connect(5, {sa_family=AF_INET, sin_port=htons(1025), 
> > sin_addr=inet_addr("0.0.0.42")}, 16) = -1 EINVAL (Invalid argument)
> > 
> > Same thing passes on my machine
> > 
> > Hmm... this is something in the kernel. Maybe something in iptables/etc. I 
> > don't yet know that
> > area that well to be honest.
> > 
> > So I googled a bit and look what I found:
> > 
> > https://lwn.net/Articles/791086/
> > 
> > And yes, while my machine runs fedora 28, it as usual runs vanilla git 
> > master
> > kernel, which I compile from time to time. Currently I am on 5.3.0-rc4+.
> > 
> > So I must say that nothing less but kernel 5.3.0, breaks iotest 162.
> > 
> > I''l prepare a patch soon to fix this.
> 
> I wonder if I am seeing some related problems, though I am running
> 5.2.11 right now and not 5.3.x:
> 
> 162 is failing in this way:
> 
> -qemu-img: Could not open 'json:{"driver": "ssh", "host": "localhost",
> "port": "0", "path": "/foo"}': Failed to connect socket: Connection refused
> -qemu-img: Could not open 'driver=ssh,host=localhost,port=0,path=/foo':
> Failed to connect socket: Connection refused
> +qemu-img: Could not open 'json:{"driver": "ssh", "host": "localhost",
> "port": "0", "path": "/foo"}': Failed to connect socket: Connection
> timed out
> +qemu-img: Could not open 'driver=ssh,host=localhost,port=0,path=/foo':
> Failed to connect socket: Connection timed out
>  qemu-img: Could not open 'json:{"driver": "ssh", "host": "localhost",
> "port": 0.42, "path": "/foo"}': Parameter 'port' expects a number
>  qemu-img: Could not open
> 'driver=ssh,host=localhost,port=0.42,path=/foo': Parameter 'port'
> expects a number

This must be the same issue, although here it fails like that:


[mlevitsk@maximlenovopc ~/qemu/tests/qemu-iotests]$./check -qcow2 162
QEMU  -- 
"/home/mlevitsk/qemu/tests/qemu-iotests/../../x86_64-softmmu/qemu-system-x86_64"
 -nodefaults -display none -machin

Re: [Qemu-block] [PATCH 0/7] block: Generic file creation fallback

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 11:16 +0200, Max Reitz wrote:
> On 05.09.19 15:30, Maxim Levitsky wrote:
> > On Fri, 2019-07-12 at 19:35 +0200, Max Reitz wrote:
> > > Hi,
> > > 
> > > Kevin commented on my RFC, so I got what an RFC wants, and he didn’t
> > > object to the creation fallback part.  So I suppose I can go down that
> > > route at least.  (Which was actually the more important part of the
> > > series.)
> > > 
> > > So as in the RFC, this series adds a fallback path for creating files
> > > (on the protocol layer) if the protocol driver does not support file
> > > creation, but the file already exists.
> > > 
> > 
> > Hi!
> > Do you have any update on this patch series by a chance?
> 
> Unfortunately, no.  I was on PTO, and before that, there was just too
> much else going on.  (And frankly, there’s still too much else going on.)

No problem!


Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH v2 3/3] qemu-iotests: Add test for bz #1745922

2019-09-10 Thread Maxim Levitsky
On Mon, 2019-09-09 at 11:35 +0100, Daniel P. Berrangé wrote:
> On Fri, Sep 06, 2019 at 10:57:50PM +0300, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  tests/qemu-iotests/263 | 75 ++
> >  tests/qemu-iotests/263.out | 19 ++
> >  tests/qemu-iotests/group   |  1 +
> >  3 files changed, 95 insertions(+)
> >  create mode 100755 tests/qemu-iotests/263
> >  create mode 100644 tests/qemu-iotests/263.out
> > 
> > diff --git a/tests/qemu-iotests/263 b/tests/qemu-iotests/263
> > new file mode 100755
> > index 00..36951ff7b4
> > --- /dev/null
> > +++ b/tests/qemu-iotests/263
> > @@ -0,0 +1,75 @@
> > +#!/usr/bin/env bash
> > +#
> > +# Test encrypted write that crosses cluster boundary of two unallocated 
> > clusters
> > +# Based on 188
> > +#
> > +# Copyright (C) 2019 Red Hat, Inc.
> > +#
> > +# This program is free software; you can redistribute it and/or modify
> > +# it under the terms of the GNU General Public License as published by
> > +# the Free Software Foundation; either version 2 of the License, or
> > +# (at your option) any later version.
> > +#
> > +# This program is distributed in the hope that it will be useful,
> > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +# GNU General Public License for more details.
> > +#
> > +# You should have received a copy of the GNU General Public License
> > +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> > +#
> > +
> > +# creator
> > +owner=mlevi...@redhat.com
> > +
> > +seq=`basename $0`
> > +echo "QA output created by $seq"
> > +
> > +status=1   # failure is the default!
> > +
> > +_cleanup()
> > +{
> > +   _cleanup_test_img
> > +}
> > +trap "_cleanup; exit \$status" 0 1 2 3 15
> > +
> > +# get standard environment, filters and checks
> > +. ./common.rc
> > +. ./common.filter
> > +
> > +_supported_fmt qcow2
> > +_supported_proto generic
> > +_supported_os Linux
> > +
> > +
> > +size=1M
> > +
> > +SECRET="secret,id=sec0,data=astrochicken"
> > +
> > +_make_test_img --object $SECRET -o 
> > "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=64K"
> >  $size
> > +
> > +IMGSPEC="driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG"
> > +
> > +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
> > +
> > +echo
> > +echo "== reading the whole image =="
> > +$QEMU_IO --object $SECRET -c "read -P 0 0 $size" --image-opts $IMGSPEC | 
> > _filter_qemu_io | _filter_testdir
> > +
> > +echo
> > +echo "== write two 512 byte sectors on a cluster boundary =="
> > +$QEMU_IO --object $SECRET -c "write -P 0xAA 0xFE00 0x400" --image-opts 
> > $IMGSPEC | _filter_qemu_io | _filter_testdir
> > +
> > +echo
> > +echo "== verify that the rest of the image is not changed =="
> > +$QEMU_IO --object $SECRET -c "read -P 0x00 0x0 0xFE00" --image-opts 
> > $IMGSPEC | _filter_qemu_io | _filter_testdir
> > +$QEMU_IO --object $SECRET -c "read -P 0xAA 0x0FE00 0x400" --image-opts 
> > $IMGSPEC | _filter_qemu_io | _filter_testdir
> > +$QEMU_IO --object $SECRET -c "read -P 0x00 0x10200 0xEFE00" --image-opts 
> > $IMGSPEC | _filter_qemu_io | _filter_testdir
> 
> This tests LUKS encryption, but the code you'r changing/fixing also used
> for the traditionl qcow2 encryption. The difference in IV handling for
> these two methods is what made this code confusing, so I'd like to see
> that the test also covers traditional qcow2 encryption.
This is very good idea. Done.

> 
> Also can you confirm that the test succeeds when run on a qemu
> built against 8c1ecb590497b0349c550607db923972b37f6963  (the change
> immediately before Vladimir's threading series) ?
Yes, the test fails with this commit. It also fails on master and works
with my fix (both encryption case).

> 
> 
> Regards,
> Daniel


Best regards,
Maxim Levitsky





Re: [Qemu-block] [Qemu-stable] [Qemu-devel] [PATCH 2/3] block/qcow2: fix the corruption when rebasing luks encrypted files

2019-09-10 Thread Maxim Levitsky
On Mon, 2019-09-09 at 12:56 +0200, Kevin Wolf wrote:
> Am 06.09.2019 um 21:17 hat Eric Blake geschrieben:
> > > -assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
> > > +assert((guest_offset & ~BDRV_SECTOR_MASK) == 0);
> > > +assert((host_offset & ~BDRV_SECTOR_MASK) == 0);
> > >  assert((bytes & ~BDRV_SECTOR_MASK) == 0);
> > 
> > Pre-existing, but we could use QEMU_IS_ALIGNED(x, BDRV_SECTOR_SIZE) for
> > slightly more legibility than open-coding the bit operation.
> > 
> > Neat trick about power-of-2 alignment checks:
> > 
> > assert(QEMU_IS_ALIGNED(offset_in_cluster | guest_offset |
> >host_offset | bytes, BDRV_SECTOR_SIZE));
> > 
> > gives the same result in one assertion.  (I've used it elsewhere in the
> > code base, but I'm not opposed to one assert per variable if you think
> > batching is too dense.)
> 
> A possible downside of this is that if a user reports an assertion
> failure, you can't tell any more which of the variables ended up in a
> bad state.
> 
> If you're lucky, you can still tell in gdb at least if the bug is
> reproducible, but I wouldn't be surprised if in release builds, half of
> the variables were actually optimised away, so that even this wouldn't
> work.
Agreed. I guess I'll keep the separate asserts anyway after all, even though
I prefer shorter code.


Best regards,
Maxim Levitsky






Re: [Qemu-block] [PATCH v2 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-10 Thread Maxim Levitsky
On Sat, 2019-09-07 at 19:08 +, Vladimir Sementsov-Ogievskiy wrote:
> 06.09.2019 22:57, Maxim Levitsky wrote:
> > This commit tries to clarify few function arguments,
> > and add comments describing the encrypt/decrypt interface
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >   block/qcow2-cluster.c | 10 +++
> >   block/qcow2-threads.c | 61 ++-
> >   2 files changed, 53 insertions(+), 18 deletions(-)
> > 
> > diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
> > index f09cc992af..1989b423da 100644
> > --- a/block/qcow2-cluster.c
> > +++ b/block/qcow2-cluster.c
> > @@ -463,8 +463,8 @@ static int coroutine_fn 
> > do_perform_cow_read(BlockDriverState *bs,
> >   }
> >   
> >   static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
> > -uint64_t 
> > src_cluster_offset,
> > -uint64_t cluster_offset,
> > +uint64_t 
> > guest_cluster_offset,
> > +uint64_t 
> > host_cluster_offset,
> >   unsigned 
> > offset_in_cluster,
> >   uint8_t *buffer,
> >   unsigned bytes)
> > @@ -474,8 +474,8 @@ static bool coroutine_fn 
> > do_perform_cow_encrypt(BlockDriverState *bs,
> >   assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
> >   assert((bytes & ~BDRV_SECTOR_MASK) == 0);
> >   assert(s->crypto);
> > -if (qcow2_co_encrypt(bs, cluster_offset,
> > - src_cluster_offset + offset_in_cluster,
> > +if (qcow2_co_encrypt(bs, host_cluster_offset,
> > + guest_cluster_offset + offset_in_cluster,
> >buffer, bytes) < 0) {
> >   return false;
> >   }
> > @@ -496,7 +496,7 @@ static int coroutine_fn 
> > do_perform_cow_write(BlockDriverState *bs,
> >   }
> >   
> >   ret = qcow2_pre_write_overlap_check(bs, 0,
> > -cluster_offset + offset_in_cluster, qiov->size, true);
> > +  cluster_offset + offset_in_cluster, qiov->size, true);
> 
> 
> Hmm, unrelated hunk.

I was asked to do this to fix coding style, so that wrapped line,
is 4 characters shifted to the right.

> 
> >   if (ret < 0) {
> >   return ret;
> >   }
> > diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
> > index 3b1e63fe41..c3cda0c6a5 100644
> > --- a/block/qcow2-threads.c
> > +++ b/block/qcow2-threads.c
> > @@ -234,15 +234,19 @@ static int qcow2_encdec_pool_func(void *opaque)
> >   }
> >   
> >   static int coroutine_fn
> > -qcow2_co_encdec(BlockDriverState *bs, uint64_t file_cluster_offset,
> > -  uint64_t offset, void *buf, size_t len, Qcow2EncDecFunc 
> > func)
> > +qcow2_co_encdec(BlockDriverState *bs, uint64_t host_cluster_offset,
> > +uint64_t guest_offset, void *buf, size_t len,
> > +Qcow2EncDecFunc func)
> >   {
> >   BDRVQcow2State *s = bs->opaque;
> > +
> > +uint64_t offset = s->crypt_physical_offset ?
> > +host_cluster_offset + offset_into_cluster(s, guest_offset) :
> > +guest_offset;
> > +
> >   Qcow2EncDecData arg = {
> >   .block = s->crypto,
> > -.offset = s->crypt_physical_offset ?
> > -  file_cluster_offset + offset_into_cluster(s, offset) 
> > :
> > -  offset,
> > +.offset = offset,
> >   .buf = buf,
> >   .len = len,
> >   .func = func,
> > @@ -251,18 +255,49 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t 
> > file_cluster_offset,
> >   return qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
> >   }
> >   
> > +
> > +/*
> > + * qcow2_co_encrypt()
> > + *
> > + * Encrypts one or more contiguous aligned sectors
> > + *
> > + * @host_cluster_offset - on disk offset of the first cluster in which
> > + * the encrypted data will be written
> 
> 
> It's not quite right, it's not on disk, but on .file child of qcow2 node, 
> which
> may be any other format or protocol node.. So, I called it 
> file_cluster_offset.
> But I'm OK with new 

Re: [Qemu-block] [PATCH v2 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:17 +, Vladimir Sementsov-Ogievskiy wrote:
> 10.09.2019 15:31, Maxim Levitsky wrote:
> > On Sat, 2019-09-07 at 19:08 +, Vladimir Sementsov-Ogievskiy wrote:
> > > 06.09.2019 22:57, Maxim Levitsky wrote:
> > > > This commit tries to clarify few function arguments,
> > > > and add comments describing the encrypt/decrypt interface
> > > > 
> > > > Signed-off-by: Maxim Levitsky 
> > > > ---
> > > >block/qcow2-cluster.c | 10 +++
> > > >block/qcow2-threads.c | 61 
> > > > ++-
> > > >2 files changed, 53 insertions(+), 18 deletions(-)
> > > > 
> > > > diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
> > > > index f09cc992af..1989b423da 100644
> > > > --- a/block/qcow2-cluster.c
> > > > +++ b/block/qcow2-cluster.c
> > > > @@ -463,8 +463,8 @@ static int coroutine_fn 
> > > > do_perform_cow_read(BlockDriverState *bs,
> > > >}
> > > >
> > > >static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
> > > > -uint64_t 
> > > > src_cluster_offset,
> > > > -uint64_t 
> > > > cluster_offset,
> > > > +uint64_t 
> > > > guest_cluster_offset,
> > > > +uint64_t 
> > > > host_cluster_offset,
> > > >unsigned 
> > > > offset_in_cluster,
> > > >uint8_t *buffer,
> > > >unsigned bytes)
> > > > @@ -474,8 +474,8 @@ static bool coroutine_fn 
> > > > do_perform_cow_encrypt(BlockDriverState *bs,
> > > >assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
> > > >assert((bytes & ~BDRV_SECTOR_MASK) == 0);
> > > >assert(s->crypto);
> > > > -if (qcow2_co_encrypt(bs, cluster_offset,
> > > > - src_cluster_offset + offset_in_cluster,
> > > > +if (qcow2_co_encrypt(bs, host_cluster_offset,
> > > > + guest_cluster_offset + offset_in_cluster,
> > > > buffer, bytes) < 0) {
> > > >return false;
> > > >}
> > > > @@ -496,7 +496,7 @@ static int coroutine_fn 
> > > > do_perform_cow_write(BlockDriverState *bs,
> > > >}
> > > >
> > > >ret = qcow2_pre_write_overlap_check(bs, 0,
> > > > -cluster_offset + offset_in_cluster, qiov->size, true);
> > > > +  cluster_offset + offset_in_cluster, qiov->size, true);
> > > 
> > > 
> > > Hmm, unrelated hunk.
> > 
> > I was asked to do this to fix coding style, so that wrapped line,
> > is 4 characters shifted to the right.
> 
> AFAIS, Eric asked only about qcow2_co_encdec calls and definition.. It's OK 
> to fix style in code
> you touch in you patch anyway, but no reason to fix style somewhere else, it 
> dirties patch for
> no reason, making it more difficult (a bit, but still) to review and more 
> difficult to backport..
All right, then I'll drop that change. I kind of agree with this, but I also 
didn't mind doing these fixes
either.

> 
> > 
> > > 
> > > >if (ret < 0) {
> > > >return ret;
> > > >}
> > > > diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
> > > > index 3b1e63fe41..c3cda0c6a5 100644
> > > > --- a/block/qcow2-threads.c
> > > > +++ b/block/qcow2-threads.c
> > > > @@ -234,15 +234,19 @@ static int qcow2_encdec_pool_func(void *opaque)
> > > >}
> > > >
> > > >static int coroutine_fn
> > > > -qcow2_co_encdec(BlockDriverState *bs, uint64_t file_cluster_offset,
> > > > -  uint64_t offset, void *buf, size_t len, 
> > > > Qcow2EncDecFunc func)
> > > > +qcow2_co_encdec(BlockDriverState *bs, uint64_t host_cluster_offset,
> > > > +uint64_t guest_offset, void *buf, size_t len,
> > > > +Qcow2EncDecFunc func)
> > >

Re: [Qemu-block] [Qemu-devel] [PATCH 2/2] block/nvme: add support for discard

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 16:49 +0200, Paolo Bonzini wrote:
> On 09/09/19 19:03, John Snow wrote:
> > 
> > 
> > On 9/9/19 5:25 AM, Max Reitz wrote:
> > > On 05.09.19 19:27, John Snow wrote:
> > > 
> > > [...]
> > > 
> > > > You also probably require review (or at least an ACK) from Keith Busch
> > > > who maintains this file.
> > > 
> > > Keith actually maintains the NVMe guest device; technically, Fam is the
> > > NVMe block driver maintainer.
> > 
> > W h o o p s. Thanks for correcting me.
> > 
> > Well, if it's Fam -- he seems a little busier lately -- it's probably
> > not so crucial to gate on his approval. I thought it'd be nice to at
> > least get an ACK from someone who has used this module before, because I
> > haven't -- I was just giving some style review to help push it along.
> > 
> > (On that note, if you felt like my style review was wrong or isn't worth
> > doing -- it is always perfectly fair to just say so, along with some
> > reason as to why you won't -- that way patches won't rot on the list
> > when people may have gotten the impression that a V2 is warranted.)
Absolutely not, your review was fine! I just was/is a bit lazy to send next 
version of the patches
before I get some kind of indication if anything else is needed for this to be 
merged,
since the module doesn't have currently an active maintainer.


> 
> Looks good to me with the changes you pointed out (especially res30;
> leaving out the unused macros is not so important).

All right, I'll send an updated version of those two patches soon.

Best regards,
Maxim Levitsky





Re: [Qemu-block] [PATCH v2 1/7] curl: Keep pointer to the CURLState in CURLSocket

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:41 +0200, Max Reitz wrote:
> A follow-up patch will make curl_multi_do() and curl_multi_read() take a
> CURLSocket instead of the CURLState.  They still need the latter,
> though, so add a pointer to it to the former.
> 
> Cc: qemu-sta...@nongnu.org
> Signed-off-by: Max Reitz 
> Reviewed-by: John Snow 
> ---
>  block/curl.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/block/curl.c b/block/curl.c
> index d4c8e94f3e..92dc2f630e 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -80,6 +80,7 @@ static CURLMcode __curl_multi_socket_action(CURLM 
> *multi_handle,
>  #define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5
>  
>  struct BDRVCURLState;
> +struct CURLState;
>  
>  static bool libcurl_initialized;
>  
> @@ -97,6 +98,7 @@ typedef struct CURLAIOCB {
>  
>  typedef struct CURLSocket {
>  int fd;
> +struct CURLState *state;
>  QLIST_ENTRY(CURLSocket) next;
>  } CURLSocket;
>  
> @@ -180,6 +182,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int 
> action,
>  if (!socket) {
>  socket = g_new0(CURLSocket, 1);
>  socket->fd = fd;
> +socket->state = state;
>  QLIST_INSERT_HEAD(&state->sockets, socket, next);
>  }
>  socket = NULL;

Reviewed-by: Maxim Levitsky 

Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH v2 2/7] curl: Keep *socket until the end of curl_sock_cb()

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:41 +0200, Max Reitz wrote:
> This does not really change anything, but it makes the code a bit easier
> to follow once we use @socket as the opaque pointer for
> aio_set_fd_handler().
> 
> Cc: qemu-sta...@nongnu.org
> Signed-off-by: Max Reitz 
> ---
>  block/curl.c | 10 +-
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/block/curl.c b/block/curl.c
> index 92dc2f630e..95d7b77dc0 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -172,10 +172,6 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, 
> int action,
>  
>  QLIST_FOREACH(socket, &state->sockets, next) {
>  if (socket->fd == fd) {
> -if (action == CURL_POLL_REMOVE) {
> -QLIST_REMOVE(socket, next);
> -g_free(socket);
> -}
>  break;
>  }
>  }
> @@ -185,7 +181,6 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int 
> action,
>  socket->state = state;
>  QLIST_INSERT_HEAD(&state->sockets, socket, next);
>  }
> -socket = NULL;
>  
>  trace_curl_sock_cb(action, (int)fd);
>  switch (action) {
> @@ -207,6 +202,11 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, 
> int action,
>  break;
>  }
>  
> +if (action == CURL_POLL_REMOVE) {
> +QLIST_REMOVE(socket, next);
> +g_free(socket);
> +}
> +
>  return 0;
>  }
>  
Reviewed-by: Maxim Levitsky 

Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH v2 3/7] curl: Check completion in curl_multi_do()

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:41 +0200, Max Reitz wrote:
> While it is more likely that transfers complete after some file
> descriptor has data ready to read, we probably should not rely on it.
> Better be safe than sorry and call curl_multi_check_completion() in
> curl_multi_do(), too, just like it is done in curl_multi_read().
> 
> With this change, curl_multi_do() and curl_multi_read() are actually the
> same, so drop curl_multi_read() and use curl_multi_do() as the sole FD
> handler.

I understand the reasoning, but I still a bit worry that this
could paper over some bug/race in the future.
If curl asks us only to deal with write, that would mean
that it doesn't expect any data to be received.

Do you by a chance have an example, of this patch
affecting the code? Maybe when a unexpected error reply
is received from the server?

I don't really know the CURL library, so I probably missed
something important.

Other than that,
Reviewed-by: Maxim Levitsky 


Best regards,
Maxim Levitsky


> 
> Signed-off-by: Max Reitz 
> ---
>  block/curl.c | 14 ++
>  1 file changed, 2 insertions(+), 12 deletions(-)
> 
> diff --git a/block/curl.c b/block/curl.c
> index 95d7b77dc0..5838afef99 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -139,7 +139,6 @@ typedef struct BDRVCURLState {
>  
>  static void curl_clean_state(CURLState *s);
>  static void curl_multi_do(void *arg);
> -static void curl_multi_read(void *arg);
>  
>  #ifdef NEED_CURL_TIMER_CALLBACK
>  /* Called from curl_multi_do_locked, with s->mutex held.  */
> @@ -186,7 +185,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int 
> action,
>  switch (action) {
>  case CURL_POLL_IN:
>  aio_set_fd_handler(s->aio_context, fd, false,
> -   curl_multi_read, NULL, NULL, state);
> +   curl_multi_do, NULL, NULL, state);
>  break;
>  case CURL_POLL_OUT:
>  aio_set_fd_handler(s->aio_context, fd, false,
> @@ -194,7 +193,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int 
> action,
>  break;
>  case CURL_POLL_INOUT:
>  aio_set_fd_handler(s->aio_context, fd, false,
> -   curl_multi_read, curl_multi_do, NULL, state);
> +   curl_multi_do, curl_multi_do, NULL, state);
>  break;
>  case CURL_POLL_REMOVE:
>  aio_set_fd_handler(s->aio_context, fd, false,
> @@ -416,15 +415,6 @@ static void curl_multi_do(void *arg)
>  {
>  CURLState *s = (CURLState *)arg;
>  
> -qemu_mutex_lock(&s->s->mutex);
> -curl_multi_do_locked(s);
> -qemu_mutex_unlock(&s->s->mutex);
> -}
> -
> -static void curl_multi_read(void *arg)
> -{
> -CURLState *s = (CURLState *)arg;
> -
>  qemu_mutex_lock(&s->s->mutex);
>  curl_multi_do_locked(s);
>  curl_multi_check_completion(s->s);





Re: [Qemu-block] [PATCH v2 6/7] curl: Handle success in multi_check_completion

2019-09-10 Thread Maxim Levitsky
t; -state->acb[i] = NULL;
> -qemu_mutex_unlock(&s->mutex);
> -aio_co_wake(acb->co);
> -qemu_mutex_lock(&s->mutex);
> +if (acb->end - acb->start < acb->bytes) {
> +size_t offset = acb->end - acb->start;
> +qemu_iovec_memset(acb->qiov, offset, 0,
> +  acb->bytes - offset);
> +}
Original code was memsetting the tail of the buffer before waking up the 
coroutine.
Is this change intended?

aio_co_wake doesn't enter the co-routine if already in coroutine, but
I think that this is an aio fd handler with isn't run in co-routine itself,
so the callback could run with not yet ready data.


>  }
> +
> +acb->ret = error ? -EIO : 0;
> +state->acb[i] = NULL;
> +qemu_mutex_unlock(&s->mutex);
> +aio_co_wake(acb->co);
> +qemu_mutex_lock(&s->mutex);
>  }
>  
>  curl_clean_state(state);


Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH v2 4/7] curl: Pass CURLSocket to curl_multi_do()

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:41 +0200, Max Reitz wrote:
> curl_multi_do_locked() currently marks all sockets as ready.  That is
> not only inefficient, but in fact unsafe (the loop is).  A follow-up
> patch will change that, but to do so, curl_multi_do_locked() needs to
> know exactly which socket is ready; and that is accomplished by this
> patch here.
> 
> Cc: qemu-sta...@nongnu.org
> Signed-off-by: Max Reitz 
> ---
>  block/curl.c | 20 +++-
>  1 file changed, 11 insertions(+), 9 deletions(-)
> 
> diff --git a/block/curl.c b/block/curl.c
> index 5838afef99..cf2686218d 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -185,15 +185,15 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, 
> int action,
>  switch (action) {
>  case CURL_POLL_IN:
>  aio_set_fd_handler(s->aio_context, fd, false,
> -   curl_multi_do, NULL, NULL, state);
> +   curl_multi_do, NULL, NULL, socket);
>  break;
>  case CURL_POLL_OUT:
>  aio_set_fd_handler(s->aio_context, fd, false,
> -   NULL, curl_multi_do, NULL, state);
> +   NULL, curl_multi_do, NULL, socket);
>  break;
>  case CURL_POLL_INOUT:
>  aio_set_fd_handler(s->aio_context, fd, false,
> -   curl_multi_do, curl_multi_do, NULL, state);
> +   curl_multi_do, curl_multi_do, NULL, socket);
>  break;
>  case CURL_POLL_REMOVE:
>  aio_set_fd_handler(s->aio_context, fd, false,
> @@ -392,9 +392,10 @@ static void curl_multi_check_completion(BDRVCURLState *s)
>  }
>  
>  /* Called with s->mutex held.  */
> -static void curl_multi_do_locked(CURLState *s)
> +static void curl_multi_do_locked(CURLSocket *ready_socket)
>  {
>  CURLSocket *socket, *next_socket;
> +CURLState *s = ready_socket->state;
>  int running;
>  int r;
>  
> @@ -413,12 +414,13 @@ static void curl_multi_do_locked(CURLState *s)
>  
>  static void curl_multi_do(void *arg)
>  {
> -CURLState *s = (CURLState *)arg;
> +CURLSocket *socket = arg;
> +BDRVCURLState *s = socket->state->s;
>  
> -qemu_mutex_lock(&s->s->mutex);
> -curl_multi_do_locked(s);
> -curl_multi_check_completion(s->s);
> -qemu_mutex_unlock(&s->s->mutex);
> +qemu_mutex_lock(&s->mutex);
> +curl_multi_do_locked(socket);
> +curl_multi_check_completion(s);
> +qemu_mutex_unlock(&s->mutex);
>  }
>  
>  static void curl_multi_timeout_do(void *arg)

Reviewed-by: Maxim Levitsky 
Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH v2 7/7] curl: Check curl_multi_add_handle()'s return code

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:41 +0200, Max Reitz wrote:
> If we had done that all along, debugging would have been much simpler.
> (Also, I/O errors are better than hangs.)
> 
> Signed-off-by: Max Reitz 
> ---
>  block/curl.c | 8 +++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/block/curl.c b/block/curl.c
> index c343c7ed3d..f86299378e 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -882,7 +882,13 @@ static void curl_setup_preadv(BlockDriverState *bs, 
> CURLAIOCB *acb)
>  trace_curl_setup_preadv(acb->bytes, start, state->range);
>  curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
>  
> -curl_multi_add_handle(s->multi, state->curl);
> +if (curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) {
> +state->acb[0] = NULL;
> +acb->ret = -EIO;
> +
> +curl_clean_state(state);
> +goto out;
> +}
>  
>  /* Tell curl it needs to kick things off */
>  curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);

Checking the return values is always a very good idea.
I would myself make this patch #1 in the series, since it doesn't
depend on others and it itself a bugfix.
But this is my style, so I don't mind if you leave this as is.

Reviewed-by: Maxim Levitsky 

Best regards,
Maxim Levitsky




Re: [Qemu-block] [PATCH v2 5/7] curl: Report only ready sockets

2019-09-10 Thread Maxim Levitsky
On Tue, 2019-09-10 at 14:41 +0200, Max Reitz wrote:
> Instead of reporting all sockets to cURL, only report the one that has
> caused curl_multi_do_locked() to be called.  This lets us get rid of the
> QLIST_FOREACH_SAFE() list, which was actually wrong: SAFE foreaches are
> only safe when the current element is removed in each iteration.  If it
> possible for the list to be concurrently modified, we cannot guarantee
> that only the current element will be removed.  Therefore, we must not
> use QLIST_FOREACH_SAFE() here.
> 
> Fixes: ff5ca1664af85b24a4180d595ea6873fd3deac57
> Cc: qemu-sta...@nongnu.org
> Signed-off-by: Max Reitz 
> ---
>  block/curl.c | 17 ++---
>  1 file changed, 6 insertions(+), 11 deletions(-)
> 
> diff --git a/block/curl.c b/block/curl.c
> index cf2686218d..fd70f1ebc4 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -392,24 +392,19 @@ static void curl_multi_check_completion(BDRVCURLState 
> *s)
>  }
>  
>  /* Called with s->mutex held.  */
> -static void curl_multi_do_locked(CURLSocket *ready_socket)
> +static void curl_multi_do_locked(CURLSocket *socket)
Here you revert the variable name change you had in previous commit

>  {
> -CURLSocket *socket, *next_socket;
> -CURLState *s = ready_socket->state;
> +BDRVCURLState *s = socket->state->s;
>  int running;
>  int r;
>  
> -if (!s->s->multi) {
> +if (!s->multi) {
>  return;
>  }
>  
> -/* Need to use _SAFE because curl_multi_socket_action() may trigger
> - * curl_sock_cb() which might modify this list */
> -QLIST_FOREACH_SAFE(socket, &s->sockets, next, next_socket) {
> -do {
> -r = curl_multi_socket_action(s->s->multi, socket->fd, 0, 
> &running);
> -} while (r == CURLM_CALL_MULTI_PERFORM);
> -}
> +do {
> +r = curl_multi_socket_action(s->multi, socket->fd, 0, &running);
> +} while (r == CURLM_CALL_MULTI_PERFORM);
>  }
>  
>  static void curl_multi_do(void *arg)

Other than that nitpick,
Reviewed-by: Maxim Levitsky 

Best regards,
Maxim Levitsky




Re: [Qemu-block] [Qemu-devel] [PATCH v2 09/13] qcrypto-block: extract check and parse header

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:11 +0100, Daniel P. Berrangé wrote:
> On Mon, Aug 26, 2019 at 04:50:59PM +0300, Maxim Levitsky wrote:
> > This is just to make qcrypto_block_luks_open more
> > reasonable in size.
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  crypto/block-luks.c | 254 +---
> >  1 file changed, 146 insertions(+), 108 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index b4dc6fc899..cc9a52c9af 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -508,6 +508,148 @@ fail:
> >  return ret;
> >  }
> >  
> > +/*
> > + * Does basic sanity checks on the LUKS header
> > + */
> > +static int
> > +qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
> > +{
> > +int ret;
> > +
> > +if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
> > +   QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
> > +error_setg(errp, "Volume is not in LUKS format");
> > +ret = -EINVAL;
> > +goto fail;
> > +}
> 
> Just 'return -1' here immediately - don't return an errno, as we're
> using Error objects for reporting.
> 
> > +
> > +if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
> > +error_setg(errp, "LUKS version %" PRIu32 " is not supported",
> > +   luks->header.version);
> > +ret = -ENOTSUP;
> > +goto fail;
> > +}
> > +
> > +return 0;
> > +fail:
> > +return ret;
> > +}
> > +
> > +/*
> > + * Parses the crypto parameters that are stored in the LUKS header
> > + */
> > +
> > +static int
> > +qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp)
> > +{
> > +g_autofree char *cipher_mode = g_strdup(luks->header.cipher_mode);
> > +char *ivgen_name, *ivhash_name;
> > +int ret = -1;
> > +Error *local_err = NULL;
> > +
> > +/*
> > + * The cipher_mode header contains a string that we have
> > + * to further parse, of the format
> > + *
> > + *-[:]
> > + *
> > + * eg  cbc-essiv:sha256, cbc-plain64
> > + */
> > +ivgen_name = strchr(cipher_mode, '-');
> > +if (!ivgen_name) {
> > +ret = -EINVAL;
> 
> Again, don't use errnos - just return -1 in this method.
> 
> > +error_setg(errp, "Unexpected cipher mode string format %s",
> > +   luks->header.cipher_mode);
> > +goto out;
> > +}
> > +*ivgen_name = '\0';
> > +ivgen_name++;
> > +
> > +ivhash_name = strchr(ivgen_name, ':');
> > +if (!ivhash_name) {
> > +luks->ivgen_hash_alg = 0;
> > +} else {
> > +*ivhash_name = '\0';
> > +ivhash_name++;
> > +
> > +luks->ivgen_hash_alg = 
> > qcrypto_block_luks_hash_name_lookup(ivhash_name,
> > +   
> > &local_err);
> > +if (local_err) {
> > +ret = -ENOTSUP;
> > +error_propagate(errp, local_err);
> > +goto out;
> > +}
> > +}
> > +
> > +luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
> > +  &local_err);
> > +if (local_err) {
> > +ret = -ENOTSUP;
> > +error_propagate(errp, local_err);
> > +goto out;
> > +}
> > +
> > +luks->cipher_alg =
> > +qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
> > +  luks->cipher_mode,
> > +  
> > luks->header.master_key_len,
> > +  &local_err);
> > +if (local_err) {
> > +ret = -ENOTSUP;
> > +error_propagate(errp, local_err);
> > +goto out;
> > +}
> > +
> > +luks->hash_alg =
> > +qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
> > +   &local_err);
> > +if (local_err) {
> > +ret = -ENOTSUP;
> > +error_propagate(errp, local_err);
&

Re: [Qemu-block] [Qemu-devel] [PATCH v2 11/13] qcrypto-luks: refactoring: simplify the math used for keyslot locations

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:17 +0100, Daniel P. Berrangé wrote:
> On Mon, Aug 26, 2019 at 04:51:01PM +0300, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  crypto/block-luks.c | 64 +
> >  1 file changed, 41 insertions(+), 23 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index d713125925..6a43d97ce5 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -409,6 +409,32 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
> > cipher,
> >  }
> >  }
> >  
> > +/*
> > + * Returns number of sectors needed to store the key material
> > + * given number of anti forensic stripes
> > + */
> > +static int
> > +qcrypto_block_luks_splitkeylen_sectors(const QCryptoBlockLUKS *luks,
> > +   unsigned int stripes)
> > +{
> > +/*
> > + * This calculation doesn't match that shown in the spec,
> > + * but instead follows the cryptsetup implementation.
> > + */
> > +
> > +size_t header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > +QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> 
> The caller already calculated that so just pass it in

All right, no problem.

> 
> > +
> > +size_t splitkeylen = luks->header.master_key_len * stripes;
> > +
> > +/* First align the key material size to block size*/
> > +size_t splitkeylen_sectors =
> > +DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);
> > +
> > +/* Then also align the key material size to the size of the header */
> > +return ROUND_UP(splitkeylen_sectors, header_sectors);
> > +}
> > +
> >  /*
> >   * Stores the main LUKS header, taking care of endianess
> >   */
> > @@ -1151,7 +1177,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >  QCryptoBlockCreateOptionsLUKS luks_opts;
> >  Error *local_err = NULL;
> >  g_autofree uint8_t *masterkey = NULL;
> > -size_t splitkeylen = 0;
> > +size_t header_sectors;
> > +size_t split_key_sectors;
> >  size_t i;
> >  g_autofree char *password;
> >  const char *cipher_alg;
> > @@ -1370,37 +1397,28 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >  goto error;
> >  }
> >  
> > +/* start with the sector that follows the header*/
> > +header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > +QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> > +
> > +split_key_sectors =
> > +qcrypto_block_luks_splitkeylen_sectors(luks,
> > +   QCRYPTO_BLOCK_LUKS_STRIPES);
> >  
> > -/* Although LUKS has multiple key slots, we're just going
> > - * to use the first key slot */
> > -splitkeylen = luks->header.master_key_len * QCRYPTO_BLOCK_LUKS_STRIPES;
> >  for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
> > -luks->header.key_slots[i].active = 
> > QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> > -luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
> > +QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
> > +slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
> >  
> > -/* This calculation doesn't match that shown in the spec,
> > - * but instead follows the cryptsetup implementation.
> > - */
> > -luks->header.key_slots[i].key_offset_sector =
> > -(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > - QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
> > -(ROUND_UP(DIV_ROUND_UP(splitkeylen, 
> > QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
> > -  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > -   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
> > +slot->key_offset_sector = header_sectors + i * split_key_sectors;
> > +slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
> >  }
> >  
> > -
> >  /* The total size of the LUKS headers is the partition header + key
> >   * slot headers, rounded up to the nearest sector, combined with
> >   * the size of each master key material region, also rounded up
> >   * to the nearest sector */
> > -luks->header.payload_offset_sector =
> > -(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > - QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
> > -(ROUND_UP(DIV_ROUND_UP(splitkeylen, 
> > QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
> > -  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
> > -   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
> > - QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> > +luks->header.payload_offset_sector = header_sectors +
> > +QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors;
> >  
> >  block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
> >  block->payload_offset = luks->header.payload_offset_sector *
> 
> Reviewed-by: Daniel P. Berrangé 
> 
> Regards,
> Daniel

Best regards,
Maxim Levitsky





Re: [Qemu-block] [Qemu-devel] [PATCH v2 13/13] qcrypto-luks: implement more rigorous header checking

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:34 +0100, Daniel P. Berrangé wrote:
> On Mon, Aug 26, 2019 at 04:51:03PM +0300, Maxim Levitsky wrote:
> > Check that keyslots don't overlap with the data,
> > and check that keyslots don't overlap with each other.
> > (this is done using naive O(n^2) nested loops,
> > but since there are just 8 keyslots, this doesn't really matter.
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  crypto/block-luks.c | 46 +++--
> >  1 file changed, 44 insertions(+), 2 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index db0fb764b4..fdf4c41f8a 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -541,12 +541,12 @@ fail:
> >  static int
> >  qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
> >  {
> > -int ret;
> > +int ret = -EINVAL;
> 
> As before, no need to use errnos, just return -1 immediately.
> 
> > +size_t i, j;
> >  
> >  if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
> > QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
> >  error_setg(errp, "Volume is not in LUKS format");
> > -ret = -EINVAL;
> >  goto fail;
> >  }
> >  
> > @@ -557,6 +557,48 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS 
> > *luks, Error **errp)
> >  goto fail;
> >  }
> >  
> > +/* Check all keyslots for corruption  */
> > +for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
> > +
> > +const QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
> > +unsigned int start1 = slot1->key_offset_sector;
> > +unsigned int len1 =
> > +qcrypto_block_luks_splitkeylen_sectors(luks, slot1->stripes);
> > +
> > +if (slot1->stripes == 0) {
> > +error_setg(errp, "Keyslot %zu is corrupted (stripes == 0)", i);
> > +goto fail;
> > +}
> 
> How about checking stripes != QCRYPTO_BLOCK_LUKS_STRIPES because
> AFAIR, you're required to use 4k stripes in luks v1.
I see that spec does allow for user defined number of stripes.

> 
> Also how about  checkingiters >= MIN_SLOT_KEY_ITERS
Also this is only a suggested minimum

> 
> > +
> > +if (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
> > +slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
> 
> Align the two lines with (
Done
> 
> > +error_setg(errp,
> > +   "Keyslot %zu state (active/disable) is corrupted", 
> > i);
> > +goto fail;
> > +}
> > +
> > +if (start1 + len1 > luks->header.payload_offset_sector) {
> > +error_setg(errp,
> > +   "Keyslot %zu is overlapping with the encrypted 
> > payload",
> > +   i);
> > +goto fail;
> > +}
> > +
> > +for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
> > +const QCryptoBlockLUKSKeySlot *slot2 = 
> > &luks->header.key_slots[j];
> > +unsigned int start2 = slot2->key_offset_sector;
> > +    unsigned int len2 =
> > +qcrypto_block_luks_splitkeylen_sectors(luks, 
> > slot2->stripes);
> > +
> > +if (start1 + len1 > start2 && start2 + len2 > start1) {
> > +error_setg(errp,
> > +   "Keyslots %zu and %zu are overlapping in the 
> > header",
> > +   i, j);
> > +goto fail;
> > +}
> > +}
> > +
> > +}
> >  return 0;
> >  fail:
> >  return ret;
> > -- 
> > 2.17.2
> > 
> 
> Regards,
> Daniel

Best regards,
Maxim Levitsky




[Qemu-block] [PATCH 04/12] qcrypto-luks: simplify masterkey and masterkey length

2019-09-12 Thread Maxim Levitsky
Let the caller allocate masterkey
Always use master key len from the header

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block-luks.c | 44 +---
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 25f8a9f1c4..9e59a791a6 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -419,7 +419,6 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 QCryptoCipherAlgorithm ivcipheralg,
 QCryptoHashAlgorithm ivhash,
 uint8_t *masterkey,
-size_t masterkeylen,
 QCryptoBlockReadFunc readfunc,
 void *opaque,
 Error **errp)
@@ -438,9 +437,9 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 return 0;
 }
 
-splitkeylen = masterkeylen * slot->stripes;
+splitkeylen = luks->header.master_key_len * slot->stripes;
 splitkey = g_new0(uint8_t, splitkeylen);
-possiblekey = g_new0(uint8_t, masterkeylen);
+possiblekey = g_new0(uint8_t, luks->header.master_key_len);
 
 /*
  * The user password is used to generate a (possible)
@@ -453,7 +452,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
(const uint8_t *)password, strlen(password),
slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
slot->iterations,
-   possiblekey, masterkeylen,
+   possiblekey, luks->header.master_key_len,
errp) < 0) {
 return -1;
 }
@@ -478,7 +477,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 /* Setup the cipher/ivgen that we'll use to try to decrypt
  * the split master key material */
 cipher = qcrypto_cipher_new(cipheralg, ciphermode,
-possiblekey, masterkeylen,
+possiblekey, luks->header.master_key_len,
 errp);
 if (!cipher) {
 return -1;
@@ -489,7 +488,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 ivgen = qcrypto_ivgen_new(ivalg,
   ivcipheralg,
   ivhash,
-  possiblekey, masterkeylen,
+  possiblekey, luks->header.master_key_len,
   errp);
 if (!ivgen) {
 return -1;
@@ -519,7 +518,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * it back together to get the actual master key.
  */
 if (qcrypto_afsplit_decode(hash,
-   masterkeylen,
+   luks->header.master_key_len,
slot->stripes,
splitkey,
masterkey,
@@ -537,11 +536,13 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * header
  */
 if (qcrypto_pbkdf2(hash,
-   masterkey, masterkeylen,
+   masterkey,
+   luks->header.master_key_len,
luks->header.master_key_salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
luks->header.master_key_iterations,
-   keydigest, G_N_ELEMENTS(keydigest),
+   keydigest,
+   G_N_ELEMENTS(keydigest),
errp) < 0) {
 return -1;
 }
@@ -574,8 +575,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 QCryptoIVGenAlgorithm ivalg,
 QCryptoCipherAlgorithm ivcipheralg,
 QCryptoHashAlgorithm ivhash,
-uint8_t **masterkey,
-size_t *masterkeylen,
+uint8_t *masterkey,
 QCryptoBlockReadFunc readfunc,
 void *opaque,
 Error **errp)
@@ -584,9 +584,6 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 size_t i;
 int rv;
 
-*masterkey = g_new0(uint8_t, luks->header.master_key_len);
-*masterkeylen = luks->header.master_key_len;
-
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 rv = qcrypto_block_luks_load_key(block,
  &luks->header.key_slots[i],
@@ -597,8 +594,7 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
  ivalg,
  ivcipheralg,
  ivhash,
- *masterkey,
- *masterkeylen,
+ 

[Qemu-block] [PATCH 00/12] crypto/luks: preparation for encryption key managment

2019-09-12 Thread Maxim Levitsky
Hi!

This patch series is the refactoring/preparation part of the
former patch series I had sent which adds support for luks
key management.

This series includes all the feedback from the last review iteration
and one new patch that removes errno values from .open
callback of luks crypto driver since these values are not
used anyway.

Best regards,
Maxim Levitsky

Maxim Levitsky (12):
  block-crypto: misc refactoring
  qcrypto-luks: rename some fields in QCryptoBlockLUKSHeader
  qcrypto-luks: don't overwrite cipher_mode in header
  qcrypto-luks: simplify masterkey and masterkey length
  qcrypto-luks: pass keyslot index rather that pointer to the keyslot
  qcrypto-luks: use the parsed encryption settings in QCryptoBlockLUKS
  qcrypto-luks: purge unused error codes from open callback
  qcrypto-luks: extract store and load header
  qcrypto-luks: extract check and parse header
  qcrypto-luks: extract store key function
  qcrypto-luks: simplify the math used for keyslot locations
  qcrypto-luks: more rigorous header checking

 block/crypto.c  |   12 +-
 crypto/block-luks.c | 1023 +--
 2 files changed, 602 insertions(+), 433 deletions(-)

-- 
2.17.2




[Qemu-block] [PATCH 02/12] qcrypto-luks: rename some fields in QCryptoBlockLUKSHeader

2019-09-12 Thread Maxim Levitsky
* key_bytes -> master_key_len
* payload_offset = payload_offset_sector (to emphasise that this isn't byte 
offset)
* key_offset -> key_offset_sector - same as above for luks slots

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block-luks.c | 91 +++--
 1 file changed, 47 insertions(+), 44 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 743949adbf..f12fa2d270 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -143,7 +143,7 @@ struct QCryptoBlockLUKSKeySlot {
 /* salt for PBKDF2 */
 uint8_t salt[QCRYPTO_BLOCK_LUKS_SALT_LEN];
 /* start sector of key material */
-uint32_t key_offset;
+uint32_t key_offset_sector;
 /* number of anti-forensic stripes */
 uint32_t stripes;
 };
@@ -172,10 +172,10 @@ struct QCryptoBlockLUKSHeader {
 char hash_spec[QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN];
 
 /* start offset of the volume data (in 512 byte sectors) */
-uint32_t payload_offset;
+uint32_t payload_offset_sector;
 
 /* Number of key bytes */
-uint32_t key_bytes;
+uint32_t master_key_len;
 
 /* master key checksum after PBKDF2 */
 uint8_t master_key_digest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN];
@@ -466,7 +466,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * then encrypted.
  */
 rv = readfunc(block,
-  slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+  slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
   splitkey, splitkeylen,
   opaque,
   errp);
@@ -584,8 +584,8 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 size_t i;
 int rv;
 
-*masterkey = g_new0(uint8_t, luks->header.key_bytes);
-*masterkeylen = luks->header.key_bytes;
+*masterkey = g_new0(uint8_t, luks->header.master_key_len);
+*masterkeylen = luks->header.master_key_len;
 
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 rv = qcrypto_block_luks_load_key(block,
@@ -677,14 +677,14 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 /* The header is always stored in big-endian format, so
  * convert everything to native */
 be16_to_cpus(&luks->header.version);
-be32_to_cpus(&luks->header.payload_offset);
-be32_to_cpus(&luks->header.key_bytes);
+be32_to_cpus(&luks->header.payload_offset_sector);
+be32_to_cpus(&luks->header.master_key_len);
 be32_to_cpus(&luks->header.master_key_iterations);
 
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 be32_to_cpus(&luks->header.key_slots[i].active);
 be32_to_cpus(&luks->header.key_slots[i].iterations);
-be32_to_cpus(&luks->header.key_slots[i].key_offset);
+be32_to_cpus(&luks->header.key_slots[i].key_offset_sector);
 be32_to_cpus(&luks->header.key_slots[i].stripes);
 }
 
@@ -743,10 +743,11 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 goto fail;
 }
 
-cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
-  ciphermode,
-  luks->header.key_bytes,
-  &local_err);
+cipheralg =
+qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+  ciphermode,
+  luks->header.master_key_len,
+  &local_err);
 if (local_err) {
 ret = -ENOTSUP;
 error_propagate(errp, local_err);
@@ -838,7 +839,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 }
 
 block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
-block->payload_offset = luks->header.payload_offset *
+block->payload_offset = luks->header.payload_offset_sector *
 block->sector_size;
 
 luks->cipher_alg = cipheralg;
@@ -993,9 +994,11 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 strcpy(luks->header.cipher_mode, cipher_mode_spec);
 strcpy(luks->header.hash_spec, hash_alg);
 
-luks->header.key_bytes = qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+luks->header.master_key_len =
+qcrypto_cipher_get_key_len(luks_opts.cipher_alg);
+
 if (luks_opts.cipher_mode == QCRYPTO_CIPHER_MODE_XTS) {
-luks->header.key_bytes *= 2;
+luks->header.master_key_len *= 2;
 }
 
 /* Generate the salt used for hashing the master key
@@ -1008,9 +1011,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 }
 
 /* Generate random master key */
-masterkey = g_new0(uint8_t, luks->header.key_bytes);
+masterkey = g_new0(uint8_t, luks->header.master_key_len);
 if (qcrypto_rando

[Qemu-block] [PATCH 05/12] qcrypto-luks: pass keyslot index rather that pointer to the keyslot

2019-09-12 Thread Maxim Levitsky
Another minor refactoring

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block-luks.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 9e59a791a6..b759cc8d19 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -410,7 +410,7 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
cipher,
  */
 static int
 qcrypto_block_luks_load_key(QCryptoBlock *block,
-QCryptoBlockLUKSKeySlot *slot,
+size_t slot_idx,
 const char *password,
 QCryptoCipherAlgorithm cipheralg,
 QCryptoCipherMode ciphermode,
@@ -424,6 +424,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 Error **errp)
 {
 QCryptoBlockLUKS *luks = block->opaque;
+const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
 g_autofree uint8_t *splitkey = NULL;
 size_t splitkeylen;
 g_autofree uint8_t *possiblekey = NULL;
@@ -580,13 +581,12 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 void *opaque,
 Error **errp)
 {
-QCryptoBlockLUKS *luks = block->opaque;
 size_t i;
 int rv;
 
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
 rv = qcrypto_block_luks_load_key(block,
- &luks->header.key_slots[i],
+ i,
  password,
  cipheralg,
  ciphermode,
-- 
2.17.2




[Qemu-block] [PATCH 08/12] qcrypto-luks: extract store and load header

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 158 ++--
 1 file changed, 94 insertions(+), 64 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index ba63e9b442..c3f3488222 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -409,6 +409,97 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
cipher,
 }
 }
 
+/*
+ * Stores the main LUKS header, taking care of endianess
+ */
+static int
+qcrypto_block_luks_store_header(QCryptoBlock *block,
+QCryptoBlockWriteFunc writefunc,
+void *opaque,
+Error **errp)
+{
+const QCryptoBlockLUKS *luks = block->opaque;
+Error *local_err = NULL;
+size_t i;
+g_autofree QCryptoBlockLUKSHeader *hdr_copy = NULL;
+
+/* Create a copy of the header */
+hdr_copy = g_new0(QCryptoBlockLUKSHeader, 1);
+memcpy(hdr_copy, &luks->header, sizeof(QCryptoBlockLUKSHeader));
+
+/*
+ * Everything on disk uses Big Endian (tm), so flip header fields
+ * before writing them
+ */
+cpu_to_be16s(&hdr_copy->version);
+cpu_to_be32s(&hdr_copy->payload_offset_sector);
+cpu_to_be32s(&hdr_copy->master_key_len);
+cpu_to_be32s(&hdr_copy->master_key_iterations);
+
+for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+cpu_to_be32s(&hdr_copy->key_slots[i].active);
+cpu_to_be32s(&hdr_copy->key_slots[i].iterations);
+cpu_to_be32s(&hdr_copy->key_slots[i].key_offset_sector);
+cpu_to_be32s(&hdr_copy->key_slots[i].stripes);
+}
+
+/* Write out the partition header and key slot headers */
+writefunc(block, 0, (const uint8_t *)hdr_copy, sizeof(*hdr_copy),
+  opaque, &local_err);
+
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+return 0;
+}
+
+/*
+ * Loads the main LUKS header,and byteswaps it to native endianess
+ * And run basic sanity checks on it
+ */
+static int
+qcrypto_block_luks_load_header(QCryptoBlock *block,
+QCryptoBlockReadFunc readfunc,
+void *opaque,
+Error **errp)
+{
+ssize_t rv;
+size_t i;
+QCryptoBlockLUKS *luks = block->opaque;
+
+/*
+ * Read the entire LUKS header, minus the key material from
+ * the underlying device
+ */
+rv = readfunc(block, 0,
+  (uint8_t *)&luks->header,
+  sizeof(luks->header),
+  opaque,
+  errp);
+if (rv < 0) {
+return rv;
+}
+
+/*
+ * The header is always stored in big-endian format, so
+ * convert everything to native
+ */
+be16_to_cpus(&luks->header.version);
+be32_to_cpus(&luks->header.payload_offset_sector);
+be32_to_cpus(&luks->header.master_key_len);
+be32_to_cpus(&luks->header.master_key_iterations);
+
+for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+be32_to_cpus(&luks->header.key_slots[i].active);
+be32_to_cpus(&luks->header.key_slots[i].iterations);
+be32_to_cpus(&luks->header.key_slots[i].key_offset_sector);
+be32_to_cpus(&luks->header.key_slots[i].stripes);
+}
+
+return 0;
+}
+
 /*
  * Given a key slot, and user password, this will attempt to unlock
  * the master encryption key from the key slot.
@@ -623,8 +714,6 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 QCryptoBlockLUKS *luks = NULL;
 Error *local_err = NULL;
 int ret = 0;
-size_t i;
-ssize_t rv;
 g_autofree uint8_t *masterkey = NULL;
 char *ivgen_name, *ivhash_name;
 g_autofree char *password = NULL;
@@ -646,31 +735,11 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 luks = g_new0(QCryptoBlockLUKS, 1);
 block->opaque = luks;
 
-/* Read the entire LUKS header, minus the key material from
- * the underlying device */
-rv = readfunc(block, 0,
-  (uint8_t *)&luks->header,
-  sizeof(luks->header),
-  opaque,
-  errp);
-if (rv < 0) {
-ret = rv;
+ret = qcrypto_block_luks_load_header(block, readfunc, opaque, errp);
+if (ret < 0) {
 goto fail;
 }
 
-/* The header is always stored in big-endian format, so
- * convert everything to native */
-be16_to_cpus(&luks->header.version);
-be32_to_cpus(&luks->header.payload_offset_sector);
-be32_to_cpus(&luks->header.master_key_len);
-be32_to_cpus(&luks->header.master_key_iterations);
-
-for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-be32_to_cpus(&luks->header.key_slots[i].active);
-be32_to_cpus(&luks->header.key_slots[i

[Qemu-block] [PATCH 01/12] block-crypto: misc refactoring

2019-09-12 Thread Maxim Levitsky
* rename the write_func to create_write_func,
  and init_func to create_init_func
  this is  preparation for other write_func that will
  be used to update the encryption keys.

No functional changes

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 block/crypto.c | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index 7eb698774e..6e822c6e50 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -78,7 +78,7 @@ struct BlockCryptoCreateData {
 };
 
 
-static ssize_t block_crypto_write_func(QCryptoBlock *block,
+static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
size_t offset,
const uint8_t *buf,
size_t buflen,
@@ -96,8 +96,7 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
 return ret;
 }
 
-
-static ssize_t block_crypto_init_func(QCryptoBlock *block,
+static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
   size_t headerlen,
   void *opaque,
   Error **errp)
@@ -109,7 +108,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
 return -EFBIG;
 }
 
-/* User provided size should reflect amount of space made
+/*
+ * User provided size should reflect amount of space made
  * available to the guest, so we must take account of that
  * which will be used by the crypto header
  */
@@ -279,8 +279,8 @@ static int block_crypto_co_create_generic(BlockDriverState 
*bs,
 };
 
 crypto = qcrypto_block_create(opts, NULL,
-  block_crypto_init_func,
-  block_crypto_write_func,
+  block_crypto_create_init_func,
+  block_crypto_create_write_func,
   &data,
   errp);
 
-- 
2.17.2




[Qemu-block] [PATCH 06/12] qcrypto-luks: use the parsed encryption settings in QCryptoBlockLUKS

2019-09-12 Thread Maxim Levitsky
Prior to that patch, the parsed encryption settings
were already stored into the QCryptoBlockLUKS but not
used anywhere but in qcrypto_block_luks_get_info

Using them simplifies the code

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block-luks.c | 169 +---
 1 file changed, 79 insertions(+), 90 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index b759cc8d19..f3bfc921b2 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -199,13 +199,25 @@ QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) 
!= 592);
 struct QCryptoBlockLUKS {
 QCryptoBlockLUKSHeader header;
 
-/* Cache parsed versions of what's in header fields,
- * as we can't rely on QCryptoBlock.cipher being
- * non-NULL */
+/* Main encryption algorithm used for encryption*/
 QCryptoCipherAlgorithm cipher_alg;
+
+/* Mode of encryption for the selected encryption algorithm */
 QCryptoCipherMode cipher_mode;
+
+/* Initialization vector generation algorithm */
 QCryptoIVGenAlgorithm ivgen_alg;
+
+/* Hash algorithm used for IV generation*/
 QCryptoHashAlgorithm ivgen_hash_alg;
+
+/*
+ * Encryption algorithm used for IV generation.
+ * Usually the same as main encryption algorithm
+ */
+QCryptoCipherAlgorithm ivgen_cipher_alg;
+
+/* Hash algorithm used in pbkdf2 function */
 QCryptoHashAlgorithm hash_alg;
 };
 
@@ -412,12 +424,6 @@ static int
 qcrypto_block_luks_load_key(QCryptoBlock *block,
 size_t slot_idx,
 const char *password,
-QCryptoCipherAlgorithm cipheralg,
-QCryptoCipherMode ciphermode,
-QCryptoHashAlgorithm hash,
-QCryptoIVGenAlgorithm ivalg,
-QCryptoCipherAlgorithm ivcipheralg,
-QCryptoHashAlgorithm ivhash,
 uint8_t *masterkey,
 QCryptoBlockReadFunc readfunc,
 void *opaque,
@@ -449,7 +455,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * the key is correct and validate the results of
  * decryption later.
  */
-if (qcrypto_pbkdf2(hash,
+if (qcrypto_pbkdf2(luks->hash_alg,
(const uint8_t *)password, strlen(password),
slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN,
slot->iterations,
@@ -477,19 +483,23 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 
 /* Setup the cipher/ivgen that we'll use to try to decrypt
  * the split master key material */
-cipher = qcrypto_cipher_new(cipheralg, ciphermode,
-possiblekey, luks->header.master_key_len,
+cipher = qcrypto_cipher_new(luks->cipher_alg,
+luks->cipher_mode,
+possiblekey,
+luks->header.master_key_len,
 errp);
 if (!cipher) {
 return -1;
 }
 
-niv = qcrypto_cipher_get_iv_len(cipheralg,
-ciphermode);
-ivgen = qcrypto_ivgen_new(ivalg,
-  ivcipheralg,
-  ivhash,
-  possiblekey, luks->header.master_key_len,
+niv = qcrypto_cipher_get_iv_len(luks->cipher_alg,
+luks->cipher_mode);
+
+ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+  luks->ivgen_cipher_alg,
+  luks->ivgen_hash_alg,
+  possiblekey,
+  luks->header.master_key_len,
   errp);
 if (!ivgen) {
 return -1;
@@ -518,7 +528,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * Now we've decrypted the split master key, join
  * it back together to get the actual master key.
  */
-if (qcrypto_afsplit_decode(hash,
+if (qcrypto_afsplit_decode(luks->hash_alg,
luks->header.master_key_len,
slot->stripes,
splitkey,
@@ -536,7 +546,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
  * then comparing that to the hash stored in the key slot
  * header
  */
-if (qcrypto_pbkdf2(hash,
+if (qcrypto_pbkdf2(luks->hash_alg,
masterkey,
luks->header.master_key_len,
luks->header.master_key_salt,
@@ -570,12 +580,6 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
 static int
 qcrypto_block_luks_find_key(QCryptoBlock *block,
 const char *password,
-

[Qemu-block] [PATCH 09/12] qcrypto-luks: extract check and parse header

2019-09-12 Thread Maxim Levitsky
This is just to make qcrypto_block_luks_open more
reasonable in size.

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 235 
 1 file changed, 127 insertions(+), 108 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index c3f3488222..24c1da3739 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -500,6 +500,129 @@ qcrypto_block_luks_load_header(QCryptoBlock *block,
 return 0;
 }
 
+/*
+ * Does basic sanity checks on the LUKS header
+ */
+static int
+qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
+{
+if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
+   QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
+error_setg(errp, "Volume is not in LUKS format");
+return -1;
+}
+
+if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
+error_setg(errp, "LUKS version %" PRIu32 " is not supported",
+   luks->header.version);
+return -1;
+}
+return 0;
+}
+
+/*
+ * Parses the crypto parameters that are stored in the LUKS header
+ */
+
+static int
+qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp)
+{
+g_autofree char *cipher_mode = g_strdup(luks->header.cipher_mode);
+char *ivgen_name, *ivhash_name;
+Error *local_err = NULL;
+
+/*
+ * The cipher_mode header contains a string that we have
+ * to further parse, of the format
+ *
+ *-[:]
+ *
+ * eg  cbc-essiv:sha256, cbc-plain64
+ */
+ivgen_name = strchr(cipher_mode, '-');
+if (!ivgen_name) {
+error_setg(errp, "Unexpected cipher mode string format %s",
+   luks->header.cipher_mode);
+return -1;
+}
+*ivgen_name = '\0';
+ivgen_name++;
+
+ivhash_name = strchr(ivgen_name, ':');
+if (!ivhash_name) {
+luks->ivgen_hash_alg = 0;
+} else {
+*ivhash_name = '\0';
+ivhash_name++;
+
+luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
+   &local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+}
+
+luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
+  &local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+
+luks->cipher_alg =
+qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name,
+  luks->cipher_mode,
+  luks->header.master_key_len,
+  &local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+
+luks->hash_alg =
+qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
+&local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+
+luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
+   &local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+
+if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
+if (!ivhash_name) {
+error_setg(errp, "Missing IV generator hash specification");
+return -1;
+}
+luks->ivgen_cipher_alg =
+qcrypto_block_luks_essiv_cipher(luks->cipher_alg,
+luks->ivgen_hash_alg,
+&local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+return -1;
+}
+} else {
+
+/*
+ * Note we parsed the ivhash_name earlier in the cipher_mode
+ * spec string even with plain/plain64 ivgens, but we
+ * will ignore it, since it is irrelevant for these ivgens.
+ * This is for compat with dm-crypt which will silently
+ * ignore hash names with these ivgens rather than report
+ * an error about the invalid usage
+ */
+luks->ivgen_cipher_alg = luks->cipher_alg;
+}
+return 0;
+}
+
 /*
  * Given a key slot, and user password, this will attempt to unlock
  * the master encryption key from the key slot.
@@ -712,12 +835,9 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 Error **errp)
 {
 QCryptoBlockLUKS *luks = NULL;
-Error *local_err = NULL;
 int ret = 0;
 g_autofree uint8_t *masterkey = NULL;
-char *i

[Qemu-block] [PATCH 03/12] qcrypto-luks: don't overwrite cipher_mode in header

2019-09-12 Thread Maxim Levitsky
This way we can store the header we loaded, which
will be used in key management code

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block-luks.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index f12fa2d270..25f8a9f1c4 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -645,6 +645,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 QCryptoHashAlgorithm hash;
 QCryptoHashAlgorithm ivhash;
 g_autofree char *password = NULL;
+g_autofree char *cipher_mode = NULL;
 
 if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) {
 if (!options->u.luks.key_secret) {
@@ -701,6 +702,8 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 goto fail;
 }
 
+cipher_mode = g_strdup(luks->header.cipher_mode);
+
 /*
  * The cipher_mode header contains a string that we have
  * to further parse, of the format
@@ -709,11 +712,11 @@ qcrypto_block_luks_open(QCryptoBlock *block,
  *
  * eg  cbc-essiv:sha256, cbc-plain64
  */
-ivgen_name = strchr(luks->header.cipher_mode, '-');
+ivgen_name = strchr(cipher_mode, '-');
 if (!ivgen_name) {
 ret = -EINVAL;
 error_setg(errp, "Unexpected cipher mode string format %s",
-   luks->header.cipher_mode);
+   cipher_mode);
 goto fail;
 }
 *ivgen_name = '\0';
@@ -735,7 +738,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 }
 }
 
-ciphermode = 
qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode,
+ciphermode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
&local_err);
 if (local_err) {
 ret = -ENOTSUP;
-- 
2.17.2




[Qemu-block] [PATCH 12/12] qcrypto-luks: more rigorous header checking

2019-09-12 Thread Maxim Levitsky
Check that keyslots don't overlap with the data,
and check that keyslots don't overlap with each other.
(this is done using naive O(n^2) nested loops,
but since there are just 8 keyslots, this doesn't really matter.

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 52 +
 1 file changed, 52 insertions(+)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 0d155c6614..6c53bdc428 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -530,6 +530,11 @@ qcrypto_block_luks_load_header(QCryptoBlock *block,
 static int
 qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp)
 {
+size_t i, j;
+
+unsigned int header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
 if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
 error_setg(errp, "Volume is not in LUKS format");
@@ -541,6 +546,53 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS 
*luks, Error **errp)
luks->header.version);
 return -1;
 }
+
+/* Check all keyslots for corruption  */
+for (i = 0 ; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; i++) {
+
+const QCryptoBlockLUKSKeySlot *slot1 = &luks->header.key_slots[i];
+unsigned int start1 = slot1->key_offset_sector;
+unsigned int len1 =
+qcrypto_block_luks_splitkeylen_sectors(luks,
+   header_sectors,
+   slot1->stripes);
+
+if (slot1->stripes == 0) {
+error_setg(errp, "Keyslot %zu is corrupted (stripes == 0)", i);
+return -1;
+}
+
+if (slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED &&
+slot1->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
+error_setg(errp,
+   "Keyslot %zu state (active/disable) is corrupted", i);
+return -1;
+}
+
+if (start1 + len1 > luks->header.payload_offset_sector) {
+error_setg(errp,
+   "Keyslot %zu is overlapping with the encrypted payload",
+   i);
+return -1;
+}
+
+for (j = i + 1 ; j < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ; j++) {
+const QCryptoBlockLUKSKeySlot *slot2 = &luks->header.key_slots[j];
+unsigned int start2 = slot2->key_offset_sector;
+unsigned int len2 =
+qcrypto_block_luks_splitkeylen_sectors(luks,
+   header_sectors,
+   slot2->stripes);
+
+if (start1 + len1 > start2 && start2 + len2 > start1) {
+error_setg(errp,
+   "Keyslots %zu and %zu are overlapping in the 
header",
+   i, j);
+return -1;
+}
+}
+
+}
 return 0;
 }
 
-- 
2.17.2




[Qemu-block] [PATCH 10/12] qcrypto-luks: extract store key function

2019-09-12 Thread Maxim Levitsky
This function will be used later to store
new keys to the luks metadata

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 304 ++--
 1 file changed, 181 insertions(+), 123 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 24c1da3739..c6045da33e 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -623,6 +623,176 @@ qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, 
Error **errp)
 return 0;
 }
 
+/*
+ * Given a key slot,  user password, and the master key,
+ * will store the encrypted master key there, and update the
+ * in-memory header. User must then write the in-memory header
+ *
+ * Returns:
+ *0 if the keyslot was written successfully
+ *  with the provided password
+ *   -1 if a fatal error occurred while storing the key
+ */
+static int
+qcrypto_block_luks_store_key(QCryptoBlock *block,
+ unsigned int slot_idx,
+ const char *password,
+ uint8_t *masterkey,
+ uint64_t iter_time,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+QCryptoBlockLUKS *luks = block->opaque;
+QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
+g_autofree uint8_t *splitkey = NULL;
+size_t splitkeylen;
+g_autofree uint8_t *slotkey = NULL;
+g_autoptr(QCryptoCipher) cipher = NULL;
+g_autoptr(QCryptoIVGen) ivgen = NULL;
+Error *local_err = NULL;
+uint64_t iters;
+int ret = -1;
+
+if (qcrypto_random_bytes(slot->salt,
+ QCRYPTO_BLOCK_LUKS_SALT_LEN,
+ errp) < 0) {
+goto cleanup;
+}
+
+splitkeylen = luks->header.master_key_len * slot->stripes;
+
+/*
+ * Determine how many iterations are required to
+ * hash the user password while consuming 1 second of compute
+ * time
+ */
+iters = qcrypto_pbkdf2_count_iters(luks->hash_alg,
+   (uint8_t *)password, strlen(password),
+   slot->salt,
+   QCRYPTO_BLOCK_LUKS_SALT_LEN,
+   luks->header.master_key_len,
+   &local_err);
+if (local_err) {
+error_propagate(errp, local_err);
+goto cleanup;
+}
+
+if (iters > (ULLONG_MAX / iter_time)) {
+error_setg_errno(errp, ERANGE,
+ "PBKDF iterations %llu too large to scale",
+ (unsigned long long)iters);
+goto cleanup;
+}
+
+/* iter_time was in millis, but count_iters reported for secs */
+iters = iters * iter_time / 1000;
+
+if (iters > UINT32_MAX) {
+error_setg_errno(errp, ERANGE,
+ "PBKDF iterations %llu larger than %u",
+ (unsigned long long)iters, UINT32_MAX);
+goto cleanup;
+}
+
+slot->iterations =
+MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+
+
+/*
+ * Generate a key that we'll use to encrypt the master
+ * key, from the user's password
+ */
+slotkey = g_new0(uint8_t, luks->header.master_key_len);
+if (qcrypto_pbkdf2(luks->hash_alg,
+   (uint8_t *)password, strlen(password),
+   slot->salt,
+   QCRYPTO_BLOCK_LUKS_SALT_LEN,
+   slot->iterations,
+   slotkey, luks->header.master_key_len,
+   errp) < 0) {
+goto cleanup;
+}
+
+
+/*
+ * Setup the encryption objects needed to encrypt the
+ * master key material
+ */
+cipher = qcrypto_cipher_new(luks->cipher_alg,
+luks->cipher_mode,
+slotkey, luks->header.master_key_len,
+errp);
+if (!cipher) {
+goto cleanup;
+}
+
+ivgen = qcrypto_ivgen_new(luks->ivgen_alg,
+  luks->ivgen_cipher_alg,
+  luks->ivgen_hash_alg,
+  slotkey, luks->header.master_key_len,
+  errp);
+if (!ivgen) {
+goto cleanup;
+}
+
+/*
+ * Before storing the master key, we need to vastly
+ * increase its size, as protection against forensic
+ * disk data recovery
+ */
+splitkey = g_new0(uint8_t, splitkeylen);
+
+if (qcrypto_afsplit_encode(luks->hash_alg,
+   luks->header.master_key_len,
+   slot->stripes,
+   masterkey,
+   splitkey,
+ 

[Qemu-block] [PATCH 07/12] qcrypto-luks: purge unused error codes from open callback

2019-09-12 Thread Maxim Levitsky
These values are not used by generic crypto code anyway

Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 26 +-
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index f3bfc921b2..ba63e9b442 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -675,13 +675,13 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 if (memcmp(luks->header.magic, qcrypto_block_luks_magic,
QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) {
 error_setg(errp, "Volume is not in LUKS format");
-ret = -EINVAL;
+ret = -1;
 goto fail;
 }
 if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) {
 error_setg(errp, "LUKS version %" PRIu32 " is not supported",
luks->header.version);
-ret = -ENOTSUP;
+ret = -1;
 goto fail;
 }
 
@@ -697,7 +697,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
  */
 ivgen_name = strchr(cipher_mode, '-');
 if (!ivgen_name) {
-ret = -EINVAL;
+ret = -1;
 error_setg(errp, "Unexpected cipher mode string format %s",
cipher_mode);
 goto fail;
@@ -715,7 +715,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 luks->ivgen_hash_alg = qcrypto_block_luks_hash_name_lookup(ivhash_name,
&local_err);
 if (local_err) {
-ret = -ENOTSUP;
+ret = -1;
 error_propagate(errp, local_err);
 goto fail;
 }
@@ -724,7 +724,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 luks->cipher_mode = qcrypto_block_luks_cipher_mode_lookup(cipher_mode,
   &local_err);
 if (local_err) {
-ret = -ENOTSUP;
+ret = -1;
 error_propagate(errp, local_err);
 goto fail;
 }
@@ -735,7 +735,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
   luks->header.master_key_len,
   &local_err);
 if (local_err) {
-ret = -ENOTSUP;
+ret = -1;
 error_propagate(errp, local_err);
 goto fail;
 }
@@ -744,7 +744,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec,
 &local_err);
 if (local_err) {
-ret = -ENOTSUP;
+ret = -1;
 error_propagate(errp, local_err);
 goto fail;
 }
@@ -752,14 +752,14 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 luks->ivgen_alg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name,
&local_err);
 if (local_err) {
-ret = -ENOTSUP;
+ret = -1;
 error_propagate(errp, local_err);
 goto fail;
 }
 
 if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) {
 if (!ivhash_name) {
-ret = -EINVAL;
+ret = -1;
 error_setg(errp, "Missing IV generator hash specification");
 goto fail;
 }
@@ -768,7 +768,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 luks->ivgen_hash_alg,
 &local_err);
 if (local_err) {
-ret = -ENOTSUP;
+ret = -1;
 error_propagate(errp, local_err);
 goto fail;
 }
@@ -795,7 +795,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 masterkey,
 readfunc, opaque,
 errp) < 0) {
-ret = -EACCES;
+ret = -1;
 goto fail;
 }
 
@@ -813,7 +813,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
  luks->header.master_key_len,
  errp);
 if (!block->ivgen) {
-ret = -ENOTSUP;
+ret = -1;
 goto fail;
 }
 
@@ -825,7 +825,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
 n_threads,
 errp);
 if (ret < 0) {
-ret = -ENOTSUP;
+ret = -1;
 goto fail;
 }
 }
-- 
2.17.2




[Qemu-block] [PATCH 11/12] qcrypto-luks: simplify the math used for keyslot locations

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block-luks.c | 63 -
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index c6045da33e..0d155c6614 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -409,6 +409,30 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm 
cipher,
 }
 }
 
+/*
+ * Returns number of sectors needed to store the key material
+ * given number of anti forensic stripes
+ */
+static int
+qcrypto_block_luks_splitkeylen_sectors(const QCryptoBlockLUKS *luks,
+   unsigned int header_sectors,
+   unsigned int stripes)
+{
+/*
+ * This calculation doesn't match that shown in the spec,
+ * but instead follows the cryptsetup implementation.
+ */
+
+size_t splitkeylen = luks->header.master_key_len * stripes;
+
+/* First align the key material size to block size*/
+size_t splitkeylen_sectors =
+DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE);
+
+/* Then also align the key material size to the size of the header */
+return ROUND_UP(splitkeylen_sectors, header_sectors);
+}
+
 /*
  * Stores the main LUKS header, taking care of endianess
  */
@@ -1124,7 +1148,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 QCryptoBlockCreateOptionsLUKS luks_opts;
 Error *local_err = NULL;
 g_autofree uint8_t *masterkey = NULL;
-size_t splitkeylen = 0;
+size_t header_sectors;
+size_t split_key_sectors;
 size_t i;
 g_autofree char *password = NULL;
 const char *cipher_alg;
@@ -1343,37 +1368,29 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 goto error;
 }
 
+/* start with the sector that follows the header*/
+header_sectors = QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
+QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
+
+split_key_sectors =
+qcrypto_block_luks_splitkeylen_sectors(luks,
+   header_sectors,
+   QCRYPTO_BLOCK_LUKS_STRIPES);
 
-/* Although LUKS has multiple key slots, we're just going
- * to use the first key slot */
-splitkeylen = luks->header.master_key_len * QCRYPTO_BLOCK_LUKS_STRIPES;
 for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
-luks->header.key_slots[i].active = 
QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
-luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
+QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[i];
+slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
 
-/* This calculation doesn't match that shown in the spec,
- * but instead follows the cryptsetup implementation.
- */
-luks->header.key_slots[i].key_offset_sector =
-(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
-(ROUND_UP(DIV_ROUND_UP(splitkeylen, 
QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
-  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i);
+slot->key_offset_sector = header_sectors + i * split_key_sectors;
+slot->stripes = QCRYPTO_BLOCK_LUKS_STRIPES;
 }
 
-
 /* The total size of the LUKS headers is the partition header + key
  * slot headers, rounded up to the nearest sector, combined with
  * the size of each master key material region, also rounded up
  * to the nearest sector */
-luks->header.payload_offset_sector =
-(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
- QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) +
-(ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE),
-  (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET /
-   QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) *
- QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+luks->header.payload_offset_sector = header_sectors +
+QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS * split_key_sectors;
 
 block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE;
 block->payload_offset = luks->header.payload_offset_sector *
-- 
2.17.2




Re: [Qemu-block] [Qemu-devel] [PATCH 03/10] qcrypto-luks: implement the encryption key management

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:55 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:01PM +0300, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  crypto/block-luks.c | 366 +++-
> >  1 file changed, 364 insertions(+), 2 deletions(-)
> > 
> > diff --git a/crypto/block-luks.c b/crypto/block-luks.c
> > index ba20d55246..21325fbc79 100644
> > --- a/crypto/block-luks.c
> > +++ b/crypto/block-luks.c
> > @@ -70,6 +70,9 @@ typedef struct QCryptoBlockLUKSKeySlot 
> > QCryptoBlockLUKSKeySlot;
> >  
> >  #define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
> >  
> > +#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME 2000
> 
> Perhaps use  ITER_TIME_MS to make it clear it is millisecs
Why not... done.

> 
> > +#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
> > +
> >  static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = 
> > {
> >  'L', 'U', 'K', 'S', 0xBA, 0xBE
> >  };
> > @@ -219,6 +222,9 @@ struct QCryptoBlockLUKS {
> >  
> >  /* Hash algorithm used in pbkdf2 function */
> >  QCryptoHashAlgorithm hash_alg;
> > +
> > +/* Name of the secret that was used to open the image */
> > +char *secret;
> > +
> > +/*
> > + * Returns true if a slot i is marked as active
> > + * (contains encrypted copy of the master key)
> > + */
> > +
> > +static bool
> 
> No blank line is wanted between the comment & function.
> Likewise for the rest of this patch series
No problem!


> 
> > +qcrypto_block_luks_slot_active(const QCryptoBlockLUKS *luks,
> > +   unsigned int slot_idx)
> > +{
> > +uint32_t val = luks->header.key_slots[slot_idx].active;
> > +return val ==  QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
> > +}
> 
> 
> 
> > +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 = &luks->header.key_slots[slot_idx];
> > +g_autofree uint8_t *garbagesplitkey = NULL;
> > +size_t splitkeylen = luks->header.master_key_len * slot->stripes;
> > +size_t i;
> > +
> > +assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
> > +assert(splitkeylen > 0);
> > +
> > +garbagesplitkey = g_malloc0(splitkeylen);
> 
> I'd prefer   g_new0(uint8_t, splitkeylen)
Done.

> 
> > +
> > +/* 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;
> > +
> 
> 
> > @@ -1522,6 +1700,187 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >  }
> >  
> >  
> > +#define CHECK_NON_AMEND_OPTION(luks, luks_opts, name) \
> > +if (luks_opts.has_##name && luks_opts.name != luks->name) { \
> > +error_setg(errp, "Option \"" #name "\" can't be amended"); \
> > +goto cleanup; \
> > +}
> > +
> > +static int
> > +qcrypto_block_luks_amend_options(QCryptoBlock *block,
> > + QCryptoBlockReadFunc readfunc,
> > + QCryptoBlockWriteFunc writefunc,
> > + void *opaque,
> > + QCryptoBlockCreateOptions *options,
> > + bool force,
> > + Error **errp)
> > +{
> > +QCryptoBlockLUKS *luks = block->opaque;
> > +QCryptoBlockCreateOptionsLUKS luks_opts;
> > +g_autofree char *old_password = NULL;
> > +g_autofree char *password = NULL;
> > +const char *unlock_secret = luks->secret;
> > +g_autofree uint8_t *masterkey = NULL;
> > +int slot = -1;
> > +int ret = -1;
> > +bool active = true;
> > +int64_t iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME;
> > +
> > +memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
> > +
> > +CHECK_NON_AMEND_OPTION(luks, luks_opts, cipher_alg);
> > +CHECK_NON_AMEND_OPTION(luks, luks_opts, cipher_mode);
> > +CHECK

Re: [Qemu-block] [Qemu-devel] [PATCH 04/10] block: amend: add 'force' option

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 14:59 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:02PM +0300, Maxim Levitsky wrote:
> 
> This could do with some text to explain what this will be
> used for.

I actually added an explanation to the man page

"
+--force allows some unsafe operations. Currently for -f luks,
+it allows to erase last encryption key, and to overwrite an active
+encryption key.
+
"

You probably mean adding few words in the commit message as well,
and I'll do that anyway.


> 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block.c   | 4 +++-
> >  block/qcow2.c | 1 +
> >  include/block/block.h | 1 +
> >  include/block/block_int.h | 1 +
> >  qemu-img-cmds.hx  | 4 ++--
> >  qemu-img.c| 8 +++-
> >  qemu-img.texi | 6 +-
> >  7 files changed, 20 insertions(+), 5 deletions(-)
> 
> For the code
> 
> Reviewed-by: Daniel P. Berrangé 
> 
> 
> Regards,
> Daniel


Best regards,
Maxim Levitsky




Re: [Qemu-block] [Qemu-devel] [PATCH 05/10] block/crypto: implement the encryption key management

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 15:04 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:03PM +0300, Maxim Levitsky wrote:
> > This implements the encryption key management
> > using the generic code in qcrypto layer
> > (currently only for qemu-img amend)
> > 
> > This code adds another 'write_func' because the initialization
> > write_func works directly on the underlying file,
> > because during the creation, there is no open instance
> > of the luks driver, but during regular use, we have it,
> > and should use it instead.
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/crypto.c | 106 +++--
> >  1 file changed, 103 insertions(+), 3 deletions(-)
> > 
> > diff --git a/block/crypto.c b/block/crypto.c
> > index a6a3e1f1d8..dbd95a99ba 100644
> > --- a/block/crypto.c
> > +++ b/block/crypto.c
> > @@ -36,6 +36,7 @@ typedef struct BlockCrypto BlockCrypto;
> >  
> >  struct BlockCrypto {
> >  QCryptoBlock *block;
> > +bool updating_keys;
> >  };
> >  
> >  
> > @@ -70,6 +71,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock 
> > *block,
> >  return ret;
> >  }
> >  
> > +static ssize_t block_crypto_write_func(QCryptoBlock *block,
> > +  size_t offset,
> > +  const uint8_t *buf,
> > +  size_t buflen,
> > +  void *opaque,
> > +  Error **errp)
> 
> Indent off-by-1 - align with param on the first line
I hope you won't hate me after all these indent bugs.
I'll learn to notice, I promise :-)

> 
> > +{
> > +BlockDriverState *bs = opaque;
> > +ssize_t ret;
> > +
> > +ret = bdrv_pwrite(bs->file, offset, buf, buflen);
> > +if (ret < 0) {
> > +error_setg_errno(errp, -ret, "Could not write encryption header");
> > +return ret;
> > +}
> > +return ret;
> > +}
> > +
> >  
> >  struct BlockCryptoCreateData {
> >  BlockBackend *blk;
> > @@ -647,6 +666,88 @@ block_crypto_get_specific_info_luks(BlockDriverState 
> > *bs, Error **errp)
> >  return spec_info;
> >  }
> >  
> > +
> > +static int
> > +block_crypto_amend_options(BlockDriverState *bs,
> > +   QemuOpts *opts,
> > +   BlockDriverAmendStatusCB *status_cb,
> > +   void *cb_opaque,
> > +   bool force,
> > +   Error **errp)
> > +{
> > +BlockCrypto *crypto = bs->opaque;
> > +QDict *cryptoopts = NULL;
> > +QCryptoBlockCreateOptions *amend_options = NULL;
> > +int ret;
> > +
> > +assert(crypto);
> > +assert(crypto->block);
> > +
> > +crypto->updating_keys = true;
> > +
> > +ret = bdrv_child_refresh_perms(bs, bs->file, errp);
> > +if (ret) {
> 
> I can;'t remember - does this need to be "ret < 0" or
> does refresh_perms return positive errnos ?
I don't really know but looking at the source the 
bdrv_child_refresh_perms calls the bdrv_child_try_set_perm
which seems to forward only negative error codes,
so I'll do this here as well.
Also an iotest for this is a must, now I remember. 


Best regards,
Maxim Levitsky




Re: [Qemu-block] [Qemu-devel] [PATCH 06/10] qcow2: implement crypto amend options

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 15:06 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:04PM +0300, Maxim Levitsky wrote:
> > ---
> >  block/qcow2.c | 79 ---
> >  1 file changed, 63 insertions(+), 16 deletions(-)
> > 
> > @@ -4888,9 +4899,22 @@ static int qcow2_amend_options(BlockDriverState *bs, 
> > QemuOpts *opts,
> >  return -ENOTSUP;
> >  }
> >  } else if (g_str_has_prefix(desc->name, "encrypt.")) {
> > -error_setg(errp,
> > -   "Changing the encryption parameters is not 
> > supported");
> > -return -ENOTSUP;
> > +
> > +if (!s->crypto) {
> > +error_setg(errp,
> > +   "Can't amend encryption options - encryption 
> > not supported");
> > +return -ENOTSUP;
> > +
> > +}
> 
> Perhaps  ' - encryption not present', and -EINVAL
Agree. Fixed.

> 
> > +
> > +if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
> > +error_setg(errp,
> > +   "Only LUKS encryption options can be amended");
> > +return -ENOTSUP;
> > +}
> > +
> > +encryption_update = true;
> > +
> >  } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
> >  cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
> >   cluster_size);
> > @@ -4927,7 +4951,7 @@ static int qcow2_amend_options(BlockDriverState *bs, 
> > QemuOpts *opts,
> >   "images");
> >  return -EINVAL;
> >  }
> > -} else {
> > +} else  {
> 
> Accidental change
Fixed.
> 
> >  /* if this point is reached, this probably means a new option 
> > was
> >   * added without having it covered here */
> >  abort();
> > @@ -4940,7 +4964,8 @@ static int qcow2_amend_options(BlockDriverState *bs, 
> > QemuOpts *opts,
> >  .original_status_cb = status_cb,
> >  .original_cb_opaque = cb_opaque,
> >  .total_operations = (new_version < old_version)
> > -  + (s->refcount_bits != refcount_bits)
> > +  + (s->refcount_bits != refcount_bits) +
> > +  (encryption_update == true)
> >  };
> >  
> >  /* Upgrade first (some features may require compat=1.1) */
> > @@ -4954,6 +4979,28 @@ static int qcow2_amend_options(BlockDriverState *bs, 
> > QemuOpts *opts,
> >  }
> >  }
> >  
> > +if (encryption_update) {
> > +
> 
> Redundant blank line
Fixed.
> 
> > +QCryptoBlockCreateOptions *cryptoopts;
> > +
> > +cryptoopts = qcow2_extract_crypto_create_opts(opts, "luks", errp);
> > +if (!cryptoopts)
> > +return -EINVAL;
> > +
> > +helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
> > +
> > +ret = qcrypto_block_amend_options(s->crypto,
> > +  qcow2_crypto_hdr_read_func,
> > +  qcow2_crypto_hdr_write_func,
> > +  bs,
> > +  cryptoopts,
> > +  force,
> > +  errp);
> > +if (ret) {
> 
> Check  ret < 0
Fixed.
> 
> > +return ret;
> > +}
> > +}
> > +
> 
> Regards,
> Daniel

Best regards,
Maxim Levitsky





Re: [Qemu-block] [Qemu-devel] [PATCH 08/10] block/crypto: implement blockdev-amend

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 15:10 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:06PM +0300, Maxim Levitsky wrote:
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/crypto.c   | 86 +---
> >  qapi/block-core.json |  4 +--
> >  2 files changed, 68 insertions(+), 22 deletions(-)
> 
> Reviewed-by: Daniel P. Berrangé 
> 
> 
> >  static int
> >  block_crypto_amend_options(BlockDriverState *bs,
> > QemuOpts *opts,
> > @@ -678,44 +722,45 @@ block_crypto_amend_options(BlockDriverState *bs,
> >  BlockCrypto *crypto = bs->opaque;
> >  QDict *cryptoopts = NULL;
> >  QCryptoBlockCreateOptions *amend_options = NULL;
> > -int ret;
> > +int ret= -EINVAL;
> 
> nitpick - space before '='
Done. This is one of the few errors that checkpatch.pl does catch,
but apparently I forgot to run it on this patch.
> 
> >  
> >  assert(crypto);
> >  assert(crypto->block);
> >  
> > -crypto->updating_keys = true;
> > -
> > -ret = bdrv_child_refresh_perms(bs, bs->file, errp);
> > -if (ret) {
> > -goto cleanup;
> > -}
> > -
> >  cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
> >   
> > &block_crypto_create_opts_luks,
> >   true);
> >  
> >  qdict_put_str(cryptoopts, "format", "luks");
> >  amend_options = block_crypto_create_opts_init(cryptoopts, errp);
> > +
> >  if (!amend_options) {
> > -ret = -EINVAL;
> > -goto cleanup;
> > +goto out;
> >  }
> >  
> > -ret = qcrypto_block_amend_options(crypto->block,
> > -  block_crypto_read_func,
> > -  block_crypto_write_func,
> > -  bs,
> > -  amend_options,
> > -  force,
> > -  errp);
> > -cleanup:
> > -crypto->updating_keys = false;
> > -bdrv_child_refresh_perms(bs, bs->file, errp);
> > +ret = block_crypto_amend_options_generic(bs, amend_options, force, 
> > errp);
> > +out:
> 
> No need to rename the "cleanup" label to "out"
All right.
> 
> >  qapi_free_QCryptoBlockCreateOptions(amend_options);
> >  qobject_unref(cryptoopts);
> >  return ret;
> >  }
> >  
> > +static int
> > +coroutine_fn block_crypto_co_amend(BlockDriverState *bs,
> > +   BlockdevCreateOptions *opts,
> > +   bool force,
> > +   Error **errp)
> > +{
> > +QCryptoBlockCreateOptions amend_opts;
> > +
> > +amend_opts = (QCryptoBlockCreateOptions) {
> > +.format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
> > +.u.luks = *qapi_BlockdevCreateOptionsLUKS_base(&opts->u.luks),
> > +};
> > +
> > +return block_crypto_amend_options_generic(bs, &amend_opts, force, 
> > errp);
> > +}
> > +
> >  
> >  static void
> >  block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
> > @@ -774,6 +819,7 @@ static BlockDriver bdrv_crypto_luks = {
> >  .bdrv_get_info  = block_crypto_get_info_luks,
> >  .bdrv_get_specific_info = block_crypto_get_specific_info_luks,
> >  .bdrv_amend_options = block_crypto_amend_options,
> > +.bdrv_co_amend  = block_crypto_co_amend,
> >  
> >  .strong_runtime_opts = block_crypto_strong_runtime_opts,
> >  };
> > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > index 7900914506..02375fb59a 100644
> > --- a/qapi/block-core.json
> > +++ b/qapi/block-core.json
> > @@ -4220,8 +4220,8 @@
> >  ##
> >  { 'struct': 'BlockdevCreateOptionsLUKS',
> >'base': 'QCryptoBlockCreateOptionsLUKS',
> > -  'data': { 'file': 'BlockdevRef',
> > -'size': 'size',
> > +  'data': { '*file': 'BlockdevRef',
> > +'*size': 'size',
> 
> Docs comment to explain they are mandatory for create 
Done
> 
> >  '*preallocation':   'PreallocMode' } }
> >  
> >  ##
> > -- 
> > 2.17.2
> > 
> 
> Regards,
> Daniel

Best regards,
Maxim Levitsky




Re: [Qemu-block] [Qemu-devel] [PATCH 09/10] block/qcow2: implement blockdev-amend

2019-09-12 Thread Maxim Levitsky
On Fri, 2019-09-06 at 15:12 +0100, Daniel P. Berrangé wrote:
> On Fri, Aug 30, 2019 at 11:56:07PM +0300, Maxim Levitsky wrote:
> > Currently only for changing crypto parameters
> > 
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/qcow2.c| 71 
> >  qapi/block-core.json |  4 +--
> >  2 files changed, 73 insertions(+), 2 deletions(-)
> > 
> > diff --git a/block/qcow2.c b/block/qcow2.c
> > index 8dff4c6b5f..327d2afd9f 100644
> > --- a/block/qcow2.c
> > +++ b/block/qcow2.c
> > @@ -3082,6 +3082,18 @@ qcow2_co_create(BlockdevCreateOptions 
> > *create_options, Error **errp)
> >  assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
> >  qcow2_opts = &create_options->u.qcow2;
> >  
> > +if (!qcow2_opts->has_size) {
> > +error_setg(errp, "Size is manadatory for image creation");
> > +return -EINVAL;
> > +
> > +}
> > +
> > +if (!qcow2_opts->has_file) {
> > +error_setg(errp, "'file' is manadatory for image creation");
> > +return -EINVAL;
> > +
> > +}
> > +
> >  bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
> >  if (bs == NULL) {
> >  return -EIO;
> > @@ -5112,6 +5124,64 @@ static int qcow2_amend_options(BlockDriverState *bs, 
> > QemuOpts *opts,
> >  return 0;
> >  }
> >  
> > +
> > +static int coroutine_fn qcow2_co_amend(BlockDriverState *bs,
> > +   BlockdevCreateOptions *opts,
> > +   bool force,
> > +   Error **errp)
> > +{
> > +BlockdevCreateOptionsQcow2 *qopts = &opts->u.qcow2;
> > +BDRVQcow2State *s = bs->opaque;
> > +int ret;
> > +
> > +/*
> > + * This is ugly as hell, in later versions of this patch
> > + * something has to be done about this
> > + */
> > +if (qopts->has_file || qopts->has_size || qopts->has_data_file ||
> > +qopts->has_data_file_raw || qopts->has_version ||
> > +qopts->has_backing_file || qopts->has_backing_fmt ||
> > +qopts->has_cluster_size || qopts->has_preallocation ||
> > +qopts->has_lazy_refcounts || qopts->has_refcount_bits) {
> > +
> > +error_setg(errp,
> > +"Only LUKS encryption options can be amended for qcow2 
> > with blockdev-amend");
> > +return -EOPNOTSUPP;
> > +
> > +}
> > +
> > +if (qopts->has_encrypt) {
> > +if (!s->crypto) {
> > +error_setg(errp, "QCOW2 image is not encrypted, can't amend");
> > +return -EOPNOTSUPP;
> > +}
> > +
> > +if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
> > +error_setg(errp,
> > +   "Amend can't be used to change the qcow2 encryption 
> > format");
> > +return -EOPNOTSUPP;
> > +}
> > +
> > +if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
> > +error_setg(errp,
> > +   "Only LUKS encryption options can be amended for 
> > qcow2 with blockdev-amend");
> > +return -EOPNOTSUPP;
> > +}
> > +
> > +ret = qcrypto_block_amend_options(s->crypto,
> > +  qcow2_crypto_hdr_read_func,
> > +  qcow2_crypto_hdr_write_func,
> > +  bs,
> > +  qopts->encrypt,
> > +  force,
> > +  errp);
> > +if (ret) {
> > +return ret;
> > +}
> > +}
> > +return 0;
> > +}
> > +
> >  /*
> >   * If offset or size are negative, respectively, they will not be included 
> > in
> >   * the BLOCK_IMAGE_CORRUPTED event emitted.
> > @@ -5304,6 +5374,7 @@ BlockDriver bdrv_qcow2 = {
> >  .mutable_opts= mutable_opts,
> >  .bdrv_co_check   = qcow2_co_check,
> >  .bdrv_amend_options  = qcow2_amend_options,
> > +.bdrv_co_amend   = qcow2_co_amend,
> >  
> >  .bdrv_detach_aio_context  = qcow2_detach_aio_context,
> >  .bdrv_attach_aio_context  = qcow2

[Qemu-block] [PATCH v2 03/11] qcrypto-luks: implement the encryption key management

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 crypto/block-luks.c | 356 +++-
 1 file changed, 354 insertions(+), 2 deletions(-)

diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index fed80e6646..26ce50b111 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -70,6 +70,9 @@ typedef struct QCryptoBlockLUKSKeySlot 
QCryptoBlockLUKSKeySlot;
 
 #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] = {
 'L', 'U', 'K', 'S', 0xBA, 0xBE
 };
@@ -219,6 +222,9 @@ struct QCryptoBlockLUKS {
 
 /* Hash algorithm used in pbkdf2 function */
 QCryptoHashAlgorithm hash_alg;
+
+/* Name of the secret that was used to open the image */
+char *secret;
 };
 
 
@@ -1070,6 +1076,170 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
 }
 
 
+
+/*
+ * 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 = 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
+ * (contains 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 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 = &luks->header.key_slots[slot_idx];
+g_autofree uint8_t *garbagesplitkey = NULL;
+size_t splitkeylen = luks->header.master_key_len * slot->stripes;
+size_t i;
+
+assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+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;
+
+qcrypto_block_luks_store_header(block,  writefunc, opaque, errp);
+
+/*
+ * 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, errp) < 0) {
+/*
+ * If we failed to get the random data, still write
+ * at least zeros to the key slot at least once
+ */
+
+if (i > 0) {
+return -1;
+}
+}
+
+if (writefunc(block,
+  slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+  garbagesplitkey,
+  splitkeylen,
+  opaque,
+  errp) != splitkeylen) {
+return -1;
+}
+}
+return 0;
+}
+
+
+/*
+ * Erase all the keys that match the given password
+ * Will stop when only one keyslot is remaining
+ * Returns number of slots that were erased or -1 on failure
+ */
+static int
+qcrypto_block_luks_erase_matching_keys(QCryptoBlock *block,
+   const char *password,
+   QCryptoBlockReadFunc readfunc,
+   QCryptoBlockWriteFunc writefunc,
+   void *opaque,
+   bool force,
+   Error **errp)
+{
+QCryptoBlockLUKS *luks = block->opaque;
+size_t i;
+int rv;
+g_autofree uint8_t *masterkey = NULL;
+unsigned int erased_cnt = 0;
+unsigned int ac

[Qemu-block] [PATCH v2 01/11] qcrypto: add suport for amend options

2019-09-12 Thread Maxim Levitsky
This adds the qcrypto_amend_options and corresponding
crypto driver callbacks for the  for encrypted
key managedment

Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 crypto/block.c | 31 +++
 crypto/blockpriv.h |  8 
 include/crypto/block.h | 22 ++
 3 files changed, 61 insertions(+)

diff --git a/crypto/block.c b/crypto/block.c
index 325752871c..14b684de7f 100644
--- a/crypto/block.c
+++ b/crypto/block.c
@@ -115,6 +115,37 @@ QCryptoBlock 
*qcrypto_block_create(QCryptoBlockCreateOptions *options,
 }
 
 
+int qcrypto_block_amend_options(QCryptoBlock *block,
+QCryptoBlockReadFunc readfunc,
+QCryptoBlockWriteFunc writefunc,
+void *opaque,
+QCryptoBlockCreateOptions *options,
+bool force,
+Error **errp)
+{
+if (options->format != block->format) {
+error_setg(errp,
+   "Its not possible to change encryption format with amend 
interface");
+return -1;
+}
+
+if (!block->driver->amend) {
+error_setg(errp,
+   "Crypto format %s doesn't support format options amendment",
+   QCryptoBlockFormat_str(block->format));
+return -1;
+}
+
+return block->driver->amend(block,
+readfunc,
+writefunc,
+opaque,
+options,
+force,
+errp);
+}
+
+
 QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block,
  Error **errp)
 {
diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
index 71c59cb542..c18a4e0b43 100644
--- a/crypto/blockpriv.h
+++ b/crypto/blockpriv.h
@@ -62,6 +62,14 @@ struct QCryptoBlockDriver {
   void *opaque,
   Error **errp);
 
+int (*amend)(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockCreateOptions *options,
+ bool force,
+ Error **errp);
+
 int (*get_info)(QCryptoBlock *block,
 QCryptoBlockInfo *info,
 Error **errp);
diff --git a/include/crypto/block.h b/include/crypto/block.h
index d49d2c2da9..777fd51ebe 100644
--- a/include/crypto/block.h
+++ b/include/crypto/block.h
@@ -144,6 +144,28 @@ QCryptoBlock 
*qcrypto_block_create(QCryptoBlockCreateOptions *options,
void *opaque,
Error **errp);
 
+/**
+ * qcrypto_block_amend_options:
+ * @block: the block encryption object
+ *
+ * @readfunc: callback for reading data from the volume header
+ * @writefunc: callback for writing data to the volume header
+ * @opaque: data to pass to @readfunc and @writefunc
+ * @options: the new/amended encryption options
+ * @force: hint for the driver to allow unsafe operation
+ * @errp: error pointer
+ *
+ * Changes the crypto options of the encryption format
+ *
+ */
+int qcrypto_block_amend_options(QCryptoBlock *block,
+QCryptoBlockReadFunc readfunc,
+QCryptoBlockWriteFunc writefunc,
+void *opaque,
+QCryptoBlockCreateOptions *options,
+bool force,
+Error **errp);
+
 
 /**
  * qcrypto_block_get_info:
-- 
2.17.2




[Qemu-block] [PATCH v2 00/11] RFC crypto/luks: encryption key managment using amend interface

2019-09-12 Thread Maxim Levitsky
This patch series is continuation of my work to add encryption
key managment to luks/qcow2 with luks.

This is second version of this patch set.
The changes are mostly addressing the review feedback,
plus I tested (and fixed sadly) the somewhat ugly code
that allows to still write share a raw luks device,
while preveting the key managment from happening in this case,
as it is unsafe.
I added a new iotest dedicated to that as well.

Best regards,
Maxim Levitsky

Maxim Levitsky (11):
  qcrypto: add suport for amend options
  qcrypto-luks: extend the create options for upcoming encryption key
management
  qcrypto-luks: implement the encryption key management
  block: amend: add 'force' option
  block/crypto: implement the encryption key management
  qcow2: implement crypto amend options
  block: add x-blockdev-amend qmp command
  block/crypto: implement blockdev-amend
  block/qcow2: implement blockdev-amend
  iotests: filter few more luks specific create options
  iotests : add tests for encryption key management

 block.c  |   4 +-
 block/Makefile.objs  |   2 +-
 block/amend.c| 116 ++
 block/crypto.c   | 167 +-
 block/crypto.h   |  16 ++
 block/qcow2.c| 151 ++--
 crypto/block-luks.c  | 382 ++-
 crypto/block.c   |  31 +++
 crypto/blockpriv.h   |   8 +
 include/block/block.h|   1 +
 include/block/block_int.h|  22 +-
 include/crypto/block.h   |  22 ++
 qapi/block-core.json |  39 +++-
 qapi/crypto.json |  19 ++
 qapi/job.json|   4 +-
 qemu-img-cmds.hx |   4 +-
 qemu-img.c   |   8 +-
 qemu-img.texi|   6 +-
 tests/qemu-iotests/082.out   |  54 +
 tests/qemu-iotests/087.out   |   6 +-
 tests/qemu-iotests/134.out   |   2 +-
 tests/qemu-iotests/158.out   |   4 +-
 tests/qemu-iotests/188.out   |   2 +-
 tests/qemu-iotests/189.out   |   4 +-
 tests/qemu-iotests/198.out   |   4 +-
 tests/qemu-iotests/300   | 202 
 tests/qemu-iotests/300.out   |  98 
 tests/qemu-iotests/301   |  90 
 tests/qemu-iotests/301.out   |  30 +++
 tests/qemu-iotests/302   | 252 
 tests/qemu-iotests/302.out   |  18 ++
 tests/qemu-iotests/303   | 228 ++
 tests/qemu-iotests/303.out   |  28 +++
 tests/qemu-iotests/common.filter |   6 +-
 tests/qemu-iotests/group |   9 +
 35 files changed, 1986 insertions(+), 53 deletions(-)
 create mode 100644 block/amend.c
 create mode 100755 tests/qemu-iotests/300
 create mode 100644 tests/qemu-iotests/300.out
 create mode 100755 tests/qemu-iotests/301
 create mode 100644 tests/qemu-iotests/301.out
 create mode 100644 tests/qemu-iotests/302
 create mode 100644 tests/qemu-iotests/302.out
 create mode 100644 tests/qemu-iotests/303
 create mode 100644 tests/qemu-iotests/303.out

-- 
2.17.2




[Qemu-block] [PATCH v2 04/11] block: amend: add 'force' option

2019-09-12 Thread Maxim Levitsky
'force' optinion will be used for some unsafe option amend operations.

This includes things like erasing last keyslot in luks (which pretty much 
guarantees
destroying the data, unless the master key is backed up by extrnal means,
but that _might_ be desired result)


Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 block.c   | 4 +++-
 block/qcow2.c | 1 +
 include/block/block.h | 1 +
 include/block/block_int.h | 1 +
 qemu-img-cmds.hx  | 4 ++--
 qemu-img.c| 8 +++-
 qemu-img.texi | 6 +-
 7 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/block.c b/block.c
index 5944124845..414303f76d 100644
--- a/block.c
+++ b/block.c
@@ -6141,6 +6141,7 @@ void bdrv_remove_aio_context_notifier(BlockDriverState 
*bs,
 
 int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+   bool force,
Error **errp)
 {
 if (!bs->drv) {
@@ -6152,7 +6153,8 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts 
*opts,
bs->drv->format_name);
 return -ENOTSUP;
 }
-return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
+return bs->drv->bdrv_amend_options(bs, opts, status_cb,
+   cb_opaque, force, errp);
 }
 
 /* This function will be called by the bdrv_recurse_is_first_non_filter method
diff --git a/block/qcow2.c b/block/qcow2.c
index 5bdb8b18f4..0618a63793 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -4822,6 +4822,7 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
 static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque,
+   bool force,
Error **errp)
 {
 BDRVQcow2State *s = bs->opaque;
diff --git a/include/block/block.h b/include/block/block.h
index 124ad40809..6bc89c7667 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -400,6 +400,7 @@ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, 
int64_t offset,
   int64_t total_work_size, void *opaque);
 int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+   bool force,
Error **errp);
 
 /* external snapshots */
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 0422acdf1c..5ea30f9d58 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -411,6 +411,7 @@ struct BlockDriver {
 int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
   BlockDriverAmendStatusCB *status_cb,
   void *cb_opaque,
+  bool force,
   Error **errp);
 
 void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 1c93e6d185..323ea10ad0 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -14,9 +14,9 @@ STEXI
 ETEXI
 
 DEF("amend", img_amend,
-"amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] 
-o options filename")
+"amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] 
[--force] -o options filename")
 STEXI
-@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] 
[-t @var{cache}] -o @var{options} @var{filename}
+@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] 
[-t @var{cache}] [--force] -o @var{options} @var{filename}
 ETEXI
 
 DEF("bench", img_bench,
diff --git a/qemu-img.c b/qemu-img.c
index 4ee436fc94..30300870ff 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -70,6 +70,7 @@ enum {
 OPTION_PREALLOCATION = 265,
 OPTION_SHRINK = 266,
 OPTION_SALVAGE = 267,
+OPTION_FORCE = 268,
 };
 
 typedef enum OutputFormat {
@@ -3915,6 +3916,7 @@ static int img_amend(int argc, char **argv)
 BlockBackend *blk = NULL;
 BlockDriverState *bs = NULL;
 bool image_opts = false;
+bool force = false;
 
 cache = BDRV_DEFAULT_CACHE;
 for (;;) {
@@ -3922,6 +3924,7 @@ static int img_amend(int argc, char **argv)
 {"help", no_argument, 0, 'h'},
 {"object", required_argument, 0, OPTION_OBJECT},
 {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+{"force", no_argument, 0, OPTION_FORCE},
 {0, 0, 0, 0}
 };
 c = getopt_long(argc, argv, ":ho:f:t:pq",
@@ -3977,6 +3980,9 @@ static int img_amend(int argc, char **argv)
 case OPTION_IMA

[Qemu-block] [PATCH v2 02/11] qcrypto-luks: extend the create options for upcoming encryption key management

2019-09-12 Thread Maxim Levitsky
Now you can specify which slot to put the encryption key to
Plus add 'active' option which will let  user erase the key secret
instead of adding it.
Check that active=true it when creating.

Signed-off-by: Maxim Levitsky 
---
 block/crypto.c |  2 ++
 block/crypto.h | 16 +++
 block/qcow2.c  |  2 ++
 crypto/block-luks.c| 26 +++---
 qapi/crypto.json   | 19 ++
 tests/qemu-iotests/082.out | 54 ++
 6 files changed, 115 insertions(+), 4 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index 6e822c6e50..a6a3e1f1d8 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -144,6 +144,8 @@ static QemuOptsList block_crypto_create_opts_luks = {
 BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
 BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
 BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
+BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT(""),
+BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE(""),
 { /* end of list */ }
 },
 };
diff --git a/block/crypto.h b/block/crypto.h
index b935695e79..05cc43d9bc 100644
--- a/block/crypto.h
+++ b/block/crypto.h
@@ -35,12 +35,14 @@
 "ID of the secret that provides the AES encryption key")
 
 #define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret"
+#define BLOCK_CRYPTO_OPT_LUKS_SLOT "slot"
 #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode"
 #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
+#define BLOCK_CRYPTO_OPT_LUKS_ACTIVE "active"
 
 #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix)\
 BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \
@@ -88,6 +90,20 @@
 .help = "Time to spend in PBKDF in milliseconds", \
 }
 
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT(prefix)   \
+{ \
+.name = prefix BLOCK_CRYPTO_OPT_LUKS_SLOT,   \
+.type = QEMU_OPT_NUMBER,  \
+.help = "Controls the slot where the secret is added/erased", \
+}
+
+#define BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE(prefix)   \
+{ \
+.name = prefix BLOCK_CRYPTO_OPT_LUKS_ACTIVE,   \
+.type = QEMU_OPT_BOOL,  \
+.help = "Controls if the added secret is added or erased", \
+}
+
 QCryptoBlockCreateOptions *
 block_crypto_create_opts_init(QDict *opts, Error **errp);
 
diff --git a/block/qcow2.c b/block/qcow2.c
index 0882ff6e92..5bdb8b18f4 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -5166,6 +5166,8 @@ static QemuOptsList qcow2_create_opts = {
 BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
 BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
 BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
+BLOCK_CRYPTO_OPT_DEF_LUKS_SLOT("encrypt."),
+BLOCK_CRYPTO_OPT_DEF_LUKS_ACTIVE("encrypt."),
 {
 .name = BLOCK_OPT_CLUSTER_SIZE,
 .type = QEMU_OPT_SIZE,
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 6c53bdc428..fed80e6646 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -1211,6 +1211,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 const char *hash_alg;
 g_autofree char *cipher_mode_spec = NULL;
 uint64_t iters;
+unsigned int slot_idx = 0;
 
 memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
 if (!luks_opts.has_iter_time) {
@@ -1244,12 +1245,30 @@ qcrypto_block_luks_create(QCryptoBlock *block,
 luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg;
 luks->hash_alg = luks_opts.hash_alg;
 
+if (luks_opts.has_active && !luks_opts.active) {
+error_setg(errp,
+   "For image creation, the added secret must be active!");
+goto error;
+
+}
+
+if (luks_opts.has_slot) {
+if (luks_opts.slot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS ||
+luks_opts.slot < 0) {
+error_setg(errp,
+   "Invalid slot %" PRId64 " is specified",
+   luks_opts.slot);
+goto error;
+}
+slot_idx = (unsigned int)luks_opts.slot;
+}
+
 
 /* Note we're allowing ivgen_hash_alg to be set even for
  * non-essiv iv generators that don't need a hash. It will
  * be silently ignored, for compatibility with dm-crypt */
 
-   

[Qemu-block] [PATCH v2 06/11] qcow2: implement crypto amend options

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 block/qcow2.c | 77 +--
 1 file changed, 62 insertions(+), 15 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 0618a63793..26f83aeb44 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -172,6 +172,25 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock 
*block, size_t offset,
 return ret;
 }
 
+static QCryptoBlockCreateOptions*
+qcow2_extract_crypto_create_opts(QemuOpts *opts, const char *fmt, Error **errp)
+{
+QDict *cryptoopts_qdict;
+QCryptoBlockCreateOptions *cryptoopts;
+QDict *opts_qdict;
+
+/* Extract "encrypt." options into a qdict */
+opts_qdict = qemu_opts_to_qdict(opts, NULL);
+qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
+qobject_unref(opts_qdict);
+
+/* Build QCryptoBlockCreateOptions object from qdict */
+qdict_put_str(cryptoopts_qdict, "format", "luks");
+cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
+qobject_unref(cryptoopts_qdict);
+return cryptoopts;
+}
+
 
 /* 
  * read qcow2 extension and fill bs
@@ -4365,20 +4384,10 @@ static ssize_t 
qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
 static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
  Error **errp)
 {
-QDict *opts_qdict;
-QDict *cryptoopts_qdict;
 QCryptoBlockCreateOptions *cryptoopts;
 QCryptoBlock *crypto;
 
-/* Extract "encrypt." options into a qdict */
-opts_qdict = qemu_opts_to_qdict(opts, NULL);
-qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
-qobject_unref(opts_qdict);
-
-/* Build QCryptoBlockCreateOptions object from qdict */
-qdict_put_str(cryptoopts_qdict, "format", "luks");
-cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
-qobject_unref(cryptoopts_qdict);
+cryptoopts = qcow2_extract_crypto_create_opts(opts, "luks", errp);
 if (!cryptoopts) {
 return false;
 }
@@ -4755,6 +4764,7 @@ typedef enum Qcow2AmendOperation {
  * invocation from an operation change */
 QCOW2_NO_OPERATION = 0,
 
+QCOW2_UPDATING_ENCRYPTION,
 QCOW2_CHANGING_REFCOUNT_ORDER,
 QCOW2_DOWNGRADING,
 } Qcow2AmendOperation;
@@ -4839,6 +4849,7 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 int ret;
 QemuOptDesc *desc = opts->list->desc;
 Qcow2AmendHelperCBInfo helper_cb_info;
+bool encryption_update = false;
 
 while (desc && desc->name) {
 if (!qemu_opt_find(opts, desc->name)) {
@@ -4887,9 +4898,22 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 return -ENOTSUP;
 }
 } else if (g_str_has_prefix(desc->name, "encrypt.")) {
-error_setg(errp,
-   "Changing the encryption parameters is not supported");
-return -ENOTSUP;
+
+if (!s->crypto) {
+error_setg(errp,
+   "Can't amend encryption options - encryption not 
present");
+return -EINVAL;
+
+}
+
+if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
+error_setg(errp,
+   "Only LUKS encryption options can be amended");
+return -ENOTSUP;
+}
+
+encryption_update = true;
+
 } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
 cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
  cluster_size);
@@ -4939,7 +4963,8 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 .original_status_cb = status_cb,
 .original_cb_opaque = cb_opaque,
 .total_operations = (new_version < old_version)
-  + (s->refcount_bits != refcount_bits)
+  + (s->refcount_bits != refcount_bits) +
+  (encryption_update == true)
 };
 
 /* Upgrade first (some features may require compat=1.1) */
@@ -4953,6 +4978,28 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 }
 }
 
+if (encryption_update) {
+QCryptoBlockCreateOptions *cryptoopts;
+
+cryptoopts = qcow2_extract_crypto_create_opts(opts, "luks", errp);
+if (!cryptoopts) {
+return -EINVAL;
+}
+
+helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
+
+ret = qcrypto_block_amend_options(s->crypto,
+  qcow2_crypto_hdr_read_func,
+  qcow2_crypto_hdr_write_func,
+  bs,
+

[Qemu-block] [PATCH v2 10/11] iotests: filter few more luks specific create options

2019-09-12 Thread Maxim Levitsky
Those options are test input anyway, and this allows more tests
to be able to have same output on both qcow2 luks encrypted images
and raw luks images

Signed-off-by: Maxim Levitsky 
---
 tests/qemu-iotests/087.out   | 6 +++---
 tests/qemu-iotests/134.out   | 2 +-
 tests/qemu-iotests/158.out   | 4 ++--
 tests/qemu-iotests/188.out   | 2 +-
 tests/qemu-iotests/189.out   | 4 ++--
 tests/qemu-iotests/198.out   | 4 ++--
 tests/qemu-iotests/common.filter | 6 --
 7 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out
index 2d92ea847b..b61ba638af 100644
--- a/tests/qemu-iotests/087.out
+++ b/tests/qemu-iotests/087.out
@@ -34,7 +34,7 @@ QMP_VERSION
 
 === Encrypted image QCow ===
 
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
 Testing:
 QMP_VERSION
 {"return": {}}
@@ -46,7 +46,7 @@ QMP_VERSION
 
 === Encrypted image LUKS ===
 
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encrypt.format=luks 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
 Testing:
 QMP_VERSION
 {"return": {}}
@@ -58,7 +58,7 @@ QMP_VERSION
 
 === Missing driver ===
 
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
 Testing: -S
 QMP_VERSION
 {"return": {}}
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
index 09d46f6b17..4abc5b5f7d 100644
--- a/tests/qemu-iotests/134.out
+++ b/tests/qemu-iotests/134.out
@@ -1,5 +1,5 @@
 QA output created by 134
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
 
 == reading whole image ==
 read 134217728/134217728 bytes at offset 0
diff --git a/tests/qemu-iotests/158.out b/tests/qemu-iotests/158.out
index 6def216e55..f28a17626b 100644
--- a/tests/qemu-iotests/158.out
+++ b/tests/qemu-iotests/158.out
@@ -1,6 +1,6 @@
 QA output created by 158
 == create base ==
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on 
encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 encryption=on
 
 == writing whole image ==
 wrote 134217728/134217728 bytes at offset 0
@@ -10,7 +10,7 @@ wrote 134217728/134217728 bytes at offset 0
 read 134217728/134217728 bytes at offset 0
 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 == create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 
backing_file=TEST_DIR/t.IMGFMT.base encryption=on encrypt.key-secret=sec0
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 
backing_file=TEST_DIR/t.IMGFMT.base encryption=on
 
 == writing part of a cluster ==
 wrote 1024/1024 bytes at offset 0
diff --git a/tests/qemu-iotests/188.out b/tests/qemu-iotests/188.out
index c568ef3701..5426861b18 100644
--- a/tests/qemu-iotests/188.out
+++ b/tests/qemu-iotests/188.out
@@ -1,5 +1,5 @@
 QA output created by 188
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 encrypt.format=luks 
encrypt.key-secret=sec0 encrypt.iter-time=10
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216
 
 == reading whole image ==
 read 16777216/16777216 bytes at offset 0
diff --git a/tests/qemu-iotests/189.out b/tests/qemu-iotests/189.out
index a0b7c9c24c..bc213cbe14 100644
--- a/tests/qemu-iotests/189.out
+++ b/tests/qemu-iotests/189.out
@@ -1,6 +1,6 @@
 QA output created by 189
 == create base ==
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 
encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216
 
 == writing whole image ==
 wrote 16777216/16777216 bytes at offset 0
@@ -10,7 +10,7 @@ wrote 16777216/16777216 bytes at offset 0
 read 16777216/16777216 bytes at offset 0
 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 == create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 
backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 
encrypt.iter-time=10
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 
backing_file=TEST_DIR/t.IMGFMT.base
 
 == writing part of a cluster ==
 wrote 1024/1024 bytes at offset 0
diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out
index e86b175e39..2eff420795 100644
--- a/tests/qemu-iotests/198.out
+++ b/tests/qemu-iotests/198.out
@@ -1,12 +1,12 @@
 QA output created by 198
 == create base ==
-Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 
encrypt.format=luks encrypt.key-secret=s

[Qemu-block] [PATCH v2 11/11] iotests : add tests for encryption key management

2019-09-12 Thread Maxim Levitsky
Note that currently I add tests 300-302, which are
placeholders to ease the rebase. In final version
of these patches I will update these.

Signed-off-by: Maxim Levitsky 
---
 tests/qemu-iotests/300 | 202 +
 tests/qemu-iotests/300.out |  98 +++
 tests/qemu-iotests/301 |  90 +
 tests/qemu-iotests/301.out |  30 +
 tests/qemu-iotests/302 | 252 +
 tests/qemu-iotests/302.out |  18 +++
 tests/qemu-iotests/303 | 228 +
 tests/qemu-iotests/303.out |  28 +
 tests/qemu-iotests/group   |   9 ++
 9 files changed, 955 insertions(+)
 create mode 100755 tests/qemu-iotests/300
 create mode 100644 tests/qemu-iotests/300.out
 create mode 100755 tests/qemu-iotests/301
 create mode 100644 tests/qemu-iotests/301.out
 create mode 100644 tests/qemu-iotests/302
 create mode 100644 tests/qemu-iotests/302.out
 create mode 100644 tests/qemu-iotests/303
 create mode 100644 tests/qemu-iotests/303.out

diff --git a/tests/qemu-iotests/300 b/tests/qemu-iotests/300
new file mode 100755
index 00..5b65ef95de
--- /dev/null
+++ b/tests/qemu-iotests/300
@@ -0,0 +1,202 @@
+#!/usr/bin/env bash
+#
+# Test encryption key management with luks
+# Based on 134
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mlevi...@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1   # failure is the default!
+
+_cleanup()
+{
+   _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2 luks
+_supported_proto file #TODO
+
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
+
+if [ "$IMGFMT" = "qcow2" ] ; then
+   PR="encrypt."
+   EXTRA_IMG_ARGS="-o encrypt.format=luks"
+fi
+
+
+# secrets: you are supposed to see the password as ***, see :-)
+S0="--object secret,id=sec0,data=hunter0"
+S1="--object secret,id=sec1,data=hunter1"
+S2="--object secret,id=sec2,data=hunter2"
+S3="--object secret,id=sec3,data=hunter3"
+S4="--object secret,id=sec4,data=hunter4"
+SECRETS="$S0 $S1 $S2 $S3 $S4"
+
+# image with given secret
+IMGS0="--image-opts 
driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec0"
+IMGS1="--image-opts 
driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec1"
+IMGS2="--image-opts 
driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec2"
+IMGS3="--image-opts 
driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec3"
+IMGS4="--image-opts 
driver=$IMGFMT,file.filename=$TEST_IMG,${PR}key-secret=sec4"
+
+
+echo "== creating a test image =="
+_make_test_img $S4 $EXTRA_IMG_ARGS -o 
${PR}key-secret=sec4,${PR}iter-time=10,${PR}slot=4 32M
+
+echo
+echo "== test that key 4 opens the image =="
+$QEMU_IO $S4 -c "read 0 4096" $IMGS4 | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== adding a password to slot 0 =="
+$QEMU_IMG amend $SECRETS $IMGS4 -o ${PR}key-secret=sec0,${PR}iter-time=10
+echo "== adding a password to slot 1 =="
+$QEMU_IMG amend $SECRETS $IMGS0 -o ${PR}key-secret=sec1,${PR}iter-time=10
+echo "== adding a password to slot 3 =="
+$QEMU_IMG amend $SECRETS $IMGS1 -o 
${PR}key-secret=sec3,${PR}iter-time=10,${PR}slot=3
+echo "== adding a password to slot 2 =="
+$QEMU_IMG amend $SECRETS $IMGS3 -o ${PR}key-secret=sec2,${PR}iter-time=10
+
+
+echo "== erase slot 4 =="
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}active=off,${PR}slot=4 | 
_filter_img_create
+
+
+echo
+echo "== all secrets should work =="
+for IMG in "$IMGS0" "$IMGS1" "$IMGS2" "$IMGS3"; do
+   $QEMU_IO $SECRETS -c "read 0 4096" $IMG | _filter_qemu_io | 
_filter_testdir
+done
+
+echo
+echo "== erase slot 0 and try it =="
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}active=off,${PR}key-secret=sec0 | 
_filter_img_create
+$QEMU_IO $SECRETS -c "read 0 4096" $IMGS0 | _filter_qemu_io | _filter_testdir
+
+echo
+echo "== erase slot 2 and try it =="
+$QEMU_IMG amend $SECRETS $IMGS1 -o ${PR}active=off,${PR}slot=

[Qemu-block] [PATCH v2 05/11] block/crypto: implement the encryption key management

2019-09-12 Thread Maxim Levitsky
This implements the encryption key management
using the generic code in qcrypto layer
(currently only for qemu-img amend)

This code adds another 'write_func' because the initialization
write_func works directly on the underlying file,
because during the creation, there is no open instance
of the luks driver, but during regular use, we have it,
and should use it instead.


This commit also adds a 'hack/workaround' I and Kevin Wolf (thanks)
made to make the driver still support write sharing,
but be safe against concurrent  metadata update (the keys)
Eventually write sharing for luks driver will be deprecated
and removed together with this hack.

The hack is that we ask (as a format driver) for
BLK_PERM_CONSISTENT_READ always
(technically always unless opened with BDRV_O_NO_IO)

and then when we want to update the keys, we
unshare that permission. So if someone else
has the image open, even readonly, this will fail.

Also thanks to Daniel Berrange for the variant of
that hack that involves asking for read,
rather that write permission

Signed-off-by: Maxim Levitsky 
---
 block/crypto.c | 118 +++--
 1 file changed, 115 insertions(+), 3 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index a6a3e1f1d8..f42fa057e6 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -36,6 +36,7 @@ typedef struct BlockCrypto BlockCrypto;
 
 struct BlockCrypto {
 QCryptoBlock *block;
+bool updating_keys;
 };
 
 
@@ -70,6 +71,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
 return ret;
 }
 
+static ssize_t block_crypto_write_func(QCryptoBlock *block,
+   size_t offset,
+   const uint8_t *buf,
+   size_t buflen,
+   void *opaque,
+   Error **errp)
+{
+BlockDriverState *bs = opaque;
+ssize_t ret;
+
+ret = bdrv_pwrite(bs->file, offset, buf, buflen);
+if (ret < 0) {
+error_setg_errno(errp, -ret, "Could not write encryption header");
+return ret;
+}
+return ret;
+}
+
 
 struct BlockCryptoCreateData {
 BlockBackend *blk;
@@ -647,6 +666,100 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, 
Error **errp)
 return spec_info;
 }
 
+
+static int
+block_crypto_amend_options(BlockDriverState *bs,
+   QemuOpts *opts,
+   BlockDriverAmendStatusCB *status_cb,
+   void *cb_opaque,
+   bool force,
+   Error **errp)
+{
+BlockCrypto *crypto = bs->opaque;
+QDict *cryptoopts = NULL;
+QCryptoBlockCreateOptions *amend_options = NULL;
+int ret;
+
+assert(crypto);
+assert(crypto->block);
+
+crypto->updating_keys = true;
+
+ret = bdrv_child_refresh_perms(bs, bs->file, errp);
+if (ret < 0) {
+goto cleanup;
+}
+
+cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
+ &block_crypto_create_opts_luks,
+ true);
+
+qdict_put_str(cryptoopts, "format", "luks");
+amend_options = block_crypto_create_opts_init(cryptoopts, errp);
+if (!amend_options) {
+ret = -EINVAL;
+goto cleanup;
+}
+
+ret = qcrypto_block_amend_options(crypto->block,
+  block_crypto_read_func,
+  block_crypto_write_func,
+  bs,
+  amend_options,
+  force,
+  errp);
+cleanup:
+crypto->updating_keys = false;
+bdrv_child_refresh_perms(bs, bs->file, errp);
+qapi_free_QCryptoBlockCreateOptions(amend_options);
+qobject_unref(cryptoopts);
+return ret;
+}
+
+
+static void
+block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
+ const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+
+BlockCrypto *crypto = bs->opaque;
+
+/*
+ * Ask for consistent read permission so that if
+ * someone else tries to open this image with this permission
+ * neither will be able to edit encryption keys
+ */
+if (!(bs->open_flags & BDRV_O_NO_IO)) {
+perm |= BLK_PERM_CONSISTENT_READ;
+}
+
+/*
+ * This driver doesn't modify LUKS metadata except
+ * when updating the encryption slots.
+ * Thus unlike a proper format driver we don't ask for
+ * shared write permission. However we need it
+ * when we area updating keys, to ensure that only w

[Qemu-block] [PATCH v3 3/3] qemu-iotests: Add test for bz #1745922

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 tests/qemu-iotests/263 | 91 ++
 tests/qemu-iotests/263.out | 40 +
 tests/qemu-iotests/group   |  1 +
 3 files changed, 132 insertions(+)
 create mode 100755 tests/qemu-iotests/263
 create mode 100644 tests/qemu-iotests/263.out

diff --git a/tests/qemu-iotests/263 b/tests/qemu-iotests/263
new file mode 100755
index 00..afbd668cda
--- /dev/null
+++ b/tests/qemu-iotests/263
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+#
+# Test encrypted write that crosses cluster boundary of two unallocated 
clusters
+# Based on 188
+#
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mlevi...@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1   # failure is the default!
+
+_cleanup()
+{
+   _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+
+size=1M
+
+SECRET="secret,id=sec0,data=astrochicken"
+QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
+
+
+_run_test()
+{
+   echo "== reading the whole image =="
+   $QEMU_IO --object $SECRET -c "read -P 0 0 $size" --image-opts $1 | 
_filter_qemu_io | _filter_testdir
+
+   echo
+   echo "== write two 512 byte sectors on a cluster boundary =="
+   $QEMU_IO --object $SECRET -c "write -P 0xAA 0xFE00 0x400" --image-opts 
$1 | _filter_qemu_io | _filter_testdir
+
+   echo
+   echo "== verify that the rest of the image is not changed =="
+   $QEMU_IO --object $SECRET -c "read -P 0x00 0x0 0xFE00" --image-opts 
$1 | _filter_qemu_io | _filter_testdir
+   $QEMU_IO --object $SECRET -c "read -P 0xAA 0x0FE00 0x400" --image-opts 
$1 | _filter_qemu_io | _filter_testdir
+   $QEMU_IO --object $SECRET -c "read -P 0x00 0x10200 0xEFE00" 
--image-opts $1 | _filter_qemu_io | _filter_testdir
+
+}
+
+
+echo
+echo "testing LUKS qcow2 encryption"
+echo
+
+_make_test_img --object $SECRET -o 
"encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10,cluster_size=64K"
 $size
+_run_test "driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG"
+_cleanup_test_img
+
+echo
+echo "testing legacy AES qcow2 encryption"
+echo
+
+
+_make_test_img --object $SECRET -o 
"encrypt.format=aes,encrypt.key-secret=sec0,cluster_size=64K" $size
+_run_test "driver=$IMGFMT,encrypt.key-secret=sec0,file.filename=$TEST_IMG"
+_cleanup_test_img
+
+
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/263.out b/tests/qemu-iotests/263.out
new file mode 100644
index 00..0c982c55cb
--- /dev/null
+++ b/tests/qemu-iotests/263.out
@@ -0,0 +1,40 @@
+QA output created by 263
+
+testing LUKS qcow2 encryption
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=luks 
encrypt.key-secret=sec0 encrypt.iter-time=10
+== reading the whole image ==
+read 1048576/1048576 bytes at offset 0
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== write two 512 byte sectors on a cluster boundary ==
+wrote 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify that the rest of the image is not changed ==
+read 65024/65024 bytes at offset 0
+63.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 982528/982528 bytes at offset 66048
+959.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+testing legacy AES qcow2 encryption
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 encrypt.format=aes 
encrypt.key-secret=sec0
+== reading the whole image ==
+read 1048576/1048576 bytes at offset 0
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== write two 512 byte sectors on a cluster boundary ==
+wrote 1024/1024 bytes at offset 65024
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== verify that the rest of the image is not changed ==
+read 65024/65024 bytes at offset 0
+63.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and

[Qemu-block] [PATCH v2 07/11] block: add x-blockdev-amend qmp command

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
---
 block/Makefile.objs   |   2 +-
 block/amend.c | 116 ++
 include/block/block_int.h |  23 ++--
 qapi/block-core.json  |  26 +
 qapi/job.json |   4 +-
 5 files changed, 163 insertions(+), 8 deletions(-)
 create mode 100644 block/amend.c

diff --git a/block/Makefile.objs b/block/Makefile.objs
index 35f3bca4d9..10d0308792 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -18,7 +18,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o
 block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
 block-obj-$(CONFIG_POSIX) += file-posix.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
-block-obj-y += null.o mirror.o commit.o io.o create.o
+block-obj-y += null.o mirror.o commit.o io.o create.o amend.o
 block-obj-y += throttle-groups.o
 block-obj-$(CONFIG_LINUX) += nvme.o
 
diff --git a/block/amend.c b/block/amend.c
new file mode 100644
index 00..9bd28e08e7
--- /dev/null
+++ b/block/amend.c
@@ -0,0 +1,116 @@
+/*
+ * Block layer code related to image options amend
+ *
+ * Copyright (c) 2018 Kevin Wolf 
+ * Copyright (c) 2019 Maxim Levitsky 
+ *
+ * Heavily based on create.c
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block_int.h"
+#include "qemu/job.h"
+#include "qemu/main-loop.h"
+#include "qapi/qapi-commands-block-core.h"
+#include "qapi/qapi-visit-block-core.h"
+#include "qapi/clone-visitor.h"
+#include "qapi/error.h"
+
+typedef struct BlockdevAmendJob {
+Job common;
+BlockdevCreateOptions *opts;
+BlockDriverState *bs;
+bool force;
+} BlockdevAmendJob;
+
+static int coroutine_fn blockdev_amend_run(Job *job, Error **errp)
+{
+BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common);
+int ret;
+
+job_progress_set_remaining(&s->common, 1);
+ret = s->bs->drv->bdrv_co_amend(s->bs, s->opts, s->force, errp);
+job_progress_update(&s->common, 1);
+
+qapi_free_BlockdevCreateOptions(s->opts);
+
+return ret;
+}
+
+static const JobDriver blockdev_amend_job_driver = {
+.instance_size = sizeof(BlockdevAmendJob),
+.job_type  = JOB_TYPE_AMEND,
+.run   = blockdev_amend_run,
+};
+
+void qmp_x_blockdev_amend(const char *job_id,
+const char *node_name,
+BlockdevCreateOptions *options,
+bool has_force,
+bool force,
+Error **errp)
+{
+BlockdevAmendJob *s;
+const char *fmt = BlockdevDriver_str(options->driver);
+BlockDriver *drv = bdrv_find_format(fmt);
+BlockDriverState *bs = bdrv_find_node(node_name);
+
+/*
+ * If the driver is in the schema, we know that it exists. But it may not
+ * be whitelisted.
+ */
+assert(drv);
+if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
+error_setg(errp, "Driver is not whitelisted");
+return;
+}
+
+if (bs->drv != drv) {
+error_setg(errp,
+   "x-blockdev-amend doesn't support changing the block 
driver");
+return;
+
+}
+
+/* Error out if the driver doesn't support .bdrv_co_amend */
+if (!drv->bdrv_co_amend) {
+error_setg(errp, "Driver does not support x-blockdev-amend");
+return;
+}
+
+/*
+ * Create the block job
+ * TODO Running in the main context. Block drivers need to error out or add
+ * locking when they use a BDS in a different AioContext.
+ */
+s = job_create(job_id, &blockdev_amend_job_driver, NULL,
+   qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
+   NULL, NULL, errp);
+if (!s) {
+   

[Qemu-block] [PATCH v2 08/11] block/crypto: implement blockdev-amend

2019-09-12 Thread Maxim Levitsky
Signed-off-by: Maxim Levitsky 
Reviewed-by: Daniel P. Berrangé 
---
 block/crypto.c   | 85 ++--
 qapi/block-core.json |  7 ++--
 2 files changed, 71 insertions(+), 21 deletions(-)

diff --git a/block/crypto.c b/block/crypto.c
index f42fa057e6..5905f7f520 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -534,6 +534,17 @@ block_crypto_co_create_luks(BlockdevCreateOptions 
*create_options, Error **errp)
 assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
 luks_opts = &create_options->u.luks;
 
+if (!luks_opts->has_size) {
+error_setg(errp, "'size' is manadatory for image creation");
+return -EINVAL;
+}
+
+if (!luks_opts->has_file) {
+error_setg(errp, "'file' is manadatory for image creation");
+return -EINVAL;
+}
+
+
 bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
 if (bs == NULL) {
 return -EIO;
@@ -667,6 +678,39 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, 
Error **errp)
 }
 
 
+static int
+block_crypto_amend_options_generic(BlockDriverState *bs,
+   QCryptoBlockCreateOptions *amend_options,
+   bool force,
+   Error **errp)
+{
+BlockCrypto *crypto = bs->opaque;
+int ret = -1;
+
+assert(crypto);
+assert(crypto->block);
+
+/* apply for exclusive write permissions to the underlying file*/
+crypto->updating_keys = true;
+ret = bdrv_child_refresh_perms(bs, bs->file, errp);
+if (ret) {
+goto cleanup;
+}
+
+ret = qcrypto_block_amend_options(crypto->block,
+  block_crypto_read_func,
+  block_crypto_write_func,
+  bs,
+  amend_options,
+  force,
+  errp);
+cleanup:
+/* release exclusive write permissions to the underlying file*/
+crypto->updating_keys = false;
+bdrv_child_refresh_perms(bs, bs->file, errp);
+return ret;
+}
+
 static int
 block_crypto_amend_options(BlockDriverState *bs,
QemuOpts *opts,
@@ -678,44 +722,45 @@ block_crypto_amend_options(BlockDriverState *bs,
 BlockCrypto *crypto = bs->opaque;
 QDict *cryptoopts = NULL;
 QCryptoBlockCreateOptions *amend_options = NULL;
-int ret;
+int ret = -EINVAL;
 
 assert(crypto);
 assert(crypto->block);
 
-crypto->updating_keys = true;
-
-ret = bdrv_child_refresh_perms(bs, bs->file, errp);
-if (ret < 0) {
-goto cleanup;
-}
-
 cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
  &block_crypto_create_opts_luks,
  true);
 
 qdict_put_str(cryptoopts, "format", "luks");
 amend_options = block_crypto_create_opts_init(cryptoopts, errp);
+
 if (!amend_options) {
-ret = -EINVAL;
 goto cleanup;
 }
 
-ret = qcrypto_block_amend_options(crypto->block,
-  block_crypto_read_func,
-  block_crypto_write_func,
-  bs,
-  amend_options,
-  force,
-  errp);
+ret = block_crypto_amend_options_generic(bs, amend_options, force, errp);
 cleanup:
-crypto->updating_keys = false;
-bdrv_child_refresh_perms(bs, bs->file, errp);
 qapi_free_QCryptoBlockCreateOptions(amend_options);
 qobject_unref(cryptoopts);
 return ret;
 }
 
+static int
+coroutine_fn block_crypto_co_amend(BlockDriverState *bs,
+   BlockdevCreateOptions *opts,
+   bool force,
+   Error **errp)
+{
+QCryptoBlockCreateOptions amend_opts;
+
+amend_opts = (QCryptoBlockCreateOptions) {
+.format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
+.u.luks = *qapi_BlockdevCreateOptionsLUKS_base(&opts->u.luks),
+};
+
+return block_crypto_amend_options_generic(bs, &amend_opts, force, errp);
+}
+
 
 static void
 block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
@@ -750,7 +795,8 @@ block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
  */
 
 if (crypto->updating_keys) {
-/*need exclusive write access for header update  */
+assert(!(bs->open_flags & BDRV_O_NO_IO));
+/*need exclusive read and write access for header update  */
 perm |= BLK_PERM_WRITE;
 shared &= ~(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE);
 }
@@ -786,6 +832,7 @@ static BlockDriver bdrv_crypto_luk

[Qemu-block] [PATCH v2 09/11] block/qcow2: implement blockdev-amend

2019-09-12 Thread Maxim Levitsky
Currently only for changing crypto parameters

Signed-off-by: Maxim Levitsky 
---
 block/qcow2.c| 71 
 qapi/block-core.json |  6 ++--
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 26f83aeb44..c8847ec6e2 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3079,6 +3079,18 @@ qcow2_co_create(BlockdevCreateOptions *create_options, 
Error **errp)
 assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2);
 qcow2_opts = &create_options->u.qcow2;
 
+if (!qcow2_opts->has_size) {
+error_setg(errp, "Size is manadatory for image creation");
+return -EINVAL;
+
+}
+
+if (!qcow2_opts->has_file) {
+error_setg(errp, "'file' is manadatory for image creation");
+return -EINVAL;
+
+}
+
 bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp);
 if (bs == NULL) {
 return -EIO;
@@ -5111,6 +5123,64 @@ static int qcow2_amend_options(BlockDriverState *bs, 
QemuOpts *opts,
 return 0;
 }
 
+
+static int coroutine_fn qcow2_co_amend(BlockDriverState *bs,
+   BlockdevCreateOptions *opts,
+   bool force,
+   Error **errp)
+{
+BlockdevCreateOptionsQcow2 *qopts = &opts->u.qcow2;
+BDRVQcow2State *s = bs->opaque;
+int ret;
+
+/*
+ * This is ugly as hell, in later versions of this patch
+ * something has to be done about this
+ */
+if (qopts->has_file || qopts->has_size || qopts->has_data_file ||
+qopts->has_data_file_raw || qopts->has_version ||
+qopts->has_backing_file || qopts->has_backing_fmt ||
+qopts->has_cluster_size || qopts->has_preallocation ||
+qopts->has_lazy_refcounts || qopts->has_refcount_bits) {
+
+error_setg(errp,
+"Only LUKS encryption options can be amended for qcow2 with 
blockdev-amend");
+return -EOPNOTSUPP;
+
+}
+
+if (qopts->has_encrypt) {
+if (!s->crypto) {
+error_setg(errp, "QCOW2 image is not encrypted, can't amend");
+return -EOPNOTSUPP;
+}
+
+if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
+error_setg(errp,
+   "Amend can't be used to change the qcow2 encryption 
format");
+return -EOPNOTSUPP;
+}
+
+if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
+error_setg(errp,
+   "Only LUKS encryption options can be amended for qcow2 
with blockdev-amend");
+return -EOPNOTSUPP;
+}
+
+ret = qcrypto_block_amend_options(s->crypto,
+  qcow2_crypto_hdr_read_func,
+  qcow2_crypto_hdr_write_func,
+  bs,
+  qopts->encrypt,
+  force,
+  errp);
+if (ret) {
+return ret;
+}
+}
+return 0;
+}
+
 /*
  * If offset or size are negative, respectively, they will not be included in
  * the BLOCK_IMAGE_CORRUPTED event emitted.
@@ -5303,6 +5373,7 @@ BlockDriver bdrv_qcow2 = {
 .mutable_opts= mutable_opts,
 .bdrv_co_check   = qcow2_co_check,
 .bdrv_amend_options  = qcow2_amend_options,
+.bdrv_co_amend   = qcow2_co_amend,
 
 .bdrv_detach_aio_context  = qcow2_detach_aio_context,
 .bdrv_attach_aio_context  = qcow2_attach_aio_context,
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 4a6db98938..0eb4e45168 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -4294,6 +4294,7 @@
 # Driver specific image creation options for qcow2.
 #
 # @file Node to create the image format on
+#   Mandatory for create
 # @data-fileNode to use as an external data file in which all guest
 #   data is stored so that only metadata remains in the qcow2
 #   file (since: 4.0)
@@ -4301,6 +4302,7 @@
 #   standalone (read-only) raw image without looking at qcow2
 #   metadata (default: false; since: 4.0)
 # @size Size of the virtual disk in bytes
+#   Mandatory for create
 # @version  Compatibility level (default: v3)
 # @backing-file File name of the backing file if a backing file
 #   should be used
@@ -4315,10 +4317,10 @@
 # Since: 2.12
 ##
 { 'struct': 'BlockdevCreateOptionsQcow2',
-  'data': { 'file': 'BlockdevRef',
+  'data': { '*file':'BlockdevRef',

[Qemu-block] [PATCH v3 2/3] block/qcow2: fix the corruption when rebasing luks encrypted files

2019-09-12 Thread Maxim Levitsky
This fixes subtle corruption introduced by luks threaded encryption
in commit 8ac0f15f335

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

The corruption happens when we do a write that
   * writes to two or more unallocated clusters at once
   * doesn't fully cover the first sector
   * doesn't fully cover the last sector

In this case, when allocating the new clusters we COW both areas
prior to the write and after the write, and we encrypt them.

The above mentioned commit accidentally made it so we encrypt the
second COW area using the physical cluster offset of the first area.

Fix this by:
 * Remove the offset_in_cluster parameter of do_perform_cow_encrypt,
   since it is misleading. That offset can be larger than cluster size
   currently.

   Instead just add the start and the end COW area offsets to both host
   and guest offsets that do_perform_cow_encrypt receives.

*  in do_perform_cow_encrypt, remove the cluster offset from the host_offset,
   and thus pass correctly to the qcow2_co_encrypt, the host cluster offset
   and full guest offset

In the bugreport that was triggered by rebasing a luks image to new,
zero filled base, which lot of such writes, and causes some files
with zero areas to contain garbage there instead.
But as described above it can happen elsewhere as well


Signed-off-by: Maxim Levitsky 
Reviewed-by: Vladimir Sementsov-Ogievskiy 
---
 block/qcow2-cluster.c | 29 +
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index b95e64c237..7203d4cb85 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -463,20 +463,21 @@ static int coroutine_fn 
do_perform_cow_read(BlockDriverState *bs,
 }
 
 static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
-uint64_t guest_cluster_offset,
-uint64_t host_cluster_offset,
-unsigned offset_in_cluster,
+uint64_t guest_offset,
+uint64_t host_offset,
 uint8_t *buffer,
 unsigned bytes)
 {
 if (bytes && bs->encrypted) {
 BDRVQcow2State *s = bs->opaque;
-assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
-assert((bytes & ~BDRV_SECTOR_MASK) == 0);
+
+assert(QEMU_IS_ALIGNED(guest_offset, BDRV_SECTOR_SIZE));
+assert(QEMU_IS_ALIGNED(host_offset, BDRV_SECTOR_SIZE));
+assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
 assert(s->crypto);
-if (qcow2_co_encrypt(bs, host_cluster_offset,
- guest_cluster_offset + offset_in_cluster,
- buffer, bytes) < 0) {
+
+if (qcow2_co_encrypt(bs, start_of_cluster(s, host_offset),
+ guest_offset, buffer, bytes) < 0) {
 return false;
 }
 }
@@ -890,11 +891,15 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta 
*m)
 
 /* Encrypt the data if necessary before writing it */
 if (bs->encrypted) {
-if (!do_perform_cow_encrypt(bs, m->offset, m->alloc_offset,
-start->offset, start_buffer,
+if (!do_perform_cow_encrypt(bs,
+m->offset + start->offset,
+m->alloc_offset + start->offset,
+start_buffer,
 start->nb_bytes) ||
-!do_perform_cow_encrypt(bs, m->offset, m->alloc_offset,
-end->offset, end_buffer, end->nb_bytes)) {
+!do_perform_cow_encrypt(bs,
+m->offset + end->offset,
+m->alloc_offset + end->offset,
+end_buffer, end->nb_bytes)) {
 ret = -EIO;
 goto fail;
 }
-- 
2.17.2




[Qemu-block] [PATCH v3 0/3] Fix qcow2+luks corruption introduced by commit 8ac0f15f335

2019-09-12 Thread Maxim Levitsky
Commit 8ac0f15f335 accidently broke the COW of non changed areas
of newly allocated clusters, when the write spans multiple clusters,
and needs COW both prior and after the write.
This results in 'after' COW area being encrypted with wrong
sector address, which render it corrupted.

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

CC: qemu-stable 

V2: grammar, spelling and code style fixes.
V3: more fixes after the review.

Best regards,
    Maxim Levitsky

Maxim Levitsky (3):
  block/qcow2: refactoring of threaded encryption code
  block/qcow2: fix the corruption when rebasing luks encrypted files
  qemu-iotests: Add test for bz #1745922

 block/qcow2-cluster.c  | 29 +++-
 block/qcow2-threads.c  | 63 --
 tests/qemu-iotests/263 | 91 ++
 tests/qemu-iotests/263.out | 40 +
 tests/qemu-iotests/group   |  1 +
 5 files changed, 199 insertions(+), 25 deletions(-)
 create mode 100755 tests/qemu-iotests/263
 create mode 100644 tests/qemu-iotests/263.out

-- 
2.17.2




Re: [Qemu-block] [Qemu-devel] [PATCH 1/2] LUKS: better error message when creating too large files

2019-09-12 Thread Maxim Levitsky
On Mon, 2019-07-22 at 10:05 +0100, Daniel P. Berrangé wrote:
> On Sun, Jul 21, 2019 at 09:15:07PM +0300, Maxim Levitsky wrote:
> > Currently if you attampt to create too large file with luks you
> > get the following error message:
> > 
> > Formatting 'test.luks', fmt=luks size=17592186044416 key-secret=sec0
> > qemu-img: test.luks: Could not resize file: File too large
> > 
> > While for raw format the error message is
> > qemu-img: test.img: The image size is too large for file format 'raw'
> > 
> > 
> > The reason for this is that qemu-img checks for errono of the failure,
> > and presents the later error when it is -EFBIG
> > 
> > However crypto generic code 'swallows' the errno and replaces it
> > with -EIO.
> > 
> > As an attempt to make it better, we can make luks driver,
> > detect -EFBIG and in this case present a better error message,
> > which is what this patch does
> > 
> > The new error message is:
> > 
> > qemu-img: error creating test.luks: The requested file size is too large
> > 
> > Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1534898
> > Signed-off-by: Maxim Levitsky 
> > ---
> >  block/crypto.c | 25 +
> >  1 file changed, 21 insertions(+), 4 deletions(-)
> 
> Reviewed-by: Daniel P. Berrangé 
> 
> Regards,
> Daniel
Hi!

Do you think that we should merge this or, shall I close the bugreport
as WONTFIX?

Best regards,
Maxim Levitsky





[Qemu-block] [PATCH v3 1/3] block/qcow2: refactoring of threaded encryption code

2019-09-12 Thread Maxim Levitsky
This commit tries to clarify few function arguments,
and add comments describing the encrypt/decrypt interface

Signed-off-by: Maxim Levitsky 
---
 block/qcow2-cluster.c |  8 +++---
 block/qcow2-threads.c | 63 ++-
 2 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index f09cc992af..b95e64c237 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -463,8 +463,8 @@ static int coroutine_fn 
do_perform_cow_read(BlockDriverState *bs,
 }
 
 static bool coroutine_fn do_perform_cow_encrypt(BlockDriverState *bs,
-uint64_t src_cluster_offset,
-uint64_t cluster_offset,
+uint64_t guest_cluster_offset,
+uint64_t host_cluster_offset,
 unsigned offset_in_cluster,
 uint8_t *buffer,
 unsigned bytes)
@@ -474,8 +474,8 @@ static bool coroutine_fn 
do_perform_cow_encrypt(BlockDriverState *bs,
 assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
 assert((bytes & ~BDRV_SECTOR_MASK) == 0);
 assert(s->crypto);
-if (qcow2_co_encrypt(bs, cluster_offset,
- src_cluster_offset + offset_in_cluster,
+if (qcow2_co_encrypt(bs, host_cluster_offset,
+ guest_cluster_offset + offset_in_cluster,
  buffer, bytes) < 0) {
 return false;
 }
diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c
index 3b1e63fe41..6da1838e95 100644
--- a/block/qcow2-threads.c
+++ b/block/qcow2-threads.c
@@ -234,15 +234,19 @@ static int qcow2_encdec_pool_func(void *opaque)
 }
 
 static int coroutine_fn
-qcow2_co_encdec(BlockDriverState *bs, uint64_t file_cluster_offset,
-  uint64_t offset, void *buf, size_t len, Qcow2EncDecFunc func)
+qcow2_co_encdec(BlockDriverState *bs, uint64_t host_cluster_offset,
+uint64_t guest_offset, void *buf, size_t len,
+Qcow2EncDecFunc func)
 {
 BDRVQcow2State *s = bs->opaque;
+
+uint64_t offset = s->crypt_physical_offset ?
+host_cluster_offset + offset_into_cluster(s, guest_offset) :
+guest_offset;
+
 Qcow2EncDecData arg = {
 .block = s->crypto,
-.offset = s->crypt_physical_offset ?
-  file_cluster_offset + offset_into_cluster(s, offset) :
-  offset,
+.offset = offset,
 .buf = buf,
 .len = len,
 .func = func,
@@ -251,18 +255,51 @@ qcow2_co_encdec(BlockDriverState *bs, uint64_t 
file_cluster_offset,
 return qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
 }
 
+
+/*
+ * qcow2_co_encrypt()
+ *
+ * Encrypts one or more contiguous aligned sectors
+ *
+ * @host_cluster_offset - underlying storage offset of the first cluster
+ * in which the encrypted data will be written
+ * Used as an initialization vector for encryption
+ *
+ * @guest_offset - guest (virtual) offset of the first sector of the
+ * data to be encrypted
+ * Used as an initialization vector for older, qcow2 native encryption
+ *
+ * @buf - buffer with the data to encrypt, that after encryption
+ *will be written to the underlying storage device at
+ *@host_cluster_offset
+ *
+ * @len - length of the buffer (in sector size multiplies)
+ *
+ * Note that the group of the sectors, don't have to be aligned
+ * on cluster boundary and can also cross a cluster boundary.
+ *
+ */
 int coroutine_fn
-qcow2_co_encrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
- uint64_t offset, void *buf, size_t len)
+qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
+ uint64_t guest_offset, void *buf, size_t len)
 {
-return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
- qcrypto_block_encrypt);
+return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
+   qcrypto_block_encrypt);
 }
 
+
+/*
+ * qcow2_co_decrypt()
+ *
+ * Decrypts one or more contiguous aligned sectors
+ * Similar to qcow2_co_encrypt
+ *
+ */
+
 int coroutine_fn
-qcow2_co_decrypt(BlockDriverState *bs, uint64_t file_cluster_offset,
- uint64_t offset, void *buf, size_t len)
+qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_cluster_offset,
+ uint64_t guest_offset, void *buf, size_t len)
 {
-return qcow2_co_encdec(bs, file_cluster_offset, offset, buf, len,
- qcrypto_block_decrypt);
+return qcow2_co_encdec(bs, host_cluster_offset, guest_offset, buf, len,
+   qcrypto_block_decrypt);
 }
-- 
2.17.2




<    1   2   3   4   5   6   7   8   9   10   >