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