Skip to content

Commit

Permalink
ui: convert VNC websockets to use crypto APIs
Browse files Browse the repository at this point in the history
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 <[email protected]>
Message-Id: <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
  • Loading branch information
berrange authored and bonzini committed Jul 8, 2015
1 parent 488981a commit 8e9b0d2
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 95 deletions.
19 changes: 1 addition & 18 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ vnc_tls=""
vnc_sasl=""
vnc_jpeg=""
vnc_png=""
vnc_ws=""
xen=""
xen_ctrl_version=""
xen_pci_passthrough=""
Expand Down Expand Up @@ -896,10 +895,6 @@ for opt do
;;
--enable-vnc-png) vnc_png="yes"
;;
--disable-vnc-ws) vnc_ws="no"
;;
--enable-vnc-ws) vnc_ws="yes"
;;
--disable-slirp) slirp="no"
;;
--disable-uuid) uuid="no"
Expand Down Expand Up @@ -2343,7 +2338,7 @@ fi

##########################################
# 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
#include <gnutls/gnutls.h>
int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
Expand All @@ -2354,20 +2349,13 @@ EOF
if test "$vnc_tls" != "no" ; then
vnc_tls=yes
fi
if test "$vnc_ws" != "no" ; then
vnc_ws=yes
fi
libs_softmmu="$vnc_tls_libs $libs_softmmu"
QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags"
else
if test "$vnc_tls" = "yes" ; then
feature_not_found "vnc-tls" "Install gnutls devel"
fi
if test "$vnc_ws" = "yes" ; then
feature_not_found "vnc-ws" "Install gnutls devel"
fi
vnc_tls=no
vnc_ws=no
fi
fi

Expand Down Expand Up @@ -4496,7 +4484,6 @@ if test "$vnc" = "yes" ; then
echo "VNC SASL support $vnc_sasl"
echo "VNC JPEG support $vnc_jpeg"
echo "VNC PNG support $vnc_png"
echo "VNC WS support $vnc_ws"
fi
if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu"
Expand Down Expand Up @@ -4708,10 +4695,6 @@ fi
if test "$vnc_png" = "yes" ; then
echo "CONFIG_VNC_PNG=y" >> $config_host_mak
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
echo "CONFIG_FNMATCH=y" >> $config_host_mak
fi
Expand Down
2 changes: 1 addition & 1 deletion ui/Makefile.objs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.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_WS) += vnc-ws.o
vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o

common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
Expand Down
22 changes: 10 additions & 12 deletions ui/vnc-ws.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "vnc.h"
#include "qemu/main-loop.h"
#include "crypto/hash.h"

#ifdef CONFIG_VNC_TLS
#include "qemu/sockets.h"
Expand Down Expand Up @@ -203,24 +204,21 @@ static char *vncws_extract_handshake_entry(const char *handshake,
static void vncws_send_handshake_response(VncState *vs, const char* key)
{
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;
gnutls_datum_t in;
int ret;
Error *err = NULL;

g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);

/* hash and encode it */
in.data = (void *)combined_key;
in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN;
ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size);
if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) {
accept = g_base64_encode(hash, hash_size);
}
if (accept == NULL) {
VNC_DEBUG("Hashing Websocket combined key failed\n");
if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
combined_key,
WS_CLIENT_KEY_LEN + WS_GUID_LEN,
&accept,
&err) < 0) {
VNC_DEBUG("Hashing Websocket combined key failed %s\n",
error_get_pretty(err));
error_free(err);
vnc_client_error(vs);
return;
}
Expand Down
2 changes: 0 additions & 2 deletions ui/vnc-ws.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
#ifndef __QEMU_UI_VNC_WS_H
#define __QEMU_UI_VNC_WS_H

#include <gnutls/gnutls.h>

#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
#define SHA1_DIGEST_LEN 20

Expand Down
67 changes: 13 additions & 54 deletions ui/vnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "qemu/osdep.h"
#include "ui/input.h"
#include "qapi-event.h"
#include "crypto/hash.h"

#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
Expand Down Expand Up @@ -355,9 +356,7 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client)
info->base->host = g_strdup(host);
info->base->service = g_strdup(serv);
info->base->family = inet_netfamily(sa.ss_family);
#ifdef CONFIG_VNC_WS
info->base->websocket = client->websocket;
#endif

#ifdef CONFIG_VNC_TLS
if (client->tls.session && client->tls.dname) {
Expand Down Expand Up @@ -582,12 +581,10 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
info->server = qmp_query_server_entry(vd->lsock, false,
info->server);
}
#ifdef CONFIG_VNC_WS
if (vd->lwebsock != -1) {
info->server = qmp_query_server_entry(vd->lwebsock, true,
info->server);
}
#endif

item = g_new0(VncInfo2List, 1);
item->value = info;
Expand Down Expand Up @@ -1231,10 +1228,8 @@ void vnc_disconnect_finish(VncState *vs)

buffer_free(&vs->input);
buffer_free(&vs->output);
#ifdef CONFIG_VNC_WS
buffer_free(&vs->ws_input);
buffer_free(&vs->ws_output);
#endif /* CONFIG_VNC_WS */

qapi_free_VncClientInfo(vs->info);

Expand Down Expand Up @@ -1413,12 +1408,9 @@ static void vnc_client_write_locked(void *opaque)
} else
#endif /* CONFIG_VNC_SASL */
{
#ifdef CONFIG_VNC_WS
if (vs->encode_ws) {
vnc_client_write_ws(vs);
} else
#endif /* CONFIG_VNC_WS */
{
} else {
vnc_client_write_plain(vs);
}
}
Expand All @@ -1429,11 +1421,7 @@ void vnc_client_write(void *opaque)
VncState *vs = opaque;

vnc_lock_output(vs);
if (vs->output.offset
#ifdef CONFIG_VNC_WS
|| vs->ws_output.offset
#endif
) {
if (vs->output.offset || vs->ws_output.offset) {
vnc_client_write_locked(opaque);
} else if (vs->csock != -1) {
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
Expand Down Expand Up @@ -1539,7 +1527,6 @@ void vnc_client_read(void *opaque)
ret = vnc_client_read_sasl(vs);
else
#endif /* CONFIG_VNC_SASL */
#ifdef CONFIG_VNC_WS
if (vs->encode_ws) {
ret = vnc_client_read_ws(vs);
if (ret == -1) {
Expand All @@ -1549,10 +1536,8 @@ void vnc_client_read(void *opaque)
vnc_client_error(vs);
return;
}
} else
#endif /* CONFIG_VNC_WS */
{
ret = vnc_client_read_plain(vs);
} else {
ret = vnc_client_read_plain(vs);
}
if (!ret) {
if (vs->csock == -1)
Expand Down Expand Up @@ -1624,11 +1609,8 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs)
{
vnc_lock_output(vs);
if (vs->csock != -1 && (vs->output.offset
#ifdef CONFIG_VNC_WS
|| vs->ws_output.offset
#endif
)) {
if (vs->csock != -1 && (vs->output.offset ||
vs->ws_output.offset)) {
vnc_client_write_locked(vs);
}
vnc_unlock_output(vs);
Expand Down Expand Up @@ -3019,7 +3001,6 @@ static void vnc_connect(VncDisplay *vd, int csock,
VNC_DEBUG("New client on socket %d\n", csock);
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
qemu_set_nonblock(vs->csock);
#ifdef CONFIG_VNC_WS
if (websocket) {
vs->websocket = 1;
#ifdef CONFIG_VNC_TLS
Expand All @@ -3031,7 +3012,6 @@ static void vnc_connect(VncDisplay *vd, int csock,
qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
}
} else
#endif /* CONFIG_VNC_WS */
{
qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
}
Expand All @@ -3040,10 +3020,7 @@ static void vnc_connect(VncDisplay *vd, int csock,
vnc_qmp_event(vs, QAPI_EVENT_VNC_CONNECTED);
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);

#ifdef CONFIG_VNC_WS
if (!vs->websocket)
#endif
{
if (!vs->websocket) {
vnc_init_state(vs);
}

Expand Down Expand Up @@ -3099,12 +3076,9 @@ static void vnc_listen_read(void *opaque, bool websocket)

/* Catch-up */
graphic_hw_update(vs->dcl.con);
#ifdef CONFIG_VNC_WS
if (websocket) {
csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
} else
#endif /* CONFIG_VNC_WS */
{
} else {
csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
}

Expand All @@ -3119,12 +3093,10 @@ static void vnc_listen_regular_read(void *opaque)
vnc_listen_read(opaque, false);
}

#ifdef CONFIG_VNC_WS
static void vnc_listen_websocket_read(void *opaque)
{
vnc_listen_read(opaque, true);
}
#endif /* CONFIG_VNC_WS */

static const DisplayChangeListenerOps dcl_ops = {
.dpy_name = "vnc",
Expand All @@ -3150,9 +3122,7 @@ void vnc_display_init(const char *id)
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);

vs->lsock = -1;
#ifdef CONFIG_VNC_WS
vs->lwebsock = -1;
#endif

QTAILQ_INIT(&vs->clients);
vs->expires = TIME_MAX;
Expand Down Expand Up @@ -3186,14 +3156,12 @@ static void vnc_display_close(VncDisplay *vs)
close(vs->lsock);
vs->lsock = -1;
}
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false;
if (vs->lwebsock != -1) {
qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
close(vs->lwebsock);
vs->lwebsock = -1;
}
#endif /* CONFIG_VNC_WS */
vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS
Expand Down Expand Up @@ -3579,13 +3547,12 @@ void vnc_display_open(const char *id, Error **errp)

websocket = qemu_opt_get(opts, "websocket");
if (websocket) {
#ifdef CONFIG_VNC_WS
vs->ws_enabled = true;
qemu_opt_set(wsopts, "port", websocket, &error_abort);
#else /* ! CONFIG_VNC_WS */
error_setg(errp, "Websockets protocol requires gnutls support");
goto fail;
#endif /* ! CONFIG_VNC_WS */
if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
error_setg(errp, "SHA1 hash support is required for websockets");
goto fail;
}
}

#ifdef CONFIG_VNC_JPEG
Expand Down Expand Up @@ -3668,9 +3635,7 @@ void vnc_display_open(const char *id, Error **errp)
/* connect to viewer */
int csock;
vs->lsock = -1;
#ifdef CONFIG_VNC_WS
vs->lwebsock = -1;
#endif
if (strncmp(vnc, "unix:", 5) == 0) {
csock = unix_connect(vnc+5, errp);
} else {
Expand All @@ -3693,7 +3658,6 @@ void vnc_display_open(const char *id, Error **errp)
if (vs->lsock < 0) {
goto fail;
}
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) {
vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
if (vs->lwebsock < 0) {
Expand All @@ -3704,16 +3668,13 @@ void vnc_display_open(const char *id, Error **errp)
goto fail;
}
}
#endif /* CONFIG_VNC_WS */
}
vs->enabled = true;
qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) {
qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
NULL, vs);
}
#endif /* CONFIG_VNC_WS */
}
qemu_opts_del(sopts);
qemu_opts_del(wsopts);
Expand All @@ -3723,9 +3684,7 @@ void vnc_display_open(const char *id, Error **errp)
qemu_opts_del(sopts);
qemu_opts_del(wsopts);
vs->enabled = false;
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false;
#endif /* CONFIG_VNC_WS */
}

void vnc_display_add_client(const char *id, int csock, bool skipauth)
Expand Down
Loading

0 comments on commit 8e9b0d2

Please sign in to comment.