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. --- src/storage_backend.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 119 insertions(+), 3 deletions(-) diff --git a/src/storage_backend.c b/src/storage_backend.c index 2d37b8b..53bca65 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,113 @@ cleanup: } static int +virStorageGenerateQcowEncryption(virConnectPtr conn, + virStorageVolDefPtr vol) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virStorageEncryptionPtr enc; + virStorageEncryptionSecretPtr enc_secret = NULL; + char *secret_id = NULL, *tmp = NULL; + unsigned char secret[16]; + int ret = -1, fd = -1; + size_t i; + + if (conn->secretDriver == NULL || conn->secretDriver->allocateID == NULL || + conn->secretDriver->setXML == 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; + } + + secret_id = conn->secretDriver->allocateID(conn); + if (secret_id == NULL) + goto cleanup; + + virBufferAddLit(&buf, "<secret ephemeral='no' private='no'>"); + 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; + } + tmp = virBufferContentAndReset(&buf); + if (conn->secretDriver->setXML(conn, secret_id, tmp) < 0) { + VIR_FREE(tmp); + goto cleanup; + } + VIR_FREE(tmp); + + /* 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 (secret)) { + ssize_t r; + + while ((r = read(fd, secret + i, 1)) == -1 && errno == EINTR) + ; + if (r <= 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot read from /dev/urandom")); + goto cleanup; + } + if (secret[i] >= 0x20 && secret[i] <= 0x7E) + i++; /* Got an acceptable character */ + } + close(fd); + fd = -1; + + if (conn->secretDriver->setValue(conn, secret_id, secret, + sizeof(secret)) < 0) + goto cleanup; + + enc_secret->type = VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE; + enc_secret->secret_id = secret_id; + secret_id = 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); + tmp = virBufferContentAndReset(&buf); + VIR_FREE(tmp); + if (secret_id != NULL) { + if (conn->secretDriver->delete != NULL) + conn->secretDriver->delete(conn, secret_id); + VIR_FREE(secret_id); + } + VIR_FREE(enc_secret); + return ret; +} + +static int virStorageBackendCreateQemuImg(virConnectPtr conn, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, @@ -428,6 +536,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 +545,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