[libvirt] [PATCH 0/2] Update qemu password support (v2)

Recently, QEMU learned set_password and expire_password monitor commands. http://qemu.com/qemu.git/commit/?id=7572150c189c6553c2448334116ab717680de66d The following patches make use of these new commands if available, and fallback on VNC-only password support. Change since v1: - moved qemuInitGraphicsPasswords to qemu_hotplug, renamed to qemuDomainChangeGraphicsPasswords. - updated what looks like a typo (that appears to work anyway) in initial patch from Daniel: - ret = qemuInitGraphicsPasswords(driver, vm, - VIR_DOMAIN_GRAPHICS_TYPE_SPICE, - &vm->def->graphics[0]->data.vnc.auth, - driver->vncPassword); + ret = qemuInitGraphicsPasswords(driver, vm, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, + &vm->def->graphics[0]->data.spice.auth, + driver->spicePassword); Marc-André Lureau (2): qemu: add set_password and expire_password monitor commands Use the new set_password monitor command to set password. src/qemu/qemu_driver.c | 22 ++++---- src/qemu/qemu_hotplug.c | 114 +++++++++++++++++++++++++++++++++++++++--- src/qemu/qemu_hotplug.h | 5 ++ src/qemu/qemu_monitor.c | 77 ++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 7 +++ src/qemu/qemu_monitor_json.c | 56 ++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 +++ src/qemu/qemu_monitor_text.c | 69 +++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 7 +++ 9 files changed, 346 insertions(+), 18 deletions(-) -- 1.7.3.4

From: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_monitor.c | 77 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 7 ++++ src/qemu/qemu_monitor_json.c | 56 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 ++++ src/qemu/qemu_monitor_text.c | 69 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 7 ++++ 6 files changed, 223 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6ad894d..49a4b60 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1097,6 +1097,83 @@ int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, return ret; } +static const char* qemuMonitorTypeToProtocol(int type) +{ + switch (type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + return "vnc"; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + return "spice"; + default: + qemuReportError(VIR_ERR_INVALID_ARG, + _("unsupported protocol type %s"), + virDomainGraphicsTypeToString(type)); + return NULL; + } +} + +/* Returns -2 if not supported with this monitor connection */ +int qemuMonitorSetPassword(qemuMonitorPtr mon, + int type, + const char *password, + const char *action_if_connected) +{ + const char *protocol = qemuMonitorTypeToProtocol(type); + int ret; + + if (!protocol) + return -1; + + DEBUG("mon=%p, protocol=%s, password=%p, action_if_connected=%s", + mon, protocol, password, action_if_connected); + + if (!mon) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!password) + password = ""; + + if (!action_if_connected) + action_if_connected = "keep"; + + if (mon->json) + ret = qemuMonitorJSONSetPassword(mon, protocol, password, action_if_connected); + else + ret = qemuMonitorTextSetPassword(mon, protocol, password, action_if_connected); + return ret; +} + +int qemuMonitorExpirePassword(qemuMonitorPtr mon, + int type, + const char *expire_time) +{ + const char *protocol = qemuMonitorTypeToProtocol(type); + int ret; + + if (!protocol) + return -1; + + DEBUG("mon=%p, protocol=%s, expire_time=%s", + mon, protocol, expire_time); + + if (!mon) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!expire_time) + expire_time = "now"; + + if (mon->json) + ret = qemuMonitorJSONExpirePassword(mon, protocol, expire_time); + else + ret = qemuMonitorTextExpirePassword(mon, protocol, expire_time); + return ret; +} int qemuMonitorSetBalloon(qemuMonitorPtr mon, unsigned long newmem) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3ac5024..b5be362 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -195,6 +195,13 @@ int qemuMonitorGetBlockExtent(qemuMonitorPtr mon, int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password); +int qemuMonitorSetPassword(qemuMonitorPtr mon, + int type, + const char *password, + const char *action_if_connected); +int qemuMonitorExpirePassword(qemuMonitorPtr mon, + int type, + const char *expire_time); int qemuMonitorSetBalloon(qemuMonitorPtr mon, unsigned long newmem); int qemuMonitorSetCPU(qemuMonitorPtr mon, int cpu, int online); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 7877731..7387089 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1265,6 +1265,62 @@ int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, return ret; } +/* Returns -1 on error, -2 if not supported */ +int qemuMonitorJSONSetPassword(qemuMonitorPtr mon, + const char *protocol, + const char *password, + const char *action_if_connected) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("set_password", + "s:protocol", protocol, + "s:password", password, + "s:connected", action_if_connected, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) { + if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { + ret = -2; + goto cleanup; + } + + ret = qemuMonitorJSONCheckError(cmd, reply); + } + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + +int qemuMonitorJSONExpirePassword(qemuMonitorPtr mon, + const char *protocol, + const char *expire_time) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("expire_password", + "s:protocol", protocol, + "s:time", expire_time, + NULL); + virJSONValuePtr reply = NULL; + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + /* * Returns: 0 if balloon not supported, +1 if balloon adjust worked * or -1 on failure diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 8d96146..4c47f10 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -63,6 +63,13 @@ int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon, int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, const char *password); +int qemuMonitorJSONSetPassword(qemuMonitorPtr mon, + const char *protocol, + const char *password, + const char *action_if_connected); +int qemuMonitorJSONExpirePassword(qemuMonitorPtr mon, + const char *protocol, + const char *expire_time); int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, unsigned long newmem); int qemuMonitorJSONSetCPU(qemuMonitorPtr mon, int cpu, int online); diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 11a9391..291d958 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -768,6 +768,75 @@ int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, return 0; } +/* Returns -1 on error, -2 if not supported */ +int qemuMonitorTextSetPassword(qemuMonitorPtr mon, + const char *protocol, + const char *password, + const char *action_if_connected) +{ + char *cmd = NULL; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "set_password %s \"%s\" %s", + protocol, password, action_if_connected) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("setting password failed")); + goto cleanup; + } + + if (strstr(reply, "unknown command:")) { + ret = -2; + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(reply); + VIR_FREE(cmd); + return ret; +} + +int qemuMonitorTextExpirePassword(qemuMonitorPtr mon, + const char *protocol, + const char *expire_time) +{ + char *cmd = NULL; + char *reply = NULL; + int ret = -1; + + if (virAsprintf(&cmd, "expire_password %s %s", + protocol, expire_time) < 0) { + virReportOOMError(); + goto cleanup; + } + + if (qemuMonitorCommand(mon, cmd, &reply) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("expiring password failed")); + goto cleanup; + } + + if (strstr(reply, "unknown command:")) { + qemuReportError(VIR_ERR_NO_SUPPORT, + _("expiring password not supported by this qemu: %s"), reply); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(reply); + VIR_FREE(cmd); + return ret; +} + /* * Returns: 0 if balloon not supported, +1 if balloon adjust worked * or -1 on failure diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 57d6e9b..b29dbcc 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -61,6 +61,13 @@ int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon, int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, const char *password); +int qemuMonitorTextSetPassword(qemuMonitorPtr mon, + const char *protocol, + const char *password, + const char *action_if_connected); +int qemuMonitorTextExpirePassword(qemuMonitorPtr mon, + const char *protocol, + const char *expire_time); int qemuMonitorTextSetBalloon(qemuMonitorPtr mon, unsigned long newmem); int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, int online); -- 1.7.3.4

On Mon, Jan 10, 2011 at 12:12:32PM +0100, Marc-André Lureau wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
--- src/qemu/qemu_monitor.c | 77 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 7 ++++ src/qemu/qemu_monitor_json.c | 56 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 ++++ src/qemu/qemu_monitor_text.c | 69 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 7 ++++ 6 files changed, 223 insertions(+), 0 deletions(-)
ACK Daniel

From: Marc-André Lureau <marcandre.lureau@redhat.com> We try to use that command first when setting a VNC/SPICE password. If that doesn't work we fallback to the legacy VNC only password Allow an expiry time to be set, if that doesn't work, throw an error if they try to use SPICE. Change since v1: - moved qemuInitGraphicsPasswords to qemu_hotplug, renamed to qemuDomainChangeGraphicsPasswords. - updated what looks like a typo (that appears to work anyway) in initial patch from Daniel: - ret = qemuInitGraphicsPasswords(driver, vm, - VIR_DOMAIN_GRAPHICS_TYPE_SPICE, - &vm->def->graphics[0]->data.vnc.auth, - driver->vncPassword); + ret = qemuInitGraphicsPasswords(driver, vm, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, + &vm->def->graphics[0]->data.spice.auth, + driver->spicePassword); Based on patch by Daniel P. Berrange <berrange@redhat.com>. --- src/qemu/qemu_driver.c | 22 +++++---- src/qemu/qemu_hotplug.c | 114 +++++++++++++++++++++++++++++++++++++++++++--- src/qemu/qemu_hotplug.h | 5 ++ 3 files changed, 123 insertions(+), 18 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e915705..2f1e741 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1999,16 +1999,18 @@ qemuInitPasswords(virConnectPtr conn, int ret = 0; qemuDomainObjPrivatePtr priv = vm->privateData; - if ((vm->def->ngraphics == 1) && - vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - (vm->def->graphics[0]->data.vnc.auth.passwd || driver->vncPassword)) { - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - ret = qemuMonitorSetVNCPassword(priv->mon, - vm->def->graphics[0]->data.vnc.auth.passwd ? - vm->def->graphics[0]->data.vnc.auth.passwd : - driver->vncPassword); - qemuDomainObjExitMonitorWithDriver(driver, vm); + if (vm->def->ngraphics == 1) { + if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + ret = qemuDomainChangeGraphicsPasswords(driver, vm, + VIR_DOMAIN_GRAPHICS_TYPE_VNC, + &vm->def->graphics[0]->data.vnc.auth, + driver->vncPassword); + } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + ret = qemuDomainChangeGraphicsPasswords(driver, vm, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, + &vm->def->graphics[0]->data.spice.auth, + driver->spicePassword); + } } if (ret < 0) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 827bcaf..d153c0d 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1038,7 +1038,6 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, virDomainGraphicsDefPtr dev) { virDomainGraphicsDefPtr olddev = qemuDomainFindGraphics(vm, dev); - qemuDomainObjPrivatePtr priv = vm->privateData; int ret = -1; if (!olddev) { @@ -1066,24 +1065,65 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, return -1; } - if (STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, dev->data.vnc.auth.passwd)) { + /* If a password lifetime was, or is set, then we must always run, + * even if new password matches old password */ + if (olddev->data.vnc.auth.expires || + dev->data.vnc.auth.expires || + STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, dev->data.vnc.auth.passwd)) { VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.auth.passwd, driver->vncPassword); - qemuDomainObjEnterMonitorWithDriver(driver, vm); - ret = qemuMonitorSetVNCPassword(priv->mon, - dev->data.vnc.auth.passwd ? - dev->data.vnc.auth.passwd : - driver->vncPassword); - qemuDomainObjExitMonitorWithDriver(driver, vm); + ret = qemuDomainChangeGraphicsPasswords(driver, vm, VIR_DOMAIN_GRAPHICS_TYPE_VNC, + &dev->data.vnc.auth, driver->vncPassword); /* Steal the new dev's char * reference */ VIR_FREE(olddev->data.vnc.auth.passwd); olddev->data.vnc.auth.passwd = dev->data.vnc.auth.passwd; dev->data.vnc.auth.passwd = NULL; + olddev->data.vnc.auth.validTo = dev->data.vnc.auth.validTo; + olddev->data.vnc.auth.expires = dev->data.vnc.auth.expires; } else { ret = 0; } break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if ((olddev->data.spice.autoport != dev->data.spice.autoport) || + (!dev->data.spice.autoport && (olddev->data.spice.port != dev->data.spice.port)) || + (!dev->data.spice.autoport && (olddev->data.spice.tlsPort != dev->data.spice.tlsPort))) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change port settings on spice graphics")); + return -1; + } + if (STRNEQ_NULLABLE(olddev->data.spice.listenAddr, dev->data.spice.listenAddr)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change listen address setting on spice graphics")); + return -1; + } + if (STRNEQ_NULLABLE(olddev->data.spice.keymap, dev->data.spice.keymap)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change keymap setting on spice graphics")); + return -1; + } + + /* If a password lifetime was, or is set, then we must always run, + * even if new password matches old password */ + if (olddev->data.spice.auth.expires || + dev->data.spice.auth.expires || + STRNEQ_NULLABLE(olddev->data.spice.auth.passwd, dev->data.spice.auth.passwd)) { + VIR_DEBUG("Updating password on SPICE server %p %p", dev->data.spice.auth.passwd, driver->spicePassword); + ret = qemuDomainChangeGraphicsPasswords(driver, vm, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, + &dev->data.spice.auth, driver->spicePassword); + + /* Steal the new dev's char * reference */ + VIR_FREE(olddev->data.spice.auth.passwd); + olddev->data.spice.auth.passwd = dev->data.spice.auth.passwd; + dev->data.spice.auth.passwd = NULL; + olddev->data.spice.auth.validTo = dev->data.spice.auth.validTo; + olddev->data.spice.auth.expires = dev->data.spice.auth.expires; + } else { + VIR_DEBUG0("Not updating since password didn't change"); + ret = 0; + } + default: qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unable to change config on '%s' graphics type"), @@ -1707,3 +1747,61 @@ int qemuDomainDetachHostDevice(struct qemud_driver *driver, return ret; } + +int +qemuDomainChangeGraphicsPasswords(struct qemud_driver *driver, + virDomainObjPtr vm, + int type, + virDomainGraphicsAuthDefPtr auth, + const char *defaultPasswd) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + time_t now = time(NULL); + char expire_time [64]; + int ret; + + if (!auth->passwd && !driver->vncPassword) + return 0; + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSetPassword(priv->mon, + type, + auth->passwd ? auth->passwd : defaultPasswd, + NULL); + + if (ret == -2) { + if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Only VNC graphics are supported")); + ret = -1; + } else { + ret = qemuMonitorSetVNCPassword(priv->mon, + auth->passwd ? auth->passwd : defaultPasswd); + } + } + + if (auth->expires) { + time_t lifetime = auth->validTo - now; + if (lifetime <= 0) + snprintf(expire_time, sizeof (expire_time), "now"); + else + snprintf(expire_time, sizeof (expire_time), "%lu", (long unsigned)auth->validTo); + } else { + snprintf(expire_time, sizeof (expire_time), "never"); + } + + ret = qemuMonitorExpirePassword(priv->mon, type, expire_time); + + if (ret == -2) { + /* XXX we could fake this with a timer */ + if (auth->expires) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expiry of passwords is not supported")); + ret = -1; + } + } + + qemuDomainObjExitMonitorWithDriver(driver, vm); + + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 0e7ca8e..217785d 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -70,6 +70,11 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver, int qemuDomainChangeGraphics(struct qemud_driver *driver, virDomainObjPtr vm, virDomainGraphicsDefPtr dev); +int qemuDomainChangeGraphicsPasswords(struct qemud_driver *driver, + virDomainObjPtr vm, + int type, + virDomainGraphicsAuthDefPtr auth, + const char *defaultPasswd); int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev, -- 1.7.3.4

On Mon, Jan 10, 2011 at 12:12:33PM +0100, Marc-André Lureau wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
We try to use that command first when setting a VNC/SPICE password. If that doesn't work we fallback to the legacy VNC only password
Allow an expiry time to be set, if that doesn't work, throw an error if they try to use SPICE.
Change since v1: - moved qemuInitGraphicsPasswords to qemu_hotplug, renamed to qemuDomainChangeGraphicsPasswords. - updated what looks like a typo (that appears to work anyway) in initial patch from Daniel: - ret = qemuInitGraphicsPasswords(driver, vm, - VIR_DOMAIN_GRAPHICS_TYPE_SPICE, - &vm->def->graphics[0]->data.vnc.auth, - driver->vncPassword); + ret = qemuInitGraphicsPasswords(driver, vm, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, + &vm->def->graphics[0]->data.spice.auth, + driver->spicePassword);
Based on patch by Daniel P. Berrange <berrange@redhat.com>. --- src/qemu/qemu_driver.c | 22 +++++---- src/qemu/qemu_hotplug.c | 114 +++++++++++++++++++++++++++++++++++++++++++--- src/qemu/qemu_hotplug.h | 5 ++ 3 files changed, 123 insertions(+), 18 deletions(-)
ACK Daniel

On 01/14/2011 11:59 AM, Daniel P. Berrange wrote:
On Mon, Jan 10, 2011 at 12:12:33PM +0100, Marc-André Lureau wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
We try to use that command first when setting a VNC/SPICE password. If that doesn't work we fallback to the legacy VNC only password
Allow an expiry time to be set, if that doesn't work, throw an error if they try to use SPICE.
ACK
I've pushed both patches, and updated AUTHORS. Thanks for the work! -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (3)
-
Daniel P. Berrange
-
Eric Blake
-
Marc-André Lureau