Changes since the second submission:
- Update for changed internal API
- s/secret_id/uuid/g
- Use "unsigned char *" for secret value
---
src/qemu_driver.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 153 insertions(+), 8 deletions(-)
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index b1a1c6a..8404bc1 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)) {
@@ -2587,14 +2581,165 @@ 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;
+ virSecretPtr secret;
+ char *passphrase;
+ unsigned char *data;
+ size_t size;
+
+ if (conn->secretDriver == NULL ||
+ conn->secretDriver->lookupByUUIDString == 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]->uuid == NULL) {
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("missing secret uuid for volume %s"), path);
+ return NULL;
+ }
+ secret = conn->secretDriver->lookupByUUIDString(conn,
+ enc->secrets[0]->uuid);
+ if (secret == NULL)
+ return NULL;
+ data = conn->secretDriver->getValue(secret, &size, true);
+ virUnrefSecret(secret);
+ if (data == NULL)
+ 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);
--
1.6.2.5