This patch adds new set_password and expire_password monitor commands
which allows to change and expire the password for spice and vnc
connections.  See the doc update patch chunk for details.

Signed-off-by: Gerd Hoffmann <kra...@redhat.com>
---
 hmp-commands.hx |   54 +++++++++++++++++++++++++++++
 monitor.c       |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qmp-commands.hx |   57 +++++++++++++++++++++++++++++++
 ui/qemu-spice.h |    5 +++
 ui/spice-core.c |   35 +++++++++++++++++++
 5 files changed, 251 insertions(+), 0 deletions(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index e5585ba..9632ee8 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1117,6 +1117,60 @@ Set the encrypted device @var{device} password to 
@var{password}
 ETEXI
 
     {
+        .name       = "set_password",
+        .args_type  = "protocol:s,password:s,connected:s?",
+        .params     = "protocol password action-if-connected",
+        .help       = "set spice/vnc password",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = set_password,
+    },
+
+STEXI
+...@item set_password [ vnc | spice ] password [ action-if-connected ]
+...@findex set_password
+
+Change spice/vnc password.  Use zero to make the password stay valid
+forever.  @var{action-if-connected} specifies what should happen in
+case a connection is established: @var{fail} makes the password change
+fail.  @var{disconnect} changes the password and disconnects the
+client.  @var{keep} changes the password and keeps the connection up.
+...@var{keep} is the default.
+ETEXI
+
+    {
+        .name       = "expire_password",
+        .args_type  = "protocol:s,time:s",
+        .params     = "protocol time",
+        .help       = "set spice/vnc password expire-time",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = expire_password,
+    },
+
+STEXI
+...@item expire_password [ vnc | spice ] expire-time
+...@findex expire_password
+
+Specify when a password for spice/vnc becomes
+invalid. @var{expire-time} accepts:
+
+...@table @var
+...@item now
+Invalidate password instantly.
+
+...@item never
+Password stays valid forever.
+
+...@item +nsec
+Password stays valid for @var{nsec} seconds starting now.
+
+...@item nsec
+Password is invalidated at the given time.  @var{nsec} are the seconds
+passed since 1970, i.e. unix epoch.
+
+...@end table
+ETEXI
+
+    {
         .name       = "info",
         .args_type  = "item:s?",
         .params     = "[subcommand]",
diff --git a/monitor.c b/monitor.c
index bfeec0f..7b68df6 100644
--- a/monitor.c
+++ b/monitor.c
@@ -34,6 +34,7 @@
 #include "net.h"
 #include "net/slirp.h"
 #include "qemu-char.h"
+#include "ui/qemu-spice.h"
 #include "sysemu.h"
 #include "monitor.h"
 #include "readline.h"
@@ -1037,6 +1038,105 @@ static int do_change(Monitor *mon, const QDict *qdict, 
QObject **ret_data)
     return ret;
 }
 
+static int set_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+    const char *protocol  = qdict_get_str(qdict, "protocol");
+    const char *password  = qdict_get_str(qdict, "password");
+    const char *connected = qdict_get_try_str(qdict, "connected");
+    int disconnect_if_connected = 0;
+    int fail_if_connected = 0;
+    int rc;
+
+    if (connected) {
+        if (strcmp(connected, "fail") == 0) {
+            fail_if_connected = 1;
+        } else if (strcmp(connected, "disconnect") == 0) {
+            disconnect_if_connected = 1;
+        } else if (strcmp(connected, "keep") == 0) {
+            /* nothing */
+        } else {
+            qerror_report(QERR_INVALID_PARAMETER, "connected");
+            return -1;
+        }
+    }
+
+    if (strcmp(protocol, "spice") == 0) {
+        if (!using_spice) {
+            /* correct one? spice isn't a device ,,, */
+            qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+            return -1;
+        }
+        rc = qemu_spice_set_passwd(password, fail_if_connected,
+                                   disconnect_if_connected);
+        if (rc != 0) {
+            qerror_report(QERR_SET_PASSWD_FAILED);
+            return -1;
+        }
+        return 0;
+    }
+
+    if (strcmp(protocol, "vnc") == 0) {
+        if (fail_if_connected || disconnect_if_connected) {
+            /* vnc supports "connected=keep" only */
+            qerror_report(QERR_INVALID_PARAMETER, "connected");
+            return -1;
+        }
+        rc = vnc_display_password(NULL, password);
+        if (rc != 0) {
+            qerror_report(QERR_SET_PASSWD_FAILED);
+            return -1;
+        }
+        return 0;
+    }
+
+    qerror_report(QERR_INVALID_PARAMETER, "protocol");
+    return -1;
+}
+
+static int expire_password(Monitor *mon, const QDict *qdict, QObject 
**ret_data)
+{
+    const char *protocol  = qdict_get_str(qdict, "protocol");
+    const char *whenstr = qdict_get_str(qdict, "time");
+    time_t when;
+    int rc;
+
+    if (strcmp(whenstr, "now")) {
+        when = 0;
+    } else if (strcmp(whenstr, "never")) {
+        when = TIME_MAX;
+    } else if (whenstr[0] == '+') {
+        when = time(NULL) + strtoull(whenstr+1, NULL, 10);
+    } else {
+        when = strtoull(whenstr, NULL, 10);
+    }
+
+    if (strcmp(protocol, "spice") == 0) {
+        if (!using_spice) {
+            /* correct one? spice isn't a device ,,, */
+            qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+            return -1;
+        }
+        rc = qemu_spice_set_pw_expire(when);
+        if (rc != 0) {
+            qerror_report(QERR_SET_PASSWD_FAILED);
+            return -1;
+        }
+        return 0;
+    }
+
+    if (strcmp(protocol, "vnc") == 0) {
+        rc = vnc_display_pw_expire(NULL, when);
+        if (rc != 0) {
+            qerror_report(QERR_SET_PASSWD_FAILED);
+            return -1;
+        }
+        return 0;
+    }
+
+    qerror_report(QERR_INVALID_PARAMETER, "protocol");
+    return -1;
+}
+
 static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
     vga_hw_screen_dump(qdict_get_str(qdict, "filename"));
diff --git a/qmp-commands.hx b/qmp-commands.hx
index b2f4fe5..125e6e9 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -738,6 +738,63 @@ Example:
 EQMP
 
     {
+        .name       = "set_password",
+        .args_type  = "protocol:s,password:s,connected:s?",
+        .params     = "protocol password action-if-connected",
+        .help       = "set spice/vnc password",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = set_password,
+    },
+
+SQMP
+set_password
+------------
+
+Set the password for vnc/spice protocols.
+
+Arguments:
+
+- "protocol": protocol name (json-string)
+- "password": password (json-string)
+- "connected": [ keep | disconnect | fail ] (josn-string, optional)
+
+Example:
+
+-> { "execute": "set_password", "arguments": { "protocol": "vnc",
+                                               "password": "secret" } }
+<- { "return": {} }
+
+EQMP
+
+    {
+        .name       = "expire_password",
+        .args_type  = "protocol:s,time:s",
+        .params     = "protocol time",
+        .help       = "set spice/vnc password expire-time",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = expire_password,
+    },
+
+SQMP
+expire_password
+---------------
+
+Set the password expire time for vnc/spice protocols.
+
+Arguments:
+
+- "protocol": protocol name (json-string)
+- "time": [ now | never | +secs | secs ] (json-string)
+
+Example:
+
+-> { "execute": "expire_password", "arguments": { "protocol": "vnc",
+                                                  "time": "+60" } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "qmp_capabilities",
         .args_type  = "",
         .params     = "",
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
index 8b23ac9..48239c3 100644
--- a/ui/qemu-spice.h
+++ b/ui/qemu-spice.h
@@ -32,6 +32,9 @@ void qemu_spice_input_init(void);
 void qemu_spice_audio_init(void);
 void qemu_spice_display_init(DisplayState *ds);
 int qemu_spice_add_interface(SpiceBaseInstance *sin);
+int qemu_spice_set_passwd(const char *passwd,
+                          bool fail_if_connected, bool 
disconnect_if_connected);
+int qemu_spice_set_pw_expire(time_t expires);
 
 void do_info_spice_print(Monitor *mon, const QObject *data);
 void do_info_spice(Monitor *mon, QObject **ret_data);
@@ -39,6 +42,8 @@ void do_info_spice(Monitor *mon, QObject **ret_data);
 #else  /* CONFIG_SPICE */
 
 #define using_spice 0
+#define qemu_spice_set_passwd(_p, _f1, _f2) (-1)
+#define qemu_spice_set_pw_expire(_e) (-1)
 
 #endif /* CONFIG_SPICE */
 
diff --git a/ui/spice-core.c b/ui/spice-core.c
index d29d203..27a1ced 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -36,6 +36,8 @@
 
 static SpiceServer *spice_server;
 static const char *auth = "spice";
+static char *auth_passwd;
+static time_t auth_expires = TIME_MAX;
 int using_spice = 0;
 
 struct SpiceTimer {
@@ -599,6 +601,39 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin)
     return spice_server_add_interface(spice_server, sin);
 }
 
+static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
+{
+    time_t lifetime, now = time(NULL);
+    char *passwd;
+
+    if (now < auth_expires) {
+        passwd = auth_passwd;
+        lifetime = (auth_expires - now);
+        if (lifetime > INT_MAX) {
+            lifetime = INT_MAX;
+        }
+    } else {
+        passwd = NULL;
+        lifetime = 1;
+    }
+    return spice_server_set_ticket(spice_server, passwd, lifetime,
+                                   fail_if_conn, disconnect_if_conn);
+}
+
+int qemu_spice_set_passwd(const char *passwd,
+                          bool fail_if_conn, bool disconnect_if_conn)
+{
+    free(auth_passwd);
+    auth_passwd = strdup(passwd);
+    return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
+}
+
+int qemu_spice_set_pw_expire(time_t expires)
+{
+    auth_expires = expires;
+    return qemu_spice_set_ticket(false, false);
+}
+
 static void spice_register_config(void)
 {
     qemu_add_opts(&qemu_spice_opts);
-- 
1.7.1


Reply via email to