[libvirt] [PATCH 20/20] Add support for qcow encrypted volumes to qemu.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Changes since the first submission:
- Update to use the generic "secrets" encryption representation, and the
  separate secret store.
- Use qemudMonitorSend()
---
 include/libvirt/virterror.h |    1 +
 src/qemu_driver.c           |  157 ++++++++++++++++++++++++++++++++++++++++--
 src/virterror.c             |    6 ++
 3 files changed, 156 insertions(+), 8 deletions(-)

diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index fc24251..53f2f20 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -169,6 +169,7 @@ typedef enum {
     VIR_ERR_MULTIPLE_INTERFACES, /* more than one matching interface found */
     VIR_WAR_NO_SECRET, /* failed to start secret storage */
     VIR_ERR_NO_SECRET, /* secret not found */
+    VIR_ERR_INVALID_SECRET, /* invalid secret */
 } virErrorNumber;
 
 /**
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index d654651..dc6e653 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -1284,12 +1284,6 @@ qemudInitPasswords(virConnectPtr conn,
                    virDomainObjPtr vm) {
     char *info = NULL;
 
-    /*
-     * NB: Might have more passwords to set in the future. eg a qcow
-     * disk decryption password, but there's no monitor command
-     * for that yet...
-     */
-
     if ((vm->def->ngraphics == 1) &&
         vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
         (vm->def->graphics[0]->data.vnc.passwd || driver->vncPassword)) {
@@ -2462,14 +2456,161 @@ qemudMonitorCommand(const virDomainObjPtr vm,
     return qemudMonitorCommandWithFd(vm, cmd, -1, reply);
 }
 
+static virStorageEncryptionPtr
+findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm,
+                         const char *path)
+{
+    bool seen_volume;
+    int i;
+
+    seen_volume = false;
+    for (i = 0; i < vm->def->ndisks; i++) {
+        virDomainDiskDefPtr disk;
+
+        disk = vm->def->disks[i];
+        if (disk->src != NULL && STREQ(disk->src, path)) {
+            seen_volume = true;
+            if (disk->encryption != NULL)
+                return disk->encryption;
+        }
+    }
+    if (seen_volume)
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("missing <encryption> for volume %s"), path);
+    else
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("unexpected passphrase request for volume %s"),
+                         path);
+    return NULL;
+}
+
+static char *
+findVolumeQcowPassphrase(virConnectPtr conn, virDomainObjPtr vm,
+                         const char *path, size_t *passphrase_len)
+{
+    virStorageEncryptionPtr enc;
+    char *passphrase;
+    void *data;
+    size_t size;
+
+    if (conn->secretDriver == NULL || conn->secretDriver->getValue == NULL) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s",
+                         _("secret storage not supported"));
+        return NULL;
+    }
+
+    enc = findDomainDiskEncryption(conn, vm, path);
+    if (enc == NULL)
+        return NULL;
+
+    if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
+        enc->nsecrets != 1 ||
+        enc->secrets[0]->type !=
+        VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("invalid <encryption> for volume %s"), path);
+        return NULL;
+    }
+
+    if (enc->secrets[0]->secret_id == NULL) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("missing secret_id for volume %s"), path);
+        return NULL;
+    }
+    data = conn->secretDriver->getValue(conn, enc->secrets[0]->secret_id,
+                                        &size, true);
+    if (data == NULL) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SECRET,
+                         _("secret not found for volume %s"), path);
+        return NULL;
+    }
+
+    if (memchr(data, '\0', size) != NULL) {
+        memset(data, 0, size);
+        VIR_FREE(data);
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET,
+                         _("format='qcow' passphrase for %s must not contain a "
+                           "'\\0'"), path);
+        return NULL;
+    }
+
+    if (VIR_ALLOC_N(passphrase, size + 1) < 0) {
+        memset(data, 0, size);
+        VIR_FREE(data);
+        virReportOOMError(conn);
+        return NULL;
+    }
+    memcpy(passphrase, data, size);
+    passphrase[size] = '\0';
+
+    memset(data, 0, size);
+    VIR_FREE(data);
+
+    *passphrase_len = size;
+    return passphrase;
+}
+
+struct sendVolumePassphraseState {
+    virConnectPtr conn;
+    bool *error_reported;
+};
+
+static int
+qemudMonitorSendVolumePassphrase(const virDomainObjPtr vm,
+                                 const char *buf,
+                                 const char *prompt,
+                                 void *data)
+{
+    const struct sendVolumePassphraseState *state = data;
+    char *passphrase, *path;
+    const char *prompt_path;
+    size_t path_len, passphrase_len = 0;
+    int res;
+
+    /* The complete prompt looks like this:
+           ide0-hd0 (/path/to/volume) is encrypted.
+           Password:
+       "prompt" starts with ") is encrypted".  Extract /path/to/volume. */
+    for (prompt_path = prompt; prompt_path > buf && prompt_path[-1] != '(';
+         prompt_path--)
+        ;
+    if (prompt_path == buf)
+        return -1;
+    path_len = prompt - prompt_path;
+    if (VIR_ALLOC_N(path, path_len + 1) < 0)
+        return -1;
+    memcpy(path, prompt_path, path_len);
+    path[path_len] = '\0';
+
+    passphrase = findVolumeQcowPassphrase(state->conn, vm, path,
+                                          &passphrase_len);
+    VIR_FREE(path);
+    if (passphrase == NULL) {
+        *state->error_reported = true;
+        return -1;
+    }
+
+    res = qemudMonitorSend(vm, passphrase, -1);
+
+    memset(passphrase, 0, passphrase_len);
+    VIR_FREE(passphrase);
+
+    return res;
+}
+
 static int
-qemudMonitorSendCont(virConnectPtr conn ATTRIBUTE_UNUSED,
+qemudMonitorSendCont(virConnectPtr conn,
                      const virDomainObjPtr vm,
                      bool *error_reported) {
+    struct sendVolumePassphraseState state;
     char *reply;
 
     *error_reported = false;
-    if (qemudMonitorCommand(vm, "cont", &reply) < 0)
+    state.conn = conn;
+    state.error_reported = error_reported;
+    if (qemudMonitorCommandWithHandler(vm, "cont", ") is encrypted.",
+                                       qemudMonitorSendVolumePassphrase,
+                                       &state, -1, &reply) < 0)
         return -1;
     qemudDebug ("%s: cont reply: %s", vm->def->name, info);
     VIR_FREE(reply);
diff --git a/src/virterror.c b/src/virterror.c
index 137405a..9392498 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -1083,6 +1083,12 @@ virErrorMsg(virErrorNumber error, const char *info)
             else
                 errmsg = _("Secret not found: %s");
             break;
+        case VIR_ERR_INVALID_SECRET:
+            if (info == NULL)
+                errmsg = _("Invalid secret");
+            else
+                errmsg = _("Invalid secret: %s");
+            break;
     }
     return (errmsg);
 }
-- 
1.6.2.5

--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]