If the <encryption format='qcow'> element does not specify a secret during volume creation, generate a suitable secret and add it to the <encryption> tag. The caller can view the updated <encryption> tag using virStorageVolGetXMLDesc(). Similarly, when <encryption format='default'/> is specified while creating a qcow or qcow2-formatted volume, change the format to "qcow" and generate a secret as described above. Changes since the fourth submission: - Use virSecretDefPtr API to create a <secret> XML string - Update for <usage type='volume'><volume/></usage> - Add "flags" parameter to virSecretDefineXML(), virSecretGetXMLDesc(), virSecretGetValue(), virSecretSetValue(), and all derived interfaces. - Split qcow passphrase generation into a separate function, move it into storage_encryption_conf.[ch]. * src/storage_encryption_conf.h (VIR_STORAGE_QCOW_PASSPHRASE_SIZE, virStorageGenerateQcowPasphrase), src/storage_encryption_conf.c (virStorageGenerateQcowPasphrase), src/libvirt_private.syms: Add virStorageGenerateQcowPasphrase(). * src/storage_backend.c (virStoragegenerateQcowEncryption, virStorageBackendCreateQemuImg): Generate a passphrase and <encryption> when creating a qcow-formatted encrypted volume and the user did not supply the information. --- src/libvirt_private.syms | 1 + src/storage_backend.c | 113 +++++++++++++++++++++++++++++++++++++++- src/storage_encryption_conf.c | 37 +++++++++++++ src/storage_encryption_conf.h | 7 +++ 4 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index cd22b96..3cb707e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -357,6 +357,7 @@ virStorageEncryptionFree; virStorageEncryptionDropSecrets; virStorageEncryptionParseNode; virStorageEncryptionFormat; +virStorageGenerateQcowPassphrase; # threads.h diff --git a/src/storage_backend.c b/src/storage_backend.c index 77243ef..afcce67 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -43,11 +43,13 @@ #include <selinux/selinux.h> #endif +#include "datatypes.h" #include "virterror_internal.h" #include "util.h" #include "memory.h" #include "node_device.h" #include "internal.h" +#include "secret_conf.h" #include "storage_backend.h" @@ -330,6 +332,103 @@ cleanup: } static int +virStorageGenerateQcowEncryption(virConnectPtr conn, + virStorageVolDefPtr vol) +{ + virSecretDefPtr def = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + virStorageEncryptionPtr enc; + virStorageEncryptionSecretPtr enc_secret = NULL; + virSecretPtr secret = NULL; + char *uuid = NULL, *xml; + unsigned char value[VIR_STORAGE_QCOW_PASSPHRASE_SIZE]; + int ret = -1; + + if (conn->secretDriver == NULL || conn->secretDriver->defineXML == NULL || + conn->secretDriver->setValue == NULL) { + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, "%s", + _("secret storage not supported")); + goto cleanup; + } + + enc = vol->target.encryption; + if (enc->nsecrets != 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("secrets already defined")); + goto cleanup; + } + + if (VIR_ALLOC(enc_secret) < 0 || VIR_REALLOC_N(enc->secrets, 1) < 0 || + VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + def->ephemeral = 0; + def->private = 0; + def->id = NULL; /* Chosen by the secret driver */ + if (virAsprintf(&def->description, "qcow passphrase for %s", + vol->target.path) == -1) { + virReportOOMError(conn); + goto cleanup; + } + def->usage_type = VIR_SECRET_USAGE_TYPE_VOLUME; + def->usage.volume = strdup(vol->target.path); + if (def->usage.volume == NULL) { + virReportOOMError(conn); + goto cleanup; + } + xml = virSecretDefFormat(conn, def); + virSecretDefFree(def); + def = NULL; + if (xml == NULL) + goto cleanup; + + secret = conn->secretDriver->defineXML(conn, xml, 0); + if (secret == NULL) { + VIR_FREE(xml); + goto cleanup; + } + VIR_FREE(xml); + + uuid = strdup(secret->uuid); + if (uuid == NULL) { + virReportOOMError(conn); + goto cleanup; + } + + if (virStorageGenerateQcowPassphrase(conn, value) < 0) + goto cleanup; + + if (conn->secretDriver->setValue(secret, value, sizeof(value), 0) < 0) + goto cleanup; + secret = NULL; + + enc_secret->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE; + enc_secret->uuid = uuid; + uuid = NULL; + enc->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; + enc->secrets[0] = enc_secret; /* Space for secrets[0] allocated above */ + enc_secret = NULL; + enc->nsecrets = 1; + + ret = 0; + +cleanup: + VIR_FREE(uuid); + if (secret != NULL) { + if (conn->secretDriver->undefine != NULL) + conn->secretDriver->undefine(secret); + virSecretFree(secret); + } + xml = virBufferContentAndReset(&buf); + VIR_FREE(xml); + virSecretDefFree(def); + VIR_FREE(enc_secret); + return ret; +} + +static int virStorageBackendCreateQemuImg(virConnectPtr conn, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, @@ -427,6 +526,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, } if (vol->target.encryption != NULL) { + virStorageEncryptionPtr enc; + if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW && vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { virStorageReportError(conn, VIR_ERR_NO_SUPPORT, @@ -434,18 +535,24 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, "volume format %s"), type); return -1; } - if (vol->target.encryption->format != - VIR_STORAGE_ENCRYPTION_FORMAT_QCOW) { + enc = vol->target.encryption; + if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW && + enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT) { virStorageReportError(conn, VIR_ERR_NO_SUPPORT, _("unsupported volume encryption format %d"), vol->target.encryption->format); return -1; } - if (vol->target.encryption->nsecrets > 1) { + if (enc->nsecrets > 1) { virStorageReportError(conn, VIR_ERR_INVALID_STORAGE_VOL, _("too many secrets for qcow encryption")); return -1; } + if (enc->format == VIR_STORAGE_ENCRYPTION_FORMAT_DEFAULT || + enc->nsecrets == 0) { + if (virStorageGenerateQcowEncryption(conn, vol) < 0) + return -1; + } } if ((create_tool = virFindFileInPath("kvm-img")) != NULL) diff --git a/src/storage_encryption_conf.c b/src/storage_encryption_conf.c index 7b9ee88..1ce76b5 100644 --- a/src/storage_encryption_conf.c +++ b/src/storage_encryption_conf.c @@ -22,6 +22,9 @@ #include <config.h> +#include <fcntl.h> +#include <unistd.h> + #include "internal.h" #include "buf.h" @@ -242,3 +245,37 @@ virStorageEncryptionFormat(virConnectPtr conn, return 0; } + +int +virStorageGenerateQcowPassphrase(virConnectPtr conn, unsigned char *dest) +{ + int fd; + size_t i; + + /* A qcow passphrase is up to 16 bytes, with any data following a NUL + ignored. Prohibit control and non-ASCII characters to avoid possible + unpleasant surprises with the qemu monitor input mechanism. */ + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot open /dev/urandom")); + return -1; + } + i = 0; + while (i < VIR_STORAGE_QCOW_PASSPHRASE_SIZE) { + ssize_t r; + + while ((r = read(fd, dest + i, 1)) == -1 && errno == EINTR) + ; + if (r <= 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot read from /dev/urandom")); + close(fd); + return -1; + } + if (dest[i] >= 0x20 && dest[i] <= 0x7E) + i++; /* Got an acceptable character */ + } + close(fd); + return 0; +} diff --git a/src/storage_encryption_conf.h b/src/storage_encryption_conf.h index 3e653b5..cdcf625 100644 --- a/src/storage_encryption_conf.h +++ b/src/storage_encryption_conf.h @@ -69,4 +69,11 @@ virStorageEncryptionPtr virStorageEncryptionParseNode(virConnectPtr conn, int virStorageEncryptionFormat(virConnectPtr conn, virBufferPtr buf, virStorageEncryptionPtr enc); +/* A helper for VIR_STORAGE_ENCRYPTION_FORMAT_QCOW */ +enum { + VIR_STORAGE_QCOW_PASSPHRASE_SIZE = 16 +}; + +int virStorageGenerateQcowPassphrase(virConnectPtr conn, unsigned char *dest); + #endif /* __VIR_STORAGE_ENCRYPTION_H__ */ -- 1.6.2.5 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list