[libvirt] [PATCH RFC 00/16] Add support for LUKS encrypted devices

Patches 1-3 were posted separately: http://www.redhat.com/archives/libvir-list/2016-June/msg00256.html But perhaps seeing the final direction will make things more clear as to why a "real" flag system wasn't used and keeping the current paradigm of constant value returns still works just fine. Patches 4-5 were posted separately: http://www.redhat.com/archives/libvir-list/2016-June/msg00091.html (4) http://www.redhat.com/archives/libvir-list/2016-June/msg00094.html (5) Although at one point patch 4 had an ACK: http://www.redhat.com/archives/libvir-list/2016-May/msg02115.html It wasn't clear if the more recent review rescinded that, so it still remains "in the list". I understand the concern about adding secret to cfg.mk checking, but without a better idea of how to handle - I left things as they were. Patches 6-16 are all new. Some parts are separable, but rather than continue piecemeal I just figured going with an RFC will at least Patch 6 is only there to "prove" that using the current encryption paradigm XML still works, although if I've read the tea leaves correctly, the qemu support isn't working as desired/expected. Patch 7 adds "usage" as an XML attribute for encryption and the associated tests with that. I've chosen to "reuse" the <encryption> XML element rather than inventing something new. I'm not opposed to something new, but let's decide up a name quickly... Patch 8-9 adds the ability for the storage backend to create/recognize a luks volume Patches 10-13 adds support for luks encryption in the storage backend. The new "<secret>" format uses "luks" as the usage type and "<key>" as the 'name'. If those names cause angst, I'm fine with changing, but just give a better suggestion! Adding <cipher> and <ivgen> were a result of using qemu constructs from qemu commit id '3e308f20'. Since we are parsing something new, I figure failing in the domain parse code for this new type was acceptible as opposed to some post processing check. Patches 14-16 adds support for luks encryption to the domain using <encryption type='luks'... <secret format='key' usage/uuid='xxx'>> I've tested using a "good" and "bad" password and got the expected results for starting a domain. I did not add 'virsh vol-create-as' support just yet. I figured that would be less to go back and redo if the names of elements changes. I've also run the changes through Coverity with no new issues detected. The whole series is a result of the following bz: https://bugzilla.redhat.com/show_bug.cgi?id=1301021 John Ferlan (16): storage: Adjust qemu-img switches check storage: Create helper to set backing for CreateQemuImg code storage: Create helper to set options for CreateQemuImg code storage: Use virSecretGetSecretString secret: Move virStorageSecretType to secret_util and rename tests: Adjust tests for encrypted storage util: Add 'usage' for encryption util: Modify the FileTypeInfo for meta data checks util: Add 'luks' to the FileTypeInfo conf: Add new secret type "luks" encryption: Add luks parsing for storageencryption encryption: Add <cipher> and <ivgen> to encryption storage: Add support to create a luks volume qemu: Change protocol parameter for secret setup qemu: Remove authdef from secret setup qemu: Add luks support for domain disk cfg.mk | 2 +- docs/aclpolkit.html.in | 4 + docs/formatsecret.html.in | 60 ++- docs/formatstorageencryption.html.in | 115 ++++- docs/schemas/secret.rng | 10 + docs/schemas/storagecommon.rng | 58 ++- include/libvirt/libvirt-secret.h | 3 +- src/Makefile.am | 1 + src/access/viraccessdriverpolkit.c | 13 + src/conf/domain_conf.c | 11 + src/conf/secret_conf.c | 26 +- src/conf/secret_conf.h | 3 +- src/conf/virsecretobj.c | 5 + src/libvirt_private.syms | 1 + src/libxl/libxl_conf.c | 2 +- src/qemu/qemu_command.c | 8 +- src/qemu/qemu_domain.c | 154 ++++--- src/qemu/qemu_process.c | 18 +- src/secret/secret_util.c | 18 +- src/secret/secret_util.h | 22 +- src/storage/storage_backend.c | 480 +++++++++++++++------ src/storage/storage_backend.h | 3 +- src/storage/storage_backend_fs.c | 10 +- src/storage/storage_backend_gluster.c | 2 + src/storage/storage_backend_iscsi.c | 55 +-- src/storage/storage_backend_rbd.c | 49 +-- src/util/virendian.h | 24 ++ src/util/virqemu.c | 23 + src/util/virqemu.h | 6 + src/util/virstorageencryption.c | 166 ++++++- src/util/virstorageencryption.h | 18 +- src/util/virstoragefile.c | 125 ++++-- src/util/virstoragefile.h | 18 +- tests/qemuargv2xmltest.c | 4 +- .../qemuxml2argv-encrypted-disk-usage.args | 24 ++ .../qemuxml2argv-encrypted-disk-usage.xml | 32 ++ .../qemuxml2argv-encrypted-disk.args | 26 +- .../qemuxml2argv-encrypted-disk.xml | 4 +- .../qemuxml2argv-luks-disk-cipher.args | 36 ++ .../qemuxml2argv-luks-disk-cipher.xml | 41 ++ .../qemuxml2argvdata/qemuxml2argv-luks-disks.args | 36 ++ tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml | 41 ++ tests/qemuxml2argvtest.c | 14 +- .../qemuxml2xmlout-encrypted-disk-usage.xml | 36 ++ .../qemuxml2xmlout-encrypted-disk.xml | 4 +- .../qemuxml2xmlout-luks-disk-cipher.xml | 45 ++ .../qemuxml2xmlout-luks-disks.xml | 45 ++ tests/qemuxml2xmltest.c | 3 + tests/secretxml2xmlin/usage-luks.xml | 7 + tests/secretxml2xmltest.c | 1 + tests/storagevolxml2argvdata/qcow2-flag.argv | 2 - .../qcow2-nobacking-convert-flag.argv | 2 - .../qcow2-nobacking-convert-none.argv | 2 - .../qcow2-nobacking-flag.argv | 1 - .../qcow2-nobacking-none.argv | 1 - tests/storagevolxml2argvdata/qcow2-none.argv | 1 - tests/storagevolxml2argvtest.c | 25 +- tests/storagevolxml2xmlin/vol-luks-cipher.xml | 23 + tests/storagevolxml2xmlin/vol-luks.xml | 21 + tests/storagevolxml2xmlout/vol-luks-cipher.xml | 23 + tests/storagevolxml2xmlout/vol-luks.xml | 21 + tests/storagevolxml2xmltest.c | 2 + tests/virendiantest.c | 18 + 63 files changed, 1619 insertions(+), 435 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk-usage.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disks.xml create mode 100644 tests/secretxml2xmlin/usage-luks.xml delete mode 100644 tests/storagevolxml2argvdata/qcow2-flag.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-convert-flag.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-convert-none.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-flag.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-none.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-none.argv create mode 100644 tests/storagevolxml2xmlin/vol-luks-cipher.xml create mode 100644 tests/storagevolxml2xmlin/vol-luks.xml create mode 100644 tests/storagevolxml2xmlout/vol-luks-cipher.xml create mode 100644 tests/storagevolxml2xmlout/vol-luks.xml -- 2.5.5

Since we support QEMU 0.12 and later, checking for support of specific flags added prior to that isn't necessary. Thus start with the base of having the "-o options" available for the qemu-img create option and then determine whether we have the compat option for qcow2 files (which would be necessary up through qemu 2.0 where the default changes to compat 0.11). Adjust test to no long check for NONE and FLAG options as well was removing results of tests that would use that option. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend.c | 91 +++++++--------------- tests/storagevolxml2argvdata/qcow2-flag.argv | 2 - .../qcow2-nobacking-convert-flag.argv | 2 - .../qcow2-nobacking-convert-none.argv | 2 - .../qcow2-nobacking-flag.argv | 1 - .../qcow2-nobacking-none.argv | 1 - tests/storagevolxml2argvdata/qcow2-none.argv | 1 - tests/storagevolxml2argvtest.c | 22 +----- 8 files changed, 29 insertions(+), 93 deletions(-) delete mode 100644 tests/storagevolxml2argvdata/qcow2-flag.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-convert-flag.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-convert-none.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-flag.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-nobacking-none.argv delete mode 100644 tests/storagevolxml2argvdata/qcow2-none.argv diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 8f03a6e..7ef9ecd 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -869,10 +869,15 @@ virStoragePloopResize(virStorageVolDefPtr vol, return ret; } +/* Flag values shared w/ storagevolxml2argvtest.c. + * + * QEMU_IMG_BACKING_FORMAT_OPTIONS (added in qemu 0.11) + * QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT + * was made necessary due to 2.0 change to change the default + * qcow2 file format from 0.10 to 1.1. + */ enum { - QEMU_IMG_BACKING_FORMAT_NONE = 0, - QEMU_IMG_BACKING_FORMAT_FLAG, - QEMU_IMG_BACKING_FORMAT_OPTIONS, + QEMU_IMG_BACKING_FORMAT_OPTIONS = 0, QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT, }; @@ -904,46 +909,18 @@ virStorageBackendQemuImgSupportsCompat(const char *qemuimg) static int virStorageBackendQEMUImgBackingFormat(const char *qemuimg) { - char *help = NULL; - char *start; - char *end; - char *tmp; - int ret = -1; - int exitstatus; - virCommandPtr cmd = virCommandNewArgList(qemuimg, "-h", NULL); - - virCommandAddEnvString(cmd, "LC_ALL=C"); - virCommandSetOutputBuffer(cmd, &help); - virCommandClearCaps(cmd); - - /* qemuimg doesn't return zero exit status on -h, - * therefore we need to provide pointer for storing - * exit status, although we don't parse it any later */ - if (virCommandRun(cmd, &exitstatus) < 0) - goto cleanup; - - if ((start = strstr(help, " create ")) == NULL || - (end = strstr(start, "\n")) == NULL) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to parse qemu-img output '%s'"), - help); - goto cleanup; - } - if (((tmp = strstr(start, "-F fmt")) && tmp < end) || - ((tmp = strstr(start, "-F backing_fmt")) && tmp < end)) { - ret = QEMU_IMG_BACKING_FORMAT_FLAG; - } else if ((tmp = strstr(start, "[-o options]")) && tmp < end) { - if (virStorageBackendQemuImgSupportsCompat(qemuimg)) - ret = QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT; - else - ret = QEMU_IMG_BACKING_FORMAT_OPTIONS; - } else { - ret = QEMU_IMG_BACKING_FORMAT_NONE; - } + /* As of QEMU 0.11 the [-o options] support was added via qemu + * commit id '9ea2ea71', so we start with that base and figure + * out what else we have */ + int ret = QEMU_IMG_BACKING_FORMAT_OPTIONS; + + /* QEMU 2.0 changed to using a format that only QEMU 1.1 and newer + * understands. Since we still support QEMU 0.12 and newer, we need + * to be able to handle the previous format as can be set via a + * compat=0.10 option. */ + if (virStorageBackendQemuImgSupportsCompat(qemuimg)) + ret = QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT; - cleanup: - virCommandFree(cmd); - VIR_FREE(help); return ret; } @@ -1218,29 +1195,17 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, if (info.backingPath) virCommandAddArgList(cmd, "-b", info.backingPath, NULL); - if (imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS) { - if (info.format == VIR_STORAGE_FILE_QCOW2 && !info.compat && - imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) - info.compat = "0.10"; + if (info.format == VIR_STORAGE_FILE_QCOW2 && !info.compat && + imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) + info.compat = "0.10"; - if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0) { - virCommandFree(cmd); - return NULL; - } - if (opts) - virCommandAddArgList(cmd, "-o", opts, NULL); - VIR_FREE(opts); - } else { - if (info.backingPath) { - if (imgformat == QEMU_IMG_BACKING_FORMAT_FLAG) - virCommandAddArgList(cmd, "-F", backingType, NULL); - else - VIR_DEBUG("Unable to set backing store format for %s with %s", - info.path, create_tool); - } - if (info.encryption) - virCommandAddArg(cmd, "-e"); + if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0) { + virCommandFree(cmd); + return NULL; } + if (opts) + virCommandAddArgList(cmd, "-o", opts, NULL); + VIR_FREE(opts); if (info.inputPath) virCommandAddArg(cmd, info.inputPath); diff --git a/tests/storagevolxml2argvdata/qcow2-flag.argv b/tests/storagevolxml2argvdata/qcow2-flag.argv deleted file mode 100644 index 5f87ad5..0000000 --- a/tests/storagevolxml2argvdata/qcow2-flag.argv +++ /dev/null @@ -1,2 +0,0 @@ -qemu-img create -f qcow2 -b /dev/null -F raw \ --e /var/lib/libvirt/images/OtherDemo.img 5242880K diff --git a/tests/storagevolxml2argvdata/qcow2-nobacking-convert-flag.argv b/tests/storagevolxml2argvdata/qcow2-nobacking-convert-flag.argv deleted file mode 100644 index 57bdd4a..0000000 --- a/tests/storagevolxml2argvdata/qcow2-nobacking-convert-flag.argv +++ /dev/null @@ -1,2 +0,0 @@ -qemu-img convert -f raw -O qcow2 -e /var/lib/libvirt/images/sparse.img \ -/var/lib/libvirt/images/OtherDemo.img diff --git a/tests/storagevolxml2argvdata/qcow2-nobacking-convert-none.argv b/tests/storagevolxml2argvdata/qcow2-nobacking-convert-none.argv deleted file mode 100644 index 57bdd4a..0000000 --- a/tests/storagevolxml2argvdata/qcow2-nobacking-convert-none.argv +++ /dev/null @@ -1,2 +0,0 @@ -qemu-img convert -f raw -O qcow2 -e /var/lib/libvirt/images/sparse.img \ -/var/lib/libvirt/images/OtherDemo.img diff --git a/tests/storagevolxml2argvdata/qcow2-nobacking-flag.argv b/tests/storagevolxml2argvdata/qcow2-nobacking-flag.argv deleted file mode 100644 index 62ad85f..0000000 --- a/tests/storagevolxml2argvdata/qcow2-nobacking-flag.argv +++ /dev/null @@ -1 +0,0 @@ -qemu-img create -f qcow2 -e /var/lib/libvirt/images/OtherDemo.img 5242880K diff --git a/tests/storagevolxml2argvdata/qcow2-nobacking-none.argv b/tests/storagevolxml2argvdata/qcow2-nobacking-none.argv deleted file mode 100644 index 62ad85f..0000000 --- a/tests/storagevolxml2argvdata/qcow2-nobacking-none.argv +++ /dev/null @@ -1 +0,0 @@ -qemu-img create -f qcow2 -e /var/lib/libvirt/images/OtherDemo.img 5242880K diff --git a/tests/storagevolxml2argvdata/qcow2-none.argv b/tests/storagevolxml2argvdata/qcow2-none.argv deleted file mode 100644 index 10b7175..0000000 --- a/tests/storagevolxml2argvdata/qcow2-none.argv +++ /dev/null @@ -1 +0,0 @@ -qemu-img create -f qcow2 -b /dev/null -e /var/lib/libvirt/images/OtherDemo.img 5242880K diff --git a/tests/storagevolxml2argvtest.c b/tests/storagevolxml2argvtest.c index 393123b..af7de0b 100644 --- a/tests/storagevolxml2argvtest.c +++ b/tests/storagevolxml2argvtest.c @@ -168,9 +168,7 @@ testCompareXMLToArgvHelper(const void *data) } enum { - FMT_NONE = 0, - FMT_FLAG, - FMT_OPTIONS, + FMT_OPTIONS = 0, FMT_COMPAT, }; @@ -217,24 +215,6 @@ mymain(void) DO_TEST_FAIL("pool-dir", "vol-qcow2", "pool-dir", "vol-file", "qcow2-convert-prealloc", flags, FMT_OPTIONS); - DO_TEST("pool-dir", "vol-qcow2", - NULL, NULL, - "qcow2-flag", 0, FMT_FLAG); - DO_TEST("pool-dir", "vol-qcow2-nobacking", - NULL, NULL, - "qcow2-nobacking-flag", 0, FMT_FLAG); - DO_TEST("pool-dir", "vol-qcow2-nobacking", - "pool-dir", "vol-file", - "qcow2-nobacking-convert-flag", 0, FMT_FLAG); - DO_TEST("pool-dir", "vol-qcow2", - NULL, NULL, - "qcow2-none", 0, FMT_NONE); - DO_TEST("pool-dir", "vol-qcow2-nobacking", - NULL, NULL, - "qcow2-nobacking-none", 0, FMT_NONE); - DO_TEST("pool-dir", "vol-qcow2-nobacking", - "pool-dir", "vol-file", - "qcow2-nobacking-convert-none", 0, FMT_NONE); DO_TEST("pool-dir", "vol-qcow2-lazy", NULL, NULL, "qcow2-lazy", 0, FMT_OPTIONS); -- 2.5.5

Create a helper virStorageBackendCreateQemuImgSetBacking to perform the backing store set Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend.c | 116 +++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 7ef9ecd..cbc32bb 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -1060,6 +1060,65 @@ virStorageBackendCreateQemuImgSetInput(virStorageVolDefPtr inputvol, } +static int +virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + virStorageVolDefPtr inputvol, + struct _virStorageBackendQemuImgInfo *info) +{ + int accessRetCode = -1; + char *absolutePath = NULL; + + info->backingFormat = vol->target.backingStore->format; + info->backingPath = vol->target.backingStore->path; + + if (info->preallocate) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("metadata preallocation conflicts with backing" + " store")); + return -1; + } + + /* XXX: Not strictly required: qemu-img has an option a different + * backing store, not really sure what use it serves though, and it + * may cause issues with lvm. Untested essentially. + */ + if (inputvol && inputvol->target.backingStore && + STRNEQ_NULLABLE(inputvol->target.backingStore->path, + info->backingPath)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("a different backing store cannot be specified.")); + return -1; + } + + if (!virStorageFileFormatTypeToString(info->backingFormat)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown storage vol backing store type %d"), + info->backingFormat); + return -1; + } + + /* Convert relative backing store paths to absolute paths for access + * validation. + */ + if ('/' != *(info->backingPath) && + virAsprintf(&absolutePath, "%s/%s", pool->def->target.path, + info->backingPath) < 0) + return -1; + accessRetCode = access(absolutePath ? absolutePath : + info->backingPath, R_OK); + VIR_FREE(absolutePath); + if (accessRetCode != 0) { + virReportSystemError(errno, + _("inaccessible backing store volume %s"), + info->backingPath); + return -1; + } + + return 0; +} + + /* Create a qemu-img virCommand from the supplied binary path, * volume definitions and imgformat @@ -1075,7 +1134,6 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, { virCommandPtr cmd = NULL; const char *type; - const char *backingType = NULL; char *opts = NULL; struct _virStorageBackendQemuImgInfo info = { .format = vol->target.format, @@ -1120,54 +1178,10 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0) return NULL; - if (vol->target.backingStore) { - int accessRetCode = -1; - char *absolutePath = NULL; - - info.backingFormat = vol->target.backingStore->format; - info.backingPath = vol->target.backingStore->path; - - if (info.preallocate) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("metadata preallocation conflicts with backing" - " store")); - return NULL; - } - - /* XXX: Not strictly required: qemu-img has an option a different - * backing store, not really sure what use it serves though, and it - * may cause issues with lvm. Untested essentially. - */ - if (inputvol && inputvol->target.backingStore && - STRNEQ_NULLABLE(inputvol->target.backingStore->path, info.backingPath)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("a different backing store cannot be specified.")); - return NULL; - } - - if (!(backingType = virStorageFileFormatTypeToString(info.backingFormat))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown storage vol backing store type %d"), - info.backingFormat); - return NULL; - } - - /* Convert relative backing store paths to absolute paths for access - * validation. - */ - if ('/' != *(info.backingPath) && - virAsprintf(&absolutePath, "%s/%s", pool->def->target.path, - info.backingPath) < 0) - return NULL; - accessRetCode = access(absolutePath ? absolutePath : info.backingPath, R_OK); - VIR_FREE(absolutePath); - if (accessRetCode != 0) { - virReportSystemError(errno, - _("inaccessible backing store volume %s"), - info.backingPath); - return NULL; - } - } + if (vol->target.backingStore && + virStorageBackendCreateQemuImgSetBacking(pool, vol, inputvol, + &info) < 0) + return NULL; if (info.encryption && virStorageBackendCreateQemuImgCheckEncryption(info.format, type, @@ -1181,10 +1195,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, cmd = virCommandNew(create_tool); /* ignore the backing volume when we're converting a volume */ - if (info.inputPath) { + if (info.inputPath) info.backingPath = NULL; - backingType = NULL; - } if (info.inputPath) virCommandAddArgList(cmd, "convert", "-f", info.inputFormatStr, -- 2.5.5

Create a helper virStorageBackendCreateQemuImgSetOptions to set either the qemu-img -o options or the previous mechanism using -F Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/storage/storage_backend.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index cbc32bb..d041530 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -1119,6 +1119,26 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool, } +static int +virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, + int imgformat, + struct _virStorageBackendQemuImgInfo info) +{ + char *opts = NULL; + + if (info.format == VIR_STORAGE_FILE_QCOW2 && !info.compat && + imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) + info.compat = "0.10"; + + if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0) + return -1; + if (opts) + virCommandAddArgList(cmd, "-o", opts, NULL); + VIR_FREE(opts); + + return 0; +} + /* Create a qemu-img virCommand from the supplied binary path, * volume definitions and imgformat @@ -1134,7 +1154,6 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, { virCommandPtr cmd = NULL; const char *type; - char *opts = NULL; struct _virStorageBackendQemuImgInfo info = { .format = vol->target.format, .path = vol->target.path, @@ -1207,17 +1226,10 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, if (info.backingPath) virCommandAddArgList(cmd, "-b", info.backingPath, NULL); - if (info.format == VIR_STORAGE_FILE_QCOW2 && !info.compat && - imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) - info.compat = "0.10"; - - if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0) { + if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, info) < 0) { virCommandFree(cmd); return NULL; } - if (opts) - virCommandAddArgList(cmd, "-o", opts, NULL); - VIR_FREE(opts); if (info.inputPath) virCommandAddArg(cmd, info.inputPath); -- 2.5.5

Rather than inline code secret lookup for rbd/iscsi, use the common function. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/Makefile.am | 1 + src/storage/storage_backend_iscsi.c | 50 +++++-------------------------------- src/storage/storage_backend_rbd.c | 48 +++-------------------------------- 3 files changed, 10 insertions(+), 89 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index f020b92..87a59e7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1615,6 +1615,7 @@ libvirt_driver_storage_impl_la_SOURCES = libvirt_driver_storage_impl_la_CFLAGS = \ -I$(srcdir)/access \ -I$(srcdir)/conf \ + -I$(srcdir)/secret \ $(AM_CFLAGS) libvirt_driver_storage_impl_la_LDFLAGS = $(AM_LDFLAGS) libvirt_driver_storage_impl_la_LIBADD = diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index bccfba3..a45c525 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -1,7 +1,7 @@ /* * storage_backend_iscsi.c: storage backend for iSCSI handling * - * Copyright (C) 2007-2014 Red Hat, Inc. + * Copyright (C) 2007-2016 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -43,7 +43,7 @@ #include "virobject.h" #include "virstring.h" #include "viruuid.h" - +#include "secret_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE VIR_LOG_INIT("storage.storage_backend_iscsi"); @@ -277,11 +277,10 @@ virStorageBackendISCSISetAuth(const char *portal, virConnectPtr conn, virStoragePoolSourcePtr source) { - virSecretPtr secret = NULL; unsigned char *secret_value = NULL; + size_t secret_size; virStorageAuthDefPtr authdef = source->auth; int ret = -1; - char uuidStr[VIR_UUID_STRING_BUFLEN]; if (!authdef || authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE) return 0; @@ -301,45 +300,9 @@ virStorageBackendISCSISetAuth(const char *portal, return -1; } - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) - secret = virSecretLookupByUUID(conn, authdef->secret.uuid); - else - secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_ISCSI, - authdef->secret.usage); - - if (secret) { - size_t secret_size; - secret_value = - conn->secretDriver->secretGetValue(secret, &secret_size, 0, - VIR_SECRET_GET_VALUE_INTERNAL_CALL); - if (!secret_value) { - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virUUIDFormat(authdef->secret.uuid, uuidStr); - virReportError(VIR_ERR_INTERNAL_ERROR, - _("could not get the value of the secret " - "for username %s using uuid '%s'"), - authdef->username, uuidStr); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("could not get the value of the secret " - "for username %s using usage value '%s'"), - authdef->username, authdef->secret.usage); - } - goto cleanup; - } - } else { - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virUUIDFormat(authdef->secret.uuid, uuidStr); - virReportError(VIR_ERR_NO_SECRET, - _("no secret matches uuid '%s'"), - uuidStr); - } else { - virReportError(VIR_ERR_NO_SECRET, - _("no secret matches usage value '%s'"), - authdef->secret.usage); - } + if (virSecretGetSecretString(conn, authdef, VIR_SECRET_USAGE_TYPE_ISCSI, + &secret_value, &secret_size) < 0) goto cleanup; - } if (virISCSINodeUpdate(portal, source->devices[0].path, @@ -358,8 +321,7 @@ virStorageBackendISCSISetAuth(const char *portal, ret = 0; cleanup: - virObjectUnref(secret); - VIR_FREE(secret_value); + VIR_DISPOSE_N(secret_value, secret_size); return ret; } diff --git a/src/storage/storage_backend_rbd.c b/src/storage/storage_backend_rbd.c index ac46b9d..64ec545 100644 --- a/src/storage/storage_backend_rbd.c +++ b/src/storage/storage_backend_rbd.c @@ -36,6 +36,7 @@ #include "virrandom.h" #include "rados/librados.h" #include "rbd/librbd.h" +#include "secret_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -62,8 +63,6 @@ virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr ptr, size_t secret_value_size = 0; char *rados_key = NULL; virBuffer mon_host = VIR_BUFFER_INITIALIZER; - virSecretPtr secret = NULL; - char secretUuid[VIR_UUID_STRING_BUFLEN]; size_t i; char *mon_buff = NULL; const char *client_mount_timeout = "30"; @@ -86,48 +85,9 @@ virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr ptr, return -1; } - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virUUIDFormat(authdef->secret.uuid, secretUuid); - VIR_DEBUG("Looking up secret by UUID: %s", secretUuid); - secret = virSecretLookupByUUIDString(conn, secretUuid); - } else if (authdef->secret.usage != NULL) { - VIR_DEBUG("Looking up secret by usage: %s", - authdef->secret.usage); - secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_CEPH, - authdef->secret.usage); - } - - if (secret == NULL) { - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virReportError(VIR_ERR_NO_SECRET, - _("no secret matches uuid '%s'"), - secretUuid); - } else { - virReportError(VIR_ERR_NO_SECRET, - _("no secret matches usage value '%s'"), - authdef->secret.usage); - } + if (virSecretGetSecretString(conn, authdef, VIR_SECRET_USAGE_TYPE_CEPH, + &secret_value, &secret_value_size) < 0) goto cleanup; - } - - secret_value = conn->secretDriver->secretGetValue(secret, - &secret_value_size, 0, - VIR_SECRET_GET_VALUE_INTERNAL_CALL); - - if (!secret_value) { - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("could not get the value of the secret " - "for username '%s' using uuid '%s'"), - authdef->username, secretUuid); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("could not get the value of the secret " - "for username '%s' using usage value '%s'"), - authdef->username, authdef->secret.usage); - } - goto cleanup; - } if (!(rados_key = virStringEncodeBase64(secret_value, secret_value_size))) goto cleanup; @@ -227,8 +187,6 @@ virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr ptr, VIR_DISPOSE_N(secret_value, secret_value_size); VIR_DISPOSE_STRING(rados_key); - virObjectUnref(secret); - virBufferFreeAndReset(&mon_host); VIR_FREE(mon_buff); return ret; -- 2.5.5

Move the enum into secret_util, rename it to be just virSecretLookupType. This includes quite a bit of collateral damage, but the goal is to remove the "virStorage*" and replace with the virSecretLookupType so that it's easier to to add new lookups that aren't necessarily storage pool related. Signed-off-by: John Ferlan <jferlan@redhat.com> --- cfg.mk | 2 +- src/conf/secret_conf.h | 2 +- src/libxl/libxl_conf.c | 2 +- src/qemu/qemu_domain.c | 4 ++-- src/secret/secret_util.c | 18 +++++++++--------- src/secret/secret_util.h | 22 ++++++++++++++++++++-- src/storage/storage_backend_iscsi.c | 7 ++++--- src/storage/storage_backend_rbd.c | 3 ++- src/util/virstoragefile.c | 33 +++++++++++++++++---------------- src/util/virstoragefile.h | 17 +++-------------- tests/qemuargv2xmltest.c | 4 ++-- 11 files changed, 62 insertions(+), 52 deletions(-) diff --git a/cfg.mk b/cfg.mk index a7b7266..0529a4e 100644 --- a/cfg.mk +++ b/cfg.mk @@ -780,7 +780,7 @@ mid_dirs=access|conf|cpu|locking|logging|network|node_device|rpc|security|storag sc_prohibit_cross_inclusion: @for dir in $(cross_dirs); do \ case $$dir in \ - util/) safe="util";; \ + util/) safe="(util|secret)";; \ access/ | conf/) safe="($$dir|conf|util)";; \ locking/) safe="($$dir|util|conf|rpc)";; \ cpu/| network/| node_device/| rpc/| security/| storage/) \ diff --git a/src/conf/secret_conf.h b/src/conf/secret_conf.h index ca1afec..4584403 100644 --- a/src/conf/secret_conf.h +++ b/src/conf/secret_conf.h @@ -35,7 +35,7 @@ struct _virSecretDef { bool isprivate; unsigned char uuid[VIR_UUID_BUFLEN]; char *description; /* May be NULL */ - int usage_type; + int usage_type; /* virSecretUsageType */ union { char *volume; /* May be NULL */ char *ceph; diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index a64b4c1..b3f78f0 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -1033,7 +1033,7 @@ libxlMakeNetworkDiskSrc(virStorageSourcePtr src, char **srcstr) if (!(conn = virConnectOpen("xen:///system"))) goto cleanup; - if (virSecretGetSecretString(conn, src->auth, + if (virSecretGetSecretString(conn, &src->auth->secdef, VIR_SECRET_USAGE_TYPE_CEPH, &secret, &secretlen) < 0) goto cleanup; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7e64545..cf687e2 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -829,7 +829,7 @@ qemuDomainSecretPlainSetup(virConnectPtr conn, if (protocol == VIR_STORAGE_NET_PROTOCOL_RBD) secretType = VIR_SECRET_USAGE_TYPE_CEPH; - return virSecretGetSecretString(conn, authdef, secretType, + return virSecretGetSecretString(conn, &authdef->secdef, secretType, &secinfo->s.plain.secret, &secinfo->s.plain.secretlen); } @@ -902,7 +902,7 @@ qemuDomainSecretAESSetup(virConnectPtr conn, goto cleanup; /* Grab the unencoded secret */ - if (virSecretGetSecretString(conn, authdef, secretType, + if (virSecretGetSecretString(conn, &authdef->secdef, secretType, &secret, &secretlen) < 0) goto cleanup; diff --git a/src/secret/secret_util.c b/src/secret/secret_util.c index 5602401..7bfe635 100644 --- a/src/secret/secret_util.c +++ b/src/secret/secret_util.c @@ -36,12 +36,12 @@ VIR_LOG_INIT("secret.secret_util"); /* virSecretGetSecretString: * @conn: Pointer to the connection driver to make secret driver call - * @authdef: Pointer to the disk storage authentication - * @secretUsageType: Type of secret usage for authdef lookup + * @secdef: Pointer to a storage type def for uuid/usage lookup + * @secretUsageType: Type of secret usage for usage lookup * @secret: returned secret as a sized stream of unsigned chars * @secret_size: Return size of the secret - either raw text or base64 * - * Lookup the secret for the authdef usage type and return it as raw text. + * Lookup the secret for the usage type and return it as raw text. * It is up to the caller to encode the secret further. * * Returns 0 on success, -1 on failure. On success the memory in secret @@ -49,7 +49,7 @@ VIR_LOG_INIT("secret.secret_util"); */ int virSecretGetSecretString(virConnectPtr conn, - virStorageAuthDefPtr authdef, + virSecretLookupTypeDefPtr secdef, virSecretUsageType secretUsageType, uint8_t **secret, size_t *secret_size) @@ -57,14 +57,14 @@ virSecretGetSecretString(virConnectPtr conn, virSecretPtr sec = NULL; int ret = -1; - switch (authdef->secretType) { - case VIR_STORAGE_SECRET_TYPE_UUID: - sec = conn->secretDriver->secretLookupByUUID(conn, authdef->secret.uuid); + switch (secdef->type) { + case VIR_SECRET_LOOKUP_TYPE_UUID: + sec = conn->secretDriver->secretLookupByUUID(conn, secdef->u.uuid); break; - case VIR_STORAGE_SECRET_TYPE_USAGE: + case VIR_SECRET_LOOKUP_TYPE_USAGE: sec = conn->secretDriver->secretLookupByUsage(conn, secretUsageType, - authdef->secret.usage); + secdef->u.usage); break; } diff --git a/src/secret/secret_util.h b/src/secret/secret_util.h index a039662..18ac86a 100644 --- a/src/secret/secret_util.h +++ b/src/secret/secret_util.h @@ -23,10 +23,28 @@ # define __VIR_SECRET_H__ # include "internal.h" -# include "virstoragefile.h" + +typedef enum { + VIR_SECRET_LOOKUP_TYPE_NONE, + VIR_SECRET_LOOKUP_TYPE_UUID, + VIR_SECRET_LOOKUP_TYPE_USAGE, + + VIR_SECRET_LOOKUP_TYPE_LAST +} virSecretLookupType; + +typedef struct _virSecretLookupTypeDef virSecretLookupTypeDef; +typedef virSecretLookupTypeDef *virSecretLookupTypeDefPtr; +struct _virSecretLookupTypeDef { + int type; /* virSecretLookupType */ + union { + unsigned char uuid[VIR_UUID_BUFLEN]; + char *usage; + } u; + +}; int virSecretGetSecretString(virConnectPtr conn, - virStorageAuthDefPtr authdef, + virSecretLookupTypeDefPtr secdef, virSecretUsageType secretUsageType, uint8_t **ret_secret, size_t *ret_secret_size) diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index a45c525..81872ea 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -285,8 +285,8 @@ virStorageBackendISCSISetAuth(const char *portal, if (!authdef || authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE) return 0; - VIR_DEBUG("username='%s' authType=%d secretType=%d", - authdef->username, authdef->authType, authdef->secretType); + VIR_DEBUG("username='%s' authType=%d secdef.type=%d", + authdef->username, authdef->authType, authdef->secdef.type); if (authdef->authType != VIR_STORAGE_AUTH_TYPE_CHAP) { virReportError(VIR_ERR_XML_ERROR, "%s", _("iscsi pool only supports 'chap' auth type")); @@ -300,7 +300,8 @@ virStorageBackendISCSISetAuth(const char *portal, return -1; } - if (virSecretGetSecretString(conn, authdef, VIR_SECRET_USAGE_TYPE_ISCSI, + if (virSecretGetSecretString(conn, &authdef->secdef, + VIR_SECRET_USAGE_TYPE_ISCSI, &secret_value, &secret_size) < 0) goto cleanup; diff --git a/src/storage/storage_backend_rbd.c b/src/storage/storage_backend_rbd.c index 64ec545..bf77c54 100644 --- a/src/storage/storage_backend_rbd.c +++ b/src/storage/storage_backend_rbd.c @@ -85,7 +85,8 @@ virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr ptr, return -1; } - if (virSecretGetSecretString(conn, authdef, VIR_SECRET_USAGE_TYPE_CEPH, + if (virSecretGetSecretString(conn, &authdef->secdef, + VIR_SECRET_USAGE_TYPE_CEPH, &secret_value, &secret_value_size) < 0) goto cleanup; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index d2da9e7..54940a0 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1506,8 +1506,8 @@ virStorageAuthDefFree(virStorageAuthDefPtr authdef) VIR_FREE(authdef->username); VIR_FREE(authdef->secrettype); - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_USAGE) - VIR_FREE(authdef->secret.usage); + if (authdef->secdef.type == VIR_SECRET_LOOKUP_TYPE_USAGE) + VIR_FREE(authdef->secdef.u.usage); VIR_FREE(authdef); } @@ -1526,11 +1526,12 @@ virStorageAuthDefCopy(const virStorageAuthDef *src) if (VIR_STRDUP(ret->secrettype, src->secrettype) < 0) goto error; ret->authType = src->authType; - ret->secretType = src->secretType; - if (ret->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - memcpy(ret->secret.uuid, src->secret.uuid, sizeof(ret->secret.uuid)); - } else if (ret->secretType == VIR_STORAGE_SECRET_TYPE_USAGE) { - if (VIR_STRDUP(ret->secret.usage, src->secret.usage) < 0) + ret->secdef.type = src->secdef.type; + if (ret->secdef.type == VIR_SECRET_LOOKUP_TYPE_UUID) { + memcpy(ret->secdef.u.uuid, src->secdef.u.uuid, + sizeof(ret->secdef.u.uuid)); + } else if (ret->secdef.type == VIR_SECRET_LOOKUP_TYPE_USAGE) { + if (VIR_STRDUP(ret->secdef.u.usage, src->secdef.u.usage) < 0) goto error; } return ret; @@ -1573,16 +1574,16 @@ virStorageAuthDefParseSecret(xmlXPathContextPtr ctxt, } if (uuid) { - if (virUUIDParse(uuid, authdef->secret.uuid) < 0) { + if (virUUIDParse(uuid, authdef->secdef.u.uuid) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid auth secret uuid")); goto cleanup; } - authdef->secretType = VIR_STORAGE_SECRET_TYPE_UUID; + authdef->secdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; } else { - authdef->secret.usage = usage; + authdef->secdef.u.usage = usage; usage = NULL; - authdef->secretType = VIR_STORAGE_SECRET_TYPE_USAGE; + authdef->secdef.type = VIR_SECRET_LOOKUP_TYPE_USAGE; } ret = 0; @@ -1625,7 +1626,7 @@ virStorageAuthDefParseXML(xmlXPathContextPtr ctxt) VIR_FREE(authtype); } - authdef->secretType = VIR_STORAGE_SECRET_TYPE_NONE; + authdef->secdef.type = VIR_SECRET_LOOKUP_TYPE_NONE; if (virStorageAuthDefParseSecret(ctxt, authdef) < 0) goto error; @@ -1680,12 +1681,12 @@ virStorageAuthDefFormat(virBufferPtr buf, else virBufferAddLit(buf, "<secret"); - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) { - virUUIDFormat(authdef->secret.uuid, uuidstr); + if (authdef->secdef.type == VIR_SECRET_LOOKUP_TYPE_UUID) { + virUUIDFormat(authdef->secdef.u.uuid, uuidstr); virBufferAsprintf(buf, " uuid='%s'/>\n", uuidstr); - } else if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_USAGE) { + } else if (authdef->secdef.type == VIR_SECRET_LOOKUP_TYPE_USAGE) { virBufferEscapeString(buf, " usage='%s'/>\n", - authdef->secret.usage); + authdef->secdef.u.usage); } else { virBufferAddLit(buf, "/>\n"); } diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index b88e715..b4ad42e 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -1,7 +1,7 @@ /* * virstoragefile.h: file utility functions for FS storage backend * - * Copyright (C) 2007-2009, 2012-2014 Red Hat, Inc. + * Copyright (C) 2007-2009, 2012-2016 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -28,6 +28,7 @@ # include "virseclabel.h" # include "virstorageencryption.h" # include "virutil.h" +# include "secret/secret_util.h" /* Minimum header size required to probe all known formats with * virStorageFileProbeFormat, or obtain metadata from a known format. @@ -201,25 +202,13 @@ typedef enum { } virStorageAuthType; VIR_ENUM_DECL(virStorageAuth) -typedef enum { - VIR_STORAGE_SECRET_TYPE_NONE, - VIR_STORAGE_SECRET_TYPE_UUID, - VIR_STORAGE_SECRET_TYPE_USAGE, - - VIR_STORAGE_SECRET_TYPE_LAST -} virStorageSecretType; - typedef struct _virStorageAuthDef virStorageAuthDef; typedef virStorageAuthDef *virStorageAuthDefPtr; struct _virStorageAuthDef { char *username; char *secrettype; /* <secret type='%s' for disk source */ int authType; /* virStorageAuthType */ - int secretType; /* virStorageSecretType */ - union { - unsigned char uuid[VIR_UUID_BUFLEN]; - char *usage; - } secret; + virSecretLookupTypeDef secdef; }; typedef struct _virStorageDriverData virStorageDriverData; diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index 0fe6b9b..212e6e8 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -36,8 +36,8 @@ static int testSanitizeDef(virDomainDefPtr vmdef) virDomainDiskDefPtr disk = vmdef->disks[i]; if (disk->src->auth) { - disk->src->auth->secretType = VIR_STORAGE_SECRET_TYPE_USAGE; - if (VIR_STRDUP(disk->src->auth->secret.usage, + disk->src->auth->secdef.type = VIR_SECRET_LOOKUP_TYPE_USAGE; + if (VIR_STRDUP(disk->src->auth->secdef.u.usage, "qemuargv2xml_usage") < 0) goto fail; } -- 2.5.5

Make them work again... The xml2xml had been working, but the xml2argv were not working. Making the xml2argv work required a few adjustments to the xml to update to more recent times. Signed-off-by: John Ferlan <jferlan@redhat.com> --- .../qemuxml2argv-encrypted-disk.args | 26 +++++++++------------- .../qemuxml2argv-encrypted-disk.xml | 4 ++-- tests/qemuxml2argvtest.c | 2 ++ .../qemuxml2xmlout-encrypted-disk.xml | 4 ++-- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.args b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.args index 206632c..4371413 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.args @@ -1,28 +1,24 @@ LC_ALL=C \ -PATH=/sbin:/usr/sbin:/bin:/usr/bin \ -HOME=/root \ -USER=root \ -LOGNAME=root \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ /usr/bin/qemu \ -name encryptdisk \ -S \ --M pc-0.13 \ +-M pc \ -m 1024 \ --smp 1,sockets=1,cores=1,threads=1 \ --uuid 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 \ +-smp 1 \ -uuid 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 \ -nographic \ --nodefconfig \ -nodefaults \ --chardev socket,id=monitor,path=//var/lib/libvirt/qemu/encryptdisk.monitor,\ -server,nowait \ --mon chardev=monitor,mode=readline \ --rtc base=utc \ +-monitor unix:/tmp/lib/domain--1-encryptdisk/monitor.sock,server,nowait \ -no-acpi \ -boot c \ -usb \ --drive file=/storage/guest_disks/encryptdisk,format=raw,if=none,\ -id=drive-virtio-disk0,\,boot=on \ --device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\ +-drive file=/storage/guest_disks/encryptdisk,format=qcow2,if=none,\ +id=drive-virtio-disk0 \ +-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\ id=virtio-disk0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.xml b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.xml index 6de570a..f2122f0 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk.xml @@ -1,11 +1,11 @@ -<domain type='kvm'> +<domain type='qemu'> <name>encryptdisk</name> <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> <memory unit='KiB'>1048576</memory> <currentMemory unit='KiB'>524288</currentMemory> <vcpu placement='static'>1</vcpu> <os> - <type arch='i686' machine='pc-0.13'>hvm</type> + <type arch='i686' machine='pc'>hvm</type> <boot dev='hd'/> </os> <clock offset='utc'/> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index f009d09..f827d1a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1322,6 +1322,8 @@ mymain(void) DO_TEST("cpu-Haswell-noTSX", QEMU_CAPS_KVM); driver.caps->host.cpu = cpuDefault; + DO_TEST("encrypted-disk", NONE); + DO_TEST("memtune", NONE); DO_TEST("memtune-unlimited", NONE); DO_TEST("blkiotune", NONE); diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk.xml index 038a0e7..1616d2e 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk.xml @@ -1,11 +1,11 @@ -<domain type='kvm'> +<domain type='qemu'> <name>encryptdisk</name> <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> <memory unit='KiB'>1048576</memory> <currentMemory unit='KiB'>524288</currentMemory> <vcpu placement='static'>1</vcpu> <os> - <type arch='i686' machine='pc-0.13'>hvm</type> + <type arch='i686' machine='pc'>hvm</type> <boot dev='hd'/> </os> <clock offset='utc'/> -- 2.5.5

In order to use more common code and set up for a future type, modify the encryption secret to allow the "usage" attribute or the "uuid" attribute to define the secret. The "usage" in the case of a volume secret would be the path to the volume. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatstorageencryption.html.in | 15 ++++++--- docs/schemas/storagecommon.rng | 11 +++++-- src/qemu/qemu_process.c | 12 ++----- src/storage/storage_backend.c | 3 +- src/storage/storage_backend_fs.c | 3 +- src/util/virstorageencryption.c | 38 +++++++++++++++++----- src/util/virstorageencryption.h | 3 +- .../qemuxml2argv-encrypted-disk-usage.args | 24 ++++++++++++++ .../qemuxml2argv-encrypted-disk-usage.xml | 32 ++++++++++++++++++ tests/qemuxml2argvtest.c | 1 + .../qemuxml2xmlout-encrypted-disk-usage.xml | 36 ++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 12 files changed, 152 insertions(+), 27 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk-usage.xml diff --git a/docs/formatstorageencryption.html.in b/docs/formatstorageencryption.html.in index 04c3346..048cc8e 100644 --- a/docs/formatstorageencryption.html.in +++ b/docs/formatstorageencryption.html.in @@ -25,10 +25,17 @@ <p> The <code>encryption</code> tag can currently contain a sequence of <code>secret</code> tags, each with mandatory attributes <code>type</code> - and <code>uuid</code>. The only currently defined value of - <code>type</code> is <code>passphrase</code>. <code>uuid</code> - refers to a secret known to libvirt. libvirt can use a secret value - previously set using <code>virSecretSetValue()</code>, or, if supported + and either <code>uuid</code> or + <code>usage</code> (<span class="since">since 1.3.6</span>). + The only currently defined value of + <code>type</code> is <code>passphrase</code>. The <code>uuid</code> + refers to a secret known to libvirt by it's "uuid" value (from the + output of a <code>virsh secret-list</code>. The <code>usage</code> + is the path to the volume as it appears in the volume + <code>source</code> element. A secret value can be set in libvirt by + using either <code>virsh secret-set-value</code> or the + <a href="html/libvirt-libvirt-secret.html#virSecretSetValue"> + <code>virSecretSetValue</code></a> API. Alternatively, if supported by the particular volume format and driver, automatically generate a secret value at the time of volume creation, and store it using the specified <code>uuid</code>. diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng index 7c04462..c5b71de 100644 --- a/docs/schemas/storagecommon.rng +++ b/docs/schemas/storagecommon.rng @@ -27,9 +27,14 @@ <value>passphrase</value> </choice> </attribute> - <attribute name='uuid'> - <ref name="UUID"/> - </attribute> + <choice> + <attribute name='uuid'> + <ref name="UUID"/> + </attribute> + <attribute name='usage'> + <text/> + </attribute> + </choice> </element> </define> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 66bc4b1..ce92e23 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -377,7 +377,6 @@ qemuProcessGetVolumeQcowPassphrase(virConnectPtr conn, char **secretRet, size_t *secretLen) { - virSecretPtr secret; char *passphrase; unsigned char *data; size_t size; @@ -416,14 +415,9 @@ qemuProcessGetVolumeQcowPassphrase(virConnectPtr conn, goto cleanup; } - secret = conn->secretDriver->secretLookupByUUID(conn, - enc->secrets[0]->uuid); - if (secret == NULL) - goto cleanup; - data = conn->secretDriver->secretGetValue(secret, &size, 0, - VIR_SECRET_GET_VALUE_INTERNAL_CALL); - virObjectUnref(secret); - if (data == NULL) + if (virSecretGetSecretString(conn, &enc->secrets[0]->secdef, + VIR_SECRET_USAGE_TYPE_VOLUME, + &data, &size) < 0) goto cleanup; if (memchr(data, '\0', size) != NULL) { diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index d041530..11f6081 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -648,7 +648,8 @@ virStorageGenerateQcowEncryption(virConnectPtr conn, goto cleanup; enc_secret->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE; - memcpy(enc_secret->uuid, secret->uuid, VIR_UUID_BUFLEN); + enc_secret->secdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; + memcpy(enc_secret->secdef.u.uuid, secret->uuid, VIR_UUID_BUFLEN); enc->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; enc->secrets[0] = enc_secret; /* Space for secrets[0] allocated above */ enc_secret = NULL; diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index a11df36..22cfbc0 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1318,7 +1318,8 @@ virStorageBackendFileSystemLoadDefaultSecrets(virConnectPtr conn, vol->target.encryption->secrets[0] = encsec; encsec->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE; - virSecretGetUUID(sec, encsec->uuid); + encsec->secdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; + virSecretGetUUID(sec, encsec->secdef.u.uuid); virObjectUnref(sec); return 0; diff --git a/src/util/virstorageencryption.c b/src/util/virstorageencryption.c index 8105158..aa1acbd 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -114,6 +114,7 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, virStorageEncryptionSecretPtr ret; char *type_str = NULL; char *uuidstr = NULL; + char *usagestr = NULL; if (VIR_ALLOC(ret) < 0) return NULL; @@ -133,10 +134,25 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, type_str); goto cleanup; } + + uuidstr = virXPathString("string(./@uuid)", ctxt); + usagestr = virXPathString("string(./@usage)", ctxt); + if (uuidstr && usagestr) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("cannot provide both uuid and usage")); + goto cleanup; + } + + if (!uuidstr && !usagestr) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("must provide either uuid or usage")); + goto cleanup; + } VIR_FREE(type_str); - if ((uuidstr = virXPathString("string(./@uuid)", ctxt))) { - if (virUUIDParse(uuidstr, ret->uuid) < 0) { + if (uuidstr) { + ret->secdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; + if (virUUIDParse(uuidstr, ret->secdef.u.uuid) < 0) { virReportError(VIR_ERR_XML_ERROR, _("malformed volume encryption uuid '%s'"), uuidstr); @@ -144,10 +160,10 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, } VIR_FREE(uuidstr); } else { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing volume encryption uuid")); - goto cleanup; + ret->secdef.type = VIR_SECRET_LOOKUP_TYPE_USAGE; + ret->secdef.u.usage = usagestr; } + ctxt->node = old_node; return ret; @@ -155,6 +171,7 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, VIR_FREE(type_str); virStorageEncryptionSecretFree(ret); VIR_FREE(uuidstr); + VIR_FREE(usagestr); ctxt->node = old_node; return NULL; } @@ -252,9 +269,14 @@ virStorageEncryptionSecretFormat(virBufferPtr buf, return -1; } - virUUIDFormat(secret->uuid, uuidstr); - virBufferAsprintf(buf, "<secret type='%s' uuid='%s'/>\n", - type, uuidstr); + if (secret->secdef.type == VIR_SECRET_LOOKUP_TYPE_USAGE) { + virBufferAsprintf(buf, "<secret type='%s' usage='%s'/>\n", + type, secret->secdef.u.usage); + } else { + virUUIDFormat(secret->secdef.u.uuid, uuidstr); + virBufferAsprintf(buf, "<secret type='%s' uuid='%s'/>\n", + type, uuidstr); + } return 0; } diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index 04641b1..a81cb6e 100644 --- a/src/util/virstorageencryption.h +++ b/src/util/virstorageencryption.h @@ -26,6 +26,7 @@ # include "internal.h" # include "virbuffer.h" # include "virutil.h" +# include "secret/secret_util.h" # include <libxml/tree.h> @@ -40,7 +41,7 @@ typedef struct _virStorageEncryptionSecret virStorageEncryptionSecret; typedef virStorageEncryptionSecret *virStorageEncryptionSecretPtr; struct _virStorageEncryptionSecret { int type; /* virStorageEncryptionSecretType */ - unsigned char uuid[VIR_UUID_BUFLEN]; + virSecretLookupTypeDef secdef; }; typedef enum { diff --git a/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.args b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.args new file mode 100644 index 0000000..4371413 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.args @@ -0,0 +1,24 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name encryptdisk \ +-S \ +-M pc \ +-m 1024 \ +-smp 1 \ +-uuid 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-encryptdisk/monitor.sock,server,nowait \ +-no-acpi \ +-boot c \ +-usb \ +-drive file=/storage/guest_disks/encryptdisk,format=qcow2,if=none,\ +id=drive-virtio-disk0 \ +-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\ +id=virtio-disk0 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.xml b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.xml new file mode 100644 index 0000000..6c5d87d --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-encrypted-disk-usage.xml @@ -0,0 +1,32 @@ +<domain type='qemu'> + <name>encryptdisk</name> + <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>524288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/storage/guest_disks/encryptdisk'/> + <target dev='vda' bus='virtio'/> + <encryption format='qcow'> + <secret type='passphrase' usage='/storage/guest_disks/encryptdisk'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index f827d1a..0fca2fb 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1323,6 +1323,7 @@ mymain(void) driver.caps->host.cpu = cpuDefault; DO_TEST("encrypted-disk", NONE); + DO_TEST("encrypted-disk-usage", NONE); DO_TEST("memtune", NONE); DO_TEST("memtune-unlimited", NONE); diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk-usage.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk-usage.xml new file mode 100644 index 0000000..ec6413f --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-encrypted-disk-usage.xml @@ -0,0 +1,36 @@ +<domain type='qemu'> + <name>encryptdisk</name> + <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>524288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/storage/guest_disks/encryptdisk'/> + <target dev='vda' bus='virtio'/> + <encryption format='qcow'> + <secret type='passphrase' usage='/storage/guest_disks/encryptdisk'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index eca8e78..59fdbd2 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -493,6 +493,7 @@ mymain(void) DO_TEST("pci-serial-dev-chardev"); DO_TEST("encrypted-disk"); + DO_TEST("encrypted-disk-usage"); DO_TEST("memtune"); DO_TEST("memtune-unlimited"); DO_TEST("blkiotune"); -- 2.5.5

Currently the assumption is there is one type of disk encryption - in some qcow format which is old and crusty... But there's a new sheriff in town known as 'luks' and we'll need to handle that shortly Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/util/virstoragefile.c | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 54940a0..5d086b9 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1,7 +1,7 @@ /* * virstoragefile.c: file utility functions for FS storage backend * - * Copyright (C) 2007-2014 Red Hat, Inc. + * Copyright (C) 2007-2014, 2016 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -111,6 +111,11 @@ enum { BACKING_STORE_ERROR, }; +enum fi_crypt { + FI_CRYPT_NONE = 0, + FI_CRYPT_QCOW +}; + #define FILE_TYPE_VERSIONS_LAST 2 /* Either 'magic' or 'extension' *must* be provided */ @@ -134,7 +139,8 @@ struct FileTypeInfo { /* Store a COW base image path (possibly relative), * or NULL if there is no COW base image, to RES; * return BACKING_STORE_* */ - int qcowCryptOffset; /* Byte offset from start of file + enum fi_crypt cryptType; /* Style of crypt */ + int cryptOffset; /* Byte offset from start of file * where to find encryption mode, * -1 if encryption is not used */ int (*getBackingStore)(char **res, int *format, @@ -189,16 +195,16 @@ qedGetBackingStore(char **, int *, const char *, size_t); static struct FileTypeInfo const fileTypeInfo[] = { [VIR_STORAGE_FILE_NONE] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, {0}, 0, 0, 0, 0, NULL, NULL }, + -1, {0}, 0, 0, 0, FI_CRYPT_NONE, 0, NULL, NULL }, [VIR_STORAGE_FILE_RAW] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, {0}, 0, 0, 0, 0, NULL, NULL }, + -1, {0}, 0, 0, 0, FI_CRYPT_NONE, 0, NULL, NULL }, [VIR_STORAGE_FILE_DIR] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, {0}, 0, 0, 0, 0, NULL, NULL }, + -1, {0}, 0, 0, 0, FI_CRYPT_NONE, 0, NULL, NULL }, [VIR_STORAGE_FILE_BOCHS] = { /*"Bochs Virtual HD Image", */ /* Untested */ 0, NULL, NULL, LV_LITTLE_ENDIAN, 64, {0x20000}, - 32+16+16+4+4+4+4+4, 8, 1, -1, NULL, NULL + 32+16+16+4+4+4+4+4, 8, 1, FI_CRYPT_NONE, -1, NULL, NULL }, [VIR_STORAGE_FILE_CLOOP] = { /* #!/bin/sh @@ -207,7 +213,7 @@ static struct FileTypeInfo const fileTypeInfo[] = { */ /* Untested */ 0, NULL, NULL, LV_LITTLE_ENDIAN, -1, {0}, - -1, 0, 0, -1, NULL, NULL + -1, 0, 0, FI_CRYPT_NONE, -1, NULL, NULL }, [VIR_STORAGE_FILE_DMG] = { /* XXX QEMU says there's no magic for dmg, @@ -215,60 +221,64 @@ static struct FileTypeInfo const fileTypeInfo[] = { * would have to match) but then disables that check. */ 0, NULL, ".dmg", 0, -1, {0}, - -1, 0, 0, -1, NULL, NULL + -1, 0, 0, FI_CRYPT_NONE, -1, NULL, NULL }, [VIR_STORAGE_FILE_ISO] = { 32769, "CD001", ".iso", LV_LITTLE_ENDIAN, -2, {0}, - -1, 0, 0, -1, NULL, NULL + -1, 0, 0, FI_CRYPT_NONE, -1, NULL, NULL }, [VIR_STORAGE_FILE_VPC] = { 0, "conectix", NULL, LV_BIG_ENDIAN, 12, {0x10000}, - 8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, -1, NULL, NULL + 8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, FI_CRYPT_NONE, -1, NULL, NULL }, /* TODO: add getBackingStore function */ [VIR_STORAGE_FILE_VDI] = { 64, "\x7f\x10\xda\xbe", ".vdi", LV_LITTLE_ENDIAN, 68, {0x00010001}, - 64 + 5 * 4 + 256 + 7 * 4, 8, 1, -1, NULL, NULL}, + 64 + 5 * 4 + 256 + 7 * 4, 8, 1, FI_CRYPT_NONE, -1, NULL, NULL}, /* Not direct file formats, but used for various drivers */ [VIR_STORAGE_FILE_FAT] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, {0}, 0, 0, 0, 0, NULL, NULL }, + -1, {0}, 0, 0, 0, FI_CRYPT_NONE, 0, NULL, NULL }, [VIR_STORAGE_FILE_VHD] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, {0}, 0, 0, 0, 0, NULL, NULL }, + -1, {0}, 0, 0, 0, FI_CRYPT_NONE, 0, NULL, NULL }, [VIR_STORAGE_FILE_PLOOP] = { 0, "WithouFreSpacExt", NULL, LV_LITTLE_ENDIAN, -2, {0}, PLOOP_IMAGE_SIZE_OFFSET, 0, - PLOOP_SIZE_MULTIPLIER, -1, NULL, NULL }, + PLOOP_SIZE_MULTIPLIER, + FI_CRYPT_NONE, -1, NULL, NULL }, /* All formats with a backing store probe below here */ [VIR_STORAGE_FILE_COW] = { 0, "OOOM", NULL, LV_BIG_ENDIAN, 4, {2}, - 4+4+1024+4, 8, 1, -1, cowGetBackingStore, NULL + 4+4+1024+4, 8, 1, FI_CRYPT_NONE, -1, cowGetBackingStore, NULL }, [VIR_STORAGE_FILE_QCOW] = { 0, "QFI", NULL, LV_BIG_ENDIAN, 4, {1}, - QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW1_HDR_CRYPT, qcow1GetBackingStore, NULL + QCOWX_HDR_IMAGE_SIZE, 8, 1, + FI_CRYPT_QCOW, QCOW1_HDR_CRYPT, + qcow1GetBackingStore, NULL }, [VIR_STORAGE_FILE_QCOW2] = { 0, "QFI", NULL, LV_BIG_ENDIAN, 4, {2, 3}, - QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow2GetBackingStore, - qcow2GetFeatures + QCOWX_HDR_IMAGE_SIZE, 8, 1, + FI_CRYPT_QCOW, QCOW2_HDR_CRYPT, + qcow2GetBackingStore, qcow2GetFeatures }, [VIR_STORAGE_FILE_QED] = { /* http://wiki.qemu.org/Features/QED */ 0, "QED", NULL, LV_LITTLE_ENDIAN, -2, {0}, - QED_HDR_IMAGE_SIZE, 8, 1, -1, qedGetBackingStore, NULL + QED_HDR_IMAGE_SIZE, 8, 1, FI_CRYPT_NONE, -1, qedGetBackingStore, NULL }, [VIR_STORAGE_FILE_VMDK] = { 0, "KDMV", NULL, LV_LITTLE_ENDIAN, 4, {1, 2}, - 4+4+4, 8, 512, -1, vmdk4GetBackingStore, NULL + 4+4+4, 8, 512, FI_CRYPT_NONE, -1, vmdk4GetBackingStore, NULL }, }; verify(ARRAY_CARDINALITY(fileTypeInfo) == VIR_STORAGE_FILE_LAST); @@ -814,11 +824,11 @@ virStorageFileGetMetadataInternal(virStorageSourcePtr meta, meta->capacity *= fileTypeInfo[meta->format].sizeMultiplier; } - if (fileTypeInfo[meta->format].qcowCryptOffset != -1) { + if (fileTypeInfo[meta->format].cryptType == FI_CRYPT_QCOW) { int crypt_format; crypt_format = virReadBufInt32BE(buf + - fileTypeInfo[meta->format].qcowCryptOffset); + fileTypeInfo[meta->format].cryptOffset); if (crypt_format && !meta->encryption && VIR_ALLOC(meta->encryption) < 0) goto cleanup; -- 2.5.5

Add the ability to detect a luks encrypted device. This also adding new 16 bit big/little endian macros since the version of a luks device is stored in a uint16_t. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/util/virendian.h | 24 ++++++++++++++++++++++++ src/util/virstoragefile.c | 38 ++++++++++++++++++++++++++++++++------ src/util/virstoragefile.h | 1 + tests/virendiantest.c | 18 ++++++++++++++++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/util/virendian.h b/src/util/virendian.h index eefe48c..97940bd 100644 --- a/src/util/virendian.h +++ b/src/util/virendian.h @@ -90,4 +90,28 @@ ((uint32_t)(uint8_t)((buf)[2]) << 16) | \ ((uint32_t)(uint8_t)((buf)[3]) << 24)) +/** + * virReadBufInt16BE: + * @buf: byte to start reading at (can be 'char*' or 'unsigned char*'); + * evaluating buf must not have any side effects + * + * Read 2 bytes at BUF as a big-endian 16-bit number. Caller is + * responsible to avoid reading beyond array bounds. + */ +# define virReadBufInt16BE(buf) \ + (((uint16_t)(uint8_t)((buf)[0]) << 8) | \ + (uint16_t)(uint8_t)((buf)[1])) + +/** + * virReadBufInt16LE: + * @buf: byte to start reading at (can be 'char*' or 'unsigned char*'); + * evaluating buf must not have any side effects + * + * Read 2 bytes at BUF as a little-endian 16-bit number. Caller is + * responsible to avoid reading beyond array bounds. + */ +# define virReadBufInt16LE(buf) \ + ((uint16_t)(uint8_t)((buf)[0]) | \ + ((uint16_t)(uint8_t)((buf)[1]) << 8)) + #endif /* __VIR_ENDIAN_H__ */ diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 5d086b9..fbdd323 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -63,7 +63,7 @@ VIR_ENUM_IMPL(virStorageFileFormat, "cloop", "dmg", "iso", "vpc", "vdi", /* Not direct file formats, but used for various drivers */ - "fat", "vhd", "ploop", + "fat", "vhd", "ploop", "luks", /* Formats with backing file below here */ "cow", "qcow", "qcow2", "qed", "vmdk") @@ -113,7 +113,8 @@ enum { enum fi_crypt { FI_CRYPT_NONE = 0, - FI_CRYPT_QCOW + FI_CRYPT_QCOW, + FI_CRYPT_LUKS }; #define FILE_TYPE_VERSIONS_LAST 2 @@ -193,6 +194,14 @@ qedGetBackingStore(char **, int *, const char *, size_t); #define PLOOP_IMAGE_SIZE_OFFSET 36 #define PLOOP_SIZE_MULTIPLIER 512 +#define LUKS_HDR_MAGIC_LEN 6 +#define LUKS_HDR_VERSION_LEN 2 + +/* Format described by qemu commit id '3e308f20e' */ +#define LUKS_HDR_VERSION_OFFSET LUKS_HDR_MAGIC_LEN +#define LUKS_HDR_CRYPT_OFFSET LUKS_HDR_MAGIC_LEN + LUKS_HDR_VERSION_LEN + + static struct FileTypeInfo const fileTypeInfo[] = { [VIR_STORAGE_FILE_NONE] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, -1, {0}, 0, 0, 0, FI_CRYPT_NONE, 0, NULL, NULL }, @@ -249,6 +258,13 @@ static struct FileTypeInfo const fileTypeInfo[] = { PLOOP_SIZE_MULTIPLIER, FI_CRYPT_NONE, -1, NULL, NULL }, + /* Magic is 'L','U','K','S', 0xBA, 0xBE + * Set sizeOffset = -1 and let hypervisor handle */ + [VIR_STORAGE_FILE_LUKS] = { + 0, "\x4c\x55\x4b\x53\xba\xbe", NULL, + LV_BIG_ENDIAN, LUKS_HDR_VERSION_OFFSET, {1}, + -1, 0, 0, FI_CRYPT_LUKS, LUKS_HDR_CRYPT_OFFSET, NULL, NULL + }, /* All formats with a backing store probe below here */ [VIR_STORAGE_FILE_COW] = { 0, "OOOM", NULL, @@ -634,7 +650,7 @@ virStorageFileMatchesVersion(int format, char *buf, size_t buflen) { - int version; + int version = 0; size_t i; /* Validate version number info */ @@ -648,10 +664,16 @@ virStorageFileMatchesVersion(int format, if ((fileTypeInfo[format].versionOffset + 4) > buflen) return false; - if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) + if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) { version = virReadBufInt32LE(buf + fileTypeInfo[format].versionOffset); - else - version = virReadBufInt32BE(buf + fileTypeInfo[format].versionOffset); + } else { + if (format == VIR_STORAGE_FILE_LUKS) + version = virReadBufInt16BE(buf + + fileTypeInfo[format].versionOffset); + else + version = virReadBufInt32BE(buf + + fileTypeInfo[format].versionOffset); + } for (i = 0; i < FILE_TYPE_VERSIONS_LAST && fileTypeInfo[format].versionNumbers[i]; @@ -832,6 +854,10 @@ virStorageFileGetMetadataInternal(virStorageSourcePtr meta, if (crypt_format && !meta->encryption && VIR_ALLOC(meta->encryption) < 0) goto cleanup; + } else if (fileTypeInfo[meta->format].cryptType == FI_CRYPT_LUKS) { + /* By definition, this is encrypted */ + if (!meta->encryption && VIR_ALLOC(meta->encryption) < 0) + goto cleanup; } VIR_FREE(meta->backingStoreRaw); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index b4ad42e..1c93f6b 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -74,6 +74,7 @@ typedef enum { VIR_STORAGE_FILE_FAT, VIR_STORAGE_FILE_VHD, VIR_STORAGE_FILE_PLOOP, + VIR_STORAGE_FILE_LUKS, /* Not a format, but a marker: all formats below this point have * libvirt support for following a backing chain */ diff --git a/tests/virendiantest.c b/tests/virendiantest.c index 2097c28..5b96ed8 100644 --- a/tests/virendiantest.c +++ b/tests/virendiantest.c @@ -50,6 +50,15 @@ test1(const void *data ATTRIBUTE_UNUSED) if (virReadBufInt32LE(array + 9) != 0x8d8c8b8aU) goto cleanup; + if (virReadBufInt16BE(array) != 0x0102U) + goto cleanup; + if (virReadBufInt16BE(array + 11) != 0x8c8dU) + goto cleanup; + if (virReadBufInt16LE(array) != 0x0201U) + goto cleanup; + if (virReadBufInt16LE(array + 11) != 0x8d8cU) + goto cleanup; + ret = 0; cleanup: return ret; @@ -81,6 +90,15 @@ test2(const void *data ATTRIBUTE_UNUSED) if (virReadBufInt32LE(array + 9) != 0x8d8c8b8aU) goto cleanup; + if (virReadBufInt16BE(array) != 0x0102U) + goto cleanup; + if (virReadBufInt16BE(array + 11) != 0x8c8dU) + goto cleanup; + if (virReadBufInt16LE(array) != 0x0201U) + goto cleanup; + if (virReadBufInt16LE(array + 11) != 0x8d8cU) + goto cleanup; + ret = 0; cleanup: return ret; -- 2.5.5

Add a new secret type known as "luks" - it will handle adding the secret object for a luks volume for both storage driver create and libvirt usage. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/aclpolkit.html.in | 4 +++ docs/formatsecret.html.in | 60 ++++++++++++++++++++++++++++++++++-- docs/schemas/secret.rng | 10 ++++++ include/libvirt/libvirt-secret.h | 3 +- src/access/viraccessdriverpolkit.c | 13 ++++++++ src/conf/secret_conf.c | 26 +++++++++++++++- src/conf/secret_conf.h | 1 + src/conf/virsecretobj.c | 5 +++ tests/secretxml2xmlin/usage-luks.xml | 7 +++++ tests/secretxml2xmltest.c | 1 + 10 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 tests/secretxml2xmlin/usage-luks.xml diff --git a/docs/aclpolkit.html.in b/docs/aclpolkit.html.in index dae0814..6b7a237 100644 --- a/docs/aclpolkit.html.in +++ b/docs/aclpolkit.html.in @@ -224,6 +224,10 @@ <td>secret_usage_target</td> <td>Name of the associated iSCSI target, if any</td> </tr> + <tr> + <td>secret_usage_luks</td> + <td>Name of the associated luks volume, if any</td> + </tr> </tbody> </table> diff --git a/docs/formatsecret.html.in b/docs/formatsecret.html.in index 599cb38..de4bf8d 100644 --- a/docs/formatsecret.html.in +++ b/docs/formatsecret.html.in @@ -41,8 +41,9 @@ <dd> Specifies what this secret is used for. A mandatory <code>type</code> attribute specifies the usage category, currently - only <code>volume</code>, <code>ceph</code> and <code>iscsi</code> - are defined. Specific usage categories are described below. + only <code>volume</code>, <code>ceph</code>, <code>iscsi</code>, + and <code>luks</code> are defined. Specific usage categories + are described below. </dd> </dl> @@ -241,5 +242,60 @@ <secret usage='libvirtiscsi'/> </auth> </pre> + + <h3><a name="luksUsageType">Usage type "luks"</a></h3> + + <p> + This secret is associated with a luks volume target providing a key + (or passphrase). The secret will be used either by the + <a href="formatstorage.html#StorageVol">storage volume</a> in order to + provide the key to encrypt a luks volume or by the + <a href="formatdomain.html#elementsDisks">disk device</a> in order to + provide the key to decrypt the luks volume for usage. + <span class="since">Since 1.3.6</span>. The following is an example + of a luks-secret.xml file: + </p> + + <pre> + # cat luks-secret.xml + <secret ephemeral='no' private='yes'> + <description>luks key secret</description> + <usage type='luks'> + <key>luks_example</key> + </usage> + </secret> + + # virsh secret-define luks-secret.xml + Secret 718c71bd-67b5-4a2b-87ec-a24e8ca200dc created + + # virsh secret-list + UUID Usage + ----------------------------------------------------------- + 718c71bd-67b5-4a2b-87ec-a24e8ca200dc luks luks_example + # + + </pre> + + <p> + A secret may also be defined via the + <a href="html/libvirt-libvirt-secret.html#virSecretDefineXML"> + <code>virSecretDefineXML</code></a> API. + + Once the secret is defined, a secret value will need to be set. This + value would be the same used to create and use the volume. + The following is a simple example of using + <code>virsh secret-set-value</code> to set the secret value. The + <a href="html/libvirt-libvirt-secret.html#virSecretSetValue"> + <code>virSecretSetValue</code></a> API may also be used to set + a more secure secret without using printable/readable characters. + </p> + + <pre> + # MYSECRET=`printf %s "letmein" | base64` + # virsh secret-set-value 718c71bd-67b5-4a2b-87ec-a24e8ca200dc $MYSECRET + Secret value set + + </pre> + </body> </html> diff --git a/docs/schemas/secret.rng b/docs/schemas/secret.rng index e21e700..ddadc70 100644 --- a/docs/schemas/secret.rng +++ b/docs/schemas/secret.rng @@ -36,6 +36,7 @@ <ref name='usagevolume'/> <ref name='usageceph'/> <ref name='usageiscsi'/> + <ref name='usageluks'/> <!-- More choices later --> </choice> </element> @@ -71,4 +72,13 @@ </element> </define> + <define name='usageluks'> + <attribute name='type'> + <value>luks</value> + </attribute> + <element name='key'> + <ref name='genericName'/> + </element> + </define> + </grammar> diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-secret.h index 3e5cdf6..e06d2e5 100644 --- a/include/libvirt/libvirt-secret.h +++ b/include/libvirt/libvirt-secret.h @@ -4,7 +4,7 @@ * Description: Provides APIs for the management of secrets * Author: Daniel Veillard <veillard@redhat.com> * - * Copyright (C) 2006-2014 Red Hat, Inc. + * Copyright (C) 2006-2014, 2016 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -43,6 +43,7 @@ typedef enum { VIR_SECRET_USAGE_TYPE_VOLUME = 1, VIR_SECRET_USAGE_TYPE_CEPH = 2, VIR_SECRET_USAGE_TYPE_ISCSI = 3, + VIR_SECRET_USAGE_TYPE_LUKS = 4, # ifdef VIR_ENUM_SENTINELS VIR_SECRET_USAGE_TYPE_LAST diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c index 89bc890..70e1337 100644 --- a/src/access/viraccessdriverpolkit.c +++ b/src/access/viraccessdriverpolkit.c @@ -338,6 +338,19 @@ virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, virAccessPermSecretTypeToString(perm), attrs); } break; + case VIR_SECRET_USAGE_TYPE_LUKS: { + const char *attrs[] = { + "connect_driver", driverName, + "secret_uuid", uuidstr, + "secret_usage_key", secret->usage.key, + NULL, + }; + + return virAccessDriverPolkitCheck(manager, + "secret", + virAccessPermSecretTypeToString(perm), + attrs); + } break; } } diff --git a/src/conf/secret_conf.c b/src/conf/secret_conf.c index de9e6cf..ab1d2bc 100644 --- a/src/conf/secret_conf.c +++ b/src/conf/secret_conf.c @@ -29,6 +29,7 @@ #include "viralloc.h" #include "secret_conf.h" #include "virsecretobj.h" +#include "virstring.h" #include "virerror.h" #include "virxml.h" #include "viruuid.h" @@ -38,7 +39,7 @@ VIR_LOG_INIT("conf.secret_conf"); VIR_ENUM_IMPL(virSecretUsage, VIR_SECRET_USAGE_TYPE_LAST, - "none", "volume", "ceph", "iscsi") + "none", "volume", "ceph", "iscsi", "luks") const char * virSecretUsageIDForDef(virSecretDefPtr def) @@ -56,6 +57,9 @@ virSecretUsageIDForDef(virSecretDefPtr def) case VIR_SECRET_USAGE_TYPE_ISCSI: return def->usage.target; + case VIR_SECRET_USAGE_TYPE_LUKS: + return def->usage.key; + default: return NULL; } @@ -85,6 +89,10 @@ virSecretDefFree(virSecretDefPtr def) VIR_FREE(def->usage.target); break; + case VIR_SECRET_USAGE_TYPE_LUKS: + VIR_FREE(def->usage.key); + break; + default: VIR_ERROR(_("unexpected secret usage type %d"), def->usage_type); break; @@ -92,6 +100,7 @@ virSecretDefFree(virSecretDefPtr def) VIR_FREE(def); } + static int virSecretDefParseUsage(xmlXPathContextPtr ctxt, virSecretDefPtr def) @@ -145,6 +154,14 @@ virSecretDefParseUsage(xmlXPathContextPtr ctxt, } break; + case VIR_SECRET_USAGE_TYPE_LUKS: + if (!(def->usage.key = virXPathString("string(./usage/key)", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("luks usage specified, but key is missing")); + return -1; + } + break; + default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected secret usage type %d"), @@ -305,6 +322,13 @@ virSecretDefFormatUsage(virBufferPtr buf, } break; + case VIR_SECRET_USAGE_TYPE_LUKS: + if (def->usage.key != NULL) { + virBufferEscapeString(buf, "<key>%s</key>\n", + def->usage.key); + } + break; + default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected secret usage type %d"), diff --git a/src/conf/secret_conf.h b/src/conf/secret_conf.h index 4584403..352c57e 100644 --- a/src/conf/secret_conf.h +++ b/src/conf/secret_conf.h @@ -40,6 +40,7 @@ struct _virSecretDef { char *volume; /* May be NULL */ char *ceph; char *target; + char *key; } usage; }; diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index c46d22c..1b6d556 100644 --- a/src/conf/virsecretobj.c +++ b/src/conf/virsecretobj.c @@ -237,6 +237,11 @@ virSecretObjSearchName(const void *payload, if (STREQ(secret->def->usage.target, data->usageID)) found = 1; break; + + case VIR_SECRET_USAGE_TYPE_LUKS: + if (STREQ(secret->def->usage.key, data->usageID)) + found = 1; + break; } cleanup: diff --git a/tests/secretxml2xmlin/usage-luks.xml b/tests/secretxml2xmlin/usage-luks.xml new file mode 100644 index 0000000..33c4c03 --- /dev/null +++ b/tests/secretxml2xmlin/usage-luks.xml @@ -0,0 +1,7 @@ +<secret ephemeral='no' private='no'> + <uuid>f52a81b2-424e-490c-823d-6bd4235bc572</uuid> + <description>LUKS Key Secret</description> + <usage type='luks'> + <key>mumblyfratz</key> + </usage> +</secret> diff --git a/tests/secretxml2xmltest.c b/tests/secretxml2xmltest.c index b4c9386..c8b8bbf 100644 --- a/tests/secretxml2xmltest.c +++ b/tests/secretxml2xmltest.c @@ -80,6 +80,7 @@ mymain(void) DO_TEST("usage-volume"); DO_TEST("usage-ceph"); DO_TEST("usage-iscsi"); + DO_TEST("usage-luks"); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.5.5

Add parse and format of the luks/key secret including tests for volume XML parsing. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatstorageencryption.html.in | 24 +++++++++++- docs/schemas/storagecommon.rng | 3 ++ src/qemu/qemu_process.c | 6 +++ src/storage/storage_backend.c | 3 +- src/storage/storage_backend_fs.c | 7 +++- src/storage/storage_backend_gluster.c | 2 + src/util/virstorageencryption.c | 4 +- src/util/virstorageencryption.h | 2 + tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml | 41 ++++++++++++++++++++ .../qemuxml2xmlout-luks-disks.xml | 45 ++++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + tests/storagevolxml2xmlin/vol-luks.xml | 21 ++++++++++ tests/storagevolxml2xmlout/vol-luks.xml | 21 ++++++++++ tests/storagevolxml2xmltest.c | 1 + 14 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disks.xml create mode 100644 tests/storagevolxml2xmlin/vol-luks.xml create mode 100644 tests/storagevolxml2xmlout/vol-luks.xml diff --git a/docs/formatstorageencryption.html.in b/docs/formatstorageencryption.html.in index 048cc8e..ae2e815 100644 --- a/docs/formatstorageencryption.html.in +++ b/docs/formatstorageencryption.html.in @@ -59,8 +59,20 @@ the <code>secret</code> element is not present during volume creation, a secret is automatically generated and attached to the volume. </p> + <h3><a name="StorageEncryptionLuks">"luks" format</a></h3> + <p> + The <code>luks</code> format is specific to a luks encrypted volume + and the secret used in order to either encrypt or decrypt the volume. + A single <code><secret type='key'></code> element is expected. + The secret may be referenced via either a <code>uuid</code> or + <code>usage</code> attribute. One of the two must be present. When + present for volume creation, the secret will be used in order for + volume encryption. When present for domain usage, the secret will + be used as the key to decrypt the volume. + <span class="since">Since 1.3.6</span>. + </p> - <h2><a name="example">Example</a></h2> + <h2><a name="example">Examples</a></h2> <p> Here is a simple example, specifying use of the <code>qcow</code> format: @@ -70,5 +82,15 @@ <encryption format='qcow'> <secret type='passphrase' uuid='c1f11a6d-8c5d-4a3e-ac7a-4e171c5e0d4a' /> </encryption></pre> + + <p> + Here is a simple example, specifying use of the <code>luks</code> format: + </p> + <pre> + <encryption format='luks'> + <secret type='key' usage='luks_example'/> + </encryption> + </pre> + </body> </html> diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng index c5b71de..44d4315 100644 --- a/docs/schemas/storagecommon.rng +++ b/docs/schemas/storagecommon.rng @@ -12,6 +12,7 @@ <choice> <value>default</value> <value>qcow</value> + <value>luks</value> </choice> </attribute> <zeroOrMore> @@ -25,6 +26,7 @@ <attribute name='type'> <choice> <value>passphrase</value> + <value>key</value> </choice> </attribute> <choice> @@ -81,6 +83,7 @@ <value>fat</value> <value>vhd</value> <value>ploop</value> + <value>luks</value> <ref name='storageFormatBacking'/> </choice> </define> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index ce92e23..4178a42 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2407,6 +2407,12 @@ qemuProcessInitPasswords(virConnectPtr conn, !virDomainDiskGetSource(vm->def->disks[i])) continue; + if (vm->def->disks[i]->src->encryption->format != + VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT && + vm->def->disks[i]->src->encryption->format != + VIR_STORAGE_ENCRYPTION_FORMAT_QCOW) + continue; + VIR_FREE(secret); if (qemuProcessGetVolumeQcowPassphrase(conn, vm->def->disks[i], diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 11f6081..4965c9e 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -1027,8 +1027,7 @@ virStorageBackendCreateQemuImgCheckEncryption(int format, } } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("qcow volume encryption unsupported with " - "volume format %s"), type); + _("volume encryption unsupported with format %s"), type); return -1; } diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 22cfbc0..cf0ddb8 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -157,7 +157,12 @@ virStorageBackendProbeTarget(virStorageSourcePtr target, case VIR_STORAGE_FILE_QCOW2: (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; break; - default: + + case VIR_STORAGE_FILE_LUKS: + (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_LUKS; + break; + + case VIR_STORAGE_ENCRYPTION_FORMAT_LAST: break; } diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index 0085052..eda060d 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -321,6 +321,8 @@ virStorageBackendGlusterRefreshVol(virStorageBackendGlusterStatePtr state, if (vol->target.format == VIR_STORAGE_FILE_QCOW || vol->target.format == VIR_STORAGE_FILE_QCOW2) vol->target.encryption->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; + if (vol->target.format == VIR_STORAGE_FILE_LUKS) + vol->target.encryption->format = VIR_STORAGE_ENCRYPTION_FORMAT_LUKS; } vol->target.features = meta->features; meta->features = NULL; diff --git a/src/util/virstorageencryption.c b/src/util/virstorageencryption.c index aa1acbd..8db49ce 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -38,11 +38,11 @@ #define VIR_FROM_THIS VIR_FROM_STORAGE VIR_ENUM_IMPL(virStorageEncryptionSecret, - VIR_STORAGE_ENCRYPTION_SECRET_TYPE_LAST, "passphrase") + VIR_STORAGE_ENCRYPTION_SECRET_TYPE_LAST, "passphrase", "key") VIR_ENUM_IMPL(virStorageEncryptionFormat, VIR_STORAGE_ENCRYPTION_FORMAT_LAST, - "default", "qcow") + "default", "qcow", "luks") static void virStorageEncryptionSecretFree(virStorageEncryptionSecretPtr secret) diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index a81cb6e..0f3a33d 100644 --- a/src/util/virstorageencryption.h +++ b/src/util/virstorageencryption.h @@ -32,6 +32,7 @@ typedef enum { VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE = 0, + VIR_STORAGE_ENCRYPTION_SECRET_TYPE_KEY, VIR_STORAGE_ENCRYPTION_SECRET_TYPE_LAST } virStorageEncryptionSecretType; @@ -48,6 +49,7 @@ typedef enum { /* "default" is only valid for volume creation */ VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT = 0, VIR_STORAGE_ENCRYPTION_FORMAT_QCOW, /* Both qcow and qcow2 */ + VIR_STORAGE_ENCRYPTION_FORMAT_LUKS, VIR_STORAGE_ENCRYPTION_FORMAT_LAST, } virStorageEncryptionFormatType; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml b/tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml new file mode 100644 index 0000000..29ea38a --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-luks-disks.xml @@ -0,0 +1,41 @@ +<domain type='qemu'> + <name>encryptdisk</name> + <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>524288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk'/> + <target dev='vda' bus='virtio'/> + <encryption format='luks'> + <secret type='key' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk2'/> + <target dev='vdb' bus='virtio'/> + <encryption format='luks'> + <secret type='key' usage='mycluster_myname'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disks.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disks.xml new file mode 100644 index 0000000..c04506a --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disks.xml @@ -0,0 +1,45 @@ +<domain type='qemu'> + <name>encryptdisk</name> + <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>524288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk'/> + <target dev='vda' bus='virtio'/> + <encryption format='luks'> + <secret type='key' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk2'/> + <target dev='vdb' bus='virtio'/> + <encryption format='luks'> + <secret type='key' usage='mycluster_myname'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 59fdbd2..3862b3b 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -494,6 +494,7 @@ mymain(void) DO_TEST("encrypted-disk"); DO_TEST("encrypted-disk-usage"); + DO_TEST("luks-disks"); DO_TEST("memtune"); DO_TEST("memtune-unlimited"); DO_TEST("blkiotune"); diff --git a/tests/storagevolxml2xmlin/vol-luks.xml b/tests/storagevolxml2xmlin/vol-luks.xml new file mode 100644 index 0000000..11e0b50 --- /dev/null +++ b/tests/storagevolxml2xmlin/vol-luks.xml @@ -0,0 +1,21 @@ +<volume> + <name>LuksDemo.img</name> + <key>/var/lib/libvirt/images/LuksDemo.img</key> + <source> + </source> + <capacity unit="G">5</capacity> + <allocation>294912</allocation> + <target> + <path>/var/lib/libvirt/images/LuksDemo.img</path> + <format type='luks'/> + <permissions> + <mode>0644</mode> + <owner>0</owner> + <group>0</group> + <label>unconfined_u:object_r:virt_image_t:s0</label> + </permissions> + <encryption format='luks'> + <secret type='key' usage='mumblyfratz'/> + </encryption> + </target> +</volume> diff --git a/tests/storagevolxml2xmlout/vol-luks.xml b/tests/storagevolxml2xmlout/vol-luks.xml new file mode 100644 index 0000000..6bb3606 --- /dev/null +++ b/tests/storagevolxml2xmlout/vol-luks.xml @@ -0,0 +1,21 @@ +<volume type='file'> + <name>LuksDemo.img</name> + <key>/var/lib/libvirt/images/LuksDemo.img</key> + <source> + </source> + <capacity unit='bytes'>5368709120</capacity> + <allocation unit='bytes'>294912</allocation> + <target> + <path>/var/lib/libvirt/images/LuksDemo.img</path> + <format type='luks'/> + <permissions> + <mode>0644</mode> + <owner>0</owner> + <group>0</group> + <label>unconfined_u:object_r:virt_image_t:s0</label> + </permissions> + <encryption format='luks'> + <secret type='key' usage='mumblyfratz'/> + </encryption> + </target> +</volume> diff --git a/tests/storagevolxml2xmltest.c b/tests/storagevolxml2xmltest.c index 148d1e6..ebb39ab 100644 --- a/tests/storagevolxml2xmltest.c +++ b/tests/storagevolxml2xmltest.c @@ -105,6 +105,7 @@ mymain(void) DO_TEST("pool-dir", "vol-qcow2-lazy"); DO_TEST("pool-dir", "vol-qcow2-0.10-lazy"); DO_TEST("pool-dir", "vol-qcow2-nobacking"); + DO_TEST("pool-dir", "vol-luks"); DO_TEST("pool-disk", "vol-partition"); DO_TEST("pool-logical", "vol-logical"); DO_TEST("pool-logical", "vol-logical-backing"); -- 2.5.5

For a luks device, allow the configuration of a specific cipher to be used for encrypting the volume. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatstorageencryption.html.in | 78 ++++++++++++- docs/schemas/storagecommon.rng | 44 +++++++- src/conf/domain_conf.c | 11 ++ src/util/virstorageencryption.c | 124 +++++++++++++++++++++ src/util/virstorageencryption.h | 13 +++ .../qemuxml2argv-luks-disk-cipher.xml | 41 +++++++ .../qemuxml2xmlout-luks-disk-cipher.xml | 45 ++++++++ tests/qemuxml2xmltest.c | 1 + tests/storagevolxml2xmlin/vol-luks-cipher.xml | 23 ++++ tests/storagevolxml2xmlout/vol-luks-cipher.xml | 23 ++++ tests/storagevolxml2xmltest.c | 1 + 11 files changed, 400 insertions(+), 4 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml create mode 100644 tests/storagevolxml2xmlin/vol-luks-cipher.xml create mode 100644 tests/storagevolxml2xmlout/vol-luks-cipher.xml diff --git a/docs/formatstorageencryption.html.in b/docs/formatstorageencryption.html.in index ae2e815..dd2f990 100644 --- a/docs/formatstorageencryption.html.in +++ b/docs/formatstorageencryption.html.in @@ -71,6 +71,58 @@ be used as the key to decrypt the volume. <span class="since">Since 1.3.6</span>. </p> + <p> + For volume creation, it is possible to specify the encryption + algorithm used to encrypt the luks volume. The following two + optional elements may be provided for that purpose. It is hypervisor + dependent as to which algorithms are supported. The default algorithm + for QEMU is 'aes-256-cbc' using 'essiv' for initialization vector + generation and 'sha256' hash algorithm for both the master key and + the initialization vector generation. + </p> + + <dl> + <dt><code>cipher</code></dt> + <dd>This element describes the cipher algorithm to be used to either + encrypt or decrypt the key. This element has the following + attributes: + <dl> + <dt><code>name</code></dt> + <dd>The name of the cipher algorithm used for data encryption, + such as 'aes', 'des', 'cast5', 'serpent', 'twofish', etc. + Support of the specific algorithm is hypervisor dependent.</dd> + <dt><code>size</code></dt> + <dd>The size of the cipher in bits, such as '256', '192', '128', + etc. Support of the specific size for a specific cipher is + hypervisor dependent.</dd> + <dt><code>mode</code></dt> + <dd>An optional cipher algorithm mode such as 'cbc', 'xts', + 'ecb', etc. Support of the specific cipher mode is + hypervisor dependent.</dd> + <dt><code>hash</code></dt> + <dd>An optional master key hash algorithm such as 'md5', 'sha1', + 'sha256', etc. Support of the specific hash algorithm is + hypervisor dependent.</dd> + </dl> + </dd> + <dt><code>ivgen</code></dt> + <dd>This optional element describes the initialization vector + generation algorithm used in conjunction with the + <code>cipher</code>. If the <code>cipher</code> is not provided, + then an error will be generated by the parser. + <dl> + <dt><code>name</code></dt> + <dd>The name of the algorithm, such as 'plain', 'plain64', + 'essiv', etc. Support of the specific algorithm is hypervisor + dependent.</dd> + <dt><code>hash</code></dt> + <dd>An optional hash algorithm such as 'md5', 'sha1', 'sha256', + etc. Support of the specific ivgen hash algorithm is hypervisor + dependent.</dd> + </dl> + </dd> + </dl> + <h2><a name="example">Examples</a></h2> @@ -84,7 +136,11 @@ </encryption></pre> <p> - Here is a simple example, specifying use of the <code>luks</code> format: + Assuming a <a href="formatsecret.html#luksUsageType"> + <code>luks secret</code></a> is already defined, the following is + a simple example specifying use of the <code>luks</code> format + for either volume creation without a specific cipher being defined or + as part of a domain volume definition: </p> <pre> <encryption format='luks'> @@ -92,5 +148,25 @@ </encryption> </pre> + <p> + Here is an example, specifying use of the <code>luks</code> format for + a specific cipher algorihm for volume creation: + </p> + <pre> + <volume> + <name>twofish.luks</name> + <capacity unit='G'>5</capacity> + <target> + <path>/var/lib/libvirt/images/demo.luks</path> + <format type='luks'/> + <encryption format='luks'> + <secret type='key' usage='luks_example'/> + <cipher name='twofish' size='256' mode='cbc' hash='sha256'/> + <ivgen name='plain64' hash='sha256'/> + </encryption> + </target> + </volume> + </pre> + </body> </html> diff --git a/docs/schemas/storagecommon.rng b/docs/schemas/storagecommon.rng index 44d4315..b6e8de4 100644 --- a/docs/schemas/storagecommon.rng +++ b/docs/schemas/storagecommon.rng @@ -15,9 +15,19 @@ <value>luks</value> </choice> </attribute> - <zeroOrMore> - <ref name='secret'/> - </zeroOrMore> + <interleave> + <zeroOrMore> + <ref name='secret'/> + </zeroOrMore> + <optional> + <element name='cipher'> + <ref name='keycipher'/> + </element> + <element name='ivgen'> + <ref name='keyivgen'/> + </element> + </optional> + </interleave> </element> </define> @@ -137,4 +147,32 @@ </optional> </define> + <define name='keycipher'> + <attribute name='name'> + <text/> + </attribute> + <attribute name='size'> + <ref name="unsignedInt"/> + </attribute> + <optional> + <attribute name='mode'> + <text/> + </attribute> + <attribute name='hash'> + <text/> + </attribute> + </optional> + </define> + + <define name='keyivgen'> + <attribute name='name'> + <text/> + </attribute> + <optional> + <attribute name='hash'> + <text/> + </attribute> + </optional> + </define> + </grammar> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index cec361f..fc098ec 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7756,6 +7756,17 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt, def->startupPolicy = val; } + if (encryption) { + if (encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS && + encryption->cipher.name) { + + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("supplying the <cipher> for a domain is " + "unnecessary")); + goto error; + } + } + def->dst = target; target = NULL; def->src->auth = authdef; diff --git a/src/util/virstorageencryption.c b/src/util/virstorageencryption.c index 8db49ce..08cbb52 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -34,6 +34,7 @@ #include "virerror.h" #include "viruuid.h" #include "virfile.h" +#include "virstring.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -45,6 +46,15 @@ VIR_ENUM_IMPL(virStorageEncryptionFormat, "default", "qcow", "luks") static void +virStorageEncryptionInfoDefFree(virStorageEncryptionInfoDefPtr def) +{ + VIR_FREE(def->name); + VIR_FREE(def->mode); + VIR_FREE(def->hash); +} + + +static void virStorageEncryptionSecretFree(virStorageEncryptionSecretPtr secret) { if (!secret) @@ -62,6 +72,8 @@ virStorageEncryptionFree(virStorageEncryptionPtr enc) for (i = 0; i < enc->nsecrets; i++) virStorageEncryptionSecretFree(enc->secrets[i]); + virStorageEncryptionInfoDefFree(&enc->cipher); + virStorageEncryptionInfoDefFree(&enc->ivgen); VIR_FREE(enc->secrets); VIR_FREE(enc); } @@ -79,6 +91,20 @@ virStorageEncryptionSecretCopy(const virStorageEncryptionSecret *src) return ret; } + +static int +virStorageEncryptionInfoDefCopy(const virStorageEncryptionInfoDef *src, + virStorageEncryptionInfoDefPtr dst) +{ + if (VIR_STRDUP(dst->name, src->name) < 0 || + VIR_STRDUP(dst->mode, src->mode) < 0 || + VIR_STRDUP(dst->hash, src->hash) < 0) + return -1; + + return 0; +} + + virStorageEncryptionPtr virStorageEncryptionCopy(const virStorageEncryption *src) { @@ -99,6 +125,12 @@ virStorageEncryptionCopy(const virStorageEncryption *src) goto error; } + if (virStorageEncryptionInfoDefCopy(&src->cipher, &ret->cipher) < 0) + goto error; + + if (virStorageEncryptionInfoDefCopy(&src->ivgen, &ret->ivgen) < 0) + goto error; + return ret; error: @@ -176,6 +208,51 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, return NULL; } + +static int +virStorageEncryptionInfoParse(xmlNodePtr info_node, + virStorageEncryptionInfoDefPtr info, + bool require_size) +{ + int ret = -1; + char *size_str = NULL; + + if (!(info->name = virXMLPropString(info_node, "name"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing secret key info name string")); + goto cleanup; + } + + /* Check for a size string - it's required for cipher, but not for ivgen + * if provided for ivgen then just ignore */ + if (require_size) { + if ((size_str = virXMLPropString(info_node, "size")) && + virStrToLong_uip(size_str, NULL, 10, &info->size) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse secret key info size string '%s'"), + size_str); + goto cleanup; + } + + if (!size_str) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("secret key info missing 'size' attribute")); + goto cleanup; + } + } + + /* Optional */ + info->mode = virXMLPropString(info_node, "mode"); + info->hash = virXMLPropString(info_node, "hash"); + + ret = 0; + + cleanup: + VIR_FREE(size_str); + return ret; +} + + static virStorageEncryptionPtr virStorageEncryptionParseXML(xmlXPathContextPtr ctxt) { @@ -219,6 +296,28 @@ virStorageEncryptionParseXML(xmlXPathContextPtr ctxt) VIR_FREE(nodes); } + if (ret->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + xmlNodePtr tmpnode; + + if ((tmpnode = virXPathNode("./cipher[1]", ctxt))) { + if (virStorageEncryptionInfoParse(tmpnode, &ret->cipher, true) < 0) + goto cleanup; + } + + if ((tmpnode = virXPathNode("./ivgen[1]", ctxt))) { + /* If no cipher node, then fail */ + if (!ret->cipher.name) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing storage encryption cipher")); + goto cleanup; + } + + if (virStorageEncryptionInfoParse(tmpnode, &ret->ivgen, false) < 0) + goto cleanup; + } + } + + return ret; cleanup: @@ -280,6 +379,28 @@ virStorageEncryptionSecretFormat(virBufferPtr buf, return 0; } + +static void +virStorageEncryptionInfoDefFormat(virBufferPtr buf, + const virStorageEncryption *enc) +{ + virBufferAsprintf(buf, "<cipher name='%s' size='%u'", + enc->cipher.name, enc->cipher.size); + if (enc->cipher.mode) + virBufferAsprintf(buf, " mode='%s'", enc->cipher.mode); + if (enc->cipher.hash) + virBufferAsprintf(buf, " hash='%s'", enc->cipher.hash); + virBufferAddLit(buf, "/>\n"); + + if (enc->ivgen.name) { + virBufferAsprintf(buf, "<ivgen name='%s'", enc->ivgen.name); + if (enc->ivgen.hash) + virBufferAsprintf(buf, " hash='%s'", enc->ivgen.hash); + virBufferAddLit(buf, "/>\n"); + } +} + + int virStorageEncryptionFormat(virBufferPtr buf, virStorageEncryptionPtr enc) @@ -300,6 +421,9 @@ virStorageEncryptionFormat(virBufferPtr buf, return -1; } + if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS && enc->cipher.name) + virStorageEncryptionInfoDefFormat(buf, enc); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</encryption>\n"); diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index 0f3a33d..74e52d1 100644 --- a/src/util/virstorageencryption.h +++ b/src/util/virstorageencryption.h @@ -45,6 +45,16 @@ struct _virStorageEncryptionSecret { virSecretLookupTypeDef secdef; }; +/* For a key type it's possible to dictate the cipher and if necessary iv */ +typedef struct _virStorageEncryptionInfoDef virStorageEncryptionInfoDef; +typedef virStorageEncryptionInfoDef *virStorageEncryptionInfoDefPtr; +struct _virStorageEncryptionInfoDef { + unsigned int size; + char *name; + char *mode; + char *hash; +}; + typedef enum { /* "default" is only valid for volume creation */ VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT = 0, @@ -62,6 +72,9 @@ struct _virStorageEncryption { size_t nsecrets; virStorageEncryptionSecretPtr *secrets; + + virStorageEncryptionInfoDef cipher; + virStorageEncryptionInfoDef ivgen; }; virStorageEncryptionPtr virStorageEncryptionCopy(const virStorageEncryption *src) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml b/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml new file mode 100644 index 0000000..29ea38a --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.xml @@ -0,0 +1,41 @@ +<domain type='qemu'> + <name>encryptdisk</name> + <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>524288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk'/> + <target dev='vda' bus='virtio'/> + <encryption format='luks'> + <secret type='key' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk2'/> + <target dev='vdb' bus='virtio'/> + <encryption format='luks'> + <secret type='key' usage='mycluster_myname'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml new file mode 100644 index 0000000..c04506a --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-luks-disk-cipher.xml @@ -0,0 +1,45 @@ +<domain type='qemu'> + <name>encryptdisk</name> + <uuid>496898a6-e6ff-f7c8-5dc2-3cf410945ee9</uuid> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>524288</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-i440fx-2.1'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk'/> + <target dev='vda' bus='virtio'/> + <encryption format='luks'> + <secret type='key' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='luks'/> + <source file='/storage/guest_disks/encryptdisk2'/> + <target dev='vdb' bus='virtio'/> + <encryption format='luks'> + <secret type='key' usage='mycluster_myname'/> + </encryption> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 3862b3b..0d1298b 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -495,6 +495,7 @@ mymain(void) DO_TEST("encrypted-disk"); DO_TEST("encrypted-disk-usage"); DO_TEST("luks-disks"); + DO_TEST("luks-disk-cipher"); DO_TEST("memtune"); DO_TEST("memtune-unlimited"); DO_TEST("blkiotune"); diff --git a/tests/storagevolxml2xmlin/vol-luks-cipher.xml b/tests/storagevolxml2xmlin/vol-luks-cipher.xml new file mode 100644 index 0000000..6c7ad82 --- /dev/null +++ b/tests/storagevolxml2xmlin/vol-luks-cipher.xml @@ -0,0 +1,23 @@ +<volume> + <name>LuksDemo.img</name> + <key>/var/lib/libvirt/images/LuksDemo.img</key> + <source> + </source> + <capacity unit="G">5</capacity> + <allocation>294912</allocation> + <target> + <path>/var/lib/libvirt/images/LuksDemo.img</path> + <format type='luks'/> + <permissions> + <mode>0644</mode> + <owner>0</owner> + <group>0</group> + <label>unconfined_u:object_r:virt_image_t:s0</label> + </permissions> + <encryption format='luks'> + <secret type='key' usage='mumblyfratz'/> + <cipher name='serpent' size='256' mode='cbc' hash='sha256'/> + <ivgen name='plain64' hash='sha256'/> + </encryption> + </target> +</volume> diff --git a/tests/storagevolxml2xmlout/vol-luks-cipher.xml b/tests/storagevolxml2xmlout/vol-luks-cipher.xml new file mode 100644 index 0000000..22a7383 --- /dev/null +++ b/tests/storagevolxml2xmlout/vol-luks-cipher.xml @@ -0,0 +1,23 @@ +<volume type='file'> + <name>LuksDemo.img</name> + <key>/var/lib/libvirt/images/LuksDemo.img</key> + <source> + </source> + <capacity unit='bytes'>5368709120</capacity> + <allocation unit='bytes'>294912</allocation> + <target> + <path>/var/lib/libvirt/images/LuksDemo.img</path> + <format type='luks'/> + <permissions> + <mode>0644</mode> + <owner>0</owner> + <group>0</group> + <label>unconfined_u:object_r:virt_image_t:s0</label> + </permissions> + <encryption format='luks'> + <secret type='key' usage='mumblyfratz'/> + <cipher name='serpent' size='256' mode='cbc' hash='sha256'/> + <ivgen name='plain64' hash='sha256'/> + </encryption> + </target> +</volume> diff --git a/tests/storagevolxml2xmltest.c b/tests/storagevolxml2xmltest.c index ebb39ab..d1f3330 100644 --- a/tests/storagevolxml2xmltest.c +++ b/tests/storagevolxml2xmltest.c @@ -106,6 +106,7 @@ mymain(void) DO_TEST("pool-dir", "vol-qcow2-0.10-lazy"); DO_TEST("pool-dir", "vol-qcow2-nobacking"); DO_TEST("pool-dir", "vol-luks"); + DO_TEST("pool-dir", "vol-luks-cipher"); DO_TEST("pool-disk", "vol-partition"); DO_TEST("pool-logical", "vol-logical"); DO_TEST("pool-logical", "vol-logical-backing"); -- 2.5.5

If the volume xml was looking to create a luks volume take the necessary steps in order to make that happen. The processing will be: 1. create a temporary file in the storage pool target path 1a. the file name will either be $volname_$UUID.tmp or $volname_$USAGE.tmp depending on how the encryption xml specified fetching the secret 1b. create/open the file, initially setting mode to 0600 with current effective uid:gid as owner 1c. fetch the secret into a buffer and write that into the file 1d. change file protections to 0400 2. create a secret object 2a. use a similarly crafted alias to the file name 2b. add the file to the object 3. create/add luks options to the commandline 3a. at the very least a "key-secret" using the secret object alias 3b. if found in the XML the various "cipher" and "ivgen" options Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/libvirt_private.syms | 1 + src/storage/storage_backend.c | 285 ++++++++++++++++++++++++++++++++++++++--- src/storage/storage_backend.h | 3 +- src/util/virqemu.c | 23 ++++ src/util/virqemu.h | 6 + tests/storagevolxml2argvtest.c | 3 +- 6 files changed, 300 insertions(+), 21 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3fd042b..ea71037 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2142,6 +2142,7 @@ virProcessWait; # util/virqemu.h +virQEMUBuildLuksOpts; virQEMUBuildObjectCommandlineFromJSON; diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 4965c9e..bb4fa2f 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -55,11 +55,14 @@ #include "viralloc.h" #include "internal.h" #include "secret_conf.h" +#include "secret_util.h" #include "viruuid.h" #include "virstoragefile.h" #include "storage_backend.h" #include "virlog.h" #include "virfile.h" +#include "virjson.h" +#include "virqemu.h" #include "stat-time.h" #include "virstring.h" #include "virxml.h" @@ -880,6 +883,7 @@ virStoragePloopResize(virStorageVolDefPtr vol, enum { QEMU_IMG_BACKING_FORMAT_OPTIONS = 0, QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT, + QEMU_IMG_FORMAT_LUKS, }; static bool @@ -907,6 +911,27 @@ virStorageBackendQemuImgSupportsCompat(const char *qemuimg) return ret; } + +static bool +virStorageBackendQemuImgSupportsLuks(const char *qemuimg) +{ + bool ret = false; + int exitstatus = -1; + virCommandPtr cmd = virCommandNewArgList(qemuimg, "create", "-o", "?", + "-f", "luks", "/dev/null", NULL); + + if (virCommandRun(cmd, &exitstatus) < 0) + goto cleanup; + + if (exitstatus == 0) + ret = true; + + cleanup: + virCommandFree(cmd); + return ret; +} + + static int virStorageBackendQEMUImgBackingFormat(const char *qemuimg) { @@ -915,12 +940,18 @@ virStorageBackendQEMUImgBackingFormat(const char *qemuimg) * out what else we have */ int ret = QEMU_IMG_BACKING_FORMAT_OPTIONS; - /* QEMU 2.0 changed to using a format that only QEMU 1.1 and newer - * understands. Since we still support QEMU 0.12 and newer, we need - * to be able to handle the previous format as can be set via a - * compat=0.10 option. */ - if (virStorageBackendQemuImgSupportsCompat(qemuimg)) - ret = QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT; + /* QEMU 2.6 added support for luks - let's check for that. + * If available, then we can also assume OPTIONS_COMPAT is present */ + if (virStorageBackendQemuImgSupportsLuks(qemuimg)) { + ret = QEMU_IMG_FORMAT_LUKS; + } else { + /* QEMU 2.0 changed to using a format that only QEMU 1.1 and newer + * understands. Since we still support QEMU 0.12 and newer, we need + * to be able to handle the previous format as can be set via a + * compat=0.10 option. */ + if (virStorageBackendQemuImgSupportsCompat(qemuimg)) + ret = QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT; + } return ret; } @@ -941,21 +972,31 @@ struct _virStorageBackendQemuImgInfo { const char *inputPath; const char *inputFormatStr; int inputFormat; + + char *secretAlias; + const char *secretPath; }; + static int -virStorageBackendCreateQemuImgOpts(char **opts, +virStorageBackendCreateQemuImgOpts(virStorageEncryptionPtr enc, + char **opts, struct _virStorageBackendQemuImgInfo info) { virBuffer buf = VIR_BUFFER_INITIALIZER; - if (info.backingPath) - virBufferAsprintf(&buf, "backing_fmt=%s,", - virStorageFileFormatTypeToString(info.backingFormat)); - if (info.encryption) - virBufferAddLit(&buf, "encryption=on,"); - if (info.preallocate) - virBufferAddLit(&buf, "preallocation=metadata,"); + if (info.format == VIR_STORAGE_FILE_LUKS) { + virQEMUBuildLuksOpts(&buf, enc, info.secretAlias); + } else { + if (info.backingPath) + virBufferAsprintf(&buf, "backing_fmt=%s,", + virStorageFileFormatTypeToString(info.backingFormat)); + if (info.encryption) + virBufferAddLit(&buf, "encryption=on,"); + if (info.preallocate) + virBufferAddLit(&buf, "preallocation=metadata,"); + } + if (info.nocow) virBufferAddLit(&buf, "nocow=on,"); @@ -1025,6 +1066,22 @@ virStorageBackendCreateQemuImgCheckEncryption(int format, if (virStorageGenerateQcowEncryption(conn, vol) < 0) return -1; } + } else if (format == VIR_STORAGE_FILE_LUKS) { + if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_LUKS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported volume encryption format %d"), + vol->target.encryption->format); + return -1; + } + if (enc->nsecrets > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("too many secrets for luks encryption")); + return -1; + } + if (enc->nsecrets == 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("no secret provided for luks encryption")); + } } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("volume encryption unsupported with format %s"), type); @@ -1069,6 +1126,12 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool, int accessRetCode = -1; char *absolutePath = NULL; + if (info->format == VIR_STORAGE_FILE_LUKS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot set backing store for luks volume")); + return -1; + } + info->backingFormat = vol->target.backingStore->format; info->backingPath = vol->target.backingStore->path; @@ -1121,6 +1184,7 @@ virStorageBackendCreateQemuImgSetBacking(virStoragePoolObjPtr pool, static int virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, + virStorageVolDefPtr vol, int imgformat, struct _virStorageBackendQemuImgInfo info) { @@ -1130,7 +1194,8 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, imgformat >= QEMU_IMG_BACKING_FORMAT_OPTIONS_COMPAT) info.compat = "0.10"; - if (virStorageBackendCreateQemuImgOpts(&opts, info) < 0) + if (virStorageBackendCreateQemuImgOpts(vol->target.encryption, + &opts, info) < 0) return -1; if (opts) virCommandAddArgList(cmd, "-o", opts, NULL); @@ -1140,6 +1205,66 @@ virStorageBackendCreateQemuImgSetOptions(virCommandPtr cmd, } +/* Create a hopefully unique enough name to be used for both the + * secretPath and secretAlias generation + */ +static char * +virStorageBackendCreateQemuImgLuksName(const char *volname, + virStorageEncryptionPtr enc) +{ + char *ret; + + if (enc->secrets[0]->secdef.type == VIR_SECRET_LOOKUP_TYPE_UUID) + ignore_value(virAsprintf(&ret, "%s_%s", volname, + enc->secrets[0]->secdef.u.uuid)); + else + ignore_value(virAsprintf(&ret, "%s_%s", volname, + enc->secrets[0]->secdef.u.usage)); + return ret; +} + + +/* Add a secret object to the command line: + * --object secret,id=$secretAlias,file=$secretPath + * + * NB: format=raw is assumed + */ +static int +virStorageBackendCreateQemuImgSecretObject(virCommandPtr cmd, + virStorageVolDefPtr vol, + struct _virStorageBackendQemuImgInfo *info) +{ + virStorageEncryptionPtr enc = vol->target.encryption; + char *str = NULL; + virJSONValuePtr props = NULL; + char *commandStr = NULL; + + if (!(str = virStorageBackendCreateQemuImgLuksName(vol->name, enc))) + return -1; + + if (virAsprintf(&info->secretAlias, "%s_luks0", str) < 0) { + VIR_FREE(str); + return -1; + } + VIR_FREE(str); + + if (virJSONValueObjectCreate(&props, "s:file", info->secretPath, NULL) < 0) + return -1; + + if (!(commandStr = virQEMUBuildObjectCommandlineFromJSON("secret", + info->secretAlias, + props))) { + virJSONValueFree(props); + return -1; + } + virJSONValueFree(props); + + virCommandAddArgList(cmd, "--object", commandStr, NULL); + + return 0; +} + + /* Create a qemu-img virCommand from the supplied binary path, * volume definitions and imgformat */ @@ -1150,7 +1275,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, virStorageVolDefPtr inputvol, unsigned int flags, const char *create_tool, - int imgformat) + int imgformat, + const char *secretPath) { virCommandPtr cmd = NULL; const char *type; @@ -1162,6 +1288,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, .compat = vol->target.compat, .features = vol->target.features, .nocow = vol->target.nocow, + .secretPath = secretPath, + .secretAlias = NULL, }; virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, NULL); @@ -1192,6 +1320,18 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, _("format features only available with qcow2")); return NULL; } + if (info.format == VIR_STORAGE_FILE_LUKS) { + if (inputvol) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot use inputvol with luks volume")); + return NULL; + } + if (!info.encryption) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing encryption description")); + return NULL; + } + } if (inputvol && virStorageBackendCreateQemuImgSetInput(inputvol, &info) < 0) @@ -1207,7 +1347,6 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, conn, vol) < 0) return NULL; - /* Size in KB */ info.size_arg = VIR_DIV_UP(vol->target.capacity, 1024); @@ -1226,10 +1365,20 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, if (info.backingPath) virCommandAddArgList(cmd, "-b", info.backingPath, NULL); - if (virStorageBackendCreateQemuImgSetOptions(cmd, imgformat, info) < 0) { + if (info.format == VIR_STORAGE_FILE_LUKS && + virStorageBackendCreateQemuImgSecretObject(cmd, vol, &info) < 0) { + VIR_FREE(info.secretAlias); + virCommandFree(cmd); + return NULL; + } + + if (virStorageBackendCreateQemuImgSetOptions(cmd, vol, imgformat, + info) < 0) { + VIR_FREE(info.secretAlias); virCommandFree(cmd); return NULL; } + VIR_FREE(info.secretAlias); if (info.inputPath) virCommandAddArg(cmd, info.inputPath); @@ -1240,6 +1389,86 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, return cmd; } + +static char * +virStorageBackendCreateQemuImgSecretPath(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + virStorageEncryptionPtr enc = vol->target.encryption; + char *str = NULL; + char *secretPath = NULL; + int fd = -1; + uint8_t *secret = NULL; + size_t secretlen = 0; + + if (!enc) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing encryption description")); + return NULL; + } + + if (!conn || !conn->secretDriver || + !conn->secretDriver->secretLookupByUUID || + !conn->secretDriver->secretLookupByUsage || + !conn->secretDriver->secretGetValue) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to look up encryption secret")); + return NULL; + } + + /* Create a temporary secret file in the pool using */ + if (!(str = virStorageBackendCreateQemuImgLuksName(vol->name, enc))) + return NULL; + + /* Since we don't have a file, just go to cleanup using NULL secretPath */ + if (!(secretPath = virFileBuildPath(pool->def->target.path, str, ".tmp"))) + goto cleanup; + + if ((fd = open(secretPath, O_WRONLY|O_TRUNC|O_CREAT, 0600)) < 0) { + virReportSystemError(errno, "%s", + _("failed to open luks secret file for write")); + goto error; + } + + if (virSecretGetSecretString(conn, &enc->secrets[0]->secdef, + VIR_SECRET_USAGE_TYPE_LUKS, + &secret, &secretlen) < 0) + goto error; + + if (safewrite(fd, secret, secretlen) < 0) { + virReportSystemError(errno, "%s", + _("failed to write luks secret file")); + goto error; + } + VIR_FORCE_CLOSE(fd); + + if (chown(secretPath, geteuid(), getegid()) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to chown luks secret file")); + goto error; + } + + if (chmod(secretPath, 0400) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to chown luks secret file")); + goto error; + } + + cleanup: + VIR_FREE(str); + VIR_DISPOSE_N(secret, secretlen); + VIR_FORCE_CLOSE(fd); + + return secretPath; + + error: + unlink(secretPath); + VIR_FREE(secretPath); + goto cleanup; +} + + int virStorageBackendCreateQemuImg(virConnectPtr conn, virStoragePoolObjPtr pool, @@ -1251,6 +1480,7 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, char *create_tool; int imgformat; virCommandPtr cmd; + char *secretPath = NULL; virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); @@ -1266,8 +1496,21 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, if (imgformat < 0) goto cleanup; + if (vol->target.format == VIR_STORAGE_FILE_LUKS) { + if (imgformat < QEMU_IMG_FORMAT_LUKS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("this version of '%s' does not support luks"), + create_tool); + goto cleanup; + } + if (!(secretPath = + virStorageBackendCreateQemuImgSecretPath(conn, pool, vol))) + goto cleanup; + } + cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, pool, vol, inputvol, - flags, create_tool, imgformat); + flags, create_tool, + imgformat, secretPath); if (!cmd) goto cleanup; @@ -1275,6 +1518,10 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, virCommandFree(cmd); cleanup: + if (secretPath) { + unlink(secretPath); + VIR_FREE(secretPath); + } VIR_FREE(create_tool); return ret; } diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 5bc622c..28e1a65 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -241,7 +241,8 @@ virStorageBackendCreateQemuImgCmdFromVol(virConnectPtr conn, virStorageVolDefPtr inputvol, unsigned int flags, const char *create_tool, - int imgformat); + int imgformat, + const char *secretPath); /* ------- virStorageFile backends ------------ */ typedef struct _virStorageFileBackend virStorageFileBackend; diff --git a/src/util/virqemu.c b/src/util/virqemu.c index 895168e..a56fb3f 100644 --- a/src/util/virqemu.c +++ b/src/util/virqemu.c @@ -140,3 +140,26 @@ virQEMUBuildObjectCommandlineFromJSON(const char *type, virBufferFreeAndReset(&buf); return ret; } + + +void +virQEMUBuildLuksOpts(virBufferPtr buf, + virStorageEncryptionPtr enc, + const char *alias) +{ + virBufferAsprintf(buf, "key-secret=%s,", alias); + + /* If there's any cipher, then add that to the command line */ + if (enc->cipher.name) { + virBufferAsprintf(buf, "cipher-alg=%s-%u,", + enc->cipher.name, enc->cipher.size); + if (enc->cipher.mode) + virBufferAsprintf(buf, "cipher-mode=%s,", enc->cipher.mode); + if (enc->cipher.hash) + virBufferAsprintf(buf, "hash-alg=%s,", enc->cipher.hash); + if (enc->ivgen.name) + virBufferAsprintf(buf, "ivgen-alg=%s,", enc->ivgen.name); + if (enc->ivgen.hash) + virBufferAsprintf(buf, "ivgen-hash-alg=%s,", enc->ivgen.hash); + } +} diff --git a/src/util/virqemu.h b/src/util/virqemu.h index af4ec1d..dfb79b9 100644 --- a/src/util/virqemu.h +++ b/src/util/virqemu.h @@ -26,9 +26,15 @@ # include "internal.h" # include "virjson.h" +# include "virstorageencryption.h" char *virQEMUBuildObjectCommandlineFromJSON(const char *type, const char *alias, virJSONValuePtr props); +void virQEMUBuildLuksOpts(virBufferPtr buf, + virStorageEncryptionPtr enc, + const char *alias) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + #endif /* __VIR_QEMU_H_ */ diff --git a/tests/storagevolxml2argvtest.c b/tests/storagevolxml2argvtest.c index af7de0b..2fd1cbf 100644 --- a/tests/storagevolxml2argvtest.c +++ b/tests/storagevolxml2argvtest.c @@ -83,7 +83,8 @@ testCompareXMLToArgvFiles(bool shouldFail, cmd = virStorageBackendCreateQemuImgCmdFromVol(conn, &poolobj, vol, inputvol, flags, - create_tool, imgformat); + create_tool, imgformat, + NULL); if (!cmd) { if (shouldFail) { virResetLastError(); -- 2.5.5

Rather than assume/pass the protocol to the qemuDomainSecretPlainSetup and qemuDomainSecretAESSetup, determine and pass the secretUsageType which is then used in the virSecretGetSecretString call For the two callers that convert from virStorageNetProtocol, add a new helper qemuDomainSecretProtocolGetUsageType. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 105 +++++++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index cf687e2..7faddd6 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -807,7 +807,7 @@ qemuDomainHostdevPrivateDispose(void *obj) /* qemuDomainSecretPlainSetup: * @conn: Pointer to connection * @secinfo: Pointer to secret info - * @protocol: Protocol for secret + * @secretUsageType: The virSecretUsageType * @authdef: Pointer to auth data * * Taking a secinfo, fill in the plaintext information @@ -817,19 +817,14 @@ qemuDomainHostdevPrivateDispose(void *obj) static int qemuDomainSecretPlainSetup(virConnectPtr conn, qemuDomainSecretInfoPtr secinfo, - virStorageNetProtocol protocol, + virSecretUsageType secretUsageType, virStorageAuthDefPtr authdef) { - int secretType = VIR_SECRET_USAGE_TYPE_ISCSI; - secinfo->type = VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN; if (VIR_STRDUP(secinfo->s.plain.username, authdef->username) < 0) return -1; - if (protocol == VIR_STORAGE_NET_PROTOCOL_RBD) - secretType = VIR_SECRET_USAGE_TYPE_CEPH; - - return virSecretGetSecretString(conn, &authdef->secdef, secretType, + return virSecretGetSecretString(conn, &authdef->secdef, secretUsageType, &secinfo->s.plain.secret, &secinfo->s.plain.secretlen); } @@ -840,7 +835,7 @@ qemuDomainSecretPlainSetup(virConnectPtr conn, * @priv: pointer to domain private object * @secinfo: Pointer to secret info * @srcalias: Alias of the disk/hostdev used to generate the secret alias - * @protocol: Protocol for secret + * @secretUsageType: The virSecretUsageType * @authdef: Pointer to auth data * * Taking a secinfo, fill in the AES specific information using the @@ -852,7 +847,7 @@ qemuDomainSecretAESSetup(virConnectPtr conn, qemuDomainObjPrivatePtr priv, qemuDomainSecretInfoPtr secinfo, const char *srcalias, - virStorageNetProtocol protocol, + virSecretUsageType secretUsageType, virStorageAuthDefPtr authdef) { int ret = -1; @@ -862,34 +857,11 @@ qemuDomainSecretAESSetup(virConnectPtr conn, size_t secretlen = 0; uint8_t *ciphertext = NULL; size_t ciphertextlen = 0; - int secretType = VIR_SECRET_USAGE_TYPE_NONE; secinfo->type = VIR_DOMAIN_SECRET_INFO_TYPE_AES; if (VIR_STRDUP(secinfo->s.aes.username, authdef->username) < 0) return -1; - switch ((virStorageNetProtocol)protocol) { - case VIR_STORAGE_NET_PROTOCOL_RBD: - secretType = VIR_SECRET_USAGE_TYPE_CEPH; - break; - - case VIR_STORAGE_NET_PROTOCOL_NONE: - case VIR_STORAGE_NET_PROTOCOL_NBD: - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - case VIR_STORAGE_NET_PROTOCOL_HTTP: - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - case VIR_STORAGE_NET_PROTOCOL_FTP: - case VIR_STORAGE_NET_PROTOCOL_FTPS: - case VIR_STORAGE_NET_PROTOCOL_TFTP: - case VIR_STORAGE_NET_PROTOCOL_LAST: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("protocol '%s' cannot be used for encrypted secrets"), - virStorageNetProtocolTypeToString(protocol)); - return -1; - } - if (!(secinfo->s.aes.alias = qemuDomainGetSecretAESAlias(srcalias))) return -1; @@ -902,7 +874,7 @@ qemuDomainSecretAESSetup(virConnectPtr conn, goto cleanup; /* Grab the unencoded secret */ - if (virSecretGetSecretString(conn, &authdef->secdef, secretType, + if (virSecretGetSecretString(conn, &authdef->secdef, secretUsageType, &secret, &secretlen) < 0) goto cleanup; @@ -936,7 +908,7 @@ qemuDomainSecretAESSetup(virConnectPtr conn, * @priv: pointer to domain private object * @secinfo: Pointer to secret info * @srcalias: Alias of the disk/hostdev used to generate the secret alias - * @protocol: Protocol for secret + * @secretUsageType: The virSecretUsageType * @authdef: Pointer to auth data * * If we have the encryption API present and can support a secret object, then @@ -951,17 +923,18 @@ qemuDomainSecretSetup(virConnectPtr conn, qemuDomainObjPrivatePtr priv, qemuDomainSecretInfoPtr secinfo, const char *srcalias, - virStorageNetProtocol protocol, + virSecretUsageType secretUsageType, virStorageAuthDefPtr authdef) { if (virCryptoHaveCipher(VIR_CRYPTO_CIPHER_AES256CBC) && virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET) && - protocol == VIR_STORAGE_NET_PROTOCOL_RBD) { - if (qemuDomainSecretAESSetup(conn, priv, secinfo, - srcalias, protocol, authdef) < 0) + secretUsageType == VIR_SECRET_USAGE_TYPE_CEPH) { + if (qemuDomainSecretAESSetup(conn, priv, secinfo, srcalias, + secretUsageType, authdef) < 0) return -1; } else { - if (qemuDomainSecretPlainSetup(conn, secinfo, protocol, authdef) < 0) + if (qemuDomainSecretPlainSetup(conn, secinfo, secretUsageType, + authdef) < 0) return -1; } return 0; @@ -985,6 +958,43 @@ qemuDomainSecretDiskDestroy(virDomainDiskDefPtr disk) } +/* qemuDomainSecretGetProtocolUsageType: + * @protocol: The virStorageNetProtocol protocol type + * + * Convert the protocl into the expected virSecretUsageType for + * eventual usage to fetch the secret + * + * Returns matched protocol type or VIR_SECRET_USAGE_TYPE_NONE with an + * error message set on failure. + */ +static virSecretUsageType +qemuDomainSecretProtocolGetUsageType(virStorageNetProtocol protocol) +{ + switch ((virStorageNetProtocol)protocol) { + case VIR_STORAGE_NET_PROTOCOL_RBD: + return VIR_SECRET_USAGE_TYPE_CEPH; + + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + return VIR_SECRET_USAGE_TYPE_ISCSI; + + case VIR_STORAGE_NET_PROTOCOL_NONE: + case VIR_STORAGE_NET_PROTOCOL_NBD: + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + case VIR_STORAGE_NET_PROTOCOL_HTTP: + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + case VIR_STORAGE_NET_PROTOCOL_FTP: + case VIR_STORAGE_NET_PROTOCOL_FTPS: + case VIR_STORAGE_NET_PROTOCOL_TFTP: + case VIR_STORAGE_NET_PROTOCOL_LAST: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("protocol '%s' cannot be used for encrypted secrets"), + virStorageNetProtocolTypeToString(protocol)); + } + return VIR_SECRET_USAGE_TYPE_NONE; +} + + /* qemuDomainSecretDiskPrepare: * @conn: Pointer to connection * @priv: pointer to domain private object @@ -1008,13 +1018,19 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI || src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { + virSecretUsageType secretUsageType; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); if (VIR_ALLOC(secinfo) < 0) return -1; + if ((secretUsageType = + qemuDomainSecretProtocolGetUsageType(src->protocol)) == + VIR_SECRET_USAGE_TYPE_NONE) + goto error; + if (qemuDomainSecretSetup(conn, priv, secinfo, disk->info.alias, - src->protocol, src->auth) < 0) + secretUsageType, src->auth) < 0) goto error; diskPriv->secinfo = secinfo; @@ -1072,14 +1088,19 @@ qemuDomainSecretHostdevPrepare(virConnectPtr conn, if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI && iscsisrc->auth) { + virSecretUsageType secretUsageType; qemuDomainHostdevPrivatePtr hostdevPriv = QEMU_DOMAIN_HOSTDEV_PRIVATE(hostdev); if (VIR_ALLOC(secinfo) < 0) return -1; + if ((secretUsageType = + qemuDomainSecretProtocolGetUsageType(VIR_STORAGE_NET_PROTOCOL_ISCSI)) == VIR_SECRET_USAGE_TYPE_NONE) + goto error; + if (qemuDomainSecretSetup(conn, priv, secinfo, hostdev->info->alias, - VIR_STORAGE_NET_PROTOCOL_ISCSI, + secretUsageType, iscsisrc->auth) < 0) goto error; -- 2.5.5

Rather than pass authdef, pass the 'authdef->username' and the '&authdef->secdef' Note that a username may be NULL. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7faddd6..9b194bc 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -808,7 +808,8 @@ qemuDomainHostdevPrivateDispose(void *obj) * @conn: Pointer to connection * @secinfo: Pointer to secret info * @secretUsageType: The virSecretUsageType - * @authdef: Pointer to auth data + * @username: username to use for authentication (may be NULL) + * @secdef: Pointer to secdef data * * Taking a secinfo, fill in the plaintext information * @@ -818,13 +819,14 @@ static int qemuDomainSecretPlainSetup(virConnectPtr conn, qemuDomainSecretInfoPtr secinfo, virSecretUsageType secretUsageType, - virStorageAuthDefPtr authdef) + const char *username, + virSecretLookupTypeDefPtr secdef) { secinfo->type = VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN; - if (VIR_STRDUP(secinfo->s.plain.username, authdef->username) < 0) + if (VIR_STRDUP(secinfo->s.plain.username, username) < 0) return -1; - return virSecretGetSecretString(conn, &authdef->secdef, secretUsageType, + return virSecretGetSecretString(conn, secdef, secretUsageType, &secinfo->s.plain.secret, &secinfo->s.plain.secretlen); } @@ -836,7 +838,8 @@ qemuDomainSecretPlainSetup(virConnectPtr conn, * @secinfo: Pointer to secret info * @srcalias: Alias of the disk/hostdev used to generate the secret alias * @secretUsageType: The virSecretUsageType - * @authdef: Pointer to auth data + * @username: username to use for authentication (may be NULL) + * @secdef: Pointer to secdef data * * Taking a secinfo, fill in the AES specific information using the * @@ -848,7 +851,8 @@ qemuDomainSecretAESSetup(virConnectPtr conn, qemuDomainSecretInfoPtr secinfo, const char *srcalias, virSecretUsageType secretUsageType, - virStorageAuthDefPtr authdef) + const char *username, + virSecretLookupTypeDefPtr secdef) { int ret = -1; uint8_t *raw_iv = NULL; @@ -859,7 +863,7 @@ qemuDomainSecretAESSetup(virConnectPtr conn, size_t ciphertextlen = 0; secinfo->type = VIR_DOMAIN_SECRET_INFO_TYPE_AES; - if (VIR_STRDUP(secinfo->s.aes.username, authdef->username) < 0) + if (VIR_STRDUP(secinfo->s.aes.username, username) < 0) return -1; if (!(secinfo->s.aes.alias = qemuDomainGetSecretAESAlias(srcalias))) @@ -874,7 +878,7 @@ qemuDomainSecretAESSetup(virConnectPtr conn, goto cleanup; /* Grab the unencoded secret */ - if (virSecretGetSecretString(conn, &authdef->secdef, secretUsageType, + if (virSecretGetSecretString(conn, secdef, secretUsageType, &secret, &secretlen) < 0) goto cleanup; @@ -909,7 +913,8 @@ qemuDomainSecretAESSetup(virConnectPtr conn, * @secinfo: Pointer to secret info * @srcalias: Alias of the disk/hostdev used to generate the secret alias * @secretUsageType: The virSecretUsageType - * @authdef: Pointer to auth data + * @username: username to use for authentication (may be NULL) + * @secdef: Pointer to secdef data * * If we have the encryption API present and can support a secret object, then * build the AES secret; otherwise, build the Plain secret. This is the magic @@ -924,17 +929,18 @@ qemuDomainSecretSetup(virConnectPtr conn, qemuDomainSecretInfoPtr secinfo, const char *srcalias, virSecretUsageType secretUsageType, - virStorageAuthDefPtr authdef) + const char *username, + virSecretLookupTypeDefPtr secdef) { if (virCryptoHaveCipher(VIR_CRYPTO_CIPHER_AES256CBC) && virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET) && secretUsageType == VIR_SECRET_USAGE_TYPE_CEPH) { if (qemuDomainSecretAESSetup(conn, priv, secinfo, srcalias, - secretUsageType, authdef) < 0) + secretUsageType, username, secdef) < 0) return -1; } else { if (qemuDomainSecretPlainSetup(conn, secinfo, secretUsageType, - authdef) < 0) + username, secdef) < 0) return -1; } return 0; @@ -1030,7 +1036,8 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, goto error; if (qemuDomainSecretSetup(conn, priv, secinfo, disk->info.alias, - secretUsageType, src->auth) < 0) + secretUsageType, src->auth->username, + &src->auth->secdef) < 0) goto error; diskPriv->secinfo = secinfo; @@ -1100,8 +1107,8 @@ qemuDomainSecretHostdevPrepare(virConnectPtr conn, goto error; if (qemuDomainSecretSetup(conn, priv, secinfo, hostdev->info->alias, - secretUsageType, - iscsisrc->auth) < 0) + secretUsageType, iscsisrc->auth->username, + &iscsisrc->auth->secdef) < 0) goto error; hostdevPriv->secinfo = secinfo; -- 2.5.5

Generate the luks command line using the AES secret key to encrypt the luks secret. Add tests for sample output Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 8 +++-- src/qemu/qemu_domain.c | 42 ++++++++++++++-------- .../qemuxml2argv-luks-disk-cipher.args | 36 +++++++++++++++++++ .../qemuxml2argvdata/qemuxml2argv-luks-disks.args | 36 +++++++++++++++++++ tests/qemuxml2argvtest.c | 11 +++++- 5 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index b5d84e6..2181638 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1238,8 +1238,12 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, virBufferAddLit(&opt, ","); if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { - virBufferAsprintf(&opt, "password-secret=%s,", - secinfo->s.aes.alias); + if (disk->src->format == VIR_STORAGE_FILE_LUKS) + virQEMUBuildLuksOpts(&opt, disk->src->encryption, + secinfo->s.aes.alias); + else + virBufferAsprintf(&opt, "password-secret=%s,", + secinfo->s.aes.alias); } if (disk->src->format > 0 && diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9b194bc..db12138 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -934,7 +934,8 @@ qemuDomainSecretSetup(virConnectPtr conn, { if (virCryptoHaveCipher(VIR_CRYPTO_CIPHER_AES256CBC) && virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_OBJECT_SECRET) && - secretUsageType == VIR_SECRET_USAGE_TYPE_CEPH) { + (secretUsageType == VIR_SECRET_USAGE_TYPE_CEPH || + secretUsageType == VIR_SECRET_USAGE_TYPE_LUKS)) { if (qemuDomainSecretAESSetup(conn, priv, secinfo, srcalias, secretUsageType, username, secdef) < 0) return -1; @@ -1017,27 +1018,38 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, { virStorageSourcePtr src = disk->src; qemuDomainSecretInfoPtr secinfo = NULL; + virSecretUsageType secretUsageType; + const char *username = NULL; + virSecretLookupTypeDefPtr secdef; - if (conn && !virStorageSourceIsEmpty(src) && - virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_NETWORK && - src->auth && - (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI || - src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { - - virSecretUsageType secretUsageType; + if (conn && !virStorageSourceIsEmpty(src)) { qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - if (VIR_ALLOC(secinfo) < 0) - return -1; + if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_NETWORK && + src->auth && + (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI || + src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { + + secretUsageType = + qemuDomainSecretProtocolGetUsageType(src->protocol); + username = src->auth->username; + secdef = &src->auth->secdef; + } else if (src->encryption && + src->format == VIR_STORAGE_FILE_LUKS) { + secretUsageType = VIR_SECRET_USAGE_TYPE_LUKS; + secdef = &src->encryption->secrets[0]->secdef; + } else { + return 0; /* No secret to attach */ + } - if ((secretUsageType = - qemuDomainSecretProtocolGetUsageType(src->protocol)) == - VIR_SECRET_USAGE_TYPE_NONE) + if (secretUsageType == VIR_SECRET_USAGE_TYPE_NONE) goto error; + if (VIR_ALLOC(secinfo) < 0) + return -1; + if (qemuDomainSecretSetup(conn, priv, secinfo, disk->info.alias, - secretUsageType, src->auth->username, - &src->auth->secdef) < 0) + secretUsageType, username, secdef) < 0) goto error; diskPriv->secinfo = secinfo; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args b/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args new file mode 100644 index 0000000..6eebc87 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args @@ -0,0 +1,36 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name encryptdisk \ +-S \ +-object secret,id=masterKey0,format=raw,\ +file=/tmp/lib/domain--1-encryptdisk/master-key.aes \ +-M pc-i440fx-2.1 \ +-m 1024 \ +-smp 1 \ +-uuid 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-encryptdisk/monitor.sock,server,nowait \ +-no-acpi \ +-boot c \ +-usb \ +-object secret,id=virtio-disk0-secret0,\ +data=9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1,\ +keyid=masterKey0,iv=AAECAwQFBgcICQoLDA0ODw==,format=base64 \ +-drive file=/storage/guest_disks/encryptdisk,key-secret=virtio-disk0-secret0,\ +format=luks,if=none,id=drive-virtio-disk0 \ +-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\ +id=virtio-disk0 \ +-object secret,id=virtio-disk1-secret0,\ +data=9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1,\ +keyid=masterKey0,iv=AAECAwQFBgcICQoLDA0ODw==,format=base64 \ +-drive file=/storage/guest_disks/encryptdisk2,key-secret=virtio-disk1-secret0,\ +format=luks,if=none,id=drive-virtio-disk1 \ +-device virtio-blk-pci,bus=pci.0,addr=0x5,drive=drive-virtio-disk1,\ +id=virtio-disk1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args b/tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args new file mode 100644 index 0000000..6eebc87 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args @@ -0,0 +1,36 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name encryptdisk \ +-S \ +-object secret,id=masterKey0,format=raw,\ +file=/tmp/lib/domain--1-encryptdisk/master-key.aes \ +-M pc-i440fx-2.1 \ +-m 1024 \ +-smp 1 \ +-uuid 496898a6-e6ff-f7c8-5dc2-3cf410945ee9 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-encryptdisk/monitor.sock,server,nowait \ +-no-acpi \ +-boot c \ +-usb \ +-object secret,id=virtio-disk0-secret0,\ +data=9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1,\ +keyid=masterKey0,iv=AAECAwQFBgcICQoLDA0ODw==,format=base64 \ +-drive file=/storage/guest_disks/encryptdisk,key-secret=virtio-disk0-secret0,\ +format=luks,if=none,id=drive-virtio-disk0 \ +-device virtio-blk-pci,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,\ +id=virtio-disk0 \ +-object secret,id=virtio-disk1-secret0,\ +data=9eao5F8qtkGt+seB1HYivWIxbtwUu6MQtg1zpj/oDtUsPr1q8wBYM91uEHCn6j/1,\ +keyid=masterKey0,iv=AAECAwQFBgcICQoLDA0ODw==,format=base64 \ +-drive file=/storage/guest_disks/encryptdisk2,key-secret=virtio-disk1-secret0,\ +format=luks,if=none,id=drive-virtio-disk1 \ +-device virtio-blk-pci,bus=pci.0,addr=0x5,drive=drive-virtio-disk1,\ +id=virtio-disk1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 0fca2fb..f5f324e 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -62,10 +62,17 @@ fakeSecretLookupByUsage(virConnectPtr conn, return virGetSecret(conn, uuid, usageType, usageID); } +static virSecretPtr +fakeSecretLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + return virGetSecret(conn, uuid, 0, ""); +} + static virSecretDriver fakeSecretDriver = { .connectNumOfSecrets = NULL, .connectListSecrets = NULL, - .secretLookupByUUID = NULL, + .secretLookupByUUID = fakeSecretLookupByUUID, .secretLookupByUsage = fakeSecretLookupByUsage, .secretDefineXML = NULL, .secretGetXMLDesc = NULL, @@ -1324,6 +1331,8 @@ mymain(void) DO_TEST("encrypted-disk", NONE); DO_TEST("encrypted-disk-usage", NONE); + DO_TEST("luks-disks", QEMU_CAPS_OBJECT_SECRET); + DO_TEST("luks-disk-cipher", QEMU_CAPS_OBJECT_SECRET); DO_TEST("memtune", NONE); DO_TEST("memtune-unlimited", NONE); -- 2.5.5

On Tue, Jun 07, 2016 at 10:45:29AM -0400, John Ferlan wrote:
Patches 1-3 were posted separately:
http://www.redhat.com/archives/libvir-list/2016-June/msg00256.html
But perhaps seeing the final direction will make things more clear as to why a "real" flag system wasn't used and keeping the current paradigm of constant value returns still works just fine.
Patches 4-5 were posted separately:
http://www.redhat.com/archives/libvir-list/2016-June/msg00091.html (4) http://www.redhat.com/archives/libvir-list/2016-June/msg00094.html (5)
Although at one point patch 4 had an ACK:
http://www.redhat.com/archives/libvir-list/2016-May/msg02115.html
It wasn't clear if the more recent review rescinded that, so it still remains "in the list". I understand the concern about adding secret to cfg.mk checking, but without a better idea of how to handle - I left things as they were.
Patches 6-16 are all new. Some parts are separable, but rather than continue piecemeal I just figured going with an RFC will at least
Patch 6 is only there to "prove" that using the current encryption paradigm XML still works, although if I've read the tea leaves correctly, the qemu support isn't working as desired/expected.
Patch 7 adds "usage" as an XML attribute for encryption and the associated tests with that. I've chosen to "reuse" the <encryption> XML element rather than inventing something new. I'm not opposed to something new, but let's decide up a name quickly...
Patch 8-9 adds the ability for the storage backend to create/recognize a luks volume
Patches 10-13 adds support for luks encryption in the storage backend. The new "<secret>" format uses "luks" as the usage type and "<key>" as the 'name'. If those names cause angst, I'm fine with changing, but just give a better suggestion! Adding <cipher> and <ivgen> were a result of using qemu constructs from qemu commit id '3e308f20'. Since we are parsing something new, I figure failing in the domain parse code for this new type was acceptible as opposed to some post processing check.
Patches 14-16 adds support for luks encryption to the domain using <encryption type='luks'... <secret format='key' usage/uuid='xxx'>>
I've tested using a "good" and "bad" password and got the expected results for starting a domain. I did not add 'virsh vol-create-as' support just yet. I figured that would be less to go back and redo if the names of elements changes. I've also run the changes through Coverity with no new issues detected.
The whole series is a result of the following bz:
BTW, one of the core reasons for the LUKS support in QEMU is to facilitate use of LUKS with non-local-file based disks. eg, LUKS over RBD, or LUKS over NBD or LUKS over iSCSI, etc. The diffstat for the files shows you've wired up luks-over-plain-file ok, but I'm wondering if the code posted here is also dealing with the broader support, or if that's something to be addressed later. No big deal either way, particularly since qemu-img can't actually create LUKS volumes on anything other than plain files/block devs yet. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/07/2016 12:01 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2016 at 10:45:29AM -0400, John Ferlan wrote:
Patches 1-3 were posted separately:
http://www.redhat.com/archives/libvir-list/2016-June/msg00256.html
But perhaps seeing the final direction will make things more clear as to why a "real" flag system wasn't used and keeping the current paradigm of constant value returns still works just fine.
Patches 4-5 were posted separately:
http://www.redhat.com/archives/libvir-list/2016-June/msg00091.html (4) http://www.redhat.com/archives/libvir-list/2016-June/msg00094.html (5)
Although at one point patch 4 had an ACK:
http://www.redhat.com/archives/libvir-list/2016-May/msg02115.html
It wasn't clear if the more recent review rescinded that, so it still remains "in the list". I understand the concern about adding secret to cfg.mk checking, but without a better idea of how to handle - I left things as they were.
Patches 6-16 are all new. Some parts are separable, but rather than continue piecemeal I just figured going with an RFC will at least
Patch 6 is only there to "prove" that using the current encryption paradigm XML still works, although if I've read the tea leaves correctly, the qemu support isn't working as desired/expected.
Patch 7 adds "usage" as an XML attribute for encryption and the associated tests with that. I've chosen to "reuse" the <encryption> XML element rather than inventing something new. I'm not opposed to something new, but let's decide up a name quickly...
Patch 8-9 adds the ability for the storage backend to create/recognize a luks volume
Patches 10-13 adds support for luks encryption in the storage backend. The new "<secret>" format uses "luks" as the usage type and "<key>" as the 'name'. If those names cause angst, I'm fine with changing, but just give a better suggestion! Adding <cipher> and <ivgen> were a result of using qemu constructs from qemu commit id '3e308f20'. Since we are parsing something new, I figure failing in the domain parse code for this new type was acceptible as opposed to some post processing check.
Patches 14-16 adds support for luks encryption to the domain using <encryption type='luks'... <secret format='key' usage/uuid='xxx'>>
I've tested using a "good" and "bad" password and got the expected results for starting a domain. I did not add 'virsh vol-create-as' support just yet. I figured that would be less to go back and redo if the names of elements changes. I've also run the changes through Coverity with no new issues detected.
The whole series is a result of the following bz:
BTW, one of the core reasons for the LUKS support in QEMU is to facilitate use of LUKS with non-local-file based disks. eg, LUKS over RBD, or LUKS over NBD or LUKS over iSCSI, etc.
The diffstat for the files shows you've wired up luks-over-plain-file ok, but I'm wondering if the code posted here is also dealing with the broader support, or if that's something to be addressed later.
No big deal either way, particularly since qemu-img can't actually create LUKS volumes on anything other than plain files/block devs yet.
Hmm, right, sigh, but I had to start somewhere. So rather than be either/or in qemuDomainSecretDiskPrepare and qemuBuildDriveStr, there would need to be a way to have multiple 'secinfo' structs per disk or maybe one for <auth> and one for <encrypt>. Can we predict the future and go with a list or just two secinfo's... Any preference? I also didn't make any hotplug changes. John

On Tue, Jun 07, 2016 at 02:34:39PM -0400, John Ferlan wrote:
On 06/07/2016 12:01 PM, Daniel P. Berrange wrote:
On Tue, Jun 07, 2016 at 10:45:29AM -0400, John Ferlan wrote:
Patches 1-3 were posted separately:
http://www.redhat.com/archives/libvir-list/2016-June/msg00256.html
But perhaps seeing the final direction will make things more clear as to why a "real" flag system wasn't used and keeping the current paradigm of constant value returns still works just fine.
Patches 4-5 were posted separately:
http://www.redhat.com/archives/libvir-list/2016-June/msg00091.html (4) http://www.redhat.com/archives/libvir-list/2016-June/msg00094.html (5)
Although at one point patch 4 had an ACK:
http://www.redhat.com/archives/libvir-list/2016-May/msg02115.html
It wasn't clear if the more recent review rescinded that, so it still remains "in the list". I understand the concern about adding secret to cfg.mk checking, but without a better idea of how to handle - I left things as they were.
Patches 6-16 are all new. Some parts are separable, but rather than continue piecemeal I just figured going with an RFC will at least
Patch 6 is only there to "prove" that using the current encryption paradigm XML still works, although if I've read the tea leaves correctly, the qemu support isn't working as desired/expected.
Patch 7 adds "usage" as an XML attribute for encryption and the associated tests with that. I've chosen to "reuse" the <encryption> XML element rather than inventing something new. I'm not opposed to something new, but let's decide up a name quickly...
Patch 8-9 adds the ability for the storage backend to create/recognize a luks volume
Patches 10-13 adds support for luks encryption in the storage backend. The new "<secret>" format uses "luks" as the usage type and "<key>" as the 'name'. If those names cause angst, I'm fine with changing, but just give a better suggestion! Adding <cipher> and <ivgen> were a result of using qemu constructs from qemu commit id '3e308f20'. Since we are parsing something new, I figure failing in the domain parse code for this new type was acceptible as opposed to some post processing check.
Patches 14-16 adds support for luks encryption to the domain using <encryption type='luks'... <secret format='key' usage/uuid='xxx'>>
I've tested using a "good" and "bad" password and got the expected results for starting a domain. I did not add 'virsh vol-create-as' support just yet. I figured that would be less to go back and redo if the names of elements changes. I've also run the changes through Coverity with no new issues detected.
The whole series is a result of the following bz:
BTW, one of the core reasons for the LUKS support in QEMU is to facilitate use of LUKS with non-local-file based disks. eg, LUKS over RBD, or LUKS over NBD or LUKS over iSCSI, etc.
The diffstat for the files shows you've wired up luks-over-plain-file ok, but I'm wondering if the code posted here is also dealing with the broader support, or if that's something to be addressed later.
No big deal either way, particularly since qemu-img can't actually create LUKS volumes on anything other than plain files/block devs yet.
Hmm, right, sigh, but I had to start somewhere. So rather than be either/or in qemuDomainSecretDiskPrepare and qemuBuildDriveStr, there would need to be a way to have multiple 'secinfo' structs per disk or maybe one for <auth> and one for <encrypt>. Can we predict the future and go with a list or just two secinfo's... Any preference?
I'd suggest just two secinfos. Then again if we have a chain of backing files that are encrpted or need authentication - of course I don't think our XML lets us deal with that yet anyway. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
John Ferlan