Commit 3e305e4a authored by Daniel P. Berrangé's avatar Daniel P. Berrangé
Browse files

ui: convert VNC server to use QCryptoTLSSession



Switch VNC server over to using the QCryptoTLSSession object
for the TLS session. This removes the direct use of gnutls
from the VNC server code. It also removes most knowledge
about TLS certificate handling from the VNC server code.
This has the nice effect that all the CONFIG_VNC_TLS
conditionals go away and the user gets an actual error
message when requesting TLS instead of it being silently
ignored.

With this change, the existing configuration options for
enabling TLS with -vnc are deprecated.

Old syntax for anon-DH credentials:

  -vnc hostname:0,tls

New syntax:

  -object tls-creds-anon,id=tls0,endpoint=server \
  -vnc hostname:0,tls-creds=tls0

Old syntax for x509 credentials, no client certs:

  -vnc hostname:0,tls,x509=/path/to/certs

New syntax:

  -object tls-creds-x509,id=tls0,dir=/path/to/certs,endpoint=server,verify-peer=no \
  -vnc hostname:0,tls-creds=tls0

Old syntax for x509 credentials, requiring client certs:

  -vnc hostname:0,tls,x509verify=/path/to/certs

New syntax:

  -object tls-creds-x509,id=tls0,dir=/path/to/certs,endpoint=server,verify-peer=yes \
  -vnc hostname:0,tls-creds=tls0

This aligns VNC with the way TLS credentials are to be
configured in the future for chardev, nbd and migration
backends. It also has the benefit that the same TLS
credentials can be shared across multiple VNC server
instances, if desired.

If someone uses the deprecated syntax, it will internally
result in the creation of a 'tls-creds' object with an ID
based on the VNC server ID. This allows backwards compat
with the CLI syntax, while still deleting all the original
TLS code from the VNC server.

Signed-off-by: default avatarDaniel P. Berrange <berrange@redhat.com>
parent fdd1ab6a
Loading
Loading
Loading
Loading
+0 −31
Original line number Diff line number Diff line
@@ -242,7 +242,6 @@ vnc="yes"
sparse="no"
uuid=""
vde=""
vnc_tls=""
vnc_sasl=""
vnc_jpeg=""
vnc_png=""
@@ -883,10 +882,6 @@ for opt do
  ;;
  --disable-strip) strip_opt="no"
  ;;
  --disable-vnc-tls) vnc_tls="no"
  ;;
  --enable-vnc-tls) vnc_tls="yes"
  ;;
  --disable-vnc-sasl) vnc_sasl="no"
  ;;
  --enable-vnc-sasl) vnc_sasl="yes"
@@ -2409,28 +2404,6 @@ EOF
  fi
fi

##########################################
# VNC TLS/WS detection
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; }
EOF
  vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
  vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
  if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then
    if test "$vnc_tls" != "no" ; then
      vnc_tls=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
    vnc_tls=no
  fi
fi

##########################################
# VNC SASL detection
@@ -4601,7 +4574,6 @@ echo "Block whitelist (ro) $block_drv_ro_whitelist"
echo "VirtFS support    $virtfs"
echo "VNC support       $vnc"
if test "$vnc" = "yes" ; then
    echo "VNC TLS support   $vnc_tls"
    echo "VNC SASL support  $vnc_sasl"
    echo "VNC JPEG support  $vnc_jpeg"
    echo "VNC PNG support   $vnc_png"
@@ -4810,9 +4782,6 @@ echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak
if test "$vnc" = "yes" ; then
  echo "CONFIG_VNC=y" >> $config_host_mak
fi
if test "$vnc_tls" = "yes" ; then
  echo "CONFIG_VNC_TLS=y" >> $config_host_mak
fi
if test "$vnc_sasl" = "yes" ; then
  echo "CONFIG_VNC_SASL=y" >> $config_host_mak
fi
+26 −2
Original line number Diff line number Diff line
@@ -1217,8 +1217,9 @@ By definition the Websocket port is 5700+@var{display}. If @var{host} is
specified connections will only be allowed from this host.
As an alternative the Websocket port could be specified by using
@code{websocket}=@var{port}.
TLS encryption for the Websocket connection is supported if the required
certificates are specified with the VNC option @option{x509}.
If no TLS credentials are provided, the websocket connection runs in
unencrypted mode. If TLS credentials are provided, the websocket connection
requires encrypted client connections.

@item password

@@ -1239,6 +1240,20 @@ date and time).
You can also use keywords "now" or "never" for the expiration time to
allow <protocol> password to expire immediately or never expire.

@item tls-creds=@var{ID}

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
and the websocket socket (if enabled). Setting TLS credentials
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.

The @option{tls-creds} parameter obsoletes the @option{tls},
@option{x509}, and @option{x509verify} options, and as such
it is not permitted to set both new and old type options at
the same time.

@item tls

Require that client use TLS when communicating with the VNC server. This
@@ -1246,6 +1261,9 @@ uses anonymous TLS credentials so is susceptible to a man-in-the-middle
attack. It is recommended that this option be combined with either the
@option{x509} or @option{x509verify} options.

This option is now deprecated in favor of using the @option{tls-creds}
argument.

@item x509=@var{/path/to/certificate/dir}

Valid if @option{tls} is specified. Require that x509 credentials are used
@@ -1255,6 +1273,9 @@ to provide authentication of the client when this is used. The path following
this option specifies where the x509 certificates are to be loaded from.
See the @ref{vnc_security} section for details on generating certificates.

This option is now deprecated in favour of using the @option{tls-creds}
argument.

@item x509verify=@var{/path/to/certificate/dir}

Valid if @option{tls} is specified. Require that x509 credentials are used
@@ -1268,6 +1289,9 @@ path following this option specifies where the x509 certificates are to
be loaded from. See the @ref{vnc_security} section for details on generating
certificates.

This option is now deprecated in favour of using the @option{tls-creds}
argument.

@item sasl

Require that the client use SASL to authenticate with the VNC server.
+1 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@ vnc-obj-y += vnc.o
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
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-y += vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o
+19 −17
Original line number Diff line number Diff line
@@ -525,21 +525,24 @@ void start_auth_sasl(VncState *vs)
        goto authabort;
    }

#ifdef CONFIG_VNC_TLS
    /* Inform SASL that we've got an external SSF layer from TLS/x509 */
    if (vs->auth == VNC_AUTH_VENCRYPT &&
        vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
        gnutls_cipher_algorithm_t cipher;
        Error *local_err = NULL;
        int keysize;
        sasl_ssf_t ssf;

        cipher = gnutls_cipher_get(vs->tls.session);
        if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
            VNC_DEBUG("%s", "cannot TLS get cipher size\n");
        keysize = qcrypto_tls_session_get_key_size(vs->tls,
                                                   &local_err);
        if (keysize < 0) {
            VNC_DEBUG("cannot TLS get cipher size: %s\n",
                      error_get_pretty(local_err));
            error_free(local_err);
            sasl_dispose(&vs->sasl.conn);
            vs->sasl.conn = NULL;
            goto authabort;
        }
        ssf *= 8; /* tls key size is bytes, sasl wants bits */
        ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */

        err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
        if (err != SASL_OK) {
@@ -549,20 +552,19 @@ void start_auth_sasl(VncState *vs)
            vs->sasl.conn = NULL;
            goto authabort;
        }
    } else
#endif /* CONFIG_VNC_TLS */
    } else {
        vs->sasl.wantSSF = 1;
    }

    memset (&secprops, 0, sizeof secprops);
    /* Inform SASL that we've got an external SSF layer from TLS */
    if (vs->vd->is_unix
#ifdef CONFIG_VNC_TLS
        /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
           is not sufficiently strong */
        || (vs->auth == VNC_AUTH_VENCRYPT &&
            vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)
#endif /* CONFIG_VNC_TLS */
        ) {
    /* Inform SASL that we've got an external SSF layer from TLS.
     *
     * Disable SSF, if using TLS+x509+SASL only. TLS without x509
     * is not sufficiently strong
     */
    if (vs->vd->is_unix ||
        (vs->auth == VNC_AUTH_VENCRYPT &&
         vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
        /* If we've got TLS or UNIX domain sock, we don't care about SSF */
        secprops.min_ssf = 0;
        secprops.max_ssf = 0;
+45 −35
Original line number Diff line number Diff line
@@ -67,38 +67,42 @@ static void vnc_tls_handshake_io(void *opaque);

static int vnc_start_vencrypt_handshake(VncState *vs)
{
    int ret;
    Error *err = NULL;

    if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
       if (!gnutls_error_is_fatal(ret)) {
           VNC_DEBUG("Handshake interrupted (blocking)\n");
           if (!gnutls_record_get_direction(vs->tls.session))
               qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
           else
               qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
           return 0;
       }
       VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
       vnc_client_error(vs);
       return -1;
    if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
        goto error;
    }

    if (vs->vd->tls.x509verify) {
        if (vnc_tls_validate_certificate(vs) < 0) {
            VNC_DEBUG("Client verification failed\n");
            vnc_client_error(vs);
            return -1;
        } else {
            VNC_DEBUG("Client verification passed\n");
    switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
    case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
        VNC_DEBUG("Handshake done, checking credentials\n");
        if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
            goto error;
        }
    }

    VNC_DEBUG("Handshake done, switching to TLS data mode\n");
        VNC_DEBUG("Client verification passed, starting TLS I/O\n");
        qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);

        start_auth_vencrypt_subauth(vs);
        break;

    case QCRYPTO_TLS_HANDSHAKE_RECVING:
        VNC_DEBUG("Handshake interrupted (blocking read)\n");
        qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
        break;

    case QCRYPTO_TLS_HANDSHAKE_SENDING:
        VNC_DEBUG("Handshake interrupted (blocking write)\n");
        qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
        break;
    }

    return 0;

 error:
    VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
    error_free(err);
    vnc_client_error(vs);
    return -1;
}

static void vnc_tls_handshake_io(void *opaque)
@@ -110,14 +114,6 @@ static void vnc_tls_handshake_io(void *opaque)
}



#define NEED_X509_AUTH(vs)                              \
    ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
     (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
     (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN ||  \
     (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)


static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
{
    int auth = read_u32(data, 0);
@@ -128,15 +124,29 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
        vnc_flush(vs);
        vnc_client_error(vs);
    } else {
        Error *err = NULL;
        VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
        vnc_write_u8(vs, 1); /* Accept auth */
        vnc_flush(vs);

        if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
            VNC_DEBUG("Failed to setup TLS\n");
        vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
                                          NULL,
                                          vs->vd->tlsaclname,
                                          QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
                                          &err);
        if (!vs->tls) {
            VNC_DEBUG("Failed to setup TLS %s\n",
                      error_get_pretty(err));
            error_free(err);
            vnc_client_error(vs);
            return 0;
        }

        qcrypto_tls_session_set_callbacks(vs->tls,
                                          vnc_tls_push,
                                          vnc_tls_pull,
                                          vs);

        VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
        if (vnc_start_vencrypt_handshake(vs) < 0) {
            VNC_DEBUG("Failed to start TLS handshake\n");
Loading