Commit 8e9b0d24 authored by Daniel P. Berrangé's avatar Daniel P. Berrangé Committed by Paolo Bonzini
Browse files

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: default avatarDaniel P. Berrange <berrange@redhat.com>
Message-Id: <1435770638-25715-9-git-send-email-berrange@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 488981a4
Loading
Loading
Loading
Loading
+1 −18
Original line number Diff line number Diff line
@@ -246,7 +246,6 @@ vnc_tls=""
vnc_sasl=""
vnc_jpeg=""
vnc_png=""
vnc_ws=""
xen=""
xen_ctrl_version=""
xen_pci_passthrough=""
@@ -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"
@@ -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; }
@@ -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

@@ -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"
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+10 −12
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

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

#ifdef CONFIG_VNC_TLS
#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)
{
    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;
    }
+0 −2
Original line number Diff line number Diff line
@@ -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

+13 −54
Original line number Diff line number Diff line
@@ -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
@@ -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) {
@@ -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;
@@ -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);

@@ -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);
        }
    }
@@ -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);
@@ -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) {
@@ -1549,9 +1536,7 @@ void vnc_client_read(void *opaque)
                vnc_client_error(vs);
                return;
            }
        } else
#endif /* CONFIG_VNC_WS */
        {
        } else {
            ret = vnc_client_read_plain(vs);
        }
    if (!ret) {
@@ -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);
@@ -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
@@ -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);
    }
@@ -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);
    }

@@ -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);
    }

@@ -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",
@@ -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;
@@ -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
@@ -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");
        if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
            error_setg(errp, "SHA1 hash support is required for websockets");
            goto fail;
#endif /* ! CONFIG_VNC_WS */
        }
    }

#ifdef CONFIG_VNC_JPEG
@@ -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 {
@@ -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) {
@@ -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);
@@ -3723,9 +3684,7 @@ fail:
    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)
Loading