mvadkert / rpms / qemu

Forked from rpms/qemu 6 years ago
Clone
188e398
From a2f48883d67b606218c98dc4996cbb41d3dc0990 Mon Sep 17 00:00:00 2001
be685f7
From: aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
be685f7
Date: Fri, 6 Mar 2009 20:27:23 +0000
d28bc6c
Subject: [PATCH] Move TLS auth into separate file ("Daniel P. Berrange")
be685f7
be685f7
This patch refactors the existing TLS code to make the main VNC code
be685f7
more managable. The code moves to two new files
be685f7
be685f7
 - vnc-tls.c: generic helpers for TLS handshake & credential setup
be685f7
 - vnc-auth-vencrypt.c: the actual VNC TLS authentication mechanism.
be685f7
be685f7
The reason for this split is that there are other TLS based auth
be685f7
mechanisms which we may like to use in the future. These can all
be685f7
share the same vnc-tls.c routines. In addition this will facilitate
be685f7
anyone who may want to port the vnc-tls.c file to allow for choice
be685f7
of GNUTLS & NSS for impl.
be685f7
be685f7
The TLS state is moved out of the VncState struct, and into a separate
be685f7
VncStateTLS struct, defined in vnc-tls.h. This is then referenced from
be685f7
the main VncState. End size of the struct is the same, but it keeps
be685f7
things a little more managable.
be685f7
be685f7
The vnc.h file gains a bunch more function prototypes, for functions
be685f7
in vnc.c that were previously static, but now need to be accessed
be685f7
from the separate auth code files.
be685f7
be685f7
The only TLS related code still in the main vl.c is the command line
be685f7
argument handling / setup, and the low level I/O routines calling
be685f7
gnutls_send/recv.
be685f7
be685f7
(cherry picked from commit 5fb6c7a8b26eab1a22207d24b4784bd2b39ab54b)
be685f7
be685f7
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
be685f7
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
be685f7
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
d28bc6c
Fedora-patch: 05-vnc-tls-vencrypt.patch
be685f7
---
be685f7
 Makefile            |   11 +-
be685f7
 vnc-auth-vencrypt.c |  167 +++++++++++++++
be685f7
 vnc-auth-vencrypt.h |   33 +++
be685f7
 vnc-tls.c           |  414 ++++++++++++++++++++++++++++++++++++
be685f7
 vnc-tls.h           |   70 ++++++
be685f7
 vnc.c               |  581 ++++-----------------------------------------------
be685f7
 vnc.h               |   76 +++++---
be685f7
 7 files changed, 780 insertions(+), 572 deletions(-)
be685f7
 create mode 100644 vnc-auth-vencrypt.c
be685f7
 create mode 100644 vnc-auth-vencrypt.h
be685f7
 create mode 100644 vnc-tls.c
be685f7
 create mode 100644 vnc-tls.h
be685f7
be685f7
diff --git a/Makefile b/Makefile
d28bc6c
index f74b7eb..680939f 100644
be685f7
--- a/Makefile
be685f7
+++ b/Makefile
Glauber Costa 47b0758
@@ -149,6 +149,9 @@ ifdef CONFIG_CURSES
Glauber Costa 47b0758
 OBJS+=curses.o
Glauber Costa 47b0758
 endif
Glauber Costa 47b0758
 OBJS+=vnc.o d3des.o
Glauber Costa 47b0758
+ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
+OBJS+=vnc-tls.o vnc-auth-vencrypt.o
Glauber Costa 47b0758
+endif
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 ifdef CONFIG_COCOA
Glauber Costa 47b0758
 OBJS+=cocoa.o
Glauber Costa 47b0758
@@ -172,10 +175,16 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-vnc.o: vnc.c keymaps.h sdl_keysym.h vnchextile.h d3des.c d3des.h
Glauber Costa 47b0758
+vnc.h: vnc-tls.h vnc-auth-vencrypt.h keymaps.h
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
Glauber Costa 47b0758
 
Glauber Costa 47b0758
+vnc-tls.o: vnc-tls.c vnc.h
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
Glauber Costa 47b0758
+
Glauber Costa 47b0758
 curses.o: curses.c keymaps.h curses_keys.h
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 bt-host.o: CFLAGS += $(CONFIG_BLUEZ_CFLAGS)
be685f7
diff --git a/vnc-auth-vencrypt.c b/vnc-auth-vencrypt.c
be685f7
new file mode 100644
be685f7
index 0000000..1f113a7
Glauber Costa 47b0758
--- /dev/null
be685f7
+++ b/vnc-auth-vencrypt.c
Glauber Costa 47b0758
@@ -0,0 +1,167 @@
Glauber Costa 47b0758
+/*
Glauber Costa 47b0758
+ * QEMU VNC display driver: VeNCrypt authentication setup
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
Glauber Costa 47b0758
+ * Copyright (C) 2006 Fabrice Bellard
Glauber Costa 47b0758
+ * Copyright (C) 2009 Red Hat, Inc
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Glauber Costa 47b0758
+ * of this software and associated documentation files (the "Software"), to deal
Glauber Costa 47b0758
+ * in the Software without restriction, including without limitation the rights
Glauber Costa 47b0758
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Glauber Costa 47b0758
+ * copies of the Software, and to permit persons to whom the Software is
Glauber Costa 47b0758
+ * furnished to do so, subject to the following conditions:
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * The above copyright notice and this permission notice shall be included in
Glauber Costa 47b0758
+ * all copies or substantial portions of the Software.
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Glauber Costa 47b0758
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Glauber Costa 47b0758
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Glauber Costa 47b0758
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Glauber Costa 47b0758
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Glauber Costa 47b0758
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Glauber Costa 47b0758
+ * THE SOFTWARE.
Glauber Costa 47b0758
+ */
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#include "vnc.h"
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static void start_auth_vencrypt_subauth(VncState *vs)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    switch (vs->vd->subauth) {
Glauber Costa 47b0758
+    case VNC_AUTH_VENCRYPT_TLSNONE:
Glauber Costa 47b0758
+    case VNC_AUTH_VENCRYPT_X509NONE:
Glauber Costa 47b0758
+       VNC_DEBUG("Accept TLS auth none\n");
Glauber Costa 47b0758
+       vnc_write_u32(vs, 0); /* Accept auth completion */
Glauber Costa 47b0758
+       start_client_init(vs);
Glauber Costa 47b0758
+       break;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    case VNC_AUTH_VENCRYPT_TLSVNC:
Glauber Costa 47b0758
+    case VNC_AUTH_VENCRYPT_X509VNC:
Glauber Costa 47b0758
+       VNC_DEBUG("Start TLS auth VNC\n");
Glauber Costa 47b0758
+       start_auth_vnc(vs);
Glauber Costa 47b0758
+       break;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    default: /* Should not be possible, but just in case */
Glauber Costa 47b0758
+       VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
Glauber Costa 47b0758
+       vnc_write_u8(vs, 1);
Glauber Costa 47b0758
+       if (vs->minor >= 8) {
Glauber Costa 47b0758
+           static const char err[] = "Unsupported authentication type";
Glauber Costa 47b0758
+           vnc_write_u32(vs, sizeof(err));
Glauber Costa 47b0758
+           vnc_write(vs, err, sizeof(err));
Glauber Costa 47b0758
+       }
Glauber Costa 47b0758
+       vnc_client_error(vs);
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static void vnc_tls_handshake_io(void *opaque);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static int vnc_start_vencrypt_handshake(struct VncState *vs) {
Glauber Costa 47b0758
+    int ret;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
Glauber Costa 47b0758
+       if (!gnutls_error_is_fatal(ret)) {
Glauber Costa 47b0758
+           VNC_DEBUG("Handshake interrupted (blocking)\n");
Glauber Costa 47b0758
+           if (!gnutls_record_get_direction(vs->tls.session))
Glauber Costa 47b0758
+               qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
Glauber Costa 47b0758
+           else
Glauber Costa 47b0758
+               qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
Glauber Costa 47b0758
+           return 0;
Glauber Costa 47b0758
+       }
Glauber Costa 47b0758
+       VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+       vnc_client_error(vs);
Glauber Costa 47b0758
+       return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (vs->vd->tls.x509verify) {
Glauber Costa 47b0758
+	if (vnc_tls_validate_certificate(vs) < 0) {
Glauber Costa 47b0758
+	    VNC_DEBUG("Client verification failed\n");
Glauber Costa 47b0758
+	    vnc_client_error(vs);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	} else {
Glauber Costa 47b0758
+	    VNC_DEBUG("Client verification passed\n");
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    VNC_DEBUG("Handshake done, switching to TLS data mode\n");
Glauber Costa 47b0758
+    vs->tls.wiremode = VNC_WIREMODE_TLS;
Glauber Costa 47b0758
+    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    start_auth_vencrypt_subauth(vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static void vnc_tls_handshake_io(void *opaque) {
Glauber Costa 47b0758
+    struct VncState *vs = (struct VncState *)opaque;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    VNC_DEBUG("Handshake IO continue\n");
Glauber Costa 47b0758
+    vnc_start_vencrypt_handshake(vs);
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#define NEED_X509_AUTH(vs)			      \
Glauber Costa 47b0758
+    ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
Glauber Costa 47b0758
+     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
Glauber Costa 47b0758
+     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    int auth = read_u32(data, 0);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (auth != vs->vd->subauth) {
Glauber Costa 47b0758
+	VNC_DEBUG("Rejecting auth %d\n", auth);
Glauber Costa 47b0758
+	vnc_write_u8(vs, 0); /* Reject auth */
Glauber Costa 47b0758
+	vnc_flush(vs);
Glauber Costa 47b0758
+	vnc_client_error(vs);
Glauber Costa 47b0758
+    } else {
Glauber Costa 47b0758
+	VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
Glauber Costa 47b0758
+	vnc_write_u8(vs, 1); /* Accept auth */
Glauber Costa 47b0758
+	vnc_flush(vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
Glauber Costa 47b0758
+	    VNC_DEBUG("Failed to setup TLS\n");
Glauber Costa 47b0758
+	    return 0;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
Glauber Costa 47b0758
+	if (vnc_start_vencrypt_handshake(vs) < 0) {
Glauber Costa 47b0758
+	    VNC_DEBUG("Failed to start TLS handshake\n");
Glauber Costa 47b0758
+	    return 0;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    if (data[0] != 0 ||
Glauber Costa 47b0758
+	data[1] != 2) {
Glauber Costa 47b0758
+	VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
Glauber Costa 47b0758
+	vnc_write_u8(vs, 1); /* Reject version */
Glauber Costa 47b0758
+	vnc_flush(vs);
Glauber Costa 47b0758
+	vnc_client_error(vs);
Glauber Costa 47b0758
+    } else {
Glauber Costa 47b0758
+	VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
Glauber Costa 47b0758
+	vnc_write_u8(vs, 0); /* Accept version */
Glauber Costa 47b0758
+	vnc_write_u8(vs, 1); /* Number of sub-auths */
Glauber Costa 47b0758
+	vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
Glauber Costa 47b0758
+	vnc_flush(vs);
Glauber Costa 47b0758
+	vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+void start_auth_vencrypt(VncState *vs)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    /* Send VeNCrypt version 0.2 */
Glauber Costa 47b0758
+    vnc_write_u8(vs, 0);
Glauber Costa 47b0758
+    vnc_write_u8(vs, 2);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    vnc_read_when(vs, protocol_client_vencrypt_init, 2);
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
be685f7
diff --git a/vnc-auth-vencrypt.h b/vnc-auth-vencrypt.h
be685f7
new file mode 100644
be685f7
index 0000000..9f674c5
Glauber Costa 47b0758
--- /dev/null
be685f7
+++ b/vnc-auth-vencrypt.h
Glauber Costa 47b0758
@@ -0,0 +1,33 @@
Glauber Costa 47b0758
+/*
Glauber Costa 47b0758
+ * QEMU VNC display driver
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
Glauber Costa 47b0758
+ * Copyright (C) 2006 Fabrice Bellard
Glauber Costa 47b0758
+ * Copyright (C) 2009 Red Hat, Inc
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Glauber Costa 47b0758
+ * of this software and associated documentation files (the "Software"), to deal
Glauber Costa 47b0758
+ * in the Software without restriction, including without limitation the rights
Glauber Costa 47b0758
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Glauber Costa 47b0758
+ * copies of the Software, and to permit persons to whom the Software is
Glauber Costa 47b0758
+ * furnished to do so, subject to the following conditions:
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * The above copyright notice and this permission notice shall be included in
Glauber Costa 47b0758
+ * all copies or substantial portions of the Software.
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Glauber Costa 47b0758
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Glauber Costa 47b0758
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Glauber Costa 47b0758
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Glauber Costa 47b0758
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Glauber Costa 47b0758
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Glauber Costa 47b0758
+ * THE SOFTWARE.
Glauber Costa 47b0758
+ */
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__
Glauber Costa 47b0758
+#define __QEMU_VNC_AUTH_VENCRYPT_H__
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+void start_auth_vencrypt(VncState *vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */
be685f7
diff --git a/vnc-tls.c b/vnc-tls.c
be685f7
new file mode 100644
be685f7
index 0000000..666aa07
Glauber Costa 47b0758
--- /dev/null
be685f7
+++ b/vnc-tls.c
Glauber Costa 47b0758
@@ -0,0 +1,414 @@
Glauber Costa 47b0758
+/*
Glauber Costa 47b0758
+ * QEMU VNC display driver: TLS helpers
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
Glauber Costa 47b0758
+ * Copyright (C) 2006 Fabrice Bellard
Glauber Costa 47b0758
+ * Copyright (C) 2009 Red Hat, Inc
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Glauber Costa 47b0758
+ * of this software and associated documentation files (the "Software"), to deal
Glauber Costa 47b0758
+ * in the Software without restriction, including without limitation the rights
Glauber Costa 47b0758
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Glauber Costa 47b0758
+ * copies of the Software, and to permit persons to whom the Software is
Glauber Costa 47b0758
+ * furnished to do so, subject to the following conditions:
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * The above copyright notice and this permission notice shall be included in
Glauber Costa 47b0758
+ * all copies or substantial portions of the Software.
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Glauber Costa 47b0758
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Glauber Costa 47b0758
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Glauber Costa 47b0758
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Glauber Costa 47b0758
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Glauber Costa 47b0758
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Glauber Costa 47b0758
+ * THE SOFTWARE.
Glauber Costa 47b0758
+ */
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#include "vnc.h"
Glauber Costa 47b0758
+#include "qemu_socket.h"
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
Glauber Costa 47b0758
+/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
Glauber Costa 47b0758
+static void vnc_debug_gnutls_log(int level, const char* str) {
Glauber Costa 47b0758
+    VNC_DEBUG("%d %s", level, str);
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#define DH_BITS 1024
Glauber Costa 47b0758
+static gnutls_dh_params_t dh_params;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static int vnc_tls_initialize(void)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    static int tlsinitialized = 0;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (tlsinitialized)
Glauber Costa 47b0758
+	return 1;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (gnutls_global_init () < 0)
Glauber Costa 47b0758
+	return 0;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    /* XXX ought to re-generate diffie-hellmen params periodically */
Glauber Costa 47b0758
+    if (gnutls_dh_params_init (&dh_params) < 0)
Glauber Costa 47b0758
+	return 0;
Glauber Costa 47b0758
+    if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
Glauber Costa 47b0758
+	return 0;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
Glauber Costa 47b0758
+    gnutls_global_set_log_level(10);
Glauber Costa 47b0758
+    gnutls_global_set_log_function(vnc_debug_gnutls_log);
Glauber Costa 47b0758
+#endif
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    tlsinitialized = 1;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return 1;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
Glauber Costa 47b0758
+			    const void *data,
Glauber Costa 47b0758
+			    size_t len) {
Glauber Costa 47b0758
+    struct VncState *vs = (struct VncState *)transport;
Glauber Costa 47b0758
+    int ret;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+ retry:
Glauber Costa 47b0758
+    ret = send(vs->csock, data, len, 0);
Glauber Costa 47b0758
+    if (ret < 0) {
Glauber Costa 47b0758
+	if (errno == EINTR)
Glauber Costa 47b0758
+	    goto retry;
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    return ret;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
Glauber Costa 47b0758
+			    void *data,
Glauber Costa 47b0758
+			    size_t len) {
Glauber Costa 47b0758
+    struct VncState *vs = (struct VncState *)transport;
Glauber Costa 47b0758
+    int ret;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+ retry:
Glauber Costa 47b0758
+    ret = recv(vs->csock, data, len, 0);
Glauber Costa 47b0758
+    if (ret < 0) {
Glauber Costa 47b0758
+	if (errno == EINTR)
Glauber Costa 47b0758
+	    goto retry;
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    return ret;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    gnutls_anon_server_credentials anon_cred;
Glauber Costa 47b0758
+    int ret;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
Glauber Costa 47b0758
+	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    gnutls_anon_set_server_dh_params(anon_cred, dh_params);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return anon_cred;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    gnutls_certificate_credentials_t x509_cred;
Glauber Costa 47b0758
+    int ret;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (!vd->tls.x509cacert) {
Glauber Costa 47b0758
+	VNC_DEBUG("No CA x509 certificate specified\n");
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    if (!vd->tls.x509cert) {
Glauber Costa 47b0758
+	VNC_DEBUG("No server x509 certificate specified\n");
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    if (!vd->tls.x509key) {
Glauber Costa 47b0758
+	VNC_DEBUG("No server private key specified\n");
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
Glauber Costa 47b0758
+	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
Glauber Costa 47b0758
+						      vd->tls.x509cacert,
Glauber Costa 47b0758
+						      GNUTLS_X509_FMT_PEM)) < 0) {
Glauber Costa 47b0758
+	VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+	gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
Glauber Costa 47b0758
+						     vd->tls.x509cert,
Glauber Costa 47b0758
+						     vd->tls.x509key,
Glauber Costa 47b0758
+						     GNUTLS_X509_FMT_PEM)) < 0) {
Glauber Costa 47b0758
+	VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+	gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
+	return NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (vd->tls.x509cacrl) {
Glauber Costa 47b0758
+	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
Glauber Costa 47b0758
+							vd->tls.x509cacrl,
Glauber Costa 47b0758
+							GNUTLS_X509_FMT_PEM)) < 0) {
Glauber Costa 47b0758
+	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+	    gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
+	    return NULL;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    gnutls_certificate_set_dh_params (x509_cred, dh_params);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return x509_cred;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+int vnc_tls_validate_certificate(struct VncState *vs)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    int ret;
Glauber Costa 47b0758
+    unsigned int status;
Glauber Costa 47b0758
+    const gnutls_datum_t *certs;
Glauber Costa 47b0758
+    unsigned int nCerts, i;
Glauber Costa 47b0758
+    time_t now;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    VNC_DEBUG("Validating client certificate\n");
Glauber Costa 47b0758
+    if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
Glauber Costa 47b0758
+	VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if ((now = time(NULL)) == ((time_t)-1)) {
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (status != 0) {
Glauber Costa 47b0758
+	if (status & GNUTLS_CERT_INVALID)
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate is not trusted.\n");
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate hasn't got a known issuer.\n");
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (status & GNUTLS_CERT_REVOKED)
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate has been revoked.\n");
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate uses an insecure algorithm\n");
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    } else {
Glauber Costa 47b0758
+	VNC_DEBUG("Certificate is valid!\n");
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    /* Only support x509 for now */
Glauber Costa 47b0758
+    if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    for (i = 0 ; i < nCerts ; i++) {
Glauber Costa 47b0758
+	gnutls_x509_crt_t cert;
Glauber Costa 47b0758
+	VNC_DEBUG ("Checking certificate chain %d\n", i);
Glauber Costa 47b0758
+	if (gnutls_x509_crt_init (&cert) < 0)
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
Glauber Costa 47b0758
+	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_x509_crt_get_expiration_time (cert) < now) {
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate has expired\n");
Glauber Costa 47b0758
+	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_x509_crt_get_activation_time (cert) > now) {
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate is not yet activated\n");
Glauber Costa 47b0758
+	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_x509_crt_get_activation_time (cert) > now) {
Glauber Costa 47b0758
+	    VNC_DEBUG("The certificate is not yet activated\n");
Glauber Costa 47b0758
+	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+int vnc_tls_client_setup(struct VncState *vs,
Glauber Costa 47b0758
+			 int needX509Creds) {
Glauber Costa 47b0758
+    static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
Glauber Costa 47b0758
+    static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
Glauber Costa 47b0758
+    static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
Glauber Costa 47b0758
+    static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    VNC_DEBUG("Do TLS setup\n");
Glauber Costa 47b0758
+    if (vnc_tls_initialize() < 0) {
Glauber Costa 47b0758
+	VNC_DEBUG("Failed to init TLS\n");
Glauber Costa 47b0758
+	vnc_client_error(vs);
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    if (vs->tls.session == NULL) {
Glauber Costa 47b0758
+	if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
Glauber Costa 47b0758
+	    vnc_client_error(vs);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_set_default_priority(vs->tls.session) < 0) {
Glauber Costa 47b0758
+	    gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+	    vs->tls.session = NULL;
Glauber Costa 47b0758
+	    vnc_client_error(vs);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
Glauber Costa 47b0758
+	    gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+	    vs->tls.session = NULL;
Glauber Costa 47b0758
+	    vnc_client_error(vs);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
Glauber Costa 47b0758
+	    gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+	    vs->tls.session = NULL;
Glauber Costa 47b0758
+	    vnc_client_error(vs);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) {
Glauber Costa 47b0758
+	    gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+	    vs->tls.session = NULL;
Glauber Costa 47b0758
+	    vnc_client_error(vs);
Glauber Costa 47b0758
+	    return -1;
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	if (needX509Creds) {
Glauber Costa 47b0758
+	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
Glauber Costa 47b0758
+	    if (!x509_cred) {
Glauber Costa 47b0758
+		gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+		vs->tls.session = NULL;
Glauber Costa 47b0758
+		vnc_client_error(vs);
Glauber Costa 47b0758
+		return -1;
Glauber Costa 47b0758
+	    }
Glauber Costa 47b0758
+	    if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
Glauber Costa 47b0758
+		gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+		vs->tls.session = NULL;
Glauber Costa 47b0758
+		gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
+		vnc_client_error(vs);
Glauber Costa 47b0758
+		return -1;
Glauber Costa 47b0758
+	    }
Glauber Costa 47b0758
+	    if (vs->vd->tls.x509verify) {
Glauber Costa 47b0758
+		VNC_DEBUG("Requesting a client certificate\n");
Glauber Costa 47b0758
+		gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST);
Glauber Costa 47b0758
+	    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	} else {
Glauber Costa 47b0758
+	    gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
Glauber Costa 47b0758
+	    if (!anon_cred) {
Glauber Costa 47b0758
+		gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+		vs->tls.session = NULL;
Glauber Costa 47b0758
+		vnc_client_error(vs);
Glauber Costa 47b0758
+		return -1;
Glauber Costa 47b0758
+	    }
Glauber Costa 47b0758
+	    if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) {
Glauber Costa 47b0758
+		gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+		vs->tls.session = NULL;
Glauber Costa 47b0758
+		gnutls_anon_free_server_credentials(anon_cred);
Glauber Costa 47b0758
+		vnc_client_error(vs);
Glauber Costa 47b0758
+		return -1;
Glauber Costa 47b0758
+	    }
Glauber Costa 47b0758
+	}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+	gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
Glauber Costa 47b0758
+	gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
Glauber Costa 47b0758
+	gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+void vnc_tls_client_cleanup(struct VncState *vs)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    if (vs->tls.session) {
Glauber Costa 47b0758
+	gnutls_deinit(vs->tls.session);
Glauber Costa 47b0758
+	vs->tls.session = NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+    vs->tls.wiremode = VNC_WIREMODE_CLEAR;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+static int vnc_set_x509_credential(VncDisplay *vd,
Glauber Costa 47b0758
+				   const char *certdir,
Glauber Costa 47b0758
+				   const char *filename,
Glauber Costa 47b0758
+				   char **cred,
Glauber Costa 47b0758
+				   int ignoreMissing)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    struct stat sb;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    if (*cred) {
Glauber Costa 47b0758
+	qemu_free(*cred);
Glauber Costa 47b0758
+	*cred = NULL;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    strcpy(*cred, certdir);
Glauber Costa 47b0758
+    strcat(*cred, "/");
Glauber Costa 47b0758
+    strcat(*cred, filename);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    VNC_DEBUG("Check %s\n", *cred);
Glauber Costa 47b0758
+    if (stat(*cred, &sb) < 0) {
Glauber Costa 47b0758
+	qemu_free(*cred);
Glauber Costa 47b0758
+	*cred = NULL;
Glauber Costa 47b0758
+	if (ignoreMissing && errno == ENOENT)
Glauber Costa 47b0758
+	    return 0;
Glauber Costa 47b0758
+	return -1;
Glauber Costa 47b0758
+    }
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#define X509_CA_CERT_FILE "ca-cert.pem"
Glauber Costa 47b0758
+#define X509_CA_CRL_FILE "ca-crl.pem"
Glauber Costa 47b0758
+#define X509_SERVER_KEY_FILE "server-key.pem"
Glauber Costa 47b0758
+#define X509_SERVER_CERT_FILE "server-cert.pem"
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
Glauber Costa 47b0758
+			       const char *certdir)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
Glauber Costa 47b0758
+	goto cleanup;
Glauber Costa 47b0758
+    if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
Glauber Costa 47b0758
+	goto cleanup;
Glauber Costa 47b0758
+    if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
Glauber Costa 47b0758
+	goto cleanup;
Glauber Costa 47b0758
+    if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
Glauber Costa 47b0758
+	goto cleanup;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    return 0;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+ cleanup:
Glauber Costa 47b0758
+    qemu_free(vd->tls.x509cacert);
Glauber Costa 47b0758
+    qemu_free(vd->tls.x509cacrl);
Glauber Costa 47b0758
+    qemu_free(vd->tls.x509cert);
Glauber Costa 47b0758
+    qemu_free(vd->tls.x509key);
Glauber Costa 47b0758
+    vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
Glauber Costa 47b0758
+    return -1;
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
be685f7
diff --git a/vnc-tls.h b/vnc-tls.h
be685f7
new file mode 100644
be685f7
index 0000000..cda95b9
Glauber Costa 47b0758
--- /dev/null
be685f7
+++ b/vnc-tls.h
Glauber Costa 47b0758
@@ -0,0 +1,70 @@
Glauber Costa 47b0758
+/*
Glauber Costa 47b0758
+ * QEMU VNC display driver. TLS helpers
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
Glauber Costa 47b0758
+ * Copyright (C) 2006 Fabrice Bellard
Glauber Costa 47b0758
+ * Copyright (C) 2009 Red Hat, Inc
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
Glauber Costa 47b0758
+ * of this software and associated documentation files (the "Software"), to deal
Glauber Costa 47b0758
+ * in the Software without restriction, including without limitation the rights
Glauber Costa 47b0758
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Glauber Costa 47b0758
+ * copies of the Software, and to permit persons to whom the Software is
Glauber Costa 47b0758
+ * furnished to do so, subject to the following conditions:
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * The above copyright notice and this permission notice shall be included in
Glauber Costa 47b0758
+ * all copies or substantial portions of the Software.
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Glauber Costa 47b0758
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Glauber Costa 47b0758
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Glauber Costa 47b0758
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Glauber Costa 47b0758
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Glauber Costa 47b0758
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Glauber Costa 47b0758
+ * THE SOFTWARE.
Glauber Costa 47b0758
+ */
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#ifndef __QEMU_VNC_TLS_H__
Glauber Costa 47b0758
+#define __QEMU_VNC_TLS_H__
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#include <gnutls/gnutls.h>
Glauber Costa 47b0758
+#include <gnutls/x509.h>
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+enum {
Glauber Costa 47b0758
+    VNC_WIREMODE_CLEAR,
Glauber Costa 47b0758
+    VNC_WIREMODE_TLS,
Glauber Costa 47b0758
+};
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+typedef struct VncDisplayTLS VncDisplayTLS;
Glauber Costa 47b0758
+typedef struct VncStateTLS VncStateTLS;
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/* Server state */
Glauber Costa 47b0758
+struct VncDisplayTLS {
Glauber Costa 47b0758
+    int x509verify; /* Non-zero if server requests & validates client cert */
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+    /* Paths to x509 certs/keys */
Glauber Costa 47b0758
+    char *x509cacert;
Glauber Costa 47b0758
+    char *x509cacrl;
Glauber Costa 47b0758
+    char *x509cert;
Glauber Costa 47b0758
+    char *x509key;
Glauber Costa 47b0758
+};
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/* Per client state */
Glauber Costa 47b0758
+struct VncStateTLS {
Glauber Costa 47b0758
+    /* Whether data is being TLS encrypted yet */
Glauber Costa 47b0758
+    int wiremode;
Glauber Costa 47b0758
+    gnutls_session_t session;
Glauber Costa 47b0758
+};
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+int vnc_tls_client_setup(VncState *vs, int x509Creds);
Glauber Costa 47b0758
+void vnc_tls_client_cleanup(VncState *vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+int vnc_tls_validate_certificate(VncState *vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
Glauber Costa 47b0758
+			       const char *path);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#endif /* __QEMU_VNC_TLS_H__ */
Glauber Costa 47b0758
+
be685f7
diff --git a/vnc.c b/vnc.c
188e398
index 4d793ab..4da5fbb 100644
be685f7
--- a/vnc.c
be685f7
+++ b/vnc.c
Glauber Costa 47b0758
@@ -34,21 +34,6 @@
Glauber Costa 47b0758
 #include "vnc_keysym.h"
Glauber Costa 47b0758
 #include "d3des.h"
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-// #define _VNC_DEBUG 1
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-#ifdef _VNC_DEBUG
Glauber Costa 47b0758
-#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-#if defined(CONFIG_VNC_TLS) && _VNC_DEBUG >= 2
Glauber Costa 47b0758
-/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
Glauber Costa 47b0758
-static void vnc_debug_gnutls_log(int level, const char* str) {
Glauber Costa 47b0758
-    VNC_DEBUG("%d %s", level, str);
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-#endif /* CONFIG_VNC_TLS && _VNC_DEBUG */
Glauber Costa 47b0758
-#else
Glauber Costa 47b0758
-#define VNC_DEBUG(fmt, ...) do { } while (0)
Glauber Costa 47b0758
-#endif
Glauber Costa 47b0758
-
Glauber Costa 47b0758
 #define count_bits(c, v) { \
Glauber Costa 47b0758
     for (c = 0; v; v >>= 1) \
Glauber Costa 47b0758
     { \
188e398
@@ -204,16 +189,9 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
Glauber Costa 47b0758
    3) resolutions > 1024
Glauber Costa 47b0758
 */
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_write(VncState *vs, const void *data, size_t len);
Glauber Costa 47b0758
-static void vnc_write_u32(VncState *vs, uint32_t value);
Glauber Costa 47b0758
-static void vnc_write_s32(VncState *vs, int32_t value);
Glauber Costa 47b0758
-static void vnc_write_u16(VncState *vs, uint16_t value);
Glauber Costa 47b0758
-static void vnc_write_u8(VncState *vs, uint8_t value);
Glauber Costa 47b0758
-static void vnc_flush(VncState *vs);
Glauber Costa 47b0758
 static void vnc_update_client(void *opaque);
188e398
 static void vnc_disconnect_start(VncState *vs);
188e398
 static void vnc_disconnect_finish(VncState *vs);
Glauber Costa 47b0758
-static void vnc_client_read(void *opaque);
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 static void vnc_colordepth(VncState *vs);
Glauber Costa 47b0758
 
188e398
@@ -894,10 +872,7 @@ static void vnc_disconnect_finish(VncState *vs)
188e398
     if (vs->input.buffer) qemu_free(vs->input.buffer);
188e398
     if (vs->output.buffer) qemu_free(vs->output.buffer);
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
188e398
-    if (vs->tls_session) {
188e398
-        gnutls_deinit(vs->tls_session);
188e398
-        vs->tls_session = NULL;
188e398
-    }
188e398
+    vnc_tls_client_cleanup(vs);
Glauber Costa 47b0758
 #endif /* CONFIG_VNC_TLS */
188e398
     audio_del(vs);
Glauber Costa 47b0758
 
188e398
@@ -943,20 +918,21 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
Glauber Costa 47b0758
     return ret;
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_client_error(VncState *vs)
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+void vnc_client_error(VncState *vs)
Glauber Costa 47b0758
 {
188e398
     VNC_DEBUG("Closing down client sock: protocol error\n");
188e398
     vnc_disconnect_start(vs);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_client_write(void *opaque)
Glauber Costa 47b0758
+void vnc_client_write(void *opaque)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     long ret;
Glauber Costa 47b0758
     VncState *vs = opaque;
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-    if (vs->tls_session) {
Glauber Costa 47b0758
-	ret = gnutls_write(vs->tls_session, vs->output.buffer, vs->output.offset);
Glauber Costa 47b0758
+    if (vs->tls.session) {
Glauber Costa 47b0758
+	ret = gnutls_write(vs->tls.session, vs->output.buffer, vs->output.offset);
Glauber Costa 47b0758
 	if (ret < 0) {
Glauber Costa 47b0758
 	    if (ret == GNUTLS_E_AGAIN)
Glauber Costa 47b0758
 		errno = EAGAIN;
188e398
@@ -979,13 +955,13 @@ static void vnc_client_write(void *opaque)
Glauber Costa 47b0758
     }
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
Glauber Costa 47b0758
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     vs->read_handler = func;
Glauber Costa 47b0758
     vs->read_handler_expect = expecting;
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_client_read(void *opaque)
Glauber Costa 47b0758
+void vnc_client_read(void *opaque)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     VncState *vs = opaque;
Glauber Costa 47b0758
     long ret;
188e398
@@ -993,8 +969,8 @@ static void vnc_client_read(void *opaque)
Glauber Costa 47b0758
     buffer_reserve(&vs->input, 4096);
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-    if (vs->tls_session) {
Glauber Costa 47b0758
-	ret = gnutls_read(vs->tls_session, buffer_end(&vs->input), 4096);
Glauber Costa 47b0758
+    if (vs->tls.session) {
Glauber Costa 47b0758
+	ret = gnutls_read(vs->tls.session, buffer_end(&vs->input), 4096);
Glauber Costa 47b0758
 	if (ret < 0) {
Glauber Costa 47b0758
 	    if (ret == GNUTLS_E_AGAIN)
Glauber Costa 47b0758
 		errno = EAGAIN;
188e398
@@ -1033,7 +1009,7 @@ static void vnc_client_read(void *opaque)
Glauber Costa 47b0758
     }
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_write(VncState *vs, const void *data, size_t len)
Glauber Costa 47b0758
+void vnc_write(VncState *vs, const void *data, size_t len)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     buffer_reserve(&vs->output, len);
Glauber Costa 47b0758
 
188e398
@@ -1044,12 +1020,12 @@ static void vnc_write(VncState *vs, const void *data, size_t len)
Glauber Costa 47b0758
     buffer_append(&vs->output, data, len);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_write_s32(VncState *vs, int32_t value)
Glauber Costa 47b0758
+void vnc_write_s32(VncState *vs, int32_t value)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     vnc_write_u32(vs, *(uint32_t *)&value);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_write_u32(VncState *vs, uint32_t value)
Glauber Costa 47b0758
+void vnc_write_u32(VncState *vs, uint32_t value)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     uint8_t buf[4];
Glauber Costa 47b0758
 
188e398
@@ -1061,7 +1037,7 @@ static void vnc_write_u32(VncState *vs, uint32_t value)
Glauber Costa 47b0758
     vnc_write(vs, buf, 4);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_write_u16(VncState *vs, uint16_t value)
Glauber Costa 47b0758
+void vnc_write_u16(VncState *vs, uint16_t value)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     uint8_t buf[2];
Glauber Costa 47b0758
 
188e398
@@ -1071,74 +1047,39 @@ static void vnc_write_u16(VncState *vs, uint16_t value)
Glauber Costa 47b0758
     vnc_write(vs, buf, 2);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_write_u8(VncState *vs, uint8_t value)
Glauber Costa 47b0758
+void vnc_write_u8(VncState *vs, uint8_t value)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     vnc_write(vs, (char *)&value, 1);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static void vnc_flush(VncState *vs)
Glauber Costa 47b0758
+void vnc_flush(VncState *vs)
Glauber Costa 47b0758
 {
188e398
     if (vs->csock != -1 && vs->output.offset)
Glauber Costa 47b0758
 	vnc_client_write(vs);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static uint8_t read_u8(uint8_t *data, size_t offset)
Glauber Costa 47b0758
+uint8_t read_u8(uint8_t *data, size_t offset)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     return data[offset];
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static uint16_t read_u16(uint8_t *data, size_t offset)
Glauber Costa 47b0758
+uint16_t read_u16(uint8_t *data, size_t offset)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static int32_t read_s32(uint8_t *data, size_t offset)
Glauber Costa 47b0758
+int32_t read_s32(uint8_t *data, size_t offset)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
Glauber Costa 47b0758
 		     (data[offset + 2] << 8) | data[offset + 3]);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static uint32_t read_u32(uint8_t *data, size_t offset)
Glauber Costa 47b0758
+uint32_t read_u32(uint8_t *data, size_t offset)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     return ((data[offset] << 24) | (data[offset + 1] << 16) |
Glauber Costa 47b0758
 	    (data[offset + 2] << 8) | data[offset + 3]);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-#ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
Glauber Costa 47b0758
-                            const void *data,
Glauber Costa 47b0758
-                            size_t len) {
Glauber Costa 47b0758
-    struct VncState *vs = (struct VncState *)transport;
Glauber Costa 47b0758
-    int ret;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
- retry:
Glauber Costa 47b0758
-    ret = send(vs->csock, data, len, 0);
Glauber Costa 47b0758
-    if (ret < 0) {
Glauber Costa 47b0758
-	if (errno == EINTR)
Glauber Costa 47b0758
-	    goto retry;
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    return ret;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
Glauber Costa 47b0758
-                            void *data,
Glauber Costa 47b0758
-                            size_t len) {
Glauber Costa 47b0758
-    struct VncState *vs = (struct VncState *)transport;
Glauber Costa 47b0758
-    int ret;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
- retry:
Glauber Costa 47b0758
-    ret = recv(vs->csock, data, len, 0);
Glauber Costa 47b0758
-    if (ret < 0) {
Glauber Costa 47b0758
-	if (errno == EINTR)
Glauber Costa 47b0758
-	    goto retry;
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    return ret;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-#endif /* CONFIG_VNC_TLS */
Glauber Costa 47b0758
-
Glauber Costa 47b0758
 static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
 }
188e398
@@ -1730,6 +1671,11 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
     return 0;
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
+void start_client_init(VncState *vs)
Glauber Costa 47b0758
+{
Glauber Costa 47b0758
+    vnc_read_when(vs, protocol_client_init, 1);
Glauber Costa 47b0758
+}
Glauber Costa 47b0758
+
Glauber Costa 47b0758
 static void make_challenge(VncState *vs)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     int i;
188e398
@@ -1785,12 +1731,12 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
 	vnc_write_u32(vs, 0); /* Accept auth */
Glauber Costa 47b0758
 	vnc_flush(vs);
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-	vnc_read_when(vs, protocol_client_init, 1);
Glauber Costa 47b0758
+        start_client_init(vs);
Glauber Costa 47b0758
     }
Glauber Costa 47b0758
     return 0;
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-static int start_auth_vnc(VncState *vs)
Glauber Costa 47b0758
+void start_auth_vnc(VncState *vs)
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     make_challenge(vs);
Glauber Costa 47b0758
     /* Send client a 'random' challenge */
188e398
@@ -1798,410 +1744,8 @@ static int start_auth_vnc(VncState *vs)
Glauber Costa 47b0758
     vnc_flush(vs);
Glauber Costa 47b0758
 
Glauber Costa 47b0758
     vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-#ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-#define DH_BITS 1024
Glauber Costa 47b0758
-static gnutls_dh_params_t dh_params;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int vnc_tls_initialize(void)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    static int tlsinitialized = 0;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (tlsinitialized)
Glauber Costa 47b0758
-	return 1;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (gnutls_global_init () < 0)
Glauber Costa 47b0758
-	return 0;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    /* XXX ought to re-generate diffie-hellmen params periodically */
Glauber Costa 47b0758
-    if (gnutls_dh_params_init (&dh_params) < 0)
Glauber Costa 47b0758
-	return 0;
Glauber Costa 47b0758
-    if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
Glauber Costa 47b0758
-	return 0;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
Glauber Costa 47b0758
-    gnutls_global_set_log_level(10);
Glauber Costa 47b0758
-    gnutls_global_set_log_function(vnc_debug_gnutls_log);
Glauber Costa 47b0758
-#endif
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    tlsinitialized = 1;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return 1;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    gnutls_anon_server_credentials anon_cred;
Glauber Costa 47b0758
-    int ret;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
Glauber Costa 47b0758
-	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    gnutls_anon_set_server_dh_params(anon_cred, dh_params);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return anon_cred;
be685f7
-}
be685f7
-
be685f7
-
Glauber Costa 47b0758
-static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *vs)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    gnutls_certificate_credentials_t x509_cred;
Glauber Costa 47b0758
-    int ret;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (!vs->vd->x509cacert) {
Glauber Costa 47b0758
-	VNC_DEBUG("No CA x509 certificate specified\n");
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    if (!vs->vd->x509cert) {
Glauber Costa 47b0758
-	VNC_DEBUG("No server x509 certificate specified\n");
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    if (!vs->vd->x509key) {
Glauber Costa 47b0758
-	VNC_DEBUG("No server private key specified\n");
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
Glauber Costa 47b0758
-	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
Glauber Costa 47b0758
-						      vs->vd->x509cacert,
Glauber Costa 47b0758
-						      GNUTLS_X509_FMT_PEM)) < 0) {
Glauber Costa 47b0758
-	VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-	gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
Glauber Costa 47b0758
-						     vs->vd->x509cert,
Glauber Costa 47b0758
-						     vs->vd->x509key,
Glauber Costa 47b0758
-						     GNUTLS_X509_FMT_PEM)) < 0) {
Glauber Costa 47b0758
-	VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-	gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
-	return NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (vs->vd->x509cacrl) {
Glauber Costa 47b0758
-	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
Glauber Costa 47b0758
-							vs->vd->x509cacrl,
Glauber Costa 47b0758
-							GNUTLS_X509_FMT_PEM)) < 0) {
Glauber Costa 47b0758
-	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-	    gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
-	    return NULL;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    gnutls_certificate_set_dh_params (x509_cred, dh_params);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return x509_cred;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int vnc_validate_certificate(struct VncState *vs)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    int ret;
Glauber Costa 47b0758
-    unsigned int status;
Glauber Costa 47b0758
-    const gnutls_datum_t *certs;
Glauber Costa 47b0758
-    unsigned int nCerts, i;
Glauber Costa 47b0758
-    time_t now;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    VNC_DEBUG("Validating client certificate\n");
Glauber Costa 47b0758
-    if ((ret = gnutls_certificate_verify_peers2 (vs->tls_session, &status)) < 0) {
Glauber Costa 47b0758
-	VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if ((now = time(NULL)) == ((time_t)-1)) {
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (status != 0) {
Glauber Costa 47b0758
-	if (status & GNUTLS_CERT_INVALID)
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate is not trusted.\n");
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate hasn't got a known issuer.\n");
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (status & GNUTLS_CERT_REVOKED)
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate has been revoked.\n");
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate uses an insecure algorithm\n");
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    } else {
Glauber Costa 47b0758
-	VNC_DEBUG("Certificate is valid!\n");
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    /* Only support x509 for now */
Glauber Costa 47b0758
-    if (gnutls_certificate_type_get(vs->tls_session) != GNUTLS_CRT_X509)
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (!(certs = gnutls_certificate_get_peers(vs->tls_session, &nCerts)))
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    for (i = 0 ; i < nCerts ; i++) {
Glauber Costa 47b0758
-	gnutls_x509_crt_t cert;
Glauber Costa 47b0758
-	VNC_DEBUG ("Checking certificate chain %d\n", i);
Glauber Costa 47b0758
-	if (gnutls_x509_crt_init (&cert) < 0)
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
Glauber Costa 47b0758
-	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_x509_crt_get_expiration_time (cert) < now) {
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate has expired\n");
Glauber Costa 47b0758
-	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_x509_crt_get_activation_time (cert) > now) {
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate is not yet activated\n");
Glauber Costa 47b0758
-	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_x509_crt_get_activation_time (cert) > now) {
Glauber Costa 47b0758
-	    VNC_DEBUG("The certificate is not yet activated\n");
Glauber Costa 47b0758
-	    gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	gnutls_x509_crt_deinit (cert);
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int start_auth_vencrypt_subauth(VncState *vs)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    switch (vs->vd->subauth) {
Glauber Costa 47b0758
-    case VNC_AUTH_VENCRYPT_TLSNONE:
Glauber Costa 47b0758
-    case VNC_AUTH_VENCRYPT_X509NONE:
Glauber Costa 47b0758
-       VNC_DEBUG("Accept TLS auth none\n");
Glauber Costa 47b0758
-       vnc_write_u32(vs, 0); /* Accept auth completion */
Glauber Costa 47b0758
-       vnc_read_when(vs, protocol_client_init, 1);
Glauber Costa 47b0758
-       break;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    case VNC_AUTH_VENCRYPT_TLSVNC:
Glauber Costa 47b0758
-    case VNC_AUTH_VENCRYPT_X509VNC:
Glauber Costa 47b0758
-       VNC_DEBUG("Start TLS auth VNC\n");
Glauber Costa 47b0758
-       return start_auth_vnc(vs);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    default: /* Should not be possible, but just in case */
Glauber Costa 47b0758
-       VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
Glauber Costa 47b0758
-       vnc_write_u8(vs, 1);
Glauber Costa 47b0758
-       if (vs->minor >= 8) {
Glauber Costa 47b0758
-           static const char err[] = "Unsupported authentication type";
Glauber Costa 47b0758
-           vnc_write_u32(vs, sizeof(err));
Glauber Costa 47b0758
-           vnc_write(vs, err, sizeof(err));
Glauber Costa 47b0758
-       }
Glauber Costa 47b0758
-       vnc_client_error(vs);
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static void vnc_handshake_io(void *opaque);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int vnc_continue_handshake(struct VncState *vs) {
Glauber Costa 47b0758
-    int ret;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if ((ret = gnutls_handshake(vs->tls_session)) < 0) {
Glauber Costa 47b0758
-       if (!gnutls_error_is_fatal(ret)) {
Glauber Costa 47b0758
-           VNC_DEBUG("Handshake interrupted (blocking)\n");
Glauber Costa 47b0758
-           if (!gnutls_record_get_direction(vs->tls_session))
Glauber Costa 47b0758
-               qemu_set_fd_handler(vs->csock, vnc_handshake_io, NULL, vs);
Glauber Costa 47b0758
-           else
Glauber Costa 47b0758
-               qemu_set_fd_handler(vs->csock, NULL, vnc_handshake_io, vs);
Glauber Costa 47b0758
-           return 0;
Glauber Costa 47b0758
-       }
Glauber Costa 47b0758
-       VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
Glauber Costa 47b0758
-       vnc_client_error(vs);
Glauber Costa 47b0758
-       return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (vs->vd->x509verify) {
Glauber Costa 47b0758
-	if (vnc_validate_certificate(vs) < 0) {
Glauber Costa 47b0758
-	    VNC_DEBUG("Client verification failed\n");
Glauber Costa 47b0758
-	    vnc_client_error(vs);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	} else {
Glauber Costa 47b0758
-	    VNC_DEBUG("Client verification passed\n");
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    VNC_DEBUG("Handshake done, switching to TLS data mode\n");
Glauber Costa 47b0758
-    vs->wiremode = VNC_WIREMODE_TLS;
Glauber Costa 47b0758
-    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return start_auth_vencrypt_subauth(vs);
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static void vnc_handshake_io(void *opaque) {
Glauber Costa 47b0758
-    struct VncState *vs = (struct VncState *)opaque;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    VNC_DEBUG("Handshake IO continue\n");
Glauber Costa 47b0758
-    vnc_continue_handshake(vs);
be685f7
 }
be685f7
 
Glauber Costa 47b0758
-#define NEED_X509_AUTH(vs)			      \
Glauber Costa 47b0758
-    ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
Glauber Costa 47b0758
-     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
Glauber Costa 47b0758
-     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int vnc_start_tls(struct VncState *vs) {
Glauber Costa 47b0758
-    static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
Glauber Costa 47b0758
-    static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
Glauber Costa 47b0758
-    static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
Glauber Costa 47b0758
-    static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    VNC_DEBUG("Do TLS setup\n");
Glauber Costa 47b0758
-    if (vnc_tls_initialize() < 0) {
Glauber Costa 47b0758
-	VNC_DEBUG("Failed to init TLS\n");
Glauber Costa 47b0758
-	vnc_client_error(vs);
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    if (vs->tls_session == NULL) {
Glauber Costa 47b0758
-	if (gnutls_init(&vs->tls_session, GNUTLS_SERVER) < 0) {
Glauber Costa 47b0758
-	    vnc_client_error(vs);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_set_default_priority(vs->tls_session) < 0) {
Glauber Costa 47b0758
-	    gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-	    vs->tls_session = NULL;
Glauber Costa 47b0758
-	    vnc_client_error(vs);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_kx_set_priority(vs->tls_session, NEED_X509_AUTH(vs) ? kx_x509 : kx_anon) < 0) {
Glauber Costa 47b0758
-	    gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-	    vs->tls_session = NULL;
Glauber Costa 47b0758
-	    vnc_client_error(vs);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_certificate_type_set_priority(vs->tls_session, cert_type_priority) < 0) {
Glauber Costa 47b0758
-	    gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-	    vs->tls_session = NULL;
Glauber Costa 47b0758
-	    vnc_client_error(vs);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (gnutls_protocol_set_priority(vs->tls_session, protocol_priority) < 0) {
Glauber Costa 47b0758
-	    gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-	    vs->tls_session = NULL;
Glauber Costa 47b0758
-	    vnc_client_error(vs);
Glauber Costa 47b0758
-	    return -1;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (NEED_X509_AUTH(vs)) {
Glauber Costa 47b0758
-	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs);
Glauber Costa 47b0758
-	    if (!x509_cred) {
Glauber Costa 47b0758
-		gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-		vs->tls_session = NULL;
Glauber Costa 47b0758
-		vnc_client_error(vs);
Glauber Costa 47b0758
-		return -1;
Glauber Costa 47b0758
-	    }
Glauber Costa 47b0758
-	    if (gnutls_credentials_set(vs->tls_session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
Glauber Costa 47b0758
-		gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-		vs->tls_session = NULL;
Glauber Costa 47b0758
-		gnutls_certificate_free_credentials(x509_cred);
Glauber Costa 47b0758
-		vnc_client_error(vs);
Glauber Costa 47b0758
-		return -1;
Glauber Costa 47b0758
-	    }
Glauber Costa 47b0758
-	    if (vs->vd->x509verify) {
Glauber Costa 47b0758
-		VNC_DEBUG("Requesting a client certificate\n");
Glauber Costa 47b0758
-		gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);
Glauber Costa 47b0758
-	    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	} else {
Glauber Costa 47b0758
-	    gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
Glauber Costa 47b0758
-	    if (!anon_cred) {
Glauber Costa 47b0758
-		gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-		vs->tls_session = NULL;
Glauber Costa 47b0758
-		vnc_client_error(vs);
Glauber Costa 47b0758
-		return -1;
Glauber Costa 47b0758
-	    }
Glauber Costa 47b0758
-	    if (gnutls_credentials_set(vs->tls_session, GNUTLS_CRD_ANON, anon_cred) < 0) {
Glauber Costa 47b0758
-		gnutls_deinit(vs->tls_session);
Glauber Costa 47b0758
-		vs->tls_session = NULL;
Glauber Costa 47b0758
-		gnutls_anon_free_server_credentials(anon_cred);
Glauber Costa 47b0758
-		vnc_client_error(vs);
Glauber Costa 47b0758
-		return -1;
Glauber Costa 47b0758
-	    }
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	gnutls_transport_set_ptr(vs->tls_session, (gnutls_transport_ptr_t)vs);
Glauber Costa 47b0758
-	gnutls_transport_set_push_function(vs->tls_session, vnc_tls_push);
Glauber Costa 47b0758
-	gnutls_transport_set_pull_function(vs->tls_session, vnc_tls_pull);
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    VNC_DEBUG("Start TLS handshake process\n");
Glauber Costa 47b0758
-    return vnc_continue_handshake(vs);
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    int auth = read_u32(data, 0);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (auth != vs->vd->subauth) {
Glauber Costa 47b0758
-	VNC_DEBUG("Rejecting auth %d\n", auth);
Glauber Costa 47b0758
-	vnc_write_u8(vs, 0); /* Reject auth */
Glauber Costa 47b0758
-	vnc_flush(vs);
Glauber Costa 47b0758
-	vnc_client_error(vs);
Glauber Costa 47b0758
-    } else {
Glauber Costa 47b0758
-	VNC_DEBUG("Accepting auth %d, starting handshake\n", auth);
Glauber Costa 47b0758
-	vnc_write_u8(vs, 1); /* Accept auth */
Glauber Costa 47b0758
-	vnc_flush(vs);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-	if (vnc_start_tls(vs) < 0) {
Glauber Costa 47b0758
-	    VNC_DEBUG("Failed to complete TLS\n");
Glauber Costa 47b0758
-	    return 0;
Glauber Costa 47b0758
-	}
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    if (data[0] != 0 ||
Glauber Costa 47b0758
-	data[1] != 2) {
Glauber Costa 47b0758
-	VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
Glauber Costa 47b0758
-	vnc_write_u8(vs, 1); /* Reject version */
Glauber Costa 47b0758
-	vnc_flush(vs);
Glauber Costa 47b0758
-	vnc_client_error(vs);
Glauber Costa 47b0758
-    } else {
Glauber Costa 47b0758
-	VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
Glauber Costa 47b0758
-	vnc_write_u8(vs, 0); /* Accept version */
Glauber Costa 47b0758
-	vnc_write_u8(vs, 1); /* Number of sub-auths */
Glauber Costa 47b0758
-	vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
Glauber Costa 47b0758
-	vnc_flush(vs);
Glauber Costa 47b0758
-	vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int start_auth_vencrypt(VncState *vs)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    /* Send VeNCrypt version 0.2 */
Glauber Costa 47b0758
-    vnc_write_u8(vs, 0);
Glauber Costa 47b0758
-    vnc_write_u8(vs, 2);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    vnc_read_when(vs, protocol_client_vencrypt_init, 2);
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-#endif /* CONFIG_VNC_TLS */
be685f7
 
Glauber Costa 47b0758
 static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
 {
188e398
@@ -2225,17 +1769,19 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
Glauber Costa 47b0758
                vnc_write_u32(vs, 0); /* Accept auth completion */
Glauber Costa 47b0758
                vnc_flush(vs);
Glauber Costa 47b0758
            }
Glauber Costa 47b0758
-           vnc_read_when(vs, protocol_client_init, 1);
Glauber Costa 47b0758
+           start_client_init(vs);
Glauber Costa 47b0758
            break;
Glauber Costa 47b0758
 
Glauber Costa 47b0758
        case VNC_AUTH_VNC:
Glauber Costa 47b0758
            VNC_DEBUG("Start VNC auth\n");
Glauber Costa 47b0758
-           return start_auth_vnc(vs);
Glauber Costa 47b0758
+           start_auth_vnc(vs);
Glauber Costa 47b0758
+           break;
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
        case VNC_AUTH_VENCRYPT:
Glauber Costa 47b0758
            VNC_DEBUG("Accept VeNCrypt auth\n");;
Glauber Costa 47b0758
-           return start_auth_vencrypt(vs);
Glauber Costa 47b0758
+           start_auth_vencrypt(vs);
Glauber Costa 47b0758
+           break;
Glauber Costa 47b0758
 #endif /* CONFIG_VNC_TLS */
Glauber Costa 47b0758
 
Glauber Costa 47b0758
        default: /* Should not be possible, but just in case */
188e398
@@ -2288,7 +1834,7 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
Glauber Costa 47b0758
             VNC_DEBUG("Tell client auth none\n");
Glauber Costa 47b0758
             vnc_write_u32(vs, vs->vd->auth);
Glauber Costa 47b0758
             vnc_flush(vs);
Glauber Costa 47b0758
-            vnc_read_when(vs, protocol_client_init, 1);
Glauber Costa 47b0758
+	    start_client_init(vs);
Glauber Costa 47b0758
        } else if (vs->vd->auth == VNC_AUTH_VNC) {
Glauber Costa 47b0758
             VNC_DEBUG("Tell client VNC auth\n");
Glauber Costa 47b0758
             vnc_write_u32(vs, vs->vd->auth);
188e398
@@ -2391,61 +1937,6 @@ void vnc_display_init(DisplayState *ds)
Glauber Costa 47b0758
     register_displaychangelistener(ds, dcl);
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-#ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-static int vnc_set_x509_credential(VncDisplay *vs,
Glauber Costa 47b0758
-				   const char *certdir,
Glauber Costa 47b0758
-				   const char *filename,
Glauber Costa 47b0758
-				   char **cred,
Glauber Costa 47b0758
-				   int ignoreMissing)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    struct stat sb;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    if (*cred) {
Glauber Costa 47b0758
-	qemu_free(*cred);
Glauber Costa 47b0758
-	*cred = NULL;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    strcpy(*cred, certdir);
Glauber Costa 47b0758
-    strcat(*cred, "/");
Glauber Costa 47b0758
-    strcat(*cred, filename);
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    VNC_DEBUG("Check %s\n", *cred);
Glauber Costa 47b0758
-    if (stat(*cred, &sb) < 0) {
Glauber Costa 47b0758
-	qemu_free(*cred);
Glauber Costa 47b0758
-	*cred = NULL;
Glauber Costa 47b0758
-	if (ignoreMissing && errno == ENOENT)
Glauber Costa 47b0758
-	    return 0;
Glauber Costa 47b0758
-	return -1;
Glauber Costa 47b0758
-    }
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-static int vnc_set_x509_credential_dir(VncDisplay *vs,
Glauber Costa 47b0758
-				       const char *certdir)
Glauber Costa 47b0758
-{
Glauber Costa 47b0758
-    if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)
Glauber Costa 47b0758
-	goto cleanup;
Glauber Costa 47b0758
-    if (vnc_set_x509_credential(vs, certdir, X509_CA_CRL_FILE, &vs->x509cacrl, 1) < 0)
Glauber Costa 47b0758
-	goto cleanup;
Glauber Costa 47b0758
-    if (vnc_set_x509_credential(vs, certdir, X509_SERVER_CERT_FILE, &vs->x509cert, 0) < 0)
Glauber Costa 47b0758
-	goto cleanup;
Glauber Costa 47b0758
-    if (vnc_set_x509_credential(vs, certdir, X509_SERVER_KEY_FILE, &vs->x509key, 0) < 0)
Glauber Costa 47b0758
-	goto cleanup;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    return 0;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
- cleanup:
Glauber Costa 47b0758
-    qemu_free(vs->x509cacert);
Glauber Costa 47b0758
-    qemu_free(vs->x509cacrl);
Glauber Costa 47b0758
-    qemu_free(vs->x509cert);
Glauber Costa 47b0758
-    qemu_free(vs->x509key);
Glauber Costa 47b0758
-    vs->x509cacert = vs->x509cacrl = vs->x509cert = vs->x509key = NULL;
Glauber Costa 47b0758
-    return -1;
Glauber Costa 47b0758
-}
Glauber Costa 47b0758
-#endif /* CONFIG_VNC_TLS */
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 void vnc_display_close(DisplayState *ds)
Glauber Costa 47b0758
 {
188e398
@@ -2465,7 +1956,7 @@ void vnc_display_close(DisplayState *ds)
Glauber Costa 47b0758
     vs->auth = VNC_AUTH_INVALID;
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
     vs->subauth = VNC_AUTH_INVALID;
Glauber Costa 47b0758
-    vs->x509verify = 0;
Glauber Costa 47b0758
+    vs->tls.x509verify = 0;
Glauber Costa 47b0758
 #endif
Glauber Costa 47b0758
 }
Glauber Costa 47b0758
 
188e398
@@ -2521,7 +2012,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
Glauber Costa 47b0758
 	    char *start, *end;
Glauber Costa 47b0758
 	    x509 = 1; /* Require x509 certificates */
Glauber Costa 47b0758
 	    if (strncmp(options, "x509verify", 10) == 0)
Glauber Costa 47b0758
-	        vs->x509verify = 1; /* ...and verify client certs */
Glauber Costa 47b0758
+	        vs->tls.x509verify = 1; /* ...and verify client certs */
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 	    /* Now check for 'x509=/some/path' postfix
Glauber Costa 47b0758
 	     * and use that to setup x509 certificate/key paths */
188e398
@@ -2532,7 +2023,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
Glauber Costa 47b0758
 		char *path = qemu_strndup(start + 1, len);
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 		VNC_DEBUG("Trying certificate path '%s'\n", path);
Glauber Costa 47b0758
-		if (vnc_set_x509_credential_dir(vs, path) < 0) {
Glauber Costa 47b0758
+		if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
Glauber Costa 47b0758
 		    fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
Glauber Costa 47b0758
 		    qemu_free(path);
Glauber Costa 47b0758
 		    qemu_free(vs->display);
be685f7
diff --git a/vnc.h b/vnc.h
be685f7
index eb33643..d69b295 100644
be685f7
--- a/vnc.h
be685f7
+++ b/vnc.h
Glauber Costa 47b0758
@@ -32,13 +32,16 @@
Glauber Costa 47b0758
 #include "audio/audio.h"
Glauber Costa 47b0758
 #include <zlib.h>
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-#ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-#include <gnutls/gnutls.h>
Glauber Costa 47b0758
-#include <gnutls/x509.h>
Glauber Costa 47b0758
-#endif /* CONFIG_VNC_TLS */
Glauber Costa 47b0758
-
Glauber Costa 47b0758
 #include "keymaps.h"
Glauber Costa 47b0758
 
Glauber Costa 47b0758
+// #define _VNC_DEBUG 1
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+#ifdef _VNC_DEBUG
Glauber Costa 47b0758
+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
Glauber Costa 47b0758
+#else
Glauber Costa 47b0758
+#define VNC_DEBUG(fmt, ...) do { } while (0)
Glauber Costa 47b0758
+#endif
Glauber Costa 47b0758
+
Glauber Costa 47b0758
 /*****************************************************************************
Glauber Costa 47b0758
  *
Glauber Costa 47b0758
  * Core data structures
be685f7
@@ -72,6 +75,11 @@ typedef void VncSendHextileTile(VncState *vs,
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 typedef struct VncDisplay VncDisplay;
Glauber Costa 47b0758
 
Glauber Costa 47b0758
+#ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
+#include "vnc-tls.h"
Glauber Costa 47b0758
+#include "vnc-auth-vencrypt.h"
Glauber Costa 47b0758
+#endif
Glauber Costa 47b0758
+
Glauber Costa 47b0758
 struct VncDisplay
Glauber Costa 47b0758
 {
Glauber Costa 47b0758
     int lsock;
Glauber Costa 47b0758
@@ -83,13 +91,8 @@ struct VncDisplay
Glauber Costa 47b0758
     char *password;
Glauber Costa 47b0758
     int auth;
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-    int subauth;
Glauber Costa 47b0758
-    int x509verify;
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-    char *x509cacert;
Glauber Costa 47b0758
-    char *x509cacrl;
Glauber Costa 47b0758
-    char *x509cert;
Glauber Costa 47b0758
-    char *x509key;
Glauber Costa 47b0758
+    int subauth; /* Used by VeNCrypt */
Glauber Costa 47b0758
+    VncDisplayTLS tls;
Glauber Costa 47b0758
 #endif
Glauber Costa 47b0758
 };
Glauber Costa 47b0758
 
Glauber Costa 47b0758
@@ -117,8 +120,7 @@ struct VncState
Glauber Costa 47b0758
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 #ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-    int wiremode;
Glauber Costa 47b0758
-    gnutls_session_t tls_session;
Glauber Costa 47b0758
+    VncStateTLS tls;
Glauber Costa 47b0758
 #endif
Glauber Costa 47b0758
 
Glauber Costa 47b0758
     Buffer output;
Glauber Costa 47b0758
@@ -162,12 +164,6 @@ enum {
Glauber Costa 47b0758
     VNC_AUTH_VENCRYPT = 19
Glauber Costa 47b0758
 };
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-#ifdef CONFIG_VNC_TLS
Glauber Costa 47b0758
-enum {
Glauber Costa 47b0758
-    VNC_WIREMODE_CLEAR,
Glauber Costa 47b0758
-    VNC_WIREMODE_TLS,
Glauber Costa 47b0758
-};
Glauber Costa 47b0758
-
Glauber Costa 47b0758
 enum {
Glauber Costa 47b0758
     VNC_AUTH_VENCRYPT_PLAIN = 256,
Glauber Costa 47b0758
     VNC_AUTH_VENCRYPT_TLSNONE = 257,
Glauber Costa 47b0758
@@ -178,12 +174,6 @@ enum {
Glauber Costa 47b0758
     VNC_AUTH_VENCRYPT_X509PLAIN = 262,
Glauber Costa 47b0758
 };
Glauber Costa 47b0758
 
Glauber Costa 47b0758
-#define X509_CA_CERT_FILE "ca-cert.pem"
Glauber Costa 47b0758
-#define X509_CA_CRL_FILE "ca-crl.pem"
Glauber Costa 47b0758
-#define X509_SERVER_KEY_FILE "server-key.pem"
Glauber Costa 47b0758
-#define X509_SERVER_CERT_FILE "server-cert.pem"
Glauber Costa 47b0758
-
Glauber Costa 47b0758
-#endif /* CONFIG_VNC_TLS */
Glauber Costa 47b0758
 
Glauber Costa 47b0758
 /*****************************************************************************
Glauber Costa 47b0758
  *
Glauber Costa 47b0758
@@ -254,4 +244,38 @@ enum {
Glauber Costa 47b0758
 #define VNC_FEATURE_ZLIB_MASK                (1 << VNC_FEATURE_ZLIB)
Glauber Costa 47b0758
 #define VNC_FEATURE_COPYRECT_MASK            (1 << VNC_FEATURE_COPYRECT)
Glauber Costa 47b0758
 
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/*****************************************************************************
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ * Internal APIs
Glauber Costa 47b0758
+ *
Glauber Costa 47b0758
+ *****************************************************************************/
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/* Event loop functions */
Glauber Costa 47b0758
+void vnc_client_read(void *opaque);
Glauber Costa 47b0758
+void vnc_client_write(void *opaque);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/* Protocol I/O functions */
Glauber Costa 47b0758
+void vnc_write(VncState *vs, const void *data, size_t len);
Glauber Costa 47b0758
+void vnc_write_u32(VncState *vs, uint32_t value);
Glauber Costa 47b0758
+void vnc_write_s32(VncState *vs, int32_t value);
Glauber Costa 47b0758
+void vnc_write_u16(VncState *vs, uint16_t value);
Glauber Costa 47b0758
+void vnc_write_u8(VncState *vs, uint8_t value);
Glauber Costa 47b0758
+void vnc_flush(VncState *vs);
Glauber Costa 47b0758
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/* Buffer I/O functions */
Glauber Costa 47b0758
+uint8_t read_u8(uint8_t *data, size_t offset);
Glauber Costa 47b0758
+uint16_t read_u16(uint8_t *data, size_t offset);
Glauber Costa 47b0758
+int32_t read_s32(uint8_t *data, size_t offset);
Glauber Costa 47b0758
+uint32_t read_u32(uint8_t *data, size_t offset);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+/* Protocol stage functions */
Glauber Costa 47b0758
+void vnc_client_error(VncState *vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
+void start_client_init(VncState *vs);
Glauber Costa 47b0758
+void start_auth_vnc(VncState *vs);
Glauber Costa 47b0758
+
Glauber Costa 47b0758
 #endif /* __QEMU_VNC_H */
be685f7
-- 
d28bc6c
1.6.2.5
be685f7