Index: qemu-kvm-0.10/qemu/Makefile =================================================================== --- qemu-kvm-0.10.orig/qemu/Makefile +++ qemu-kvm-0.10/qemu/Makefile @@ -149,6 +149,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 @@ -172,10 +175,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) Index: qemu-kvm-0.10/qemu/vnc-auth-vencrypt.c =================================================================== --- /dev/null +++ qemu-kvm-0.10/qemu/vnc-auth-vencrypt.c @@ -0,0 +1,167 @@ +/* + * QEMU VNC display driver: VeNCrypt authentication setup + * + * Copyright (C) 2006 Anthony Liguori + * 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); +} + Index: qemu-kvm-0.10/qemu/vnc-auth-vencrypt.h =================================================================== --- /dev/null +++ qemu-kvm-0.10/qemu/vnc-auth-vencrypt.h @@ -0,0 +1,33 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * 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__ */ Index: qemu-kvm-0.10/qemu/vnc-tls.c =================================================================== --- /dev/null +++ qemu-kvm-0.10/qemu/vnc-tls.c @@ -0,0 +1,414 @@ +/* + * QEMU VNC display driver: TLS helpers + * + * Copyright (C) 2006 Anthony Liguori + * 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; +} + Index: qemu-kvm-0.10/qemu/vnc-tls.h =================================================================== --- /dev/null +++ qemu-kvm-0.10/qemu/vnc-tls.h @@ -0,0 +1,70 @@ +/* + * QEMU VNC display driver. TLS helpers + * + * Copyright (C) 2006 Anthony Liguori + * 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 +#include + +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__ */ + Index: qemu-kvm-0.10/qemu/vnc.c =================================================================== --- qemu-kvm-0.10.orig/qemu/vnc.c +++ qemu-kvm-0.10/qemu/vnc.c @@ -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); Index: qemu-kvm-0.10/qemu/vnc.h =================================================================== --- qemu-kvm-0.10.orig/qemu/vnc.h +++ qemu-kvm-0.10/qemu/vnc.h @@ -32,13 +32,16 @@ #include "audio/audio.h" #include -#ifdef CONFIG_VNC_TLS -#include -#include -#endif /* CONFIG_VNC_TLS */ - #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 + /***************************************************************************** * * Core data structures @@ -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 */