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