qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qcow: convert QCow to use QCryptoBlock for encryption

This converts the qcow driver to make use of the QCryptoBlock
APIs for encrypting image content. This is only wired up to
permit use of the legacy QCow encryption format. Users who wish
to have the strong LUKS format should switch to qcow2 instead.

With this change it is now required to use the QCryptoSecret
object for providing passwords, instead of the current block
password APIs / interactive prompting.

$QEMU \
-object secret,id=sec0,file=/home/berrange/encrypted.pw \
-drive file=/home/berrange/encrypted.qcow,encrypt.format=aes,\
encrypt.key-secret=sec0

Though note that running QEMU system emulators with the AES
encryption is no longer supported, so while the above syntax
is valid, QEMU will refuse to actually run the VM in this
particular example.

Likewise when creating images with the legacy AES-CBC format

qemu-img create -f qcow \
--object secret,id=sec0,file=/home/berrange/encrypted.pw \
-o encrypt.format=aes,encrypt.key-secret=sec0 \
/home/berrange/encrypted.qcow 64M

Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-id: 20170623162419.26068-10-berrange@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>

authored by

Daniel P. Berrange and committed by
Max Reitz
d85f4222 1fad1f94

+158 -108
+10
block/crypto.c
··· 181 181 v, &ret->u.luks, &local_err); 182 182 break; 183 183 184 + case Q_CRYPTO_BLOCK_FORMAT_QCOW: 185 + visit_type_QCryptoBlockOptionsQCow_members( 186 + v, &ret->u.qcow, &local_err); 187 + break; 188 + 184 189 default: 185 190 error_setg(&local_err, "Unsupported block format %d", format); 186 191 break; ··· 225 230 case Q_CRYPTO_BLOCK_FORMAT_LUKS: 226 231 visit_type_QCryptoBlockCreateOptionsLUKS_members( 227 232 v, &ret->u.luks, &local_err); 233 + break; 234 + 235 + case Q_CRYPTO_BLOCK_FORMAT_QCOW: 236 + visit_type_QCryptoBlockOptionsQCow_members( 237 + v, &ret->u.qcow, &local_err); 228 238 break; 229 239 230 240 default:
+15 -5
block/crypto.h
··· 21 21 #ifndef BLOCK_CRYPTO_H__ 22 22 #define BLOCK_CRYPTO_H__ 23 23 24 + #define BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, helpstr) \ 25 + { \ 26 + .name = prefix BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, \ 27 + .type = QEMU_OPT_STRING, \ 28 + .help = helpstr, \ 29 + } 30 + 31 + #define BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET "key-secret" 32 + 33 + #define BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET(prefix) \ 34 + BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \ 35 + "ID of the secret that provides the AES encryption key") 36 + 24 37 #define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret" 25 38 #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg" 26 39 #define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode" ··· 30 43 #define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time" 31 44 32 45 #define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix) \ 33 - { \ 34 - .name = prefix BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, \ 35 - .type = QEMU_OPT_STRING, \ 36 - .help = "ID of the secret that provides the keyslot passphrase", \ 37 - } 46 + BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \ 47 + "ID of the secret that provides the keyslot passphrase") 38 48 39 49 #define BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG(prefix) \ 40 50 { \
+96 -102
block/qcow.c
··· 31 31 #include "qemu/bswap.h" 32 32 #include <zlib.h> 33 33 #include "qapi/qmp/qerror.h" 34 - #include "crypto/cipher.h" 34 + #include "qapi/qmp/qstring.h" 35 + #include "crypto/block.h" 35 36 #include "migration/blocker.h" 37 + #include "block/crypto.h" 36 38 37 39 /**************************************************************/ 38 40 /* QEMU COW block driver with compression and encryption support */ ··· 77 79 uint8_t *cluster_cache; 78 80 uint8_t *cluster_data; 79 81 uint64_t cluster_cache_offset; 80 - QCryptoCipher *cipher; /* NULL if no key yet */ 82 + QCryptoBlock *crypto; /* Disk encryption format driver */ 81 83 uint32_t crypt_method_header; 82 84 CoMutex lock; 83 85 Error *migration_blocker; ··· 97 99 return 0; 98 100 } 99 101 102 + static QemuOptsList qcow_runtime_opts = { 103 + .name = "qcow", 104 + .head = QTAILQ_HEAD_INITIALIZER(qcow_runtime_opts.head), 105 + .desc = { 106 + BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET("encrypt."), 107 + { /* end of list */ } 108 + }, 109 + }; 110 + 100 111 static int qcow_open(BlockDriverState *bs, QDict *options, int flags, 101 112 Error **errp) 102 113 { ··· 105 116 int ret; 106 117 QCowHeader header; 107 118 Error *local_err = NULL; 119 + QCryptoBlockOpenOptions *crypto_opts = NULL; 120 + unsigned int cflags = 0; 121 + QDict *encryptopts = NULL; 122 + const char *encryptfmt; 123 + 124 + qdict_extract_subqdict(options, &encryptopts, "encrypt."); 125 + encryptfmt = qdict_get_try_str(encryptopts, "format"); 108 126 109 127 bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, 110 128 false, errp); 111 129 if (!bs->file) { 112 - return -EINVAL; 130 + ret = -EINVAL; 131 + goto fail; 113 132 } 114 133 115 134 ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); ··· 155 174 goto fail; 156 175 } 157 176 158 - if (header.crypt_method > QCOW_CRYPT_AES) { 159 - error_setg(errp, "invalid encryption method in qcow header"); 160 - ret = -EINVAL; 161 - goto fail; 162 - } 163 - if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128, 164 - QCRYPTO_CIPHER_MODE_CBC)) { 165 - error_setg(errp, "AES cipher not available"); 166 - ret = -EINVAL; 167 - goto fail; 168 - } 169 177 s->crypt_method_header = header.crypt_method; 170 178 if (s->crypt_method_header) { 171 179 if (bdrv_uses_whitelist() && ··· 181 189 ret = -ENOSYS; 182 190 goto fail; 183 191 } 192 + if (s->crypt_method_header == QCOW_CRYPT_AES) { 193 + if (encryptfmt && !g_str_equal(encryptfmt, "aes")) { 194 + error_setg(errp, 195 + "Header reported 'aes' encryption format but " 196 + "options specify '%s'", encryptfmt); 197 + ret = -EINVAL; 198 + goto fail; 199 + } 200 + qdict_del(encryptopts, "format"); 201 + crypto_opts = block_crypto_open_opts_init( 202 + Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); 203 + if (!crypto_opts) { 204 + ret = -EINVAL; 205 + goto fail; 206 + } 184 207 208 + if (flags & BDRV_O_NO_IO) { 209 + cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; 210 + } 211 + s->crypto = qcrypto_block_open(crypto_opts, NULL, NULL, 212 + cflags, errp); 213 + if (!s->crypto) { 214 + ret = -EINVAL; 215 + goto fail; 216 + } 217 + } else { 218 + error_setg(errp, "invalid encryption method in qcow header"); 219 + ret = -EINVAL; 220 + goto fail; 221 + } 185 222 bs->encrypted = true; 223 + bs->valid_key = true; 186 224 } 187 225 s->cluster_bits = header.cluster_bits; 188 226 s->cluster_size = 1 << s->cluster_bits; ··· 266 304 goto fail; 267 305 } 268 306 307 + QDECREF(encryptopts); 308 + qapi_free_QCryptoBlockOpenOptions(crypto_opts); 269 309 qemu_co_mutex_init(&s->lock); 270 310 return 0; 271 311 ··· 274 314 qemu_vfree(s->l2_cache); 275 315 g_free(s->cluster_cache); 276 316 g_free(s->cluster_data); 317 + qcrypto_block_free(s->crypto); 318 + QDECREF(encryptopts); 319 + qapi_free_QCryptoBlockOpenOptions(crypto_opts); 277 320 return ret; 278 321 } 279 322 ··· 286 329 return 0; 287 330 } 288 331 289 - static int qcow_set_key(BlockDriverState *bs, const char *key) 290 - { 291 - BDRVQcowState *s = bs->opaque; 292 - uint8_t keybuf[16]; 293 - int len, i; 294 - Error *err; 295 - 296 - memset(keybuf, 0, 16); 297 - len = strlen(key); 298 - if (len > 16) 299 - len = 16; 300 - /* XXX: we could compress the chars to 7 bits to increase 301 - entropy */ 302 - for(i = 0;i < len;i++) { 303 - keybuf[i] = key[i]; 304 - } 305 - assert(bs->encrypted); 306 - 307 - qcrypto_cipher_free(s->cipher); 308 - s->cipher = qcrypto_cipher_new( 309 - QCRYPTO_CIPHER_ALG_AES_128, 310 - QCRYPTO_CIPHER_MODE_CBC, 311 - keybuf, G_N_ELEMENTS(keybuf), 312 - &err); 313 - 314 - if (!s->cipher) { 315 - /* XXX would be nice if errors in this method could 316 - * be properly propagate to the caller. Would need 317 - * the bdrv_set_key() API signature to be fixed. */ 318 - error_free(err); 319 - return -1; 320 - } 321 - return 0; 322 - } 323 - 324 - /* The crypt function is compatible with the linux cryptoloop 325 - algorithm for < 4 GB images. */ 326 - static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num, 327 - uint8_t *buf, int nb_sectors, bool enc, 328 - Error **errp) 329 - { 330 - union { 331 - uint64_t ll[2]; 332 - uint8_t b[16]; 333 - } ivec; 334 - int i; 335 - int ret; 336 - 337 - for(i = 0; i < nb_sectors; i++) { 338 - ivec.ll[0] = cpu_to_le64(sector_num); 339 - ivec.ll[1] = 0; 340 - if (qcrypto_cipher_setiv(s->cipher, 341 - ivec.b, G_N_ELEMENTS(ivec.b), 342 - errp) < 0) { 343 - return -1; 344 - } 345 - if (enc) { 346 - ret = qcrypto_cipher_encrypt(s->cipher, 347 - buf, buf, 348 - 512, 349 - errp); 350 - } else { 351 - ret = qcrypto_cipher_decrypt(s->cipher, 352 - buf, buf, 353 - 512, 354 - errp); 355 - } 356 - if (ret < 0) { 357 - return -1; 358 - } 359 - sector_num++; 360 - buf += 512; 361 - } 362 - return 0; 363 - } 364 332 365 333 /* 'allocate' is: 366 334 * ··· 475 443 if (bs->encrypted && 476 444 (n_end - n_start) < s->cluster_sectors) { 477 445 uint64_t start_sect; 478 - assert(s->cipher); 446 + assert(s->crypto); 479 447 start_sect = (offset & ~(s->cluster_size - 1)) >> 9; 480 448 for(i = 0; i < s->cluster_sectors; i++) { 481 449 if (i < n_start || i >= n_end) { 482 450 Error *err = NULL; 483 451 memset(s->cluster_data, 0x00, 512); 484 - if (encrypt_sectors(s, start_sect + i, 485 - s->cluster_data, 1, 486 - true, &err) < 0) { 452 + if (qcrypto_block_encrypt(s->crypto, start_sect + i, 453 + s->cluster_data, 454 + BDRV_SECTOR_SIZE, 455 + &err) < 0) { 487 456 error_free(err); 488 457 errno = EIO; 489 458 return -1; ··· 528 497 if (!cluster_offset) { 529 498 return 0; 530 499 } 531 - if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->cipher) { 500 + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypto) { 532 501 return BDRV_BLOCK_DATA; 533 502 } 534 503 cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS); ··· 659 628 break; 660 629 } 661 630 if (bs->encrypted) { 662 - assert(s->cipher); 663 - if (encrypt_sectors(s, sector_num, buf, 664 - n, false, &err) < 0) { 631 + assert(s->crypto); 632 + if (qcrypto_block_decrypt(s->crypto, sector_num, buf, 633 + n * BDRV_SECTOR_SIZE, &err) < 0) { 665 634 goto fail; 666 635 } 667 636 } ··· 734 703 } 735 704 if (bs->encrypted) { 736 705 Error *err = NULL; 737 - assert(s->cipher); 738 - if (encrypt_sectors(s, sector_num, buf, n, true, &err) < 0) { 706 + assert(s->crypto); 707 + if (qcrypto_block_encrypt(s->crypto, sector_num, buf, 708 + n * BDRV_SECTOR_SIZE, &err) < 0) { 739 709 error_free(err); 740 710 ret = -EIO; 741 711 break; ··· 770 740 { 771 741 BDRVQcowState *s = bs->opaque; 772 742 773 - qcrypto_cipher_free(s->cipher); 774 - s->cipher = NULL; 743 + qcrypto_block_free(s->crypto); 744 + s->crypto = NULL; 775 745 g_free(s->l1_table); 776 746 qemu_vfree(s->l2_cache); 777 747 g_free(s->cluster_cache); ··· 792 762 int ret; 793 763 BlockBackend *qcow_blk; 794 764 const char *encryptfmt = NULL; 765 + QDict *options; 766 + QDict *encryptopts = NULL; 767 + QCryptoBlockCreateOptions *crypto_opts = NULL; 768 + QCryptoBlock *crypto = NULL; 795 769 796 770 /* Read out options */ 797 771 total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), ··· 866 840 l1_size = (total_size + (1LL << shift) - 1) >> shift; 867 841 868 842 header.l1_table_offset = cpu_to_be64(header_size); 843 + 844 + options = qemu_opts_to_qdict(opts, NULL); 845 + qdict_extract_subqdict(options, &encryptopts, "encrypt."); 846 + QDECREF(options); 869 847 if (encryptfmt) { 870 848 if (!g_str_equal(encryptfmt, "aes")) { 871 849 error_setg(errp, "Unknown encryption format '%s', expected 'aes'", ··· 874 852 goto exit; 875 853 } 876 854 header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); 855 + 856 + crypto_opts = block_crypto_create_opts_init( 857 + Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); 858 + if (!crypto_opts) { 859 + ret = -EINVAL; 860 + goto exit; 861 + } 862 + 863 + crypto = qcrypto_block_create(crypto_opts, NULL, NULL, NULL, errp); 864 + if (!crypto) { 865 + ret = -EINVAL; 866 + goto exit; 867 + } 877 868 } else { 878 869 header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); 879 870 } ··· 908 899 exit: 909 900 blk_unref(qcow_blk); 910 901 cleanup: 902 + QDECREF(encryptopts); 903 + qcrypto_block_free(crypto); 904 + qapi_free_QCryptoBlockCreateOptions(crypto_opts); 911 905 g_free(backing_file); 912 906 return ret; 913 907 } ··· 1054 1048 .type = QEMU_OPT_STRING, 1055 1049 .help = "Encrypt the image, format choices: 'aes'", 1056 1050 }, 1051 + BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET("encrypt."), 1057 1052 { /* end of list */ } 1058 1053 } 1059 1054 }; ··· 1074 1069 .bdrv_co_writev = qcow_co_writev, 1075 1070 .bdrv_co_get_block_status = qcow_co_get_block_status, 1076 1071 1077 - .bdrv_set_key = qcow_set_key, 1078 1072 .bdrv_make_empty = qcow_make_empty, 1079 1073 .bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed, 1080 1074 .bdrv_get_info = qcow_get_info,
+37 -1
qapi/block-core.json
··· 2282 2282 'mode': 'Qcow2OverlapCheckMode' } } 2283 2283 2284 2284 ## 2285 + # @BlockdevQcowEncryptionFormat: 2286 + # 2287 + # @aes: AES-CBC with plain64 initialization vectors 2288 + # 2289 + # Since: 2.10 2290 + ## 2291 + { 'enum': 'BlockdevQcowEncryptionFormat', 2292 + 'data': [ 'aes' ] } 2293 + 2294 + ## 2295 + # @BlockdevQcowEncryption: 2296 + # 2297 + # Since: 2.10 2298 + ## 2299 + { 'union': 'BlockdevQcowEncryption', 2300 + 'base': { 'format': 'BlockdevQcowEncryptionFormat' }, 2301 + 'discriminator': 'format', 2302 + 'data': { 'aes': 'QCryptoBlockOptionsQCow' } } 2303 + 2304 + ## 2305 + # @BlockdevOptionsQcow: 2306 + # 2307 + # Driver specific block device options for qcow. 2308 + # 2309 + # @encrypt: Image decryption options. Mandatory for 2310 + # encrypted images, except when doing a metadata-only 2311 + # probe of the image. 2312 + # 2313 + # Since: 2.10 2314 + ## 2315 + { 'struct': 'BlockdevOptionsQcow', 2316 + 'base': 'BlockdevOptionsGenericCOWFormat', 2317 + 'data': { '*encrypt': 'BlockdevQcowEncryption' } } 2318 + 2319 + 2320 + ## 2285 2321 # @BlockdevOptionsQcow2: 2286 2322 # 2287 2323 # Driver specific block device options for qcow2. ··· 2976 3012 'null-co': 'BlockdevOptionsNull', 2977 3013 'parallels': 'BlockdevOptionsGenericFormat', 2978 3014 'qcow2': 'BlockdevOptionsQcow2', 2979 - 'qcow': 'BlockdevOptionsGenericCOWFormat', 3015 + 'qcow': 'BlockdevOptionsQcow', 2980 3016 'qed': 'BlockdevOptionsGenericCOWFormat', 2981 3017 'quorum': 'BlockdevOptionsQuorum', 2982 3018 'raw': 'BlockdevOptionsRaw',