|
@@ -34,21 +34,6 @@
|
|
#include "vnc_keysym.h"
|
|
#include "vnc_keysym.h"
|
|
#include "d3des.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) { \
|
|
#define count_bits(c, v) { \
|
|
for (c = 0; v; v >>= 1) \
|
|
for (c = 0; v; v >>= 1) \
|
|
{ \
|
|
{ \
|
|
@@ -204,14 +189,7 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
|
|
3) resolutions > 1024
|
|
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_update_client(void *opaque);
|
|
-static void vnc_client_read(void *opaque);
|
|
|
|
|
|
|
|
static void vnc_colordepth(VncState *vs);
|
|
static void vnc_colordepth(VncState *vs);
|
|
|
|
|
|
@@ -868,10 +846,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
|
|
if (vs->input.buffer) qemu_free(vs->input.buffer);
|
|
if (vs->input.buffer) qemu_free(vs->input.buffer);
|
|
if (vs->output.buffer) qemu_free(vs->output.buffer);
|
|
if (vs->output.buffer) qemu_free(vs->output.buffer);
|
|
#ifdef CONFIG_VNC_TLS
|
|
#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 */
|
|
#endif /* CONFIG_VNC_TLS */
|
|
audio_del(vs);
|
|
audio_del(vs);
|
|
|
|
|
|
@@ -897,19 +872,20 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static void vnc_client_error(VncState *vs)
|
|
|
|
|
|
+
|
|
|
|
+void vnc_client_error(VncState *vs)
|
|
{
|
|
{
|
|
vnc_client_io_error(vs, -1, EINVAL);
|
|
vnc_client_io_error(vs, -1, EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
-static void vnc_client_write(void *opaque)
|
|
|
|
|
|
+void vnc_client_write(void *opaque)
|
|
{
|
|
{
|
|
long ret;
|
|
long ret;
|
|
VncState *vs = opaque;
|
|
VncState *vs = opaque;
|
|
|
|
|
|
#ifdef CONFIG_VNC_TLS
|
|
#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 < 0) {
|
|
if (ret == GNUTLS_E_AGAIN)
|
|
if (ret == GNUTLS_E_AGAIN)
|
|
errno = EAGAIN;
|
|
errno = EAGAIN;
|
|
@@ -932,13 +908,13 @@ static void vnc_client_write(void *opaque)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-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 = func;
|
|
vs->read_handler_expect = expecting;
|
|
vs->read_handler_expect = expecting;
|
|
}
|
|
}
|
|
|
|
|
|
-static void vnc_client_read(void *opaque)
|
|
|
|
|
|
+void vnc_client_read(void *opaque)
|
|
{
|
|
{
|
|
VncState *vs = opaque;
|
|
VncState *vs = opaque;
|
|
long ret;
|
|
long ret;
|
|
@@ -946,8 +922,8 @@ static void vnc_client_read(void *opaque)
|
|
buffer_reserve(&vs->input, 4096);
|
|
buffer_reserve(&vs->input, 4096);
|
|
|
|
|
|
#ifdef CONFIG_VNC_TLS
|
|
#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 < 0) {
|
|
if (ret == GNUTLS_E_AGAIN)
|
|
if (ret == GNUTLS_E_AGAIN)
|
|
errno = EAGAIN;
|
|
errno = EAGAIN;
|
|
@@ -981,7 +957,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);
|
|
buffer_reserve(&vs->output, len);
|
|
|
|
|
|
@@ -992,12 +968,12 @@ static void vnc_write(VncState *vs, const void *data, size_t len)
|
|
buffer_append(&vs->output, data, len);
|
|
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);
|
|
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];
|
|
uint8_t buf[4];
|
|
|
|
|
|
@@ -1009,7 +985,7 @@ static void vnc_write_u32(VncState *vs, uint32_t value)
|
|
vnc_write(vs, buf, 4);
|
|
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];
|
|
uint8_t buf[2];
|
|
|
|
|
|
@@ -1019,74 +995,39 @@ static void vnc_write_u16(VncState *vs, uint16_t value)
|
|
vnc_write(vs, buf, 2);
|
|
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);
|
|
vnc_write(vs, (char *)&value, 1);
|
|
}
|
|
}
|
|
|
|
|
|
-static void vnc_flush(VncState *vs)
|
|
|
|
|
|
+void vnc_flush(VncState *vs)
|
|
{
|
|
{
|
|
if (vs->output.offset)
|
|
if (vs->output.offset)
|
|
vnc_client_write(vs);
|
|
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];
|
|
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);
|
|
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) |
|
|
return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
|
|
(data[offset + 2] << 8) | data[offset + 3]);
|
|
(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) |
|
|
return ((data[offset] << 24) | (data[offset + 1] << 16) |
|
|
(data[offset + 2] << 8) | data[offset + 3]);
|
|
(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)
|
|
static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
@@ -1669,6 +1610,11 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void start_client_init(VncState *vs)
|
|
|
|
+{
|
|
|
|
+ vnc_read_when(vs, protocol_client_init, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
static void make_challenge(VncState *vs)
|
|
static void make_challenge(VncState *vs)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
@@ -1724,12 +1670,12 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
|
|
vnc_write_u32(vs, 0); /* Accept auth */
|
|
vnc_write_u32(vs, 0); /* Accept auth */
|
|
vnc_flush(vs);
|
|
vnc_flush(vs);
|
|
|
|
|
|
- vnc_read_when(vs, protocol_client_init, 1);
|
|
|
|
|
|
+ start_client_init(vs);
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int start_auth_vnc(VncState *vs)
|
|
|
|
|
|
+void start_auth_vnc(VncState *vs)
|
|
{
|
|
{
|
|
make_challenge(vs);
|
|
make_challenge(vs);
|
|
/* Send client a 'random' challenge */
|
|
/* Send client a 'random' challenge */
|
|
@@ -1737,411 +1683,9 @@ static int start_auth_vnc(VncState *vs)
|
|
vnc_flush(vs);
|
|
vnc_flush(vs);
|
|
|
|
|
|
vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
|
|
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)
|
|
static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
|
|
{
|
|
{
|
|
/* We only advertise 1 auth scheme at a time, so client
|
|
/* We only advertise 1 auth scheme at a time, so client
|
|
@@ -2164,17 +1708,19 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
|
|
vnc_write_u32(vs, 0); /* Accept auth completion */
|
|
vnc_write_u32(vs, 0); /* Accept auth completion */
|
|
vnc_flush(vs);
|
|
vnc_flush(vs);
|
|
}
|
|
}
|
|
- vnc_read_when(vs, protocol_client_init, 1);
|
|
|
|
|
|
+ start_client_init(vs);
|
|
break;
|
|
break;
|
|
|
|
|
|
case VNC_AUTH_VNC:
|
|
case VNC_AUTH_VNC:
|
|
VNC_DEBUG("Start VNC auth\n");
|
|
VNC_DEBUG("Start VNC auth\n");
|
|
- return start_auth_vnc(vs);
|
|
|
|
|
|
+ start_auth_vnc(vs);
|
|
|
|
+ break;
|
|
|
|
|
|
#ifdef CONFIG_VNC_TLS
|
|
#ifdef CONFIG_VNC_TLS
|
|
case VNC_AUTH_VENCRYPT:
|
|
case VNC_AUTH_VENCRYPT:
|
|
VNC_DEBUG("Accept VeNCrypt auth\n");;
|
|
VNC_DEBUG("Accept VeNCrypt auth\n");;
|
|
- return start_auth_vencrypt(vs);
|
|
|
|
|
|
+ start_auth_vencrypt(vs);
|
|
|
|
+ break;
|
|
#endif /* CONFIG_VNC_TLS */
|
|
#endif /* CONFIG_VNC_TLS */
|
|
|
|
|
|
default: /* Should not be possible, but just in case */
|
|
default: /* Should not be possible, but just in case */
|
|
@@ -2227,7 +1773,7 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
|
|
VNC_DEBUG("Tell client auth none\n");
|
|
VNC_DEBUG("Tell client auth none\n");
|
|
vnc_write_u32(vs, vs->vd->auth);
|
|
vnc_write_u32(vs, vs->vd->auth);
|
|
vnc_flush(vs);
|
|
vnc_flush(vs);
|
|
- vnc_read_when(vs, protocol_client_init, 1);
|
|
|
|
|
|
+ start_client_init(vs);
|
|
} else if (vs->vd->auth == VNC_AUTH_VNC) {
|
|
} else if (vs->vd->auth == VNC_AUTH_VNC) {
|
|
VNC_DEBUG("Tell client VNC auth\n");
|
|
VNC_DEBUG("Tell client VNC auth\n");
|
|
vnc_write_u32(vs, vs->vd->auth);
|
|
vnc_write_u32(vs, vs->vd->auth);
|
|
@@ -2329,61 +1875,6 @@ void vnc_display_init(DisplayState *ds)
|
|
register_displaychangelistener(ds, dcl);
|
|
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)
|
|
void vnc_display_close(DisplayState *ds)
|
|
{
|
|
{
|
|
@@ -2403,7 +1894,7 @@ void vnc_display_close(DisplayState *ds)
|
|
vs->auth = VNC_AUTH_INVALID;
|
|
vs->auth = VNC_AUTH_INVALID;
|
|
#ifdef CONFIG_VNC_TLS
|
|
#ifdef CONFIG_VNC_TLS
|
|
vs->subauth = VNC_AUTH_INVALID;
|
|
vs->subauth = VNC_AUTH_INVALID;
|
|
- vs->x509verify = 0;
|
|
|
|
|
|
+ vs->tls.x509verify = 0;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2459,7 +1950,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
|
|
char *start, *end;
|
|
char *start, *end;
|
|
x509 = 1; /* Require x509 certificates */
|
|
x509 = 1; /* Require x509 certificates */
|
|
if (strncmp(options, "x509verify", 10) == 0)
|
|
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
|
|
/* Now check for 'x509=/some/path' postfix
|
|
* and use that to setup x509 certificate/key paths */
|
|
* and use that to setup x509 certificate/key paths */
|
|
@@ -2470,7 +1961,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
|
|
char *path = qemu_strndup(start + 1, len);
|
|
char *path = qemu_strndup(start + 1, len);
|
|
|
|
|
|
VNC_DEBUG("Trying certificate path '%s'\n", path);
|
|
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);
|
|
fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
|
|
qemu_free(path);
|
|
qemu_free(path);
|
|
qemu_free(vs->display);
|
|
qemu_free(vs->display);
|