vnc+spice: password-secret option.

bugfixes for cocoa, vnc, opengl.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmBQRDYACgkQTLbY7tPo
 cTj2Qg//d3Pt0v9yiEvj5K+UxSl2ogKhNHLXDiN9eK0SrFcEQWaD0FqqbYLn3o8A
 oBG2rW6HlpBUiB07dNl9NKu53FvFqDGFgTMvrFzbLDsOrGpxJZe2kcf0rBqbuagT
 owEv4cqFaW6hHzWQTGkE55cBxgoVJLRcpyhINbt6Q1ryBNTPqKBkSrfhvCQtpVOC
 He0meGT52F8dHNJwEBe+wzOLPBmO+EtLslVyb5f12o+BmQLcvHcA9LCDdUYUAa7e
 yppreE8Z7Xwaix31MPMntnclRBrLab0X55DHhC6yUgm9430vRrN055xrcvEnlfit
 FTE9L9zOX049GdJWvYQKDUllABoB+wo1kXFkFQeSqiOTXkGP4uc3S3GO5BKwMNuC
 0dBwEEAYJ5h2gaiY+csAmU6aj2nxb/2apbsoYEGLmRthbp5/XnJcxbAteEBuIoHr
 YsBemb/Zweb4+CvECFl9tWFIhhgSMQqMGPh4NsgsCk0I+dwRb7aoFo32ycxqdHCF
 jqpFOjF527aGFo6XGUY2/DdRSELMXHupqObWTAGBQQAfePMv6MMvrCY9+KYtpkO8
 awgfGgLU1B70qGQ8v6kLANHE7ZyC36PC3P+unFSjB1CBRN2VJTII23lJkxjqjhkh
 bdwF4no460pCQs28M83f/jEnKuJvcLmYAj8Wi4goA/Wp6gboHxw=
 =Ma/x
 -----END PGP SIGNATURE-----

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

vnc+spice: password-secret option.
bugfixes for cocoa, vnc, opengl.

# gpg: Signature made Tue 16 Mar 2021 05:37:58 GMT
# gpg:                using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138
# 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-20210316-pull-request:
  ui/cocoa: Comment about modifier key input quirks
  ui: fold qemu_alloc_display in only caller
  ui: honour the actual guest display dimensions without rounding
  ui: use client width/height in WMVi message
  ui: avoid sending framebuffer updates outside client desktop bounds
  ui: add more trace points for VNC client/server messages
  ui/cocoa: Do not exit immediately after shutdown
  opengl: Do not convert format with glTexImage2D on OpenGL ES
  ui: deprecate "password" option for SPICE server
  ui: introduce "password-secret" option for SPICE server
  ui: introduce "password-secret" option for VNC servers

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-03-17 13:38:15 +00:00
commit 25a7751043
10 changed files with 238 additions and 39 deletions

View file

@ -165,6 +165,14 @@ Input parameters that take a size value should only use a size suffix
the value is hexadecimal. That is, '0x20M' is deprecated, and should the value is hexadecimal. That is, '0x20M' is deprecated, and should
be written either as '32M' or as '0x2000000'. be written either as '32M' or as '0x2000000'.
``-spice password=string`` (since 6.0)
''''''''''''''''''''''''''''''''''''''
This option is insecure because the SPICE password remains visible in
the process listing. This is replaced by the new ``password-secret``
option which lets the password be securely provided on the command
line using a ``secret`` object instance.
QEMU Machine Protocol (QMP) commands QEMU Machine Protocol (QMP) commands
------------------------------------ ------------------------------------

View file

@ -1922,7 +1922,8 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
" [,tls-ciphers=<list>]\n" " [,tls-ciphers=<list>]\n"
" [,tls-channel=[main|display|cursor|inputs|record|playback]]\n" " [,tls-channel=[main|display|cursor|inputs|record|playback]]\n"
" [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n" " [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n"
" [,sasl=on|off][,password=<secret>][,disable-ticketing=on|off]\n" " [,sasl=on|off][,disable-ticketing=on|off]\n"
" [,password=<string>][,password-secret=<secret-id>]\n"
" [,image-compression=[auto_glz|auto_lz|quic|glz|lz|off]]\n" " [,image-compression=[auto_glz|auto_lz|quic|glz|lz|off]]\n"
" [,jpeg-wan-compression=[auto|never|always]]\n" " [,jpeg-wan-compression=[auto|never|always]]\n"
" [,zlib-glz-wan-compression=[auto|never|always]]\n" " [,zlib-glz-wan-compression=[auto|never|always]]\n"
@ -1947,9 +1948,17 @@ SRST
``ipv4=on|off``; \ ``ipv6=on|off``; \ ``unix=on|off`` ``ipv4=on|off``; \ ``ipv6=on|off``; \ ``unix=on|off``
Force using the specified IP version. Force using the specified IP version.
``password=<secret>`` ``password=<string>``
Set the password you need to authenticate. Set the password you need to authenticate.
This option is deprecated and insecure because it leaves the
password visible in the process listing. Use ``password-secret``
instead.
``password-secret=<secret-id>``
Set the ID of the ``secret`` object containing the password
you need to authenticate.
``sasl=on|off`` ``sasl=on|off``
Require that the client use SASL to authenticate with the spice. Require that the client use SASL to authenticate with the spice.
The exact choice of authentication method used is controlled The exact choice of authentication method used is controlled
@ -2188,6 +2197,11 @@ SRST
time to allow <protocol> password to expire immediately or never time to allow <protocol> password to expire immediately or never
expire. expire.
``password-secret=<secret-id>``
Require that password based authentication is used for client
connections, using the password provided by the ``secret``
object identified by ``secret-id``.
``tls-creds=ID`` ``tls-creds=ID``
Provides the ID of a set of TLS credentials to use to secure the Provides the ID of a set of TLS credentials to use to secure the
VNC server. They will apply to both the normal VNC server socket VNC server. They will apply to both the normal VNC server socket

View file

@ -690,7 +690,43 @@ QemuCocoaView *cocoaView;
NSPoint p = [self screenLocationOfEvent:event]; NSPoint p = [self screenLocationOfEvent:event];
NSUInteger modifiers = [event modifierFlags]; NSUInteger modifiers = [event modifierFlags];
// emulate caps lock keydown and keyup /*
* Check -[NSEvent modifierFlags] here.
*
* There is a NSEventType for an event notifying the change of
* -[NSEvent modifierFlags], NSEventTypeFlagsChanged but these operations
* are performed for any events because a modifier state may change while
* the application is inactive (i.e. no events fire) and we don't want to
* wait for another modifier state change to detect such a change.
*
* NSEventModifierFlagCapsLock requires a special treatment. The other flags
* are handled in similar manners.
*
* NSEventModifierFlagCapsLock
* ---------------------------
*
* If CapsLock state is changed, "up" and "down" events will be fired in
* sequence, effectively updates CapsLock state on the guest.
*
* The other flags
* ---------------
*
* If a flag is not set, fire "up" events for all keys which correspond to
* the flag. Note that "down" events are not fired here because the flags
* checked here do not tell what exact keys are down.
*
* If one of the keys corresponding to a flag is down, we rely on
* -[NSEvent keyCode] of an event whose -[NSEvent type] is
* NSEventTypeFlagsChanged to know the exact key which is down, which has
* the following two downsides:
* - It does not work when the application is inactive as described above.
* - It malfactions *after* the modifier state is changed while the
* application is inactive. It is because -[NSEvent keyCode] does not tell
* if the key is up or down, and requires to infer the current state from
* the previous state. It is still possible to fix such a malfanction by
* completely leaving your hands from the keyboard, which hopefully makes
* this implementation usable enough.
*/
if (!!(modifiers & NSEventModifierFlagCapsLock) != if (!!(modifiers & NSEventModifierFlagCapsLock) !=
qkbd_state_modifier_get(kbd, QKBD_MOD_CAPSLOCK)) { qkbd_state_modifier_get(kbd, QKBD_MOD_CAPSLOCK)) {
qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, true); qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, true);
@ -1115,7 +1151,13 @@ QemuCocoaView *cocoaView;
COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
exit(0);
/*
* Sleep here, because returning will cause OSX to kill us
* immediately; the QEMU main loop will handle the shutdown
* request and terminate the process.
*/
[NSThread sleepForTimeInterval:INFINITY];
} }
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication

View file

@ -73,11 +73,20 @@ void surface_gl_create_texture(QemuGLShader *gls,
glBindTexture(GL_TEXTURE_2D, surface->texture); glBindTexture(GL_TEXTURE_2D, surface->texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT,
surface_stride(surface) / surface_bytes_per_pixel(surface)); surface_stride(surface) / surface_bytes_per_pixel(surface));
if (epoxy_is_desktop_gl()) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
surface_width(surface), surface_width(surface),
surface_height(surface), surface_height(surface),
0, surface->glformat, surface->gltype, 0, surface->glformat, surface->gltype,
surface_data(surface)); surface_data(surface));
} else {
glTexImage2D(GL_TEXTURE_2D, 0, surface->glformat,
surface_width(surface),
surface_height(surface),
0, surface->glformat, surface->gltype,
surface_data(surface));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

View file

@ -1386,26 +1386,18 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
return s; return s;
} }
static void qemu_alloc_display(DisplaySurface *surface, int width, int height)
{
qemu_pixman_image_unref(surface->image);
surface->image = NULL;
surface->format = PIXMAN_x8r8g8b8;
surface->image = pixman_image_create_bits(surface->format,
width, height,
NULL, width * 4);
assert(surface->image != NULL);
surface->flags = QEMU_ALLOCATED_FLAG;
}
DisplaySurface *qemu_create_displaysurface(int width, int height) DisplaySurface *qemu_create_displaysurface(int width, int height)
{ {
DisplaySurface *surface = g_new0(DisplaySurface, 1); DisplaySurface *surface = g_new0(DisplaySurface, 1);
trace_displaysurface_create(surface, width, height); trace_displaysurface_create(surface, width, height);
qemu_alloc_display(surface, width, height); surface->format = PIXMAN_x8r8g8b8;
surface->image = pixman_image_create_bits(surface->format,
width, height,
NULL, width * 4);
assert(surface->image != NULL);
surface->flags = QEMU_ALLOCATED_FLAG;
return surface; return surface;
} }

View file

@ -34,6 +34,7 @@
#include "qapi/qapi-events-ui.h" #include "qapi/qapi-events-ui.h"
#include "qemu/notify.h" #include "qemu/notify.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "crypto/secret_common.h"
#include "migration/misc.h" #include "migration/misc.h"
#include "hw/pci/pci_bus.h" #include "hw/pci/pci_bus.h"
#include "ui/spice-display.h" #include "ui/spice-display.h"
@ -415,6 +416,9 @@ static QemuOptsList qemu_spice_opts = {
},{ },{
.name = "password", .name = "password",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
},{
.name = "password-secret",
.type = QEMU_OPT_STRING,
},{ },{
.name = "disable-ticketing", .name = "disable-ticketing",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
@ -636,7 +640,9 @@ void qemu_spice_display_init_done(void)
static void qemu_spice_init(void) static void qemu_spice_init(void)
{ {
QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
const char *password, *str, *x509_dir, *addr, char *password = NULL;
const char *passwordSecret;
const char *str, *x509_dir, *addr,
*x509_key_password = NULL, *x509_key_password = NULL,
*x509_dh_file = NULL, *x509_dh_file = NULL,
*tls_ciphers = NULL; *tls_ciphers = NULL;
@ -663,7 +669,28 @@ static void qemu_spice_init(void)
error_report("spice tls-port is out of range"); error_report("spice tls-port is out of range");
exit(1); exit(1);
} }
password = qemu_opt_get(opts, "password"); passwordSecret = qemu_opt_get(opts, "password-secret");
if (passwordSecret) {
Error *local_err = NULL;
if (qemu_opt_get(opts, "password")) {
error_report("'password' option is mutually exclusive with "
"'password-secret'");
exit(1);
}
password = qcrypto_secret_lookup_as_utf8(passwordSecret,
&local_err);
if (!password) {
error_report_err(local_err);
exit(1);
}
} else {
str = qemu_opt_get(opts, "password");
if (str) {
warn_report("'password' option is deprecated and insecure, "
"use 'password-secret' instead");
password = g_strdup(str);
}
}
if (tls_port) { if (tls_port) {
x509_dir = qemu_opt_get(opts, "x509-dir"); x509_dir = qemu_opt_get(opts, "x509-dir");
@ -809,6 +836,7 @@ static void qemu_spice_init(void)
g_free(x509_key_file); g_free(x509_key_file);
g_free(x509_cert_file); g_free(x509_cert_file);
g_free(x509_cacert_file); g_free(x509_cacert_file);
g_free(password);
#ifdef HAVE_SPICE_GL #ifdef HAVE_SPICE_GL
if (qemu_opt_get_bool(opts, "gl", 0)) { if (qemu_opt_get_bool(opts, "gl", 0)) {

View file

@ -37,6 +37,15 @@ vnc_key_event_ext(bool down, int sym, int keycode, const char *name) "down %d, s
vnc_key_event_map(bool down, int sym, int keycode, const char *name) "down %d, sym 0x%x -> keycode 0x%x [%s]" vnc_key_event_map(bool down, int sym, int keycode, const char *name) "down %d, sym 0x%x -> keycode 0x%x [%s]"
vnc_key_sync_numlock(bool on) "%d" vnc_key_sync_numlock(bool on) "%d"
vnc_key_sync_capslock(bool on) "%d" vnc_key_sync_capslock(bool on) "%d"
vnc_msg_server_audio_begin(void *state, void *ioc) "VNC server msg audio begin state=%p ioc=%p"
vnc_msg_server_audio_end(void *state, void *ioc) "VNC server msg audio end state=%p ioc=%p"
vnc_msg_server_audio_data(void *state, void *ioc, const void *buf, size_t len) "VNC server msg audio data state=%p ioc=%p buf=%p len=%zd"
vnc_msg_server_desktop_resize(void *state, void *ioc, int width, int height) "VNC server msg ext resize state=%p ioc=%p size=%dx%d"
vnc_msg_server_ext_desktop_resize(void *state, void *ioc, int width, int height, int reason) "VNC server msg ext resize state=%p ioc=%p size=%dx%d reason=%d"
vnc_msg_client_audio_enable(void *state, void *ioc) "VNC client msg audio enable state=%p ioc=%p"
vnc_msg_client_audio_disable(void *state, void *ioc) "VNC client msg audio disable state=%p ioc=%p"
vnc_msg_client_audio_format(void *state, void *ioc, int fmt, int channels, int freq) "VNC client msg audio format state=%p ioc=%p fmt=%d channels=%d freq=%d"
vnc_msg_client_set_desktop_size(void *state, void *ioc, int width, int height, int screens) "VNC client msg set desktop size state=%p ioc=%p size=%dx%d screens=%d"
vnc_client_eof(void *state, void *ioc) "VNC client EOF state=%p ioc=%p" vnc_client_eof(void *state, void *ioc) "VNC client EOF state=%p ioc=%p"
vnc_client_io_error(void *state, void *ioc, const char *msg) "VNC client I/O error state=%p ioc=%p errmsg=%s" vnc_client_io_error(void *state, void *ioc, const char *msg) "VNC client I/O error state=%p ioc=%p errmsg=%s"
vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p" vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p"
@ -50,6 +59,13 @@ vnc_client_throttle_audio(void *state, void *ioc, size_t offset) "VNC client thr
vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client unthrottle forced offset state=%p ioc=%p" vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client unthrottle forced offset state=%p ioc=%p"
vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset) "VNC client unthrottle incremental state=%p ioc=%p offset=%zu" vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset) "VNC client unthrottle incremental state=%p ioc=%p offset=%zu"
vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t threshold) "VNC client output limit state=%p ioc=%p offset=%zu threshold=%zu" vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t threshold) "VNC client output limit state=%p ioc=%p offset=%zu threshold=%zu"
vnc_server_dpy_pageflip(void *dpy, int w, int h, int fmt) "VNC server dpy pageflip dpy=%p size=%dx%d fmt=%d"
vnc_server_dpy_recreate(void *dpy, int w, int h, int fmt) "VNC server dpy recreate dpy=%p size=%dx%d fmt=%d"
vnc_job_add_rect(void *state, void *job, int x, int y, int w, int h) "VNC add rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_discard_rect(void *state, void *job, int x, int y, int w, int h) "VNC job discard rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_clamp_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_clamped_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d"
vnc_job_nrects(void *state, void *job, int nrects) "VNC job state=%p job=%p nrects=%d"
vnc_auth_init(void *display, int websock, int auth, int subauth) "VNC auth init state=%p websock=%d auth=%d subauth=%d" vnc_auth_init(void *display, int websock, int auth, int subauth) "VNC auth init state=%p websock=%d auth=%d subauth=%d"
vnc_auth_start(void *state, int method) "VNC client auth start state=%p method=%d" vnc_auth_start(void *state, int method) "VNC client auth start state=%p method=%d"
vnc_auth_pass(void *state, int method) "VNC client auth passed state=%p method=%d" vnc_auth_pass(void *state, int method) "VNC client auth passed state=%p method=%d"

View file

@ -32,6 +32,7 @@
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "block/aio.h" #include "block/aio.h"
#include "trace.h"
/* /*
* Locking: * Locking:
@ -94,6 +95,8 @@ int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
{ {
VncRectEntry *entry = g_new0(VncRectEntry, 1); VncRectEntry *entry = g_new0(VncRectEntry, 1);
trace_vnc_job_add_rect(job->vs, job, x, y, w, h);
entry->rect.x = x; entry->rect.x = x;
entry->rect.y = y; entry->rect.y = y;
entry->rect.w = w; entry->rect.w = w;
@ -190,6 +193,8 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
local->zlib = orig->zlib; local->zlib = orig->zlib;
local->hextile = orig->hextile; local->hextile = orig->hextile;
local->zrle = orig->zrle; local->zrle = orig->zrle;
local->client_width = orig->client_width;
local->client_height = orig->client_height;
} }
static void vnc_async_encoding_end(VncState *orig, VncState *local) static void vnc_async_encoding_end(VncState *orig, VncState *local)
@ -202,6 +207,34 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local)
orig->lossy_rect = local->lossy_rect; orig->lossy_rect = local->lossy_rect;
} }
static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect)
{
trace_vnc_job_clamp_rect(vs, job, rect->x, rect->y, rect->w, rect->h);
if (rect->x >= vs->client_width) {
goto discard;
}
rect->w = MIN(vs->client_width - rect->x, rect->w);
if (rect->w == 0) {
goto discard;
}
if (rect->y >= vs->client_height) {
goto discard;
}
rect->h = MIN(vs->client_height - rect->y, rect->h);
if (rect->h == 0) {
goto discard;
}
trace_vnc_job_clamped_rect(vs, job, rect->x, rect->y, rect->w, rect->h);
return true;
discard:
trace_vnc_job_discard_rect(vs, job, rect->x, rect->y, rect->w, rect->h);
return false;
}
static int vnc_worker_thread_loop(VncJobQueue *queue) static int vnc_worker_thread_loop(VncJobQueue *queue)
{ {
VncJob *job; VncJob *job;
@ -260,14 +293,17 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
goto disconnected; goto disconnected;
} }
if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) {
n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y, n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
entry->rect.w, entry->rect.h); entry->rect.w, entry->rect.h);
if (n >= 0) { if (n >= 0) {
n_rectangles += n; n_rectangles += n;
} }
}
g_free(entry); g_free(entry);
} }
trace_vnc_job_nrects(&vs, job, n_rectangles);
vnc_unlock_display(job->vs->vd); vnc_unlock_display(job->vs->vd);
/* Put n_rectangles at the beginning of the message */ /* Put n_rectangles at the beginning of the message */

View file

@ -48,6 +48,7 @@
#include "crypto/tlscredsanon.h" #include "crypto/tlscredsanon.h"
#include "crypto/tlscredsx509.h" #include "crypto/tlscredsx509.h"
#include "crypto/random.h" #include "crypto/random.h"
#include "crypto/secret_common.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/help_option.h" #include "qemu/help_option.h"
@ -607,6 +608,11 @@ static int vnc_width(VncDisplay *vd)
VNC_DIRTY_PIXELS_PER_BIT)); VNC_DIRTY_PIXELS_PER_BIT));
} }
static int vnc_true_width(VncDisplay *vd)
{
return MIN(VNC_MAX_WIDTH, surface_width(vd->ds));
}
static int vnc_height(VncDisplay *vd) static int vnc_height(VncDisplay *vd)
{ {
return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
@ -658,6 +664,9 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
static void vnc_desktop_resize_ext(VncState *vs, int reject_reason) static void vnc_desktop_resize_ext(VncState *vs, int reject_reason)
{ {
trace_vnc_msg_server_ext_desktop_resize(
vs, vs->ioc, vs->client_width, vs->client_height, reject_reason);
vnc_lock_output(vs); vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0); vnc_write_u8(vs, 0);
@ -687,16 +696,16 @@ static void vnc_desktop_resize(VncState *vs)
!vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT))) { !vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT))) {
return; return;
} }
if (vs->client_width == pixman_image_get_width(vs->vd->server) && if (vs->client_width == vs->vd->true_width &&
vs->client_height == pixman_image_get_height(vs->vd->server)) { vs->client_height == pixman_image_get_height(vs->vd->server)) {
return; return;
} }
assert(pixman_image_get_width(vs->vd->server) < 65536 && assert(vs->vd->true_width < 65536 &&
pixman_image_get_width(vs->vd->server) >= 0); vs->vd->true_width >= 0);
assert(pixman_image_get_height(vs->vd->server) < 65536 && assert(pixman_image_get_height(vs->vd->server) < 65536 &&
pixman_image_get_height(vs->vd->server) >= 0); pixman_image_get_height(vs->vd->server) >= 0);
vs->client_width = pixman_image_get_width(vs->vd->server); vs->client_width = vs->vd->true_width;
vs->client_height = pixman_image_get_height(vs->vd->server); vs->client_height = pixman_image_get_height(vs->vd->server);
if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) { if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) {
@ -704,6 +713,9 @@ static void vnc_desktop_resize(VncState *vs)
return; return;
} }
trace_vnc_msg_server_desktop_resize(
vs, vs->ioc, vs->client_width, vs->client_height);
vnc_lock_output(vs); vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0); vnc_write_u8(vs, 0);
@ -767,6 +779,7 @@ static void vnc_update_server_surface(VncDisplay *vd)
width = vnc_width(vd); width = vnc_width(vd);
height = vnc_height(vd); height = vnc_height(vd);
vd->true_width = vnc_true_width(vd);
vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
width, height, width, height,
NULL, 0); NULL, 0);
@ -802,13 +815,22 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
vd->guest.fb = pixman_image_ref(surface->image); vd->guest.fb = pixman_image_ref(surface->image);
vd->guest.format = surface->format; vd->guest.format = surface->format;
if (pageflip) { if (pageflip) {
trace_vnc_server_dpy_pageflip(vd,
surface_width(surface),
surface_height(surface),
surface_format(surface));
vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
surface_width(surface), surface_width(surface),
surface_height(surface)); surface_height(surface));
return; return;
} }
trace_vnc_server_dpy_recreate(vd,
surface_width(surface),
surface_height(surface),
surface_format(surface));
/* server surface */ /* server surface */
vnc_update_server_surface(vd); vnc_update_server_surface(vd);
@ -1181,6 +1203,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
assert(vs->magic == VNC_MAGIC); assert(vs->magic == VNC_MAGIC);
switch (cmd) { switch (cmd) {
case AUD_CNOTIFY_DISABLE: case AUD_CNOTIFY_DISABLE:
trace_vnc_msg_server_audio_end(vs, vs->ioc);
vnc_lock_output(vs); vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
@ -1190,6 +1213,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
break; break;
case AUD_CNOTIFY_ENABLE: case AUD_CNOTIFY_ENABLE:
trace_vnc_msg_server_audio_begin(vs, vs->ioc);
vnc_lock_output(vs); vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
@ -1209,6 +1233,7 @@ static void audio_capture(void *opaque, const void *buf, int size)
VncState *vs = opaque; VncState *vs = opaque;
assert(vs->magic == VNC_MAGIC); assert(vs->magic == VNC_MAGIC);
trace_vnc_msg_server_audio_data(vs, vs->ioc, buf, size);
vnc_lock_output(vs); vnc_lock_output(vs);
if (vs->output.offset < vs->throttle_output_offset) { if (vs->output.offset < vs->throttle_output_offset) {
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
@ -2309,8 +2334,8 @@ static void vnc_colordepth(VncState *vs)
vnc_write_u8(vs, 0); vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */ vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, vnc_framebuffer_update(vs, 0, 0,
pixman_image_get_width(vs->vd->server), vs->client_width,
pixman_image_get_height(vs->vd->server), vs->client_height,
VNC_ENCODING_WMVi); VNC_ENCODING_WMVi);
pixel_format_message(vs); pixel_format_message(vs);
vnc_unlock_output(vs); vnc_unlock_output(vs);
@ -2453,9 +2478,11 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
switch (read_u16 (data, 2)) { switch (read_u16 (data, 2)) {
case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE: case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
trace_vnc_msg_client_audio_enable(vs, vs->ioc);
audio_add(vs); audio_add(vs);
break; break;
case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE: case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
trace_vnc_msg_client_audio_disable(vs, vs->ioc);
audio_del(vs); audio_del(vs);
break; break;
case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT: case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
@ -2491,6 +2518,8 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
break; break;
} }
vs->as.freq = freq; vs->as.freq = freq;
trace_vnc_msg_client_audio_format(
vs, vs->ioc, vs->as.fmt, vs->as.nchannels, vs->as.freq);
break; break;
default: default:
VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4)); VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
@ -2509,6 +2538,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
{ {
size_t size; size_t size;
uint8_t screens; uint8_t screens;
int w, h;
if (len < 8) { if (len < 8) {
return 8; return 8;
@ -2519,12 +2549,15 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
if (len < size) { if (len < size) {
return size; return size;
} }
w = read_u16(data, 2);
h = read_u16(data, 4);
trace_vnc_msg_client_set_desktop_size(vs, vs->ioc, w, h, screens);
if (dpy_ui_info_supported(vs->vd->dcl.con)) { if (dpy_ui_info_supported(vs->vd->dcl.con)) {
QemuUIInfo info; QemuUIInfo info;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
info.width = read_u16(data, 2); info.width = w;
info.height = read_u16(data, 4); info.height = h;
dpy_set_ui_info(vs->vd->dcl.con, &info); dpy_set_ui_info(vs->vd->dcl.con, &info);
vnc_desktop_resize_ext(vs, 4 /* Request forwarded */); vnc_desktop_resize_ext(vs, 4 /* Request forwarded */);
} else { } else {
@ -3459,6 +3492,9 @@ static QemuOptsList qemu_vnc_opts = {
},{ },{
.name = "password", .name = "password",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
},{
.name = "password-secret",
.type = QEMU_OPT_STRING,
},{ },{
.name = "reverse", .name = "reverse",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
@ -3931,6 +3967,7 @@ void vnc_display_open(const char *id, Error **errp)
int lock_key_sync = 1; int lock_key_sync = 1;
int key_delay_ms; int key_delay_ms;
const char *audiodev; const char *audiodev;
const char *passwordSecret;
if (!vd) { if (!vd) {
error_setg(errp, "VNC display not active"); error_setg(errp, "VNC display not active");
@ -3948,7 +3985,23 @@ void vnc_display_open(const char *id, Error **errp)
goto fail; goto fail;
} }
passwordSecret = qemu_opt_get(opts, "password-secret");
if (passwordSecret) {
if (qemu_opt_get(opts, "password")) {
error_setg(errp,
"'password' flag is redundant with 'password-secret'");
goto fail;
}
vd->password = qcrypto_secret_lookup_as_utf8(passwordSecret,
errp);
if (!vd->password) {
goto fail;
}
password = true;
} else {
password = qemu_opt_get_bool(opts, "password", false); password = qemu_opt_get_bool(opts, "password", false);
}
if (password) { if (password) {
if (fips_get_state()) { if (fips_get_state()) {
error_setg(errp, error_setg(errp,

View file

@ -164,6 +164,7 @@ struct VncDisplay
struct VncSurface guest; /* guest visible surface (aka ds->surface) */ struct VncSurface guest; /* guest visible surface (aka ds->surface) */
pixman_image_t *server; /* vnc server surface */ pixman_image_t *server; /* vnc server surface */
int true_width; /* server surface width before rounding up */
const char *id; const char *id;
QTAILQ_ENTRY(VncDisplay) next; QTAILQ_ENTRY(VncDisplay) next;