qemu/qemu-sasl-05-vnc-tls-vencrypt.patch

1674 lines
48 KiB
Diff

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