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 second submission: - Update for new internal API - s/secret_id/uuid/g --- src/storage_backend.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 124 insertions(+), 3 deletions(-) diff --git a/src/storage_backend.c b/src/storage_backend.c index c818142..867c9b0 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -43,6 +43,7 @@ #include <selinux/selinux.h> #endif +#include "datatypes.h" #include "virterror_internal.h" #include "util.h" #include "memory.h" @@ -331,6 +332,118 @@ cleanup: } static int +virStorageGenerateQcowEncryption(virConnectPtr conn, + virStorageVolDefPtr vol) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virStorageEncryptionPtr enc; + virStorageEncryptionSecretPtr enc_secret = NULL; + virSecretPtr secret = NULL; + char *uuid = NULL, *xml; + unsigned char value[16]; + int ret = -1, fd = -1; + size_t i; + + 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) { + virReportOOMError(conn); + goto cleanup; + } + + virBufferAddLit(&buf, "<secret ephemeral='no' private='no'>"); + /* <uuid/> is chosen by the secret driver */ + virBufferEscapeString(&buf, + "<description>qcow passphrase for %s</description>", + vol->target.path); + virBufferEscapeString(&buf, "<volume>%s</volume>", vol->target.path); + virBufferAddLit(&buf, "</secret>"); + if (virBufferError(&buf)) { + virReportOOMError(conn); + goto cleanup; + } + xml = virBufferContentAndReset(&buf); + secret = conn->secretDriver->defineXML(conn, xml); + if (secret == NULL) { + VIR_FREE(xml); + goto cleanup; + } + VIR_FREE(xml); + + uuid = strdup(secret->uuid); + if (uuid == NULL) { + virReportOOMError(conn); + goto cleanup; + } + + /* 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")); + goto cleanup; + } + i = 0; + while (i < sizeof (value)) { + ssize_t r; + + while ((r = read(fd, value + i, 1)) == -1 && errno == EINTR) + ; + if (r <= 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot read from /dev/urandom")); + goto cleanup; + } + if (value[i] >= 0x20 && value[i] <= 0x7E) + i++; /* Got an acceptable character */ + } + close(fd); + fd = -1; + + if (conn->secretDriver->setValue(secret, value, sizeof(value)) < 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: + if (fd != -1) + close(fd); + VIR_FREE(uuid); + if (secret != NULL) { + if (conn->secretDriver->undefine != NULL) + conn->secretDriver->undefine(secret); + virSecretFree(secret); + } + xml = virBufferContentAndReset(&buf); + VIR_FREE(xml); + VIR_FREE(enc_secret); + return ret; +} + +static int virStorageBackendCreateQemuImg(virConnectPtr conn, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, @@ -428,6 +541,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, @@ -435,18 +550,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) -- 1.6.2.5 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list