ui: convert VNC websockets to use crypto APIs

Remove the direct use of gnutls for hash processing in the
websockets code, in favour of using the crypto APIs. This
allows the websockets code to be built unconditionally
removing countless conditional checks from the VNC code.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1435770638-25715-9-git-send-email-berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Daniel P. Berrange 2015-07-01 18:10:36 +01:00 committed by Paolo Bonzini
parent 488981a4af
commit 8e9b0d24fb
6 changed files with 25 additions and 95 deletions

19
configure vendored
View File

@ -246,7 +246,6 @@ vnc_tls=""
vnc_sasl="" vnc_sasl=""
vnc_jpeg="" vnc_jpeg=""
vnc_png="" vnc_png=""
vnc_ws=""
xen="" xen=""
xen_ctrl_version="" xen_ctrl_version=""
xen_pci_passthrough="" xen_pci_passthrough=""
@ -896,10 +895,6 @@ for opt do
;; ;;
--enable-vnc-png) vnc_png="yes" --enable-vnc-png) vnc_png="yes"
;; ;;
--disable-vnc-ws) vnc_ws="no"
;;
--enable-vnc-ws) vnc_ws="yes"
;;
--disable-slirp) slirp="no" --disable-slirp) slirp="no"
;; ;;
--disable-uuid) uuid="no" --disable-uuid) uuid="no"
@ -2343,7 +2338,7 @@ fi
########################################## ##########################################
# VNC TLS/WS detection # VNC TLS/WS detection
if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then
cat > $TMPC <<EOF cat > $TMPC <<EOF
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
@ -2354,20 +2349,13 @@ EOF
if test "$vnc_tls" != "no" ; then if test "$vnc_tls" != "no" ; then
vnc_tls=yes vnc_tls=yes
fi fi
if test "$vnc_ws" != "no" ; then
vnc_ws=yes
fi
libs_softmmu="$vnc_tls_libs $libs_softmmu" libs_softmmu="$vnc_tls_libs $libs_softmmu"
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
else else
if test "$vnc_tls" = "yes" ; then if test "$vnc_tls" = "yes" ; then
feature_not_found "vnc-tls" "Install gnutls devel" feature_not_found "vnc-tls" "Install gnutls devel"
fi fi
if test "$vnc_ws" = "yes" ; then
feature_not_found "vnc-ws" "Install gnutls devel"
fi
vnc_tls=no vnc_tls=no
vnc_ws=no
fi fi
fi fi
@ -4496,7 +4484,6 @@ if test "$vnc" = "yes" ; then
echo "VNC SASL support $vnc_sasl" echo "VNC SASL support $vnc_sasl"
echo "VNC JPEG support $vnc_jpeg" echo "VNC JPEG support $vnc_jpeg"
echo "VNC PNG support $vnc_png" echo "VNC PNG support $vnc_png"
echo "VNC WS support $vnc_ws"
fi fi
if test -n "$sparc_cpu"; then if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu" echo "Target Sparc Arch $sparc_cpu"
@ -4708,10 +4695,6 @@ fi
if test "$vnc_png" = "yes" ; then if test "$vnc_png" = "yes" ; then
echo "CONFIG_VNC_PNG=y" >> $config_host_mak echo "CONFIG_VNC_PNG=y" >> $config_host_mak
fi fi
if test "$vnc_ws" = "yes" ; then
echo "CONFIG_VNC_WS=y" >> $config_host_mak
echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak
fi
if test "$fnmatch" = "yes" ; then if test "$fnmatch" = "yes" ; then
echo "CONFIG_FNMATCH=y" >> $config_host_mak echo "CONFIG_FNMATCH=y" >> $config_host_mak
fi fi

View File

@ -4,7 +4,7 @@ vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o vnc-obj-y += vnc-enc-zrle.o
vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o

View File

@ -20,6 +20,7 @@
#include "vnc.h" #include "vnc.h"
#include "qemu/main-loop.h" #include "qemu/main-loop.h"
#include "crypto/hash.h"
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
#include "qemu/sockets.h" #include "qemu/sockets.h"
@ -203,24 +204,21 @@ static char *vncws_extract_handshake_entry(const char *handshake,
static void vncws_send_handshake_response(VncState *vs, const char* key) static void vncws_send_handshake_response(VncState *vs, const char* key)
{ {
char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
unsigned char hash[SHA1_DIGEST_LEN];
size_t hash_size = sizeof(hash);
char *accept = NULL, *response = NULL; char *accept = NULL, *response = NULL;
gnutls_datum_t in; Error *err = NULL;
int ret;
g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
/* hash and encode it */ /* hash and encode it */
in.data = (void *)combined_key; if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN; combined_key,
ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size); WS_CLIENT_KEY_LEN + WS_GUID_LEN,
if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) { &accept,
accept = g_base64_encode(hash, hash_size); &err) < 0) {
} VNC_DEBUG("Hashing Websocket combined key failed %s\n",
if (accept == NULL) { error_get_pretty(err));
VNC_DEBUG("Hashing Websocket combined key failed\n"); error_free(err);
vnc_client_error(vs); vnc_client_error(vs);
return; return;
} }

View File

@ -21,8 +21,6 @@
#ifndef __QEMU_UI_VNC_WS_H #ifndef __QEMU_UI_VNC_WS_H
#define __QEMU_UI_VNC_WS_H #define __QEMU_UI_VNC_WS_H
#include <gnutls/gnutls.h>
#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) #define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
#define SHA1_DIGEST_LEN 20 #define SHA1_DIGEST_LEN 20

View File

@ -40,6 +40,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "ui/input.h" #include "ui/input.h"
#include "qapi-event.h" #include "qapi-event.h"
#include "crypto/hash.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_INC 50
@ -355,9 +356,7 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
info->base->host = g_strdup(host); info->base->host = g_strdup(host);
info->base->service = g_strdup(serv); info->base->service = g_strdup(serv);
info->base->family = inet_netfamily(sa.ss_family); info->base->family = inet_netfamily(sa.ss_family);
#ifdef CONFIG_VNC_WS
info->base->websocket = client->websocket; info->base->websocket = client->websocket;
#endif
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
if (client->tls.session && client->tls.dname) { if (client->tls.session && client->tls.dname) {
@ -582,12 +581,10 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
info->server = qmp_query_server_entry(vd->lsock, false, info->server = qmp_query_server_entry(vd->lsock, false,
info->server); info->server);
} }
#ifdef CONFIG_VNC_WS
if (vd->lwebsock != -1) { if (vd->lwebsock != -1) {
info->server = qmp_query_server_entry(vd->lwebsock, true, info->server = qmp_query_server_entry(vd->lwebsock, true,
info->server); info->server);
} }
#endif
item = g_new0(VncInfo2List, 1); item = g_new0(VncInfo2List, 1);
item->value = info; item->value = info;
@ -1231,10 +1228,8 @@ void vnc_disconnect_finish(VncState *vs)
buffer_free(&vs->input); buffer_free(&vs->input);
buffer_free(&vs->output); buffer_free(&vs->output);
#ifdef CONFIG_VNC_WS
buffer_free(&vs->ws_input); buffer_free(&vs->ws_input);
buffer_free(&vs->ws_output); buffer_free(&vs->ws_output);
#endif /* CONFIG_VNC_WS */
qapi_free_VncClientInfo(vs->info); qapi_free_VncClientInfo(vs->info);
@ -1413,12 +1408,9 @@ static void vnc_client_write_locked(void *opaque)
} else } else
#endif /* CONFIG_VNC_SASL */ #endif /* CONFIG_VNC_SASL */
{ {
#ifdef CONFIG_VNC_WS
if (vs->encode_ws) { if (vs->encode_ws) {
vnc_client_write_ws(vs); vnc_client_write_ws(vs);
} else } else {
#endif /* CONFIG_VNC_WS */
{
vnc_client_write_plain(vs); vnc_client_write_plain(vs);
} }
} }
@ -1429,11 +1421,7 @@ void vnc_client_write(void *opaque)
VncState *vs = opaque; VncState *vs = opaque;
vnc_lock_output(vs); vnc_lock_output(vs);
if (vs->output.offset if (vs->output.offset || vs->ws_output.offset) {
#ifdef CONFIG_VNC_WS
|| vs->ws_output.offset
#endif
) {
vnc_client_write_locked(opaque); vnc_client_write_locked(opaque);
} else if (vs->csock != -1) { } else if (vs->csock != -1) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
@ -1539,7 +1527,6 @@ void vnc_client_read(void *opaque)
ret = vnc_client_read_sasl(vs); ret = vnc_client_read_sasl(vs);
else else
#endif /* CONFIG_VNC_SASL */ #endif /* CONFIG_VNC_SASL */
#ifdef CONFIG_VNC_WS
if (vs->encode_ws) { if (vs->encode_ws) {
ret = vnc_client_read_ws(vs); ret = vnc_client_read_ws(vs);
if (ret == -1) { if (ret == -1) {
@ -1549,9 +1536,7 @@ void vnc_client_read(void *opaque)
vnc_client_error(vs); vnc_client_error(vs);
return; return;
} }
} else } else {
#endif /* CONFIG_VNC_WS */
{
ret = vnc_client_read_plain(vs); ret = vnc_client_read_plain(vs);
} }
if (!ret) { if (!ret) {
@ -1624,11 +1609,8 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs) void vnc_flush(VncState *vs)
{ {
vnc_lock_output(vs); vnc_lock_output(vs);
if (vs->csock != -1 && (vs->output.offset if (vs->csock != -1 && (vs->output.offset ||
#ifdef CONFIG_VNC_WS vs->ws_output.offset)) {
|| vs->ws_output.offset
#endif
)) {
vnc_client_write_locked(vs); vnc_client_write_locked(vs);
} }
vnc_unlock_output(vs); vnc_unlock_output(vs);
@ -3019,7 +3001,6 @@ static void vnc_connect(VncDisplay *vd, int csock,
VNC_DEBUG("New client on socket %d\n", csock); VNC_DEBUG("New client on socket %d\n", csock);
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
qemu_set_nonblock(vs->csock); qemu_set_nonblock(vs->csock);
#ifdef CONFIG_VNC_WS
if (websocket) { if (websocket) {
vs->websocket = 1; vs->websocket = 1;
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
@ -3031,7 +3012,6 @@ static void vnc_connect(VncDisplay *vd, int csock,
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
} }
} else } else
#endif /* CONFIG_VNC_WS */
{ {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
} }
@ -3040,10 +3020,7 @@ static void vnc_connect(VncDisplay *vd, int csock,
vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED); vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
#ifdef CONFIG_VNC_WS if (!vs->websocket) {
if (!vs->websocket)
#endif
{
vnc_init_state(vs); vnc_init_state(vs);
} }
@ -3099,12 +3076,9 @@ static void vnc_listen_read(void *opaque, bool websocket)
/* Catch-up */ /* Catch-up */
graphic_hw_update(vs->dcl.con); graphic_hw_update(vs->dcl.con);
#ifdef CONFIG_VNC_WS
if (websocket) { if (websocket) {
csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
} else } else {
#endif /* CONFIG_VNC_WS */
{
csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
} }
@ -3119,12 +3093,10 @@ static void vnc_listen_regular_read(void *opaque)
vnc_listen_read(opaque, false); vnc_listen_read(opaque, false);
} }
#ifdef CONFIG_VNC_WS
static void vnc_listen_websocket_read(void *opaque) static void vnc_listen_websocket_read(void *opaque)
{ {
vnc_listen_read(opaque, true); vnc_listen_read(opaque, true);
} }
#endif /* CONFIG_VNC_WS */
static const DisplayChangeListenerOps dcl_ops = { static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "vnc", .dpy_name = "vnc",
@ -3150,9 +3122,7 @@ void vnc_display_init(const char *id)
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next); QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
vs->lsock = -1; vs->lsock = -1;
#ifdef CONFIG_VNC_WS
vs->lwebsock = -1; vs->lwebsock = -1;
#endif
QTAILQ_INIT(&vs->clients); QTAILQ_INIT(&vs->clients);
vs->expires = TIME_MAX; vs->expires = TIME_MAX;
@ -3186,14 +3156,12 @@ static void vnc_display_close(VncDisplay *vs)
close(vs->lsock); close(vs->lsock);
vs->lsock = -1; vs->lsock = -1;
} }
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false; vs->ws_enabled = false;
if (vs->lwebsock != -1) { if (vs->lwebsock != -1) {
qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
close(vs->lwebsock); close(vs->lwebsock);
vs->lwebsock = -1; vs->lwebsock = -1;
} }
#endif /* CONFIG_VNC_WS */
vs->auth = VNC_AUTH_INVALID; vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS #ifdef CONFIG_VNC_TLS
@ -3579,13 +3547,12 @@ void vnc_display_open(const char *id, Error **errp)
websocket = qemu_opt_get(opts, "websocket"); websocket = qemu_opt_get(opts, "websocket");
if (websocket) { if (websocket) {
#ifdef CONFIG_VNC_WS
vs->ws_enabled = true; vs->ws_enabled = true;
qemu_opt_set(wsopts, "port", websocket, &error_abort); qemu_opt_set(wsopts, "port", websocket, &error_abort);
#else /* ! CONFIG_VNC_WS */ if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
error_setg(errp, "Websockets protocol requires gnutls support"); error_setg(errp, "SHA1 hash support is required for websockets");
goto fail; goto fail;
#endif /* ! CONFIG_VNC_WS */ }
} }
#ifdef CONFIG_VNC_JPEG #ifdef CONFIG_VNC_JPEG
@ -3668,9 +3635,7 @@ void vnc_display_open(const char *id, Error **errp)
/* connect to viewer */ /* connect to viewer */
int csock; int csock;
vs->lsock = -1; vs->lsock = -1;
#ifdef CONFIG_VNC_WS
vs->lwebsock = -1; vs->lwebsock = -1;
#endif
if (strncmp(vnc, "unix:", 5) == 0) { if (strncmp(vnc, "unix:", 5) == 0) {
csock = unix_connect(vnc+5, errp); csock = unix_connect(vnc+5, errp);
} else { } else {
@ -3693,7 +3658,6 @@ void vnc_display_open(const char *id, Error **errp)
if (vs->lsock < 0) { if (vs->lsock < 0) {
goto fail; goto fail;
} }
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) { if (vs->ws_enabled) {
vs->lwebsock = inet_listen_opts(wsopts, 0, errp); vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
if (vs->lwebsock < 0) { if (vs->lwebsock < 0) {
@ -3704,16 +3668,13 @@ void vnc_display_open(const char *id, Error **errp)
goto fail; goto fail;
} }
} }
#endif /* CONFIG_VNC_WS */
} }
vs->enabled = true; vs->enabled = true;
qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) { if (vs->ws_enabled) {
qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
NULL, vs); NULL, vs);
} }
#endif /* CONFIG_VNC_WS */
} }
qemu_opts_del(sopts); qemu_opts_del(sopts);
qemu_opts_del(wsopts); qemu_opts_del(wsopts);
@ -3723,9 +3684,7 @@ fail:
qemu_opts_del(sopts); qemu_opts_del(sopts);
qemu_opts_del(wsopts); qemu_opts_del(wsopts);
vs->enabled = false; vs->enabled = false;
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false; vs->ws_enabled = false;
#endif /* CONFIG_VNC_WS */
} }
void vnc_display_add_client(const char *id, int csock, bool skipauth) void vnc_display_add_client(const char *id, int csock, bool skipauth)

View File

@ -108,9 +108,7 @@ typedef struct VncDisplay VncDisplay;
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
#include "vnc-auth-sasl.h" #include "vnc-auth-sasl.h"
#endif #endif
#ifdef CONFIG_VNC_WS
#include "vnc-ws.h" #include "vnc-ws.h"
#endif
struct VncRectStat struct VncRectStat
{ {
@ -156,10 +154,8 @@ struct VncDisplay
int connections_limit; int connections_limit;
VncSharePolicy share_policy; VncSharePolicy share_policy;
int lsock; int lsock;
#ifdef CONFIG_VNC_WS
int lwebsock; int lwebsock;
bool ws_enabled; bool ws_enabled;
#endif
DisplaySurface *ds; DisplaySurface *ds;
DisplayChangeListener dcl; DisplayChangeListener dcl;
kbd_layout_t *kbd_layout; kbd_layout_t *kbd_layout;
@ -294,21 +290,17 @@ struct VncState
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
VncStateSASL sasl; VncStateSASL sasl;
#endif #endif
#ifdef CONFIG_VNC_WS
bool encode_ws; bool encode_ws;
bool websocket; bool websocket;
#endif /* CONFIG_VNC_WS */
VncClientInfo *info; VncClientInfo *info;
Buffer output; Buffer output;
Buffer input; Buffer input;
#ifdef CONFIG_VNC_WS
Buffer ws_input; Buffer ws_input;
Buffer ws_output; Buffer ws_output;
size_t ws_payload_remain; size_t ws_payload_remain;
WsMask ws_payload_mask; WsMask ws_payload_mask;
#endif
/* current output mode information */ /* current output mode information */
VncWritePixels *write_pixels; VncWritePixels *write_pixels;
PixelFormat client_pf; PixelFormat client_pf;