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

See RFC: http://www.redhat.com/archives/libvir-list/2016-June/msg00312.html For: https://bugzilla.redhat.com/show_bug.cgi?id=1301021 Changes since RFC: 1. Address Dan's comment regarding providing secinfo objects for both secret for RBD as well as secret for LUKS 2. Remove code from secret/secret_util.{h,c} and need for including "secret/secret_util.h" (as well as cfg.mk change) 3. Change secret usage name from "luks" to "key". The "key" secret type will be reused for work I have in other local trees (eg. TLS) 4. Reorder the patches a bit. Patches 1-9 are more or less setup for patches 10-19. John Ferlan (19): 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 and rename util: Move and rename virStorageAuthDefParseSecret util: Introduce virSecretFormatSecret qemu: Change protocol parameter for secret setup qemu: Remove authdef from secret setup 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 "key" encryption: Add luks parsing for storageencryption encryption: Add <cipher> and <ivgen> to encryption storage: Add support to create a luks volume qemu: Add new secret info type qemu: Add luks support for domain disk docs/aclpolkit.html.in | 4 + docs/formatsecret.html.in | 62 ++- docs/formatstorageencryption.html.in | 115 ++++- docs/schemas/secret.rng | 10 + docs/schemas/storagecommon.rng | 58 ++- include/libvirt/libvirt-secret.h | 3 +- po/POTFILES.in | 1 + src/Makefile.am | 2 + 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 | 8 + src/libxl/libxl_conf.c | 2 +- src/qemu/qemu_command.c | 12 +- src/qemu/qemu_domain.c | 148 ++++--- src/qemu/qemu_domain.h | 5 + src/qemu/qemu_process.c | 19 +- src/secret/secret_util.c | 18 +- src/secret/secret_util.h | 10 +- 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 | 54 +-- src/storage/storage_backend_rbd.c | 49 +-- src/util/virendian.h | 24 ++ src/util/virqemu.c | 23 + src/util/virqemu.h | 6 + src/util/virsecret.c | 127 ++++++ src/util/virsecret.h | 56 +++ src/util/virstorageencryption.c | 156 ++++++- src/util/virstorageencryption.h | 18 +- src/util/virstoragefile.c | 196 ++++----- 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-key.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 + 66 files changed, 1792 insertions(+), 506 deletions(-) create mode 100644 src/util/virsecret.c create mode 100644 src/util/virsecret.h 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-key.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 8934471..ccfe9ab 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

On Mon, Jun 13, 2016 at 20:27:40 -0400, John Ferlan wrote:
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
I still think the code should be refactored sooner or later, but ACK.

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

On Mon, Jun 13, 2016 at 20:27:41 -0400, John Ferlan wrote:
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(-)
ACK

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

On Mon, Jun 13, 2016 at 20:27:42 -0400, John Ferlan wrote:
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(-)
ACK

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 | 49 +++++-------------------------------- src/storage/storage_backend_rbd.c | 48 +++--------------------------------- 3 files changed, 10 insertions(+), 88 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 1aff57d..4333c2b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1618,6 +1618,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..6cefd50 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,6 +43,7 @@ #include "virobject.h" #include "virstring.h" #include "viruuid.h" +#include "secret_util.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -277,11 +278,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 +301,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 +322,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

On Mon, Jun 13, 2016 at 20:27:43 -0400, John Ferlan wrote:
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 | 49 +++++-------------------------------- src/storage/storage_backend_rbd.c | 48 +++--------------------------------- 3 files changed, 10 insertions(+), 88 deletions(-)
ACK

Move the enum into a new src/util/virsecret.h, rename it to be virSecretLookupType. Add a src/util/virsecret.h in order to perform a couple of simple operations on the secret XML and virSecretLookupTypeDef for clearing and copying. 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> --- src/Makefile.am | 1 + src/conf/secret_conf.h | 2 +- src/libvirt_private.syms | 5 ++++ src/libxl/libxl_conf.c | 2 +- src/qemu/qemu_domain.c | 4 +-- src/secret/secret_util.c | 18 ++++++------ src/secret/secret_util.h | 10 +++---- src/storage/storage_backend_iscsi.c | 7 +++-- src/storage/storage_backend_rbd.c | 3 +- src/util/virsecret.c | 57 +++++++++++++++++++++++++++++++++++++ src/util/virsecret.h | 50 ++++++++++++++++++++++++++++++++ src/util/virstoragefile.c | 32 +++++++++------------ src/util/virstoragefile.h | 17 ++--------- tests/qemuargv2xmltest.c | 4 +-- 14 files changed, 156 insertions(+), 56 deletions(-) create mode 100644 src/util/virsecret.c create mode 100644 src/util/virsecret.h diff --git a/src/Makefile.am b/src/Makefile.am index 4333c2b..ad80cc9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -161,6 +161,7 @@ UTIL_SOURCES = \ util/virrotatingfile.h util/virrotatingfile.c \ util/virscsi.c util/virscsi.h \ util/virseclabel.c util/virseclabel.h \ + util/virsecret.c util/virsecret.h \ util/virsexpr.c util/virsexpr.h \ util/virsocketaddr.h util/virsocketaddr.c \ util/virstats.c util/virstats.h \ 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/libvirt_private.syms b/src/libvirt_private.syms index ec197a1..32d5179 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2208,6 +2208,11 @@ virSecurityLabelDefFree; virSecurityLabelDefNew; +# util/virsecret.h +virSecretLookupDefClear; +virSecretLookupDefCopy; + + # util/virsexpr.h sexpr2string; sexpr_append; diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index acb6594..062a0e4 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -656,7 +656,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 d1f8175..34e3d95 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..f7dedfc 100644 --- a/src/secret/secret_util.h +++ b/src/secret/secret_util.h @@ -19,17 +19,17 @@ * */ -#ifndef __VIR_SECRET_H__ -# define __VIR_SECRET_H__ +#ifndef __VIR_SECRET_UTIL_H__ +# define __VIR_SECRET_UTIL_H__ # include "internal.h" -# include "virstoragefile.h" +# include "virsecret.h" int virSecretGetSecretString(virConnectPtr conn, - virStorageAuthDefPtr authdef, + virSecretLookupTypeDefPtr secdef, virSecretUsageType secretUsageType, uint8_t **ret_secret, size_t *ret_secret_size) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK; -#endif /* __VIR_SECRET_H__ */ +#endif /* __VIR_SECRET_UTIL_H__ */ diff --git a/src/storage/storage_backend_iscsi.c b/src/storage/storage_backend_iscsi.c index 6cefd50..ff013c6 100644 --- a/src/storage/storage_backend_iscsi.c +++ b/src/storage/storage_backend_iscsi.c @@ -286,8 +286,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")); @@ -301,7 +301,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/virsecret.c b/src/util/virsecret.c new file mode 100644 index 0000000..e7a03b7 --- /dev/null +++ b/src/util/virsecret.c @@ -0,0 +1,57 @@ +/* + * virsecret.c: secret utility functions + * + * Copyright (C) 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include "viralloc.h" +#include "virerror.h" +#include "virlog.h" +#include "virsecret.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.secret"); + + +void +virSecretLookupDefClear(virSecretLookupTypeDefPtr secdef) +{ + if (secdef->type == VIR_SECRET_LOOKUP_TYPE_USAGE) + VIR_FREE(secdef->u.usage); + else if (secdef->type == VIR_SECRET_LOOKUP_TYPE_UUID) + memset(&secdef->u.uuid, 0, VIR_UUID_BUFLEN); +} + + +int +virSecretLookupDefCopy(virSecretLookupTypeDefPtr dst, + const virSecretLookupTypeDef *src) +{ + dst->type = src->type; + if (dst->type == VIR_SECRET_LOOKUP_TYPE_UUID) { + memcpy(dst->u.uuid, src->u.uuid, VIR_UUID_BUFLEN); + } else if (dst->type == VIR_SECRET_LOOKUP_TYPE_USAGE) { + if (VIR_STRDUP(dst->u.usage, src->u.usage) < 0) + return -1; + } + return 0; +} diff --git a/src/util/virsecret.h b/src/util/virsecret.h new file mode 100644 index 0000000..f2a0b63 --- /dev/null +++ b/src/util/virsecret.h @@ -0,0 +1,50 @@ +/* + * virsecret.h: secret utility functions + * + * Copyright (C) 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __VIR_SECRET_H__ +# define __VIR_SECRET_H__ + +# include "internal.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; + +}; + +void virSecretLookupDefClear(virSecretLookupTypeDefPtr secdef); +int virSecretLookupDefCopy(virSecretLookupTypeDefPtr dst, + const virSecretLookupTypeDef *src); + +#endif /* __VIR_SECRET_H__ */ diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index d2da9e7..7ed52ab 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1506,8 +1506,7 @@ virStorageAuthDefFree(virStorageAuthDefPtr authdef) VIR_FREE(authdef->username); VIR_FREE(authdef->secrettype); - if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_USAGE) - VIR_FREE(authdef->secret.usage); + virSecretLookupDefClear(&authdef->secdef); VIR_FREE(authdef); } @@ -1526,13 +1525,10 @@ 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) - goto error; - } + + if (virSecretLookupDefCopy(&ret->secdef, &src->secdef) < 0) + goto error; + return ret; error: @@ -1573,16 +1569,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 +1621,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 +1676,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..9424fed 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 "virsecret.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 c5fe776..2e79533 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

On Mon, Jun 13, 2016 at 20:27:44 -0400, John Ferlan wrote:
Move the enum into a new src/util/virsecret.h, rename it to be virSecretLookupType. Add a src/util/virsecret.h in order to perform a couple of simple operations on the secret XML and virSecretLookupTypeDef for clearing and copying.
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> --- src/Makefile.am | 1 + src/conf/secret_conf.h | 2 +- src/libvirt_private.syms | 5 ++++ src/libxl/libxl_conf.c | 2 +- src/qemu/qemu_domain.c | 4 +-- src/secret/secret_util.c | 18 ++++++------ src/secret/secret_util.h | 10 +++---- src/storage/storage_backend_iscsi.c | 7 +++-- src/storage/storage_backend_rbd.c | 3 +- src/util/virsecret.c | 57 +++++++++++++++++++++++++++++++++++++ src/util/virsecret.h | 50 ++++++++++++++++++++++++++++++++ src/util/virstoragefile.c | 32 +++++++++------------ src/util/virstoragefile.h | 17 ++--------- tests/qemuargv2xmltest.c | 4 +-- 14 files changed, 156 insertions(+), 56 deletions(-) create mode 100644 src/util/virsecret.c create mode 100644 src/util/virsecret.h
ACK

Move to virsecret.c and rename to virSecretParseSecret. Also convert to usage xmlNodePtr and virXMLPropString rather than virXPathString. Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/libvirt_private.syms | 1 + src/util/virsecret.c | 44 +++++++++++++++++++++++++++++ src/util/virsecret.h | 5 +++- src/util/virstoragefile.c | 71 ++++++++++++----------------------------------- 5 files changed, 67 insertions(+), 55 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 822cfbc..67838f5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -233,6 +233,7 @@ src/util/virqemu.c src/util/virrandom.c src/util/virrotatingfile.c src/util/virscsi.c +src/util/virsecret.c src/util/virsexpr.c src/util/virsocketaddr.c src/util/virstats.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 32d5179..ca65885 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2211,6 +2211,7 @@ virSecurityLabelDefNew; # util/virsecret.h virSecretLookupDefClear; virSecretLookupDefCopy; +virSecretParseSecret; # util/virsexpr.h diff --git a/src/util/virsecret.c b/src/util/virsecret.c index e7a03b7..da5f7af 100644 --- a/src/util/virsecret.c +++ b/src/util/virsecret.c @@ -26,6 +26,7 @@ #include "virlog.h" #include "virsecret.h" #include "virstring.h" +#include "viruuid.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -55,3 +56,46 @@ virSecretLookupDefCopy(virSecretLookupTypeDefPtr dst, } return 0; } + + +int +virSecretParseSecret(xmlNodePtr secretnode, + virSecretLookupTypeDefPtr secdef) +{ + char *uuid; + char *usage; + int ret = -1; + + uuid = virXMLPropString(secretnode, "uuid"); + usage = virXMLPropString(secretnode, "usage"); + if (uuid == NULL && usage == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing secret uuid or usage attribute")); + goto cleanup; + } + + if (uuid && usage) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("either secret uuid or usage expected")); + goto cleanup; + } + + if (uuid) { + if (virUUIDParse(uuid, secdef->u.uuid) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid secret uuid '%s'"), uuid); + goto cleanup; + } + secdef->type = VIR_SECRET_LOOKUP_TYPE_UUID; + } else { + secdef->u.usage = usage; + usage = NULL; + secdef->type = VIR_SECRET_LOOKUP_TYPE_USAGE; + } + ret = 0; + + cleanup: + VIR_FREE(uuid); + VIR_FREE(usage); + return ret; +} diff --git a/src/util/virsecret.h b/src/util/virsecret.h index f2a0b63..d97c17d 100644 --- a/src/util/virsecret.h +++ b/src/util/virsecret.h @@ -24,6 +24,8 @@ # include "internal.h" +# include "virxml.h" + typedef enum { VIR_SECRET_LOOKUP_TYPE_NONE, VIR_SECRET_LOOKUP_TYPE_UUID, @@ -46,5 +48,6 @@ struct _virSecretLookupTypeDef { void virSecretLookupDefClear(virSecretLookupTypeDefPtr secdef); int virSecretLookupDefCopy(virSecretLookupTypeDefPtr dst, const virSecretLookupTypeDef *src); - +int virSecretParseSecret(xmlNodePtr secretnode, + virSecretLookupTypeDefPtr secdef); #endif /* __VIR_SECRET_H__ */ diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 7ed52ab..3a2fd75 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1537,62 +1537,11 @@ virStorageAuthDefCopy(const virStorageAuthDef *src) } -static int -virStorageAuthDefParseSecret(xmlXPathContextPtr ctxt, - virStorageAuthDefPtr authdef) -{ - char *uuid; - char *usage; - int ret = -1; - - /* Used by the domain disk xml parsing in order to ensure the - * <secret type='%s' value matches the expected secret type for - * the style of disk (iscsi is chap, nbd is ceph). For some reason - * the virSecretUsageType{From|To}String() cannot be linked here - * and because only the domain parsing code cares - just keep - * it as a string. - */ - authdef->secrettype = virXPathString("string(./secret/@type)", ctxt); - - uuid = virXPathString("string(./secret/@uuid)", ctxt); - usage = virXPathString("string(./secret/@usage)", ctxt); - if (uuid == NULL && usage == NULL) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing auth secret uuid or usage attribute")); - goto cleanup; - } - - if (uuid && usage) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("either auth secret uuid or usage expected")); - goto cleanup; - } - - if (uuid) { - if (virUUIDParse(uuid, authdef->secdef.u.uuid) < 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("invalid auth secret uuid")); - goto cleanup; - } - authdef->secdef.type = VIR_SECRET_LOOKUP_TYPE_UUID; - } else { - authdef->secdef.u.usage = usage; - usage = NULL; - authdef->secdef.type = VIR_SECRET_LOOKUP_TYPE_USAGE; - } - ret = 0; - - cleanup: - VIR_FREE(uuid); - VIR_FREE(usage); - return ret; -} - - static virStorageAuthDefPtr virStorageAuthDefParseXML(xmlXPathContextPtr ctxt) { virStorageAuthDefPtr authdef = NULL; + xmlNodePtr secretnode = NULL; char *username = NULL; char *authtype = NULL; @@ -1621,8 +1570,22 @@ virStorageAuthDefParseXML(xmlXPathContextPtr ctxt) VIR_FREE(authtype); } - authdef->secdef.type = VIR_SECRET_LOOKUP_TYPE_NONE; - if (virStorageAuthDefParseSecret(ctxt, authdef) < 0) + if (!(secretnode = virXPathNode("./secret ", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing <secret> element in auth")); + goto error; + } + + /* Used by the domain disk xml parsing in order to ensure the + * <secret type='%s' value matches the expected secret type for + * the style of disk (iscsi is chap, nbd is ceph). For some reason + * the virSecretUsageType{From|To}String() cannot be linked here + * and because only the domain parsing code cares - just keep + * it as a string. + */ + authdef->secrettype = virXMLPropString(secretnode, "type"); + + if (virSecretParseSecret(secretnode, &authdef->secdef) < 0) goto error; return authdef; -- 2.5.5

On Mon, Jun 13, 2016 at 20:27:45 -0400, John Ferlan wrote:
Move to virsecret.c and rename to virSecretParseSecret. Also convert to
NACK to the naming. It implies that it parses <secret> xmls.
usage xmlNodePtr and virXMLPropString rather than virXPathString.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/libvirt_private.syms | 1 + src/util/virsecret.c | 44 +++++++++++++++++++++++++++++ src/util/virsecret.h | 5 +++- src/util/virstoragefile.c | 71 ++++++++++++----------------------------------- 5 files changed, 67 insertions(+), 55 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 32d5179..ca65885 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2211,6 +2211,7 @@ virSecurityLabelDefNew; # util/virsecret.h virSecretLookupDefClear; virSecretLookupDefCopy; +virSecretParseSecret;
It should be named similarly to the above or to the original name. ACK to the code as long as you name it reasonably

On 06/21/2016 10:05 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:45 -0400, John Ferlan wrote:
Move to virsecret.c and rename to virSecretParseSecret. Also convert to
NACK to the naming. It implies that it parses <secret> xmls.
The "Secret" just replaced the "StorageAuthDef" since no longer was a <secret> going to be only an element of <auth>.
usage xmlNodePtr and virXMLPropString rather than virXPathString.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- po/POTFILES.in | 1 + src/libvirt_private.syms | 1 + src/util/virsecret.c | 44 +++++++++++++++++++++++++++++ src/util/virsecret.h | 5 +++- src/util/virstoragefile.c | 71 ++++++++++++----------------------------------- 5 files changed, 67 insertions(+), 55 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 32d5179..ca65885 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2211,6 +2211,7 @@ virSecurityLabelDefNew; # util/virsecret.h virSecretLookupDefClear; virSecretLookupDefCopy; +virSecretParseSecret;
It should be named similarly to the above or to the original name.
ACK to the code as long as you name it reasonably
OK - is virSecretLookupParseSecret more palatable? Or an even longer name virSecretLookupParseDomainSecret. Up through this point it's only a subelement of <auth>, but adding to <encryption> short term and to <serial> for TLS secrets in another series. Also, As I was working in the TLS code - I began to realize using 'secdef' for variable names became confusing, so I changed to 'seclookupdef'... That was posted as a separate series I also note that patch 5 doesn't yet have any review, but this patch relies on that. Tks - John

Add utility to format the virSecretLookupTypeDefPtr in XML Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virsecret.c | 26 ++++++++++++++++++++++++++ src/util/virsecret.h | 3 +++ src/util/virstoragefile.c | 19 +------------------ 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ca65885..fdf06ae 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2209,6 +2209,7 @@ virSecurityLabelDefNew; # util/virsecret.h +virSecretFormatSecret; virSecretLookupDefClear; virSecretLookupDefCopy; virSecretParseSecret; diff --git a/src/util/virsecret.c b/src/util/virsecret.c index da5f7af..c08ace8 100644 --- a/src/util/virsecret.c +++ b/src/util/virsecret.c @@ -99,3 +99,29 @@ virSecretParseSecret(xmlNodePtr secretnode, VIR_FREE(usage); return ret; } + + +void +virSecretFormatSecret(virBufferPtr buf, + const char *secrettype, + virSecretLookupTypeDefPtr secdef) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virBufferAdjustIndent(buf, 2); + if (secrettype) + virBufferAsprintf(buf, "<secret type='%s'", secrettype); + else + virBufferAddLit(buf, "<secret"); + + if (secdef->type == VIR_SECRET_LOOKUP_TYPE_UUID) { + virUUIDFormat(secdef->u.uuid, uuidstr); + virBufferAsprintf(buf, " uuid='%s'/>\n", uuidstr); + } else if (secdef->type == VIR_SECRET_LOOKUP_TYPE_USAGE) { + virBufferEscapeString(buf, " usage='%s'/>\n", + secdef->u.usage); + } else { + virBufferAddLit(buf, "/>\n"); + } + virBufferAdjustIndent(buf, -2); +} diff --git a/src/util/virsecret.h b/src/util/virsecret.h index d97c17d..f70ed31 100644 --- a/src/util/virsecret.h +++ b/src/util/virsecret.h @@ -50,4 +50,7 @@ int virSecretLookupDefCopy(virSecretLookupTypeDefPtr dst, const virSecretLookupTypeDef *src); int virSecretParseSecret(xmlNodePtr secretnode, virSecretLookupTypeDefPtr secdef); +void virSecretFormatSecret(virBufferPtr buf, + const char *secrettype, + virSecretLookupTypeDefPtr secdef); #endif /* __VIR_SECRET_H__ */ diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 3a2fd75..6d7e5d9 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1623,8 +1623,6 @@ int virStorageAuthDefFormat(virBufferPtr buf, virStorageAuthDefPtr authdef) { - char uuidstr[VIR_UUID_STRING_BUFLEN]; - if (authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE) { virBufferEscapeString(buf, "<auth username='%s'>\n", authdef->username); } else { @@ -1633,22 +1631,7 @@ virStorageAuthDefFormat(virBufferPtr buf, virBufferEscapeString(buf, "username='%s'>\n", authdef->username); } - virBufferAdjustIndent(buf, 2); - if (authdef->secrettype) - virBufferAsprintf(buf, "<secret type='%s'", authdef->secrettype); - else - virBufferAddLit(buf, "<secret"); - - if (authdef->secdef.type == VIR_SECRET_LOOKUP_TYPE_UUID) { - virUUIDFormat(authdef->secdef.u.uuid, uuidstr); - virBufferAsprintf(buf, " uuid='%s'/>\n", uuidstr); - } else if (authdef->secdef.type == VIR_SECRET_LOOKUP_TYPE_USAGE) { - virBufferEscapeString(buf, " usage='%s'/>\n", - authdef->secdef.u.usage); - } else { - virBufferAddLit(buf, "/>\n"); - } - virBufferAdjustIndent(buf, -2); + virSecretFormatSecret(buf, authdef->secrettype, &authdef->secdef); virBufferAddLit(buf, "</auth>\n"); return 0; -- 2.5.5

On Mon, Jun 13, 2016 at 20:27:46 -0400, John Ferlan wrote:
Add utility to format the virSecretLookupTypeDefPtr in XML
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virsecret.c | 26 ++++++++++++++++++++++++++ src/util/virsecret.h | 3 +++ src/util/virstoragefile.c | 19 +------------------ 4 files changed, 31 insertions(+), 18 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ca65885..fdf06ae 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2209,6 +2209,7 @@ virSecurityLabelDefNew;
# util/virsecret.h +virSecretFormatSecret;
ACK if you pick a more reasonable name.

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 34e3d95..52cbc72 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

On Mon, Jun 13, 2016 at 20:27:47 -0400, John Ferlan wrote:
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 34e3d95..52cbc72 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c
[...]
+/* 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));
You could change this error message so that it actually makes some sense. The protocols above don't support any form of authentication at least in context of our interaction with qemu, not only specifically encrypted secrets.
+ } + 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)
Dead code. The condition above guarantees that this doesn't ever return _NONE. I think you could set the usage type here rather than having an extra helper that doesn't do much else.
+ 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)
Same complaint.
+ goto error; +

On 06/23/2016 11:57 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:47 -0400, John Ferlan wrote:
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 34e3d95..52cbc72 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c
[...]
+/* 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));
You could change this error message so that it actually makes some sense. The protocols above don't support any form of authentication at least in context of our interaction with qemu, not only specifically encrypted secrets.
OK - poof this is gone...
+ } + 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)
Dead code. The condition above guarantees that this doesn't ever return _NONE. I think you could set the usage type here rather than having an extra helper that doesn't do much else.
Changed to: if (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI) secretUsageType = VIR_SECRET_USAGE_TYPE_ISCSI; else secretUsageType = VIR_SECRET_USAGE_TYPE_CEPH;
+ 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;
Changed to: virSecretUsageType secretUsageType = VIR_SECRET_USAGE_TYPE_ISCSI; Tks - John
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)
Same complaint.
+ goto error; +

On Thu, Jun 23, 2016 at 12:16:06 -0400, John Ferlan wrote:
On 06/23/2016 11:57 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:47 -0400, John Ferlan wrote:
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 34e3d95..52cbc72 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c
[...]
+/* 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));
You could change this error message so that it actually makes some sense. The protocols above don't support any form of authentication at least in context of our interaction with qemu, not only specifically encrypted secrets.
OK - poof this is gone...
+ } + 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)
Dead code. The condition above guarantees that this doesn't ever return _NONE. I think you could set the usage type here rather than having an extra helper that doesn't do much else.
Changed to:
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI) secretUsageType = VIR_SECRET_USAGE_TYPE_ISCSI; else secretUsageType = VIR_SECRET_USAGE_TYPE_CEPH;
+ 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;
Changed to:
virSecretUsageType secretUsageType = VIR_SECRET_USAGE_TYPE_ISCSI;
Tks -
thanks for doing that. ACK to those.

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 52cbc72..2c583d8 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

On Mon, Jun 13, 2016 at 20:27:48 -0400, John Ferlan wrote:
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(-)
ACK

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 573162f..09950fb 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1336,6 +1336,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

On Mon, Jun 13, 2016 at 20:27:49 -0400, John Ferlan wrote:
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(-)
ACK

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. This code will make use of the virSecretParseSecret common code. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatstorageencryption.html.in | 15 ++++++--- docs/schemas/storagecommon.rng | 11 +++++-- src/qemu/qemu_process.c | 13 +++----- src/storage/storage_backend.c | 3 +- src/storage/storage_backend_fs.c | 3 +- src/util/virstorageencryption.c | 28 ++++++----------- 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, 132 insertions(+), 38 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 01d607d..513664e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -70,6 +70,7 @@ #include "virnuma.h" #include "virstring.h" #include "virhostdev.h" +#include "secret_util.h" #include "storage/storage_driver.h" #include "configmake.h" #include "nwfilter_conf.h" @@ -377,7 +378,6 @@ qemuProcessGetVolumeQcowPassphrase(virConnectPtr conn, char **secretRet, size_t *secretLen) { - virSecretPtr secret; char *passphrase; unsigned char *data; size_t size; @@ -416,14 +416,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..4ac7476 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 "virsecret.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -114,6 +115,7 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, virStorageEncryptionSecretPtr ret; char *type_str = NULL; char *uuidstr = NULL; + char *usagestr = NULL; if (VIR_ALLOC(ret) < 0) return NULL; @@ -133,21 +135,12 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, type_str); goto cleanup; } - VIR_FREE(type_str); - if ((uuidstr = virXPathString("string(./@uuid)", ctxt))) { - if (virUUIDParse(uuidstr, ret->uuid) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("malformed volume encryption uuid '%s'"), - uuidstr); - goto cleanup; - } - VIR_FREE(uuidstr); - } else { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing volume encryption uuid")); + if (virSecretParseSecret(node, &ret->secdef) < 0) goto cleanup; - } + + VIR_FREE(type_str); + ctxt->node = old_node; return ret; @@ -155,6 +148,7 @@ virStorageEncryptionSecretParse(xmlXPathContextPtr ctxt, VIR_FREE(type_str); virStorageEncryptionSecretFree(ret); VIR_FREE(uuidstr); + VIR_FREE(usagestr); ctxt->node = old_node; return NULL; } @@ -244,7 +238,6 @@ virStorageEncryptionSecretFormat(virBufferPtr buf, virStorageEncryptionSecretPtr secret) { const char *type; - char uuidstr[VIR_UUID_STRING_BUFLEN]; if (!(type = virStorageEncryptionSecretTypeToString(secret->type))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -252,9 +245,8 @@ virStorageEncryptionSecretFormat(virBufferPtr buf, return -1; } - virUUIDFormat(secret->uuid, uuidstr); - virBufferAsprintf(buf, "<secret type='%s' uuid='%s'/>\n", - type, uuidstr); + virSecretFormatSecret(buf, type, &secret->secdef); + return 0; } @@ -271,14 +263,12 @@ virStorageEncryptionFormat(virBufferPtr buf, return -1; } virBufferAsprintf(buf, "<encryption format='%s'>\n", format); - virBufferAdjustIndent(buf, 2); for (i = 0; i < enc->nsecrets; i++) { if (virStorageEncryptionSecretFormat(buf, enc->secrets[i]) < 0) return -1; } - virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</encryption>\n"); return 0; diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index 04641b1..3bab36d 100644 --- a/src/util/virstorageencryption.h +++ b/src/util/virstorageencryption.h @@ -25,6 +25,7 @@ # include "internal.h" # include "virbuffer.h" +# include "virsecret.h" # include "virutil.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 09950fb..e74fb95 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1337,6 +1337,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 ba55919..7115423 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -501,6 +501,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 6d7e5d9..5c2519c 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

On Mon, Jun 13, 2016 at 20:27:51 -0400, John Ferlan wrote:
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 6d7e5d9..5c2519c 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c
[...]
@@ -111,6 +111,11 @@ enum { BACKING_STORE_ERROR, };
+enum fi_crypt { + FI_CRYPT_NONE = 0, + FI_CRYPT_QCOW
This lacks the "VIR_" prefix. Also I don't really see a point in adding this. Currently it's used to distinguish between an encrypted QCOW and an unencrypted QCOW. With LUKS (as you note later in a comment) it's implied that they are encrypted and thus we don't need a side band to store the same data. Peter

On 06/21/2016 08:20 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:51 -0400, John Ferlan wrote:
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 6d7e5d9..5c2519c 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c
[...]
@@ -111,6 +111,11 @@ enum { BACKING_STORE_ERROR, };
+enum fi_crypt { + FI_CRYPT_NONE = 0, + FI_CRYPT_QCOW
This lacks the "VIR_" prefix. Also I don't really see a point in adding this. Currently it's used to distinguish between an encrypted QCOW and an unencrypted QCOW. With LUKS (as you note later in a comment) it's implied that they are encrypted and thus we don't need a side band to store the same data.
OK I can drop this... It would be replaced in "a" subsequent patch with a more direct "meta->format == VIR_STORAGE_FILE_LUKS" type check in order to allocate meta->encryption John

On Tue, Jun 21, 2016 at 10:01:50 -0400, John Ferlan wrote:
On 06/21/2016 08:20 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:51 -0400, John Ferlan wrote:
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 6d7e5d9..5c2519c 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c
[...]
@@ -111,6 +111,11 @@ enum { BACKING_STORE_ERROR, };
+enum fi_crypt { + FI_CRYPT_NONE = 0, + FI_CRYPT_QCOW
This lacks the "VIR_" prefix. Also I don't really see a point in adding this. Currently it's used to distinguish between an encrypted QCOW and an unencrypted QCOW. With LUKS (as you note later in a comment) it's implied that they are encrypted and thus we don't need a side band to store the same data.
OK I can drop this... It would be replaced in "a" subsequent patch with a more direct "meta->format == VIR_STORAGE_FILE_LUKS" type check in order to allocate meta->encryption
I concluded that it might be desired to keep this as long as you want to parse more data from the LUKS header. The name change is desired though.

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 5c2519c..f7a9632 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 9424fed..8d5c45a 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 4072507..f858e5c 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

On Mon, Jun 13, 2016 at 20:27:52 -0400, John Ferlan wrote:
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__ */
Since you are adding this code and also the tests below it looks like a job for a separate patch.
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 5c2519c..f7a9632 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c
[...]
@@ -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
The encryption header offset is not used here. You are not extracting the cipher name from the header.
+ }, /* 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)
This should be selected with a different property than the file type. The idea of the structure above was to avoid having to tweak this code with individual cases for every single file type. You'll either need to pass a function pointer for extraction or switch it according to the passed size.
+ 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 */
FI_CRYPT_LUKS is implied by by the type which in turn implies encryption. Also the encryption header offset is rather useless here since it's not used. Are you expecting to add extraction of the actual cipher type here? In such case it would be actually desired to have the switch as you've added here, but you should change it to VIR_ prefix and perhaps add a more sane comment than /* style of crypt */ ... I prefer gothic crypts.
+ 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 9424fed..8d5c45a 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 4072507..f858e5c 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; +
With these it's definitely stuff for a separate patch.
ret = 0; cleanup: return ret; -- 2.5.5
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 06/21/2016 08:41 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:52 -0400, John Ferlan wrote:
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__ */
Since you are adding this code and also the tests below it looks like a job for a separate patch.
OK - easily done
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 5c2519c..f7a9632 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c
[...]
@@ -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
The encryption header offset is not used here. You are not extracting the cipher name from the header.
Hmm... I think at one time I had thought it might need to be. I can drop LUKS_HDR_CRYPT_OFFSET
+ }, /* 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)
This should be selected with a different property than the file type. The idea of the structure above was to avoid having to tweak this code with individual cases for every single file type. You'll either need to pass a function pointer for extraction or switch it according to the passed size.
Ewww. I think going with versionSize will be easier, although versionFunc would be just as entertaining
+ 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 */
FI_CRYPT_LUKS is implied by by the type which in turn implies encryption. Also the encryption header offset is rather useless here since it's not used.
Are you expecting to add extraction of the actual cipher type here?
At one time I thought I might have to get more data, but it seems it really doesn't matter unless we wanted to display it somehow. As I was going through the implementation phase, I thought I'd have to know which crypt algorithm was used, but all that is handled by qemu - all we need to provide on open/read is the AES secret in order to provide the password used to encrypt the data. Since this code was written I've noted Daniel has a qemu patches: https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg03885.html which will allow qemu-img display a whole lot more luks information which I think would be a better option than us needing to keep track of more header information. When v2 comes along, we'd have more work to do in order to print out information. Perhaps a nice addition, but as I've found out not technically necessary to make things work.
In such case it would be actually desired to have the switch as you've added here, but you should change it to VIR_ prefix and perhaps add a more sane comment than /* style of crypt */ ... I prefer gothic crypts.
I prefer not to visit the crypt... John
+ 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 9424fed..8d5c45a 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 4072507..f858e5c 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; +
With these it's definitely stuff for a separate patch.
ret = 0; cleanup: return ret; -- 2.5.5
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Add a new secret type known as "key" - it will handle adding the secret objects that need a key (or passphrase), such as will soon be the case for a luks volume for both storage driver create and libvirt domain usage. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/aclpolkit.html.in | 4 +++ docs/formatsecret.html.in | 57 +++++++++++++++++++++++++++++++++++-- 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-key.xml | 7 +++++ tests/secretxml2xmltest.c | 1 + 10 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 tests/secretxml2xmlin/usage-key.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..3bb810a 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>key</code> are defined. Specific usage categories + are described below. </dd> </dl> @@ -241,5 +242,57 @@ <secret usage='libvirtiscsi'/> </auth> </pre> + + <h3><a name="keyUsageType">Usage type "key"</a></h3> + + <p> + This secret is a general purpose secret to be used by various libvirt + objects to provide a single key (or passphrase) as required by the + object in order to perform its authentication. + <span class="since">Since 1.3.6</span>. The following is an example + of a key-secret.xml file: + </p> + + <pre> + # cat key-secret.xml + <secret ephemeral='no' private='yes'> + <description>sample key secret</description> + <usage type='key'> + <key>key_example</key> + </usage> + </secret> + + # virsh secret-define key-secret.xml + Secret 718c71bd-67b5-4a2b-87ec-a24e8ca200dc created + + # virsh secret-list + UUID Usage + ----------------------------------------------------------- + 718c71bd-67b5-4a2b-87ec-a24e8ca200dc key key_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..3d131eb 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='usagekey'/> <!-- More choices later --> </choice> </element> @@ -71,4 +72,13 @@ </element> </define> + <define name='usagekey'> + <attribute name='type'> + <value>key</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..fadf811 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_KEY = 4, # ifdef VIR_ENUM_SENTINELS VIR_SECRET_USAGE_TYPE_LAST diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c index 89bc890..97419df 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_KEY: { + 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..ccda2c3 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", "key") 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_KEY: + 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_KEY: + 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_KEY: + if (!(def->usage.key = virXPathString("string(./usage/key)", ctxt))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("key 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_KEY: + 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..84a32b1 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_KEY: + if (STREQ(secret->def->usage.key, data->usageID)) + found = 1; + break; } cleanup: diff --git a/tests/secretxml2xmlin/usage-key.xml b/tests/secretxml2xmlin/usage-key.xml new file mode 100644 index 0000000..4b3afd5 --- /dev/null +++ b/tests/secretxml2xmlin/usage-key.xml @@ -0,0 +1,7 @@ +<secret ephemeral='no' private='no'> + <uuid>f52a81b2-424e-490c-823d-6bd4235bc572</uuid> + <description>Sample Key Secret</description> + <usage type='key'> + <key>mumblyfratz</key> + </usage> +</secret> diff --git a/tests/secretxml2xmltest.c b/tests/secretxml2xmltest.c index 8dcbb40..018fa7f 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-key"); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.5.5

On Mon, Jun 13, 2016 at 20:27:53 -0400, John Ferlan wrote:
Add a new secret type known as "key" - it will handle adding the secret objects that need a key (or passphrase), such as will soon be the case
This may be misleading a "key" is not equal to a "passprhase" in usual encryption terminology. Key usually refers to the actual encryption key used to encrypt the data whereas passprhase is usually a human readable secret string (which may not be random at all) used to access the key later. The cryptsetup man page tends to treat them interchangably to some extent (eg a key slot equals to passprhase, but the master key refers to the actual encryption key used for the data). To avoid confusion I'd rather stick with "passphrase".
for a luks volume for both storage driver create and libvirt domain usage.
Signed-off-by: John Ferlan <jferlan@redhat.com> ---

On 06/21/2016 08:08 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:53 -0400, John Ferlan wrote:
Add a new secret type known as "key" - it will handle adding the secret objects that need a key (or passphrase), such as will soon be the case
This may be misleading a "key" is not equal to a "passprhase" in usual encryption terminology. Key usually refers to the actual encryption key used to encrypt the data whereas passprhase is usually a human readable secret string (which may not be random at all) used to access the key later.
The cryptsetup man page tends to treat them interchangably to some extent (eg a key slot equals to passprhase, but the master key refers to the actual encryption key used for the data).
To avoid confusion I'd rather stick with "passphrase".
That was my other choice... 'key' was just shorter and easier to type. I'll make that adjustment, so it'll be: <secret ephemeral='no' private='no'> <uuid>f52a81b2-424e-490c-823d-6bd4235bc572</uuid> <description>Sample Passphrase Secret</description> <usage type='passphrase'> <passphrase>mumblyfratz</passphrase> </usage> </secret> John
for a luks volume for both storage driver create and libvirt domain usage.
Signed-off-by: John Ferlan <jferlan@redhat.com> ---

On 06/21/2016 08:08 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:53 -0400, John Ferlan wrote:
Add a new secret type known as "key" - it will handle adding the secret objects that need a key (or passphrase), such as will soon be the case
This may be misleading a "key" is not equal to a "passprhase" in usual encryption terminology. Key usually refers to the actual encryption key used to encrypt the data whereas passprhase is usually a human readable secret string (which may not be random at all) used to access the key later.
The cryptsetup man page tends to treat them interchangably to some extent (eg a key slot equals to passprhase, but the master key refers to the actual encryption key used for the data).
To avoid confusion I'd rather stick with "passphrase".
for a luks volume for both storage driver create and libvirt domain usage.
Signed-off-by: John Ferlan <jferlan@redhat.com> ---
While replying to review comments from 6/19, I realized another reason I went with "key" over "passphrase". Consider the existing/old qcow encryption format (http://libvirt.org/formatsecret.html) The <secret> XML looks like: <secret ephemeral='no' private='yes'> <description>Super secret name of my first puppy</description> <uuid>0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f</uuid> <usage type='volume'> <volume>/var/lib/libvirt/images/puppyname.img</volume> </usage> </secret> while the <domain> XML has: <encryption format='qcow'> <secret type='passphrase' uuid='0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f'/> </encryption> or once patch 11 hits: <encryption format='qcow'> <secret type='passphrase' usage='/var/lib/libvirt/images/puppyname.img'/> </encryption> where 'usage' matches 'volume' Using something other than passphrase allowed me to distinguish between that 'old' format and this new style... Using "passphrase" will then have <domain> format of: <encryption format='luks'> <secret type='passphrase' {uuid|usage}='...'>/ And a <secret> format of <secret ephemeral='no' private='yes'> <description>Sample</description> <uuid>0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f</uuid> <usage type='passphrase'> <passphrase>somestring</passphrase> </usage> </secret> where "somestring" is just a 'usage' string and not the actual passphrase which would be set by the 'secret-set-value' command. I could have the <secret> XML use something different than passphrase, but key just seemed to be the most reasonable beyond passphrase. Unless you have a different suggestion for a better name. John Hopefully this was clear...

On 06/21/2016 08:08 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:53 -0400, John Ferlan wrote:
Add a new secret type known as "key" - it will handle adding the secret objects that need a key (or passphrase), such as will soon be the case
This may be misleading a "key" is not equal to a "passprhase" in usual encryption terminology. Key usually refers to the actual encryption key used to encrypt the data whereas passprhase is usually a human readable secret string (which may not be random at all) used to access the key later.
The cryptsetup man page tends to treat them interchangably to some extent (eg a key slot equals to passprhase, but the master key refers to the actual encryption key used for the data).
To avoid confusion I'd rather stick with "passphrase".
for a luks volume for both storage driver create and libvirt domain usage.
Signed-off-by: John Ferlan <jferlan@redhat.com> ---
Perhaps a rephrasing... Instead of: <secret ...> ... <usage type='key'> <key>Text</key> </usage> ... </secret> The preference is: <secret ...> ... <usage type='passphrase'> <XXX>Text</XXX> </usage> ... </secret> Where, I'm struggling what to call "XXX". It's not a <passphrase>... <usage type='volume'> uses <volume> <usage type='ceph'> uses <name> <usage type='iscsi'> uses <target> So given that, does the following work? <usage type='passphrase'> <id>Text</id> </usage> In the long run "Text" is what's used by the <domain...> in order to match/find the secret. Currently the domain secrets have: <domain> ... <encryption format='qcow'> <secret type='passphrase' uuid='xxxx'/} </encryption> ... <disk> ... <auth ...> <secret type='{iscsi|ceph}' {usage|uuid}='string'/> </auth> </domain> where "usage='string'" essentially the contents of <secret....> <usage...> "Text" NB: There are patches to allow usage for <encryption ... <secret...> So, for LUKS we would then have <domain> ... <encryption format='luks'> <secret type='YYY' {uuid|usage}='string'/> </encryption> The YYY could be 'passphrase', right? Furthermore "the future" would "reuse" this <secret> type - so I'm trying to make it generic as possible. John

Add parse and format of the luks/key secret including tests for volume XML parsing. Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatsecret.html.in | 7 +++- 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 + 15 files changed, 181 insertions(+), 7 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/formatsecret.html.in b/docs/formatsecret.html.in index 3bb810a..a774199 100644 --- a/docs/formatsecret.html.in +++ b/docs/formatsecret.html.in @@ -248,7 +248,12 @@ <p> This secret is a general purpose secret to be used by various libvirt objects to provide a single key (or passphrase) as required by the - object in order to perform its authentication. + object in order to perform its authentication. For example, this + 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 key-secret.xml file: </p> 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 513664e..3ed1e43 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2411,6 +2411,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 4ac7476..9cca3e8 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -39,11 +39,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 3bab36d..b966770 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 7115423..fed8351 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -502,6 +502,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 f722452..a36a706 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

On Mon, Jun 13, 2016 at 20:27:54 -0400, John Ferlan wrote:
Add parse and format of the luks/key secret including tests for volume XML parsing.
Signed-off-by: John Ferlan <jferlan@redhat.com> --- docs/formatsecret.html.in | 7 +++- 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 + 15 files changed, 181 insertions(+), 7 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.
I've explained in some other patch why 'key' is not a desired name.
+ 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:
I'll like to see a updated version.

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 | 126 +++++++++++++++++++++ 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, 402 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 10e61da..27f71a5 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7786,6 +7786,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 9cca3e8..608a57b 100644 --- a/src/util/virstorageencryption.c +++ b/src/util/virstorageencryption.c @@ -35,6 +35,7 @@ #include "viruuid.h" #include "virfile.h" #include "virsecret.h" +#include "virstring.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -46,6 +47,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) @@ -63,6 +73,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); } @@ -80,6 +92,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) { @@ -100,6 +126,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: @@ -153,6 +185,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) { @@ -196,6 +273,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: @@ -250,6 +349,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) @@ -269,6 +390,11 @@ virStorageEncryptionFormat(virBufferPtr buf, return -1; } + virBufferAdjustIndent(buf, 2); + if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS && enc->cipher.name) + virStorageEncryptionInfoDefFormat(buf, enc); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</encryption>\n"); return 0; diff --git a/src/util/virstorageencryption.h b/src/util/virstorageencryption.h index b966770..45c5260 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 fed8351..147eff8 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -503,6 +503,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 a36a706..db82bea 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 fdf06ae..0d6d080 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2153,6 +2153,7 @@ virProcessWait; # util/virqemu.h +virQEMUBuildLuksOpts; virQEMUBuildObjectCommandlineFromJSON; diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 4965c9e..fc50807 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_KEY, + &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 ccfe9ab..e300821 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

On Mon, Jun 13, 2016 at 20:27:56 -0400, John Ferlan wrote:
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 fdf06ae..0d6d080 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2153,6 +2153,7 @@ virProcessWait;
# util/virqemu.h +virQEMUBuildLuksOpts; virQEMUBuildObjectCommandlineFromJSON;
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 4965c9e..fc50807 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; + }
This looks like it's becoming a source of technical debt. Heaps of comments aren't helping.
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
This won't fly. There's only one way to guarantee that there won't be a collision. You need to create a file in a private path.
+ */ +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));
usage is user provided text. Also your example in previous patch uses slashes in it. I don't think it will end up well in this case.
+ 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")))a
Apart from creating colliding paths and making possibly volumes appear after a refresh this isn't a good idea also due to the fact that creating the file with the secret may not be possible on NETFS pools due to root squashing.
+ 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_KEY, + &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) {
You also need to take into account the uid and gid the qemu-img process will be using rather than the effective values.
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to chown luks secret file")); + goto error; + } + + if (chmod(secretPath, 0400) < 0) {
You've already created this with mode 600. Is 400 really necessary? If the process manages to destroy the secret, do you care?
+ 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,
[...]

On 06/21/2016 09:53 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:56 -0400, John Ferlan wrote:
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(-)
[...]
+ 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; + }
This looks like it's becoming a source of technical debt. Heaps of comments aren't helping.
It's on the short list of things to deal with.
return ret; } @@ -941,21 +972,31 @@ struct _virStorageBackendQemuImgInfo { const char *inputPath; const char *inputFormatStr; int inputFormat; + + char *secretAlias; + const char *secretPath; };
[...]
+/* Create a hopefully unique enough name to be used for both the + * secretPath and secretAlias generation
This won't fly. There's only one way to guarantee that there won't be a collision. You need to create a file in a private path.
+ */ +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));
usage is user provided text. Also your example in previous patch uses slashes in it. I don't think it will end up well in this case.
Ugh... right. I'm almost tempted to avoid a file type of secret and make it be an AES type secret. This goes away anyway.
+ return ret; +} +
[...]
+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")))
Apart from creating colliding paths and making possibly volumes appear after a refresh this isn't a good idea also due to the fact that creating the file with the secret may not be possible on NETFS pools due to root squashing.
Using an update I have in my local repository, this does work with the "correct" XML (gotta have the <permissions> set properly).
+ 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_KEY, + &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) {
You also need to take into account the uid and gid the qemu-img process will be using rather than the effective values.
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to chown luks secret file")); + goto error; + } + + if (chmod(secretPath, 0400) < 0) {
You've already created this with mode 600. Is 400 really necessary? If the process manages to destroy the secret, do you care?
Not necessarily - the whole generate a file and set protections is a reaction to a comment Dan made in an earlier review: http://www.redhat.com/archives/libvir-list/2016-June/msg00021.html Would mkostemp() be a "reasonable" option here since we're just going to delete the file anyway? I'd have to add something to storage_driver to return a "path" that included "driver->stateDir", then use mkostemp in order to create a unique name... The alias name thus just becomes the vol->name+"_luks0". Once I get some feedback regarding patch 5, 8, & 11 I can create a shorter v2 with the current set of changes. Thanks for taking the plunge... John
+ 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,
[...]

Add 'encinfo' to the extended disk structure. This will contain the encryption secret (if present). Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 2c583d8..c288fa0 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -758,6 +758,7 @@ qemuDomainDiskPrivateDispose(void *obj) qemuDomainDiskPrivatePtr priv = obj; qemuDomainSecretInfoFree(&priv->secinfo); + qemuDomainSecretInfoFree(&priv->encinfo); } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 2443e97..fa536e0 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -299,6 +299,11 @@ struct _qemuDomainDiskPrivate { * NB: *not* to be written to qemu domain object XML */ qemuDomainSecretInfoPtr secinfo; + /* for storage devices using encryption/secret + * Can have both <auth> and <encryption> for some disks + * NB:*not* to be written to qemu domain object XML */ + qemuDomainSecretInfoPtr encinfo; + /* information about the device */ bool tray; /* device has tray */ bool removable; /* device media can be removed/changed */ -- 2.5.5

On Mon, Jun 13, 2016 at 20:27:57 -0400, John Ferlan wrote:
Add 'encinfo' to the extended disk structure. This will contain the encryption secret (if present).
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 5 +++++ 2 files changed, 6 insertions(+)
ACK

Generate the luks command line using the AES secret key to encrypt the luks secret. A luks secret object will be in addition to a an AES secret. Add tests for sample output Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 12 ++++++-- src/qemu/qemu_domain.c | 19 ++++++++++-- .../qemuxml2argv-luks-disk-cipher.args | 36 ++++++++++++++++++++++ .../qemuxml2argvdata/qemuxml2argv-luks-disks.args | 36 ++++++++++++++++++++++ tests/qemuxml2argvtest.c | 11 ++++++- 5 files changed, 109 insertions(+), 5 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 490260f..7062c17 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1103,6 +1103,7 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, int actualType = virStorageSourceGetActualType(disk->src); qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo; + qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo; bool emitDeviceSyntax = qemuDiskBusNeedsDeviceArg(disk->bus); if (idx < 0) { @@ -1237,10 +1238,13 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, qemuBufferEscapeComma(&opt, source); virBufferAddLit(&opt, ","); - if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { + if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) virBufferAsprintf(&opt, "password-secret=%s,", secinfo->s.aes.alias); - } + + if (encinfo) + virQEMUBuildLuksOpts(&opt, disk->src->encryption, + encinfo->s.aes.alias); if (disk->src->format > 0 && disk->src->type != VIR_STORAGE_TYPE_DIR) @@ -1920,6 +1924,7 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd, virDomainDiskDefPtr disk = def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo; + qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo; /* PowerPC pseries based VMs do not support floppy device */ if ((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) && @@ -1949,6 +1954,9 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd, if (qemuBuildDiskSecinfoCommandLine(cmd, secinfo) < 0) return -1; + if (qemuBuildDiskSecinfoCommandLine(cmd, encinfo) < 0) + return -1; + virCommandAddArg(cmd, "-drive"); optstr = qemuBuildDriveStr(disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c288fa0..fb3e91f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -935,7 +935,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_KEY)) { if (qemuDomainSecretAESSetup(conn, priv, secinfo, srcalias, secretUsageType, username, secdef) < 0) return -1; @@ -1018,6 +1019,7 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, { virStorageSourcePtr src = disk->src; qemuDomainSecretInfoPtr secinfo = NULL; + qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); if (conn && !virStorageSourceIsEmpty(src) && virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_NETWORK && @@ -1026,7 +1028,6 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { virSecretUsageType secretUsageType; - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); if (VIR_ALLOC(secinfo) < 0) return -1; @@ -1044,6 +1045,20 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, diskPriv->secinfo = secinfo; } + if (conn && !virStorageSourceIsEmpty(src) && + src->encryption && src->format == VIR_STORAGE_FILE_LUKS) { + + if (VIR_ALLOC(secinfo) < 0) + return -1; + + if (qemuDomainSecretSetup(conn, priv, secinfo, disk->info.alias, + VIR_SECRET_USAGE_TYPE_KEY, NULL, + &src->encryption->secrets[0]->secdef) < 0) + goto error; + + diskPriv->encinfo = secinfo; + } + return 0; error: 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 e74fb95..12f0621 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, @@ -1338,6 +1345,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 Mon, Jun 13, 2016 at 20:27:58 -0400, John Ferlan wrote:
Generate the luks command line using the AES secret key to encrypt the luks secret. A luks secret object will be in addition to a an AES secret.
Add tests for sample output
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 12 ++++++-- src/qemu/qemu_domain.c | 19 ++++++++++-- .../qemuxml2argv-luks-disk-cipher.args | 36 ++++++++++++++++++++++ .../qemuxml2argvdata/qemuxml2argv-luks-disks.args | 36 ++++++++++++++++++++++ tests/qemuxml2argvtest.c | 11 ++++++- 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args
Note that this won't work with disk hotplug until you add code that will add the secret objects too. Also I've noticed that RBD hotplug is now broken too due to the same reason. The code that selects whether to use plaintext password or pass it via the secret object does not check whether it's called on the hotplug path and thus uses the secret object. The secret object is not added using the monitor though. You'll need to fix that first and if you opt for the correct approach this will then work automagically. Looks okay but I can't ACK this with hotplug not working. Also please note that on hot-unplug you need to remove the secrets as you'll possibly be adding a secret with the same name (Since alias will be reused)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 490260f..7062c17 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1103,6 +1103,7 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, int actualType = virStorageSourceGetActualType(disk->src); qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo; + qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo; bool emitDeviceSyntax = qemuDiskBusNeedsDeviceArg(disk->bus);
if (idx < 0) { @@ -1237,10 +1238,13 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, qemuBufferEscapeComma(&opt, source); virBufferAddLit(&opt, ",");
- if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { + if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) virBufferAsprintf(&opt, "password-secret=%s,", secinfo->s.aes.alias); - } + + if (encinfo) + virQEMUBuildLuksOpts(&opt, disk->src->encryption, + encinfo->s.aes.alias);
This wrapper is not really useful here. It only adds "key-secret=" all the other options are necessary only if creating the volume.
if (disk->src->format > 0 && disk->src->type != VIR_STORAGE_TYPE_DIR) @@ -1920,6 +1924,7 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd, virDomainDiskDefPtr disk = def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo; + qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo;
/* PowerPC pseries based VMs do not support floppy device */ if ((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) && @@ -1949,6 +1954,9 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd, if (qemuBuildDiskSecinfoCommandLine(cmd, secinfo) < 0) return -1;
+ if (qemuBuildDiskSecinfoCommandLine(cmd, encinfo) < 0) + return -1; + virCommandAddArg(cmd, "-drive");
optstr = qemuBuildDriveStr(disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c288fa0..fb3e91f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c
[...]
@@ -1026,7 +1028,6 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) {
virSecretUsageType secretUsageType; - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
if (VIR_ALLOC(secinfo) < 0) return -1; @@ -1044,6 +1045,20 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, diskPriv->secinfo = secinfo; }
+ if (conn && !virStorageSourceIsEmpty(src) &&
This part is the same with the block above. It may be worth extracting it ...
+ src->encryption && src->format == VIR_STORAGE_FILE_LUKS) { + + if (VIR_ALLOC(secinfo) < 0) + return -1; + + if (qemuDomainSecretSetup(conn, priv, secinfo, disk->info.alias, + VIR_SECRET_USAGE_TYPE_KEY, NULL, + &src->encryption->secrets[0]->secdef) < 0) + goto error; + + diskPriv->encinfo = secinfo; + } + return 0;
error:

On Tue, Jun 21, 2016 at 15:03:51 +0200, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:58 -0400, John Ferlan wrote:
Generate the luks command line using the AES secret key to encrypt the luks secret. A luks secret object will be in addition to a an AES secret.
Add tests for sample output
Signed-off-by: John Ferlan <jferlan@redhat.com> ---
[...]
@@ -1237,10 +1238,13 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, qemuBufferEscapeComma(&opt, source); virBufferAddLit(&opt, ",");
- if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { + if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) virBufferAsprintf(&opt, "password-secret=%s,", secinfo->s.aes.alias); - } + + if (encinfo) + virQEMUBuildLuksOpts(&opt, disk->src->encryption, + encinfo->s.aes.alias);
This wrapper is not really useful here. It only adds "key-secret=" all the other options are necessary only if creating the volume.
Okay, in the end this might be a reasonable idea if we'll want to add support for block-copy-ing into a luks volume. On the other hand, you'll need to disallow snapshots if the disk is LUKS until we add support for full backing chain tracking since you'll lose the definitions for the key once you take a snapshot. A second start of that VM will not be possible then.

On 06/21/2016 09:03 AM, Peter Krempa wrote:
On Mon, Jun 13, 2016 at 20:27:58 -0400, John Ferlan wrote:
Generate the luks command line using the AES secret key to encrypt the luks secret. A luks secret object will be in addition to a an AES secret.
Add tests for sample output
Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/qemu/qemu_command.c | 12 ++++++-- src/qemu/qemu_domain.c | 19 ++++++++++-- .../qemuxml2argv-luks-disk-cipher.args | 36 ++++++++++++++++++++++ .../qemuxml2argvdata/qemuxml2argv-luks-disks.args | 36 ++++++++++++++++++++++ tests/qemuxml2argvtest.c | 11 ++++++- 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disk-cipher.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-luks-disks.args
Note that this won't work with disk hotplug until you add code that will add the secret objects too.
Oh right - I had forgotten about the secret object... I had the same issue with the TLS code, so I know what has to be done...
Also I've noticed that RBD hotplug is now broken too due to the same reason. The code that selects whether to use plaintext password or pass it via the secret object does not check whether it's called on the hotplug path and thus uses the secret object.
Oh damn... I think that's a disconnect in qemuDomainSecretSetup (called from qemuDomainSecretDiskPrepare from hotplug) and the assumption of an AES secinfo for RBD when there's a secret object available.
The secret object is not added using the monitor though. You'll need to fix that first and if you opt for the correct approach this will then work automagically.
Looks okay but I can't ACK this with hotplug not working.
Also please note that on hot-unplug you need to remove the secrets as you'll possibly be adding a secret with the same name (Since alias will be reused)
yep...
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 490260f..7062c17 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1103,6 +1103,7 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, int actualType = virStorageSourceGetActualType(disk->src); qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo; + qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo; bool emitDeviceSyntax = qemuDiskBusNeedsDeviceArg(disk->bus);
if (idx < 0) { @@ -1237,10 +1238,13 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk, qemuBufferEscapeComma(&opt, source); virBufferAddLit(&opt, ",");
- if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) { + if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) virBufferAsprintf(&opt, "password-secret=%s,", secinfo->s.aes.alias); - } + + if (encinfo) + virQEMUBuildLuksOpts(&opt, disk->src->encryption, + encinfo->s.aes.alias);
This wrapper is not really useful here. It only adds "key-secret=" all the other options are necessary only if creating the volume.
I was thinking of the future if/when new things are added. I'm not clear yet what would have to be done for block-copy and snapshots.
if (disk->src->format > 0 && disk->src->type != VIR_STORAGE_TYPE_DIR) @@ -1920,6 +1924,7 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd, virDomainDiskDefPtr disk = def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo; + qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo;
/* PowerPC pseries based VMs do not support floppy device */ if ((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) && @@ -1949,6 +1954,9 @@ qemuBuildDiskDriveCommandLine(virCommandPtr cmd, if (qemuBuildDiskSecinfoCommandLine(cmd, secinfo) < 0) return -1;
+ if (qemuBuildDiskSecinfoCommandLine(cmd, encinfo) < 0) + return -1; + virCommandAddArg(cmd, "-drive");
optstr = qemuBuildDriveStr(disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c288fa0..fb3e91f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c
[...]
@@ -1026,7 +1028,6 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) {
virSecretUsageType secretUsageType; - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
if (VIR_ALLOC(secinfo) < 0) return -1; @@ -1044,6 +1045,20 @@ qemuDomainSecretDiskPrepare(virConnectPtr conn, diskPriv->secinfo = secinfo; }
+ if (conn && !virStorageSourceIsEmpty(src) &&
This part is the same with the block above. It may be worth extracting it ...
Oh right... I think I had it that way at one time, too... Thanks again - John
+ src->encryption && src->format == VIR_STORAGE_FILE_LUKS) { + + if (VIR_ALLOC(secinfo) < 0) + return -1; + + if (qemuDomainSecretSetup(conn, priv, secinfo, disk->info.alias, + VIR_SECRET_USAGE_TYPE_KEY, NULL, + &src->encryption->secrets[0]->secdef) < 0) + goto error; + + diskPriv->encinfo = secinfo; + } + return 0;
error:
participants (2)
-
John Ferlan
-
Peter Krempa