[libvirt] [PATCH 07/10] Provide missing passphrase when creating a volume.

Miloslav Trmač mitr at redhat.com
Mon Sep 7 14:12:42 UTC 2009


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




More information about the libvir-list mailing list