Commit 7572150c authored by Gerd Hoffmann's avatar Gerd Hoffmann
Browse files

vnc/spice: add set_passwd monitor command.



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: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 3c9405a0
Loading
Loading
Loading
Loading
+54 −0
Original line number Diff line number Diff line
@@ -1132,6 +1132,60 @@ STEXI
@item block_passwd @var{device} @var{password}
@findex block_passwd
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

    {
+100 −0
Original line number Diff line number Diff line
@@ -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"
@@ -1075,6 +1076,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"));
+57 −0
Original line number Diff line number Diff line
@@ -735,6 +735,63 @@ Example:
                                               "password": "12345" } }
<- { "return": {} }

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

    {
+5 −0
Original line number Diff line number Diff line
@@ -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 */

+35 −0
Original line number Diff line number Diff line
@@ -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);