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