qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2] TLS support for VNC Websockets


From: Anthony Liguori
Subject: Re: [Qemu-devel] [PATCH v2] TLS support for VNC Websockets
Date: Tue, 30 Apr 2013 09:58:40 -0500
User-agent: Notmuch/0.15.2+77~g661dcf8 (http://notmuchmail.org) Emacs/23.3.1 (x86_64-pc-linux-gnu)

Tim Hardeck <address@hidden> writes:

> Added TLS support to the VNC QEMU Websockets implementation.
> VNC-TLS needs to be enabled for this feature to be used.
>
> The required certificates are specified as in case of VNC-TLS
> with the VNC parameter "x509=<path>".
>
> If the server certificate isn't signed by a rooth authority it needs to
> be manually imported in the browser because at least in case of Firefox
> and Chrome there is no user dialog, the connection just gets canceled.
>
> As a side note VEncrypt over Websocket doesn't work atm because TLS can't
> be stacked in the current implementation. (It also didn't work before)
> Nevertheless to my knowledge there is no HTML 5 VNC client which supports
> it and the Websocket connection can be encrypted with regular TLS now so
> it should be fine for most use cases.
>
> Signed-off-by: Tim Hardeck <address@hidden>

It would have been nice to perhaps split out a refactoring patch of the
existing TLS code but it looks okay as-is.

I'm not a TLS expert but:

Reviewed-by: Anthony Liguori <address@hidden>

I'll give folks a couple more days to review and then I'll apply.  Thanks.

Regards,

Anthony Liguori

> ---
>
> Changes v2
>
> * a peek buffer of 4 Byte is enough
> ---
>  qemu-options.hx |  2 ++
>  ui/vnc-tls.c    | 61 +++++++++++++++++++++++++---------------
>  ui/vnc-ws.c     | 63 ++++++++++++++++++++++++++++++++++++++++++
>  ui/vnc-ws.h     |  3 ++
>  ui/vnc.c        | 86 
> ++++++++++++++++++++++++++++++++++++++++++++-------------
>  ui/vnc.h        |  5 +++-
>  6 files changed, 178 insertions(+), 42 deletions(-)
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 7cd6002..e19db6f 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1127,6 +1127,8 @@ By definition the Websocket port is address@hidden If 
> @var{host} is
>  specified connections will only be allowed from this host.
>  As an alternative the Websocket port could be specified by using
>  @address@hidden
> +TLS encryption for the Websocket connection is supported if the required
> +certificates are specified with the VNC option @option{x509}.
>  
>  @item password
>  
> diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
> index 8d4cc8e..50275de 100644
> --- a/ui/vnc-tls.c
> +++ b/ui/vnc-tls.c
> @@ -334,29 +334,38 @@ static int vnc_set_gnutls_priority(gnutls_session_t s, 
> int x509)
>  
>  int vnc_tls_client_setup(struct VncState *vs,
>                           int needX509Creds) {
> +    VncStateTLS *tls;
>  
>      VNC_DEBUG("Do TLS setup\n");
> +#ifdef CONFIG_VNC_WS
> +    if (vs->websocket) {
> +        tls = &vs->ws_tls;
> +    } else
> +#endif /* CONFIG_VNC_WS */
> +    {
> +        tls = &vs->tls;
> +    }
>      if (vnc_tls_initialize() < 0) {
>          VNC_DEBUG("Failed to init TLS\n");
>          vnc_client_error(vs);
>          return -1;
>      }
> -    if (vs->tls.session == NULL) {
> -        if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
> +    if (tls->session == NULL) {
> +        if (gnutls_init(&tls->session, GNUTLS_SERVER) < 0) {
>              vnc_client_error(vs);
>              return -1;
>          }
>  
> -        if (gnutls_set_default_priority(vs->tls.session) < 0) {
> -            gnutls_deinit(vs->tls.session);
> -            vs->tls.session = NULL;
> +        if (gnutls_set_default_priority(tls->session) < 0) {
> +            gnutls_deinit(tls->session);
> +            tls->session = NULL;
>              vnc_client_error(vs);
>              return -1;
>          }
>  
> -        if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) {
> -            gnutls_deinit(vs->tls.session);
> -            vs->tls.session = NULL;
> +        if (vnc_set_gnutls_priority(tls->session, needX509Creds) < 0) {
> +            gnutls_deinit(tls->session);
> +            tls->session = NULL;
>              vnc_client_error(vs);
>              return -1;
>          }
> @@ -364,43 +373,43 @@ int vnc_tls_client_setup(struct VncState *vs,
>          if (needX509Creds) {
>              gnutls_certificate_server_credentials x509_cred = 
> vnc_tls_initialize_x509_cred(vs->vd);
>              if (!x509_cred) {
> -                gnutls_deinit(vs->tls.session);
> -                vs->tls.session = NULL;
> +                gnutls_deinit(tls->session);
> +                tls->session = NULL;
>                  vnc_client_error(vs);
>                  return -1;
>              }
> -            if (gnutls_credentials_set(vs->tls.session, 
> GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
> -                gnutls_deinit(vs->tls.session);
> -                vs->tls.session = NULL;
> +            if (gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, 
> x509_cred) < 0) {
> +                gnutls_deinit(tls->session);
> +                tls->session = NULL;
>                  gnutls_certificate_free_credentials(x509_cred);
>                  vnc_client_error(vs);
>                  return -1;
>              }
>              if (vs->vd->tls.x509verify) {
>                  VNC_DEBUG("Requesting a client certificate\n");
> -                gnutls_certificate_server_set_request (vs->tls.session, 
> GNUTLS_CERT_REQUEST);
> +                gnutls_certificate_server_set_request (tls->session, 
> GNUTLS_CERT_REQUEST);
>              }
>  
>          } else {
>              gnutls_anon_server_credentials_t anon_cred = 
> vnc_tls_initialize_anon_cred();
>              if (!anon_cred) {
> -                gnutls_deinit(vs->tls.session);
> -                vs->tls.session = NULL;
> +                gnutls_deinit(tls->session);
> +                tls->session = NULL;
>                  vnc_client_error(vs);
>                  return -1;
>              }
> -            if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, 
> anon_cred) < 0) {
> -                gnutls_deinit(vs->tls.session);
> -                vs->tls.session = NULL;
> +            if (gnutls_credentials_set(tls->session, GNUTLS_CRD_ANON, 
> anon_cred) < 0) {
> +                gnutls_deinit(tls->session);
> +                tls->session = NULL;
>                  gnutls_anon_free_server_credentials(anon_cred);
>                  vnc_client_error(vs);
>                  return -1;
>              }
>          }
>  
> -        gnutls_transport_set_ptr(vs->tls.session, 
> (gnutls_transport_ptr_t)vs);
> -        gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
> -        gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
> +        gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr_t)vs);
> +        gnutls_transport_set_push_function(tls->session, vnc_tls_push);
> +        gnutls_transport_set_pull_function(tls->session, vnc_tls_pull);
>      }
>      return 0;
>  }
> @@ -414,6 +423,14 @@ void vnc_tls_client_cleanup(struct VncState *vs)
>      }
>      vs->tls.wiremode = VNC_WIREMODE_CLEAR;
>      g_free(vs->tls.dname);
> +#ifdef CONFIG_VNC_WS
> +    if (vs->ws_tls.session) {
> +        gnutls_deinit(vs->ws_tls.session);
> +        vs->ws_tls.session = NULL;
> +    }
> +    vs->ws_tls.wiremode = VNC_WIREMODE_CLEAR;
> +    g_free(vs->ws_tls.dname);
> +#endif /* CONFIG_VNC_WS */
>  }
>  
>  
> diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
> index 3e30209..df89315 100644
> --- a/ui/vnc-ws.c
> +++ b/ui/vnc-ws.c
> @@ -20,6 +20,69 @@
>  
>  #include "vnc.h"
>  
> +#ifdef CONFIG_VNC_TLS
> +#include "qemu/sockets.h"
> +
> +static void vncws_tls_handshake_io(void *opaque);
> +
> +static int vncws_start_tls_handshake(struct VncState *vs)
> +{
> +    int ret = gnutls_handshake(vs->ws_tls.session);
> +
> +    if (ret < 0) {
> +        if (!gnutls_error_is_fatal(ret)) {
> +            VNC_DEBUG("Handshake interrupted (blocking)\n");
> +            if (!gnutls_record_get_direction(vs->ws_tls.session)) {
> +                qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io,
> +                                    NULL, vs);
> +            } else {
> +                qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io,
> +                                    vs);
> +            }
> +            return 0;
> +        }
> +        VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
> +        vnc_client_error(vs);
> +        return -1;
> +    }
> +
> +    VNC_DEBUG("Handshake done, switching to TLS data mode\n");
> +    vs->ws_tls.wiremode = VNC_WIREMODE_TLS;
> +    qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs);
> +
> +    return 0;
> +}
> +
> +static void vncws_tls_handshake_io(void *opaque)
> +{
> +    struct VncState *vs = (struct VncState *)opaque;
> +
> +    VNC_DEBUG("Handshake IO continue\n");
> +    vncws_start_tls_handshake(vs);
> +}
> +
> +void vncws_tls_handshake_peek(void *opaque)
> +{
> +    VncState *vs = opaque;
> +    long ret;
> +
> +    if (!vs->ws_tls.session) {
> +        char peek[4];
> +        ret = qemu_recv(vs->csock, peek, sizeof(peek), MSG_PEEK);
> +        if (ret && (strncmp(peek, "\x16", 1) == 0
> +                    || strncmp(peek, "\x80", 1) == 0)) {
> +            VNC_DEBUG("TLS Websocket connection recognized");
> +            vnc_tls_client_setup(vs, 1);
> +            vncws_start_tls_handshake(vs);
> +        } else {
> +            vncws_handshake_read(vs);
> +        }
> +    } else {
> +        qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, 
> vs);
> +    }
> +}
> +#endif /* CONFIG_VNC_TLS */
> +
>  void vncws_handshake_read(void *opaque)
>  {
>      VncState *vs = opaque;
> diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
> index 039a587..95c1b0a 100644
> --- a/ui/vnc-ws.h
> +++ b/ui/vnc-ws.h
> @@ -74,6 +74,9 @@ enum {
>      WS_OPCODE_PONG = 0xA
>  };
>  
> +#ifdef CONFIG_VNC_TLS
> +void vncws_tls_handshake_peek(void *opaque);
> +#endif /* CONFIG_VNC_TLS */
>  void vncws_handshake_read(void *opaque);
>  long vnc_client_write_ws(VncState *vs);
>  long vnc_client_read_ws(VncState *vs);
> diff --git a/ui/vnc.c b/ui/vnc.c
> index 5ddb696..99d5e61 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -1118,6 +1118,23 @@ void vnc_client_error(VncState *vs)
>      vnc_disconnect_start(vs);
>  }
>  
> +#ifdef CONFIG_VNC_TLS
> +static long vnc_client_write_tls(gnutls_session_t *session,
> +                                 const uint8_t *data,
> +                                 size_t datalen)
> +{
> +    long ret = gnutls_write(*session, data, datalen);
> +    if (ret < 0) {
> +        if (ret == GNUTLS_E_AGAIN) {
> +            errno = EAGAIN;
> +        } else {
> +            errno = EIO;
> +        }
> +        ret = -1;
> +    }
> +    return ret;
> +}
> +#endif /* CONFIG_VNC_TLS */
>  
>  /*
>   * Called to write a chunk of data to the client socket. The data may
> @@ -1139,17 +1156,20 @@ long vnc_client_write_buf(VncState *vs, const uint8_t 
> *data, size_t datalen)
>      long ret;
>  #ifdef CONFIG_VNC_TLS
>      if (vs->tls.session) {
> -        ret = gnutls_write(vs->tls.session, data, datalen);
> -        if (ret < 0) {
> -            if (ret == GNUTLS_E_AGAIN)
> -                errno = EAGAIN;
> -            else
> -                errno = EIO;
> -            ret = -1;
> +        ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
> +    } else {
> +#ifdef CONFIG_VNC_WS
> +        if (vs->ws_tls.session) {
> +            ret = vnc_client_write_tls(&vs->ws_tls.session, data, datalen);
> +        } else
> +#endif /* CONFIG_VNC_WS */
> +#endif /* CONFIG_VNC_TLS */
> +        {
> +            ret = send(vs->csock, (const void *)data, datalen, 0);
>          }
> -    } else
> +#ifdef CONFIG_VNC_TLS
> +    }
>  #endif /* CONFIG_VNC_TLS */
> -        ret = send(vs->csock, (const void *)data, datalen, 0);
>      VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
>      return vnc_client_io_error(vs, ret, socket_error());
>  }
> @@ -1247,6 +1267,22 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, 
> size_t expecting)
>      vs->read_handler_expect = expecting;
>  }
>  
> +#ifdef CONFIG_VNC_TLS
> +static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
> +                                size_t datalen)
> +{
> +    long ret = gnutls_read(*session, data, datalen);
> +    if (ret < 0) {
> +        if (ret == GNUTLS_E_AGAIN) {
> +            errno = EAGAIN;
> +        } else {
> +            errno = EIO;
> +        }
> +        ret = -1;
> +    }
> +    return ret;
> +}
> +#endif /* CONFIG_VNC_TLS */
>  
>  /*
>   * Called to read a chunk of data from the client socket. The data may
> @@ -1268,17 +1304,20 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, 
> size_t datalen)
>      long ret;
>  #ifdef CONFIG_VNC_TLS
>      if (vs->tls.session) {
> -        ret = gnutls_read(vs->tls.session, data, datalen);
> -        if (ret < 0) {
> -            if (ret == GNUTLS_E_AGAIN)
> -                errno = EAGAIN;
> -            else
> -                errno = EIO;
> -            ret = -1;
> +        ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
> +    } else {
> +#ifdef CONFIG_VNC_WS
> +        if (vs->ws_tls.session) {
> +            ret = vnc_client_read_tls(&vs->ws_tls.session, data, datalen);
> +        } else
> +#endif /* CONFIG_VNC_WS */
> +#endif /* CONFIG_VNC_TLS */
> +        {
> +            ret = qemu_recv(vs->csock, data, datalen, 0);
>          }
> -    } else
> +#ifdef CONFIG_VNC_TLS
> +    }
>  #endif /* CONFIG_VNC_TLS */
> -        ret = qemu_recv(vs->csock, data, datalen, 0);
>      VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
>      return vnc_client_io_error(vs, ret, socket_error());
>  }
> @@ -2736,7 +2775,16 @@ static void vnc_connect(VncDisplay *vd, int csock, int 
> skipauth, bool websocket)
>  #ifdef CONFIG_VNC_WS
>      if (websocket) {
>          vs->websocket = 1;
> -        qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, 
> vs);
> +#ifdef CONFIG_VNC_TLS
> +        if (vd->tls.x509cert) {
> +            qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_peek,
> +                                 NULL, vs);
> +        } else
> +#endif /* CONFIG_VNC_TLS */
> +        {
> +            qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read,
> +                                 NULL, vs);
> +        }
>      } else
>  #endif /* CONFIG_VNC_WS */
>      {
> diff --git a/ui/vnc.h b/ui/vnc.h
> index 58e002e..e54a89a 100644
> --- a/ui/vnc.h
> +++ b/ui/vnc.h
> @@ -278,9 +278,12 @@ struct VncState
>      VncStateSASL sasl;
>  #endif
>  #ifdef CONFIG_VNC_WS
> +#ifdef CONFIG_VNC_TLS
> +    VncStateTLS ws_tls;
> +#endif /* CONFIG_VNC_TLS */
>      bool encode_ws;
>      bool websocket;
> -#endif
> +#endif /* CONFIG_VNC_WS */
>  
>      QObject *info;
>  
> -- 
> 1.8.1.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]