提交 79d8b1dc 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/kraxel/tags/ui-20190311-v2-pull-request' into staging

curses: wide char input support.
vnc: acl update, stall fix.

# gpg: Signature made Mon 11 Mar 2019 08:25:24 GMT
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/ui-20190311-v2-pull-request:
  monitor: deprecate acl_show, acl_reset, acl_policy, acl_add, acl_remove
  vnc: allow specifying a custom authorization object name
  vnc: fix update stalls
  curses: support wide input
  Reduce curses escdelay from 1s to 25ms
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
......@@ -2032,6 +2032,19 @@ static QAuthZList *find_auth(Monitor *mon, const char *name)
return QAUTHZ_LIST(obj);
}
static bool warn_acl;
static void hmp_warn_acl(void)
{
if (warn_acl) {
return;
}
error_report("The acl_show, acl_reset, acl_policy, acl_add, acl_remove "
"commands are deprecated with no replacement. Authorization "
"for VNC should be performed using the pluggable QAuthZ "
"objects");
warn_acl = true;
}
static void hmp_acl_show(Monitor *mon, const QDict *qdict)
{
const char *aclname = qdict_get_str(qdict, "aclname");
......@@ -2039,6 +2052,8 @@ static void hmp_acl_show(Monitor *mon, const QDict *qdict)
QAuthZListRuleList *rules;
size_t i = 0;
hmp_warn_acl();
if (!auth) {
return;
}
......@@ -2062,6 +2077,8 @@ static void hmp_acl_reset(Monitor *mon, const QDict *qdict)
const char *aclname = qdict_get_str(qdict, "aclname");
QAuthZList *auth = find_auth(mon, aclname);
hmp_warn_acl();
if (!auth) {
return;
}
......@@ -2080,6 +2097,8 @@ static void hmp_acl_policy(Monitor *mon, const QDict *qdict)
int val;
Error *err = NULL;
hmp_warn_acl();
if (!auth) {
return;
}
......@@ -2124,6 +2143,8 @@ static void hmp_acl_add(Monitor *mon, const QDict *qdict)
QAuthZListFormat format;
size_t i = 0;
hmp_warn_acl();
if (!auth) {
return;
}
......@@ -2169,6 +2190,8 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict)
QAuthZList *auth = find_auth(mon, aclname);
ssize_t i = 0;
hmp_warn_acl();
if (!auth) {
return;
}
......
......@@ -60,6 +60,11 @@ Support for invalid topologies will be removed, the user must ensure
topologies described with -smp include all possible cpus, i.e.
@math{@var{sockets} * @var{cores} * @var{threads} = @var{maxcpus}}.
@subsection -vnc acl (since 4.0.0)
The @code{acl} option to the @code{-vnc} argument has been replaced
by the @code{tls-authz} and @code{sasl-authz} options.
@section QEMU Machine Protocol (QMP) commands
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
......@@ -99,6 +104,12 @@ The @option{[hub_id name]} parameter tuple of the 'hostfwd_add' and
Use ``device_add'' for hotplugging vCPUs instead of ``cpu-add''. See
documentation of ``query-hotpluggable-cpus'' for additional details.
@subsection acl_show, acl_reset, acl_policy, acl_add, acl_remove (since 4.0.0)
The ``acl_show'', ``acl_reset'', ``acl_policy'', ``acl_add'', and
``acl_remove'' commands are deprecated with no replacement. Authorization
for VNC should be performed using the pluggable QAuthZ objects.
@section System emulator devices
@subsection bluetooth (since 3.1)
......
......@@ -1624,6 +1624,14 @@ will cause the VNC server socket to enable the VeNCrypt auth
mechanism. The credentials should have been previously created
using the @option{-object tls-creds} argument.
@item tls-authz=@var{ID}
Provides the ID of the QAuthZ authorization object against which
the client's x509 distinguished name will validated. This object is
only resolved at time of use, so can be deleted and recreated on the
fly while the VNC server is active. If missing, it will default
to denying access.
@item sasl
Require that the client use SASL to authenticate with the VNC server.
......@@ -1639,18 +1647,25 @@ ensures a data encryption preventing compromise of authentication
credentials. See the @ref{vnc_security} section for details on using
SASL authentication.
@item sasl-authz=@var{ID}
Provides the ID of the QAuthZ authorization object against which
the client's SASL username will validated. This object is
only resolved at time of use, so can be deleted and recreated on the
fly while the VNC server is active. If missing, it will default
to denying access.
@item acl
Turn on access control lists for checking of the x509 client certificate
and SASL party. For x509 certs, the ACL check is made against the
certificate's distinguished name. This is something that looks like
@code{C=GB,O=ACME,L=Boston,CN=bob}. For SASL party, the ACL check is
made against the username, which depending on the SASL plugin, may
include a realm component, eg @code{bob} or @code{bob@@EXAMPLE.COM}.
When the @option{acl} flag is set, the initial access list will be
empty, with a @code{deny} policy. Thus no one will be allowed to
use the VNC server until the ACLs have been loaded. This can be
achieved using the @code{acl} monitor command.
Legacy method for enabling authorization of clients against the
x509 distinguished name and SASL username. It results in the creation
of two @code{authz-list} objects with IDs of @code{vnc.username} and
@code{vnc.x509dname}. The rules for these objects must be configured
with the HMP ACL commands.
This option is deprecated and should no longer be used. The new
@option{sasl-authz} and @option{tls-authz} options are a
replacement.
@item lossy
......
......@@ -42,6 +42,12 @@
#define FONT_HEIGHT 16
#define FONT_WIDTH 8
enum maybe_keycode {
CURSES_KEYCODE,
CURSES_CHAR,
CURSES_CHAR_OR_KEYCODE,
};
static DisplayChangeListener *dcl;
static console_ch_t screen[160 * 100];
static WINDOW *screenpad = NULL;
......@@ -194,9 +200,54 @@ static void curses_cursor_position(DisplayChangeListener *dcl,
static kbd_layout_t *kbd_layout = NULL;
static wint_t console_getch(enum maybe_keycode *maybe_keycode)
{
wint_t ret;
switch (get_wch(&ret)) {
case KEY_CODE_YES:
*maybe_keycode = CURSES_KEYCODE;
break;
case OK:
*maybe_keycode = CURSES_CHAR;
break;
case ERR:
ret = -1;
break;
}
return ret;
}
static int curses2foo(const int _curses2foo[], const int _curseskey2foo[],
int chr, enum maybe_keycode maybe_keycode)
{
int ret = -1;
if (maybe_keycode == CURSES_CHAR) {
if (chr < CURSES_CHARS) {
ret = _curses2foo[chr];
}
} else {
if (chr < CURSES_KEYS) {
ret = _curseskey2foo[chr];
}
if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE &&
chr < CURSES_CHARS) {
ret = _curses2foo[chr];
}
}
return ret;
}
#define curses2keycode(chr, maybe_keycode) \
curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode)
#define curses2keysym(chr, maybe_keycode) \
curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode)
#define curses2qemu(chr, maybe_keycode) \
curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode)
static void curses_refresh(DisplayChangeListener *dcl)
{
int chr, keysym, keycode, keycode_alt;
enum maybe_keycode maybe_keycode;
curses_winch_check();
......@@ -212,14 +263,14 @@ static void curses_refresh(DisplayChangeListener *dcl)
while (1) {
/* while there are any pending key strokes to process */
chr = getch();
chr = console_getch(&maybe_keycode);
if (chr == ERR)
if (chr == -1)
break;
#ifdef KEY_RESIZE
/* this shouldn't occur when we use a custom SIGWINCH handler */
if (chr == KEY_RESIZE) {
if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) {
clear();
refresh();
curses_calc_pad();
......@@ -228,17 +279,19 @@ static void curses_refresh(DisplayChangeListener *dcl)
}
#endif
keycode = curses2keycode[chr];
keycode = curses2keycode(chr, maybe_keycode);
keycode_alt = 0;
/* alt key */
/* alt or esc key */
if (keycode == 1) {
int nextchr = getch();
enum maybe_keycode next_maybe_keycode;
int nextchr = console_getch(&next_maybe_keycode);
if (nextchr != ERR) {
if (nextchr != -1) {
chr = nextchr;
maybe_keycode = next_maybe_keycode;
keycode_alt = ALT;
keycode = curses2keycode[chr];
keycode = curses2keycode(chr, maybe_keycode);
if (keycode != -1) {
keycode |= ALT;
......@@ -258,9 +311,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
}
if (kbd_layout) {
keysym = -1;
if (chr < CURSES_KEYS)
keysym = curses2keysym[chr];
keysym = curses2keysym(chr, maybe_keycode);
if (keysym == -1) {
if (chr < ' ') {
......@@ -326,10 +377,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
qemu_input_event_send_key_delay(0);
}
} else {
keysym = -1;
if (chr < CURSES_KEYS) {
keysym = curses2qemu[chr];
}
keysym = curses2qemu(chr, maybe_keycode);
if (keysym == -1)
keysym = chr;
......@@ -361,6 +409,7 @@ static void curses_setup(void)
initscr(); noecho(); intrflush(stdscr, FALSE);
nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
start_color(); raw(); scrollok(stdscr, FALSE);
set_escdelay(25);
/* Make color pair to match color format (3bits bg:3bits fg) */
for (i = 0; i < 64; i++) {
......
......@@ -49,22 +49,28 @@
/* curses won't detect a Control + Alt + 1, so use Alt + 1 */
#define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */
#define CURSES_CHARS 0x100 /* Support latin1 only */
#define CURSES_KEYS KEY_MAX /* KEY_MAX defined in <curses.h> */
static const int curses2keysym[CURSES_KEYS] = {
[0 ... (CURSES_KEYS - 1)] = -1,
static const int _curses2keysym[CURSES_CHARS] = {
[0 ... (CURSES_CHARS - 1)] = -1,
[0x7f] = KEY_BACKSPACE,
['\r'] = KEY_ENTER,
['\n'] = KEY_ENTER,
[27] = 27,
};
static const int _curseskey2keysym[CURSES_KEYS] = {
[0 ... (CURSES_KEYS - 1)] = -1,
[KEY_BTAB] = '\t' | KEYSYM_SHIFT,
[KEY_SPREVIOUS] = KEY_PPAGE | KEYSYM_SHIFT,
[KEY_SNEXT] = KEY_NPAGE | KEYSYM_SHIFT,
};
static const int curses2keycode[CURSES_KEYS] = {
[0 ... (CURSES_KEYS - 1)] = -1,
static const int _curses2keycode[CURSES_CHARS] = {
[0 ... (CURSES_CHARS - 1)] = -1,
[0x01b] = 1, /* Escape */
['1'] = 2,
......@@ -80,7 +86,6 @@ static const int curses2keycode[CURSES_KEYS] = {
['-'] = 12,
['='] = 13,
[0x07f] = 14, /* Backspace */
[KEY_BACKSPACE] = 14, /* Backspace */
['\t'] = 15, /* Tab */
['q'] = 16,
......@@ -97,7 +102,6 @@ static const int curses2keycode[CURSES_KEYS] = {
[']'] = 27,
['\n'] = 28, /* Return */
['\r'] = 28, /* Return */
[KEY_ENTER] = 28, /* Return */
['a'] = 30,
['s'] = 31,
......@@ -126,33 +130,6 @@ static const int curses2keycode[CURSES_KEYS] = {
[' '] = 57,
[KEY_F(1)] = 59, /* Function Key 1 */
[KEY_F(2)] = 60, /* Function Key 2 */
[KEY_F(3)] = 61, /* Function Key 3 */
[KEY_F(4)] = 62, /* Function Key 4 */
[KEY_F(5)] = 63, /* Function Key 5 */
[KEY_F(6)] = 64, /* Function Key 6 */
[KEY_F(7)] = 65, /* Function Key 7 */
[KEY_F(8)] = 66, /* Function Key 8 */
[KEY_F(9)] = 67, /* Function Key 9 */
[KEY_F(10)] = 68, /* Function Key 10 */
[KEY_F(11)] = 87, /* Function Key 11 */
[KEY_F(12)] = 88, /* Function Key 12 */
[KEY_HOME] = 71 | GREY, /* Home */
[KEY_UP] = 72 | GREY, /* Up Arrow */
[KEY_PPAGE] = 73 | GREY, /* Page Up */
[KEY_LEFT] = 75 | GREY, /* Left Arrow */
[KEY_RIGHT] = 77 | GREY, /* Right Arrow */
[KEY_END] = 79 | GREY, /* End */
[KEY_DOWN] = 80 | GREY, /* Down Arrow */
[KEY_NPAGE] = 81 | GREY, /* Page Down */
[KEY_IC] = 82 | GREY, /* Insert */
[KEY_DC] = 83 | GREY, /* Delete */
[KEY_SPREVIOUS] = 73 | GREY | SHIFT, /* Shift + Page Up */
[KEY_SNEXT] = 81 | GREY | SHIFT, /* Shift + Page Down */
['!'] = 2 | SHIFT,
['@'] = 3 | SHIFT,
['#'] = 4 | SHIFT,
......@@ -166,7 +143,6 @@ static const int curses2keycode[CURSES_KEYS] = {
['_'] = 12 | SHIFT,
['+'] = 13 | SHIFT,
[KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */
['Q'] = 16 | SHIFT,
['W'] = 17 | SHIFT,
['E'] = 18 | SHIFT,
......@@ -205,19 +181,6 @@ static const int curses2keycode[CURSES_KEYS] = {
['>'] = 52 | SHIFT,
['?'] = 53 | SHIFT,
[KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */
[KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */
[KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */
[KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */
[KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */
[KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */
[KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */
[KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */
[KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */
[KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */
[KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */
[KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */
['Q' - '@'] = 16 | CNTRL, /* Control + q */
['W' - '@'] = 17 | CNTRL, /* Control + w */
['E' - '@'] = 18 | CNTRL, /* Control + e */
......@@ -249,13 +212,67 @@ static const int curses2keycode[CURSES_KEYS] = {
};
static const int curses2qemu[CURSES_KEYS] = {
static const int _curseskey2keycode[CURSES_KEYS] = {
[0 ... (CURSES_KEYS - 1)] = -1,
[KEY_BACKSPACE] = 14, /* Backspace */
[KEY_ENTER] = 28, /* Return */
[KEY_F(1)] = 59, /* Function Key 1 */
[KEY_F(2)] = 60, /* Function Key 2 */
[KEY_F(3)] = 61, /* Function Key 3 */
[KEY_F(4)] = 62, /* Function Key 4 */
[KEY_F(5)] = 63, /* Function Key 5 */
[KEY_F(6)] = 64, /* Function Key 6 */
[KEY_F(7)] = 65, /* Function Key 7 */
[KEY_F(8)] = 66, /* Function Key 8 */
[KEY_F(9)] = 67, /* Function Key 9 */
[KEY_F(10)] = 68, /* Function Key 10 */
[KEY_F(11)] = 87, /* Function Key 11 */
[KEY_F(12)] = 88, /* Function Key 12 */
[KEY_HOME] = 71 | GREY, /* Home */
[KEY_UP] = 72 | GREY, /* Up Arrow */
[KEY_PPAGE] = 73 | GREY, /* Page Up */
[KEY_LEFT] = 75 | GREY, /* Left Arrow */
[KEY_RIGHT] = 77 | GREY, /* Right Arrow */
[KEY_END] = 79 | GREY, /* End */
[KEY_DOWN] = 80 | GREY, /* Down Arrow */
[KEY_NPAGE] = 81 | GREY, /* Page Down */
[KEY_IC] = 82 | GREY, /* Insert */
[KEY_DC] = 83 | GREY, /* Delete */
[KEY_SPREVIOUS] = 73 | GREY | SHIFT, /* Shift + Page Up */
[KEY_SNEXT] = 81 | GREY | SHIFT, /* Shift + Page Down */
[KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */
[KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */
[KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */
[KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */
[KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */
[KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */
[KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */
[KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */
[KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */
[KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */
[KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */
[KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */
[KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */
};
static const int _curses2qemu[CURSES_CHARS] = {
[0 ... (CURSES_CHARS - 1)] = -1,
['\n'] = '\n',
['\r'] = '\n',
[0x07f] = QEMU_KEY_BACKSPACE,
};
static const int _curseskey2qemu[CURSES_KEYS] = {
[0 ... (CURSES_KEYS - 1)] = -1,
[KEY_DOWN] = QEMU_KEY_DOWN,
[KEY_UP] = QEMU_KEY_UP,
......
......@@ -700,6 +700,12 @@ static void vnc_abort_display_jobs(VncDisplay *vd)
}
QTAILQ_FOREACH(vs, &vd->clients, next) {
vnc_lock_output(vs);
if (vs->update == VNC_STATE_UPDATE_NONE &&
vs->job_update != VNC_STATE_UPDATE_NONE) {
/* job aborted before completion */
vs->update = vs->job_update;
vs->job_update = VNC_STATE_UPDATE_NONE;
}
vs->abort = false;
vnc_unlock_output(vs);
}
......@@ -3357,6 +3363,12 @@ static QemuOptsList qemu_vnc_opts = {
},{
.name = "acl",
.type = QEMU_OPT_BOOL,
},{
.name = "tls-authz",
.type = QEMU_OPT_STRING,
},{
.name = "sasl-authz",
.type = QEMU_OPT_STRING,
},{
.name = "lossy",
.type = QEMU_OPT_BOOL,
......@@ -3796,6 +3808,8 @@ void vnc_display_open(const char *id, Error **errp)
const char *credid;
bool sasl = false;
int acl = 0;
const char *tlsauthz;
const char *saslauthz;
int lock_key_sync = 1;
int key_delay_ms;
......@@ -3867,7 +3881,33 @@ void vnc_display_open(const char *id, Error **errp)
goto fail;
}
}
if (qemu_opt_get(opts, "acl")) {
error_report("The 'acl' option to -vnc is deprecated. "
"Please use the 'tls-authz' and 'sasl-authz' "
"options instead");
}
acl = qemu_opt_get_bool(opts, "acl", false);
tlsauthz = qemu_opt_get(opts, "tls-authz");
if (acl && tlsauthz) {
error_setg(errp, "'acl' option is mutually exclusive with the "
"'tls-authz' option");
goto fail;
}
if (tlsauthz && !vd->tlscreds) {
error_setg(errp, "'tls-authz' provided but TLS is not enabled");
goto fail;
}
saslauthz = qemu_opt_get(opts, "sasl-authz");
if (acl && saslauthz) {
error_setg(errp, "'acl' option is mutually exclusive with the "
"'sasl-authz' option");
goto fail;
}
if (saslauthz && !sasl) {
error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled");
goto fail;
}
share = qemu_opt_get(opts, "share");
if (share) {
......@@ -3897,7 +3937,9 @@ void vnc_display_open(const char *id, Error **errp)
vd->non_adaptive = true;
}
if (acl) {
if (tlsauthz) {
vd->tlsauthzid = g_strdup(tlsauthz);
} else if (acl) {
if (strcmp(vd->id, "default") == 0) {
vd->tlsauthzid = g_strdup("vnc.x509dname");
} else {
......@@ -3908,15 +3950,19 @@ void vnc_display_open(const char *id, Error **errp)
&error_abort));
}
#ifdef CONFIG_VNC_SASL
if (acl && sasl) {
if (strcmp(vd->id, "default") == 0) {
vd->sasl.authzid = g_strdup("vnc.username");
} else {
vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id);
if (sasl) {
if (saslauthz) {
vd->sasl.authzid = g_strdup(saslauthz);
} else if (acl) {
if (strcmp(vd->id, "default") == 0) {
vd->sasl.authzid = g_strdup("vnc.username");
} else {
vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id);
}
vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid,
QAUTHZ_LIST_POLICY_DENY,
&error_abort));
}
vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid,
QAUTHZ_LIST_POLICY_DENY,
&error_abort));
}
#endif
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册