[libvirt] [PATCH] Support 'block_passwd' command for QEMU disk encryption

Daniel P. Berrange berrange at redhat.com
Thu Feb 11 14:31:37 UTC 2010


The old text mode monitor prompts for a password when disks are
encrypted. This interactive approach doesn't work for JSON mode
monitor. Thus there is a new 'block_passwd' command that can be
used.

* src/qemu/qemu_driver.c: Split out code for looking up a disk
  secret from findVolumeQcowPassphrase, into a new method
  getVolumeQcowPassphrase. Enhance qemuInitPasswords() to also
  set the disk encryption password via the monitor
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
  src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
  support for the 'block_passwd' monitor command.
---
 src/qemu/qemu_driver.c       |  117 +++++++++++++++++++++++++++++-------------
 src/qemu/qemu_monitor.c      |   15 +++++
 src/qemu/qemu_monitor.h      |    4 ++
 src/qemu/qemu_monitor_json.c |   33 ++++++++++++
 src/qemu/qemu_monitor_json.h |    4 ++
 src/qemu/qemu_monitor_text.c |   47 +++++++++++++++++
 src/qemu/qemu_monitor_text.h |    4 ++
 7 files changed, 188 insertions(+), 36 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 0d77d57..03d0f5f 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -694,51 +694,46 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
 }
 
 
-static virStorageEncryptionPtr
-findDomainDiskEncryption(virDomainObjPtr vm,
-                         const char *path)
+static virDomainDiskDefPtr
+findDomainDiskByPath(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 (disk->src != NULL && STREQ(disk->src, path))
+            return disk;
     }
-    if (seen_volume)
-        qemuReportError(VIR_ERR_INVALID_DOMAIN,
-                        _("missing <encryption> for volume %s"), path);
-    else
-        qemuReportError(VIR_ERR_INTERNAL_ERROR,
-                        _("unexpected passphrase request for volume %s"),
-                        path);
+
+    qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                    _("no disk found with path %s"),
+                    path);
     return NULL;
 }
 
-
 static int
-findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
-                         virConnectPtr conn,
-                         virDomainObjPtr vm,
-                         const char *path,
-                         char **secretRet,
-                         size_t *secretLen)
+getVolumeQcowPassphrase(virConnectPtr conn,
+                        virDomainDiskDefPtr disk,
+                        char **secretRet,
+                        size_t *secretLen)
 {
-    virStorageEncryptionPtr enc;
     virSecretPtr secret;
     char *passphrase;
     unsigned char *data;
     size_t size;
     int ret = -1;
+    virStorageEncryptionPtr enc;
 
-    virDomainObjLock(vm);
+    if (!disk->encryption) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("disk %s does not have any encryption information"),
+                        disk->src);
+        return -1;
+    }
+    enc = disk->encryption;
 
     if (!conn) {
         qemuReportError(VIR_ERR_NO_SUPPORT,
@@ -754,16 +749,12 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
         goto cleanup;
     }
 
-    enc = findDomainDiskEncryption(vm, path);
-    if (enc == NULL)
-        return -1;
-
     if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
         enc->nsecrets != 1 ||
         enc->secrets[0]->type !=
         VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
         qemuReportError(VIR_ERR_INVALID_DOMAIN,
-                        _("invalid <encryption> for volume %s"), path);
+                        _("invalid <encryption> for volume %s"), disk->src);
         goto cleanup;
     }
 
@@ -782,7 +773,7 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
         VIR_FREE(data);
         qemuReportError(VIR_ERR_INVALID_SECRET,
                         _("format='qcow' passphrase for %s must not contain a "
-                          "'\\0'"), path);
+                          "'\\0'"), disk->src);
         goto cleanup;
     }
 
@@ -804,8 +795,30 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
     ret = 0;
 
 cleanup:
-    virDomainObjUnlock(vm);
+    return ret;
+}
 
+static int
+findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+                         virConnectPtr conn,
+                         virDomainObjPtr vm,
+                         const char *path,
+                         char **secretRet,
+                         size_t *secretLen)
+{
+    virDomainDiskDefPtr disk;
+    int ret = -1;
+
+    virDomainObjLock(vm);
+    disk = findDomainDiskByPath(vm, path);
+
+    if (!disk)
+        goto cleanup;
+
+    ret = getVolumeQcowPassphrase(conn, disk, secretRet, secretLen);
+
+cleanup:
+    virDomainObjUnlock(vm);
     return ret;
 }
 
@@ -1681,8 +1694,10 @@ qemudInitCpuAffinity(virDomainObjPtr vm)
 
 
 static int
-qemuInitPasswords(struct qemud_driver *driver,
-                  virDomainObjPtr vm) {
+qemuInitPasswords(virConnectPtr conn,
+                  struct qemud_driver *driver,
+                  virDomainObjPtr vm,
+                  unsigned long long qemuCmdFlags) {
     int ret = 0;
     qemuDomainObjPrivatePtr priv = vm->privateData;
 
@@ -1698,6 +1713,36 @@ qemuInitPasswords(struct qemud_driver *driver,
         qemuDomainObjExitMonitorWithDriver(driver, vm);
     }
 
+    if (ret < 0)
+        goto cleanup;
+
+    if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+        int i;
+
+        for (i = 0 ; i < vm->def->ndisks ; i++) {
+            char *secret;
+            size_t secretLen;
+
+            if (!vm->def->disks[i]->encryption ||
+                !vm->def->disks[i]->src)
+                continue;
+
+            if (getVolumeQcowPassphrase(conn,
+                                        vm->def->disks[i],
+                                        &secret, &secretLen) < 0)
+                goto cleanup;
+
+            qemuDomainObjEnterMonitorWithDriver(driver, vm);
+            ret = qemuMonitorSetDrivePassphrase(priv->mon,
+                                                vm->def->disks[i]->info.alias,
+                                                secret);
+            qemuDomainObjExitMonitorWithDriver(driver, vm);
+            if (ret < 0)
+                goto cleanup;
+        }
+    }
+
+cleanup:
     return ret;
 }
 
@@ -2721,7 +2766,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
     if (qemudInitCpuAffinity(vm) < 0)
         goto abort;
 
-    if (qemuInitPasswords(driver, vm) < 0)
+    if (qemuInitPasswords(conn, driver, vm, qemuCmdFlags) < 0)
         goto abort;
 
     /* If we have -device, then addresses are assigned explicitly.
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 64c6cba..c1d369b 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1331,3 +1331,18 @@ int qemuMonitorAddDrive(qemuMonitorPtr mon,
         ret = qemuMonitorTextAddDrive(mon, drivestr);
     return ret;
 }
+
+
+int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon,
+                                  const char *alias,
+                                  const char *passphrase)
+{
+    DEBUG("mon=%p, fd=%d alias=%s passphrase=%p(value hidden)", mon, mon->fd, alias, passphrase);
+    int ret;
+
+    if (mon->json)
+        ret = qemuMonitorJSONSetDrivePassphrase(mon, alias, passphrase);
+    else
+        ret = qemuMonitorTextSetDrivePassphrase(mon, alias, passphrase);
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index a330eff..786ad7a 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -291,4 +291,8 @@ int qemuMonitorAddDevice(qemuMonitorPtr mon,
 int qemuMonitorAddDrive(qemuMonitorPtr mon,
                         const char *drivestr);
 
+int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon,
+                                  const char *alias,
+                                  const char *passphrase);
+
 #endif /* QEMU_MONITOR_H */
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 032afef..c9b8d60 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1841,3 +1841,36 @@ int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
     virJSONValueFree(reply);
     return ret;
 }
+
+
+int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
+                                      const char *alias,
+                                      const char *passphrase)
+{
+    int ret;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    char *drive;
+
+    if (virAsprintf(&drive, "%s%s", QEMU_DRIVE_HOST_PREFIX, alias) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    cmd = qemuMonitorJSONMakeCommand("block_passwd",
+                                     "s:device", drive,
+                                     "s:password", passphrase,
+                                     NULL);
+    VIR_FREE(drive);
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index ac6458c..65a70e3 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -162,4 +162,8 @@ int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
 int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
                             const char *drivestr);
 
+int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
+                                      const char *alias,
+                                      const char *passphrase);
+
 #endif /* QEMU_MONITOR_JSON_H */
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index a6a4598..e993699 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -2127,3 +2127,50 @@ cleanup:
     VIR_FREE(safe_str);
     return ret;
 }
+
+
+int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon,
+                                      const char *alias,
+                                      const char *passphrase)
+{
+    char *cmd = NULL;
+    char *reply = NULL;
+    int ret = -1;
+    char *safe_str;
+
+    safe_str = qemuMonitorEscapeArg(passphrase);
+    if (!safe_str) {
+        virReportOOMError();
+        return -1;
+    }
+
+    ret = virAsprintf(&cmd, "block_passwd %s%s \"%s\"", QEMU_DRIVE_HOST_PREFIX, alias, safe_str);
+    if (ret == -1) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        _("failed to close fd in qemu with '%s'"), cmd);
+        goto cleanup;
+    }
+
+    if (strstr(reply, "\nunknown command:")) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                        _("setting disk password is not supported"));
+        goto cleanup;
+    } else if (strstr(reply, "The entered password is invalid")) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                        _("the disk password is incorrect"));
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(cmd);
+    VIR_FREE(reply);
+    VIR_FREE(safe_str);
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 12d75f5..1937e99 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -166,4 +166,8 @@ int qemuMonitorTextAddDevice(qemuMonitorPtr mon,
 int qemuMonitorTextAddDrive(qemuMonitorPtr mon,
                              const char *drivestr);
 
+int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon,
+                                      const char *alias,
+                                      const char *passphrase);
+
 #endif /* QEMU_MONITOR_TEXT_H */
-- 
1.6.5.2




More information about the libvir-list mailing list