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@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to