Support 'block_passwd' command for QEMU disk encryption
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.
This commit is contained in:
parent
dfab48ac54
commit
c31a116b72
@ -694,51 +694,46 @@ qemuHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static virStorageEncryptionPtr
|
static virDomainDiskDefPtr
|
||||||
findDomainDiskEncryption(virDomainObjPtr vm,
|
findDomainDiskByPath(virDomainObjPtr vm,
|
||||||
const char *path)
|
const char *path)
|
||||||
{
|
{
|
||||||
bool seen_volume;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
seen_volume = false;
|
|
||||||
for (i = 0; i < vm->def->ndisks; i++) {
|
for (i = 0; i < vm->def->ndisks; i++) {
|
||||||
virDomainDiskDefPtr disk;
|
virDomainDiskDefPtr disk;
|
||||||
|
|
||||||
disk = vm->def->disks[i];
|
disk = vm->def->disks[i];
|
||||||
if (disk->src != NULL && STREQ(disk->src, path)) {
|
if (disk->src != NULL && STREQ(disk->src, path))
|
||||||
seen_volume = true;
|
return disk;
|
||||||
if (disk->encryption != NULL)
|
|
||||||
return disk->encryption;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (seen_volume)
|
|
||||||
qemuReportError(VIR_ERR_INVALID_DOMAIN,
|
|
||||||
_("missing <encryption> for volume %s"), path);
|
|
||||||
else
|
|
||||||
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
_("unexpected passphrase request for volume %s"),
|
_("no disk found with path %s"),
|
||||||
path);
|
path);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
getVolumeQcowPassphrase(virConnectPtr conn,
|
||||||
virConnectPtr conn,
|
virDomainDiskDefPtr disk,
|
||||||
virDomainObjPtr vm,
|
|
||||||
const char *path,
|
|
||||||
char **secretRet,
|
char **secretRet,
|
||||||
size_t *secretLen)
|
size_t *secretLen)
|
||||||
{
|
{
|
||||||
virStorageEncryptionPtr enc;
|
|
||||||
virSecretPtr secret;
|
virSecretPtr secret;
|
||||||
char *passphrase;
|
char *passphrase;
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
int ret = -1;
|
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) {
|
if (!conn) {
|
||||||
qemuReportError(VIR_ERR_NO_SUPPORT,
|
qemuReportError(VIR_ERR_NO_SUPPORT,
|
||||||
@ -754,16 +749,12 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
enc = findDomainDiskEncryption(vm, path);
|
|
||||||
if (enc == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
|
if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
|
||||||
enc->nsecrets != 1 ||
|
enc->nsecrets != 1 ||
|
||||||
enc->secrets[0]->type !=
|
enc->secrets[0]->type !=
|
||||||
VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
|
VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
|
||||||
qemuReportError(VIR_ERR_INVALID_DOMAIN,
|
qemuReportError(VIR_ERR_INVALID_DOMAIN,
|
||||||
_("invalid <encryption> for volume %s"), path);
|
_("invalid <encryption> for volume %s"), disk->src);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,7 +773,7 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|||||||
VIR_FREE(data);
|
VIR_FREE(data);
|
||||||
qemuReportError(VIR_ERR_INVALID_SECRET,
|
qemuReportError(VIR_ERR_INVALID_SECRET,
|
||||||
_("format='qcow' passphrase for %s must not contain a "
|
_("format='qcow' passphrase for %s must not contain a "
|
||||||
"'\\0'"), path);
|
"'\\0'"), disk->src);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,8 +795,30 @@ findVolumeQcowPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1681,8 +1694,10 @@ qemudInitCpuAffinity(virDomainObjPtr vm)
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuInitPasswords(struct qemud_driver *driver,
|
qemuInitPasswords(virConnectPtr conn,
|
||||||
virDomainObjPtr vm) {
|
struct qemud_driver *driver,
|
||||||
|
virDomainObjPtr vm,
|
||||||
|
unsigned long long qemuCmdFlags) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
qemuDomainObjPrivatePtr priv = vm->privateData;
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
||||||
|
|
||||||
@ -1698,6 +1713,36 @@ qemuInitPasswords(struct qemud_driver *driver,
|
|||||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2721,7 +2766,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
|
|||||||
if (qemudInitCpuAffinity(vm) < 0)
|
if (qemudInitCpuAffinity(vm) < 0)
|
||||||
goto abort;
|
goto abort;
|
||||||
|
|
||||||
if (qemuInitPasswords(driver, vm) < 0)
|
if (qemuInitPasswords(conn, driver, vm, qemuCmdFlags) < 0)
|
||||||
goto abort;
|
goto abort;
|
||||||
|
|
||||||
/* If we have -device, then addresses are assigned explicitly.
|
/* If we have -device, then addresses are assigned explicitly.
|
||||||
|
@ -1331,3 +1331,18 @@ int qemuMonitorAddDrive(qemuMonitorPtr mon,
|
|||||||
ret = qemuMonitorTextAddDrive(mon, drivestr);
|
ret = qemuMonitorTextAddDrive(mon, drivestr);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
@ -291,4 +291,8 @@ int qemuMonitorAddDevice(qemuMonitorPtr mon,
|
|||||||
int qemuMonitorAddDrive(qemuMonitorPtr mon,
|
int qemuMonitorAddDrive(qemuMonitorPtr mon,
|
||||||
const char *drivestr);
|
const char *drivestr);
|
||||||
|
|
||||||
|
int qemuMonitorSetDrivePassphrase(qemuMonitorPtr mon,
|
||||||
|
const char *alias,
|
||||||
|
const char *passphrase);
|
||||||
|
|
||||||
#endif /* QEMU_MONITOR_H */
|
#endif /* QEMU_MONITOR_H */
|
||||||
|
@ -1841,3 +1841,36 @@ int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
|
|||||||
virJSONValueFree(reply);
|
virJSONValueFree(reply);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
@ -162,4 +162,8 @@ int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
|
|||||||
int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
|
int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
|
||||||
const char *drivestr);
|
const char *drivestr);
|
||||||
|
|
||||||
|
int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
|
||||||
|
const char *alias,
|
||||||
|
const char *passphrase);
|
||||||
|
|
||||||
#endif /* QEMU_MONITOR_JSON_H */
|
#endif /* QEMU_MONITOR_JSON_H */
|
||||||
|
@ -2127,3 +2127,50 @@ cleanup:
|
|||||||
VIR_FREE(safe_str);
|
VIR_FREE(safe_str);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
|
@ -166,4 +166,8 @@ int qemuMonitorTextAddDevice(qemuMonitorPtr mon,
|
|||||||
int qemuMonitorTextAddDrive(qemuMonitorPtr mon,
|
int qemuMonitorTextAddDrive(qemuMonitorPtr mon,
|
||||||
const char *drivestr);
|
const char *drivestr);
|
||||||
|
|
||||||
|
int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon,
|
||||||
|
const char *alias,
|
||||||
|
const char *passphrase);
|
||||||
|
|
||||||
#endif /* QEMU_MONITOR_TEXT_H */
|
#endif /* QEMU_MONITOR_TEXT_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user